/* APHELION — Improved Contrast Palette v2.0
 * 
 * CONTRAST FIXES APPLIED:
 * 1. Background layers: ΔL increased from 0.04 to 0.08+ for clear depth
 * 2. Text hierarchy: Stepped at 0.15-0.20 L intervals  
 * 3. Faction colors: Spread across L=0.45 to L=0.85 spectrum
 * 4. Enemy differentiation: Each enemy gets unique L+C+h signature
 * 5. Grid visibility: Slightly more visible for map readability
 * 6. Success/danger: Explicit tokens for combat feedback
 */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: #0b0c0e;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  color: #e8eaed;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

/* ─── Bottom inset for the NavRail and anything anchored above it ───
 * Chrome on Android in a regular browser tab reports
 * env(safe-area-inset-bottom) as 0 even when the OS draws a gesture
 * indicator overlay on top of the page, which would otherwise clip
 * the NavRail's label row. The default branch enforces a 28px floor
 * so labels stay clear on browser tabs regardless of what env()
 * returns. When the same page is installed as a PWA or wrapped by
 * Capacitor, display-mode flips to `standalone` and the OS *does*
 * report a real safe-area-inset-bottom, so the floor would just add
 * unnecessary whitespace — drop it and trust env() there.
 *
 * Pure JS detection (window.visualViewport, UA sniff) doesn't help:
 * visualViewport tracks the top URL bar, not the bottom gesture bar,
 * and UA strings can't be trusted. The display-mode signal is the
 * only reliable handle to "is this a browser tab vs a launched app". */
:root {
  --nav-bottom-inset: max(env(safe-area-inset-bottom, 0px), 28px);
  /* --panel was referenced by several modals/cards (event cards, swarm/wave
     cards, dropdowns) but never defined, so var(--panel) resolved invalid →
     transparent, making them unreadable. Alias it to the opaque elevated panel
     colour; resolves per-theme via the active --bg-elev. */
  --panel: var(--bg-elev, oklch(0.17 0.007 250));
}
@media (display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui) {
  :root {
    --nav-bottom-inset: env(safe-area-inset-bottom, 0px);
  }
}

/* ═══════════════════════════════════════════════════════════════
   THEME: GRAPHITE — High Contrast Revision
   ═══════════════════════════════════════════════════════════════ */
[data-theme="graphite"] {
  /* ─── Background Depth Hierarchy ───
   * Each layer separated by ΔL ≥ 0.08 for clear elevation.
   * Phase 12 retune: hue 240° → 250° kills the OLED purple cast; line
   * chroma bumped so borders don't look stamped under low ambient light.
   */
  --bg:         oklch(0.10 0.005 250);   /* Less violet, more void */
  --bg-elev:    oklch(0.17 0.007 250);
  --bg-elev-2:  oklch(0.25 0.009 250);
  --line:       oklch(0.40 0.012 250);   /* Bumped chroma 0.014 → 0.012 hue 250° */
  --line-strong: oklch(0.55 0.018 250);

  /* ─── Text Stepped Hierarchy ───
   * Clear 0.15-0.20 L steps for instant readability levels
   */
  --text:       oklch(0.95 0.005 240);   /* Primary — near white */
  --text-mid:   oklch(0.75 0.008 240);   /* Secondary — labels */
  --text-dim:   oklch(0.55 0.010 240);   /* Tertiary — timestamps */
  --text-faint: oklch(0.38 0.012 240);   /* Quaternary — disabled */

  /* ─── Gameplay Colors — Spread Across Lightness Spectrum ───
   * YOU:    L=0.78 bright cyan — immediately identifiable
   * ALLY:   L=0.72 green — friendly, distinct from cyan
   * ENEMY:  L=0.65 red — aggressive, mid-bright
   * ENEMY2: L=0.45 purple — dark threat, clearly different
   * ENEMY3: L=0.85 amber — light warning, stands out
   * NEUTRAL:L=0.60 gray — unaligned, muted
   */
  --accent:     oklch(0.78 0.16 195);    /* Cyan — brighter, more saturated */
  --you:        oklch(0.78 0.16 195);    /* YOUR fleet — pops against dark bg */
  --ally:       oklch(0.72 0.14 145);    /* Green — friendly distinct hue */
  --enemy:      oklch(0.65 0.20 25);     /* Red — high chroma aggression */
  --swarm:      oklch(0.88 0.24 128);    /* Toxic lime — alien infestation */
  --enemy-2:    oklch(0.55 0.20 305);    /* Purple — mid-bright, mysterious */
  --enemy-3:    oklch(0.85 0.16 85);     /* Amber — light, urgent */
  --enemy-4:    oklch(0.62 0.18 250);    /* Blue — cool threat */
  --enemy-5:    oklch(0.72 0.20 350);    /* Pink — bright, distinct */
  --enemy-6:    oklch(0.72 0.20 55);     /* Orange — warm hostile */
  --enemy-7:    oklch(0.70 0.14 175);    /* Teal — sea-green threat */
  --enemy-8:    oklch(0.65 0.24 330);    /* Magenta — vivid signal */
  --enemy-9:    oklch(0.58 0.22 280);    /* Violet — deep purple-blue */
  --neutral:    oklch(0.60 0.012 240);   /* Gray — truly neutral */
  --warn:       oklch(0.82 0.18 75);     /* Bright amber alerts */

  /* ─── Utility ─── */
  --fog:        oklch(0.10 0.008 240 / 0.92);
  --grid:       oklch(0.30 0.010 240 / 0.35);  /* More visible grid */

  /* ─── Semantic Feedback ─── */
  --success:    oklch(0.75 0.16 145);     /* Victory, reinforcements */
  --danger:     oklch(0.65 0.20 25);     /* Defeat, alerts — matches enemy */

  /* ─── Theme Polish Tokens (Phase 12 — visual overhaul) ───
   * Defaults match the current hardcoded literals in src/app.jsx and
   * APHELION.html so Step 1 is visual-diff-zero. Step 3 retunes per
   * theme; Step 4 wires the flourish tokens (glow-accent, shadow-*)
   * into hover / modal / button CSS.
   */
  --accent-dim:     oklch(0.55 0.12 195);          /* Lower-L accent for halos */
  --map-fog:        oklch(0 0 0 / 0.55);            /* Was rgba(0,0,0,0.55) in StarMap */
  --shadow-color:   oklch(0 0 0 / 0.45);            /* Base shadow tint for box-shadow stacks */
  --shadow-sm:      0 1px 3px oklch(0 0 0 / 0.35);
  --shadow-md:      0 2px 12px oklch(0 0 0 / 0.35), 0 1px 3px oklch(0 0 0 / 0.2);
  --shadow-lg:      0 30px 80px oklch(0 0 0 / 0.6), 0 0 0 1px oklch(1 0 0 / 0.08);
  --glow-accent:    oklch(0.78 0.16 195 / 0.40);    /* Subtle accent halo on hover */
  --success-bg:     oklch(0.75 0.16 145 / 0.18);    /* Replaces rgba(26,74,42,0.22) */
  --danger-bg:      oklch(0.65 0.20 25 / 0.18);     /* Replaces rgba(122,24,24,0.10) */
  --warn-bg:        oklch(0.82 0.18 75 / 0.18);
  --info:           oklch(0.78 0.16 230);           /* FR ship class — replaces #6cf */
  --overlay-scrim:  oklch(0 0 0 / 0.50);            /* Replaces rgba(0,0,0,0.5) modal scrim */
}

/* ═══════════════════════════════════════════════════════════════
   THEME: BONE — Light Mode with Proper Contrast
   ═══════════════════════════════════════════════════════════════ */
[data-theme="bone"] {
  /* Aged-ivory base. Hue 85° lands on true bone-yellow (think ivory
   * piano keys, old skull); chroma 0.025 gives it a tangible paper
   * cast instead of "neutral light mode". Each elev layer pulls
   * warmer + slightly darker so depth reads as stacked vellum. */
  --bg:         oklch(0.93 0.025 85);
  --bg-elev:    oklch(0.87 0.030 82);
  --bg-elev-2:  oklch(0.79 0.034 78);
  --line:       oklch(0.55 0.040 70);    /* Sepia ink lines */
  --line-strong: oklch(0.38 0.050 60);

  /* Ink-on-bone text. Hue shifted to 55° (sepia/brown ink) so the
   * text feels written rather than typeset. L=0.18 is near-black
   * but warm — like dried iron-gall ink. */
  --text:       oklch(0.18 0.040 55);
  --text-mid:   oklch(0.34 0.045 58);    /* AA-safe against L=0.93 bg */
  --text-dim:   oklch(0.50 0.040 62);
  --text-faint: oklch(0.65 0.035 68);

  /* Faction colors: inked-stamp palette. Chroma 0.08–0.12 (down from
   * 0.14–0.18 in the dark themes) so factions read as dried inks on
   * parchment — present, distinguishable by hue, but never neon. The
   * map's atmosphere bubbles and the StarDetail title both pull from
   * these, so taming chroma here is what kills the "too bright" cast
   * on system names without code changes. */
  --accent:     oklch(0.46 0.12 42);     /* Burnt sienna — inked seal */
  --you:        oklch(0.46 0.12 42);     /* Match accent so YOU reads as the rust signature */
  --ally:       oklch(0.44 0.09 155);    /* Oxidised-copper green */
  --enemy:      oklch(0.44 0.13 22);     /* Dried-blood red */
  --swarm:      oklch(0.62 0.18 128);    /* Sickly moss lime — infestation */
  --enemy-2:    oklch(0.38 0.10 295);    /* Indigo-ink purple */
  --enemy-3:    oklch(0.55 0.10 68);     /* Mustard ochre */
  --enemy-4:    oklch(0.42 0.10 245);    /* Prussian blue */
  --enemy-5:    oklch(0.48 0.11 355);    /* Madder rose */
  --enemy-6:    oklch(0.52 0.12 48);     /* Burnt umber */
  --enemy-7:    oklch(0.46 0.08 180);    /* Verdigris teal */
  --enemy-8:    oklch(0.46 0.12 325);    /* Tyrian violet */
  --enemy-9:    oklch(0.40 0.10 270);    /* Iron-gall indigo */
  --neutral:    oklch(0.50 0.028 65);    /* Aged paper grey */
  --warn:       oklch(0.55 0.13 65);     /* Mustard warning */

  --fog:        oklch(0.92 0.020 80 / 0.86);
  --grid:       oklch(0.55 0.030 70 / 0.32);  /* Ruled-paper sepia */

  --success:    oklch(0.46 0.10 150);
  --danger:     oklch(0.44 0.13 22);

  /* Map canvas paper tone. Slightly darker + warmer than --bg so the
   * play area reads as an inset/aged region of the document rather
   * than vanishing into the frame chrome around it. Picked up by the
   * SVG map base rect via `fill: var(--map-bg, #000)` — dark themes
   * fall back to the #000 default. */
  --map-bg:     oklch(0.86 0.034 80);

  /* Map label outline — drawn UNDER the fill via paint-order="stroke"
   * on the SVG <text> elements that render system names and garrison
   * counts. Dark themes leave this unset and the JSX fallback paints
   * transparent (no outline). On bone, a dark sepia stroke gives the
   * labels enough edge contrast to stay readable against parchment
   * without dimming the faction-coloured fill itself. */
  --map-label-stroke: oklch(0.25 0.030 60 / 0.85);

  /* ─── Phase 12 polish tokens — bone variant ───
   * Shadows are very light + warm — paper on paper barely casts.
   * Vignette intensity lives in --bone-vignette (consumed by the
   * .aphelion-frame::after rule lower in this file). */
  --accent-dim:     oklch(0.34 0.09 42);
  --map-fog:        oklch(0.38 0.040 65 / 0.42);    /* Sepia ink wash — slightly heavier to read against parchment map */
  --shadow-color:   oklch(0.30 0.040 60 / 0.16);    /* Warm sepia tint */
  --shadow-sm:      0 1px 2px oklch(0.30 0.040 60 / 0.10);
  --shadow-md:      0 2px 6px oklch(0.30 0.040 60 / 0.12), 0 1px 2px oklch(0.25 0.040 60 / 0.06);
  --shadow-lg:      0 18px 48px oklch(0.25 0.040 60 / 0.16), 0 0 0 1px oklch(0.35 0.040 60 / 0.10);
  --glow-accent:    oklch(0.46 0.16 42 / 0.0);      /* No glow — paper aesthetic */
  --success-bg:     oklch(0.46 0.10 150 / 0.12);
  --danger-bg:      oklch(0.44 0.13 22 / 0.12);
  --warn-bg:        oklch(0.55 0.13 65 / 0.14);
  --info:           oklch(0.42 0.10 245);           /* Prussian blue info — sits with the inked palette */
  --overlay-scrim:  oklch(0.28 0.040 60 / 0.42);    /* Sepia scrim */

  /* Per-theme custom props consumed by flourish rules below */
  --bone-vignette:  0.18;                           /* Edge-darkening strength */
  --bone-noise:     0.10;                           /* Paper-grain opacity */
}

/* ═══════════════════════════════════════════════════════════════
   THEME: PHOSPHOR — CRT Terminal with Maximum Contrast
   ═══════════════════════════════════════════════════════════════ */
[data-theme="phosphor"] {
  /* Phosphor green monochrome with high contrast.
   * Phase 12 retune: hue 145° → 142° (true green); text-mid chroma
   * 0.14 → 0.10 for long-read comfort; accent L 0.88 → 0.90 to match.
   */
  --bg:         oklch(0.07 0.022 142);   /* Pushed toward true green */
  --bg-elev:    oklch(0.15 0.028 142);
  --bg-elev-2:  oklch(0.23 0.033 142);
  --line:       oklch(0.45 0.060 142);
  --line-strong: oklch(0.65 0.080 142);

  /* Text: Phosphor glow intensity — chroma stepped down for comfort */
  --text:       oklch(0.92 0.18 142);     /* Pure phosphor */
  --text-mid:   oklch(0.72 0.10 142);     /* Less searing for long reads */
  --text-dim:   oklch(0.50 0.07 142);
  --text-faint: oklch(0.35 0.05 142);

  /* Factions: Phosphor green variants with hue shifts */
  --accent:     oklch(0.90 0.22 142);     /* Brighter, true green */
  --you:        oklch(0.88 0.20 145);     /* Brightest — YOU */
  --ally:       oklch(0.78 0.16 175);     /* Cyan-tinted green */
  --enemy:      oklch(0.70 0.22 60);      /* Amber warning */
  --swarm:      oklch(0.90 0.26 132);     /* Toxic lime — alien infestation */
  --enemy-2:    oklch(0.55 0.18 120);     /* Yellow-green */
  --enemy-3:    oklch(0.82 0.16 90);      /* Light amber */
  --enemy-4:    oklch(0.65 0.16 35);      /* Red-orange */
  --enemy-5:    oklch(0.60 0.14 200);     /* Cyan-tinted threat */
  --enemy-6:    oklch(0.75 0.18 75);      /* Bright amber-orange */
  --enemy-7:    oklch(0.50 0.14 160);     /* Phosphor teal-green */
  --enemy-8:    oklch(0.78 0.18 105);     /* Bright yellow-green */
  --enemy-9:    oklch(0.62 0.16 50);      /* Warm phosphor amber */
  --neutral:    oklch(0.55 0.10 145);     /* Muted phosphor */
  --warn:       oklch(0.85 0.18 95);      /* Bright amber */

  --fog:        oklch(0.08 0.025 145 / 0.92);
  --grid:       oklch(0.35 0.040 145 / 0.45); /* Visible scan lines */

  --success:    oklch(0.80 0.18 145);
  --danger:     oklch(0.70 0.22 60);

  /* ─── Phase 12 polish tokens — phosphor variant ─── */
  --accent-dim:     oklch(0.65 0.14 145);
  --map-fog:        oklch(0.04 0.020 145 / 0.55);   /* Slightly green-tinted near-black */
  --shadow-color:   oklch(0.04 0.020 145 / 0.55);   /* Bg-coloured shadow → "glow swallowed by void" */
  --shadow-sm:      0 1px 3px oklch(0.04 0.020 145 / 0.45);
  --shadow-md:      0 2px 12px oklch(0.04 0.020 145 / 0.50), 0 1px 3px oklch(0.04 0.020 145 / 0.30);
  --shadow-lg:      0 24px 60px oklch(0.04 0.020 145 / 0.70), 0 0 0 1px oklch(0.65 0.080 145 / 0.20);
  --glow-accent:    oklch(0.88 0.20 145 / 0.55);    /* Strong phosphor glow on hover */
  --success-bg:     oklch(0.80 0.18 145 / 0.20);
  --danger-bg:      oklch(0.70 0.22 60 / 0.20);
  --warn-bg:        oklch(0.85 0.18 95 / 0.20);
  --info:           oklch(0.78 0.16 175);           /* Cyan-tinted for phosphor palette */
  --overlay-scrim:  oklch(0.04 0.020 145 / 0.55);
}

/* ═══════════════════════════════════════════════════════════════
   PREMIUM THEME PACK (Phase 11 — gated by `themePack1` entitlement)
   ═══════════════════════════════════════════════════════════════ */

[data-theme="ember"] {
  /* Warm-amber CRT — phosphor's hot cousin. Phase 12 retune: hue
   * 50° → 45° (true amber from warm-orange); accent pushed to
   * C=0.24 / L=0.80 for proper ember heat; text chroma matches.
   * line-strong heated to make active edges feel hot. */
  --bg:         oklch(0.07 0.028 45);
  --bg-elev:    oklch(0.15 0.040 45);
  --bg-elev-2:  oklch(0.23 0.045 45);
  --line:       oklch(0.42 0.080 45);
  --line-strong: oklch(0.60 0.140 50);   /* Heated active edges */
  --fog:        oklch(0.15 0.040 45 / 0.85);
  --accent:     oklch(0.80 0.24 55);     /* Ember-orange peak */
  --accent-dim: oklch(0.55 0.18 55);
  --text:       oklch(0.92 0.18 55);
  --text-mid:   oklch(0.72 0.14 55);
  --text-dim:   oklch(0.50 0.10 55);
  --text-faint: oklch(0.35 0.08 55);
  --success:    oklch(0.75 0.16 130);
  --danger:     oklch(0.68 0.20 30);

  /* ─── Phase 12 polish tokens — ember variant ─── */
  --grid:           oklch(0.30 0.050 50 / 0.40);    /* Amber-tinted grid */
  --map-fog:        oklch(0.05 0.025 50 / 0.55);    /* Warm-tinted near-black */
  --shadow-color:   oklch(0.05 0.025 50 / 0.55);    /* Bg-coloured shadow */
  --shadow-sm:      0 1px 3px oklch(0.05 0.025 50 / 0.45);
  --shadow-md:      0 2px 12px oklch(0.05 0.025 50 / 0.50), 0 1px 3px oklch(0.05 0.025 50 / 0.30);
  --shadow-lg:      0 24px 60px oklch(0.05 0.025 50 / 0.70), 0 0 0 1px oklch(0.60 0.140 50 / 0.20);
  --glow-accent:    oklch(0.78 0.22 50 / 0.55);     /* Strong amber glow */
  --success-bg:     oklch(0.75 0.16 130 / 0.20);
  --danger-bg:      oklch(0.68 0.20 30 / 0.22);
  --warn-bg:        oklch(0.82 0.20 70 / 0.20);
  --info:           oklch(0.75 0.16 200);           /* Cool counterpoint to the amber bg */
  --overlay-scrim:  oklch(0.05 0.025 50 / 0.55);
}

[data-theme="void"] {
  /* Cold near-black with cyan accent.
   * Phase 12 retune: bg L 0.06 → 0.05 / hue 260° → 255° (deeper,
   * truer blue-black); line chroma bumped for visible structure;
   * accent more saturated cyan punctuation. */
  --bg:         oklch(0.05 0.012 255);
  --bg-elev:    oklch(0.13 0.016 255);
  --bg-elev-2:  oklch(0.22 0.020 255);
  --line:       oklch(0.42 0.040 255);   /* More visible structure */
  --line-strong: oklch(0.58 0.070 255);
  --fog:        oklch(0.13 0.016 255 / 0.85);
  --accent:     oklch(0.78 0.20 215);    /* Saturated cyan stylus */
  --accent-dim: oklch(0.52 0.14 215);
  --text:       oklch(0.95 0.010 260);
  --text-mid:   oklch(0.70 0.014 260);
  --text-dim:   oklch(0.50 0.018 260);
  --text-faint: oklch(0.36 0.020 260);
  --success:    oklch(0.72 0.14 165);
  --danger:     oklch(0.65 0.20 25);

  /* ─── Phase 12 polish tokens — void variant ─── */
  --grid:           oklch(0.25 0.020 260 / 0.40);
  --map-fog:        oklch(0 0 0 / 0.65);            /* Deeper black for void */
  --shadow-color:   oklch(0 0 0 / 0.55);            /* Pure black, premium-minimal */
  --shadow-sm:      0 1px 2px oklch(0 0 0 / 0.40);
  --shadow-md:      0 2px 10px oklch(0 0 0 / 0.50), 0 1px 2px oklch(0 0 0 / 0.25);
  --shadow-lg:      0 24px 60px oklch(0 0 0 / 0.70), 0 0 0 1px oklch(0.52 0.050 255 / 0.18);
  --glow-accent:    oklch(0 0 0 / 0);               /* Zero glow — the polish IS the restraint */
  --success-bg:     oklch(0.72 0.14 165 / 0.16);
  --danger-bg:      oklch(0.65 0.20 25 / 0.16);
  --warn-bg:        oklch(0.78 0.18 75 / 0.16);
  --info:           oklch(0.78 0.20 215);           /* Same cyan stylus as accent */
  --overlay-scrim:  oklch(0 0 0 / 0.65);
}

[data-theme="cobalt"] {
  /* Mid-saturation cobalt blue. Phase 12 retune: matte aesthetic —
   * chroma lives in --bg-elev (panels carry the colour), not --bg
   * (calmer base); --accent desaturated for matte feel. */
  --bg:         oklch(0.13 0.030 245);   /* Calmer base */
  --bg-elev:    oklch(0.22 0.060 245);   /* Panels pop the blue */
  --bg-elev-2:  oklch(0.30 0.070 245);
  --line:       oklch(0.46 0.090 245);
  --line-strong: oklch(0.62 0.120 245);
  --fog:        oklch(0.22 0.060 245 / 0.85);
  --accent:     oklch(0.78 0.16 235);    /* Slightly desaturated — matte */
  --accent-dim: oklch(0.56 0.12 235);
  --text:       oklch(0.94 0.020 245);
  --text-mid:   oklch(0.74 0.030 245);
  --text-dim:   oklch(0.55 0.040 245);
  --text-faint: oklch(0.38 0.050 245);
  --success:    oklch(0.75 0.15 155);
  --danger:     oklch(0.66 0.20 25);

  /* ─── Phase 12 polish tokens — cobalt variant ─── */
  --grid:           oklch(0.35 0.060 245 / 0.45);
  --map-fog:        oklch(0.04 0.030 245 / 0.55);   /* Blue-tinted near-black */
  --shadow-color:   oklch(0.04 0.030 245 / 0.45);
  --shadow-sm:      0 1px 3px oklch(0.04 0.030 245 / 0.35);
  --shadow-md:      0 2px 12px oklch(0.04 0.030 245 / 0.40), 0 1px 3px oklch(0.04 0.030 245 / 0.20);
  --shadow-lg:      0 24px 60px oklch(0.04 0.030 245 / 0.55), 0 0 0 1px oklch(0.62 0.120 245 / 0.20);
  --glow-accent:    oklch(0 0 0 / 0);               /* No glow — matte */
  --success-bg:     oklch(0.75 0.15 155 / 0.20);
  --danger-bg:      oklch(0.66 0.20 25 / 0.20);
  --warn-bg:        oklch(0.78 0.18 75 / 0.20);
  --info:           oklch(0.78 0.18 215);
  --overlay-scrim:  oklch(0.04 0.030 245 / 0.50);
}

/* ═══════════════════════════════════════════════════════════════
   DENSITY MODIFIERS
   ═══════════════════════════════════════════════════════════════ */
[data-density="compact"] { 
  --pad: 8px; --pad-lg: 12px; --row-h: 28px; 
  --type: 12px; --type-sm: 10px; 
}
[data-density="regular"] { 
  --pad: 10px; --pad-lg: 14px; --row-h: 32px; 
  --type: 13px; --type-sm: 11px; 
}

/* ═══════════════════════════════════════════════════════════════
   TYPOGRAPHY UTILITIES
   ═══════════════════════════════════════════════════════════════ */
.font-mono { 
  font-family: 'IBM Plex Mono', ui-monospace, monospace; 
  font-variant-numeric: tabular-nums; 
}
.font-ui { font-family: 'IBM Plex Sans', system-ui, sans-serif; }
.uppercase { text-transform: uppercase; letter-spacing: 0.08em; }

/* Mobile-first scale. Labels/captions read at arm's length; body text
   reads at standard mobile size; numbers always tabular. Headings sit
   in component-local styles. */
.t-micro   { font-size: 11px; letter-spacing: 0.10em; text-transform: uppercase; }
.t-mini    { font-size: 12px; letter-spacing: 0.04em; }
.t-data    { font-size: 14px; }
.t-num     { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums; }

/* ═══════════════════════════════════════════════════════════════
   DIVIDERS
   ═══════════════════════════════════════════════════════════════ */
.divider { height: 1px; background: var(--line); width: 100%; }
.divider-v { width: 1px; background: var(--line); align-self: stretch; }

/* ═══════════════════════════════════════════════════════════════
   BUTTONS — Enhanced contrast states
   ═══════════════════════════════════════════════════════════════ */
button.aph-btn {
  appearance: none;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--text);
  font: inherit;
  /* Min-height satisfies Apple HIG (44 px) for default sizing; padding
     keeps the visual bounds comfortable for thumbs without being huge.
     Per-component overrides can shrink for inline pill chips. */
  min-height: 40px;
  padding: 10px 14px;
  cursor: pointer;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 6px;
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease, transform 100ms ease;
}
button.aph-btn.compact {
  min-height: 32px;
  padding: 6px 10px;
  font-size: 11px;
  border-radius: 4px;
}
button.aph-btn.tiny {
  min-height: 28px;
  padding: 4px 8px;
  font-size: 10px;
  border-radius: 4px;
}
button.aph-btn:hover { 
  background: var(--bg-elev); 
  border-color: var(--line-strong);
}
button.aph-btn:active {
  transform: scale(0.97);
}
button.aph-btn.primary { 
  background: var(--accent); 
  color: var(--bg);           /* High contrast: bright on dark */
  border-color: var(--accent); 
  font-weight: 600;
}
button.aph-btn.primary:hover { 
  filter: brightness(1.12); 
}
button.aph-btn.ghost { 
  border-color: var(--line); 
  color: var(--text-mid); 
}
button.aph-btn.ghost:hover {
  color: var(--text);
  border-color: var(--line-strong);
}
button.aph-btn:disabled { 
  opacity: 0.30; 
  cursor: not-allowed;
  border-color: var(--line);
  color: var(--text-faint);
}

/* Success / Danger variants */
button.aph-btn.success { 
  background: var(--success); 
  color: var(--bg); 
  border-color: var(--success);
  font-weight: 600;
}
button.aph-btn.danger { 
  background: var(--danger); 
  color: var(--bg); 
  border-color: var(--danger);
  font-weight: 600;
}

/* ═══════════════════════════════════════════════════════════════
   CHIPS / BADGES
   ═══════════════════════════════════════════════════════════════ */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  background: var(--bg-elev);
  color: var(--text-mid);
}
.chip .dot { 
  width: 6px; 
  height: 6px; 
  border-radius: 1px; 
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════════════════════════
   SCROLLBARS
   ═══════════════════════════════════════════════════════════════ */
.aph-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
.aph-scroll::-webkit-scrollbar-track { background: transparent; }
.aph-scroll::-webkit-scrollbar-thumb { 
  background: var(--line); 
  border-radius: 3px; 
}
.aph-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--line-strong);
}

/* Tighter scrollbar for game UI */
.aph-scroll::-webkit-scrollbar { width: 4px; height: 4px; }
.aph-scroll::-webkit-scrollbar-thumb { border-radius: 2px; }

/* ═══════════════════════════════════════════════════════════════
   SHEET ANIMATIONS
   ═══════════════════════════════════════════════════════════════ */
/* Full-screen panel — fills the .aphelion-frame above the nav rail so
   bottom-bar tabs (Tech, Diplo, Reports, Settings) have the entire
   visible area for their content. The transform classes drive the
   slide-up open/close animation, and the drag handle still works to
   dismiss with a downward swipe. */
.sheet-container {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: calc(68px + var(--nav-bottom-inset));  /* clear nav rail — uses shared gesture-bar inset (see :root rule near top of file) */
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border-top: 1px solid var(--line);
  z-index: 70;
  box-shadow: 0 -8px 28px rgba(0,0,0,0.45);
  touch-action: pan-y;
}
.sheet-enter { transform: translateY(100%); }
.sheet-show  { transform: translateY(0); transition: transform 280ms cubic-bezier(.2,.7,.2,1); }
.sheet-hide  { transform: translateY(100%); transition: transform 220ms cubic-bezier(.4,0,.6,1); pointer-events: none; }
.sheet-container.dragging { transition: none; }

/* ═══════════════════════════════════════════════════════════════
   SELECTION RING PULSE
   ═══════════════════════════════════════════════════════════════ */
@keyframes pulse-ring {
  0%   { stroke-opacity: 0.9; }
  50%  { stroke-opacity: 0.35; }
  100% { stroke-opacity: 0.9; }
}
.pulse { animation: pulse-ring 2.4s ease-in-out infinite; }

/* ═══════════════════════════════════════════════════════════════
   FLEET PATH DASH ANIMATION
   ═══════════════════════════════════════════════════════════════ */
@keyframes dash-march {
  to { stroke-dashoffset: -16; }
}
.fleet-path { animation: dash-march 4s linear infinite; }

/* ═══════════════════════════════════════════════════════════════
   TICK BLINK
   ═══════════════════════════════════════════════════════════════ */
@keyframes tick-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0.25; }
}
.tick-cursor { animation: tick-blink 1s step-end infinite; }

/* ═══════════════════════════════════════════════════════════════
   LOG OVERLAY SLIDE
   ═══════════════════════════════════════════════════════════════ */
.log-enter { transform: translateX(100%); }
.log-show  { transform: translateX(0); transition: transform 260ms cubic-bezier(.2,.7,.2,1); }
.log-hide  { transform: translateX(100%); transition: transform 220ms cubic-bezier(.4,0,.6,1); }

/* ═══════════════════════════════════════════════════════════════
   NAV RAIL
   ═══════════════════════════════════════════════════════════════ */
.nav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 4px 10px;
  cursor: pointer;
  color: var(--text-dim);
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 10.5px;
  line-height: 1.15;             /* tight enough that the label clears descenders */
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-top: 2px solid transparent;
  position: relative;
  transition: color 160ms ease, border-color 160ms ease;
  overflow: visible;             /* don't clip descenders ("p" in Diplo / Reports) */
}
.nav-item > span {
  white-space: nowrap;
}
.nav-item:hover {
  color: var(--text-mid);
}
.nav-item.active {
  color: var(--text);
  border-top-color: var(--accent);
}
.nav-item .nav-glyph {
  font-size: 20px;
  line-height: 1;
  font-weight: 400;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
}

/* ═══════════════════════════════════════════════════════════════
   BOTTOM SHEET DRAG HANDLE
   ═══════════════════════════════════════════════════════════════ */
.sheet-handle {
  width: 48px;
  height: 5px;
  border-radius: 3px;
  background: var(--line-strong);
  margin: 10px auto;
}

/* ═══════════════════════════════════════════════════════════════
   FACTION DOT
   ═══════════════════════════════════════════════════════════════ */
.faction-dot { 
  width: 8px; 
  height: 8px; 
  flex-shrink: 0; 
  display: inline-block; 
}

/* ═══════════════════════════════════════════════════════════════
   HAIRLINE
   ═══════════════════════════════════════════════════════════════ */
.hr { height: 1px; background: var(--line); }

/* ═══════════════════════════════════════════════════════════════
   SHEET TITLE
   ═══════════════════════════════════════════════════════════════ */
.sheet-title {
  font-size: 20px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text);
}

/* ═══════════════════════════════════════════════════════════════
   STAT TILE
   ═══════════════════════════════════════════════════════════════ */
.stat {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-elev);
}
.stat .lbl {
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.stat .val {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 18px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.stat .sub {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 11px;
  color: var(--text-faint);
}

/* ═══════════════════════════════════════════════════════════════
   RANGE SLIDER
   ═══════════════════════════════════════════════════════════════ */
input.aph-range {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  /* Thumb is 22 px, track 4 px — total height must be ≥ thumb for a
     comfortable thumb hit area on a phone. */
  height: 26px;
  background: transparent;
  outline: none;
  /* Only the thumb is interactive — taps on the track do nothing, so the
     player has to land their finger on the circle to change the value.
     touch-action: pan-y on the thumb itself means a vertical drag that
     starts on the thumb still scrolls the panel; only a horizontal drag
     scrubs the slider. */
  pointer-events: none;
  touch-action: pan-y;
}
input.aph-range::-webkit-slider-runnable-track {
  height: 4px;
  background: var(--bg-elev-2);
  border: 0;
  border-radius: 2px;
  pointer-events: none;
}
input.aph-range::-moz-range-track {
  height: 4px;
  background: var(--bg-elev-2);
  border: 0;
  border-radius: 2px;
  pointer-events: none;
}
input.aph-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -9px;
  background: var(--accent);
  border: 2px solid var(--bg);
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
  pointer-events: auto;
  touch-action: pan-y;
}
input.aph-range::-moz-range-thumb {
  width: 22px;
  height: 22px;
  background: var(--accent);
  border: 2px solid var(--bg);
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
  pointer-events: auto;
  touch-action: pan-y;
}

/* ═══════════════════════════════════════════════════════════════
   FADE ENTER
   ═══════════════════════════════════════════════════════════════ */
@keyframes fadein { 
  from { opacity: 0; transform: translateY(8px); } 
  to { opacity: 1; transform: translateY(0); } 
}
.fade-in { animation: fadein 200ms ease-out; }

/* ═══════════════════════════════════════════════════════════════
   SEGMENTED SWITCH
   ═══════════════════════════════════════════════════════════════ */
.seg {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg-elev);
}
.seg button {
  appearance: none;
  border: 0;
  background: transparent;
  min-height: 36px;
  padding: 8px 12px;
  color: var(--text-dim);
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background 160ms ease, color 160ms ease;
}
.seg button:hover {
  background: var(--bg-elev-2);
  color: var(--text-mid);
}
.seg button.on {
  background: var(--bg-elev-2);
  color: var(--text);
  font-weight: 600;
}

/* ═══════════════════════════════════════════════════════════════
   FOCUS RINGS — Accessibility
   ═══════════════════════════════════════════════════════════════ */
button.aph-btn:focus-visible,
.seg button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════
   COMPACT DENSITY
   ═══════════════════════════════════════════════════════════════ */
[data-density="compact"] .nav-item { height: 48px; }
[data-density="compact"] .stat { padding: 6px 8px; }

/* ═══════════════════════════════════════════════════════════════
   ACCENT OVERRIDES
   ═══════════════════════════════════════════════════════════════ */
[data-accent="amber"] { 
  --accent: oklch(0.82 0.16 75); 
  --you: oklch(0.82 0.16 75); 
}
[data-accent="magenta"] { 
  --accent: oklch(0.78 0.20 330); 
  --you: oklch(0.78 0.20 330); 
}

[data-theme="bone"][data-accent="amber"] {
  --accent: oklch(0.55 0.11 70);     /* Mustard ochre — chroma dropped to match inked-stamp register */
  --you:    oklch(0.55 0.11 70);
}
[data-theme="bone"][data-accent="magenta"] {
  --accent: oklch(0.44 0.13 340);    /* Tyrian rose — inked, not neon */
  --you:    oklch(0.44 0.13 340);
}

/* ═══════════════════════════════════════════════════════════════
   COMBAT FLASH EFFECT
   ═══════════════════════════════════════════════════════════════ */
@keyframes combat-flash {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 0; }
}
.combat-flash {
  animation: combat-flash 800ms ease-out forwards;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════
   TOAST NOTIFICATIONS
   ═══════════════════════════════════════════════════════════════ */
@keyframes toast-in { 
  from { opacity: 0; transform: translateY(-12px); } 
  to { opacity: 1; transform: translateY(0); } 
}
.toast-in { animation: toast-in 300ms ease-out; }

@keyframes toast-out { 
  from { opacity: 1; transform: translateY(0); } 
  to { opacity: 0; transform: translateY(-12px); } 
}
.toast-out { animation: toast-out 200ms ease-in forwards; }

/* ═══════════════════════════════════════════════════════════════
   SLIDE-IN RIGHT
   ═══════════════════════════════════════════════════════════════ */
@keyframes slide-in-right { 
  from { transform: translateX(100%); } 
  to { transform: translateX(0); } 
}
.slide-in-right { animation: slide-in-right 260ms cubic-bezier(.2,.7,.2,1); }

/* ═══════════════════════════════════════════════════════════════
   MAP GRID ENHANCEMENT
   ═══════════════════════════════════════════════════════════════ */
/* Major grid lines more visible */
.grid-major line {
  stroke: var(--line);
  stroke-width: 0.8;
  opacity: 0.5;
}

/* ═══════════════════════════════════════════════════════════════
   STAR SELECTION ENHANCEMENT
   ═══════════════════════════════════════════════════════════════ */
/* Selected star gets stronger glow */
.star-selected {
  filter: drop-shadow(0 0 8px var(--accent));
}

/* Dispatch target gets distinct pulsing */
.star-dispatch-target {
  animation: pulse-ring 1.2s ease-in-out infinite;
}

/* ═══════════════════════════════════════════════════════════════
   MOBILE PANEL UTILITIES
   Components used by the bottom sheets, star detail card, and
   report rows. Keep these in CSS so panel JSX stays clean.
   ═══════════════════════════════════════════════════════════════ */

/* Section header inside a panel — small caps label above content. */
.panel-section {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 16px 0 8px;
}
.panel-section:first-child { margin-top: 0; }

/* Tappable row — used for fleets, reports, tech tracks. Provides a
   generous tap area without forcing a fixed height. */
.tap-row {
  display: grid;
  align-items: center;
  gap: 10px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--line);
  min-height: 56px;
}
.tap-row:last-child { border-bottom: 0; }
.tap-row.interactive { cursor: pointer; border-radius: 6px; padding: 12px 8px; }
.tap-row.interactive:active { background: var(--bg-elev-2); transform: scale(0.99); }

/* Card sectioning — bordered block used for tech focus, key system info. */
.panel-card {
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px;
  margin-bottom: 14px;
}
.panel-card.accent { border-color: var(--accent); }
.panel-card.warn { border-color: var(--warn); }
.panel-card.success { border-color: var(--success); }

/* Composition list — one row per ship class. Class glyph + name on the
   left, count right-aligned in monospace. Used in DispatchModal and
   StarDetail to break a fleet/garrison down across BS/CR/FR/CV. */
.comp-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.comp-tile {
  background: var(--bg-elev-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.comp-tile .comp-label {
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  min-width: 0;
}
.comp-tile .comp-value {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 18px;
  color: var(--text);
  flex-shrink: 0;
}

/* Progress bar — visible on mobile, not a hairline. */
.bar-track {
  width: 100%;
  height: 6px;
  background: var(--bg-elev-2);
  border-radius: 3px;
  overflow: hidden;
}
.bar-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  transition: width 200ms ease;
}
.bar-fill.success { background: var(--success); }
.bar-fill.warn { background: var(--warn); }
.bar-fill.danger { background: var(--danger); }

/* Outcome badge — bigger than .chip for combat report scanability. */
.outcome-badge {
  display: inline-flex;
  align-items: center;
  padding: 5px 10px;
  border-radius: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}
.outcome-badge.victory  { background: color-mix(in oklch, var(--success) 18%, transparent); color: var(--success); border: 1px solid var(--success); }
.outcome-badge.pyrrhic  { background: color-mix(in oklch, var(--warn) 18%, transparent);    color: var(--warn);    border: 1px solid var(--warn); }
.outcome-badge.withdraw { background: color-mix(in oklch, var(--text-mid) 14%, transparent); color: var(--text-mid); border: 1px solid var(--text-mid); }
.outcome-badge.mutual   { background: color-mix(in oklch, var(--text-faint) 14%, transparent); color: var(--text-faint); border: 1px solid var(--text-faint); }
.outcome-badge.defeat   { background: color-mix(in oklch, var(--danger) 18%, transparent);  color: var(--danger);  border: 1px solid var(--danger); }

/* Inline metric — used in summary rows where label/value pairs need to
   line up cleanly. */
.metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.metric .metric-label {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.metric .metric-value {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--text);
}

/* Class-coloured swatch for BS/CR/FR/CV identification. */
.class-swatch {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: 6px;
  vertical-align: middle;
}
.class-swatch.bs { background: var(--enemy-2); }
.class-swatch.cr { background: var(--accent); }
.class-swatch.fr { background: var(--info); }
.class-swatch.cv { background: var(--warn); }
.class-swatch.fig { background: var(--enemy-3); }
.class-swatch.def { background: var(--success); }

/* ═══════════════════════════════════════════════════════════════
   PHASE 12 — SIGNATURE FLOURISHES
   ═══════════════════════════════════════════════════════════════
   Each theme's bold per-theme character. Designed for mid-range
   Android perf: pseudo-element overlays sit on `.aphelion-frame`
   (NOT `.map-container` / SVG children), so the StarMap pan loop
   never inherits a `mix-blend-mode` filter chain.
   */

/* ─── graphite — accent glow on interactive elements ─────────────
   The disciplined default. Restrained but present. */
[data-theme="graphite"] .aph-btn.primary:hover,
[data-theme="graphite"] .aph-btn.primary:focus-visible {
  box-shadow: 0 0 12px var(--glow-accent), var(--shadow-sm);
}
[data-theme="graphite"] .panel-card.accent {
  box-shadow: 0 0 14px var(--glow-accent), var(--shadow-md);
}

/* ─── bone — paper-grain noise + aged-vellum vignette ────────────
   Two stacked overlays on .aphelion-frame:
     ::before — paper-grain (fractal noise tinted sepia, multiply)
     ::after  — radial vignette darkening edges like an old document
   Inline-SVG data URIs keep network cost at zero; both paint once
   and never repaint (pointer-events:none keeps interactions on the
   real UI underneath). Tuned via --bone-noise / --bone-vignette so
   the theme block above is the single source of strength values. */
[data-theme="bone"] .aphelion-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Lower baseFrequency (0.85 → 0.65) produces longer fibre-like
   * streaks instead of uniform speckle — feels more like vellum.
   * Color matrix tinted toward sepia (0.45/0.30/0.18) so multiply
   * darkens warm instead of cold. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' seed='5' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.45  0 0 0 0 0.30  0 0 0 0 0.18  0 0 0 0.7 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 160px 160px;
  mix-blend-mode: multiply;
  opacity: var(--bone-noise, 0.10);
  z-index: 1;
}
[data-theme="bone"] .aphelion-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      ellipse at center,
      transparent 55%,
      oklch(0.30 0.045 60 / var(--bone-vignette, 0.18)) 100%
    );
  mix-blend-mode: multiply;
  z-index: 1;
}

/* Bone: system-name labels read as sepia ink, not faction colour.
   On a paper-aesthetic theme, multi-colour labels feel like crayon
   on parchment. Ownership is still legible via the star marker
   shape (player rect outline / AI diamond / neutral circle), so the
   text can rejoin the document's ink. CSS fill/stroke override the
   inline SVG presentation attributes on .map-system-label, so dark
   themes — which never match this selector — keep the faction-
   coloured fill the JSX paints by default. Stroke dropped to none
   because dark ink on parchment doesn't need a contrast halo. */
[data-theme="bone"] .map-system-label {
  fill: var(--text);
  stroke: none;
}

/* Bone: fleet-in-transit ship counts adopt the garrison-number style —
   same sepia ink fill and 700 weight as the count rendered under each
   owned star, so a 12-ship garrison and a 12-ship fleet read as the
   same kind of number, not two different ones. Dark themes (which
   never match this selector) keep the faction-coloured fleet labels
   that work fine against a black map. Targets .map-fleet-label on
   both the live-fleet <text> and the ghost-fleet <text>. */
[data-theme="bone"] .map-fleet-label {
  fill: var(--text);
  font-weight: 700;
}

/* Bone: the top-HUD income tag and tick counter both adopt plain
   sepia ink at the 'mid' level, not the faction accent. The earlier
   accent-bumped sienna read as a red splash against the parchment
   scrim and clashed with the inked-cartographer aesthetic; the
   default --text-mid was too faded to scan at a glance. Settling on
   --text with 600 weight gives a consistent, document-coloured pair
   of small numbers that still feels like written labels. Dark themes
   never match the selector and keep their original accent/text-mid
   colouring, which was working fine. */
[data-theme="bone"] .income-tag,
[data-theme="bone"] .tick-tag {
  color: var(--text);
  font-weight: 600;
}

/* ─── phosphor — CRT scanlines + accent bloom ────────────────────
   2px stride, multiply blend. Static (no animation) — animated
   scanlines look like a meme screenshot, not a premium theme. */
[data-theme="phosphor"] .aphelion-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    0deg,
    transparent 0,
    transparent 1px,
    oklch(0 0 0 / 0.20) 1px,
    oklch(0 0 0 / 0.20) 2px
  );
  mix-blend-mode: multiply;
  opacity: 0.6;
  z-index: 1;
}
/* Phosphor bloom on tabular numerals. .t-num is used heavily in
   the dispatch tray, combat reports, HUD strip. */
[data-theme="phosphor"] .t-num {
  text-shadow: 0 0 4px var(--glow-accent);
}
[data-theme="phosphor"] .aph-btn.primary:hover,
[data-theme="phosphor"] .aph-btn.primary:focus-visible {
  box-shadow: 0 0 16px var(--glow-accent), var(--shadow-sm);
  text-shadow: 0 0 6px var(--glow-accent);
}

/* ─── ember — warmer scanlines + CRT hot-center on modals ───────
   3px stride, lower opacity, warm multiply. The radial-gradient
   on .modal-card simulates the "hot center" of a heating CRT —
   each modal feels like an alert dispatch. */
[data-theme="ember"] .aphelion-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    0deg,
    transparent 0,
    transparent 2px,
    oklch(0.05 0.020 50 / 0.25) 2px,
    oklch(0.05 0.020 50 / 0.25) 3px
  );
  mix-blend-mode: multiply;
  opacity: 0.4;
  z-index: 1;
}
[data-theme="ember"] .t-num {
  text-shadow: 0 0 4px var(--glow-accent);
}
[data-theme="ember"] .modal-card {
  background-image: radial-gradient(
    circle at 50% 40%,
    var(--accent-dim) 0%,
    transparent 60%
  );
  background-blend-mode: screen;
  background-color: var(--bg-elev);
}
[data-theme="ember"] .aph-btn.primary:hover,
[data-theme="ember"] .aph-btn.primary:focus-visible {
  box-shadow: 0 0 18px var(--glow-accent), var(--shadow-sm);
}

/* ─── void — no glow, sharper chrome ─────────────────────────────
   The polish IS the restraint. Glow tokens are already transparent
   (set in [data-theme="void"]'s --glow-accent: oklch(0 0 0 / 0)),
   so hover effects naturally fall flat. We just tighten the chrome. */
[data-theme="void"] .chip {
  border-width: 1.5px;
}
[data-theme="void"] .chip .dot {
  border-radius: 0;
}
/* No hover-glow rule needed — --glow-accent is transparent, so the
   graphite-shared box-shadow rule above produces no visible halo.
   That's deliberate; restraint by default. */

/* ─── cobalt — matte panel gradient (no glow) ───────────────────
   Chroma already lives in --bg-elev (panels carry the colour).
   A gentle bottom-tint on .panel-card suggests depth without using
   shadow — keeps the matte aesthetic intact. */
[data-theme="cobalt"] .panel-card {
  background-image: linear-gradient(
    180deg,
    transparent 0%,
    oklch(0.18 0.055 245 / 0.40) 100%
  );
}
[data-theme="cobalt"] .modal-card {
  background-image: linear-gradient(
    180deg,
    transparent 0%,
    oklch(0.18 0.055 245 / 0.30) 100%
  );
}

/* ─── Tutorial overlay ──────────────────────────────────────────
   Highlight ring + NEW GAME pulse used by src/tutorial/. The ring
   sits over a data-tut target; the button class pulses the home
   screen's NEW GAME after a natural tutorial completion. */
@keyframes aph-tut-ring-pulse {
  0%, 100% {
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4),
                0 0 18px 4px var(--accent);
    transform: scale(1);
  }
  50% {
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4),
                0 0 28px 10px var(--accent);
    transform: scale(1.025);
  }
}

@keyframes aph-tut-button-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent); }
  50%      { box-shadow: 0 0 0 6px rgba(64, 200, 220, 0); }
}
.aph-tut-pulse-ring {
  animation: aph-tut-button-pulse 1.4s ease-in-out infinite;
}
