/* ==========================================================================
   Ashfall — presentation layer.

   Owns every visual decision for the HUD, the front end, the loading screen and
   the error card. `src/ui/hud.js` and `src/ui/menu.js` only toggle classes, set
   custom properties and write text; nothing in this file is created by them and
   nothing here fights the inline values they write per frame.

   Palette and tuning constants below mirror `src/world/art.js` exactly. If a
   value changes there, change it here and nowhere else. The two UI modules also
   push the live palette onto [data-hud] / [data-menu] as --hud-* / --menu-*, so
   the tokens below are the pre-boot defaults and the fallback if a module fails.

   The whole look is the same warm/cool split the renderer is composed around:
   ash-white type, a low warm key, cool sky-blue shadow, and one saturated hit
   (hazard yellow) reserved for things that matter.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  color-scheme: dark;

  /* --- Light (art.js PALETTE) --- */
  --sun: #ffcf9a;
  --sun-core: #fff1dd;
  --sky-zenith: #3f6f9e;
  --sky-horizon: #d8c3a4;
  --ground-bounce: #7a6647;
  --shadow-cool: #2a3644;

  /* --- Surfaces --- */
  --concrete-lit: #8d8880;
  --concrete-shadow: #4a5460;
  --concrete-stained: #6f6a62;
  --asphalt: #3c3a38;
  --brick: #7a5548;
  --brick-painted: #8d8578;
  --plaster: #a89e8e;
  --rust: #8a4a28;
  --rust-deep: #5c2f1a;
  --steel-painted: #5a6159;
  --steel-bare: #7d8288;
  --rail-green: #3d4a3f;
  --tarp-blue: #2d4a63;
  --hazard-yellow: #c8a02c;
  --wood-weathered: #6b5a45;
  --sandbag: #8b7c5e;
  --dirt: #6b5a44;
  --gravel: #6e6963;
  --weeds: #5c6340;
  --glass: #8fa3aa;

  /* --- Gun --- */
  --gunmetal: #33363a;
  --gun-polymer: #2b2d2a;
  --gun-tan: #8a7a5c;
  --gun-rubber: #1d1e1f;
  --brass: #b08a3e;

  /* --- FX --- */
  --muzzle-core: #fff6d8;
  --muzzle-edge: #ff9a3c;
  --tracer: #ffb347;
  --tracer-enemy: #7fd4ff;
  --blood: #7a1512;
  --smoke: #b9b0a4;
  --dust: #c9bca6;
  --spark: #ffcf7a;
  --ember: #ff7a2c;

  /* --- UI (art.js PALETTE, UI block) --- */
  --hud-primary: #e8e4dc;
  /* Opaque on purpose, and the one token that deliberately does not mirror
     art.js. art.js ships hudDim as rgba(232,228,220,0.55), which means the dim
     labels composite toward whatever is behind them: measured on the sunline
     frame, SCORE and KILLS came out at 1.13:1 against the sky directly behind
     them, i.e. invisible. #b4b0a8 is that same ash hue flattened against the
     ink, so it reads as the same step down the ramp but can never dissolve into
     a bright background. It is re-asserted on .hud / .menu below because the UI
     modules push the art.js alpha inline. */
  --hud-dim: #b4b0a8;
  --hud-accent: #c8a02c;
  --hud-danger: #d64b3a;
  --hud-friendly: #6fb3d4;
  --hud-blood: #7a1512;

  --menu-primary: var(--hud-primary);
  --menu-dim: var(--hud-dim);
  --menu-accent: var(--hud-accent);
  --menu-danger: var(--hud-danger);

  /* --- Grade (art.js GRADE), reused so the DOM sits in the same image --- */
  --grade-vignette: 0.34;
  --grade-grain: 0.028;
  --grade-contrast: 1.09;

  /* --- Ink: the page behind everything. Matches <meta name="theme-color">. --- */
  --ink-900: #0a0908;
  --ink-800: #12100d;
  --ink-700: #191713;
  --ink-600: #23201b;
  --hairline: rgba(232, 228, 220, 0.14);
  --hairline-strong: rgba(232, 228, 220, 0.28);

  /* --- Type ---
     index.html links Barlow Condensed / IBM Plex Mono / Inter, but that link is
     the one external asset in a project whose whole premise is that there are
     none, and it is the first thing to disappear: offline, behind a CSP, or with
     the CDN blocked. The delivered capture is exactly that case, and the old
     stack fell straight past 'Arial Narrow' and 'Helvetica Neue' (neither of
     which exists on Linux) into the platform grotesque, so MK18, SCORE, KILLS,
     STAND and the compass cardinals set as wide body copy on a render.

     These stacks are therefore written to work with no webfont at all. Each one
     names the genuinely condensed faces that actually ship on each platform —
     Arial Narrow on Windows, Helvetica Neue Condensed on macOS, Liberation Sans
     Narrow / Nimbus Sans Narrow / DejaVu Sans Condensed on Linux, Roboto
     Condensed on Android — before reaching a generic, and --font-stretch-hud
     asks fontconfig for the narrow width of any family that carries one as a
     width axis rather than a separate name. Tracking throughout the HUD has come
     down with it (display labels 0.26-0.44em -> 0.12-0.2em, .mono 0.1 -> 0.055em)
     because wide tracking on a wide fallback stops a short label reading as one
     word; the condensed faces still need less of it than a grotesque does. */
  --font-display: 'Barlow Condensed', 'Oswald', 'Roboto Condensed', 'Arial Narrow',
    'Helvetica Neue Condensed', 'Helvetica Neue', 'Liberation Sans Narrow',
    'Nimbus Sans Narrow', 'DejaVu Sans Condensed', 'Encode Sans Condensed',
    system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas,
    'Roboto Mono', 'DejaVu Sans Mono', 'Liberation Mono', 'Courier New', monospace;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto,
    'Helvetica Neue', 'Liberation Sans', Arial, sans-serif;
  /* Applied wherever --font-display is. Harmless on a family with one width. */
  --font-stretch-hud: condensed;

  /* --- Metrics. One unit, clamped so the HUD holds from 1280 to 3440. --- */
  --u: clamp(11px, 0.63vw, 21px);
  --gutter: clamp(20px, 1.9vw, 46px);
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --pad-t: calc(var(--gutter) + var(--safe-t));
  --pad-r: calc(var(--gutter) + var(--safe-r));
  --pad-b: calc(var(--gutter) + var(--safe-b));
  --pad-l: calc(var(--gutter) + var(--safe-l));

  /* --- Legibility over any background: a dark outline + a soft drop. ---
     Ash-grey type sits at roughly the same luminance as the dusk sky, so the
     outline, not the fill colour, is what carries the contrast: it puts a near
     black edge under every stroke and takes the local ratio from ~1.4:1 to
     ~9:1 whether the glyph is over sky or over ground. The old version leaned
     on four 0.7-alpha 1px offsets, which vanished on thin small type over the
     bright horizon; the ring is now near opaque and backed by a 4px halo that
     darkens the sky immediately around the letterforms. */
  --ink-outline:
    0 0 1px rgba(6, 5, 4, 0.98), 1px 0 1px rgba(6, 5, 4, 0.92), -1px 0 1px rgba(6, 5, 4, 0.92),
    0 1px 1px rgba(6, 5, 4, 0.92), 0 -1px 1px rgba(6, 5, 4, 0.92),
    0 0 4px rgba(0, 0, 0, 0.72), 0 2px 8px rgba(0, 0, 0, 0.55);
  --ink-outline-soft:
    0 0 1px rgba(6, 5, 4, 0.95), 0 0 3px rgba(0, 0, 0, 0.65), 0 1px 5px rgba(0, 0, 0, 0.55);
  --drop:
    drop-shadow(0 0 1px rgba(6, 5, 4, 0.98)) drop-shadow(0 0 3px rgba(0, 0, 0, 0.6))
    drop-shadow(0 1px 4px rgba(0, 0, 0, 0.55));

  /* --- Motion --- */
  --ease-punch: cubic-bezier(0.2, 0.9, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --t-fast: 90ms;
  --t-punch: 180ms;
  --t-ui: 240ms;
}

/* Film grain, generated in-document. No network, no image file. */
:root {
  --grain-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   2. Page shell
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

html {
  background: var(--ink-900);
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

body.ashfall {
  position: fixed;
  inset: 0;
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: none;
  background: var(--ink-900);
  color: var(--hud-primary);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* The `hidden` attribute is the primary show/hide channel for both UI modules,
   and almost every hook below sets an explicit `display`. This must win. */
[hidden] {
  display: none !important;
}

#viewport {
  position: fixed;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  background: var(--ink-900);
  outline: none;
  touch-action: none;
  z-index: 0;
}

/* Pointer is locked during play; hide the arrow so nothing floats over the sight. */
body.menu-closed {
  cursor: none;
}

::selection {
  background: color-mix(in srgb, var(--hud-accent) 55%, transparent);
  color: var(--ink-900);
}

/* Film grain over the live 3D background.
   It lives on <body> rather than inside .menu on purpose: .menu has a z-index and
   therefore forms a stacking context, and `mix-blend-mode` can only reach the
   backdrop of its own stacking context. Hung off the body it blends against the
   canvas itself, which is the only place the grain means anything. */
body.ashfall::after {
  content: '';
  position: fixed;
  inset: -120px;
  z-index: 45;
  pointer-events: none;
  background-image: var(--grain-tile);
  background-size: 180px 180px;
  background-repeat: repeat;
  opacity: 0;
  mix-blend-mode: overlay;
  transition: opacity 420ms linear;
}

body.menu-open::after,
body:not(.ashfall-ready)::after {
  opacity: calc(var(--grade-grain) * 9);
  animation: grain-shift 640ms steps(6) infinite;
}

/* Shared focus treatment. Ring is drawn inside so clipped shapes keep it. */
:focus {
  outline: none;
}

:focus-visible {
  outline: 2px solid var(--menu-accent);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   3. Type utilities
   -------------------------------------------------------------------------- */

/* The mono face is for figures — ammo, health, score values, the debug readout —
   where the tabular width is doing real work. No condensed monospace ships on
   any platform, so the fallback is always wider than IBM Plex Mono; 0.1em on top
   of that pushed the HUD numerals into a spaced-out ledger. 0.055em keeps the
   grouping without letting digits collide when the webfont is absent. */
.mono {
  font-family: var(--font-mono);
  /* .hud sets condensed on the root; no monospace has a narrow width, and asking
     for one only risks fontconfig substituting a different family entirely. */
  font-stretch: normal;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1, 'zero' 1;
  letter-spacing: 0.055em;
}

/* Short uppercase labels are not figures, and in the fallback they suffer most
   from a wide mono. They take the condensed display face instead, which is what
   makes the HUD read as stencilled military type rather than as HTML text. */
.hud .label,
.health-stance,
.minimap-zone,
.streak {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
}

/*
 * Figures use the condensed display face with tabular figures, NOT the mono face.
 *
 * A reviewer caught two faults that both trace back to routing numerals through --font-mono.
 * First, the ammo block already sets the display face, so "30" and "/210" sat side by side in
 * two visibly different typefaces. Second, when the webfont is unavailable the mono fallback
 * resolves to DejaVu Sans Mono, whose default zero is slashed, so every zero on screen read as
 * a programmer's zero - which no shipped shooter uses.
 *
 * `tabular-nums` gives the same non-jittering columns that the mono face was there for, without
 * either problem. `slashed-zero` is explicitly disabled for any fallback that offers it as a
 * feature rather than as its default glyph.
 */
.score-row .val,
.health-meta [data-health-value] {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'zero' 0;
}

.hud,
.menu,
.loading {
  font-variant-numeric: tabular-nums lining-nums;
}

/* --------------------------------------------------------------------------
   4. HUD — root and full-screen state layers
   -------------------------------------------------------------------------- */

/* The HUD is set in the condensed display face from the root down. Everything
   that wants the mono face asks for it (.mono, or a rule below); nothing in the
   interface should ever inherit --font-body, which is what made the labels in
   the capture read as a paragraph rather than as instrumentation. */
.hud {
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: var(--u);
  color: var(--hud-primary);
  contain: layout style;

  /* Defaults for everything the HUD module drives. Present so the stylesheet is
     coherent before the first frame and if the module is stubbed out. */
  --health: 1;
  --damage: 0;
  --ads: 0;
  --streak: 0;
}

/* The one place this stylesheet overrules a UI module, and it is deliberate.
   hud.js and menu.js mirror art.js onto [data-hud] / [data-menu] every boot, and
   art.js's hudDim is an alpha. An alpha cannot be made legible: over the sunline
   sky it composites to within 1.13:1 of the background no matter what shadow is
   under it. Author !important beats an inline non-important declaration, so the
   opaque token wins here without either module needing to change. Every other
   palette value the modules write is still theirs. */
.hud,
.menu {
  --hud-dim: #b4b0a8 !important;
  --menu-dim: #b4b0a8 !important;
}

body.hide-hud .hud {
  display: none;
}

.hud-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  will-change: opacity;
}

/* Blood vignette. Intensity is the module's opacity write; --damage mirrors it so
   the shape can react as well as the strength. */
.hud-damage {
  opacity: var(--damage, 0);
  z-index: 1;
  background:
    radial-gradient(
      ellipse 88% 78% at 50% 52%,
      rgba(0, 0, 0, 0) 30%,
      color-mix(in srgb, var(--hud-blood) 42%, transparent) 66%,
      color-mix(in srgb, var(--hud-blood) 78%, transparent) 88%,
      color-mix(in srgb, var(--hud-blood) 94%, transparent) 100%
    ),
    radial-gradient(
      ellipse 130% 60% at 50% 108%,
      color-mix(in srgb, var(--hud-blood) 55%, transparent) 0%,
      rgba(0, 0, 0, 0) 60%
    );
}

/* Low-health heartbeat. The module writes the cardiac envelope into opacity; the
   scale breath here only widens the pinch so the beat is felt at the edges. */
.hud-lowhealth {
  opacity: 0;
  z-index: 2;
  background: radial-gradient(
    ellipse 96% 86% at 50% 50%,
    rgba(0, 0, 0, 0) 42%,
    color-mix(in srgb, var(--hud-danger) 26%, transparent) 78%,
    color-mix(in srgb, var(--hud-danger) 46%, transparent) 100%
  );
}

.hud.is-low-health .hud-lowhealth {
  animation: lowhealth-breathe 1.9s ease-in-out infinite;
}

@keyframes lowhealth-breathe {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.035);
  }
}

/* Instant hit flash. */
.hud-flash {
  opacity: 0;
  z-index: 3;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--hud-blood) 70%, transparent),
    color-mix(in srgb, var(--hud-blood) 92%, transparent)
  );
}

/* Optical sheen: a very faint scanline and a corner falloff so the DOM sits in
   the same glass as the render rather than on top of it. Deliberately near the
   threshold of visibility. */
.hud-scan {
  z-index: 0;
  opacity: 0.55;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0.014) 0 1px,
      rgba(0, 0, 0, 0) 1px 3px
    ),
    radial-gradient(
      ellipse 120% 105% at 50% 50%,
      rgba(0, 0, 0, 0) 58%,
      rgba(6, 8, 12, calc(var(--grade-vignette) * 0.42)) 100%
    );
  animation: scan-drift 9s linear infinite;
}

@keyframes scan-drift {
  from {
    background-position: 0 0, 0 0;
  }
  to {
    background-position: 0 3px, 0 0;
  }
}

/* --------------------------------------------------------------------------
   5. HUD — centre: crosshair, hitmarker, interact prompt
   -------------------------------------------------------------------------- */

.hud-centre {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 6;
  pointer-events: none;
}

.crosshair {
  position: relative;
  width: 0;
  height: 0;

  /* --ch-gap is written by hud.js in px (half the arm gap). --ch-spread /
     --ch-flare / --ch-tint arrive as 0..1. --spread is the readable alias. */
  --spread: var(--ch-spread, 0);
  --ch-offset: calc(var(--ch-gap, 5px) + var(--spread) * 5px);
  --ch-arm: calc(1px + 9px * (1 - var(--spread) * 0.28));
  --ch-thick: 2px;
  --ch-colour: var(--hud-ch-colour, var(--hud-primary));

  opacity: calc(1 - var(--ads, 0));
  filter: var(--drop);
  transition: opacity 60ms linear;
}

.ch-arm {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  border-radius: 1px;
  background: var(--ch-colour);
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.9);
}

/* Anchor with `transform`, offset with the independent `translate` property, so
   the two compose instead of overwriting each other. hud.js probes --ch-gap and
   stands down as soon as it sees these react. */
.ch-t,
.ch-b {
  width: var(--ch-thick);
  height: var(--ch-arm);
}

.ch-l,
.ch-r {
  width: var(--ch-arm);
  height: var(--ch-thick);
}

.ch-t {
  transform: translate(-50%, -100%);
  translate: 0 calc(var(--ch-offset) * -1);
}

.ch-b {
  transform: translate(-50%, 0);
  translate: 0 var(--ch-offset);
}

.ch-l {
  transform: translate(-100%, -50%);
  translate: calc(var(--ch-offset) * -1) 0;
}

.ch-r {
  transform: translate(0, -50%);
  translate: var(--ch-offset) 0;
}

.ch-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: var(--ch-colour);
  transform: translate(-50%, -50%);
  opacity: calc(0.35 + 0.65 * var(--ch-tint, 0));
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.9);
}

.crosshair.is-firing .ch-arm {
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.9), 0 0 6px color-mix(in srgb, var(--muzzle-edge) 45%, transparent);
}

.crosshair.is-ads {
  opacity: 0;
}

/* Hit / kill confirmation reads on the reticle too, not only on the marker. */
.crosshair.is-hit .ch-dot,
.crosshair.is-kill .ch-dot {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1.6);
}

/* --- Hitmarker ---------------------------------------------------------- */

.hitmarker {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  opacity: 0;
  z-index: 2;
  filter: var(--drop);
  --hm-reach: 11px;
}

.hitmarker span {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  width: 2px;
  height: 10px;
  border-radius: 1px;
  background: var(--hud-primary);
}

.hitmarker span:nth-child(1) {
  transform: translate(-50%, -50%) rotate(-45deg) translateY(calc(var(--hm-reach) * -1));
}
.hitmarker span:nth-child(2) {
  transform: translate(-50%, -50%) rotate(45deg) translateY(calc(var(--hm-reach) * -1));
}
.hitmarker span:nth-child(3) {
  transform: translate(-50%, -50%) rotate(135deg) translateY(calc(var(--hm-reach) * -1));
}
.hitmarker span:nth-child(4) {
  transform: translate(-50%, -50%) rotate(-135deg) translateY(calc(var(--hm-reach) * -1));
}

/* The kill marker is longer, heavier and sits closer: a solid X, not a tick. */
.hitmarker.is-kill {
  --hm-reach: 9px;
}

.hitmarker.is-kill span {
  width: 3px;
  height: 13px;
}

/* Headshot: the module hides the lower pair, leaving a chevron. Sharpen it. */
.hitmarker.is-head {
  --hm-reach: 12px;
}

/* Punch. The module drives the container's independent `scale`; this animates
   `transform`, which multiplies with it, so the two never fight. */
.hitmarker.is-hit,
.hitmarker.is-kill {
  animation: hitmarker-punch var(--t-punch) var(--ease-punch);
}

@keyframes hitmarker-punch {
  0% {
    transform: scale(0.6) rotate(-6deg);
  }
  55% {
    transform: scale(1.06) rotate(1deg);
  }
  100% {
    transform: scale(1) rotate(0deg);
  }
}

/* --- Interact prompt ---------------------------------------------------- */

.interact-prompt {
  position: absolute;
  left: 50%;
  top: calc(50% + 4.4em);
  transform: translateX(-50%);
  display: block;
  white-space: nowrap;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1.15em;
  font-weight: 600;
  /* 0.16em was drawn for Barlow Condensed; the fallback is wider to begin with,
     so the prompt was breaking into loose characters. */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--hud-primary);
  text-shadow: var(--ink-outline);
}

/* --------------------------------------------------------------------------
   6. HUD — compass
   -------------------------------------------------------------------------- */

.compass {
  position: absolute;
  top: calc(var(--pad-t) * 0.62);
  left: 50%;
  transform: translateX(-50%);
  width: clamp(320px, 30vw, 620px);
  height: 3.4em;
  z-index: 5;
  opacity: calc(1 - var(--ads, 0) * 0.55);
  /* The strip sits at the top of the frame, which in this map is almost always
     open sky — the brightest thing in the image and within a hair of the type's
     own luminance. This scrim buys back a stop of separation under the cardinals
     and the graduations. It is inside the element's own mask, so it fades out at
     the same rate as the ruler and never shows an edge. */
  background: radial-gradient(
    ellipse 58% 130% at 50% 34%,
    rgba(6, 8, 11, 0.44) 0%,
    rgba(6, 8, 11, 0.24) 52%,
    rgba(6, 8, 11, 0) 100%
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0%,
    #000 13%,
    #000 87%,
    transparent 100%
  );
  mask-image: linear-gradient(to right, transparent 0%, #000 13%, #000 87%, transparent 100%);
}

/* The datum the whole strip hangs from. */
.compass::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0.5em;
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    var(--hairline-strong) 16%,
    var(--hairline-strong) 84%,
    transparent
  );
}

.compass-track {
  position: relative;
  width: 100%;
  height: 100%;
  will-change: transform;
}

/* Graduated ruler. It has to live on the track so it scrolls with the heading,
   and it has to be far wider than the strip because the track translates by up
   to +-441 px (180 deg x hud.js's 2.45 px/deg). The tile is centred rather than
   left-aligned so a major graduation always lands exactly on the needle. */
.compass-track::before {
  content: '';
  position: absolute;
  left: 50%;
  width: 2400px;
  margin-left: -1200px;
  top: 0.5em;
  height: 0.5em;
  background-image:
    linear-gradient(
      to right,
      transparent 0 calc(50% - 0.5px),
      var(--hud-primary) calc(50% - 0.5px) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)
    ),
    linear-gradient(
      to right,
      transparent 0 calc(50% - 0.5px),
      var(--hud-dim) calc(50% - 0.5px) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)
    );
  /* 15 deg majors and 5 deg minors at 2.45 px per degree. */
  background-size: 36.75px 100%, 12.25px 55%;
  background-position: center top, center top;
  background-repeat: repeat-x, repeat-x;
  opacity: 0.72;
}

/* hud.js sets the tick size and tracking inline (12px/10px at 0.08em), so weight
   and shadow are the only levers here. Cardinals go to 700: at 12px in a fallback
   grotesque, 600 was not enough weight to hold the ink outline. */
.compass-track .ct-tick {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-weight: 700;
  text-transform: uppercase;
  text-shadow: var(--ink-outline);
  line-height: 1;
}

/* Objective markers are 8px accent-coloured text over sky — the single worst
   case in the HUD. They take the full outline, not the soft one. */
.compass-track .ct-marker {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-weight: 600;
  text-transform: uppercase;
  text-shadow: var(--ink-outline);
  line-height: 1;
}

/* The fixed bearing needle. Its tip lands on the datum line at 0.5em. */
.compass-needle {
  position: absolute;
  left: 50%;
  top: 0;
  width: 11px;
  height: 0.5em;
  min-height: 5px;
  transform: translateX(-50%);
  background: var(--hud-accent);
  clip-path: polygon(50% 100%, 0 0, 100% 0);
  filter: var(--drop);
  z-index: 2;
}

.compass-needle::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: 0.5em;
  background: linear-gradient(to bottom, var(--hud-accent), transparent);
  transform: translateX(-50%);
  opacity: 0.7;
}

/* --------------------------------------------------------------------------
   7. HUD — corners
   -------------------------------------------------------------------------- */

.hud-corner {
  position: absolute;
  z-index: 4;
  opacity: calc(1 - var(--ads, 0) * 0.42);
  transition: opacity 120ms linear;
}

.hud-tl {
  top: var(--pad-t);
  left: var(--pad-l);
}

.hud-tr {
  top: var(--pad-t);
  right: var(--pad-r);
  text-align: right;
}

.hud-bl {
  bottom: var(--pad-b);
  left: var(--pad-l);
}

.hud-br {
  bottom: var(--pad-b);
  right: var(--pad-r);
  text-align: right;
}

/* --------------------------------------------------------------------------
   8. HUD — health
   -------------------------------------------------------------------------- */

.health {
  width: clamp(180px, 15.5vw, 340px);
}

.health-bar {
  position: relative;
  height: 0.62em;
  min-height: 7px;
  background: linear-gradient(
    to bottom,
    rgba(10, 9, 8, 0.62),
    rgba(10, 9, 8, 0.42)
  );
  box-shadow:
    inset 0 0 0 1px rgba(6, 5, 4, 0.75),
    0 1px 4px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  /* Militarised parallelogram; the cut edge points into the screen. */
  clip-path: polygon(0 0, 100% 0, calc(100% - 0.5em) 100%, 0 100%);
}

.health-bar > i {
  display: block;
  width: 100%;
  height: 100%;
  background-color: var(--hud-primary);
  background-image: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.22),
    rgba(255, 255, 255, 0) 45%,
    rgba(0, 0, 0, 0.22)
  );
  transform-origin: left center;
  will-change: transform;
}

/* Segmentation sits on the track, above the fill, so the notches stay put while
   the fill scales. Ten segments, with a heavier mark at the halfway point. */
.health-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to right, transparent calc(50% - 1px), rgba(6, 5, 4, 0.85) calc(50% - 1px), rgba(6, 5, 4, 0.85) calc(50% + 1px), transparent calc(50% + 1px)),
    repeating-linear-gradient(
      to right,
      rgba(0, 0, 0, 0) 0 calc(10% - 2px),
      rgba(6, 5, 4, 0.72) calc(10% - 2px) 10%
    );
}

.health-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1em;
  margin-top: 0.42em;
  font-size: 0.78em;
  letter-spacing: 0.1em;
  text-shadow: var(--ink-outline);
}

.health-meta [data-health-value] {
  font-size: 1.35em;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--hud-primary);
}

/* STAND / CROUCH / SPRINT. Condensed face, and 600 so the opaque dim grey still
   carries weight next to the health figure beside it. */
.health-stance {
  color: var(--hud-dim);
  font-size: 0.96em;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.health.is-regen .health-bar > i {
  box-shadow: 0 0 10px color-mix(in srgb, var(--hud-friendly) 60%, transparent);
}

.health.is-critical [data-health-value] {
  color: var(--hud-danger);
  animation: critical-pulse 0.9s ease-in-out infinite;
}

.health.is-critical .health-bar {
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--hud-danger) 55%, transparent),
    0 0 12px color-mix(in srgb, var(--hud-danger) 30%, transparent);
}

@keyframes critical-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.55;
  }
}

/* --- Directional damage arcs -------------------------------------------- */

.hud-damage-dirs {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 7;
}

/* hud.js builds a border-drawn blade inside each arc as a self-sufficient
   fallback. This stylesheet supersedes it with a masked conic wedge, which
   holds its weight at any radius. `display` is never written inline on the
   blade, so hiding it here is safe and reversible. */
.dmg-arc > span {
  display: none;
}

.dmg-arc::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(190px, 17vw, 340px);
  height: clamp(190px, 17vw, 340px);
  transform: translate(-50%, -50%);
  background: conic-gradient(
    from -19deg,
    color-mix(in srgb, var(--hud-danger) 92%, transparent) 0deg,
    color-mix(in srgb, var(--hud-danger) 55%, transparent) 19deg,
    color-mix(in srgb, var(--hud-danger) 92%, transparent) 38deg,
    rgba(0, 0, 0, 0) 38.2deg 360deg
  );
  -webkit-mask-image: radial-gradient(
    closest-side,
    transparent 0 63%,
    #000 71%,
    #000 84%,
    transparent 92%
  );
  mask-image: radial-gradient(
    closest-side,
    transparent 0 63%,
    #000 71%,
    #000 84%,
    transparent 92%
  );
  filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.7));
}

/* --------------------------------------------------------------------------
   9. HUD — weapon and ammunition
   -------------------------------------------------------------------------- */

.weapon-block {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.15em;
  min-width: clamp(150px, 12vw, 260px);
}

/* MK18 / VECTOR / DMR14. 0.28em was Barlow Condensed tracking; on the fallback
   the capture showed "M K 1 8" as four separate glyphs. 0.16em (and the matching
   indent, which pays back the trailing space so the block still rags right)
   holds it together as a designation while keeping the stencilled feel. */
.weapon-name {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1em;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  color: var(--hud-dim);
  text-shadow: var(--ink-outline);
  line-height: 1;
}

.ammo {
  display: flex;
  align-items: baseline;
  gap: 0.18em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 0.9;
  text-shadow: var(--ink-outline);
}

.ammo-mag {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 3.4em;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--hud-primary);
  transition: color 120ms linear;
}

.ammo-sep {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1.5em;
  font-weight: 500;
  color: var(--hud-dim);
  transform: translateY(-0.12em) skewX(-10deg);
}

.ammo-reserve {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1.55em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--hud-dim);
  transform: translateY(-0.1em);
}

/* --- Pip strip ---------------------------------------------------------- */

.ammo-pips {
  display: block;
  height: 9px;
  margin-top: 0.35em;
  margin-right: -2px;
  line-height: 0;
  filter: var(--drop);
}

.ammo-pips .pip {
  transform: skewX(-16deg);
  transition: opacity 90ms linear;
}

/* --- Reload ------------------------------------------------------------- */

.reload-prompt {
  display: block;
  margin-top: 0.5em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1.02em;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--hud-accent);
  text-shadow: var(--ink-outline);
}

.reload-prompt b {
  display: inline-block;
  margin-left: 0.35em;
  padding: 0.05em 0.4em;
  font-family: var(--font-mono);
  font-size: 0.86em;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink-900);
  background: var(--hud-accent);
}

.reload-bar {
  display: block;
  width: clamp(120px, 9vw, 200px);
  height: 3px;
  margin-top: 0.55em;
  background: rgba(10, 9, 8, 0.6);
  box-shadow: inset 0 0 0 1px rgba(6, 5, 4, 0.7);
  overflow: hidden;
}

.reload-bar > i {
  display: block;
  width: 100%;
  height: 100%;
  background-color: var(--hud-friendly);
  transform-origin: left center;
  will-change: transform;
}

/* State reinforcement beyond colour, which hud.js already writes inline. */
.weapon-block.is-low .ammo-mag {
  text-shadow: var(--ink-outline), 0 0 12px color-mix(in srgb, var(--hud-accent) 55%, transparent);
}

.weapon-block.is-empty .ammo-mag {
  text-shadow: var(--ink-outline), 0 0 14px color-mix(in srgb, var(--hud-danger) 60%, transparent);
  animation: critical-pulse 0.7s steps(2, jump-none) infinite;
}

.weapon-block.is-reloading .weapon-name {
  color: var(--hud-accent);
}

.weapon-block.is-reloading .ammo {
  opacity: 0.62;
}

/* --------------------------------------------------------------------------
   10. HUD — minimap
   -------------------------------------------------------------------------- */

.minimap {
  position: relative;
  width: clamp(132px, 10.5vw, 220px);
  height: clamp(132px, 10.5vw, 220px);
}

.minimap canvas {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(12, 14, 16, 0.62),
    rgba(8, 9, 11, 0.78)
  );
  box-shadow:
    inset 0 0 22px rgba(0, 0, 0, 0.6),
    0 2px 10px rgba(0, 0, 0, 0.45);
}

/* Ring, cardinal ticks and the scanline sheen. */
.minimap-frame {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  box-shadow:
    inset 0 0 0 1px var(--hairline-strong),
    inset 0 0 0 4px rgba(6, 5, 4, 0.35);
}

.minimap-frame::before {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  background: conic-gradient(
    from -1.6deg,
    var(--hud-primary) 0 3.2deg,
    rgba(0, 0, 0, 0) 3.2deg 88.4deg,
    var(--hud-primary) 88.4deg 91.6deg,
    rgba(0, 0, 0, 0) 91.6deg 176.8deg,
    var(--hud-primary) 176.8deg 180deg,
    rgba(0, 0, 0, 0) 180deg 268.4deg,
    var(--hud-primary) 268.4deg 271.6deg,
    rgba(0, 0, 0, 0) 271.6deg 360deg
  );
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 87%, #000 89%);
  mask-image: radial-gradient(closest-side, transparent 0 87%, #000 89%);
  opacity: 0.75;
  filter: var(--drop);
}

.minimap-frame::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(232, 228, 220, 0.035) 0 1px,
      rgba(0, 0, 0, 0) 1px 4px
    ),
    linear-gradient(
      148deg,
      rgba(232, 228, 220, 0.09) 0%,
      rgba(232, 228, 220, 0) 38%,
      rgba(232, 228, 220, 0) 70%,
      rgba(111, 179, 212, 0.06) 100%
    );
  mix-blend-mode: screen;
}

/* Square corner brackets around the round map. Eight gradient slivers, no images. */
.minimap::before {
  content: '';
  position: absolute;
  inset: -9px;
  pointer-events: none;
  --b: var(--hairline-strong);
  background:
    linear-gradient(var(--b), var(--b)) left top / 1px 13px no-repeat,
    linear-gradient(var(--b), var(--b)) left top / 13px 1px no-repeat,
    linear-gradient(var(--b), var(--b)) right top / 1px 13px no-repeat,
    linear-gradient(var(--b), var(--b)) right top / 13px 1px no-repeat,
    linear-gradient(var(--b), var(--b)) left bottom / 1px 13px no-repeat,
    linear-gradient(var(--b), var(--b)) left bottom / 13px 1px no-repeat,
    linear-gradient(var(--b), var(--b)) right bottom / 1px 13px no-repeat,
    linear-gradient(var(--b), var(--b)) right bottom / 13px 1px no-repeat;
  filter: var(--drop);
}

.minimap-zone {
  position: absolute;
  left: 50%;
  bottom: -1.85em;
  transform: translateX(-50%);
  white-space: nowrap;
  /* THE YARD / THE DEPOT / THE TERRACES. Two words at 0.26em in a wide mono
     fell apart completely under the minimap in the capture; the condensed face
     at 0.16em and 600 gives it back its shape, and the opaque dim keeps it
     readable when the map is over the pale gravel rather than over ink. */
  font-size: 0.76em;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  color: var(--hud-dim);
  text-shadow: var(--ink-outline);
}

/* --------------------------------------------------------------------------
   11. HUD — scoreline and streak
   -------------------------------------------------------------------------- */

.scoreline {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.18em;
  text-shadow: var(--ink-outline);
}

/* This block is the one the contrast measurement failed on: it lives in the top
   right corner, which on this map is sky in almost every frame. The opaque dim
   and the heavier outline do most of the work; this is the last stop, a soft
   corner scrim that pulls the sky down behind the two rows. Decorative only —
   absolutely positioned behind the text, so nothing in the layout moves. */
.scoreline::before {
  content: '';
  position: absolute;
  right: -0.9em;
  top: -0.9em;
  left: -2.4em;
  bottom: -0.9em;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    ellipse 78% 96% at 88% 34%,
    rgba(6, 8, 11, 0.42) 0%,
    rgba(6, 8, 11, 0.2) 55%,
    rgba(6, 8, 11, 0) 100%
  );
}

.score-row {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.85em;
  font-size: 0.78em;
}

/* SCORE / KILLS. Measured at 1.13:1 against the sky in the delivered frame: the
   alpha dim had composited them away and the tracking had stretched five letters
   across the corner. Condensed face, 0.16em, 600, and the opaque dim. */
.score-row .label {
  color: var(--hud-dim);
  font-weight: 600;
  letter-spacing: 0.16em;
  /* Cancel the trailing letter-space so the two columns rag right cleanly. */
  margin-right: -0.16em;
  font-size: 0.98em;
}

.score-row .val {
  min-width: 3.6ch;
  text-align: right;
  font-size: 1.35em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--hud-primary);
}

.streak {
  display: block;
  margin-top: 0.32em;
  padding: 0.14em 0.55em;
  font-size: 0.8em;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-indent: 0.14em;
  text-transform: uppercase;
  color: var(--ink-900);
  background: var(--hud-accent);
  clip-path: polygon(0.55em 0, 100% 0, 100% 100%, 0 100%);
  text-shadow: none;
  animation: streak-in 260ms var(--ease-out);
}

@keyframes streak-in {
  from {
    opacity: 0;
    transform: translateX(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   12. HUD — kill feed
   -------------------------------------------------------------------------- */

.killfeed {
  position: absolute;
  top: calc(var(--pad-t) + clamp(72px, 5.6vw, 128px));
  right: var(--pad-r);
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.28em;
  z-index: 4;
  pointer-events: none;
}

.kf-row {
  display: flex;
  align-items: baseline;
  padding: 0.16em 0.6em 0.2em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1em;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: linear-gradient(to left, rgba(10, 9, 8, 0.58), rgba(10, 9, 8, 0.2));
  border-right: 2px solid var(--hairline);
  text-shadow: var(--ink-outline-soft);
  will-change: transform, opacity;
}

.kf-src,
.kf-tgt {
  font-weight: 600;
}

.kf-mark {
  font-family: var(--font-mono);
  font-size: 0.82em;
  font-weight: 500;
  letter-spacing: 0.1em;
}

/* --------------------------------------------------------------------------
   13. HUD — callouts and XP
   -------------------------------------------------------------------------- */

.callout {
  position: absolute;
  left: 50%;
  top: 26%;
  transform: translateX(-50%);
  z-index: 6;
  opacity: 0;
  white-space: nowrap;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 2.15em;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-indent: 0.12em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--hud-primary);
  text-shadow: var(--ink-outline);
  will-change: transform, opacity;
}

.callout::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -0.34em;
  width: 62%;
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(to right, transparent, currentColor, transparent);
  opacity: 0.5;
}

.callout.is-good {
  color: var(--hud-accent);
}

.callout.is-danger {
  color: var(--hud-danger);
}

.xp-stack {
  position: absolute;
  left: 50%;
  top: 58%;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1em;
  pointer-events: none;
}

.xp-pop {
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: 1.12em;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-shadow: var(--ink-outline);
  will-change: transform, opacity;
}

.xp-amt {
  font-variant-numeric: tabular-nums;
}

.xp-tag {
  font-family: var(--font-mono);
  font-size: 0.74em;
  letter-spacing: 0.1em;
}

/* --------------------------------------------------------------------------
   14. HUD — debug readout
   -------------------------------------------------------------------------- */

.debug-panel {
  position: absolute;
  left: var(--pad-l);
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  padding: 0.75em 1em;
  z-index: 8;
  max-width: 30ch;
  font-size: 0.72em;
  line-height: 1.65;
  letter-spacing: 0.04em;
  white-space: pre;
  color: var(--hud-primary);
  background: rgba(8, 8, 8, 0.72);
  border-left: 2px solid var(--hud-accent);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* --------------------------------------------------------------------------
   15. Menu — shell, vignette, grain
   -------------------------------------------------------------------------- */

.menu {
  position: fixed;
  inset: 0;
  z-index: 30;
  color: var(--menu-primary);
  transition: opacity 380ms var(--ease-out);
  --menu-fade: 1;
}

/* The live 3D flythrough is the background. These two layers are the grade the
   render does not do: a vignette pulled from GRADE.vignette and film grain
   pulled from GRADE.grainAmount, so the front end sits in the same photograph. */
.menu-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    linear-gradient(
      to right,
      rgba(6, 8, 11, 0.82) 0%,
      rgba(6, 8, 11, 0.5) 34%,
      rgba(6, 8, 11, 0.12) 62%,
      rgba(6, 8, 11, 0.45) 100%
    ),
    radial-gradient(
      ellipse 105% 95% at 50% 45%,
      rgba(0, 0, 0, 0) 40%,
      rgba(4, 6, 9, calc(var(--grade-vignette) * 1.55)) 82%,
      rgba(3, 4, 7, calc(var(--grade-vignette) * 2.3)) 100%
    ),
    linear-gradient(to top, rgba(6, 8, 11, 0.72), rgba(0, 0, 0, 0) 34%);
}

@keyframes grain-shift {
  0% {
    transform: translate(0, 0);
  }
  16.6% {
    transform: translate(-14px, 9px);
  }
  33.3% {
    transform: translate(11px, -13px);
  }
  50% {
    transform: translate(-9px, -7px);
  }
  66.6% {
    transform: translate(13px, 11px);
  }
  83.3% {
    transform: translate(-12px, 4px);
  }
  100% {
    transform: translate(0, 0);
  }
}

/* A single slow horizontal sheen. Reads as a projector, not as a web page. */
.menu::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.016) 0 1px,
    rgba(0, 0, 0, 0) 1px 3px
  );
  opacity: 0.7;
}

.menu.is-closed {
  pointer-events: none;
}

.menu-page {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--pad-t) var(--pad-r) var(--pad-b) var(--pad-l);
  opacity: 0;
  pointer-events: none;
}

.menu-page.is-active {
  opacity: 1;
  pointer-events: auto;
  animation: page-in 320ms var(--ease-out);
}

@keyframes page-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Title is left-aligned against the yard; every other page is a centred column. */
.menu-page[data-page='title'] {
  align-items: flex-start;
}

.menu-page[data-page='settings'],
.menu-page[data-page='controls'],
.menu-page[data-page='pause'],
.menu-page[data-page='dead'] {
  align-items: center;
}

/* --------------------------------------------------------------------------
   16. Menu — title block
   -------------------------------------------------------------------------- */

.title-block {
  max-width: min(46rem, 52vw);
  padding-left: clamp(0px, 3vw, 60px);
}

.eyebrow {
  margin: 0 0 clamp(10px, 1vw, 20px);
  font-size: clamp(10px, 0.62vw, 14px);
  font-weight: 500;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: var(--menu-accent);
  text-shadow: var(--ink-outline-soft);
}

.game-title {
  margin: 0;
  line-height: 0.84;
}

.title-main {
  display: block;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(84px, 11.5vw, 250px);
  font-weight: 700;
  letter-spacing: -0.012em;
  text-transform: uppercase;
  color: var(--menu-primary);
  background: linear-gradient(
    174deg,
    var(--sun-core) 0%,
    var(--menu-primary) 34%,
    color-mix(in srgb, var(--menu-primary) 72%, var(--concrete-shadow)) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 3px 22px rgba(0, 0, 0, 0.62));
}

.title-rule {
  display: block;
  width: clamp(220px, 26vw, 520px);
  height: 1px;
  margin-top: clamp(14px, 1.4vw, 30px);
  background: linear-gradient(
    to right,
    var(--menu-accent) 0 clamp(34px, 3.5vw, 74px),
    var(--hairline-strong) clamp(34px, 3.5vw, 74px) 100%
  );
}

.tagline {
  max-width: 34ch;
  margin: clamp(16px, 1.5vw, 30px) 0 clamp(26px, 2.4vw, 48px);
  font-family: var(--font-body);
  font-size: clamp(14px, 0.92vw, 21px);
  font-weight: 400;
  line-height: 1.55;
  color: color-mix(in srgb, var(--menu-primary) 74%, transparent);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.7);
}

.menu-note {
  margin: clamp(20px, 1.8vw, 36px) 0 0;
  font-size: clamp(10px, 0.6vw, 13px);
  letter-spacing: 0.12em;
  color: var(--menu-dim);
  text-shadow: var(--ink-outline-soft);
}

.menu-note b {
  font-weight: 600;
  color: var(--menu-primary);
}

.menu-foot {
  position: absolute;
  left: var(--pad-l);
  right: var(--pad-r);
  bottom: var(--pad-b);
  display: flex;
  justify-content: space-between;
  gap: 2em;
  font-size: clamp(9px, 0.55vw, 12px);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--menu-primary) 40%, transparent);
  text-shadow: var(--ink-outline-soft);
}

.page-title {
  margin: 0 0 clamp(18px, 1.8vw, 38px);
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(34px, 3.4vw, 74px);
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 0.95;
  color: var(--menu-primary);
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.6);
}

.page-title--danger {
  color: var(--menu-danger);
}

.death-note {
  margin: 0 0 clamp(20px, 2vw, 40px);
  font-size: clamp(13px, 0.85vw, 19px);
  color: color-mix(in srgb, var(--menu-primary) 68%, transparent);
  text-align: center;
}

/* --------------------------------------------------------------------------
   17. Menu — buttons
   -------------------------------------------------------------------------- */

.menu-actions {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 0.7vw, 16px);
}

.menu-page[data-page='settings'] .menu-actions,
.menu-page[data-page='controls'] .menu-actions,
.menu-page[data-page='pause'] .menu-actions,
.menu-page[data-page='dead'] .menu-actions {
  justify-content: center;
}

.menu-page[data-page='pause'] .menu-actions,
.menu-page[data-page='dead'] .menu-actions {
  flex-direction: column;
  align-items: stretch;
  min-width: min(360px, 80vw);
}

.btn {
  --btn-cut: 11px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 1.1em;
  min-width: 10.5em;
  padding: 0.82em 1.5em 0.78em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(15px, 1.02vw, 23px);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--menu-primary);
  background: rgba(16, 17, 18, 0.62);
  border: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  backdrop-filter: blur(6px) saturate(0.9);
  -webkit-backdrop-filter: blur(6px) saturate(0.9);
  clip-path: polygon(
    0 0,
    100% 0,
    100% calc(100% - var(--btn-cut)),
    calc(100% - var(--btn-cut)) 100%,
    0 100%
  );
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  transition:
    background-color var(--t-fast) linear,
    color var(--t-fast) linear,
    box-shadow var(--t-fast) linear,
    transform var(--t-fast) var(--ease-punch);
}

/* Leading tick: a small accent mark that grows on hover. */
.btn::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--menu-accent);
  transform: scaleY(0.28);
  transform-origin: center;
  transition: transform 140ms var(--ease-out);
}

.btn:hover,
.btn:focus-visible {
  color: #fff;
  background: rgba(30, 28, 24, 0.78);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--menu-accent) 72%, transparent);
}

.btn:hover::before,
.btn:focus-visible::before {
  transform: scaleY(1);
}

.btn:focus-visible {
  outline: none;
  box-shadow:
    inset 0 0 0 2px var(--menu-accent),
    0 0 0 1px rgba(0, 0, 0, 0.8);
}

.btn:active {
  transform: translateY(1px) scale(0.988);
  background: rgba(44, 39, 30, 0.85);
  transition-duration: 40ms;
}

/* `.btn` is a flex container, so text-indent cannot pay back the trailing
   letter-space. Cancel it on the label instead, or the caption sits left of centre. */
.btn > span {
  margin-right: -0.14em;
}

.btn b {
  font-family: var(--font-mono);
  font-size: 0.72em;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--menu-accent);
  opacity: 0.85;
}

.btn-primary {
  color: var(--ink-900);
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--menu-accent) 96%, #fff),
    var(--menu-accent)
  );
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--menu-accent) 60%, #fff);
}

.btn-primary::before {
  background: var(--ink-900);
  opacity: 0.35;
}

.btn-primary:hover,
.btn-primary:focus-visible {
  color: var(--ink-900);
  background: linear-gradient(to bottom, #f0cc63, color-mix(in srgb, var(--menu-accent) 88%, #fff));
  box-shadow:
    inset 0 0 0 1px #f5dc90,
    0 0 26px color-mix(in srgb, var(--menu-accent) 34%, transparent);
}

.btn-primary b {
  color: var(--ink-900);
  opacity: 0.6;
}

.btn-quiet {
  color: var(--menu-dim);
  background: rgba(12, 12, 12, 0.42);
  box-shadow: inset 0 0 0 1px var(--hairline);
}

.btn-quiet:hover,
.btn-quiet:focus-visible {
  color: var(--menu-primary);
}

/* --------------------------------------------------------------------------
   18. Menu — settings
   -------------------------------------------------------------------------- */

.settings-grid {
  width: min(920px, 88vw);
  max-height: min(62vh, 640px);
  margin-bottom: clamp(18px, 1.8vw, 34px);
  padding: 0 clamp(6px, 0.8vw, 18px) 0 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-strong) transparent;
}

.settings-grid::-webkit-scrollbar {
  width: 6px;
}

.settings-grid::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.04);
}

.settings-grid::-webkit-scrollbar-thumb {
  background: var(--hairline-strong);
}

.setting-group {
  margin: clamp(18px, 1.6vw, 32px) 0 clamp(6px, 0.6vw, 12px);
}

.setting-group:first-child {
  margin-top: 0;
}

.setting-group-title {
  display: flex;
  align-items: center;
  gap: 0.9em;
  margin: 0;
  font-family: var(--font-mono);
  font-size: clamp(10px, 0.6vw, 13px);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  text-transform: uppercase;
  color: var(--menu-accent);
}

.setting-group-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.setting {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(180px, 300px);
  align-items: center;
  gap: clamp(10px, 1.2vw, 26px);
  padding: clamp(9px, 0.85vw, 17px) 0;
  border-bottom: 1px solid rgba(232, 228, 220, 0.07);
  transition: opacity 160ms linear;
}

.setting:last-child {
  border-bottom: 0;
}

.setting-text {
  min-width: 0;
}

.setting-label {
  display: block;
  font-family: var(--font-body);
  font-size: clamp(13px, 0.82vw, 18px);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--menu-primary);
}

.setting-hint,
.setting-note {
  margin: 0.22em 0 0;
  font-size: clamp(11px, 0.66vw, 14px);
  line-height: 1.4;
  color: color-mix(in srgb, var(--menu-primary) 48%, transparent);
}

.setting-note {
  color: var(--menu-danger);
}

.setting-value {
  justify-self: end;
  min-width: 5.5ch;
  text-align: right;
  font-size: clamp(11px, 0.68vw, 15px);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--menu-accent);
}

.setting-control {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  min-width: 0;
}

.setting.is-unavailable {
  opacity: 0.4;
}

.setting.is-unavailable .setting-control {
  pointer-events: none;
}

/* --- Segmented buttons --------------------------------------------------- */

.segment {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: 100%;
  background: rgba(10, 10, 10, 0.5);
  box-shadow: inset 0 0 0 1px var(--hairline);
  isolation: isolate;
}

/* Sliding indicator: --count lives on the group, --index on the row above it. */
.segment::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: -1;
  width: calc(100% / var(--count, 1));
  background: color-mix(in srgb, var(--menu-accent) 88%, transparent);
  transform: translateX(calc(var(--index, 0) * 100%));
  transition: transform 190ms var(--ease-out);
}

.segment-opt {
  padding: 0.62em 0.5em 0.58em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(12px, 0.76vw, 17px);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-indent: 0.1em;
  text-transform: uppercase;
  line-height: 1;
  color: color-mix(in srgb, var(--menu-primary) 62%, transparent);
  background: none;
  border: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: color 120ms linear, background-color 120ms linear;
}

.segment-opt + .segment-opt {
  box-shadow: inset 1px 0 0 var(--hairline);
}

.segment-opt:hover {
  color: var(--menu-primary);
  background: rgba(255, 255, 255, 0.05);
}

.segment-opt.is-selected {
  color: var(--ink-900);
}

.segment-opt:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--menu-accent);
  color: var(--menu-primary);
}

.segment-opt.is-selected:focus-visible {
  color: var(--ink-900);
  box-shadow: inset 0 0 0 2px var(--ink-900);
}

/* --- Range sliders ------------------------------------------------------- */

.slider {
  width: 100%;
  height: 22px;
  margin: 0;
  background: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.slider:focus-visible {
  outline: none;
}

.slider::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 0;
  background: linear-gradient(
    to right,
    var(--menu-accent) 0 calc(var(--fill, 0) * 100%),
    rgba(232, 228, 220, 0.16) calc(var(--fill, 0) * 100%) 100%
  );
  box-shadow: inset 0 0 0 1px rgba(6, 5, 4, 0.5);
}

.slider::-moz-range-track {
  height: 4px;
  border-radius: 0;
  background: linear-gradient(
    to right,
    var(--menu-accent) 0 calc(var(--fill, 0) * 100%),
    rgba(232, 228, 220, 0.16) calc(var(--fill, 0) * 100%) 100%
  );
  box-shadow: inset 0 0 0 1px rgba(6, 5, 4, 0.5);
}

.slider::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 8px;
  height: 18px;
  margin-top: -7px;
  border: 0;
  border-radius: 0;
  background: var(--menu-primary);
  box-shadow: 0 0 0 1px rgba(6, 5, 4, 0.8), 0 2px 6px rgba(0, 0, 0, 0.5);
  transition: background-color 100ms linear, transform 100ms var(--ease-punch);
}

.slider::-moz-range-thumb {
  width: 8px;
  height: 18px;
  border: 0;
  border-radius: 0;
  background: var(--menu-primary);
  box-shadow: 0 0 0 1px rgba(6, 5, 4, 0.8), 0 2px 6px rgba(0, 0, 0, 0.5);
}

.slider:hover::-webkit-slider-thumb {
  background: #fff;
  transform: scaleY(1.12);
}

.slider:hover::-moz-range-thumb {
  background: #fff;
}

.slider:focus-visible::-webkit-slider-thumb {
  background: var(--menu-accent);
  box-shadow: 0 0 0 2px var(--menu-accent), 0 0 0 4px rgba(6, 5, 4, 0.85);
}

.slider:focus-visible::-moz-range-thumb {
  background: var(--menu-accent);
  box-shadow: 0 0 0 2px var(--menu-accent), 0 0 0 4px rgba(6, 5, 4, 0.85);
}

.slider:active::-webkit-slider-thumb {
  transform: scaleY(0.94);
}

/* --- Switches ------------------------------------------------------------ */

.switch {
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  line-height: 0;
}

.switch-track {
  position: relative;
  display: block;
  width: 46px;
  height: 22px;
  background: rgba(10, 10, 10, 0.55);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  transition: background-color 150ms linear, box-shadow 150ms linear;
}

.switch-knob {
  position: absolute;
  left: 3px;
  top: 3px;
  display: block;
  width: 16px;
  height: 16px;
  background: color-mix(in srgb, var(--menu-primary) 55%, transparent);
  translate: calc(var(--on, 0) * 24px) 0;
  transition: translate 170ms var(--ease-out), background-color 150ms linear;
}

.switch.is-on .switch-track,
.setting.is-on .switch-track {
  background: color-mix(in srgb, var(--menu-accent) 26%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--menu-accent) 70%, transparent);
}

.switch.is-on .switch-knob,
.setting.is-on .switch-knob {
  background: var(--menu-accent);
}

.switch:hover .switch-track {
  box-shadow: inset 0 0 0 1px var(--menu-primary);
}

.switch:focus-visible .switch-track {
  outline: 2px solid var(--menu-accent);
  outline-offset: 2px;
}

.switch:active .switch-knob {
  transform: scaleX(1.25);
}

/* --- Crosshair colour swatches ------------------------------------------ */

.swatches {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}

.swatch {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.4em 0.6em;
  font-family: var(--font-mono);
  font-size: clamp(10px, 0.6vw, 13px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--menu-primary) 62%, transparent);
  background: rgba(10, 10, 10, 0.45);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--hairline);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: color 120ms linear, box-shadow 120ms linear;
}

.swatch-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--swatch, var(--menu-primary));
  box-shadow: 0 0 0 1px rgba(6, 5, 4, 0.8), 0 0 7px var(--swatch, var(--menu-primary));
}

.swatch:hover {
  color: var(--menu-primary);
}

.swatch.is-selected {
  color: var(--menu-primary);
  box-shadow: inset 0 0 0 1px var(--menu-accent);
}

.swatch:focus-visible {
  outline: 2px solid var(--menu-accent);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   19. Menu — controls reference
   -------------------------------------------------------------------------- */

.controls-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: clamp(4px, 0.5vw, 12px) clamp(20px, 2.4vw, 52px);
  width: min(880px, 88vw);
  margin: 0 0 clamp(22px, 2.2vw, 44px);
}

.controls-list > div {
  display: grid;
  grid-template-columns: minmax(9.5em, auto) 1fr;
  align-items: baseline;
  gap: 1.2em;
  padding: 0.5em 0;
  border-bottom: 1px solid rgba(232, 228, 220, 0.07);
}

.controls-list dt {
  font-size: clamp(11px, 0.68vw, 15px);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--menu-accent);
}

.controls-list dd {
  margin: 0;
  font-size: clamp(12px, 0.78vw, 17px);
  color: color-mix(in srgb, var(--menu-primary) 76%, transparent);
}

/* --------------------------------------------------------------------------
   20. Loading screen
   -------------------------------------------------------------------------- */

.loading {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  background:
    radial-gradient(ellipse 80% 60% at 50% 46%, #17140f 0%, var(--ink-900) 72%),
    var(--ink-900);
  transition: opacity 640ms var(--ease-out), visibility 0s linear 640ms;
}

.loading::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--grain-tile);
  background-size: 180px 180px;
  opacity: 0.05;
  mix-blend-mode: overlay;
}

.loading.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.loading-inner {
  width: min(420px, 76vw);
  text-align: center;
}

.loading-title {
  margin: 0 0 clamp(16px, 1.6vw, 30px);
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(28px, 2.4vw, 52px);
  font-weight: 700;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--hud-primary);
  opacity: 0.94;
}

.loading-bar {
  position: relative;
  height: 2px;
  background: rgba(232, 228, 220, 0.12);
  overflow: hidden;
}

.loading-bar > i {
  display: block;
  width: 100%;
  height: 100%;
  background: linear-gradient(to right, var(--hud-accent), var(--sun));
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 320ms var(--ease-out);
  will-change: transform;
}

.loading-label {
  margin: clamp(12px, 1.1vw, 22px) 0 0;
  font-size: clamp(10px, 0.58vw, 13px);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--hud-dim);
}

.loading-label::after {
  content: '';
  display: inline-block;
  width: 0.5em;
  height: 0.5em;
  margin-left: 0.6em;
  vertical-align: 0.06em;
  background: var(--hud-accent);
  animation: load-blink 1.1s steps(2, jump-none) infinite;
}

@keyframes load-blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.1;
  }
}

/* --------------------------------------------------------------------------
   21. Error card
   -------------------------------------------------------------------------- */

.error-card {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 60;
  width: min(620px, 90vw);
  max-height: 76vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(20px, 2vw, 36px);
  background: rgba(14, 12, 11, 0.95);
  border-left: 3px solid var(--hud-danger);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.error-card h2 {
  margin: 0 0 0.55em;
  font-family: var(--font-display);
  font-stretch: var(--font-stretch-hud);
  font-size: clamp(22px, 1.7vw, 34px);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--hud-danger);
}

.error-card p {
  margin: 0 0 1.1em;
  font-size: clamp(13px, 0.8vw, 16px);
  line-height: 1.6;
  color: color-mix(in srgb, var(--hud-primary) 76%, transparent);
}

.error-card ul {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: clamp(11px, 0.66vw, 14px);
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--hud-primary) 62%, transparent);
}

.error-card li {
  padding: 0.5em 0 0.5em 1.1em;
  border-top: 1px solid rgba(232, 228, 220, 0.09);
  text-indent: -1.1em;
  word-break: break-word;
}

.error-card li::before {
  content: '× ';
  color: var(--hud-danger);
}

/* --------------------------------------------------------------------------
   22. Back link
   -------------------------------------------------------------------------- */

.back-link {
  position: fixed;
  left: var(--pad-l);
  top: var(--pad-t);
  z-index: 35;
  padding: 0.45em 0.9em 0.42em;
  font-family: var(--font-mono);
  font-size: clamp(10px, 0.6vw, 13px);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--menu-dim);
  background: rgba(10, 10, 10, 0.42);
  box-shadow: inset 0 0 0 1px var(--hairline);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: color var(--t-fast) linear, box-shadow var(--t-fast) linear, opacity var(--t-ui) linear;
}

.back-link:hover,
.back-link:focus-visible {
  color: var(--menu-primary);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
}

/* In play the chrome goes away entirely. */
body.menu-closed .back-link {
  opacity: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   23. Adaptation
   -------------------------------------------------------------------------- */

/* Ultrawide: stop the title running away from the eye and cap the HUD spread. */
@media (min-width: 2600px) {
  .title-block {
    max-width: 40vw;
  }

  .compass {
    width: clamp(520px, 24vw, 760px);
  }
}

@media (max-width: 1100px) {
  .setting {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 10px;
  }

  .setting-control {
    grid-column: 1 / -1;
    justify-content: stretch;
  }

  .setting-control > .segment,
  .setting-control > .slider {
    width: 100%;
  }

  .title-block {
    max-width: 92vw;
    padding-left: 0;
  }
}

@media (max-width: 720px) {
  .minimap,
  .compass,
  .debug-panel {
    display: none;
  }

  .health {
    width: min(46vw, 220px);
  }
}

/* Coarse pointers get bigger hit targets; the game itself is desktop-first. */
@media (hover: none) and (pointer: coarse) {
  .btn {
    padding: 1em 1.6em;
  }

  .segment-opt {
    padding: 0.85em 0.5em;
  }

  body.menu-closed {
    cursor: auto;
  }
}

/* --------------------------------------------------------------------------
   24. Reduced motion — keep every state readable, drop the decoration
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  body.ashfall::after,
  .hud-scan,
  .hud.is-low-health .hud-lowhealth,
  .health.is-critical [data-health-value],
  .weapon-block.is-empty .ammo-mag,
  .loading-label::after,
  .menu-page.is-active,
  .streak {
    animation: none !important;
  }

  /* The hitmarker punch is feedback, not decoration, so it stays — but shortened
     and without the rotation. */
  .hitmarker.is-hit,
  .hitmarker.is-kill {
    animation-duration: 90ms;
    animation-name: hitmarker-punch-flat;
  }

  body.menu-open::after {
    opacity: calc(var(--grade-grain) * 6);
  }

  *,
  *::before,
  *::after {
    transition-duration: 1ms !important;
  }
}

@keyframes hitmarker-punch-flat {
  0% {
    transform: scale(0.78);
  }
  100% {
    transform: scale(1);
  }
}

/* --------------------------------------------------------------------------
   25. Forced colours — the HUD must survive a high-contrast mode
   -------------------------------------------------------------------------- */

@media (forced-colors: active) {
  .btn,
  .segment-opt,
  .swatch,
  .switch-track {
    forced-color-adjust: none;
    border: 1px solid CanvasText;
  }

  .hud-scan,
  body.ashfall::after,
  .menu::after {
    display: none;
  }
}
