/* Nullcrest Idle UI - cyberpunk-loud, vanilla CSS. Per branding.md: single dark
   surface, mono base + display headers, per-screen accent rotation, bloom
   as elevation, fx-layer frame, motion as feedback. Data legibility wins.
   Layout follows runs_mockup.png: header band with the city image, icon
   STATUS HUD, a collapsible nav row, structured list cards with pip
   meters, persistent queue strip. */

/* ---- self-hosted display face (one weight; mono is a system stack) ---- */
@font-face {
    font-family: "Rajdhani";
    src: local("Rajdhani SemiBold"), local("Rajdhani-SemiBold");
    font-weight: 600;
    font-display: swap;
}

:root {
    /* surface tokens (branding.md §2.1) */
    --c-bg-0: #05060a;
    --c-bg-1: #0a0d14;
    --c-bg-2: #11161f;
    --c-bg-3: #1a2030;
    --c-fg: #d6f5e6;
    --c-fg-dim: #7a8a99;
    --c-fg-disabled: #454f5c;
    --c-border: #1f2a3a;
    --c-border-strong: #2c3a4f;

    /* district / faction accents (§2.2) */
    --c-acid: #b6ff3c;
    --c-cyan: #1ff5ff;
    --c-magenta: #ff2bd6;
    --c-amber: #ffb020;
    --c-violet: #9b6bff;
    --c-coral: #ff7860;
    --c-mint: #3affb0;

    /* status colors (§2.3) - reserved, never decorative */
    --c-success: #19c37d;
    --c-danger: #ff2046;
    --c-warn: #ffa94d;
    --c-loss: #d92e6f;

    /* item rarity palette (TAD §15.14) - a dedicated five-tier ramp.
       Distinct from the four reserved stat / bar colors; a rarity color
       only ever means rarity. */
    --c-rarity-common: #8b97a6;
    --c-rarity-uncommon: #5fce7a;
    --c-rarity-rare: #4aa8ff;
    --c-rarity-epic: #b478ff;
    --c-rarity-legendary: #ffb020;

    /* Chrome accent - a restrained neutral. Panels, headers and the fx
       grid no longer each carry a bright per-screen hue; color is spent
       only where it MEANS something (reserved bars, rarity, gain/loss).
       A muted steel-grey reads as structure, not decoration. */
    --c-chrome: #45566b;

    /* Default accent - the neutral chrome. mountView() swaps --c-accent
       per route on <body>, but every route now resolves to --c-chrome,
       so the chrome stays uniform and calm across screens. */
    --c-accent: var(--c-chrome);

    /* Accent bloom (§3.5) - a real box-shadow halo on interactive accent
       elements. Defined as the color-mix RATIOS only, not a finished
       box-shadow: a baked box-shadow token substitutes var(--c-accent) at
       :root, freezing the root accent (acid green) into every consumer
       regardless of the per-route accent swap. Each .vis-full consumer
       below composes its own box-shadow against the in-scope --c-accent
       so the bloom always tracks the live route accent - never green on a
       coral / amber / cyan screen. */
    /* Soft accent surface - mixed into the panel color (not transparent)
       so the tint stays subtle and clean on any accent, never muddy. */
    --accent-soft: color-mix(in oklab, var(--c-accent) 14%, var(--c-bg-2));

    /* Selection / hover highlight - a single clean white treatment, used
       for active nav, selected cards, and hover. Distinct from the
       per-route content accents and the commit-button affordance. */
    --c-sel: #f4f7fb;
    --sel-soft: color-mix(in oklab, var(--c-sel) 9%, var(--c-bg-2));
    --sel-glow: 0 0 6px color-mix(in oklab, var(--c-sel) 35%, transparent);

    --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    --font-display: "Rajdhani", "Orbitron", "Audiowide", sans-serif;

    --grid: 4px;
    --radius-sm: 2px;
    --radius-md: 4px;
    --radius-lg: 6px;

    /* fixed-chrome heights - the view area pads around these */
    --topbar-h: 84px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
    background: var(--c-bg-0);
    color: var(--c-fg);
    font-family: var(--font-mono);
    font-size: 14px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

body {
    min-height: 100vh;
    padding: env(safe-area-inset-top) env(safe-area-inset-right)
             env(safe-area-inset-bottom) env(safe-area-inset-left);
}

.shell {
    position: relative;
    z-index: 1;
    max-width: 560px;
    margin: 0 auto;
}

/* The app root is a column: the header band, the STATUS HUD, the nav
   row, a scrolling view slot, and the queue strip. */
#app-root {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* ====================================================================
   fx-layer (§5) - animated grid, scanlines, CRT vignette behind content
   ==================================================================== */

.fx-layer {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}

.fx-grid {
    position: absolute;
    inset: -50%;
    background-image:
        linear-gradient(color-mix(in oklab, var(--c-accent) 8%, transparent) 1px, transparent 1px),
        linear-gradient(90deg, color-mix(in oklab, var(--c-accent) 8%, transparent) 1px, transparent 1px);
    background-size: 44px 44px;
    opacity: 0.5;
    animation: fx-drift 38s linear infinite;
}

@keyframes fx-drift {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(44px, 44px, 0); }
}

.fx-scanline {
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        transparent 0 2px, rgba(255, 255, 255, 0.04) 2px 3px);
    mix-blend-mode: overlay;
}

.fx-vignette {
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at center,
        transparent 58%, rgba(0, 0, 0, 0.62) 100%);
}

/* Visuals tier: Reduced - freeze the drift, drop overlays (§5.2) */
body.vis-reduced .fx-grid { animation: none; opacity: 0.32; }
body.vis-reduced .fx-scanline,
body.vis-reduced .fx-vignette { display: none; }

/* Visuals tier: Off - kill the whole frame (§5.3) */
body.vis-off .fx-layer { display: none; }

/* ====================================================================
   Typography
   ==================================================================== */

h1, h2, h3 { font-weight: 700; }

.section-title { font-family: var(--font-display); font-weight: 600; }

a { color: var(--c-cyan); }

/* ====================================================================
   Top bar - the header band: city image behind menu / wordmark / credits
   ==================================================================== */

.topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2);
    height: var(--topbar-h);
    padding: 0 calc(var(--grid) * 3);
    border-bottom: 1px solid var(--c-border);
    /* city image confined to the header band; the gradient overlay keeps
       the wordmark + credits legible against bright pixels. */
    background-image:
        linear-gradient(rgba(5, 6, 10, 0.55), rgba(5, 6, 10, 0.78)),
        url('/assets/bg_images/hero_001.png');
    background-position: center 30%;
    background-size: cover;
    background-repeat: no-repeat;
}

/* Visuals Off - drop the header image, fall back to the flat surface */
body.vis-off .topbar {
    background-image: none;
    background-color: color-mix(in oklab, var(--c-bg-0) 92%, transparent);
}

.topbar-menu {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    background: var(--c-bg-2);
    color: var(--c-fg-dim);
}
.topbar-menu svg { width: 18px; height: 18px; }
.topbar-menu:hover { color: var(--c-sel); border-color: var(--c-sel); }

/* Logo lockup - the skull + Nullcrest Idle wordmark image. The PNG is cropped
   tight to its artwork, so its visible height equals the rendered height:
   sized to the hamburger button (34px) and vertically centered in the
   header band. A drop-shadow keeps it crisp over the header city image. */
.topbar-logo {
    flex: 1;
    height: 34px;
    width: auto;
    object-fit: contain;
    object-position: left center;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.85));
}

.credits-readout {
    display: inline-flex;
    align-items: center;
    gap: var(--grid);
    padding: calc(var(--grid) * 1) calc(var(--grid) * 2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    background: var(--c-bg-2);
    font-variant-numeric: tabular-nums;
}
.credits-icon { display: inline-flex; color: var(--c-amber); }
.credits-icon svg { width: 16px; height: 16px; }
.credits-num { font-weight: 700; color: var(--c-fg); }
.credits-unit { font-size: 10px; color: var(--c-fg-dim); }

/* ====================================================================
   Visuals toggle - segmented control, lives in the Settings popup
   ==================================================================== */

.vis-toggle {
    display: inline-flex;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.vis-toggle button {
    background: transparent;
    border: none;
    border-radius: 0;
    color: var(--c-fg-dim);
    font-size: 10px;
    letter-spacing: 0.06em;
    padding: 4px 8px;
}
.vis-toggle button:hover:not(:disabled) {
    color: var(--c-fg);
    border-color: var(--c-border-strong);
}
.vis-toggle button[aria-pressed="true"] {
    background: var(--sel-soft);
    color: var(--c-sel);
}

/* ====================================================================
   Primary nav - a panel with the shared header, directly under STATUS
   ==================================================================== */

/* NAVIGATE and STATUS are direct children of #app-root (outside the
   padded .view-slot), so both get the same horizontal inset to line up
   with the topbar and the view panels below. */
.panel.nav-row { margin: calc(var(--grid) * 3) calc(var(--grid) * 3) 0; }

/* The nav item container - wraps to multiple rows as items grow. */
.nav-items {
    display: flex;
    flex-wrap: wrap;
    gap: var(--grid);
}

/* Each nav button is only as wide as its content - no stretch, no wrap.
   They sit left-aligned in the row (.nav-items) with consistent gaps. */
.nav-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: calc(var(--grid) * 1.5);
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-fg-dim);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 11px;
    white-space: nowrap;
}
.nav-icon { display: inline-flex; }
.nav-icon svg { width: 16px; height: 16px; }
/* Selection / hover highlight is white - a single, clean treatment for
   the active nav item and any hovered item. Per-route accents stay on
   content, not on the selection cue. */
.nav-btn:hover:not([aria-pressed="true"]) {
    color: var(--c-sel);
    border-color: var(--c-sel);
}
.nav-btn[aria-pressed="true"] {
    color: var(--c-sel);
    border-color: var(--c-sel);
    background: var(--c-bg-2);
}
body.vis-full .nav-btn[aria-pressed="true"] { box-shadow: var(--sel-glow); }

/* ====================================================================
   Dev account switcher - DEV-only, no-Telegram local testing
   Rendered above the topbar only on a dev session (isDevSession). Amber
   is the deliberate "this is a dev surface" cue, distinct from the white
   real-nav selection. Never present in production (DEV_MODE off -> the
   no-Telegram path 401s -> no dev account -> the bar never renders).
   ==================================================================== */
.dev-switcher {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--grid);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 3);
    background: color-mix(in oklab, var(--c-amber) 8%, var(--c-bg-1));
    border-bottom: 1px solid color-mix(in oklab, var(--c-amber) 30%, transparent);
}
.dev-switcher-label {
    color: var(--c-amber);
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.14em;
    margin-right: var(--grid);
}
.dev-switcher-btn {
    flex: 0 0 auto;
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-fg-dim);
    padding: var(--grid) calc(var(--grid) * 2);
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    white-space: nowrap;
}
.dev-switcher-btn.is-admin { color: var(--c-fg); }
.dev-switcher-btn:hover:not(.is-active) {
    color: var(--c-amber);
    border-color: var(--c-amber);
}
.dev-switcher-btn.is-active {
    color: var(--c-bg-0);
    background: var(--c-amber);
    border-color: var(--c-amber);
    font-weight: 700;
}

/* ====================================================================
   View slot - the routed, scrolling screen area
   ==================================================================== */

/* The view slot is content-sized, not flex-greedy: the ACTIVE QUEUE
   panel flows directly after it in the normal stack instead of being
   pushed to the viewport bottom. */
.view-slot {
    padding: calc(var(--grid) * 3) calc(var(--grid) * 3) 0;
}
.view { display: block; }
body.vis-full .view { animation: view-in 220ms ease-out; }
body.vis-reduced .view { animation: view-fade 140ms ease-out; }
@keyframes view-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes view-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ====================================================================
   Panels - bloom as elevation (§4.2)
   ==================================================================== */

.panel {
    background: var(--c-bg-1);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: calc(var(--grid) * 3);
    margin-bottom: calc(var(--grid) * 3);
    transition: border-color 150ms, box-shadow 150ms;
}
/* Section header - the one shared panel header: a punchy display title +
   an optional tagline, an accent rule on the left, and a collapse
   chevron on the right. Every panel (STATUS, NAVIGATE, view sections)
   uses this so all headers look and collapse identically. */
.section-head {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2);
    border-left: 2px solid var(--c-border-strong);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 3);
    margin-bottom: calc(var(--grid) * 3);
}
.section-head-text { flex: 1; min-width: 0; }
.section-title {
    font-size: 22px;
    font-weight: 600;
    line-height: 1.05;
    color: #ffffff;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}
.section-tag {
    margin-top: 2px;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--c-fg-dim);
}

/* Collapse control - a chevron toggle on every panel header */
.section-collapse {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    padding: 0;
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-fg-dim);
}
.section-collapse:hover {
    color: var(--c-fg);
    border-color: var(--c-border-strong);
}
.section-chevron {
    display: inline-flex;
    transition: transform 150ms ease-out;
}
.section-chevron svg { width: 16px; height: 16px; }
.section-head.collapsed .section-chevron { transform: rotate(-90deg); }

/* Collapsed panel body - hidden, and the header drops its bottom margin
   so the panel shrinks to just the header. */
.panel-body.collapsed { display: none; }
.section-head.collapsed { margin-bottom: 0; }

/* STATUS shares the standard panel header. The title font size is NOT
   overridden - every panel header shares the one .section-title size. */
.panel.hud { margin: calc(var(--grid) * 3) calc(var(--grid) * 3) 0; }

/* reduced-transparency kill-switch: flat surface, brighter border (§4.2) */
@media (prefers-reduced-transparency: reduce) {
    .panel { background: var(--c-bg-3); }
    body .section-head { box-shadow: none; }
    .topbar { backdrop-filter: none; }
}

/* ====================================================================
   STATUS HUD bars - each bar gets its own icon + clean track
   ==================================================================== */

.bar-row {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2.5);
    margin-bottom: calc(var(--grid) * 2);
}
.bar-row:last-child { margin-bottom: 0; }
.bar-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    background: var(--c-bg-2);
}
.bar-icon svg { width: 18px; height: 18px; }
/* The bar icon is a <button> opening the §15.1 explainer - reset the
   native button chrome and add a tap affordance. */
button.bar-icon {
    cursor: pointer;
    color: inherit;
    padding: 0;
    transition: border-color 120ms ease, background 120ms ease;
}
button.bar-icon:hover,
button.bar-icon:focus-visible {
    border-color: var(--c-sel);
}
.bar-row.stim .bar-icon { color: var(--c-acid); }
.bar-row.edge .bar-icon { color: var(--c-magenta); }
.bar-row.integrity .bar-icon { color: var(--c-success); }
.bar-row.sync .bar-icon { color: var(--c-cyan); }

.bar-body { flex: 1; min-width: 0; }
.bar-label {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    margin-bottom: 3px;
}
.bar-label .name {
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--c-fg-dim);
}
.bar-num { font-variant-numeric: tabular-nums; color: var(--c-fg); }
.bar-track {
    height: 9px;
    background: var(--c-bg-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.bar-fill { height: 100%; transition: width 250ms ease-out; }
.bar-fill.stim { background: var(--c-acid); }
.bar-fill.edge { background: var(--c-magenta); }
.bar-fill.integrity { background: var(--c-success); }
.bar-fill.sync { background: var(--c-cyan); }
/* Bar-fill bloom (§3.5) - the green bars (stim acid, integrity success)
   carry no glow per the no-green-glow rule; the fill color itself still
   reads the bar identity. The non-green bars keep their bloom. */
body.vis-full .bar-fill.edge { box-shadow: 0 0 8px var(--c-magenta); }
body.vis-full .bar-fill.sync { box-shadow: 0 0 8px var(--c-cyan); }
.bar-regen { font-size: 10px; color: var(--c-fg-dim); margin-top: 2px; }

/* Sync above max (TAD §15.19) - a sync_boost consumable pushes Sync past
   its ceiling; only Sync has this designed headroom. The over-max state
   reads explicitly so the player sees the stacked training buffer. */
.bar-row.over-max .bar-track {
    border-color: color-mix(in oklab, var(--c-cyan) 55%, var(--c-border));
}
.bar-num .over-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 4px;
    font-size: 8px;
    font-weight: 700;
    letter-spacing: 0.1em;
    border-radius: 2px;
    color: var(--c-cyan);
    border: 1px solid color-mix(in oklab, var(--c-cyan) 55%, var(--c-border));
    background: color-mix(in oklab, var(--c-cyan) 14%, transparent);
}
/* The over-max headroom segment - a hatched cyan extension sitting on top
   of the full 100% fill, so the stacked Sync buffer is visually legible
   past the track's normal ceiling. */
.bar-overflow {
    position: absolute;
    top: 0;
    right: 0;
    width: 22%;
    height: 100%;
    background: repeating-linear-gradient(
        -45deg,
        color-mix(in oklab, var(--c-cyan) 70%, transparent) 0 3px,
        transparent 3px 6px);
    border-left: 1px solid var(--c-cyan);
}
.bar-row.over-max .bar-track { position: relative; }
.bar-row.over-max .bar-regen { color: var(--c-cyan); }

/* ====================================================================
   Stat / wallet grid - numerics in tables never glitch (§9)
   ==================================================================== */

.kv-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--grid) calc(var(--grid) * 3);
}
.kv {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    padding: calc(var(--grid) * 1) calc(var(--grid) * 1.5);
    border-bottom: 1px solid var(--c-border);
}
.kv .k { color: var(--c-fg-dim); text-transform: uppercase; letter-spacing: 0.04em; }
.kv .v { color: var(--c-fg); font-variant-numeric: tabular-nums; }

/* Stat row - a tappable icon opens the §15.13 explainer popup. The icon
   button is a real tap target (mobile-first), never hover-only. */
.kv.stat-row {
    align-items: center;
    gap: var(--grid);
}
.kv.stat-row .k { flex: 1; }
.stat-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    padding: 0;
    color: var(--c-accent);
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}
.stat-icon-btn svg { width: 15px; height: 15px; }
.stat-icon-btn:hover,
.stat-icon-btn:focus-visible {
    border-color: var(--c-sel);
}
.stat-hint {
    margin-top: calc(var(--grid) * 2);
    font-size: 10px;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* Per-action bonus block (TAD §15.15) - the stat + equipment modifiers
   the server computed for this action, source-attributed. The numbers
   come from the server `modifiers` block; the client only displays. */
.bonus-block {
    margin-top: calc(var(--grid) * 2);
    padding: calc(var(--grid) * 2);
    background: var(--accent-soft);
    border: 1px solid var(--c-border);
    border-left: 2px solid var(--c-accent);
    border-radius: var(--radius-sm);
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.bonus-block.clinic-bonus { margin-bottom: calc(var(--grid) * 3); }
.bonus-head {
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--c-fg-dim);
    margin-bottom: 2px;
}
.bonus-line {
    display: flex;
    align-items: center;
    gap: var(--grid);
}
.bonus-tick {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--c-accent);
}
.bonus-tick svg { width: 12px; height: 12px; }
.bonus-text {
    font-size: 11px;
    color: var(--c-fg);
    font-variant-numeric: tabular-nums;
}

/* Run-card Integrity-risk line (TAD §15.18) - every run drains Integrity
   on a bad outcome; the player sees that risk before queuing. A muted
   caution row, not an alarm - the danger token tints the icon only. */
.risk-line {
    display: flex;
    align-items: center;
    gap: var(--grid);
    margin-top: calc(var(--grid) * 1.5);
}
.risk-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--c-danger);
}
.risk-icon svg { width: 13px; height: 13px; }
.risk-text {
    font-size: 11px;
    color: var(--c-fg-dim);
}
/* The exact per-outcome Integrity loss (TAD §15.18) - the danger token
   so the number reads as a cost, not a neutral stat. */
.risk-num {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--c-danger);
}
.risk-sep {
    font-size: 11px;
    color: var(--c-fg-dim);
}

/* ====================================================================
   Buttons (§7)
   ==================================================================== */

button {
    font-family: var(--font-mono);
    font-size: 12px;
    background: transparent;
    color: var(--c-fg);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    padding: calc(var(--grid) * 2) calc(var(--grid) * 3);
    cursor: pointer;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    transition: border-color 150ms, color 150ms, background 150ms, box-shadow 150ms;
}
button:hover:not(:disabled) {
    border-color: var(--c-sel);
    color: var(--c-sel);
}
button:disabled { opacity: 0.4; cursor: not-allowed; }

button.ghost { font-size: 11px; padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2); }

/* big-action primary - run / train / buy. A neutral white commit
   treatment: white text on a faint surface with a white border, no
   route-accent fill. The resting state is calm; hover lifts to the
   shared white-selection cue so the commit affordance stays legible. */
button.primary {
    background: var(--sel-soft);
    color: var(--c-sel);
    font-weight: 700;
    border-color: var(--c-border-strong);
}
button.primary:hover:not(:disabled) {
    border-color: var(--c-sel);
}
body.vis-full button.primary:hover:not(:disabled) {
    box-shadow: var(--sel-glow);
}
/* brief flash on commit (§7.4) - the action weight tell */
button.primary.flash { animation: btn-flash 220ms ease-out; }
@keyframes btn-flash {
    0%   { filter: brightness(2.2); }
    100% { filter: brightness(1); }
}

/* ====================================================================
   Structured list cards - left icon, content column, action slot
   ==================================================================== */

.card {
    display: flex;
    align-items: stretch;
    gap: calc(var(--grid) * 2.5);
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: calc(var(--grid) * 2.5);
    margin-bottom: calc(var(--grid) * 2);
    transition: border-color 150ms, box-shadow 150ms;
}
.card:last-child { margin-bottom: 0; }
.card.rig-card:hover,
.card.inv-row:hover { border-color: var(--c-sel); }

.card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    background: var(--c-bg-1);
    color: var(--c-accent);
}
.card-icon svg { width: 20px; height: 20px; }

.card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.card-title {
    display: flex;
    align-items: center;
    gap: var(--grid);
    font-weight: 700;
    color: var(--c-fg);
}
.card-title .badge {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--c-fg-dim);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: 3px 6px;
}
.card-cost {
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}
.card-cost strong { font-weight: 700; }
.card-cost .dim { color: var(--c-fg-dim); font-weight: 400; font-size: 11px; }
/* Resource cost colors - pinned to bar color tokens, not the route accent */
.card-cost .cost-stim      { color: var(--c-acid); }
.card-cost .cost-edge      { color: var(--c-magenta); }
.card-cost .cost-integrity { color: var(--c-success); }
.card-cost .cost-sync      { color: var(--c-cyan); }
.card-cost .cost-credit    { color: var(--c-amber); }
.card-sub {
    font-size: 11px;
    color: var(--c-fg-dim);
    font-variant-numeric: tabular-nums;
}
.card-desc {
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.4;
    color: var(--c-fg-dim);
}
.card-sub .dim, .card-cost .dim { color: var(--c-fg-dim); }

.card-action {
    display: flex;
    align-items: center;
    flex-shrink: 0;
}
.card-actions { display: flex; gap: var(--grid); align-items: center; }
.pick-tag {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-fg-dim);
    white-space: nowrap;
}

/* Pip meter - the run-card risk / success indicator */
.card-pip { margin-top: calc(var(--grid) * 1); }
.pips {
    display: inline-flex;
    align-items: center;
    gap: 3px;
}
.pip {
    width: 14px;
    height: 6px;
    border-radius: 1px;
    background: var(--c-bg-3);
    border: 1px solid var(--c-border);
}
.pips.safe .pip.on  { background: var(--c-acid);   border-color: var(--c-acid); }
.pips.mid .pip.on   { background: var(--c-amber);  border-color: var(--c-amber); }
.pips.risky .pip.on { background: var(--c-danger); border-color: var(--c-danger); }
.pip-pct {
    margin-left: calc(var(--grid) * 1.5);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Unaffordable card - a "cannot pay yet" cue. Desaturated + dimmed so it
   stays readable; the action button is disabled in JS. The Info button
   on shop cards stays usable, so the dim is partial, not a full block. */
.card.unaffordable {
    opacity: 0.55;
    filter: grayscale(0.85);
}
.card.unaffordable:hover { border-color: var(--c-border); }
.card.rig-card.unaffordable { cursor: not-allowed; }

/* Clinic rig cards act as the rig selector - selected = white highlight */
.card.rig-card { cursor: pointer; }
.card.rig-card.selected {
    border-color: var(--c-sel);
    background: var(--sel-soft);
}
body.vis-full .card.rig-card.selected { box-shadow: var(--sel-glow); }
.card.rig-card:hover .pick-tag,
.card.rig-card.selected .pick-tag { color: var(--c-sel); }

/* Inventory rows are click-to-inspect */
.card.inv-row { cursor: pointer; }
.card.inv-row:hover .pick-tag { color: var(--c-sel); }

/* ====================================================================
   Selects / forms (§8)
   ==================================================================== */

select {
    font-family: var(--font-mono);
    font-size: 12px;
    background: var(--c-bg-1);
    color: var(--c-fg);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
}
select:focus {
    outline: none;
    border-color: var(--c-accent);
    /* Inline the focus ring against the in-scope --c-accent: the baked
       --accent-focus token freezes the :root accent (same reason as the
       bloom rules above). */
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--c-accent) 30%, transparent);
}
.field-row { display: flex; gap: var(--grid); align-items: center; flex-wrap: wrap; }
.field-row label {
    font-size: 11px;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
/* Clinic input row - just STAT + TRAIN, comfortably spaced. */
.field-row.clinic-input {
    gap: calc(var(--grid) * 3);
    margin-bottom: calc(var(--grid) * 3);
}
.field-row.clinic-input select { min-width: 140px; }

/* ====================================================================
   Sort / filter bar - sort chips + a name-filter control
   ==================================================================== */

.sort-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--grid);
    margin-bottom: calc(var(--grid) * 2.5);
}
.sort-bar-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--c-fg-dim);
    margin-right: calc(var(--grid) * 1);
}
.sort-chip {
    font-family: var(--font-mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--c-bg-1);
    color: var(--c-fg-dim);
    border: 1px solid var(--c-border);
    border-radius: 999px;
    padding: calc(var(--grid) * 1) calc(var(--grid) * 2.5);
    cursor: pointer;
    transition: border-color 120ms, color 120ms, background 120ms;
}
.sort-chip:hover { color: var(--c-fg); border-color: var(--c-border-strong); }
.sort-chip.active {
    color: var(--c-sel);
    border-color: var(--c-sel);
    background: var(--sel-soft);
}
.sort-chip.active::after {
    content: attr(data-dir);
    margin-left: calc(var(--grid) * 1);
    font-size: 9px;
}

/* Filter control - a name-search box pushed to the right of the chips */
.sort-filter {
    display: inline-flex;
    align-items: center;
    gap: var(--grid);
    margin-left: auto;
    padding: calc(var(--grid) * 0.5) calc(var(--grid) * 1.5);
    border: 1px solid var(--c-border);
    border-radius: 999px;
    background: var(--c-bg-1);
}
.sort-filter:focus-within { border-color: var(--c-accent); }
.filter-icon { display: inline-flex; color: var(--c-fg-dim); }
.filter-icon svg { width: 14px; height: 14px; }
.sort-filter input {
    width: 86px;
    background: transparent;
    border: none;
    outline: none;
    color: var(--c-fg);
    font-family: var(--font-mono);
    font-size: 11px;
}
.sort-filter input::placeholder { color: var(--c-fg-disabled); }

/* ====================================================================
   Action queue - rows + the persistent ACTIVE QUEUE strip
   ==================================================================== */

/* ACTIVE QUEUE uses the shared .panel chrome; it flows as the last panel
   in the normal stack (directly after the view slot), so it takes the
   same horizontal inset and standard panel spacing - no bottom-anchor,
   no dead gap. Only a hide rule and the count badge are queue-specific. */
.panel.queue-strip { margin: 0 calc(var(--grid) * 3) calc(var(--grid) * 3); }
.queue-strip.empty { display: none; }
.qs-count {
    font-size: 10px;
    color: var(--c-amber);
    font-variant-numeric: tabular-nums;
}

.queue-row {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2);
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-left: 3px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
    margin-bottom: var(--grid);
}
.queue-row:last-child { margin-bottom: 0; }
.queue-row.pending { border-left-color: var(--c-amber); }
.queue-row.resolved { border-left-color: var(--c-success); }
.q-icon { display: inline-flex; flex-shrink: 0; }
.q-icon svg { width: 16px; height: 16px; }
.queue-row.pending .q-icon { color: var(--c-amber); }
.queue-row.resolved .q-icon { color: var(--c-success); }
.q-body {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--grid);
}
.q-kind { font-size: 12px; color: var(--c-fg); }
.queue-row .countdown {
    font-variant-numeric: tabular-nums;
    color: var(--c-amber);
}
.queue-row .outcome {
    text-transform: uppercase;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}
.outcome.success { color: var(--c-success); }
.outcome.partial { color: var(--c-warn); }
.outcome.fail, .outcome.disaster { color: var(--c-danger); }
.outcome.cancelled { color: var(--c-fg-dim); }
/* outcome + Integrity loss share one right-aligned group (§15.18). */
.q-outcome-group {
    display: flex;
    align-items: baseline;
    gap: var(--grid);
    flex-wrap: wrap;
    justify-content: flex-end;
}
/* The -N Integrity line on a bad run resolution - the negative-valued
   counterpart of the credit-gain readout. */
.integrity-loss {
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--c-danger);
    white-space: nowrap;
}
.queue-row.cancelled { border-left-color: var(--c-fg-disabled); }
.queue-row.cancelled .q-icon { color: var(--c-fg-dim); }

/* "Load 50 more" - the activity-queue lazy-load control */
.queue-more {
    display: block;
    width: 100%;
    margin-top: var(--grid);
    font-size: 11px;
    color: var(--c-fg-dim);
    border-color: var(--c-border);
}
.queue-more:hover:not(:disabled) {
    color: var(--c-sel);
    border-color: var(--c-sel);
}
.q-cancel {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
}
.q-cancel svg { width: 14px; height: 14px; }

/* ====================================================================
   Wallet activity log - the money ledger panel on Profile
   ==================================================================== */
.ledger-list { display: flex; flex-direction: column; gap: var(--grid); }
.ledger-row {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2);
    background: var(--c-bg-2);
    border: 1px solid var(--c-border);
    border-left: 3px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
}
.l-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.l-kind { font-size: 12px; color: var(--c-fg); }
.l-meta {
    font-size: 10px;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.l-deltas {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    flex-shrink: 0;
}
.l-delta {
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.l-delta.gain { color: var(--c-acid); }
.l-delta.loss { color: var(--c-loss); }
.l-delta.zero { color: var(--c-fg-dim); font-weight: 400; }
/* Tokens read distinctly from credits - a bordered chip in token cyan */
.l-delta.tokens {
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    border: 1px solid color-mix(in oklab, var(--c-cyan) 40%, var(--c-border));
}
.l-delta.tokens.gain { color: var(--c-cyan); }

/* glitch-reveal: a freshly resolved row flashes in (§5.1, §11.1) */
body.vis-full .queue-row.reveal { animation: glitch-reveal 480ms steps(2, end); }
body.vis-reduced .queue-row.reveal { animation: fade-reveal 200ms ease-out; }
@keyframes glitch-reveal {
    0%   { clip-path: inset(0 0 100% 0); transform: translateX(-6px); filter: brightness(2.4) hue-rotate(40deg); }
    20%  { clip-path: inset(0 0 0 0);    transform: translateX(5px);  filter: brightness(1.8); }
    40%  { transform: translateX(-3px); filter: brightness(1.4) hue-rotate(-20deg); }
    60%  { transform: translateX(2px);  filter: brightness(1.2); }
    100% { transform: translateX(0);    filter: brightness(1); }
}
@keyframes fade-reveal {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ====================================================================
   Resolve banner - big display numeric count-up (§11.1)
   ==================================================================== */

.resolve-banner {
    position: fixed;
    left: 50%;
    top: 18%;
    transform: translateX(-50%);
    z-index: 60;
    text-align: center;
    pointer-events: none;
    padding: calc(var(--grid) * 4) calc(var(--grid) * 6);
    background: color-mix(in oklab, var(--c-bg-1) 92%, transparent);
    border: 1px solid var(--c-accent);
    border-radius: var(--radius-md);
}
body.vis-full .resolve-banner {
    box-shadow: 0 0 12px var(--c-accent),
                0 0 32px color-mix(in oklab, var(--c-accent) 40%, transparent);
}
.resolve-banner .rb-label {
    font-size: 12px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--c-fg-dim);
}
.resolve-banner .rb-value {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 32px;
    color: var(--c-accent);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    margin-top: var(--grid);
}
.resolve-banner.enter { animation: rb-in 200ms ease-out; }
.resolve-banner.leave { animation: rb-out 260ms ease-in forwards; }
@keyframes rb-in {
    from { opacity: 0; transform: translateX(-50%) scale(0.92); }
    to   { opacity: 1; transform: translateX(-50%) scale(1); }
}
@keyframes rb-out {
    from { opacity: 1; }
    to   { opacity: 0; transform: translateX(-50%) scale(0.96); }
}

/* ====================================================================
   Toasts (§10) - top-right, slide-in
   ==================================================================== */

.toast-stack {
    position: fixed;
    top: calc(var(--topbar-h) + var(--grid) * 2 + env(safe-area-inset-top));
    right: calc(var(--grid) * 3);
    display: flex;
    flex-direction: column;
    gap: var(--grid);
    z-index: 70;
    max-width: min(340px, 86vw);
}
.toast {
    background: var(--c-bg-2);
    border: 1px solid var(--c-accent);
    border-left-width: 3px;
    color: var(--c-fg);
    padding: calc(var(--grid) * 2) calc(var(--grid) * 3);
    border-radius: var(--radius-sm);
    font-size: 12px;
    cursor: pointer;
    animation: toast-in 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.vis-full .toast {
    box-shadow: 0 0 6px color-mix(in oklab, var(--c-accent) 50%, transparent);
}
.toast.error { border-color: var(--c-danger); color: #ffd0d6; }
.toast.leave { animation: toast-out 160ms ease-in forwards; }
@keyframes toast-in {
    from { opacity: 0; transform: translateX(40px); }
    to   { opacity: 1; transform: translateX(0); }
}
@keyframes toast-out {
    from { opacity: 1; }
    to   { opacity: 0; transform: translateX(40px); }
}

.muted { color: var(--c-fg-dim); font-size: 12px; }
.center { text-align: center; padding: calc(var(--grid) * 6) 0; }
.error { color: var(--c-danger); }

/* ====================================================================
   Item detail popover - centered card over a click-to-dismiss backdrop
   ==================================================================== */

.popover-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: calc(var(--grid) * 4);
    background: color-mix(in oklab, #05060a 78%, transparent);
    animation: pop-fade 140ms ease-out;
}
.popover {
    background: var(--c-bg-2);
    border: 1px solid var(--c-accent);
    border-radius: var(--radius-md);
    padding: calc(var(--grid) * 4);
    max-width: min(420px, 92vw);
    width: 100%;
}
body.vis-full .popover {
    box-shadow: 0 0 6px color-mix(in oklab, var(--c-accent) 50%, transparent);
}
.popover-head {
    display: flex;
    align-items: center;
    gap: var(--grid);
    flex-wrap: wrap;
}
.popover-title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 20px;
    color: var(--c-accent);
}
.popover-cat {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-fg-dim);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: 3px calc(var(--grid) * 1.5);
}
.popover-desc {
    margin-top: calc(var(--grid) * 2);
    font-size: 12px;
    line-height: 1.5;
    color: var(--c-fg);
}
.popover-rows {
    margin-top: calc(var(--grid) * 3);
    display: flex;
    flex-direction: column;
}
.popover-row {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    padding: calc(var(--grid) * 1) 0;
    border-bottom: 1px solid var(--c-border);
}
.popover-row .k {
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.popover-row .v {
    color: var(--c-fg);
    font-variant-numeric: tabular-nums;
}
.popover-close { margin-top: calc(var(--grid) * 4); width: 100%; }
@keyframes pop-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ====================================================================
   "While you were away" resume modal (TAD §11.3, §15.21 C1)
   ==================================================================== */

.resume-modal {
    max-width: min(440px, 94vw);
    max-height: 88vh;
    overflow-y: auto;
}
.resume-span {
    margin-top: calc(var(--grid) * 2);
    font-size: 12px;
    line-height: 1.5;
    color: var(--c-fg-dim);
}
/* Aggregate headline - the bounded-set totals, big display numerics. */
.resume-agg {
    margin-top: calc(var(--grid) * 3);
    display: flex;
    flex-wrap: wrap;
    gap: var(--grid);
}
.resume-agg-cell {
    flex: 1 1 0;
    min-width: 80px;
    padding: calc(var(--grid) * 2);
    background: var(--c-bg-1);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    text-align: center;
}
.resume-agg-num {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 18px;
    color: var(--c-accent);
    font-variant-numeric: tabular-nums;
}
.resume-agg-cell.danger .resume-agg-num { color: var(--c-danger); }
.resume-agg-label {
    margin-top: calc(var(--grid) * 0.5);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-fg-dim);
}
/* The bounded resolved list - one row per resolved action. */
.resume-list {
    margin-top: calc(var(--grid) * 3);
    display: flex;
    flex-direction: column;
    gap: var(--grid);
}
.resume-row {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
    background: var(--c-bg-1);
    border-left: 2px solid var(--c-accent);
    border-radius: var(--radius-sm);
}
/* Glitch-reveal the resolved rows in (§3.5 batched resume reveal). The
   resume modal reuses the live queue's glitch keyframes. */
body.vis-full .resume-row.reveal {
    animation: glitch-reveal 480ms steps(2, end) both;
}
body.vis-reduced .resume-row.reveal {
    animation: fade-reveal 220ms ease-out both;
}
.resume-row-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--c-accent);
}
.resume-row-icon svg { width: 15px; height: 15px; }
.resume-row-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.resume-row-name {
    font-size: 12px;
    color: var(--c-fg);
}
.resume-row-line {
    font-size: 11px;
    color: var(--c-fg-dim);
    font-variant-numeric: tabular-nums;
}
.resume-seeall { margin-top: calc(var(--grid) * 2); width: 100%; }
/* The one server-picked recommendation - the §11.3 "do this next" line. */
.resume-rec {
    margin-top: calc(var(--grid) * 3);
    padding: calc(var(--grid) * 3);
    background: var(--c-bg-1);
    border: 1px solid var(--c-accent);
    border-radius: var(--radius-sm);
}
body.vis-full .resume-rec {
    box-shadow: inset 0 0 12px color-mix(in oklab, var(--c-accent) 14%, transparent);
}
.resume-rec-head {
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--c-fg-dim);
}
.resume-rec-name {
    margin-top: calc(var(--grid) * 0.5);
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 16px;
    color: var(--c-accent);
}
.resume-rec-reason {
    margin-top: var(--grid);
    font-size: 11px;
    line-height: 1.4;
    color: var(--c-fg-dim);
}
.resume-rec-go { margin-top: calc(var(--grid) * 2.5); width: 100%; }

/* Hamburger menu popover - a short list of large tap targets (Settings,
   Info). Mobile-first: every row is a full-width button. */
.menu-list {
    margin-top: calc(var(--grid) * 3);
    display: flex;
    flex-direction: column;
    gap: var(--grid);
}
.menu-item {
    display: flex;
    align-items: center;
    gap: calc(var(--grid) * 2.5);
    width: 100%;
    text-align: left;
    text-transform: none;
    letter-spacing: 0;
    padding: calc(var(--grid) * 2.5) calc(var(--grid) * 3);
    background: var(--c-bg-1);
    border: 1px solid var(--c-border);
}
.menu-item:hover:not(:disabled) {
    border-color: var(--c-sel);
    color: var(--c-fg);
}
.menu-item-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--c-accent);
}
.menu-item-icon svg { width: 20px; height: 20px; }
.menu-item-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.menu-item-label {
    font-size: 13px;
    font-weight: 700;
    color: var(--c-fg);
}
.menu-item-sub {
    font-size: 10px;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Settings popover - the display-options row hosting the visuals toggle. */
.settings-row {
    margin-top: calc(var(--grid) * 3);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: calc(var(--grid) * 3);
    flex-wrap: wrap;
    padding: calc(var(--grid) * 2.5) calc(var(--grid) * 3);
    background: var(--c-bg-1);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
}
.settings-row-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.settings-row-label {
    font-size: 12px;
    font-weight: 700;
    color: var(--c-fg);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.settings-row-sub {
    font-size: 10px;
    color: var(--c-fg-dim);
}
.settings-row .vis-toggle { flex-shrink: 0; }

/* Notifications block in the Settings popup - a section header above the
   per-category opt-in rows (§15.21 C3). */
.settings-section-head {
    margin-top: calc(var(--grid) * 4);
    font-size: 11px;
    font-weight: 700;
    color: var(--c-fg-dim);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.settings-notify { display: flex; flex-direction: column; }

/* The On/Off notification toggle - a single button, min 44px tall so it
   is a comfortable tap target on mobile (hover is enhancement only). */
.notify-toggle {
    flex-shrink: 0;
    min-width: 56px;
    min-height: 44px;
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-fg-dim);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.notify-toggle:hover:not(:disabled) {
    color: var(--c-fg);
    border-color: var(--c-border-strong);
}
.notify-toggle[aria-pressed="true"] {
    background: var(--sel-soft);
    border-color: var(--c-sel);
    color: var(--c-sel);
}
.notify-toggle:disabled { opacity: 0.5; }

/* ====================================================================
   Inventory + Equipment views (TAD §15.16, §15.17)
   ==================================================================== */

/* Inventory deep-link filter pill - shown when an empty Equipment slot
   routed here filtered to a category. Tap-clearable (no hover path). */
.inv-filter-pill {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--grid);
    padding: calc(var(--grid) * 2) calc(var(--grid) * 3);
    margin-bottom: calc(var(--grid) * 3);
    background: var(--accent-soft);
    border: 1px solid var(--c-accent);
    border-radius: var(--radius-md);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--c-fg);
}

/* Inventory cards carry three actions (Inspect, Equip, Sell) and the
   Sell button shows a sell-value preview, so the action row needs room
   it does not have beside the body on a phone. Stack it: the card wraps,
   icon + body share the top row, the action slot drops to its own
   full-width line below. Mobile-first - every control stays tappable. */
.card.inv-row { flex-wrap: wrap; }
.card.inv-row .card-body { flex: 1 1 60%; }
.card.inv-row .card-action {
    flex: 1 0 100%;
    margin-top: calc(var(--grid) * 2);
    padding-top: calc(var(--grid) * 2);
    border-top: 1px solid var(--c-border);
}
.card.inv-row .card-actions {
    width: 100%;
    justify-content: flex-end;
    flex-wrap: wrap;
}
/* The inventory card's Sell button - a primary commit-style action. It
   carries a sell-value preview, so let it size to its content. */
.card-actions .sell-btn { white-space: nowrap; }

/* Rarity chip - the item's rarity tier, color-keyed off the dedicated
   --c-rarity-* ramp. A tinted, bordered pill in the card title row. */
.rarity-chip {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    border-radius: var(--radius-sm);
    padding: 3px 6px;
    color: var(--c-rarity-common);
    border: 1px solid color-mix(in oklab, var(--c-rarity-common) 45%, var(--c-border));
    background: color-mix(in oklab, var(--c-rarity-common) 12%, var(--c-bg-1));
}
.rarity-chip.rarity-uncommon {
    color: var(--c-rarity-uncommon);
    border-color: color-mix(in oklab, var(--c-rarity-uncommon) 45%, var(--c-border));
    background: color-mix(in oklab, var(--c-rarity-uncommon) 12%, var(--c-bg-1));
}
.rarity-chip.rarity-rare {
    color: var(--c-rarity-rare);
    border-color: color-mix(in oklab, var(--c-rarity-rare) 45%, var(--c-border));
    background: color-mix(in oklab, var(--c-rarity-rare) 12%, var(--c-bg-1));
}
.rarity-chip.rarity-epic {
    color: var(--c-rarity-epic);
    border-color: color-mix(in oklab, var(--c-rarity-epic) 45%, var(--c-border));
    background: color-mix(in oklab, var(--c-rarity-epic) 12%, var(--c-bg-1));
}
.rarity-chip.rarity-legendary {
    color: var(--c-rarity-legendary);
    border-color: color-mix(in oklab, var(--c-rarity-legendary) 45%, var(--c-border));
    background: color-mix(in oklab, var(--c-rarity-legendary) 12%, var(--c-bg-1));
}

/* Inventory card rarity-keyed left edge - mirrors the chip color so the
   rarity reads at a glance down the list. */
.card.rarity-edge-common    { border-left: 3px solid var(--c-rarity-common); }
.card.rarity-edge-uncommon  { border-left: 3px solid var(--c-rarity-uncommon); }
.card.rarity-edge-rare      { border-left: 3px solid var(--c-rarity-rare); }
.card.rarity-edge-epic      { border-left: 3px solid var(--c-rarity-epic); }
.card.rarity-edge-legendary { border-left: 3px solid var(--c-rarity-legendary); }

/* Inventory grant line - the equippable item's stat grants, mint-keyed
   like the equipment-screen grant readout. */
.card-sub.inv-grants .grant-on {
    color: var(--c-mint);
    font-weight: 700;
}
.card-sub.inv-grants .dim { color: var(--c-fg-dim); }

/* Equipment screen ---------------------------------------------------- */

.eq-section-title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--c-accent);
    margin-bottom: calc(var(--grid) * 2);
}

/* Effective-stats summary block - per stat base + equipped = effective */
.eq-summary {
    padding: calc(var(--grid) * 3);
    margin-bottom: calc(var(--grid) * 3);
    background: var(--c-bg-1);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
}
.eff-stats { display: flex; flex-direction: column; gap: var(--grid); }
.eff-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--grid);
    padding: calc(var(--grid) * 1.5) calc(var(--grid) * 2);
    background: var(--c-bg-2);
    border-radius: var(--radius-sm);
}
.eff-name {
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 11px;
    color: var(--c-fg-dim);
}
.eff-calc { font-variant-numeric: tabular-nums; font-size: 13px; }
.eff-base  { color: var(--c-fg); }
.eff-op    { color: var(--c-fg-dim); }
.eff-bonus { color: var(--c-fg-dim); }
.eff-bonus.on { color: var(--c-mint); font-weight: 700; }
.eff-total { color: var(--c-accent); font-weight: 700; }

/* Contribution-by-item itemization (§15.15 source attribution) */
.eff-contrib { margin-top: calc(var(--grid) * 3); }
.eff-contrib-head {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-fg-dim);
    margin-bottom: var(--grid);
}
.eff-contrib-row {
    display: flex;
    justify-content: space-between;
    gap: var(--grid);
    padding: calc(var(--grid) * 1) 0;
    font-size: 12px;
}
.ec-name { color: var(--c-fg); }
.ec-vals { color: var(--c-mint); font-variant-numeric: tabular-nums; }

/* The four-slot rig - one slot card per row, shared .card chrome */
.eq-rig { display: flex; flex-direction: column; gap: 0; }
.card.equip-slot.empty { border-style: dashed; }
.card.equip-slot.empty .card-icon { color: var(--c-fg-dim); }
.card.equip-slot.filled { border-color: var(--c-border-strong); }
.equip-grants .grant-on { color: var(--c-mint); font-weight: 700; }

/* ====================================================================
   Login (standalone web)
   ==================================================================== */

.login-box { text-align: center; padding: calc(var(--grid) * 8) 0; }
.login-box h1 {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: clamp(36px, 9vw, 56px);
    color: var(--c-accent);
    margin-bottom: calc(var(--grid) * 3);
}

/* ====================================================================
   Motion kill-switch (§5, §6) - reduced-motion forces at least Reduced
   ==================================================================== */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
    .fx-grid { animation: none; }
}
