.switch-toggle {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
}

.switch-toggle input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.switch-toggle-shell {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border: 0.5px solid rgba(255, 219, 176, 0.11);
    border-radius: 999px;
    background: rgba(26, 25, 40, 0.28);
}

.switch-toggle-option {
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
    color: var(--text-min);
    text-transform: uppercase;
    transition: color 0.25s ease, text-shadow 0.25s ease;
}

.switch-toggle-track {
    position: relative;
    width: 34px;
    height: 14px;
    border: 0.5px solid rgba(255, 255, 255, 0.12);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.03);
}

.switch-toggle-thumb {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 14px;
    height: 10px;
    border-radius: 999px;
    background: rgba(139, 139, 245, 0.88);
    box-shadow: 0 0 12px rgba(139, 139, 245, 0.35);
    transition: transform 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
}

.switch-toggle input:checked + .switch-toggle-shell .switch-toggle-thumb {
    transform: translateX(16px);
    background: rgba(75, 120, 255, 0.92);
    box-shadow: 0 0 12px rgba(75, 120, 255, 0.38);
}

.switch-toggle input:not(:checked) + .switch-toggle-shell .switch-toggle-option-left {
    color: var(--text-primary);
    text-shadow: 0 0 10px rgba(139, 139, 245, 0.18);
}

.switch-toggle input:checked + .switch-toggle-shell .switch-toggle-option-right {
    color: var(--text-primary);
    text-shadow: 0 0 10px rgba(75, 120, 255, 0.16);
}

.switch-toggle:hover .switch-toggle-shell {
    border-color: rgba(255, 255, 255, 0.16);
    box-shadow: 0 0 18px rgba(255, 255, 255, 0.03);
}

.switch-toggle-merge input:checked + .switch-toggle-shell .switch-toggle-thumb {
    background: rgba(139, 139, 245, 0.92);
    box-shadow: 0 0 12px rgba(139, 139, 245, 0.35);
}

.switch-toggle-merge input:checked + .switch-toggle-shell .switch-toggle-option-right {
    text-shadow: 0 0 10px rgba(139, 139, 245, 0.18);
}

.switch-toggle-merge input:not(:checked) + .switch-toggle-shell .switch-toggle-option-left {
    text-shadow: 0 0 10px rgba(75, 120, 255, 0.16);
}

.split-chart-label {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.status-hero {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
}

/* Single small loading indicator that lives in the top-right corner of
   the hero panel. The previous design dropped a giant ring on top of the
   rank icon AND the RP value AND the comparison bar, which made the
   loading state look broken. This corner spinner is the only loading
   chrome — everything else is just hidden via .is-hero-loading. */
.hero-corner-spinner {
    position: absolute;
    top: 0;
    right: 0;
    width: 14px;
    height: 14px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.4s cubic-bezier(0.22, 1, 0.36, 1);
    z-index: 4;
}

.status-hero.is-hero-loading .hero-corner-spinner {
    opacity: 1;
}

.hero-corner-spinner-arc {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    border: 1.4px solid rgba(255, 255, 255, 0.16);
    border-top-color: rgba(139, 139, 245, 0.85);
    border-right-color: rgba(139, 139, 245, 0.5);
    animation: heroCornerSpin 0.9s linear infinite;
    box-shadow: 0 0 8px rgba(139, 139, 245, 0.22);
}

@keyframes heroCornerSpin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* Hide every data-bearing element while loading — the corner spinner
   communicates the loading state and nothing else should distract from
   it. The .is-hero-revealing entry choreography fades them back in
   together. .apex-hero-season is hidden too because it sits in the
   top-right corner where the spinner lives, and the two would
   otherwise overlap on top of each other. */
.status-hero.is-hero-loading .apex-hero-top,
.status-hero.is-hero-loading .apex-hero-gap,
.status-hero.is-hero-loading .apex-hero-identity,
.status-hero.is-hero-loading .apex-hero-maprot,
.status-hero.is-hero-loading .apex-hero-season,
.status-hero.is-hero-loading .comparison-vector-head,
.status-hero.is-hero-loading .comparison-vector-anchor-row,
.status-hero.is-hero-loading .comparison-vector-scale,
.status-hero.is-hero-loading .comparison-vector-marker,
.status-hero.is-hero-loading .comparison-vector-marker-tag-row,
.status-hero.is-hero-loading .comparison-vector-player-fill {
    opacity: 0;
    pointer-events: none;
}

/* Entry reveal — runs once when real data arrives. Each element fades
   up with a small stagger so the panel "boots" in sequence. The JS
   removes .is-hero-revealing after ~1.4s so subsequent renders don't
   replay the keyframes. */
.status-hero.is-hero-revealing .apex-hero-primary {
    animation: heroFadeUp 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.06s both;
}
.status-hero.is-hero-revealing .apex-hero-season {
    animation: heroFadeUp 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.14s both;
}
.status-hero.is-hero-revealing .apex-hero-maprot {
    animation: heroFadeUp 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.18s both;
}
.status-hero.is-hero-revealing .comparison-vector-head,
.status-hero.is-hero-revealing .comparison-vector-anchor-row,
.status-hero.is-hero-revealing .comparison-vector-scale {
    animation: heroFadeUp 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.30s both;
}
.status-hero.is-hero-revealing .apex-hero-gap {
    animation: heroFadeUp 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.44s both;
}
.status-hero.is-hero-revealing .apex-hero-identity {
    animation: heroFadeUp 0.45s cubic-bezier(0.22, 1, 0.36, 1) 0.58s both;
}

@keyframes heroFadeUp {
    from { opacity: 0; transform: translateY(8px); filter: blur(3px); }
    to   { opacity: 1; transform: translateY(0);   filter: blur(0); }
}

/* ═════════════════════════════════════════════════════════════════════
   APEX STATUS hero — typography-first redesign
   No glass card, no neon glows, no sub-frames. Hierarchy comes from a
   strict 4-tier font scale (62 / 20 / 14 / 12 / 9 px) and three colour
   tiers (primary 0.88α / secondary 0.64α / min 0.4α). Sections are
   divided by pure hairline rules and whitespace rhythm.
   ═════════════════════════════════════════════════════════════════════ */

/* Apex panel only — squeeze the gap between the "APEX STATUS"
   section label and the hero content beneath it. Scoped via :has()
   so the other panels (KVK, QUEUE) keep their original 20px section
   spacing. */
.panel:has(> .rp-core-grid) > .section-label {
    margin-bottom: 10px;
}

.apex-hero {
    position: relative;
    display: flex;
    flex-direction: column;
    /* Tightening pass: section gap collapsed to 10px now that the
       hairline rules are out of the layout entirely (display:none).
       Each gap is the only separation between primary row / RP
       vector / gap pill / identity row, so they need to be tight
       but still distinct enough to read as separate sections. */
    gap: 10px;
    min-width: 0;
    padding: 0;
    /* Pull the whole hero block up by a few pixels so the badge +
       DIAMOND 1 + 15,554 cluster sits closer to the section label
       above instead of floating in the middle of the panel. */
    margin-top: -4px;
    /* No border, no background, no backdrop filter — the hero sits on
       the parent .panel glass and inherits the same surface. The
       accent palette is unified to the warm neon orange used across
       the rest of the dashboard (weather temp, sys-bar hairlines,
       ECharts axis) rather than the rank-specific cold blue — the
       rank badge SVG itself still carries the game-accurate rank hue
       so DIAMOND reads blue at a glance. */
    --apex-accent: #e8923a;
    --apex-accent-soft: rgba(232, 146, 58, 0.72);
    --apex-glow: rgba(232, 146, 58, 0.34);
    /* Rank accent — set per-rank by renderHero/patchHero via inline style.
       The RP score uses this so the number reads as the rank colour
       (Diamond blue, Master purple, etc.) while the rest of the hero
       stays on the shared warm orange accent. Default falls back to the
       shared accent so a missing rank still looks correct. */
    --apex-rank-accent: var(--apex-accent);
    --apex-rank-glow: var(--apex-glow);
}

/* ── Primary row — two clearly separated zones ─────────────────────
   LEFT  : badge + (TIER stack stacked over PLAYER RP stack)
   RIGHT : SEASON card stacked over the map rotation block
   The row collapses to a single column on narrow viewports so the
   right zone wraps below the left zone instead of crushing the RP
   number against the map names. */

.apex-hero-primary {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    column-gap: 48px;
    /* Row-gap matters when the layout collapses to a single column on
       narrow viewports — kept moderate so the wrapped right zone
       still has separation from the left zone. */
    row-gap: 12px;
    align-items: center;
}

.apex-hero-primary-left {
    display: flex;
    /* Bottom-align the badge with the text stack so the flex layout
       lines its bottom edge with the RP value's bottom. The badge
       then gets pulled visually downward via translateY (see below)
       to land its center exactly on the RP value's optical center.
       Pure align-items: center looked "too high" because the 34px
       RP number carries far more visual mass than the 18px DIAMOND 1
       line, so the geometric centre of the stack sits ~12px above
       the actual visual centre of the text. */
    align-items: flex-end;
    gap: 18px;
    min-width: 0;
}

/* Center the 44px badge on the 34px RP value's vertical centre.
   With align-items: flex-end the badge bottom sits at the stack
   bottom (= RP value bottom). To centre the badge on the RP value,
   shift it down by half the height difference between the badge
   (44px) and the RP value (~34px) → ~5px. translateY keeps the
   adjustment visual-only so the surrounding layout doesn't reflow. */
.apex-hero-badge {
    transform: translateY(5px);
}

.apex-hero-primary-left-stack {
    display: flex;
    flex-direction: column;
    /* TIER row → PLAYER RP row gap, tightened so the rank string and
       big RP number cluster as one block instead of two. */
    gap: 6px;
    min-width: 0;
}

.apex-hero-primary-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    min-width: 0;
    justify-self: end;
}

@media (max-width: 980px) {
    .apex-hero-primary {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 22px;
    }
    .apex-hero-primary-right {
        align-items: flex-start;
        justify-self: stretch;
    }
}

.apex-hero-badge {
    /* Compact 44px square — the badge is now a supporting marker for
       the DIAMOND 1 / 15,554 RP text stack on its right, not the
       hero of the row, so it shrinks from the old 56px footprint.
       position: relative is kept in case the numeral overlay chip
       comes back. */
    position: relative;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
}

.apex-hero-badge-icon {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Subtle drop shadow only — no colored glow bloom. The badge gradient
       itself carries the rank colour. */
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.45));
}

.apex-hero-badge-icon .apex-rank-badge-svg,
.apex-hero-badge-icon .apex-rank-badge-img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: contain;
}

.apex-hero-badge-numeral {
    /* Overlaid on the icon instead of stacked below. Sits centred
       horizontally and nudged into the lower portion of the icon
       (where the base of the diamond art is) so it reads as
       part of the badge. A semi-opaque chip + blurred backdrop gives
       the numeral a bit of contrast against the icon's colour. */
    position: absolute;
    left: 50%;
    bottom: 4px;
    transform: translateX(-50%);
    padding: 1px 5px;
    font-family: var(--display-font);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.12em;
    line-height: 1;
    color: #fff;
    text-shadow:
        0 0 3px rgba(0, 0, 0, 0.9),
        0 0 6px var(--apex-rank-glow);
    font-variant-numeric: tabular-nums;
    background: rgba(0, 0, 0, 0.55);
    border: 0.5px solid color-mix(in srgb, var(--apex-rank-accent) 40%, transparent);
    border-radius: 3px;
    pointer-events: none;
}

.apex-hero-rank {
    font-family: var(--display-font);
    /* Was 22px when it was a headline by itself; trimmed to 18px now
       that it sits stacked above the RP number in the new compact
       two-row layout. Still tier-1 / weight 700 / near-white so the
       rank name stays prominent within the stack. */
    font-size: 18px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    line-height: 1;
    white-space: nowrap;
    color: var(--text-tier-1);
}

.apex-hero-rp-value {
    font-family: var(--numeric-font);
    /* Was clamp(36, 4.2vw, 54) — that was sized for a number sitting
       alone with a "PLAYER RP" caption above it. Now it sits in a
       two-row stack alongside the rank name (and gains a "RP" unit
       suffix via ::after below), so it shrinks to clamp(26, 3vw, 34)
       which still reads as the headline of the left zone. */
    font-size: clamp(26px, 3vw, 34px);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    /* Rank-tinted fluorescent — near-white core with a soft rank-hue
       halo. */
    color: color-mix(in srgb, var(--apex-rank-accent) 28%, #ffffff);
    text-shadow: 0 0 12px var(--apex-rank-glow);
    display: inline-flex;
    align-items: baseline;
}

/* "积分" 单位后缀 —— 通过 ::after 渲染而不是内联 span，这样 JS 滚
   动器（animateNumberTo）可以继续把 .apex-hero-rp-value 当成纯数字
   文本节点对待。单位继承父元素的色调，但缩到约 52% 尺寸 + tier-3
   alpha，让数字本身保持焦点。 */
.apex-hero-rp-value::after {
    content: " 积分";
    font-size: 0.5em;
    font-weight: 600;
    letter-spacing: 0.04em;
    margin-left: 0.2em;
    color: color-mix(in srgb, var(--apex-rank-accent) 22%, rgba(255, 232, 195, 0.62));
    text-shadow: none;
}

/* Season card — single inline row at the top of the right zone.
   Layout: 赛季 · S28 · [wireframe season clock]
   The label is tier-3, the season name is tier-1 (it's a primary
   identifier), and the wireframe clock chip replaces what used to
   be a "剩 25天 7小时" text countdown. */
.apex-hero-season {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    white-space: nowrap;
}

/* Wireframe season-progress clock canvas host — sits inline at the
   end of the season row. Same diameter as the chrono clock so the
   two read as siblings. */
.apex-hero-season-clock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    margin-left: 2px;
    filter: drop-shadow(0 0 6px rgba(255, 200, 120, 0.18));
}

.apex-hero-season-label {
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--text-tier-3);
    line-height: 1;
}

.apex-hero-season-name {
    font-family: var(--display-font);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-tier-1);
    line-height: 1;
    /* A subtle warm dot before the countdown reinforces the chip
       feel without needing an actual border. */
    padding-right: 9px;
    border-right: 0.5px solid rgba(255, 255, 255, 0.16);
}

.apex-hero-season-countdown {
    font-family: var(--numeric-font);
    font-size: 9px;
    font-weight: 500;
    letter-spacing: 0.08em;
    color: var(--text-tier-3);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.apex-hero-maprot {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 5px;
    min-width: 0;
    text-align: right;
}

/* Two explicit rows: one for "CURRENT" and one for "NEXT". Each row
   is a center-aligned (formerly baseline) inline group so label + map
   icon + map name + optional timer sit on one line. Center alignment
   replaces baseline so the wireframe canvas chips line up vertically
   with the text instead of dropping below the baseline. */
.apex-hero-maprot-row {
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
    min-width: 0;
}

/* Wireframe map icon canvas host — inline chip between the row label
   and the map name. CURRENT row gets a slightly bigger icon than the
   NEXT row to reinforce the existing visual hierarchy. */
.apex-hero-maprot-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    filter: drop-shadow(0 0 5px rgba(255, 220, 156, 0.18));
}

.apex-hero-maprot-icon-next {
    width: 16px;
    height: 16px;
    opacity: 0.78;
}

.apex-hero-maprot-label {
    flex-shrink: 0;
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    /* Tier 3 — the "CURRENT" / "NEXT" caption recedes so the map
       name next to it is the element the eye lands on. */
    color: var(--text-tier-3);
    line-height: 1;
}

.apex-hero-maprot-now {
    font-family: var(--display-font);
    /* Bumped from 14 → 19px so the current map name towers over the
       NEXT row and reads as the headline of the rotation block. */
    font-size: 19px;
    font-weight: 700;
    /* Removed uppercase + tight letter-spacing — the tightening was
       calibrated for English caps. CJK glyphs need a touch more
       horizontal breathing room and don't take uppercase. */
    letter-spacing: 0.04em;
    text-transform: none;
    /* Tier 1 + warm accent glow so the current map gets a subtle
       highlight against the dark panel — the eye lands on it first. */
    color: var(--text-tier-1);
    text-shadow:
        0 0 6px rgba(255, 170, 80, 0.32),
        0 0 14px rgba(232, 146, 58, 0.18);
    line-height: 1.05;
}

.apex-hero-maprot-next-name {
    font-family: var(--display-font);
    /* Was 11px — dropped a notch so the contrast vs the 19px CURRENT
       row reads as "headline + footnote" instead of "two map names". */
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: none;
    /* Tier 2 — next map is informational context, not the focal point. */
    color: var(--text-tier-2);
    line-height: 1;
}

/* ── Nixie tube countdown ───────────────────────────────────────────
   Each digit sits inside a glass-tube capsule with a warm amber glow.
   A dim "8" cathode ghost shows behind the active digit so the tubes
   look mechanically real even when displaying 0/1. */

.nixie-tube {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    font-family: var(--numeric-font);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    line-height: 1;
}

.nixie-digit {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 20px;
    font-size: 13px;
    font-weight: 600;
    color: var(--orange);
    text-shadow:
        0 0 4px  rgba(232, 146, 58, 0.8),
        0 0 12px rgba(232, 146, 58, 0.4),
        0 0 24px rgba(232, 146, 58, 0.15);
    background:
        radial-gradient(ellipse at 50% 40%,
            rgba(232, 146, 58, 0.06) 0%,
            transparent 70%),
        linear-gradient(180deg,
            rgba(26, 25, 40, 0.8) 0%,
            rgba(19, 18, 29, 0.9) 100%);
    border: 1px solid rgba(232, 146, 58, 0.12);
    border-radius: 4px;
    box-shadow:
        inset 0 0 8px rgba(232, 146, 58, 0.06),
        0 0 3px rgba(0, 0, 0, 0.4);
}

.nixie-digit::before {
    content: "8";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: inherit;
    font-weight: inherit;
    color: rgba(232, 146, 58, 0.05);
    pointer-events: none;
    z-index: -1;
}

.nixie-sep {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 6px;
    font-size: 13px;
    font-weight: 600;
    color: rgba(232, 146, 58, 0.45);
    text-shadow: 0 0 6px rgba(232, 146, 58, 0.25);
    animation: nixieBlink 1.6s ease-in-out infinite;
}

@keyframes nixieBlink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}

/* ── Hairline rule between vertical sections ──────────────────────
   Hidden in the compaction pass: each rule used to consume two
   .apex-hero gap slots (one above, one below) for almost no visual
.apex-hero-rule { display: none; }

/* ── Cutoff gap callout (pure typography, no frame) ──────────────── */

.apex-hero-gap {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 0;
    border: 0;
    background: none;
    box-shadow: none;
    font-family: var(--system-font);
    line-height: 1;
}

.apex-hero-gap-arrow {
    font-size: 13px;
    font-weight: 700;
    color: var(--apex-accent);
    text-shadow: 0 0 8px var(--apex-glow);
    align-self: center;
    margin-right: 2px;
}

.apex-hero-gap-value {
    font-family: var(--numeric-font);
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--apex-accent);
    text-shadow: 0 0 10px var(--apex-glow);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.apex-hero-gap-unit {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.14em;
    color: rgba(255, 255, 255, 0.58);
    text-transform: uppercase;
    align-self: center;
    padding-bottom: 1px;
}

.apex-hero-gap-divider {
    display: inline-block;
    width: 1px;
    height: 14px;
    background: rgba(255, 255, 255, 0.14);
    margin: 0 6px;
    align-self: center;
}

.apex-hero-gap-label {
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.22em;
    color: rgba(255, 255, 255, 0.62);
    text-transform: uppercase;
    white-space: nowrap;
    align-self: center;
}

/* ── Bottom identity row ─────────────────────────────────────────── */

.apex-hero-identity {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 500;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.5);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.apex-hero-identity-name {
    color: rgba(255, 240, 220, 0.82);
    font-weight: 600;
    letter-spacing: 0.16em;
}

.apex-hero-identity-sep {
    color: rgba(255, 255, 255, 0.22);
}

/* UID chip inside the bottom identity row. The label makes it
   obvious this is a player identifier, not some random 10-digit
   value floating in the corner. Label is tier-3, value is slightly
   brighter so the number is still scannable. */
.apex-hero-identity-uid {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
}

.apex-hero-identity-uid-label {
    color: var(--text-tier-3);
    font-weight: 500;
    letter-spacing: 0.2em;
}

.apex-hero-identity-uid-value {
    color: rgba(255, 232, 195, 0.62);
    font-family: var(--numeric-font);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    letter-spacing: 0.04em;
}

.hero-player-tag {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.hero-state-block {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 16px 22px;
}

.hero-rank-block {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    min-width: 82px;
}

.hero-rp-stack {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-width: 0;
}

.hero-rank-icon {
    width: auto;
    height: 34px;
    object-fit: contain;
    filter: drop-shadow(0 0 12px rgba(75, 120, 255, 0.3));
}

.hero-rp-value {
    font-family: var(--numeric-font);
    font-size: clamp(46px, 5.6vw, 84px);
    font-weight: 500;
    line-height: 0.95;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.hero-rank {
    color: var(--text-primary);
    font-family: var(--display-font);
    font-size: clamp(13px, 1.5vw, 18px);
    font-weight: 500;
    letter-spacing: 0.01em;
    text-align: center;
}

.hero-meta {
    color: rgba(224, 224, 232, 0.62);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
}

.comparison-vector {
    display: flex;
    flex-direction: column;
    /* Tightened from 10px → 6px so the head label, track, anchor row,
       and scale row sit closer together. The bar still has clear
       internal separation but the overall height drops by ~12-16px. */
    gap: 6px;
    min-width: 0;
}

.comparison-vector-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
}

.comparison-vector-label {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.comparison-vector-state {
    color: var(--text-secondary);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
}

.comparison-vector-track {
    /* Protected layout token: keep anchor dots permanently below the bar. */
    --vector-bar-height: 8px;
    --vector-dot-gap: 10px;
    --vector-dot-size: 8px;
    position: relative;
    padding: 18px 0 calc(var(--vector-dot-gap) + var(--vector-dot-size) + 4px);
}

.comparison-vector-marker-tag-row {
    position: absolute;
    inset: 0 0 auto 0;
    height: 12px;
    pointer-events: none;
}

.comparison-vector-marker-tag {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    color: var(--text-min);
    font-family: var(--text-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    line-height: 1;
    white-space: nowrap;
}

.comparison-vector-marker-tag.player {
    color: var(--player-label);
}

.comparison-vector-marker-tag.threshold {
    left: 100%;
    color: var(--threshold-label);
}

.comparison-vector-marker-tag.tight.player {
    transform: translateX(-100%);
}

.comparison-vector-marker-tag.tight.threshold {
    transform: translateX(-100%);
}

.comparison-vector-anchor {
    position: absolute;
    top: 0;
    max-width: min(132px, 32vw);
    color: var(--text-min);
    /* Share Tech Mono = HUD-style fixed-width numerics. Falls back
       to the dashboard's numeric stack if the web font fails. */
    font-family: "Share Tech Mono", var(--numeric-font);
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.06em;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    /* Number-jump pulse — runs once per render. patchHero rebuilds
       the vector's outerHTML so the element is fresh each sync tick
       and the animation auto-restarts. We animate opacity + a
       coloured text-shadow rather than transform so the anchor
       shift (translateX carrying --*-anchor-shift) stays intact. */
    animation: vectorAnchorPulse 520ms cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes vectorAnchorPulse {
    0%   { opacity: 0.35; text-shadow: 0 0 0 currentColor; filter: brightness(1.6); }
    45%  { opacity: 1;    text-shadow: 0 0 8px currentColor; filter: brightness(1.4); }
    100% { opacity: 1;    text-shadow: 0 0 0 currentColor; filter: brightness(1); }
}

.comparison-vector-anchor.player {
    transform: translateX(var(--player-anchor-shift));
    color: var(--player-label);
}

.comparison-vector-anchor.threshold {
    left: 100%;
    transform: translateX(var(--threshold-anchor-shift));
    text-align: center;
    color: var(--threshold-label);
}

.comparison-vector-anchor.tight.player {
    transform: translateX(-100%);
}

.comparison-vector-anchor.tight.threshold {
    top: 14px;
    transform: translateX(-100%);
}

.comparison-vector-anchor-row {
    position: relative;
    /* Was 24px — anchor row only needs ~16px to hold the dots and
       their RP value tags without overlap, the rest was slack. */
    min-height: 16px;
}

/* ── Comparison vector bar — flat twin-colour progress ─────────────
   No glass, no gloss, no volumetric depth. A flat neutral track
   holds two solid fills (blue player, red threshold) whose colours
   come straight from the chart palette (--player-accent /
   --threshold-accent) so the bar reads as the same telemetry
   element as the ECharts line. The particles, shimmer sweep, and
   neon halos are preserved — only the plasticky 3D treatment is
   stripped out. */
.comparison-vector-segments {
    position: relative;
    width: 100%;
    height: 10px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    box-sizing: border-box;
    overflow: hidden;
}

/* All CSS meteor / scan sweeps on the RP bar have been retired. The
   canvas-engine's rp-bar-meteor module now paints a single SOFT
   horizontal scan band onto its own overlay canvas — no CSS
   pseudo-element flows remain in the non-disabled state. The
   .is-disabled loading flow on ::before (orange sweep while the
   hero panel is booting) stays intact and is untouched. */

/* Keep the loading-state flow animation clipped to the track — it
   sweeps past the edges and would otherwise leak into the layout. */
.comparison-vector.is-disabled .comparison-vector-segments {
    overflow: hidden;
}


/* Loading (is-disabled = no RP/cutoff data yet): neutralize the red
   track to a dim warm tone and flow a bright horizontal light across it
   continuously. This replaces the old spinner circle — "光条上绕着流动的
   光线". Different from the loaded state below, which has its own
   subtle shine only over the filled portion. */
.comparison-vector.is-disabled .comparison-vector-segments {
    background: rgba(255, 255, 255, 0.08);
    box-shadow:
        inset 0 0 0 1px rgba(255, 220, 180, 0.08),
        inset 0 0 10px rgba(139, 139, 245, 0.06);
}

.comparison-vector.is-disabled .comparison-vector-segments::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 40%;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 182, 96, 0.18) 20%,
        rgba(255, 210, 140, 0.88) 50%,
        rgba(255, 182, 96, 0.18) 80%,
        transparent 100%);
    box-shadow: 0 0 14px rgba(139, 139, 245, 0.42);
    transform: translateX(-100%);
    animation: comparisonLoadingFlow 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
    pointer-events: none;
    z-index: 3;
}

@keyframes comparisonLoadingFlow {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(260%); }
}

/* ── Player fill (chart blue) ──────────────────────────────────────
   Flat solid accent straight from the chart palette. No vertical
   gradient, no inset rim, no inset floor — the only depth cue is
   the outer neon halo which preserves the "glowing line" reading
   without the plasticky bevel. */
.comparison-vector-player-fill {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    /* Full capsule — both ends rounded. */
    border-radius: 999px;
    /* Pure hex, no alpha, no gradient. */
    background: var(--orange);
    /* Above the red ground (z:1). */
    z-index: 2;
    -webkit-mask-image: none;
    mask-image: none;
}

.comparison-vector-player-fill.disabled { display: none; }

/* Repurposed as the red "remaining" ground layer beneath the blue
   fill. The element is still created and positioned by ui.js, so
   every layout-affecting property is forced with !important to
   override any inline styles the JS writes to it. */
.comparison-vector-threshold-fill {
    display: block !important;
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    right: auto !important;
    bottom: auto !important;
    width: 100% !important;
    height: 100% !important;
    background: #8b8bf5 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-mask-image: none !important;
    mask-image: none !important;
    z-index: 1;
    pointer-events: none;
}

.comparison-vector-blend { display: none !important; }

.comparison-vector-marker {
    position: absolute;
    top: calc(var(--vector-bar-height) + var(--vector-dot-gap));
    width: 2px;
    height: 16px;
    transform: translateX(-50%);
}

.comparison-vector-marker::before {
    content: "";
}

.comparison-vector-marker::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: calc(var(--vector-dot-size) * -1);
    width: var(--vector-dot-size);
    height: var(--vector-dot-size);
    border-radius: 50%;
    transform: translateX(-50%);
}

.comparison-vector-marker.player {
    background: transparent;
    box-shadow: none;
}

.comparison-vector-marker.player::before {
    content: none;
}

.comparison-vector-marker.player::after {
    background: var(--player-accent);
    box-shadow: 0 0 10px var(--player-glow);
    animation: markerPulsePlayer 2.6s ease-in-out infinite;
}

.comparison-vector-marker.threshold {
    /* No stem, no diamond — same treatment as the player marker:
       transparent container with a single small red dot as ::after. */
    background: transparent;
    box-shadow: none;
}

.comparison-vector-marker.threshold::before {
    content: none;
}

.comparison-vector-marker.threshold::after {
    background: var(--threshold-accent);
    border: none;
    box-shadow: 0 0 10px var(--threshold-glow);
    animation: markerPulseThreshold 2.9s ease-in-out infinite;
}

/* ── Traveling-dot entry animation ──────────────────────────────────
   When the hero panel finishes loading, JS adds .is-traveling on the
   .comparison-vector and resets each marker's `left` to 0%, then on
   the next frame sets it to the target position. The CSS transition
   below carries the marker (and the player fill width) from the start
   of the bar to its destination over ~1.2s. The .arrived class then
   fires a one-shot pulse keyframe to signal "locked in", and the JS
   later strips .is-traveling so subsequent updates can hop the marker
   freely without re-running the entry. */
.comparison-vector.is-traveling .comparison-vector-marker.player,
.comparison-vector.is-traveling .comparison-vector-marker.threshold,
.comparison-vector.is-traveling .comparison-vector-marker-tag,
.comparison-vector.is-traveling .comparison-vector-anchor {
    transition: left 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.comparison-vector.is-traveling .comparison-vector-player-fill {
    transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.comparison-vector.is-traveling .comparison-vector-threshold-fill,
.comparison-vector.is-traveling .comparison-vector-blend {
    transition: left 1.2s cubic-bezier(0.22, 1, 0.36, 1),
                width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}

/* While traveling, the marker shows a small glowing orb instead of the
   thin vertical line. After arrival the standard ::after dot returns. */
.comparison-vector.is-traveling .comparison-vector-marker.player::after,
.comparison-vector.is-traveling .comparison-vector-marker.threshold::after {
    animation: none;
    transform: translateX(-50%) scale(1.2);
    box-shadow:
        0 0 12px var(--player-glow, rgba(75, 120, 255, 0.6)),
        0 0 24px var(--player-glow, rgba(75, 120, 255, 0.3));
}

.comparison-vector.is-traveling .comparison-vector-marker.threshold::after {
    box-shadow:
        0 0 12px var(--threshold-glow, rgba(255, 59, 59, 0.6)),
        0 0 24px var(--threshold-glow, rgba(255, 59, 59, 0.3));
}

/* Arrival pulse — a radial ring expanding outward from the marker
   position the moment the dot stops at its target. The ::before
   pseudo-element is normally `content: none`, but during arrival we
   override that and run the keyframe. */
.comparison-vector-marker.player.arrived::before,
.comparison-vector-marker.threshold.arrived::before {
    content: "";
    position: absolute;
    left: 50%;
    bottom: calc(var(--vector-dot-size) * -1);
    width: var(--vector-dot-size);
    height: var(--vector-dot-size);
    border-radius: 50%;
    transform: translate(-50%, 0);
    pointer-events: none;
    animation: markerArrivePulse 0.85s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.comparison-vector-marker.player.arrived::before {
    box-shadow:
        0 0 0 0 var(--player-accent, rgba(75, 120, 255, 0.9)),
        0 0 14px 0 var(--player-glow, rgba(75, 120, 255, 0.6));
}

.comparison-vector-marker.threshold.arrived::before {
    box-shadow:
        0 0 0 0 var(--threshold-accent, rgba(255, 59, 59, 0.9)),
        0 0 14px 0 var(--threshold-glow, rgba(255, 59, 59, 0.6));
}

@keyframes markerArrivePulse {
    0% {
        opacity: 0;
        transform: translate(-50%, 0) scale(0.4);
    }
    20% {
        opacity: 1;
    }
    100% {
        opacity: 0;
        transform: translate(-50%, 0) scale(4.2);
        box-shadow:
            0 0 0 1px transparent,
            0 0 24px 6px transparent;
    }
}

/* The .arrived ::after dot gets a brief brightness kick on landing. */
.comparison-vector-marker.player.arrived::after,
.comparison-vector-marker.threshold.arrived::after {
    animation: markerArriveDotFlash 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes markerArriveDotFlash {
    0%   { transform: translateX(-50%) scale(1.6); filter: brightness(1.8); }
    60%  { transform: translateX(-50%) scale(1.05); filter: brightness(1.2); }
    100% { transform: translateX(-50%) scale(1);    filter: brightness(1); }
}

.comparison-vector-scale {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    color: var(--text-min);
    font-family: var(--numeric-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.comparison-vector-mapline {
    min-height: 34px;
}

.comparison-vector-rotation {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.comparison-vector-rotation-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    min-width: 0;
}

.comparison-vector-rotation.is-unavailable .comparison-vector-rotation-line {
    color: var(--text-min);
}

.comparison-vector-rotation-label,
.comparison-vector-rotation-next {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.comparison-vector-rotation-current,
.comparison-vector-rotation-timer {
    color: rgba(224, 224, 232, 0.74);
    font-family: var(--numeric-font);
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.comparison-vector-rotation-timer {
    flex-shrink: 0;
}

.comparison-vector-rotation-current,
.comparison-vector-rotation-next {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.comparison-vector-rotation-subline {
    align-items: center;
}

.comparison-vector-unknown {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
}

.comparison-vector.is-disabled .comparison-vector-marker.player {
    display: none;
}

.status-hero.hero-entrance .hero-player-tag,
.status-hero.hero-entrance .hero-rank-block,
.status-hero.hero-entrance .hero-rp-stack,
.status-hero.hero-entrance .hero-meta,
.status-hero.hero-entrance .comparison-vector-head,
.status-hero.hero-entrance .comparison-vector-scale,
.status-hero.hero-entrance .cv-map-micro,
.status-hero.hero-entrance .comparison-vector-unknown {
    opacity: 0;
    transform: translateY(6px);
    transition:
        opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

.status-hero.hero-entrance .comparison-vector-marker-tag,
.status-hero.hero-entrance .comparison-vector-anchor {
    opacity: 0;
    transition: opacity 0.38s cubic-bezier(0.22, 1, 0.36, 1);
}

.status-hero.hero-entrance .comparison-vector-segments,
.status-hero.hero-entrance .comparison-vector-player-fill {
    opacity: 0;
    transform: scaleX(0);
    transform-origin: left center;
    transition:
        opacity 0.58s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.72s cubic-bezier(0.22, 1, 0.36, 1);
}

.status-hero.hero-entrance .comparison-vector-marker {
    opacity: 0;
    transform: translateX(-50%) scale(0.66);
    transition:
        opacity 0.34s cubic-bezier(0.22, 1, 0.36, 1),
        transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}

.status-hero.hero-entrance.hero-entrance-active .hero-player-tag {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.04s;
}

.status-hero.hero-entrance.hero-entrance-active .hero-rank-block {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.16s;
}

.status-hero.hero-entrance.hero-entrance-active .hero-rp-stack {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.22s;
}

.status-hero.hero-entrance.hero-entrance-active .hero-meta {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.34s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-head {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.44s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-marker-tag {
    opacity: 1;
    transition-delay: 0.52s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-segments {
    opacity: 1;
    transform: scaleX(1);
    transition-delay: 0.58s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-player-fill {
    opacity: 1;
    transform: scaleX(1);
    transition-delay: 0.68s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-marker {
    opacity: 1;
    transform: translateX(-50%) scale(1);
    transition-delay: 0.88s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-anchor {
    opacity: 1;
    transition-delay: 0.98s;
}

.status-hero.hero-entrance.hero-entrance-active .comparison-vector-scale,
.status-hero.hero-entrance.hero-entrance-active .cv-map-micro,
.status-hero.hero-entrance.hero-entrance-active .comparison-vector-unknown {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 1.04s;
}

@keyframes playerPulse {
    0%,
    100% {
        transform: translateX(-50%) scale(1);
        box-shadow: 0 0 10px var(--player-glow), 0 0 20px var(--player-glow);
    }
    50% {
        transform: translateX(-50%) scale(1.38);
        box-shadow: 0 0 18px var(--player-glow), 0 0 34px var(--player-glow);
    }
}

@keyframes markerPulsePlayer {
    0%,
    100% {
        transform: translateX(-50%) scale(1);
        opacity: 0.85;
        box-shadow: 0 0 8px var(--player-glow);
    }
    50% {
        transform: translateX(-50%) scale(1.28);
        opacity: 1;
        box-shadow: 0 0 14px var(--player-glow), 0 0 26px var(--player-glow);
    }
}

@keyframes markerPulseThreshold {
    0%,
    100% {
        transform: translateX(-50%) scale(1);
        opacity: 0.88;
        box-shadow: 0 0 9px var(--threshold-glow);
    }
    50% {
        transform: translateX(-50%) scale(1.24);
        opacity: 1;
        box-shadow: 0 0 16px var(--threshold-glow), 0 0 28px var(--threshold-glow);
    }
}

.overview-stat {
    padding: 14px 14px 12px;
    border: 0.5px solid rgba(255, 211, 154, 0.07);
    background:
        radial-gradient(circle at 12% 0%, rgba(255, 187, 112, 0.04), transparent 36%),
        rgba(22, 13, 10, 0.34);
}

.overview-stat-label {
    margin-bottom: 7px;
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.overview-stat-value {
    color: var(--text-primary);
    font-family: var(--numeric-font);
    font-size: 18px;
    font-weight: 500;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.intel-block {
    padding-bottom: 16px;
    border-bottom: 0.5px solid var(--divider);
}

.intel-block:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.intel-label {
    margin-bottom: 14px;
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.intel-empty,
.module-05-copy {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.rotation-grid {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.rotation-block {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.rotation-mode,
.feed-meta {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.rotation-current,
.rotation-next,
.server-line,
.feed-item {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.rotation-now-label {
    color: rgba(120, 184, 64, 0.76);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    text-shadow: 0 0 8px rgba(120, 184, 64, 0.28);
}

.rotation-map,
.rotation-next-map,
.server-label,
.feed-title {
    color: var(--text-primary);
    font-family: var(--system-font);
    font-size: 12px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.rotation-timer,
.server-value {
    margin-left: auto;
    color: var(--text-min);
    font-family: var(--numeric-font);
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

.server-status-list,
.feed-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.server-line {
    align-items: center;
}

.server-badge {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}

.server-badge.ok {
    background: var(--ok);
    box-shadow: 0 0 7px rgba(120, 184, 64, 0.48), 0 0 14px rgba(120, 184, 64, 0.18);
    animation: dotBreath 3.2s ease-in-out infinite;
}

.server-badge.warn {
    background: var(--warn);
    box-shadow: 0 0 6px rgba(176, 140, 56, 0.4);
    animation: dotBreath 4.5s ease-in-out infinite;
}

.server-badge.fail {
    background: var(--fail);
    box-shadow: 0 0 8px rgba(122, 40, 40, 0.55);
}

.feed-item {
    align-items: flex-start;
    justify-content: space-between;
    gap: 14px;
}

.feed-copy {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.feed-title {
    font-size: 11px;
    line-height: 1.5;
}

.feed-view-btn {
    flex-shrink: 0;
}

.module-05-shell {
    min-height: 96px;
}

/* ── KVK Analytics Dashboard ────────────────────────────────────── */

.kvk-analytics {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-top: 8px;
}

.kvk-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    padding: 10px 12px;
    border: 0.5px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--surface-radius-sm);
    background: rgba(19, 18, 29, 0.5);
}

.kvk-scenario-multiselect {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
}

.kvk-scenario-empty {
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: 9px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 4px 6px;
}

.kvk-scenario-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 9px;
    border: 0.5px solid rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.02);
    color: var(--text-tier-2);
    font-family: var(--system-font);
    font-size: 9px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    user-select: none;
    transition: border-color 0.18s, background 0.18s, color 0.18s;
}

.kvk-scenario-chip:hover {
    border-color: rgba(232, 146, 58, 0.24);
    color: var(--text-tier-1);
}

.kvk-scenario-chip.is-on {
    border-color: rgba(232, 146, 58, 0.45);
    background: rgba(232, 146, 58, 0.15);
    color: #f0c98a;
}

.kvk-scenario-chip input[type="checkbox"] {
    display: none;
}

.kvk-scenario-chip-label {
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kvk-scenario-chip-count {
    color: var(--text-tier-3);
    font-family: var(--numeric-font);
    font-size: 8px;
}

.kvk-scenario-chip.is-on .kvk-scenario-chip-count {
    color: rgba(232, 146, 58, 0.7);
}

/* KVK status pill (preserved for toolbar) */

.kvk-status-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 8px;
    border: 0.5px solid rgba(255, 255, 255, 0.06);
    color: var(--text-secondary);
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.kvk-status-pill.is-busy {
    color: rgba(214, 173, 80, 0.84);
    border-color: rgba(214, 173, 80, 0.24);
}

/* KVK button (preserved for toolbar IMPORT/RESCAN) */

.kvk-btn {
    padding: 7px 10px;
    border: 0.5px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.02);
    color: var(--text-secondary);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    transition:
        border-color 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        color 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        background 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s;
}

.kvk-btn:hover:not(:disabled) {
    border-color: rgba(232, 146, 58, 0.32);
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.032);
}

.kvk-btn:disabled {
    cursor: default;
    opacity: 0.55;
}

/* Scenario breakdown table */

.kvk-breakdown-table {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.kvk-breakdown-row {
    display: grid;
    grid-template-columns: 1.7fr 0.5fr 0.7fr 0.7fr 0.7fr 0.8fr;
    gap: 10px;
    align-items: center;
    padding: 8px 6px;
    font-family: var(--system-font);
    font-size: 10px;
    color: var(--text-tier-2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

.kvk-breakdown-row > span {
    font-family: var(--numeric-font);
    text-align: right;
}

.kvk-breakdown-row > span:first-child {
    text-align: left;
}

.kvk-breakdown-row.kvk-breakdown-head {
    color: var(--text-tier-3);
    font-size: 8.5px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border-bottom-color: rgba(255, 255, 255, 0.08);
}

.kvk-breakdown-row.kvk-breakdown-head > span {
    cursor: pointer;
    font-family: var(--system-font);
    user-select: none;
}

.kvk-breakdown-row.kvk-breakdown-head > span:hover {
    color: var(--text-tier-1);
}

.kvk-breakdown-name {
    color: var(--text-tier-1);
    font-family: var(--text-font);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kvk-trend-cell.is-up {
    color: var(--ok, #4ade80);
}

.kvk-trend-cell.is-flat {
    color: var(--orange, #e8923a);
}

.kvk-trend-cell.is-down {
    color: var(--fail, #f87171);
}

/* KVK session row warmup borders */

.analytics-session-row.kvk-session-row {
    grid-template-columns: 0.9fr 0.5fr 0.5fr 0.5fr 0.7fr 0.7fr;
    border-left: 2px solid transparent;
    padding-left: 6px;
}

.analytics-session-row.kvk-session-row.is-warmup-positive {
    border-left-color: var(--ok, #4ade80);
}

.analytics-session-row.kvk-session-row.is-warmup-negative {
    border-left-color: var(--fail, #f87171);
}

/* Heatmap card override (calendar series sizes itself smaller than line/bar charts) */

.kvk-heatmap-card .analytics-chart {
    height: 200px;
}

/* Correlation card meta inline */

.kvk-correlation-meta {
    color: var(--text-tier-3);
    font-family: var(--numeric-font);
    font-size: 9px;
    text-transform: none;
    letter-spacing: 0.02em;
    margin-left: 6px;
}

/* Mobile collapse */

@media (max-width: 880px) {
    .kvk-toolbar {
        flex-direction: column;
        align-items: stretch;
    }
    .kvk-scenario-multiselect {
        order: 3;
    }
    .kvk-breakdown-row {
        grid-template-columns: 1.4fr 0.5fr 0.6fr 0.6fr 0.6fr 0.7fr;
        font-size: 9px;
    }
}

.news-link {
    color: rgba(139, 139, 245, 0.84);
    text-decoration: none;
}


@media (max-width: 760px) {
    .hero-rp-value {
        font-size: clamp(38px, 12vw, 64px);
    }

    .hero-state-block {
        align-items: flex-start;
        gap: 14px 18px;
    }

    .hero-rank-block {
        align-items: flex-start;
        min-width: 0;
    }

    .hero-rank {
        text-align: left;
    }

    .comparison-vector-head,
    .rotation-current,
    .rotation-next,
    .feed-item,
    .kvk-topbar-actions,
    .kvk-topbar-status,
    .kvk-meta-row,
    .kvk-control-meta-row,
    .kvk-run-row,
    .kvk-panel-head,
    .kvk-recent-row {
        flex-direction: column;
        align-items: flex-start;
    }

    .comparison-vector-anchor {
        max-width: min(108px, 42vw);
        font-size: 8px;
    }

    .comparison-vector-marker-tag.threshold {
        transform: translateX(-100%);
    }

    .comparison-vector-anchor-row {
        min-height: 34px;
    }

    .comparison-vector-rotation-line {
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
    }

    .rotation-timer,
    .server-value {
        margin-left: 0;
    }

    .kvk-btn,
    .kvk-scenario-select {
        width: 100%;
    }

    .kvk-topbar-actions {
        align-items: stretch;
    }

    .kvk-recent-value-block {
        align-items: flex-start;
    }

    .kvk-summary-row {
        grid-template-columns: 1fr;
        gap: 4px;
    }
}

@media (max-width: 980px) {
    .kvk-distribution-body {
        grid-template-columns: 1fr;
        gap: 14px;
        align-items: stretch;
    }

    .kvk-distribution-chart-wrap {
        min-height: 200px;
    }

    .kvk-distribution-chart {
        height: 200px;
    }
}

.comparison-vector-marker::before,
.module-05-copy,
.rotation-map,
.rotation-next-map,
.server-label,
.server-value,
.feed-title,
.feed-meta {
    font-family: var(--system-font);
}

.comparison-vector-marker::before,
.module-05-copy,
.server-label,
.server-value,
.feed-title,
.feed-meta {
    font-weight: 400;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

/* ── Chart view selector (MY_RP // CUTOFF // MERGED) ───────────────────── */

.view-selector {
    display: inline-flex;
    align-items: center;
    border: 0.5px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.018);
}

.view-btn {
    padding: 6px 10px;
    font-family: var(--system-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-min);
    background: transparent;
    border: none;
    cursor: pointer;
    transition: color 0.2s ease, background 0.2s ease;
}

.view-btn:hover {
    color: var(--text-secondary);
}

.view-btn.is-active {
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.042);
}

.view-sep {
    font-family: var(--system-font);
    font-size: 8px;
    color: var(--text-min);
    pointer-events: none;
    user-select: none;
}

/* ── Atmospheric labels & comparison bar enhancements ───────────────────── */

.comparison-vector-atmo {
    font-family: var(--display-font);
    font-style: italic;
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.1em;
    color: var(--text-min);
    text-transform: uppercase;
}

/* Map micro-text beneath the progress bar */

.cv-map-micro {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    padding-top: 7px;
    border-top: 0.5px solid rgba(255, 255, 255, 0.04);
    flex-wrap: nowrap;
    overflow: hidden;
}

.cv-map-mode {
    font-family: var(--system-font);
    font-size: 7px;
    font-weight: 400;
    letter-spacing: 0.12em;
    color: var(--text-min);
    text-transform: uppercase;
    flex-shrink: 0;
}

.cv-map-now {
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cv-map-arrow {
    color: var(--text-min);
    font-size: 9px;
    flex-shrink: 0;
}

.cv-map-next {
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--text-min);
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cv-map-timer {
    margin-left: auto;
    font-family: var(--numeric-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--text-min);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    flex-shrink: 0;
}

/* Hover tooltips on comparison vector anchors */

.cv-tooltip {
    position: fixed;
    z-index: 200;
    transform: translate(-50%, calc(-100% - 8px));
    padding: 3px 8px;
    border: 0.5px solid;
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
    pointer-events: none;
    background: rgba(6, 8, 16, 0.97);
}

.cv-tooltip.cv-tooltip-player {
    color: rgba(75, 120, 255, 0.92);
    border-color: rgba(75, 120, 255, 0.28);
}

.cv-tooltip.cv-tooltip-threshold {
    color: rgba(255, 59, 59, 0.9);
    border-color: rgba(255, 59, 59, 0.26);
}

/* ── Weather panel ──────────────────────────────────────────────────────── */

/* ─────────────────────────────────────────────────────────────────────────
   CHRONO + WEATHER + ATMOS — vertical sections, fills the panel column
   Three stacked sections (CHRONO / WEATHER / ATMOS), each with a fixed
   label column on the left and content on the right. The panel stretches
   to match the status panel height beside it (align-items: stretch on
   .app-header-top-row), and the .header-cw-stack distributes its sections
   to fill the available height.

   When there's no atmos event, the ATMOS section collapses via [hidden]
   and the chrono+weather sections expand to fill the leftover space.
   ───────────────────────────────────────────────────────────────────────── */

.header-weather-panel {
    display: flex;
    flex-direction: column;
    /* Pin to the locked .app-header-top-row height so the weather
       column never grows taller than its siblings. */
    min-height: 100%;
    max-height: 100%;
    overflow: hidden;
}

.header-cw-stack {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    margin-top: 2px;
}

.header-cw-section {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 0;
    /* All three sections (CHRONO / WEATHER / ATMOS) share equal
       height via flex: 1 1 0 (not auto). The 0 flex-basis forces
       each section to start at zero and grow equally, so they
       divide the available stack height into thirds regardless of
       their content height. */
    flex: 1 1 0;
    min-height: 0;
}

.header-cw-section[hidden] {
    display: none;
}

.header-cw-section + .header-cw-section {
    margin-top: 6px;
}

.header-cw-section-label {
    display: none;
}

.header-cw-section-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    flex: 1;
}

.header-cw-dot {
    color: rgba(255, 215, 173, 0.32);
    font-size: 9px;
    margin: 0 1px;
}

/* ── CHRONO section (clock + date · zone) ───────────────────────────── */

/* Time + wireframe clock icon row — mirrors the layout used by
   .header-cw-weather-headline / .header-cw-atmos-headline so all
   three CHRONO/WEATHER sub-sections share the same icon-on-the-left
   typography rhythm. */
.header-cw-time-headline {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Date sits right-aligned inside the time headline row, pushed to
   the far end by flex so it reads as context for the big clock. */
.header-cw-time-headline .identity-date {
    margin-left: auto;
    color: var(--text-tier-3);
    font-family: var(--text-font);
    font-size: 9px;
    font-weight: 400;
    letter-spacing: 0.03em;
}

/* Wireframe analog clock canvas container — same sizing treatment
   as the weather + atmos icons. The child <canvas> attached by
   clock-icon.js fills 100% of this box. */
.header-clock-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    filter: drop-shadow(0 0 6px rgba(232, 146, 58, 0.15));
}

.identity-time {
    color: var(--text-tier-1);
    font-family: var(--numeric-font);
    font-size: 24px;
    font-weight: 500;
    letter-spacing: -0.015em;
    line-height: 1;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    white-space: nowrap;
    display: inline-flex;
    align-items: baseline;
    gap: 0;
    text-shadow: 0 0 16px rgba(255, 220, 180, 0.08);
}

.identity-time-hours-minutes {
    color: inherit;
}

.identity-time-separator {
    /* Separator is a visual device, not info — keep it dim. */
    color: rgba(255, 250, 236, 0.3);
}

.identity-time-seconds {
    /* Seconds are secondary to HH:MM — slightly subdued. */
    color: rgba(255, 250, 236, 0.58);
    font-size: 0.78em;
    margin-left: 2px;
}

.header-cw-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 5px;
    /* Tier 3 — "Thursday, 09 Apr 2026 · SG / SGT" is meta context. */
    color: var(--text-tier-3);
    font-family: var(--text-font);
    font-size: 9.5px;
    font-weight: 400;
    letter-spacing: 0.04em;
    margin-top: 2px;
}

.identity-date {
    /* Tier 3 — date string, secondary to the clock. */
    color: var(--text-tier-3);
    font-family: var(--text-font);
    font-size: 9.5px;
    font-weight: 400;
    letter-spacing: 0.03em;
}

.identity-zone {
    color: var(--text-min);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* ── WEATHER section (icon + temp + condition + chips) ─────────────── */

.header-weather-loading {
    font-family: var(--system-font);
    font-size: 8.5px;
    letter-spacing: 0.1em;
    color: var(--text-min);
    animation: weatherPulse 1.6s ease-in-out infinite;
}

.header-cw-weather-headline {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 10px;
    line-height: 1;
}

/* Same fix as weather icon — align to the top of the body so the
   icon sits next to the title (first line) instead of centering
   against the full body height (title + subline). */
.header-cw-atmos > .header-atmos-icon {
    align-self: flex-start;
    margin-top: 2px;
}

.header-weather-icon {
    /* Canvas / SVG container — the child <canvas> (or fallback SVG)
       fills 100% of this box. Sized so the icon visually balances
       the 22px temperature next to it. Bumped 26 → 32 so the line-
       art and the canvas-painted detail have more room to read. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    align-self: center;
    margin-right: 2px;
    filter: drop-shadow(0 0 5px rgba(232, 146, 58, 0.12));
}

.header-weather-icon svg {
    width: 100%;
    height: 100%;
    display: block;
}

.header-weather-temp {
    font-family: var(--numeric-font);
    font-size: 20px;
    font-weight: 500;
    letter-spacing: -0.02em;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    line-height: 1;
}

.header-weather-unit {
    font-size: 9px;
    font-weight: 400;
    color: var(--text-min);
    letter-spacing: 0.04em;
    margin-left: 1px;
    font-variant-numeric: normal;
}

.header-weather-condition,
.header-weather-location {
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-tier-3);
}

.header-weather-location {
    color: var(--text-min);
}

.header-weather-meta {
    display: flex;
    justify-content: flex-end;
    margin-top: 2px;
}

.header-weather-chip {
    font-family: var(--system-font);
    font-size: 7px;
    letter-spacing: 0.08em;
    color: var(--text-min);
    text-transform: uppercase;
    padding: 2px 5px;
    border: 0.5px solid rgba(255, 255, 255, 0.07);
    background: rgba(255, 255, 255, 0.014);
    border-radius: 1px;
}

/* ── ATMOS section (only mounts when there's an active sun event) ──── */

.header-cw-atmos-headline {
    display: flex;
    align-items: center;
    gap: 0;
}

/* Wireframe canvas container — the child <canvas> fills 100% of this
   box. Sized larger than the 26px weather icon because the line-art
   atmos compositions (star fields, double-stroke sun rings, crescent
   bites) need a few more pixels of breathing room to read. */
.header-atmos-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    align-self: center;
    filter: drop-shadow(0 0 7px rgba(255, 200, 120, 0.2));
}

.header-atmos-icon svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* Per-event icon tint boost — the SVGs already carry their own gradient
   colors but a subtle drop-shadow that matches the event mood makes the
   tiny 26px icon pop more on the dark panel. */
.header-cw-atmos.atmos-gold .header-atmos-icon {
    filter: drop-shadow(0 0 7px rgba(255, 176, 80, 0.36));
}

.header-cw-atmos.atmos-blue .header-atmos-icon {
    filter: drop-shadow(0 0 7px rgba(120, 180, 255, 0.32));
}

.header-cw-atmos.atmos-night .header-atmos-icon {
    filter: drop-shadow(0 0 7px rgba(160, 180, 255, 0.28));
}

.header-cw-atmos.atmos-dawn .header-atmos-icon {
    filter: drop-shadow(0 0 7px rgba(255, 208, 140, 0.3));
}

.header-cw-atmos.atmos-rain .header-atmos-icon {
    filter: drop-shadow(0 0 7px rgba(120, 200, 255, 0.3));
}

.header-atmos-title {
    color: var(--text-primary);
    font-family: var(--system-font);
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    line-height: 1.2;
}

.header-atmos-subline {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 2px;
}

.header-atmos-time {
    color: var(--text-min);
    font-family: var(--numeric-font);
    font-size: 8.5px;
    letter-spacing: 0.06em;
}

.header-atmos-mood {
    color: var(--weather-accent);
    font-family: var(--system-font);
    font-size: 7.5px;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* NEXT+1 line — the event after the next one. Same layout as the
   NEXT subline but dimmer, signaling it's further into the future. */
.header-atmos-subline-plus1 {
    display: none;
}

.header-atmos-tag {
    font-family: var(--system-font);
    font-size: 7px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-tier-3);
    margin-right: 6px;
}

.header-atmos-plus1-label {
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
}

.header-atmos-plus1-time {
    font-family: var(--numeric-font);
    font-size: 7.5px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--text-tier-3);
    margin-left: 4px;
}

.header-weather-offline {
    font-family: var(--system-font);
    font-size: 8.5px;
    letter-spacing: 0.08em;
    color: var(--text-min);
}

@keyframes weatherPulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}

/* ── Atmospheric warmth: data glow overlays ─────────────────────────────── */

/* Hero RP large number �?floats on warm amber glow */
.hero-rp-value {
    filter: drop-shadow(0 0 18px rgba(139, 139, 245, 0.08));
}

/* Overview stat values �?subtle amber luminance */
.overview-stat-value {
    text-shadow: 0 0 14px rgba(139, 139, 245, 0.1);
}

/* Identity note �?warm, very dim */
.identity-note {
    color: var(--text-min);
}

/* Trace/detail toggle �?warm hover */
.trace-toggle-btn:hover {
    border-color: rgba(200, 160, 80, 0.24);
    color: var(--text-primary);
    box-shadow: 0 0 18px rgba(200, 160, 80, 0.08);
}

/* Section label text — slightly warmer */
.section-label {
    color: var(--text-secondary);
}

/* View selector buttons �?warm amber when active */
.view-btn.is-active {
    color: var(--text-primary);
    text-shadow: 0 0 10px rgba(139, 139, 245, 0.16);
}

/* Intel section labels �?warm amber */
.intel-label {
    color: var(--text-min);
    border-bottom: 0.5px solid rgba(255, 255, 255, 0.05);
}

/* Scroll bar �?warm tint */

/* Switch toggle active thumb �?warm amber glow */
.switch-toggle input:not(:checked) + .switch-toggle-shell .switch-toggle-thumb {
    background: rgba(139, 139, 245, 0.84);
    box-shadow: 0 0 10px rgba(139, 139, 245, 0.28);
}

/* Loading spinner warm tint */
.spin {
    border-color: rgba(255, 255, 255, 0.1);
    border-top-color: rgba(180, 140, 80, 0.72);
}

/* ── Hover micro-interactions ──────────────────────────────────────────── */

section.panel {
    transition:
        border-color 0.38s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        box-shadow 0.38s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        opacity 0.72s cubic-bezier(0.19, 1, 0.22, 1),
        transform 0.72s cubic-bezier(0.19, 1, 0.22, 1);
}

section.panel:hover {
    border-color: rgba(255, 255, 255, 0.11);
    box-shadow:
        0 28px 80px rgba(0, 0, 0, 0.62),
        inset 0 1px 0 rgba(255, 255, 255, 0.04),
        0 0 70px rgba(150, 72, 30, 0.06),
        0 0 28px rgba(139, 139, 245, 0.016);
}

.overview-stat {
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.overview-stat:hover {
    border-color: rgba(255, 255, 255, 0.09);
    box-shadow: 0 0 20px rgba(139, 139, 245, 0.02);
}

.view-btn {
    transition:
        color 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        background 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s,
        box-shadow 0.28s cubic-bezier(0.22, 1, 0.36, 1) 0.08s;
}

.view-btn:hover {
    box-shadow: 0 0 10px rgba(139, 139, 245, 0.035);
}

.header-micro-panel {
    transition:
        opacity 0.66s cubic-bezier(0.19, 1, 0.22, 1),
        transform 0.66s cubic-bezier(0.19, 1, 0.22, 1);
}

/* hover borders removed — borderless hero row */

/* ═══════════════════════════════════════════════════════════════════
   TAB PANEL SYSTEM
   ═══════════════════════════════════════════════════════════════════ */

/* ── Tab bar pill — bridge between hero & content cards ───────── */
.tab-bar {
    position: relative;
    display: flex;
    align-items: center;
    align-self: center;
    gap: 2px;
    padding: 4px 8px;
    margin: 14px 0;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 24px;
    background: rgba(255, 255, 255, 0.03);
    opacity: 0;
    transition: opacity 0.5s ease;
}

.tab-bar.entered {
    opacity: 1;
}

/* Sliding pill indicator — positioned via JS custom properties */
.tab-bar::before {
    content: "";
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: calc(var(--tab-x, 8px));
    width: var(--tab-w, 60px);
    background: rgba(255, 255, 255, 0.08);
    border-radius: 20px;
    transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    pointer-events: none;
    z-index: 0;
}

.tab-btn {
    position: relative;
    z-index: 1;
    padding: 6px clamp(14px, 1.2vw, 22px);
    border: none;
    border-radius: 20px;
    background: transparent;
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: clamp(9px, 0.55vw, 11px);
    font-weight: 400;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    cursor: pointer;
    transition: color 0.25s ease;
}

.tab-btn::after {
    content: "";
    position: absolute;
    bottom: 2px;
    left: 25%;
    right: 25%;
    height: 2px;
    border-radius: 1px;
    background: transparent;
    transition: background 0.25s ease;
}

.tab-btn:hover {
    color: var(--text-tier-2);
}

.tab-btn.is-active {
    color: var(--orange);
}

.tab-btn.is-active::after {
    background: var(--orange);
}

.tab-content {
    min-width: 0;
}

/* ═══════════════════════════════════════════════════════════════════
   OVERVIEW TAB — Summary Cards
   ═══════════════════════════════════════════════════════════════════ */

.overview-summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(clamp(200px, 18vw, 340px), 1fr));
    gap: 16px;
    margin-bottom: 28px;
}

.overview-card {
    padding: clamp(14px, 1vw, 22px) clamp(16px, 1.2vw, 24px);
    border: 0.5px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--surface-radius-sm);
    background: rgba(19, 18, 29, 0.5);
    cursor: default;
    display: flex;
    flex-direction: column;
    gap: 8px;
    transition: border-color 0.25s ease;
}

.overview-card[data-navigate-tab] {
    cursor: pointer;
}

.overview-card[data-navigate-tab]:hover {
    border-color: rgba(232, 146, 58, 0.3);
}

.overview-card-tag {
    color: rgba(224, 224, 232, 0.5);
    font-family: var(--system-font);
    font-size: clamp(7px, 0.45vw, 9px);
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.overview-card-value {
    color: var(--text-tier-1);
    font-family: var(--system-font);
    font-size: clamp(11px, 0.75vw, 15px);
    font-weight: 400;
    letter-spacing: 0.03em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.overview-card-meta {
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: clamp(8px, 0.5vw, 10px);
}

/* ── Savings progress bar (inside overview card) ─────────────── */

.savings-bar-track {
    flex: 1;
    height: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

.savings-bar-fill {
    height: 100%;
    border-radius: 2px;
    background: var(--orange);
    transition: width 0.5s ease;
}

.overview-card-meta:has(.savings-bar-track) {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ── Savings detail section ──────────────────────────────────── */

.savings-detail {
    margin-top: 24px;
}

.savings-add-row {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
}

.savings-input {
    padding: 6px 10px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-tier-1);
    font-family: var(--system-font);
    font-size: 10px;
    outline: none;
    transition: border-color 0.2s ease;
}

.savings-input:focus {
    border-color: rgba(232, 146, 58, 0.3);
}

.savings-input-amount {
    width: 100px;
}

.savings-input-note {
    flex: 1;
}

.savings-input::placeholder {
    color: var(--text-tier-3);
    opacity: 0.5;
}

.savings-add-btn {
    padding: 6px 14px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-tier-2);
    font-family: var(--system-font);
    font-size: 10px;
    cursor: pointer;
    transition: border-color 0.2s ease, color 0.2s ease;
}

.savings-add-btn:hover {
    border-color: rgba(232, 146, 58, 0.3);
    color: var(--orange);
}

.savings-log {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.savings-row {
    display: grid;
    grid-template-columns: 60px 80px 1fr 90px;
    gap: 12px;
    align-items: center;
    padding: 5px 0;
    font-family: var(--system-font);
    font-size: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

.savings-row-date {
    color: var(--text-tier-3);
    font-family: var(--numeric-font);
    font-size: 9px;
}

.savings-row-amount {
    font-family: var(--numeric-font);
    font-weight: 500;
}

.savings-row-note {
    color: var(--text-tier-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.savings-row-total {
    text-align: right;
    color: var(--text-tier-2);
    font-family: var(--numeric-font);
    font-size: 9px;
}

.savings-empty {
    color: var(--text-tier-3);
    font-size: 10px;
    opacity: 0.5;
    padding: 8px 0;
}

/* ── Health detail section ───────────────────────────────────── */

/* ── Match History ────────────────────────────────────────── */

.match-history {
    margin-top: 8px;
}

.match-header,
.match-row {
    display: grid;
    grid-template-columns: 90px 80px 50px 50px 70px 60px 40px;
    gap: 12px;
    padding: 6px 8px;
    font-family: var(--system-font);
    font-size: 10px;
    align-items: center;
    border-radius: 6px;
}

.match-header {
    color: var(--text-tier-3);
    font-weight: 500;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    margin-bottom: 2px;
}

.match-row {
    color: var(--text-tier-2);
    cursor: pointer;
    transition: background 0.15s ease;
}

.match-row:hover {
    background: rgba(255, 255, 255, 0.03);
}

.match-row:nth-child(even) {
    background: rgba(255, 255, 255, 0.015);
}

.match-row:nth-child(even):hover {
    background: rgba(255, 255, 255, 0.04);
}

.match-time {
    font-family: var(--numeric-font);
    font-size: 9px;
    color: var(--text-tier-3);
}

.match-legend {
    font-weight: 500;
    color: var(--text-tier-1);
}

.match-placement {
    font-family: var(--numeric-font);
    font-weight: 600;
    color: var(--text-tier-2);
}

.match-placement-win {
    color: var(--orange);
}

.match-kills,
.match-damage {
    font-family: var(--numeric-font);
}

.match-rp {
    font-family: var(--numeric-font);
    font-weight: 500;
}

.match-list {
    max-height: 400px;
    overflow-y: auto;
}

.match-detail {
    padding: 8px 12px 8px 100px;
    margin-bottom: 2px;
}

.match-detail-grid {
    display: flex;
    gap: 16px;
    font-family: var(--system-font);
    font-size: 9px;
    color: var(--text-tier-3);
}

.match-legend-editable {
    cursor: pointer;
    border-bottom: 1px dashed rgba(255, 255, 255, 0.15);
    transition: color 0.15s ease;
}

.match-legend-editable:hover {
    color: var(--orange);
}

.match-legend-select {
    padding: 2px 4px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 4px;
    background: rgba(19, 18, 29, 0.95);
    color: var(--text-tier-1);
    font-family: var(--system-font);
    font-size: 10px;
    outline: none;
    max-width: 90px;
}

.match-empty {
    color: var(--text-tier-3);
    font-size: 10px;
    opacity: 0.5;
    padding: 16px 0;
}

/* ── Apex section header + HISTORY/ANALYTICS toggle ──────────── */

.apex-section-header {
    display: flex;
    align-items: center;
    gap: 12px;
}

.apex-view-selector {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
}

/* ── Review indicator dot + editor ──────────────────────────── */

.match-review-indicator {
    text-align: center;
    font-size: 11px;
    cursor: pointer;
    opacity: 0.8;
    transition: opacity 0.15s ease;
    user-select: none;
}

.match-review-indicator:hover {
    opacity: 1;
}

.review-editor {
    margin-top: 10px;
    padding: 10px 12px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--surface-radius-sm);
    background: rgba(19, 18, 29, 0.5);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.review-section {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.review-section[hidden] {
    display: none;
}

.review-section-label {
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: 9px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    min-width: 44px;
    padding-top: 4px;
    flex-shrink: 0;
}

.review-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    flex: 1;
}

.review-pill {
    padding: 3px 10px;
    border-radius: 10px;
    font-family: var(--system-font);
    font-size: 9px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: var(--text-tier-2);
    cursor: pointer;
    transition: all 0.15s ease;
}

.review-pill:hover {
    border-color: rgba(232, 146, 58, 0.3);
    color: var(--text-tier-1);
}

.review-pill.is-active[data-review-attr="my_fault"] {
    background: rgba(232, 146, 58, 0.18);
    border-color: var(--orange);
    color: var(--orange);
}

.review-pill.is-active[data-review-attr="teammate"] {
    background: rgba(96, 165, 250, 0.18);
    border-color: var(--blue);
    color: var(--blue);
}

.review-pill.is-active[data-review-attr="uncontrollable"] {
    background: rgba(107, 107, 123, 0.22);
    border-color: var(--text-tier-3);
    color: var(--text-tier-1);
}

.review-pill.is-active[data-review-tag] {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(255, 255, 255, 0.22);
    color: var(--text-tier-1);
}

.review-note {
    flex: 1;
    padding: 6px 8px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 4px;
    resize: vertical;
    min-height: 28px;
    max-height: 80px;
    color: var(--text-tier-1);
    font-family: var(--system-font);
    font-size: 10px;
    outline: none;
}

.review-note:focus {
    border-color: rgba(232, 146, 58, 0.3);
}

.review-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    justify-content: flex-end;
}

.review-save-btn {
    padding: 4px 14px;
    border-radius: 6px;
    background: rgba(232, 146, 58, 0.12);
    border: 1px solid rgba(232, 146, 58, 0.3);
    color: var(--orange);
    font-family: var(--system-font);
    font-size: 10px;
    cursor: pointer;
    transition: all 0.15s ease;
}

.review-save-btn:hover:not(:disabled) {
    background: rgba(232, 146, 58, 0.24);
}

.review-save-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.review-save-status {
    font-family: var(--system-font);
    font-size: 9px;
    color: var(--text-tier-3);
}

.review-save-status.is-ok {
    color: var(--ok);
}

.review-save-status.is-fail {
    color: var(--fail);
}

/* ── Analytics dashboard ─────────────────────────────────────── */

.apex-analytics {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-top: 8px;
}

.analytics-filters {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--surface-radius-sm);
    background: rgba(19, 18, 29, 0.5);
}

.analytics-filter-input,
.analytics-filter-select {
    padding: 4px 8px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 4px;
    color: var(--text-tier-1);
    font-family: var(--system-font);
    font-size: 10px;
    outline: none;
}

.analytics-filter-input:focus,
.analytics-filter-select:focus {
    border-color: rgba(232, 146, 58, 0.3);
}

.analytics-filter-sep {
    color: var(--text-tier-3);
    font-size: 10px;
}

.analytics-filter-reset {
    padding: 4px 12px;
    border-radius: 4px;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: var(--text-tier-2);
    font-family: var(--system-font);
    font-size: 10px;
    cursor: pointer;
    transition: all 0.15s ease;
    margin-left: auto;
}

.analytics-filter-reset:hover {
    border-color: rgba(232, 146, 58, 0.3);
    color: var(--orange);
}

.analytics-card {
    padding: 14px 18px;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--surface-radius-sm);
    background: rgba(19, 18, 29, 0.5);
}

.analytics-card-head {
    font-family: var(--system-font);
    font-size: 9px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-tier-3);
    margin-bottom: 10px;
}

.analytics-subhead {
    font-family: var(--system-font);
    font-size: 8px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-tier-3);
    margin-bottom: 6px;
    margin-top: 8px;
}

.analytics-overview-grid {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 10px;
}

.analytics-chart {
    width: 100%;
    height: 220px;
}

.analytics-chart-tall {
    height: 360px;
}

.analytics-chart-half {
    height: 200px;
}

.analytics-time-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-bottom: 10px;
}

.analytics-donut-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.analytics-session-table {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.analytics-session-row {
    display: grid;
    grid-template-columns: 220px 60px 70px 70px 1fr;
    gap: 12px;
    padding: 6px 10px;
    font-family: var(--system-font);
    font-size: 10px;
    color: var(--text-tier-2);
    border-radius: 4px;
}

.analytics-session-row.is-header {
    color: var(--text-tier-3);
    font-weight: 500;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    margin-bottom: 2px;
}

.analytics-session-row.is-best {
    background: rgba(74, 222, 128, 0.08);
    border-left: 2px solid var(--ok);
}

.analytics-session-row.is-worst {
    background: rgba(248, 113, 113, 0.08);
    border-left: 2px solid var(--fail);
}

@media (max-width: 1100px) {
    .analytics-overview-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
    .analytics-time-grid,
    .analytics-donut-grid {
        grid-template-columns: 1fr;
    }
    .analytics-session-row {
        grid-template-columns: 1fr 50px 60px 60px;
    }
    .analytics-session-row > span:last-child {
        display: none;
    }
}

.health-detail {
    margin-top: 24px;
}

.health-today {
    display: flex;
    gap: 24px;
    margin-bottom: 20px;
}

.health-stat {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.health-stat-value {
    font-family: var(--numeric-font);
    font-size: 18px;
    font-weight: 600;
    color: var(--text-tier-1);
}

.health-stat-label {
    font-family: var(--system-font);
    font-size: 8px;
    color: var(--text-tier-3);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.health-history-header .health-row {
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    padding-bottom: 4px;
    margin-bottom: 4px;
}

.health-history-header .health-row-metric,
.health-history-header .health-row-date {
    font-weight: 500;
    color: var(--text-tier-2);
}

.health-row {
    display: grid;
    grid-template-columns: 50px repeat(4, 1fr);
    gap: 8px;
    align-items: center;
    padding: 4px 0;
    font-family: var(--system-font);
    font-size: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}

.health-row-date {
    color: var(--text-tier-3);
    font-family: var(--numeric-font);
    font-size: 9px;
}

.health-row-metric {
    color: var(--text-tier-2);
    font-family: var(--numeric-font);
    font-size: 10px;
}

/* ═══════════════════════════════════════════════════════════════════
   TO DO PANEL
   ═══════════════════════════════════════════════════════════════════ */

/* ── Apple-style spring curve ─────────────────────────────────── */
/* Reusable timing for all todo interactions */

.todo-panel {
    display: flex;
    flex-direction: column;
    overflow: visible;
}

.todo-panel-inner {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.todo-habits-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}

.todo-date-header {
    font-family: var(--system-font);
    font-size: 1.05rem;
    font-weight: 500;
    color: #e0e0e8;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* ── Class schedule ──────────────────────────────────────── */

.schedule-display {
    margin-left: auto;
    flex-shrink: 0;
}

.schedule-label {
    font-family: var(--system-font);
    font-size: 7px;
    font-weight: 400;
    color: var(--text-tier-3);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    margin-bottom: 4px;
    opacity: 0.7;
}

.schedule-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.schedule-item {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-family: var(--system-font);
    font-size: 8px;
    color: var(--text-tier-3);
    line-height: 1.4;
}

.schedule-time {
    font-family: var(--numeric-font);
    font-size: 8px;
    color: var(--text-tier-3);
    opacity: 0.8;
    flex-shrink: 0;
    min-width: 32px;
}

.schedule-code {
    color: var(--text-tier-2);
    font-weight: 500;
    flex-shrink: 0;
}

.schedule-name {
    color: var(--text-tier-3);
    opacity: 0.7;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.schedule-item.is-past {
    opacity: 0.3;
}

.schedule-item.is-now {
    border-left: 2px solid var(--orange);
    padding-left: 6px;
    margin-left: -8px;
}

.schedule-item.is-now .schedule-time,
.schedule-item.is-now .schedule-code {
    color: var(--text-tier-1);
}

.schedule-now-badge {
    font-family: var(--system-font);
    font-size: 6px;
    font-weight: 600;
    color: var(--orange);
    letter-spacing: 0.06em;
    margin-left: 4px;
}

.schedule-empty {
    font-size: 8px;
    color: var(--text-tier-3);
    opacity: 0.5;
}

.todo-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.todo-habits-section { flex: 0 0 auto; }

.todo-tasks-section {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.todo-tasks-list {
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
}

/* ── Progress ring row ───────────────────────────────────────── */

.todo-ring-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
    padding: 8px 0;
}

.todo-progress-ring {
    position: relative;
    width: 56px;
    height: 56px;
    flex-shrink: 0;
    overflow: visible;
}

/* Ring + confetti now rendered by Canvas in todoModule.js */

.todo-ring-canvas {
    display: block;
}

.todo-section-tag {
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: 8px;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.todo-divider {
    height: 0;
    margin: 12px 0;
}

/* ── Habit / Task items ──────────────────────────────────────── */

.todo-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: 8px;
    font-family: var(--system-font);
    font-size: 11px;
    color: var(--text-tier-3);
    transition:
        color 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
        background 0.3s ease,
        transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),
        box-shadow 0.4s ease;
}

.todo-item:hover {
    background: rgba(255, 255, 255, 0.02);
    transform: scale(1.015);
}

.todo-item.is-done {
    color: var(--text-tier-2);
}

/* Glow flash on check */
.todo-item.is-done {
    animation: todoRowGlow 0.8s ease-out;
}

@keyframes todoRowGlow {
    0%   { box-shadow: inset 0 0 0 rgba(232, 146, 58, 0); }
    30%  { box-shadow: inset 0 0 20px rgba(232, 146, 58, 0.08); }
    100% { box-shadow: inset 0 0 0 rgba(232, 146, 58, 0); }
}

/* Time-aware suggestion accent */
.todo-item-suggested {
    border-left: 3px solid var(--orange);
    padding-left: 8px;
    color: var(--text-tier-1);
}

.todo-item-suggested .todo-label {
    color: var(--text-tier-1);
}

/* ── Circular checkbox with bounce ───────────────────────────── */

.todo-check-wrap {
    position: relative;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.todo-check {
    appearance: none;
    -webkit-appearance: none;
    position: absolute;
    inset: 0;
    width: 18px;
    height: 18px;
    border: 1.5px solid rgba(255, 255, 255, 0.15);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    transition:
        border-color 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
        background 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
        transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.todo-check:checked {
    border-color: var(--orange);
    background: var(--orange);
    animation: todoCheckBounce 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes todoCheckBounce {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.25); }
    70%  { transform: scale(0.95); }
    100% { transform: scale(1); }
}

.todo-check-icon {
    position: absolute;
    inset: 2px;
    width: 14px;
    height: 14px;
    color: var(--bg);
    opacity: 0;
    transform: scale(0.5);
    transition:
        opacity 0.25s ease,
        transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
    pointer-events: none;
}

.todo-check:checked ~ .todo-check-icon {
    opacity: 1;
    transform: scale(1);
}

.todo-item-left {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 0;
    cursor: pointer;
}

/* ── Label with strikethrough reveal ─────────────────────────── */

.todo-label {
    position: relative;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color 0.3s ease;
}

.todo-item.is-done .todo-label {
    color: var(--text-tier-3);
}

.todo-item.is-done .todo-label::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    height: 1px;
    background: var(--text-tier-3);
    animation: todoStrikeReveal 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

@keyframes todoStrikeReveal {
    0%   { width: 0; }
    100% { width: 100%; }
}

/* ── Streak counter ──────────────────────────────────────────── */

.todo-streak {
    font-family: var(--system-font);
    font-size: 9px;
    color: var(--text-tier-3);
    opacity: 0.7;
    flex-shrink: 0;
    transition: color 0.3s ease;
}

.todo-streak.milestone {
    color: var(--orange);
    opacity: 1;
    animation: todoStreakPulse 0.6s ease-out;
}

@keyframes todoStreakPulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.15); color: var(--orange); }
}

/* ── Delete button ───────────────────────────────────────────── */

.todo-delete-btn {
    border: none;
    background: transparent;
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: 12px;
    cursor: pointer;
    padding: 2px 6px;
    opacity: 0;
    border-radius: 4px;
    transition:
        opacity 0.2s ease,
        color 0.2s ease,
        transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.todo-item:hover .todo-delete-btn {
    opacity: 0.6;
}

.todo-delete-btn:hover {
    color: var(--fail);
    opacity: 1;
    transform: scale(1.15);
}

/* ── Add task row ────────────────────────────────────────────── */

.todo-add-row {
    display: flex;
    gap: 8px;
    margin-top: auto;
    padding-top: 10px;
}

.todo-add-input {
    flex: 1;
    padding: 6px 0;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 0;
    background: transparent;
    color: var(--text-tier-2);
    font-family: var(--system-font);
    font-size: 10px;
    letter-spacing: 0.01em;
    outline: none;
    transition: border-color 0.3s ease;
}

.todo-add-input:focus {
    border-bottom-color: rgba(232, 146, 58, 0.4);
}

.todo-add-input::placeholder {
    color: var(--text-tier-3);
    opacity: 0.5;
}

.todo-add-btn {
    padding: 4px 8px;
    border: none;
    border-radius: 50%;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.04);
    color: var(--text-tier-3);
    font-family: var(--system-font);
    font-size: 14px;
    cursor: pointer;
    transition:
        color 0.25s ease,
        background 0.25s ease,
        transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.todo-add-btn:hover {
    color: var(--orange);
    background: rgba(232, 146, 58, 0.1);
    transform: scale(1.1);
}

.todo-empty {
    color: var(--text-tier-3);
    font-size: 10px;
    opacity: 0.4;
    padding: 4px 0;
}
