/* ============================================================================
   HAPPINESS CITY — مدينة السعادة
   UI stylesheet, built from the "Game logo UI theme" design canvas.

   Two temperature families taken straight off the logo:
     FIRE (gold → red)  = action, money, urgency, rewards
     ICE  (pale → blue) = navigation, panels, info, calm
   Every raised thing has a heavy black outline and a solid bottom face that
   collapses when pressed. Nothing is soft — no blur, no drop shadows that
   fade; the depth is all flat extrusion, the way the logo's letters are.

   ── The unit system ────────────────────────────────────────────────────────
   The whole game lives inside #stage, whose short side is 360rem and whose
   long side follows the window: landscape 480..936rem wide (4:3..13:5; a
   16:9 window is the classic 640x360rem, rendered at up to 1920x1080 on a
   16:9 screen), portrait 600..840rem tall (9:15..9:21) in a window taller
   than wide. So 1rem = the stage's short side / 360 and the entire UI scales
   as one piece with it. On a phone 1rem is the same physical size either way
   up (393 px across = 360rem upright, 393 px tall = 360rem sideways), so the
   44 px tap targets and the 11 px touch floors hold in both. Never use px
   for UI sizes here. _integration/DUAL_ASPECT_SPEC.md has the numbers.

   ── Landscape layout ───────────────────────────────────────────────────────
   Height is the scarce axis (332rem inside the padding), width is not. The
   middle third belongs to the runner and the street: in a run the hazards
   appear at the vanishing point (~38% down) and the runner's feet sit at
   ~80%, so the HUD keeps to the top edge and the four corners. On the menu
   the runner stands in the clear middle third and the panels take side
   columns. Sheets lay out in two columns rather
   than one long scroll, and dialogs are capped in width and centred. A wider
   window sees more street (the camera's vertical lens is fixed): corners
   ride the stage edges, centred layers stay centred (ADAPTIVE LANDSCAPE).

   ── Portrait layout ────────────────────────────────────────────────────────
   html.portrait (a phone held upright, a narrow desktop window, a vertical
   cabinet) is a CSS layer over the same markup, at the end of this file: one
   column, logo and board on top, the runner in the middle band (38-61 %),
   PLAY at the bottom; the HUD keeps its top row and bottom corners.

   ── Direction ──────────────────────────────────────────────────────────────
   Arabic and English share one layout. Every row is flex and the handful of
   directional rules key off :root[dir], so switching `dir` on <html> mirrors
   the whole UI with no second stylesheet.
   ========================================================================== */

@import url('./fonts.css');

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  /* fire ramp — action / money (warm painted amber, not neon) */
  --fire-1: #FFC44E;
  --fire-2: #F5952F;
  --fire-3: #E2672A;
  --fire-4: #B5361B;
  --fire-deep: #5A1E08;
  --fire-ink: #2A1206;

  /* ice ramp — toggles, best (pulled toward the teal of the panels) */
  --ice-1: #C2F0F0;
  --ice-2: #5FC7D1;
  --ice-3: #2A8FA6;
  --ice-deep: #1B6478;
  --ice-ink: #082A33;

  /* night ramp — ground: deep slate-teal ink */
  --night: #0A1418;
  --night-2: #1A303A;
  --night-3: #284753;
  --slab: #2F5562;
  --slab-2: #1D3641;
  --well: #0B161B;

  --outline: #160A04;
  --outline-drop: #0A0E10;

  --text: #F4E6C8;
  --text-ice: #E6D7B8;
  --text-mute: rgba(226, 210, 178, .62);
  --danger: #FF8A72;

  /* ---- the painted bronze plaque (Arcane-style frames) ------------------
     Every framed thing is three background layers in one box: a neutral
     brush-stroke tile, the fill (clipped to the padding box) and a bronze
     gradient showing through a transparent 3rem border (the rim, lit from
     above so it reads bevelled). Box-shadows add a dark ink line outside the
     rim and another inside it. All flat layers: nothing blurs, nothing
     filters, so a repaint of the live score costs next to nothing. The
     generator for paint.webp and the orn-*.svg scrolls is
     assets/ui/make_ui_paint.py. */
  --ink: #160A04;
  --teal-hi: #406B7A;
  --teal: #2A4A58;
  --teal-lo: #172E39;
  --rim: linear-gradient(#FBDDAE, #DC9A5C 15%, #B26B35 38%, #7B3E1A 62%, #BC763F 82%, #4A240E) border-box;
  --paint: url(../../../assets/ui/paint.webp) 0 0 / 128rem repeat padding-box;
  --teal-fill: radial-gradient(140% 120% at 36% 26%, var(--teal-hi), var(--teal) 46%, var(--teal-lo)) padding-box;
  --amber-fill: linear-gradient(#FFD27A, var(--fire-2) 52%, var(--fire-3) 80%, #C24A1E) padding-box;
  --frame: 0 0 0 1.5rem var(--ink), inset 0 0 0 1.5rem rgba(18, 8, 2, .85);
  --drop: rgba(8, 8, 8, .5);
  --cream: #F7E9CB;
  --orn: url(../../../assets/ui/orn-tl.svg) left top / 22rem 22rem no-repeat padding-box,
         url(../../../assets/ui/orn-tr.svg) right top / 22rem 22rem no-repeat padding-box,
         url(../../../assets/ui/orn-bl.svg) left bottom / 22rem 22rem no-repeat padding-box,
         url(../../../assets/ui/orn-br.svg) right bottom / 22rem 22rem no-repeat padding-box;
  --orn-sm: url(../../../assets/ui/orn-tl.svg) left top / 13rem 13rem no-repeat padding-box,
         url(../../../assets/ui/orn-tr.svg) right top / 13rem 13rem no-repeat padding-box,
         url(../../../assets/ui/orn-bl.svg) left bottom / 13rem 13rem no-repeat padding-box,
         url(../../../assets/ui/orn-br.svg) right bottom / 13rem 13rem no-repeat padding-box;

  --ar: 'Baloo Bhaijaan 2', 'Cairo', system-ui, sans-serif;
  --ui: 'Cairo', system-ui, -apple-system, sans-serif;
  --display: 'Bungee', 'Cairo', system-ui, sans-serif;
  --mono: ui-monospace, 'Cascadia Mono', Consolas, monospace;
}

/* ---------------------------------------------------------------- the stage
   The game lives in one stage whose short side is 360rem and whose long side
   follows the window, clamped: landscape 4:3 .. 13:5 (a 16:9 window gives the
   classic 640x360rem; a 16:10 laptop 576, a sideways phone ~780, 21:9 ~855),
   portrait (html.portrait, a window taller than wide) 9:15 .. 9:21. So the
   stage fills the window edge to edge; only a window outside the clamp gets
   bars. 1rem = the stage's short side / 360 in both orientations. The layout
   class comes from the inline script in index.html before the first paint;
   js/main.js reads the stage box (never the class), so CSS and the camera
   cannot disagree. The canvas inside is drawn at a per-tier pixel budget
   (js/main.js, renderSize), not at this CSS size times the device ratio. */
html {
  --vw: 100vw;
  --vh: 100vh;
  --env-t: env(safe-area-inset-top, 0px);
  --env-r: env(safe-area-inset-right, 0px);
  --env-b: env(safe-area-inset-bottom, 0px);
  --env-l: env(safe-area-inset-left, 0px);
  --ls-min: 1.3333;   /* landscape long/short: 4:3, the narrowest the two menu columns frame the runner */
  --ls-max: 2.6;      /* landscape: 13:5, every phone sideways incl. browser tabs (<= 2.57), 21:9 monitors */
  --win-s: var(--vh);  /* the window's side that becomes the stage's short side */
  --win-l: var(--vw);
}
@supports (height: 100dvh) {
  html { --vh: 100dvh; }
}
html.portrait {
  --ls-min: 1.6667;   /* portrait: never wider than 9:15 (1.7778 = strict 9:16) */
  --ls-max: 2.3334;   /* portrait: never taller than 9:21 */
  --win-s: var(--vw);
  --win-l: var(--vh);
}
/* an arcade cabinet in landscape keeps today's 16:9 minimum: its control
   legend does not fit a 4:3 column (a 4:3 / 5:4 cabinet letterboxes as today) */
html.arcade:not(.portrait) { --ls-min: calc(16 / 9); }
html {
  --s: min(var(--win-s), calc(var(--win-l) / var(--ls-min)));   /* short side */
  --l: min(var(--win-l), calc(var(--s) * var(--ls-max)));       /* long side  */
  --stage-w: var(--l);
  --stage-h: var(--s);
  font-size: calc(var(--stage-h) / 360);
}
html.portrait {
  --stage-w: var(--s);
  --stage-h: var(--l);
  font-size: calc(var(--stage-w) / 360);
}
/* font-size reads --stage-h in landscape and --stage-w in portrait, never
   --s: the sign-in keyboard freeze (js/board.js freeze) pins --stage-w AND
   --stage-h inline on <html>, which then freezes rem too.

   The bar around the stage, and the part of each safe-area inset that reaches
   past it (env() is measured against the window, not the stage). Every
   element that pads for the notch or the home indicator uses --sa-*, never a
   raw env(). The side insets are applied symmetrically (--sa-x): iOS reports
   59/59 sideways anyway, and a one-sided Android cutout must not push the
   centred score plaque or a dialog off the road's vanishing point. A 180°
   flip changes only env(), so it needs no JS. ?safe=t,r,b,l (index.html)
   fakes --env-* for headless shots. */
html {
  --bar-x: max(0px, calc((var(--vw) - var(--stage-w)) / 2));
  --bar-y: max(0px, calc((var(--vh) - var(--stage-h)) / 2));
  --sa-t: max(0px, calc(var(--env-t) - var(--bar-y)));
  --sa-b: max(0px, calc(var(--env-b) - var(--bar-y)));
  --sa-l: max(0px, calc(var(--env-l) - var(--bar-x)));
  --sa-r: max(0px, calc(var(--env-r) - var(--bar-x)));
  --sa-x: max(var(--sa-l), var(--sa-r));
}

html, body {
  width: 100%; height: 100%;
  overflow: hidden;
  background:
    radial-gradient(60% 50% at 50% 42%, #101A36 0%, transparent 70%),
    #04060D;
  font-family: var(--ui);
  color: var(--text);
  touch-action: none;           /* blocks pinch, double-tap zoom and pull-to-refresh on iOS */
  overscroll-behavior: none;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* iPhone Safari enlarges text on rotation otherwise; no long-press save sheet
   on the logo (the sign-in fields turn the callout back on, .field input) */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; -webkit-touch-callout: none; }
img { -webkit-user-drag: none; }

button, input { font-family: inherit; color: inherit; border: none; background: none; }
button { cursor: pointer; }

#stage {
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--stage-w);
  height: var(--stage-h);
  overflow: hidden;
  background: var(--night);
  box-shadow: 0 0 0 3rem var(--outline), 0 24rem 70rem rgba(0, 0, 0, .75);
}
/* Only round the corners where the stage is really a framed box (a bar on two
   sides) and the window is not a phone: a window the stage fills edge to edge
   keeps square corners, rounding there would just clip the render. Landscape
   bars exist only outside the 4:3 .. 13:5 clamp, portrait ones outside
   9:15 .. 9:21 (a square window counts as portrait and is framed). */
@media (min-aspect-ratio: 53/20) and (min-width: 480px), (max-aspect-ratio: 13/10) and (min-width: 480px) {
  html:not(.portrait) #stage { border-radius: 12rem; }
}
@media (min-aspect-ratio: 31/50) and (min-width: 480px), (max-aspect-ratio: 2/5) and (min-width: 480px) {
  html.portrait #stage { border-radius: 12rem; }
}
/* the arcade cabinet's 16:9 minimum (the stage block): a 4:3 / 5:4 cabinet is framed as today */
@media (max-aspect-ratio: 15/9) and (min-width: 480px) {
  html.arcade:not(.portrait) #stage { border-radius: 12rem; }
}

#app, #app canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }

.hidden { display: none !important; }

/* Any full-frame layer. Safe-area insets keep the rows clear of the notch,
   the Dynamic Island and the home indicator: only the part of each inset that
   reaches past a bar around the stage counts (--sa-*, the stage block). A
   phone held sideways now fills the screen, so its side insets apply in full
   (59 px on an iPhone: the UI sits 54rem in, the 3D still runs edge to edge),
   and symmetrically, so a centred plaque stays on the vanishing point. */
.screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  padding: calc(14rem + var(--sa-t)) calc(14rem + var(--sa-x))
           calc(14rem + var(--sa-b)) calc(14rem + var(--sa-x));
  z-index: 10;
}

/* ============================================================== keyframes */
@keyframes hcShine  { 0% { transform: translateX(-140%) skewX(-18deg); }
                     55%,100% { transform: translateX(420%) skewX(-18deg); } }
@keyframes hcFloat  { 0%, 100% { transform: translateY(0); }
                      50% { transform: translateY(-7rem); } }
@keyframes hcPulse  { 0%, 100% { opacity: .5; transform: scale(1); }
                      50% { opacity: .95; transform: scale(1.07); } }
@keyframes hcBar    { 0% { background-position: 0 0; }
                      100% { background-position: 28rem 0; } }
@keyframes hcSpin   { to { transform: rotate(360deg); } }
@keyframes hcPop    { from { transform: scale(.86) translateY(10rem); opacity: 0; } }
@keyframes hcBadge  { 0%, 100% { transform: scale(1); }
                      50% { transform: scale(1.06); } }
@keyframes hcToast  { 0% { transform: translate(-50%, -16rem); opacity: 0; }
                      12%, 82% { transform: translate(-50%, 0); opacity: 1; }
                      100% { transform: translate(-50%, -10rem); opacity: 0; } }
@keyframes hcToastSide { 0% { transform: translateY(-12rem); opacity: 0; }
                         12%, 82% { transform: translateY(0); opacity: 1; }
                         100% { transform: translateY(-8rem); opacity: 0; } }

/* ============================================================== primitives */

.hc-slab {
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent;
  border-radius: 8rem;
  box-shadow: var(--frame);
}

/* The pills: the plaque recipe without corner scrolls (too tight for them). */
.hc-chip {
  display: flex; align-items: center; gap: 6rem;
  padding: 4rem 9rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent;
  border-radius: 24rem;
  box-shadow: var(--frame), 0 3rem 0 1.5rem var(--drop);
  font: 400 12rem/1 var(--display);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .85);
}
.hc-chip.fire { color: var(--fire-1); }
.hc-chip.ice  { color: #A6ECEF; }
/* the HUD best readout is cream, like the score (the reference), not gem blue */
#high-chip:not(.beat) { color: var(--cream); }
.hc-chip .k {
  font: 700 9rem/1 var(--ar); letter-spacing: 0;
  color: rgba(226, 210, 178, .72);
  text-shadow: none;
}
/* The HUD best chip once this run's score has passed it: an amber badge that
   pops once, then keeps counting with the score (ui.setScore). */
.hc-chip.beat {
  color: var(--fire-ink);
  background: var(--paint), var(--amber-fill), var(--rim);
  text-shadow: 0 1rem 0 rgba(255, 230, 170, .5);
  animation: hcBadge .5s ease-out 1;
}
.hc-chip.beat .k { color: rgba(42, 18, 6, .75); }

/* The little geometric currency marks. The coin is a minted piece: gold face,
   a struck inner ring, a dark bronze edge. */
.pip { display: block; flex: none; }
.pip-coin {
  width: 16rem; height: 16rem; border-radius: 50%;
  background:
    radial-gradient(circle at 38% 32%, #FFF0B8 0 12%, transparent 30%),
    radial-gradient(circle, #FFE58A 0 9%, #A8571A 11% 18%, transparent 21%),
    radial-gradient(circle, transparent 0 52%, rgba(122, 56, 10, .55) 56% 62%, transparent 66%),
    radial-gradient(circle at 50% 40%, #FFD45E, #F0A02A 60%, #B9631A);
  border: 2rem solid #5A2A0C;
  box-shadow: 0 0 0 1rem #C98446;
}

/* Square icon buttons (pause, back, char arrows): a small ornate plaque with
   the corner scrolls; the bottom face collapses when pressed. */
.hc-icon {
  position: relative;
  width: 44rem; height: 44rem; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--orn-sm), var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent;
  border-radius: 9rem;
  box-shadow: var(--frame), 0 5rem 0 1.5rem var(--drop);
  color: var(--cream);
  font: 700 18rem/1 var(--ui);
  text-shadow: 0 1.5rem 0 var(--ink);
  transition: transform .06s, box-shadow .06s, filter .12s;
}
.hc-icon:hover { filter: brightness(1.15); }
.hc-icon:active, .hc-icon.is-down {
  transform: translateY(4rem);
  box-shadow: var(--frame), 0 1rem 0 1.5rem var(--drop);
}
/* The pause bars are cast bronze, inked like the scrolls. */
.ic-pause { display: flex; gap: 4rem; }
.ic-pause i {
  width: 5rem; height: 16rem; border-radius: 1.5rem;
  background: linear-gradient(90deg, #FCE0B2, #DE9C5E 45%, #A9602C);
  box-shadow: 0 0 0 1.2rem var(--ink), 0 2rem 0 1.2rem rgba(10, 6, 2, .45);
}

/* The primary action block: amber paint in the bronze rim, 9rem bottom face,
   sweeping shine. */
.hc-play {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center; gap: 10rem;
  width: 100%; padding: 16rem;
  background: var(--orn), var(--paint), var(--amber-fill), var(--rim);
  border: 3rem solid transparent;
  border-radius: 10rem;
  box-shadow:
    var(--frame),
    inset 0 5rem 0 rgba(255, 240, 200, .35),
    0 9rem 0 #4A1F09,
    0 9rem 0 1.5rem var(--ink);
  transition: transform .06s, box-shadow .06s, filter .12s;
}
.hc-play .ar { font: 800 25rem/1 var(--ar); color: var(--fire-ink); text-shadow: 0 1.5rem 0 rgba(255, 228, 170, .55); }
.hc-play .en { font: 400 12rem/1 var(--display); color: rgba(42, 18, 6, .62); letter-spacing: .08em; }
.hc-play .shine {
  position: absolute; top: 0; bottom: 0; width: 38rem;
  background: linear-gradient(90deg, transparent, rgba(255, 246, 220, .45), transparent);
  animation: hcShine 3s ease-in-out infinite;
}
.hc-play:hover { filter: brightness(1.05); }
.hc-play:active, .hc-play.is-down {
  transform: translateY(7rem);
  box-shadow: var(--frame), inset 0 5rem 0 rgba(255, 240, 200, .35),
              0 2rem 0 #4A1F09, 0 2rem 0 1.5rem var(--ink);
}
.hc-play[disabled] { filter: grayscale(.85) brightness(.6); pointer-events: none; }

.hc-btn {
  flex: 1; padding: 12rem 8rem;
  display: flex; flex-direction: column; align-items: center; gap: 2rem;
  border: 3rem solid transparent;
  border-radius: 8rem;
  transition: transform .06s, box-shadow .06s, filter .12s;
}
.hc-btn.wide { flex: 1 1 100%; }
.hc-btn b     { font: 800 15rem/1.15 var(--ar); }
.hc-btn small { font: 600 9rem/1 var(--mono); letter-spacing: .1em; }
.hc-btn.fire {
  background: var(--paint), var(--amber-fill), var(--rim);
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 240, 200, .32),
              0 6rem 0 #4A1F09, 0 6rem 0 1.5rem var(--ink);
}
.hc-btn.fire b     { color: var(--fire-ink); }
.hc-btn.fire small { color: rgba(42, 18, 6, .62); }
.hc-btn.fire:active, .hc-btn.fire.is-down {
  transform: translateY(4rem);
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 240, 200, .32),
              0 2rem 0 #4A1F09, 0 2rem 0 1.5rem var(--ink);
}
.hc-btn.dark {
  background: var(--paint), var(--teal-fill), var(--rim);
  box-shadow: var(--frame), 0 6rem 0 1.5rem var(--drop);
}
.hc-btn.dark b     { color: var(--cream); text-shadow: 0 1.5rem 0 var(--ink); }
.hc-btn.dark small { color: rgba(226, 210, 178, .55); }
.hc-btn.dark.danger b { color: var(--danger); }
.hc-btn.dark:active, .hc-btn.dark.is-down {
  transform: translateY(4rem);
  box-shadow: var(--frame), 0 2rem 0 1.5rem var(--drop);
}
.hc-btn:hover { filter: brightness(1.12); }
.hc-btn[disabled] { filter: grayscale(.8) brightness(.6); pointer-events: none; }
/* A tap leaves :hover stuck on a touch screen, so the hover lift would keep a
   button lit until the next tap somewhere else. Disabled ones keep their grey. */
@media (hover: none) {
  .hc-icon:hover, .hc-play:hover:not([disabled]), .hc-btn:hover:not([disabled]) { filter: none; }
}

/* A thin progress rail, used inside chips, rows and rails. */
.mini-bar {
  height: 7rem; padding: 1rem;
  background: var(--well); border: 1.5rem solid #9A5A2C; border-radius: 5rem;
  box-shadow: 0 0 0 1rem var(--ink), inset 0 1.5rem 0 rgba(0, 0, 0, .5);
}
.mini-bar > i {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(var(--fire-1), var(--fire-3));
  border-radius: 3rem;
  transition: width .35s ease-out;
}

/* ================================================================== FX layers */
#vignette {
  position: absolute; inset: 0; pointer-events: none; z-index: 4;
  background: radial-gradient(ellipse at center, transparent 72%, rgba(4, 6, 13, .4) 100%);
}
#flash {
  position: absolute; inset: 0; pointer-events: none; z-index: 9;
  background: radial-gradient(ellipse at center, rgba(224, 27, 18, .25), rgba(224, 27, 18, .6));
  opacity: 0; transition: opacity .1s;
}
#flash.on { opacity: 1; }
/* Fast Start's purchase (ui.flash('fs')): the door's emerald (grove/portal.js C0)
   over the road being cleared, where a tram may stand a few metres ahead */
#flash.fs { background: radial-gradient(ellipse at center, rgba(6, 222, 90, .16), rgba(6, 222, 90, .5)); }

/* ====================================================================== HUD */
#hud { pointer-events: none; z-index: 5; }
#hud button { pointer-events: auto; }

/* Top edge: pause in the inline-start corner, the score plaque dead centre
   over the sky (a grid, so the uneven corners never push it off-centre), the
   coin and best chips in the inline-end corner. */
.hud-top { display: grid; grid-template-columns: 1fr auto 1fr; align-items: start; gap: 8rem; }
.hud-left  { display: flex; gap: 8rem; justify-self: start; }
.hud-right { display: flex; flex-direction: column; gap: 6rem; align-items: flex-end; justify-self: end; }
.hud-gap { flex: 1; }

/* The city behind the HUD is a pale, hazy daylight street, not the mockup's
   night sky — bare text vanished into it, so the readout sits on its own chip. */
.hud-score {
  position: relative;               /* anchors the "+N" floats */
  /* Holds a 7-character score ("999,999": ~96rem of Bungee at 22rem, plus
     the padding and border) so the chip never resizes mid-run below 1M. */
  /* The ornate plaque of the reference: teal paint, bronze rim, a filigree
     scroll in each corner. Sized for the landscape top edge: it ends ~62rem
     down, well above the vanishing point at ~137rem. */
  text-align: center; min-width: 136rem;
  padding: 5rem 16rem 7rem;
  background: var(--orn), var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent;
  border-radius: 12rem;
  box-shadow: var(--frame), 0 5rem 0 1.5rem var(--drop);
}
.hud-score-label {
  font: 400 8.5rem/1 var(--display);
  letter-spacing: .1em; color: rgba(222, 214, 198, .78);
  text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .7);
  white-space: nowrap;
}
.hud-score-label .ar { font: 700 8.5rem/1 var(--ar); letter-spacing: 0; }
/* Bungee, the logo's blocky digits, in cream with an inked edge; the drop
   shadow is the extrusion. */
.hud-score-val {
  margin-top: 4rem;
  font: 400 22rem/1 var(--display); font-variant-numeric: tabular-nums;
  color: var(--cream);
  -webkit-text-stroke: 1.5rem var(--ink); paint-order: stroke fill;
  text-shadow: 0 2.5rem 0 rgba(12, 22, 28, .9);
  /* its own small layer: the per-frame score text re-rasters only the
     digits, never the plaque, scrolls and paint tile under them */
  will-change: transform;
}
/* "+N" per coin, drifting down beside the score plaque on its inline-end
   side (ui.floatText). Not under it: the combo pill sits under the plaque for
   most of a run and would cover it. LTR and isolated so "+30" never reads
   "30+" in Arabic; that makes its own logical sides LTR too, so the side is
   set physically, with an RTL twin. Starts invisible; the WAAPI fade leaves
   it at opacity 0. */
.score-float {
  position: absolute; left: 100%; top: 50%; margin-left: 6rem;
  z-index: 1;
  direction: ltr; unicode-bidi: isolate;
  pointer-events: none; opacity: 0;
  font: 400 12rem/1 var(--display); color: var(--fire-1);
  white-space: nowrap;
  -webkit-text-stroke: 1.2rem var(--ink); paint-order: stroke fill;
  text-shadow: 0 2rem 0 var(--outline);
}
:root[dir='rtl'] .score-float { left: auto; right: 100%; margin-left: 0; margin-right: 6rem; }
/* 40rem is about 43 CSS px on a landscape phone; the apron below takes the
   hit area to 56rem. */
#hud .hc-icon { width: 40rem; height: 40rem; font-size: 15rem; }
#hud .ic-pause i { height: 15rem; width: 5rem; }
/* The pause button is smaller than a fingertip; an invisible 8rem apron
   around it takes the near misses. */
#hud .hc-icon::after { content: ''; position: absolute; inset: -8rem; }

/* Under the score, centred: it still ends above the vanishing point (~112rem
   down against ~137rem), so the streak reads without covering what is coming. */
.combo {
  align-self: center; margin-top: 8rem;
  display: flex; align-items: center; gap: 7rem;
  padding: 5rem 13rem;
  background: var(--paint), linear-gradient(#F08A3A, var(--fire-3) 50%, #A8331A) padding-box, var(--rim);
  border: 3rem solid transparent;
  border-radius: 24rem;
  /* Solid: the old opacity pulse dipped to 50% and the pill went unread. It
     pops on a step up instead (.pop, set by ui.setCombo). */
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 220, 160, .25), 0 4rem 0 1.5rem var(--drop);
  animation: none;
}
.combo #combo-x {
  font: 400 14rem/1 var(--display); color: var(--cream);
  -webkit-text-stroke: 1.2rem var(--ink); paint-order: stroke fill;
}
@keyframes hcComboPop { 0% { transform: scale(1.6) rotate(-6deg); }
                        60% { transform: scale(.92); }
                        100% { transform: scale(1); } }
.combo.pop { animation: hcComboPop .35s cubic-bezier(.2, 1.6, .4, 1); }
/* Coins toward the next step, five per step; hidden at the x5 cap. */
.combo-pips { display: flex; gap: 3rem; }
.combo-pips i { width: 6rem; height: 6rem; border-radius: 2rem; background: rgba(40, 12, 2, .4); }
.combo-pips i.on { background: var(--cream); box-shadow: 0 0 0 1rem var(--ink); }
.combo-pips.hidden { display: none !important; }

/* Near-miss pop under the score. White, not a hazard colour: amber and coral
   stay the only saturated things in the frame. Animated by ui.nearMiss(),
   which lifts it at most 4rem. The combo pill occupies ~74-112rem while a
   streak is live, so the pop drops below it then (122rem, so the lift never
   tucks it under the pill) and sits right under the score otherwise. */
.near-pop {
  position: absolute; inset-inline: 0;
  /* #hud's padding-top carries the safe-area inset and the score and combo
     pill flow below it, so this absolute offset has to carry it too. */
  top: calc(122rem + var(--sa-t));
  text-align: center; pointer-events: none;
  font: 400 14rem/1 var(--display); color: var(--cream);
  -webkit-text-stroke: 1.5rem var(--ink); paint-order: stroke fill;
  text-shadow: 0 2rem 0 var(--outline);
}
.combo.hidden + .near-pop { top: calc(76rem + var(--sa-t)); }

/* A green door close ahead (main.js: under 60 m, and through the flash) owns
   the top centre: the score plaque and the combo pill dim so the whole lit
   rectangle and its bright lintel read, as in the film. The corners (pause,
   coins, mission) stay. Opacity only, so reduced motion keeps it. */
.hud-score, .combo, .near-pop { transition: opacity .25s ease-out; }
#hud.door-near .hud-score, #hud.door-near .combo, #hud.door-near .near-pop { opacity: .3; }
/* the world's name takes that band while it shows */
#hud.wn-on .combo, #hud.wn-on .near-pop { opacity: 0; }

/* The world a green door opens onto (ui.worldName): the Arabic name in cream
   over a small bronze-ruled English line. The flash is its title card: it
   lands on the opaque peak and is gone ~1.1 s later, before the first hazard
   row after a door comes up the road. It sits in the combo band under the
   (dimmed) score plaque, ending ~112rem down, above the vanishing point
   (~137rem). No plaque: a caption, not a dialog. */
.world-name {
  position: absolute; inset-inline: 0; top: calc(66rem + var(--sa-t));
  display: flex; flex-direction: column; align-items: center; gap: 4rem;
  text-align: center; pointer-events: none; opacity: 0;
}
.world-name.show { animation: hcWorldName 1.1s ease-out both; }
.world-name .wn-ar {
  font: 800 28rem/1.15 var(--ar); color: var(--cream);
  -webkit-text-stroke: 1.5rem var(--ink); paint-order: stroke fill;
  text-shadow: 0 2rem 0 var(--outline), 0 0 18rem rgba(6, 40, 26, .55);
}
.world-name .wn-en {
  display: flex; align-items: center; gap: 7rem;
  font: 400 10rem/1 var(--display); letter-spacing: .2em; color: #F2C48A;
  -webkit-text-stroke: 1.2rem var(--ink); paint-order: stroke fill;
  text-shadow: 0 1.5rem 0 var(--ink), 0 0 10rem rgba(6, 40, 26, .6);
}
.world-name .wn-en::before, .world-name .wn-en::after {
  content: ''; width: 30rem; height: 2rem; border-radius: 1rem;
  background: linear-gradient(90deg, transparent, #DC9A5C 40%, #FBDDAE 50%, #DC9A5C 60%, transparent);
}
@keyframes hcWorldName { 0% { opacity: 0; transform: translateY(6rem) scale(.96); }
                         15% { opacity: 1; transform: none; }
                         60% { opacity: 1; transform: none; }
                         100% { opacity: 0; transform: translateY(-4rem); } }

/* First-run hint pill (ui.showHint): neutral ink, not a hazard colour, so
   amber and coral stay the only saturated things in the frame. Bottom centre,
   under the runner's feet (~80% down) and between the mission and magnet
   corners, so it never sits on the road the hazard is coming down. Its
   distance from the bottom edge is 16rem or the home indicator's inset,
   whichever is more (not their sum: on an iPhone held sideways, 16rem + its
   21 px inset put the pill over the runner's feet, ~305rem down). */
#run-hint {
  position: absolute; inset-inline: 0;
  bottom: max(16rem, var(--sa-b));
  margin: auto; width: fit-content; max-width: 200rem;
  padding: 6rem 14rem; border-radius: 20rem;
  border: 2.5rem solid transparent;
  background: var(--paint), var(--teal-fill), var(--rim);
  box-shadow: var(--frame);
  color: var(--cream); font: 700 11.5rem/1.2 var(--ui); text-align: center;
  text-shadow: 0 1.5rem 0 var(--ink);
  pointer-events: none;
  animation: hcNudge 1s ease-in-out infinite;
}
#run-hint[lang="ar"] { font-family: var(--ar); }
@keyframes hcNudge { 50% { transform: translateY(-4rem); } }

/* The 3-2-1 resume count (ui.showCountdown), over the frozen frame. Each
   number punches in and settles; the JS replays the animation per step. */
.countdown {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
  font: 400 84rem/1 var(--display); color: var(--cream);
  -webkit-text-stroke: 5rem var(--outline); paint-order: stroke fill;
  text-shadow: 0 6rem 0 var(--outline);
  animation: hcCount .7s cubic-bezier(.2, 1.5, .4, 1) both;
}
@keyframes hcCount {
  0%   { transform: scale(1.6); opacity: 0; }
  25%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(.9);  opacity: .85; }
}
.combo .combo-label { font: 700 10rem/1 var(--ar); color: #FFF2DA; text-shadow: 0 1.5rem 0 rgba(60, 14, 2, .8); }

/* The bottom corners: the mission at the inline start, the magnet timer at
   the inline end, the runner in the clear middle between. */
.hud-bottom { display: flex; align-items: flex-end; gap: 12rem; }
.hud-boosts { display: flex; gap: 8rem; margin-inline-start: auto; }
.boost-slot {
  width: 46rem; height: 46rem;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3rem;
  background: var(--orn-sm), var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 9rem;
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
}
.boost-slot .mark.magnet { width: 30rem; height: 15rem; border: 0; border-radius: 0; background: url(../../../assets/logo_token.png) center / contain no-repeat; }
.boost-slot em { font: 600 9rem/1 var(--mono); font-style: normal; color: var(--fire-1); }

/* A compact two-line card, fixed width so a new label never resizes it: the
   label on top, then the bar and the live count. */
.hud-mission {
  width: 190rem; flex: none;
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 5rem 8rem;
  padding: 7rem 11rem 8rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 10rem;
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
}
.hud-mission-text {
  grid-column: 1 / -1;
  font: 700 9.5rem/1.3 var(--ui); color: rgba(240, 228, 204, .9);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hud-mission .mini-bar { grid-row: 2; grid-column: 1; }
/* Live "cur/goal", always LTR so the slash never flips. */
.mission-n {
  flex: none;
  font: 600 9.5rem/1 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--text-mute);
}
.hud-mission .mission-n { grid-row: 2; grid-column: 2; }
/* Goal reached mid-run: fire outline and count, and one double pop. It pays
   out when the run is banked. */
.hud-mission.is-done { background: var(--paint), var(--teal-fill), linear-gradient(#FFE6A8, var(--fire-1) 40%, #C7701E) border-box; }
.hud-mission.is-done .mission-n { color: var(--fire-1); }
.hud-mission.pop { animation: hcBadge .5s ease-in-out 2; }
/* On a phone 1rem is only ~1.08 CSS px (either way up), which left the small
   HUD labels at 9-10 px: floor them at 11 px there. Desktop and cabinet sizes
   (1rem = 1.5-3 CSS px) are already above the floor and do not change. */
@media (hover: none) and (pointer: coarse) {
  .hud-score-label, .hud-score-label .ar { font-size: max(8.5rem, 11px); }
  .hud-mission-text, .hud-mission .mission-n { font-size: max(9.5rem, 11px); }
  #hud .hc-chip .k { font-size: max(9rem, 11px); }
}

/* ===================================================================== MENU */
#menu { z-index: 10; flex-direction: row; justify-content: space-between; gap: 16rem; }
/* Scrim only where type sits on the render: the two side columns. The logo
   and the stat line ride over a bright daylight sky, and the middle third
   has to stay clear, because dimming the runner is the one thing this
   screen must not do. Symmetric, so it needs no RTL twin. The stops are in
   rem from each edge (exactly today's 20 / 31 / 37 % of a 640rem stage), so
   on a wider stage the dark stays on the columns at the edges and on a 4:3
   one it never reaches past them. Portrait has its own, top and bottom. */
#menu::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(90deg,
    rgba(6, 12, 16, .88) 0,
    rgba(8, 15, 20, .62) min(128rem, 20%),
    rgba(9, 18, 23, .16) min(198.4rem, 31%),
    rgba(9, 18, 23, 0) min(236.8rem, 37%),
    rgba(9, 18, 23, 0) max(calc(100% - 236.8rem), 63%),
    rgba(9, 18, 23, .16) max(calc(100% - 198.4rem), 69%),
    rgba(8, 15, 20, .62) max(calc(100% - 128rem), 80%),
    rgba(6, 12, 16, .88) 100%);
}

/* The two side columns. ~200rem each leaves the middle ~210rem for the
   runner, which the menu camera stands dead centre (js/main.js MENU_CAM). */
.menu-col {
  width: 200rem; max-width: 36%; min-height: 0;
  display: flex; flex-direction: column; gap: 9rem;
}

/* The coin readout in the inline-end corner. Taken out of the column's flow
   (the column is the containing block) so the runner pick, PLAY and settings
   centre on the same line as the logo and stats opposite. */
.menu-end { position: relative; }
.menu-end .wallet { position: absolute; top: 0; inset-inline-end: 0; display: flex; }

.menu-logo {
  display: block;
  width: 100%;
  margin: 2rem auto 0;
  filter: drop-shadow(0 6rem 0 rgba(0, 0, 0, .55)) drop-shadow(0 0 24rem rgba(255, 160, 70, .32));
  animation: hcFloat 6s ease-in-out infinite;
}

/* Flexible space: centres each column's cluster in the height left over. */
.menu-gap { flex: 1; min-height: 0; }

#char-select {
  display: flex; align-items: center; gap: 8rem;
  padding: 6rem 7rem;
  background: var(--orn-sm), var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 10rem;
  box-shadow: var(--frame), 0 5rem 0 1.5rem var(--drop);
}
.char-arrow {
  width: 34rem; height: 34rem; font-size: 19rem;
  background: var(--paint), var(--teal-fill), var(--rim);   /* too small for scrolls */
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
}
/* 34rem is ~37 CSS px on a phone (either way up): an apron takes it past 44.
   The inset counts from the padding box (inside the 3rem rim), so -8rem makes
   a 44rem target (48 px at 1.09 px/rem); -5rem gave 38rem, 41 px. */
.char-arrow::after { content: ''; position: absolute; inset: -8rem; }
.char-arrow:active, .char-arrow.is-down {
  box-shadow: var(--frame), 0 1rem 0 1.5rem var(--drop);
}
#char-preview { flex: 1; text-align: center; min-width: 0; }
#char-name {
  font: 900 15rem/1.15 var(--ui); color: var(--cream);
  text-shadow: 0 2rem 0 var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#char-sub {
  margin-top: 2rem;
  font: 400 8.5rem/1 var(--display); letter-spacing: .14em;
  color: rgba(226, 210, 178, .5);
}

/* PLAY is the menu's one shout: the full block, with the runner pick above
   it and settings, the quiet way out, under it. */
.menu-end #start-btn { margin-top: 4rem; }

/* Settings: a teal plaque one step down from PLAY, its mark and the two
   labels on one centred line. */
.nav-tile {
  min-height: 42rem;   /* 45 CSS px on a landscape phone */
  padding: 6rem 12rem;
  display: flex; align-items: center; justify-content: center; gap: 8rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 9rem;
  box-shadow: var(--frame), 0 5rem 0 1.5rem var(--drop);
  transition: transform .06s, box-shadow .06s;
}
.nav-tile:active, .nav-tile.is-down {
  transform: translateY(4rem);
  box-shadow: var(--frame), 0 1rem 0 1.5rem var(--drop);
}
/* Narrower than the two full-width blocks above it, centred under PLAY. */
.menu-end .menu-settings { margin-top: 6rem; align-self: center; min-width: 64%; }
.nav-tile > span { font: 700 12rem/1 var(--ui); color: var(--cream); text-shadow: 0 1.5rem 0 var(--ink); white-space: nowrap; }
.nav-tile em {
  font: 600 8rem/1 var(--mono); font-style: normal;
  letter-spacing: .08em; color: rgba(226, 210, 178, .55);
}

/* Keyboard wording vs. swipe wording. The query matches IS_TOUCH in ui.js. */
.only-touch { display: none; }
@media (hover: none) and (pointer: coarse) {
  .only-key { display: none; }
  .only-touch { display: block; }
}

/* ---- arcade cabinet (?arcade, html.arcade) -------------------------------
   What the old wide-screen side panels said, now inside the stage: the
   stick and button map under the logo and stats, and a blinking PRESS START
   under settings. */
.arcade-ctrls, .arcade-start { display: none; }
html.arcade .menu-logo { width: 74%; }
html.arcade .arcade-ctrls {
  display: grid; grid-template-columns: 1fr 1fr; gap: 5rem 8rem;
  list-style: none;
  padding: 7rem 9rem;
  background: var(--orn-sm), var(--paint), var(--teal-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 9rem;
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
}
.arcade-ctrls li { display: flex; align-items: center; gap: 5rem; min-width: 0; }
.arcade-ctrls li.wide { grid-column: 1 / -1; }
.arcade-ctrls i {
  flex: none; min-width: 30rem; padding: 3rem 4rem; text-align: center;
  font: 400 7.5rem/1 var(--display); font-style: normal; color: var(--ice-1);
  background: var(--slab-2); border: 1.5rem solid #8A4E26; border-bottom-width: 3rem;
  border-radius: 4rem; box-shadow: 0 0 0 1rem var(--ink);
}
.arcade-ctrls i.btn { color: var(--fire-ink); background: linear-gradient(#FFE07A, var(--fire-2)); border-color: #5A2A0C; }
.arcade-ctrls i.logo-mark { height: 16rem; background: url(../../../assets/logo_token.png) center / 90% no-repeat, var(--slab-2); }
.arcade-ctrls b { font: 700 9rem/1 var(--ar); color: var(--cream); white-space: nowrap; }
.arcade-ctrls span { font: 400 6.5rem/1 var(--display); letter-spacing: .06em; color: rgba(226, 210, 178, .55); white-space: nowrap; }
html.arcade .arcade-start {
  display: flex; flex-direction: column; align-items: center; gap: 3rem;
  align-self: center; width: fit-content;
  padding: 5rem 16rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 2.5rem solid transparent; border-radius: 14rem;
  box-shadow: var(--frame);
}
/* Two lines, not one: an Arabic run and a Latin run on one RTL line read
   back to front. Only the words blink, never the plaque. */
.arcade-start b, .arcade-start span { animation: hcBlink 1.2s steps(2, jump-none) infinite; }
.arcade-start b { font: 700 11rem/1 var(--ar); color: var(--fire-1); }
.arcade-start span { font: 400 8.5rem/1 var(--display); letter-spacing: .1em; color: var(--cream); }
@keyframes hcBlink { 50% { opacity: .25; } }

/* ============================================================ sheet screens */
.sheet {
  z-index: 12; gap: 10rem;
  background:
    radial-gradient(90% 70% at 50% 0%, rgba(52, 88, 102, .55), transparent 70%),
    linear-gradient(rgba(16, 32, 40, .97), rgba(9, 18, 23, .99) 40%),
    var(--night);
}
.sheet-head { display: flex; align-items: center; gap: 9rem; flex: none; }
/* 36rem is ~39 CSS px on a landscape phone: an apron takes it past 44. */
.back-btn { width: 36rem; height: 36rem; font-size: 20rem; }
.back-btn::after { content: ''; position: absolute; inset: -5rem; }
/* The glyph is U+2039, which the browser mirrors on its own inside an RTL run,
   so it points back in both directions with no CSS help. */
.sheet-title { flex: 1; min-width: 0; text-align: start; }
.sheet-title > div { font: 900 15rem/1.15 var(--ui); color: var(--cream); text-shadow: 0 2rem 0 var(--ink); }
.sheet-title em {
  /* its own line, clear of the Arabic title's descenders (the hamza under
     الإعدادات touched it), and inked */
  display: block; margin-top: 3rem;
  font: 400 8rem/1 var(--display); font-style: normal;
  letter-spacing: .12em; color: rgba(226, 210, 178, .62);
  text-shadow: 0 1.2rem 0 var(--ink), 0 0 5rem rgba(4, 8, 10, .85);
}

.sheet-body {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 8rem;
  padding-bottom: 6rem;
  touch-action: pan-y;
  scrollbar-width: none;
}
.sheet-body::-webkit-scrollbar { display: none; }
/* Landscape sheets: two columns side by side, scrolling together. With only
   ~280rem of height under the head, one long column wasted the width. */
.sheet-body.cols {
  display: grid; grid-template-columns: 1fr 1fr; align-items: start; gap: 0 18rem;
  padding: 2rem 2rem 6rem;   /* room for the 1.5rem ink line outside each rim */
}
.sheet-col { display: flex; flex-direction: column; gap: 8rem; min-width: 0; }

.sec-label {
  margin-top: 4rem;
  font: 700 9rem/1 var(--ui); letter-spacing: .1em; text-transform: uppercase;
  color: #E8B27A;
  text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .8);
}
.sheet-col > .sec-label:first-child { margin-top: 2rem; }

/* =============================================================== settings */
.set-group { padding: 4rem 12rem; display: flex; flex-direction: column; }
.set-row {
  display: flex; align-items: center; gap: 10rem; flex-wrap: wrap;
  padding: 6rem 0;
  border-bottom: 2rem solid rgba(246, 200, 140, .08);
}
.set-row:last-child { border-bottom: none; }
.set-k { flex: 1; font: 700 11rem/1 var(--ui); color: var(--cream); }
.set-v { order: 2; font: 600 11rem/1 var(--mono); color: var(--fire-1); min-width: 26rem; text-align: end; }

/* Settings fits its two columns without scrolling at 640x360; nothing at
   the foot of either column casts a bottom face, so no scroll apron. */
#screen-settings .sheet-body { padding-bottom: 2rem; }
#set-reset { flex: none; padding-block: 10rem; }
/* rows holding a 41rem segmented control need little padding of their own */
.set-row:has(.seg) { padding-block: 2rem; }
/* Landscape: label, slider, value on one line. Height is the scarce axis,
   and a slider on its own line cost each row ~30rem. */
.set-row:has(.slider) { padding-block: 0; }
.set-row:has(.slider) .set-k { flex: none; min-width: 60rem; }
/* 41rem tall (44 CSS px on a landscape phone) for the finger; the track and
   thumb still draw at their own size in the middle. */
.slider {
  order: 1; flex: 1 1 100rem; height: 41rem; margin: 0;
  -webkit-appearance: none; appearance: none; background: transparent;
  touch-action: pan-x;
}
.slider::-webkit-slider-runnable-track {
  height: 12rem; border-radius: 7rem;
  background: var(--well); border: 2rem solid #9A5A2C;
  box-shadow: inset 0 2rem 0 rgba(0, 0, 0, .6);
}
.slider::-moz-range-track {
  height: 12rem; border-radius: 7rem;
  background: var(--well); border: 2rem solid var(--outline);
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20rem; height: 20rem; margin-top: -6rem; border-radius: 5rem;
  background: linear-gradient(#FFE6A8, var(--fire-1) 40%, var(--fire-3));
  border: 2rem solid var(--ink);
  box-shadow: 0 2rem 0 #4A1F09;
}
.slider::-moz-range-thumb {
  width: 20rem; height: 20rem; border-radius: 5rem;
  background: linear-gradient(var(--fire-1), var(--fire-3));
  border: 2rem solid var(--fire-ink);
}

.toggle {
  position: relative;
  width: 48rem; height: 26rem; flex: none; padding: 2rem;
  background: var(--well); border: 2.5rem solid #9A5A2C; border-radius: 16rem;
  box-shadow: 0 0 0 1.2rem var(--ink), inset 0 2rem 0 rgba(0, 0, 0, .5);
  display: flex; align-items: center;
  transition: background .15s;
}
.toggle > i {
  display: block; width: 18rem; height: 18rem; border-radius: 5rem;
  background: linear-gradient(#F3C38C, #B06A35 60%, #7B3E1A);
  border: 1.5rem solid var(--ink);
  transition: transform .15s, background .15s;
}
/* the switch is 26rem tall: an apron takes its hit area past 44 px */
.toggle::after { content: ''; position: absolute; inset: -10rem -8rem; }
.toggle.is-on { background: linear-gradient(var(--ice-3), var(--ice-deep)); }
.toggle.is-on > i { background: linear-gradient(#FFF0C4, var(--fire-1) 50%, #C7701E); transform: translateX(20rem); }
:root[dir='rtl'] .toggle.is-on > i { transform: translateX(-20rem); }

.seg { display: flex; flex: none; border: 2.5rem solid #A9622F; border-radius: 8rem; overflow: hidden; box-shadow: 0 0 0 1.2rem var(--ink); }
.seg button { min-height: 41rem; min-width: 44rem; padding: 6rem 13rem; background: var(--slab-2); font: 700 10rem/1 var(--ui); color: rgba(226, 210, 178, .65); }
.seg button + button { border-inline-start: 1.5rem solid var(--ink); }
.seg button.is-on { background: var(--amber-fill); color: var(--fire-ink); }
/* A shade, not a translate: the segments sit flush inside overflow:hidden. */
.seg button:active, .seg button.is-down { filter: brightness(.8); }
/* Keyboard focus drawn inside the segment (the default ring is outside and
   overflow:hidden clips it), in cream so it shows on the amber selected
   segment too; inset from the edge so the rounded ends cannot eat it. */
.seg button:focus-visible { outline: 2rem solid var(--cream); outline-offset: -4rem; }
.set-note { padding: 3rem 0 3rem; font: 400 9rem/1.4 var(--ui); color: rgba(226, 210, 178, .4); }

.ctrl-row {
  display: flex; align-items: center; gap: 10rem;
  padding: 3rem 0; border-bottom: 2rem solid rgba(246, 200, 140, .08);
}
.ctrl-row:last-child { border-bottom: none; }
.ctrl-row b { font: 700 10.5rem/1 var(--ui); color: var(--cream); }
.key {
  min-width: 58rem; padding: 4rem 8rem; text-align: center;
  background: var(--slab-2); border: 2rem solid #8A4E26;
  border-bottom-width: 4rem; border-radius: 5rem;
  box-shadow: 0 0 0 1rem var(--ink);
  font: 600 9rem/1 var(--mono); color: var(--text);
}
.set-version { padding: 0 4rem; text-align: center; font: 600 8rem/1 var(--mono); color: rgba(226, 210, 178, .3); }

/* =================================================== LEADERBOARD + SIGN-IN */
/* js/board.js: the sign-in sheet (gate and rename), the menu plaque, the
   full board sheet, the game-over rank and the settings player row. Same
   painted plaque recipe as everything else; names are any script, so every
   name cell is one line with an ellipsis and overflow:hidden (a fallback font
   for Cyrillic or CJK must never grow a row). */

/* ---- the menu plaque (replaces the old stats line) ----------------------
   A button with a FIXED height: the column is centred by flexible gaps, so a
   plaque that grew with its content would make the column jump. Rows: head,
   five names, the player's own row, the lifetime distance. */
.menu-board {
  position: relative; flex: none;
  width: 184rem; height: 133rem; margin: 0 auto;
  display: grid; grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 15rem 65rem 1fr 11rem;
  padding: 6rem 11rem 7rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 2.5rem solid transparent; border-radius: 12rem;
  box-shadow: var(--frame), 0 5rem 0 1.5rem var(--drop);
  text-align: start; color: var(--cream);
  transition: transform .06s, box-shadow .06s, filter .12s;
}
.menu-board:active, .menu-board.is-down {
  transform: translateY(3rem);
  box-shadow: var(--frame), 0 1rem 0 1.5rem var(--drop);
}
@media (hover: hover) { .menu-board:hover { filter: brightness(1.08); } }
.menu-board > * { grid-column: 1; min-width: 0; }
.lb-head { grid-row: 1; display: flex; align-items: baseline; gap: 5rem; white-space: nowrap; }
.lb-head .lb-t { font: 700 9rem/1 var(--ui); color: #E8B27A; text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .8); }
.lb-head em { font: 400 7rem/1 var(--display); font-style: normal; letter-spacing: .1em; color: rgba(226, 210, 178, .5); }
/* a small cast-bronze pill: the plaque is the button, this says so */
.lb-all {
  margin-inline-start: auto; align-self: center;
  padding: 2rem 6rem 2.5rem; border-radius: 7rem;
  background: var(--paint), linear-gradient(#FBDDAE, #D99657 35%, #A75F2C) padding-box;
  box-shadow: 0 0 0 1rem var(--ink);
  font: 700 7.5rem/1 var(--ui); color: var(--fire-ink);
}
.lb-rows { grid-row: 2; display: grid; grid-template-rows: repeat(5, 13rem); }
.lb-rows.is-dim { opacity: .45; }
.lb-row { display: grid; grid-template-columns: 14rem minmax(0, 1fr) auto; align-items: center; gap: 6rem; }
.lb-rows .lb-row .r { text-align: center; font: 600 9rem/1 var(--mono); color: rgba(226, 210, 178, .55); }
.lb-row .n {
  min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font: 700 10rem/1.25 var(--ui); color: var(--cream);
}
/* The name truncates inside its own direction (the bdi), so a Latin name in
   the Arabic UI (or an Arabic one in English) loses its end, not its start;
   the column keeps the page's side. Not text-align: match-parent, which
   Chrome resolves against the dir=auto bdi itself. */
.lb-row .n > bdi { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: right; }
:root[dir='ltr'] .lb-row .n > bdi { text-align: left; }
.lb-row .s { font: 700 10rem/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--cream); }
.lb-rows .lb-row.is-me .n, .lb-rows .lb-row.is-me .s, .lb-rows .lb-row.is-me .r { color: var(--fire-1); }
/* the offline / empty line sits over the rows (they keep their space) */
.lb-msg {
  grid-row: 2; align-self: center; text-align: center;
  font: 700 9.5rem/1.35 var(--ui); color: var(--cream);
}
.lb-msg.is-off { font-weight: 600; color: var(--text-mute); }
/* the player's own row: rank, YOU (or PLAY TO GET RANKED), best */
.lb-you {
  grid-row: 3; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 6rem;
  margin-top: 3rem; border-top: 2rem solid rgba(246, 200, 140, .12);
  color: var(--fire-1);
}
.lb-you .r { font: 700 9.5rem/1 var(--mono); }
.lb-you .n { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: 800 10rem/1.25 var(--ui); }
.lb-you .s { font: 700 10.5rem/1 var(--mono); font-variant-numeric: tabular-nums; }
.lb-foot {
  grid-row: 4; align-self: center; text-align: center; white-space: nowrap; overflow: hidden;
  font: 600 8.5rem/1 var(--ui); color: rgba(226, 210, 178, .6);
}
.lb-foot b { font: 700 9rem/1 var(--mono); color: var(--cream); }
/* An arcade cabinet: the top five only, read-only (board.js drops data-goto). */
html.arcade .menu-board { height: auto; grid-template-rows: 15rem 65rem; pointer-events: none; }
html.arcade .lb-all, html.arcade .lb-you, html.arcade .lb-foot { display: none; }
/* a phone (either way up): 1rem is ~1.08 CSS px, so the 9-10rem labels get an 11 px floor */
@media (hover: none) and (pointer: coarse) {
  .lb-row .n, .lb-row .s, .lb-you .n, .lb-you .s, .lb-msg { font-size: max(10rem, 11px); }
  .lb-rows .lb-row .r, .lb-you .r { font-size: max(9rem, 10.5px); }
}

/* ---- the board sheet ------------------------------------------------------
   One centred list, 22rem a row; the player's row amber, the podium in
   medallions. The pinned row under it is the player when they rank outside
   the top 100 (or the "play to get ranked" line). */
#screen-leaderboard .sheet-body { position: relative; align-items: center; padding: 2rem 2rem 8rem; }
.lb-list { flex: none; width: min(100%, 470rem); padding: 4rem 10rem; list-style: none; }
.lb-big {
  height: 22rem; padding-inline: 6rem; border-radius: 4rem;
  grid-template-columns: 34rem minmax(0, 1fr) 64rem 72rem; gap: 8rem;
}
/* :not(.is-me): the stripe (0,3,0) would beat the own row's amber (0,2,0)
   and leave its ink text on the dark slab at every even rank */
.lb-list .lb-big:nth-child(even):not(.is-me) { background: rgba(255, 255, 255, .03); }
.lb-big .r { text-align: center; }
.lb-big .r i {
  display: inline-grid; place-items: center; min-width: 18rem; height: 18rem; padding: 0 3rem; border-radius: 9rem;
  font: 600 9.5rem/1 var(--mono); font-style: normal; color: var(--text-mute);
}
.lb-big.m1 .r i, .lb-big.m2 .r i, .lb-big.m3 .r i { font-weight: 700; color: var(--fire-ink); box-shadow: 0 0 0 1.2rem var(--ink), inset 0 1.5rem 0 rgba(255, 255, 255, .45); }
.lb-big.m1 .r i { background: #FFD27A; }
.lb-big.m2 .r i { background: #D9E4E8; }
.lb-big.m3 .r i { background: #C98446; }
.lb-big .n { font: 700 11.5rem/1.3 var(--ui); }
.lb-big .d { text-align: right; font: 600 9.5rem/1 var(--mono); color: var(--text-mute); white-space: nowrap; }
.lb-big .s { text-align: right; font: 700 11.5rem/1 var(--mono); color: var(--fire-1); }
.lb-big.is-me { background: var(--amber-fill); box-shadow: 0 0 0 1.2rem var(--ink); }
.lb-big.is-me .n, .lb-big.is-me .d, .lb-big.is-me .s, .lb-big.is-me .r i { color: var(--fire-ink); }
#lb-pin {
  flex: none; width: min(100%, 470rem); height: 28rem; margin: 0 auto;
  padding-inline: 14rem;
  background: var(--paint), var(--amber-fill), var(--rim);
  border: 2.5rem solid transparent; border-radius: 8rem;
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
  /* the rank column grows for "#12,345" (past 10,000 players) instead of
     spilling into the name; the 34rem floor keeps it lined up with the list */
  grid-template-columns: minmax(34rem, max-content) minmax(0, 1fr) 64rem 72rem;
}
/* Unranked: "PLAY TO GET RANKED" is a hint, not a control (the pin has no
   handler), so it drops the raised amber button look for the menu plaque's
   flat fire text. The cells are recoloured directly: .is-me sets them to ink. */
#lb-pin.is-cta { background: none; border-color: transparent; box-shadow: none; }
#lb-pin.is-cta .n, #lb-pin.is-cta .r i { color: var(--fire-1); font-weight: 800; }
.lb-state { padding: 40rem 0; text-align: center; font: 600 11rem/1.4 var(--ui); color: var(--text-mute); }
#lb-me-chip { font-size: 11rem; }
#lb-me-chip[lang="ar"] { font: 700 11rem/1 var(--ar); }

/* ---- the sign-in sheet ----------------------------------------------------
   Over the loading screen at first launch (z 21 > 20), opaque, so the city
   idles behind it. Everything a finger must touch ends by ~125rem down: a
   landscape phone's keyboard leaves about the top 130-175rem of the stage
   visible (board.js freezes the stage while a field has focus). Portrait
   stacks the row and ends the submit at ~224rem, the top third (PORTRAIT). */
#screen-signin { z-index: 21; gap: 8rem; }
.signin[data-mode="gate"] .back-btn { display: none; }
/* the sub-title is dir=auto (its "?" must end the line in either script), so
   its side follows the page, not its own direction */
#signin-title-sub { text-align: right; }
:root[dir='ltr'] #signin-title-sub { text-align: left; }
/* an empty dir=auto input resolves LTR, which put the Arabic placeholder and
   the caret on the left; until something is typed it follows the page */
:root[dir='rtl'] #signin-name:placeholder-shown { direction: rtl; }
.signin .sheet-head { min-height: 36rem; }
/* 36rem with the seg's rim: the head stays 36rem tall (y 14-50) */
.signin .seg button { min-height: 31rem; padding: 4rem 12rem; }
.signin-form { display: flex; flex-direction: column; }
.signin-row { display: grid; grid-template-columns: 1fr 1.3fr auto; gap: 9rem; align-items: end; }
.signin[data-mode="rename"] .signin-row { grid-template-columns: minmax(0, 300rem) auto; justify-content: center; }
.signin[data-mode="rename"] #signin-email-f { display: none; }
.field { display: flex; flex-direction: column; gap: 2rem; min-width: 0; }
.field-k {
  font: 700 8.5rem/1 var(--ui); color: #E8B27A;
  text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .8);
}
/* The wells of the settings controls, at finger height (41rem is 44 CSS px
   on a landscape phone). 16 px text at least: iOS zooms into anything less. */
.field input {
  width: 100%; height: 41rem; padding: 0 10rem;
  background: var(--well); border: 2.5rem solid #9A5A2C; border-radius: 8rem;
  box-shadow: 0 0 0 1.2rem var(--ink), inset 0 2rem 0 rgba(0, 0, 0, .5);
  color: var(--cream); font: 700 max(13rem, 16px)/1 var(--ui);
  -webkit-user-select: text; user-select: text; touch-action: manipulation;
  -webkit-touch-callout: default;   /* html sets none (the logo's save sheet); paste needs it here */
  outline: none; transition: border-color .12s;
}
.field input:focus { border-color: var(--fire-1); }
.field input.is-bad, .field input[aria-invalid="true"] { border-color: var(--danger); }
/* the placeholder may be smaller than the 16 px text (only the text size
   decides iOS zoom): the long name hint fits the phone's narrower field */
.field input { text-overflow: ellipsis; }
.field input::placeholder { color: rgba(226, 210, 178, .4); font-weight: 600; font-size: 11.5rem; }
/* the browser's autofill paint would be a pale yellow box in a dark well */
.field input:-webkit-autofill, .field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--cream);
  box-shadow: 0 0 0 1.2rem var(--ink), inset 0 0 0 100rem var(--well);
  caret-color: var(--cream);
}
#signin-go { flex: none; min-width: 128rem; padding: 4rem 18rem; justify-content: center; }
.signin-err {
  height: 12rem; margin-top: 3rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font: 700 10rem/1.2 var(--ui); color: var(--danger);
}
.signin-note {
  margin-top: 4rem; text-align: center;
  font: 400 10.5rem/1.5 var(--ui); color: rgba(240, 228, 204, .6);
}
.signin-logo {
  align-self: center; width: 210rem; margin-top: 12rem;
  filter: drop-shadow(0 6rem 0 rgba(0, 0, 0, .55)) drop-shadow(0 0 24rem rgba(255, 160, 70, .32));
  animation: hcFloat 6s ease-in-out infinite;
  pointer-events: none;
}
.signin-load {
  margin-top: auto; margin-bottom: 16rem; text-align: center;
  font: 600 9rem/1 var(--ui); color: var(--text-mute);
}
.signin-load b { font: 700 9rem/1 var(--mono); }

/* ---- settings: who is playing (in the head) ------------------------------ */
.set-player { display: flex; align-items: center; gap: 7rem; min-width: 0; flex: none; }
.set-player-k { font: 700 9rem/1 var(--ui); color: #E8B27A; text-shadow: 0 1.5rem 0 rgba(10, 6, 2, .8); }
.set-player-n {
  display: block; max-width: 110rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font: 700 11rem/1.3 var(--ui); color: var(--cream); text-shadow: 0 1.5rem 0 var(--ink);
}
/* 36rem tall with the seg's rim, like the back button: a taller head pushed
   the two columns below into a scroll */
.set-player-acts button { min-height: 31rem; min-width: 0; padding: 4rem 9rem; font-size: 9.5rem; white-space: nowrap; }
/* Those 31rem buttons (here and in the sign-in head) are 34 CSS px on the
   phone; a vertical apron takes the finger target to ~44 px without growing
   either head. Only these two segs overflow, so their outer buttons take
   the rim's rounding (8rem - 2.5rem) themselves. */
.signin .seg, .set-player-acts { overflow: visible; }
.signin .seg button, .set-player-acts button { position: relative; }
.signin .seg button::after, .set-player-acts button::after { content: ''; position: absolute; inset: -5rem 0; }
.signin .seg button:first-child, .set-player-acts button:first-child { border-start-start-radius: 5.5rem; border-end-start-radius: 5.5rem; }
.signin .seg button:last-child, .set-player-acts button:last-child { border-start-end-radius: 5.5rem; border-end-end-radius: 5.5rem; }

/* ---- game over: "GLOBAL #12 OF 340" on the distance line ----------------- */
.go-rank { white-space: nowrap; color: var(--fire-1); }
.go-rank b { font: 700 11rem var(--mono); }
.go-rank.is-dim { opacity: .55; }
.go-rank.is-best #go-rank-t::before { content: '★ '; }

/* ================================================================ POWER-UPS */
/* The coin-bought Fast Start and Continue (js/main.js, js/ui.js,
   _integration/POWERUPS_SPEC.md §4), in the bronze HUD's own materials: the
   amber plaque of the combo pill and PLAY, the teal chips, the cast-bronze
   tab. Positions are logical (inline-start / -end), so Arabic mirrors them;
   a `dir="ltr"` sits on an inner span only, never on a positioned box (its
   own inset-inline would then resolve LTR and land on the wrong corner). */

/* ---- Fast Start offer: the HUD's bottom inline-end corner, first 4 s of a run */
.fs-offer {
  position: relative; flex: none; pointer-events: auto;
  display: flex; align-items: center; gap: 7rem;
  height: 50rem; padding: 0 13rem 3rem 10rem;
  background: var(--orn-sm), var(--paint), var(--amber-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 10rem;
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 240, 200, .32), 0 6rem 0 #4A1F09, 0 6rem 0 1.5rem var(--ink);
  touch-action: manipulation;
  transition: transform .06s, box-shadow .06s;
  animation: hcRise .32s cubic-bezier(.2, 1.4, .4, 1) both;
}
/* pressed like an .hc-btn: the block's bottom face collapses */
.fs-offer:active, .fs-offer.is-down {
  transform: translateY(3rem);
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 240, 200, .32), 0 3rem 0 #4A1F09, 0 3rem 0 1.5rem var(--ink);
}
/* an invisible apron: a fingertip that lands just off the pill still buys */
.fs-offer::after { content: ''; position: absolute; inset: -8rem; }
.fs-bolt { flex: none; display: block; width: 20rem; height: 27rem; overflow: visible; }
.fs-bolt path { fill: #FFF3D2; stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; paint-order: stroke; }
.fs-txt { display: flex; flex-direction: column; gap: 4rem; text-align: start; }
.fs-txt b { font: 800 14rem/1 var(--ar); color: var(--fire-ink); white-space: nowrap; text-shadow: 0 1.5rem 0 rgba(255, 228, 170, .55); }
.fs-txt small { font: 400 8rem/1 var(--display); letter-spacing: .08em; color: rgba(42, 18, 6, .62); white-space: nowrap; }
.fs-txt b[lang="en"] { font: 400 12rem/1 var(--display); letter-spacing: .04em; }
.fs-txt small[lang="ar"] { font: 700 9rem/1 var(--ar); letter-spacing: 0; }
/* the price tag over the inline-end corner (pill and CONTINUE alike) */
.pu-cost {
  position: absolute; top: -13rem; inset-inline-end: -9rem;
  display: flex; align-items: center; gap: 3rem; padding: 3rem 6rem 3rem 7rem;
  background: var(--paint), var(--teal-fill), var(--rim);
  border: 2rem solid transparent; border-radius: 12rem;
  box-shadow: 0 0 0 1.2rem var(--ink), 0 2.5rem 0 1.2rem var(--drop);
  font: 400 10rem/1 var(--display); color: var(--fire-1); text-shadow: 0 1.2rem 0 var(--ink);
  white-space: nowrap;
}
.pu-cost > span { display: flex; align-items: center; gap: 3rem; }
.pu-cost .pip-coin { width: 11rem; height: 11rem; border-width: 1.5rem; }
/* the key cap over the inline-start corner: ENTER / ESC, A / B, RB (hidden on touch) */
.pu-kcap {
  position: absolute; top: -11rem; inset-inline-start: -7rem;
  padding: 3rem 5rem; border-radius: 4rem;
  background: var(--slab-2); border: 1.5rem solid #8A4E26; border-bottom-width: 3rem;
  box-shadow: 0 0 0 1rem var(--ink);
  font: 600 7.5rem/1 var(--mono); color: var(--ice-1);
}
.pu-kcap[hidden] { display: none; }
#cont-yes .pu-kcap { top: -10rem; inset-inline-start: -6rem; }
#cont-no .pu-kcap { top: -10rem; inset-inline-start: auto; inset-inline-end: -6rem; }
/* 10rem in from the corner, clear of the NO button's key cap; and 5rem higher
   than the pill's default since the price has 4 digits (2,500, 2026-10-06): at
   -13rem the wider tag sat on the top of the label ("CONTINUE" lost the top of
   its UE, أكمل the top of its ك) */
#cont-yes .pu-cost { inset-inline-end: 10rem; top: -18rem; }
/* the pill's tag sits higher: in Arabic it is over the end of the title, and
   at -13rem it hid the two dots of the final ة ("سريعة" read "سريعه") */
#fs-cost { top: -18rem; }
/* the window draining along the pill's bottom edge (ui.setFastStartTime: --fs-left) */
.fs-time { position: absolute; inset-inline: 9rem; bottom: 4rem; height: 3rem; border-radius: 2rem; background: rgba(42, 18, 6, .28); overflow: hidden; }
.fs-time i { display: block; height: 100%; background: var(--fire-ink); transform-origin: left center; transform: scaleX(var(--fs-left, 1)); }
:root[dir='rtl'] .fs-time i { transform-origin: right center; }

/* ---- the dash: a badge in the combo band; the combo and the near pop give way */
.fs-badge {
  position: absolute; inset-inline: 0; top: calc(70rem + var(--sa-t));
  display: flex; flex-direction: column; align-items: center; gap: 6rem;
  pointer-events: none;
}
.fs-pill {
  position: relative;
  display: flex; align-items: center; gap: 7rem; padding: 4rem 14rem 8rem 11rem;
  background: var(--paint), var(--amber-fill), var(--rim);
  border: 3rem solid transparent; border-radius: 24rem;
  box-shadow: var(--frame), inset 0 4rem 0 rgba(255, 240, 200, .3), 0 4rem 0 1.5rem var(--drop);
}
.fs-pill .fs-bolt { width: 14rem; height: 19rem; }
.fs-pill b { font: 800 14rem/1 var(--ar); color: var(--fire-ink); white-space: nowrap; }
.fs-pill small { font: 400 8rem/1 var(--display); letter-spacing: .08em; color: rgba(42, 18, 6, .62); white-space: nowrap; }
.fs-pill b[lang="en"] { font: 400 12rem/1 var(--display); }
.fs-pill small[lang="ar"] { font: 700 9rem/1 var(--ar); letter-spacing: 0; }
.fs-pill .fs-time { inset-inline: 12rem; bottom: 3.5rem; }
.fs-pill .fs-time i { transform: scaleX(var(--fs-done, 0)); }
#hud.fs-on .combo, #hud.fs-on .near-pop { opacity: 0; }

/* ---- Continue offer: over the crash, before the game-over panel. Hung high
   like the panel (the runner's head is ~14rem under its bottom edge), the
   dim weighted to the top so the fallen runner stays readable under it. */
#continue {
  justify-content: flex-start; align-items: center;
  background: linear-gradient(rgba(5, 11, 14, .74) 0, rgba(5, 11, 14, .66) 58%, rgba(5, 11, 14, .28) 74%, rgba(5, 11, 14, .18));
}
.hc-dialog.cont {
  margin-top: 60rem; width: min(100%, 372rem);
  display: grid; grid-template-columns: 62rem minmax(0, 1fr);
  grid-template-areas: "ring head" "acts acts";
  align-items: center; gap: 8rem 13rem;
  padding: 22rem 16rem 14rem;
}
/* the 5 s ring (ui.setContinueTime: --off), its seconds in the middle */
.cont-ring { grid-area: ring; position: relative; width: 62rem; height: 62rem; }
.cont-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.cont-ring .rt { fill: rgba(8, 16, 20, .8); stroke: rgba(10, 6, 2, .75); stroke-width: 8; }
.cont-ring .rf { fill: none; stroke: var(--fire-1); stroke-width: 5; stroke-linecap: round; stroke-dasharray: 100 100; stroke-dashoffset: var(--off, 0); }
.cont-ring.is-low .rf { stroke: var(--danger); }
.cont-ring b {
  position: absolute; inset: 0; display: grid; place-items: center;
  font: 400 27rem/1 var(--display); color: var(--cream);
  -webkit-text-stroke: 1.5rem var(--ink); paint-order: stroke fill; text-shadow: 0 2rem 0 var(--outline);
}
.cont-ring.is-tick { animation: hcBadge .35s ease-out 1; }
.cont-head { grid-area: head; min-width: 0; text-align: start; }
.cont-head h2 { font: 900 17rem/1.2 var(--ui); color: var(--cream); text-shadow: 0 2rem 0 var(--ink); }
.cont-line { margin-top: 5rem; display: flex; align-items: center; gap: 5rem; flex-wrap: wrap; font: 600 10.5rem/1.2 var(--ui); color: var(--text-mute); }
.cont-line b { font: 400 12rem/1 var(--display); color: var(--fire-1); text-shadow: 0 1.2rem 0 var(--ink); }
.cont-line .pip-coin { width: 12rem; height: 12rem; border-width: 1.5rem; }
.cont-acts { grid-area: acts; margin-top: 6rem !important; }
#cont-yes { position: relative; flex: 1.35; }
#cont-no { position: relative; }
/* NO THANKS or the ring running out: the panel takes over in the same frame
   under the card's dim, so its own dim does not fade in again */
#gameover.from-offer { animation: none; }
/* ... and its buttons hold for 0.4 s (ui.showGameOver fx.hold): NO THANKS
   sits where BACK appears, so a double tap would land on the menu */
#gameover.hold .go-actions { pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .fs-offer, .cont-ring.is-tick { animation: none !important; } }
/* (the toast over the card: the toast section, after the in-run side slot it overrides) */

/* ============================================================ dialogs / popups */
.overlay-dim { justify-content: center; align-items: center; background: rgba(5, 11, 14, .72); z-index: 16; }

.hc-dialog {
  position: relative;
  padding: 22rem 14rem 14rem;
  background: var(--orn), var(--paint),
    radial-gradient(120% 80% at 50% 18%, #3A6271, #24414E 55%, #142731) padding-box, var(--rim);
  border: 3.5rem solid transparent;
  border-radius: 12rem;
  box-shadow: var(--frame), 0 8rem 0 1.5rem var(--drop),
              0 0 40rem rgba(255, 150, 60, .16);
  animation: hcPop .28s cubic-bezier(.2, 1.5, .4, 1);
  /* centred and capped: a full-width dialog on a 640rem stage stretched
     every button into a bar */
  width: min(100%, 340rem);
}
/* Game over: the run's numbers at the inline start, the way out (run
   again, back) in a column at the inline end. Side by side it is
   ~175rem tall, where stacked it would not fit the 332rem of height.
   Hung high, just under the score plaque, with the dim easing off below it:
   the runner lies at ~75-85% of the frame height and the death camera's
   push-in onto him stays in view under the dialog. */
#gameover {
  justify-content: flex-start;
  background: linear-gradient(rgba(5, 11, 14, .76) 0, rgba(5, 11, 14, .7) 58%, rgba(5, 11, 14, .3) 74%, rgba(5, 11, 14, .2));
}
.hc-dialog.go {
  margin-top: 66rem;
  width: min(100%, 492rem);
  display: grid; grid-template-columns: 1fr 196rem; align-items: center; gap: 0 16rem;
  padding: 22rem 16rem 14rem;
}
.hc-dialog.go .dialog-tip { margin-top: 8rem; min-height: 0; }
.hc-dialog.go .dialog-row { margin-top: 8rem; }
.hc-dialog.go .dialog-hint { margin-top: 8rem; }
.go-main { min-width: 0; }
.go-actions > .dialog-row:first-child { margin-top: 0; }
.dialog-tab {
  position: absolute; top: -13rem; left: 50%; transform: translateX(-50%);
  padding: 5rem 18rem; white-space: nowrap;
  /* a cast-bronze name plate riveted over the top rim */
  background: var(--paint), linear-gradient(#FBDDAE, #D99657 30%, #A75F2C 70%, #6E3616) padding-box,
              linear-gradient(#5A2C10, #2A1206) border-box;
  border: 2rem solid transparent; border-radius: 7rem;
  box-shadow: 0 0 0 1.5rem var(--ink), inset 0 2rem 0 rgba(255, 245, 220, .45), 0 3rem 0 1.5rem var(--drop);
  font: 400 12rem/1 var(--display); color: #2A1206; letter-spacing: .06em;
  text-shadow: 0 1.2rem 0 rgba(255, 236, 200, .5);
}
.dialog-tab.ice {
  background: var(--paint), var(--teal-fill), var(--rim);
  color: var(--cream); text-shadow: 0 1.5rem 0 var(--ink);
}
.dialog-tab.danger {
  background: var(--paint), linear-gradient(#E0623A, #A8301A) padding-box, var(--rim);
  color: #FFE9CC; text-shadow: 0 1.5rem 0 var(--ink);
}
.dialog-title { text-align: center; font: 900 20rem/1.2 var(--ui); color: var(--cream); text-shadow: 0 2rem 0 var(--ink); }
.dialog-sub { margin-top: 6rem; text-align: center; font: 400 11rem/1.6 var(--ui); color: rgba(240, 228, 204, .6); }
.dialog-row { margin-top: 11rem; display: flex; gap: 9rem; flex-wrap: wrap; }
.dialog-tip { margin-top: 11rem; min-height: 30rem; text-align: center; font: 700 10.5rem/1.5 var(--ui); color: var(--fire-1); }
.dialog-hint { margin-top: 10rem; text-align: center; font: 400 9.5rem/1 var(--ui); color: rgba(226, 210, 178, .7); }
.dialog-hint b { color: var(--text-ice); }

.stat-row { display: flex; gap: 9rem; }
.stat {
  flex: 1; padding: 9rem 4rem; text-align: center;
  background: var(--paint), linear-gradient(#142630, #0E1C24) padding-box, linear-gradient(#C27C45, #6A3416) border-box;
  border: 2rem solid transparent; border-radius: 7rem;
  box-shadow: 0 0 0 1.2rem var(--ink), inset 0 2rem 0 rgba(0, 0, 0, .35);
}
.stat span { display: block; font: 600 9rem/1 var(--ui); color: var(--text-mute); }
.stat b {
  display: block; margin-top: 4rem;
  font: 400 16rem/1 var(--display); font-variant-numeric: tabular-nums; color: var(--cream);
  text-shadow: 0 2rem 0 var(--ink);
}
.stat b.fire { color: var(--fire-1); }
.stat b.ice  { color: #A6ECEF; }
/* The game-over boxes pop in one after another while ui.countUp() runs. */
.stat-row .stat { animation: hcPop .3s both; }
.stat-row .stat:nth-child(2) { animation-delay: .08s; }
.stat-row .stat:nth-child(3) { animation-delay: .16s; }
.go-dist { margin: 6rem 0 0; text-align: center; font: 600 10rem/1.3 var(--ui); color: var(--text-ice); }
.go-dist b { font: 700 11rem var(--mono); }

#new-best {
  margin: 0 auto 9rem; width: fit-content; padding: 5rem 14rem;
  background: var(--paint), radial-gradient(140% 120% at 30% 20%, #5A3220, #3A1E14 50%, #22121A) padding-box, linear-gradient(#FFF0C4, var(--fire-1) 35%, #C7701E 70%, #7A3A10) border-box;
  border: 3rem solid transparent; border-radius: 8rem;
  box-shadow: var(--frame), 0 0 26rem rgba(255, 150, 50, .4);
  font: 900 11rem/1 var(--ui); color: #FFE3C0;
  animation: hcBadge 1.1s ease-in-out infinite;
}

/* ================================================================== toast */
/* Top centre: on the menu that is the free sky between the two columns, on
   a sheet the space beside its title. Lower down it would sit on the runner. */
.toast {
  position: absolute; z-index: 24;
  left: 50%; top: calc(16rem + var(--sa-t));
  transform: translateX(-50%);
  max-width: 300rem;
  padding: 9rem 16rem;
  background: var(--paint), var(--teal-fill), linear-gradient(#FFF0C4, var(--fire-1) 35%, #C7701E 70%, #7A3A10) border-box;
  border: 3rem solid transparent; border-radius: 24rem;
  box-shadow: var(--frame), 0 4rem 0 1.5rem var(--drop);
  font: 900 11rem/1.3 var(--ui); color: var(--cream);
  text-shadow: 0 1.5rem 0 var(--ink);
  text-align: center;
  animation: hcToast 2.2s ease-out forwards;
  pointer-events: none;
}
.toast.bad { background: var(--paint), var(--teal-fill), linear-gradient(#FFC2A8, var(--danger) 40%, #8A2A16) border-box; }
/* In a run the top centre is the score and the combo, so a milestone toast
   drops in under the coin and best chips in the inline-end corner instead.
   The toast is the one layer outside a .screen (a direct child of #stage), so
   it adds the side safe area itself: a sideways iPhone fills the screen now,
   and 14rem alone put it under the Dynamic Island, out of line with the chips
   above it. On a narrow stage (4:3..16:10) it stops short of the centred
   combo pill and Fast Start badge: 190rem from 16:9 up (today), 124rem at 4:3. */
#hud:not(.hidden) ~ .toast {
  left: auto; inset-inline-end: calc(14rem + var(--sa-x));
  top: calc(90rem + var(--sa-t));
  max-width: min(190rem, calc(50% - 116rem - var(--sa-x)));
  transform: none;
  animation-name: hcToastSide;
}
/* Over the game-over panel the side slot lands on RUN AGAIN: a mission
   completed by that run drops in at the top centre instead, above the panel
   (it hangs from 66rem; the score plaque behind it is repeated in the panel). */
#gameover:not(.hidden) ~ .toast,
#continue:not(.hidden) ~ .toast {   /* the Continue card too: "YOU NEED N MORE COINS" over it */
  right: auto; left: 50%;   /* physical: the side rule's inset-inline-end is left in RTL */
  top: calc(8rem + var(--sa-t));
  max-width: 300rem;
  transform: translateX(-50%);
  animation-name: hcToast;
}

/* ================================================================== LOADING */
.loading-screen {
  z-index: 20; align-items: center;
  padding: calc(18rem + var(--sa-t)) calc(24rem + var(--sa-x)) calc(18rem + var(--sa-b));
  background:
    linear-gradient(#1E4250, #08121A 60%),
    radial-gradient(80% 40% at 50% 74%, rgba(255, 130, 50, .3), transparent 70%),
    #08121A;
}
/* Flat city skyline cut out of a striped block — same trick as the mockup. */
.city-silhouette {
  position: absolute; left: 0; right: 0; bottom: 0; height: 150rem;
  background: repeating-linear-gradient(90deg, #1C3440 0 40rem, #142731 40rem 80rem);
  border-top: 3rem solid var(--outline);
  clip-path: polygon(0 46%, 10% 46%, 10% 18%, 22% 18%, 22% 60%, 34% 60%, 34% 26%,
                     46% 26%, 46% 52%, 58% 52%, 58% 12%, 72% 12%, 72% 40%,
                     86% 40%, 86% 22%, 100% 22%, 100% 100%, 0 100%);
}
.loading-screen > :not(.city-silhouette) { position: relative; }

.load-logo {
  margin-top: 14rem; width: 236rem; max-width: 100%;
  filter: drop-shadow(0 0 34rem rgba(255, 106, 24, .45));
  animation: hcFloat 5.5s ease-in-out infinite;
}
.load-spin { margin-top: 12rem; display: flex; align-items: center; gap: 9rem; }
.load-spin i {
  width: 14rem; height: 14rem;
  background: linear-gradient(var(--ice-1), var(--ice-3));
  border: 2rem solid var(--ice-ink);
  animation: hcSpin 1.6s linear infinite;
}
.load-spin span { font: 400 11rem/1 var(--display); letter-spacing: .14em; color: rgba(226, 210, 178, .7); }

/* Capped and centred: a 612rem-wide tip line was too long to read. */
.load-bottom { margin-top: auto; width: min(100%, 460rem); display: flex; flex-direction: column; gap: 10rem; }
.tip-slab { padding: 9rem 14rem; background: var(--orn-sm), var(--paint), var(--teal-fill), var(--rim); }
.tip-k { font: 400 9rem/1 var(--display); letter-spacing: .12em; color: var(--fire-1); margin-bottom: 5rem; }
.tip-v { font: 400 10.5rem/1.55 var(--ui); color: rgba(240, 228, 204, .74); }

.bar-head {
  display: flex; justify-content: space-between; gap: 10rem; margin-bottom: 6rem;
  font: 600 10rem/1.3 var(--mono); color: var(--text-mute);
}
.bar-head .status { font-family: var(--ui); font-weight: 600; text-align: start; }
.bar {
  height: 22rem; padding: 3rem;
  background: var(--well); border: 3rem solid #A9622F; border-radius: 12rem;
  box-shadow: 0 0 0 1.5rem var(--ink), inset 0 3rem 0 rgba(0, 0, 0, .6);
}
.bar-fill {
  width: 0%; height: 100%; border-radius: 8rem;
  background-color: var(--fire-2);
  background-image:
    repeating-linear-gradient(115deg, rgba(255, 255, 255, .28) 0 6rem, transparent 6rem 14rem),
    linear-gradient(var(--fire-1), var(--fire-3));
  background-size: 28rem 100%, 100% 100%;
  box-shadow: 0 0 16rem rgba(255, 138, 15, .75);
  animation: hcBar .7s linear infinite;
  transition: width .3s ease-out;
}

/* ============================================================ failure panels */
.fail-screen { justify-content: center; align-items: center; background: rgba(5, 11, 14, .95); z-index: 30; }
.fail-screen .hc-dialog { width: min(100%, 480rem); }
.boot-msg {
  max-width: 100%; margin: 12rem 0; padding: 12rem;
  text-align: left; direction: ltr;
  white-space: pre-wrap; word-break: break-word;
  font: 11rem/1.6 var(--mono); color: rgba(240, 228, 204, .78);
  background: #0A0F1E; border: 2rem solid var(--outline); border-radius: 6rem;
  max-height: 170rem; overflow: auto;
}

/* ======================================================= Arabic sub-lines */
/* applyLang (i18n.js) tags every filled element with the language it holds.
   In English mode the Arabic sub-lines sit in Latin slots (mono or Bungee,
   tracked out, sometimes uppercased), and tracking alone pulls the joins of
   Arabic letters apart. Attribute selectors, not :lang(): <html lang=ar> would
   make :lang(ar) match every element and strip the English tracking too.
   Sizes are explicit rem because the parents set none for em to scale from. */
[lang="ar"]:not(html) { letter-spacing: 0 !important; text-transform: none !important; }
.hc-btn small[lang="ar"]   { font: 700 10rem/1.1 var(--ar); }
.nav-tile em[lang="ar"]    { font: 700 8rem/1.1 var(--ar); }
.hc-play .en[lang="ar"]    { font: 700 14rem/1.1 var(--ar); }
.sheet-title em[lang="ar"], #char-sub[lang="ar"] { font: 700 10rem/1.1 var(--ar); }
.lb-head em[lang="ar"]     { font: 700 7.5rem/1 var(--ar); }
.dialog-tab[lang="ar"], .load-spin span[lang="ar"], .tip-k span[lang="ar"],
.hud-score-label span[lang="ar"]:not(.ar) { font-family: var(--ar); font-weight: 700; }

/* ===================================================== screen-enter motion */
/* Every screen is shown by dropping .hidden (display:none), which restarts
   these, so each entry plays with no JS. */
@keyframes hcDrop { from { transform: translateY(-40rem); opacity: 0; } }
@keyframes hcRise { from { transform: translateY(40rem); opacity: 0; } }
@keyframes hcFade { from { opacity: 0; } }
#hud .hud-top { animation: hcDrop .32s cubic-bezier(.2, 1.4, .4, 1) both; }
#hud .hud-mission { animation: hcRise .32s .08s cubic-bezier(.2, 1.4, .4, 1) both; }
#menu .menu-start { animation: hcDrop .3s ease-out both; }
#menu .menu-end { animation: hcRise .3s cubic-bezier(.2, 1.3, .4, 1) both; }
.sheet { animation: hcFade .16s ease-out both; }
.sheet .sheet-head { animation: hcDrop .26s ease-out both; }
.overlay-dim { animation: hcFade .18s ease-out both; }
/* The goal-reached pop (ui.setMission) has to beat the entry rise above,
   which outranks the plain .hud-mission.pop rule in the HUD section. */
#hud .hud-mission.pop { animation: hcBadge .5s ease-in-out 2; }

/* --------------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
  .menu-logo, .load-logo, .signin-logo, .load-spin i, .hc-play .shine, .bar-fill,
  #new-best, .hc-dialog, .combo,
  .near-pop, .hc-chip.beat, .world-name.show, .hud-mission.pop, .combo-pips,
  #hud .hud-top, #hud .hud-mission, #menu .menu-start, #menu .menu-end,
  .sheet, .sheet .sheet-head, .overlay-dim, .countdown, .stat, #run-hint,
  .arcade-start b, .arcade-start span {
    animation: none !important;
  }
  .world-name.show { opacity: 1; }
}

/* arcade kiosk (?arcade): no pointer on screen */
html.arcade, html.arcade * { cursor: none !important; }

/* ================================================= ADAPTIVE LANDSCAPE
   The landscape stage is 480..936rem wide (4:3..13:5, the stage block at the
   top of this file; _integration/DUAL_ASPECT_SPEC.md §2.3, §4.9). Corners
   ride the stage edges, centred layers stay centred. Rows sized for 640rem
   tighten on a narrow stage (4:3..16:10); at 16:9 and wider every value below
   is today's. Edited in place instead, where the cascade needs it: the menu
   scrim (stops in rem), the loading screen's sides, every safe-area inset
   (--sa-*) and the in-run toast's side slot (in the toast section, so the
   game-over and Continue toasts after it still win). An arcade cabinet keeps
   its 16:9 minimum, so the aspect rules below leave it as it was. */
/* sheets keep today's 612rem content column, centred, at any width */
.screen.sheet { padding-inline: max(calc(14rem + var(--sa-x)), calc((100% - 612rem) / 2)); }
/* PLAY: the Arabic label never breaks in two. It needs a ~188rem column on
   one line (aspect 1.529); from 157:100 down the Arabic button is laid out on
   two lines on purpose, with less padding, so its height (64.2-64.6rem) does
   not depend on where a browser's text happens to wrap. English fits one line
   at every aspect and keeps today's padding. */
.hc-play { flex-wrap: wrap; row-gap: 2rem; }
.hc-play .ar { white-space: nowrap; }
@media (orientation: landscape) and (max-aspect-ratio: 157/100) {
  html[lang="ar"]:not(.arcade) .hc-play { padding-block: 10rem; }
  html[lang="ar"]:not(.arcade) .hc-play .en { flex-basis: 100%; text-align: center; }
}
/* the leaderboard plaque fits a 162.7rem column at 4:3 */
.menu-board { width: min(100%, 184rem); }
/* mission card vs the centred hint pill: 190rem from 16:9 up, 158 at 16:10,
   138 at 4:3; the pill is capped to the gap between the two bottom corners */
.hud-mission { width: clamp(138rem, calc(50% - 116rem), 190rem); }
#run-hint { max-width: min(200rem, calc(100% - 44rem - 2 * clamp(138rem, calc(50% - 130rem), 190rem))); }
/* settings: label, slider, value stay on one line in a 217rem column */
.set-row:has(.slider) .slider { flex-basis: 64rem; min-width: 0; }
/* settings head: the player block needs the version tag's room below 3:2 */
@media (orientation: landscape) and (max-aspect-ratio: 3/2) { html:not(.arcade) #screen-settings .set-version { display: none; } }
/* the hint pill wraps to two lines below ~29:20 (its cap is the gap between
   the bottom corners, 160rem at 4:3), and a two-line pill reached the
   runner's feet (304.7rem): it sits 10rem lower there, its top at ~309rem */
@media (orientation: landscape) and (max-aspect-ratio: 29/20) { html:not(.arcade) #run-hint { bottom: max(6rem, var(--sa-b)); } }

/* ============================================================ FULL SCREEN
   js/fullscreen.js (DUAL_ASPECT_SPEC §10). The toggle is the back button's
   36rem plaque: in the menu's top inline-start corner (opposite the coin
   chip) and at the end of the Settings head. Hidden where the API is not
   used (iPhone, iPad, in-app browsers, ?arcade) and where the window is
   already full screen (F11, a kiosk, an installed app): js/ui.js setFull. */
.full-btn { width: 36rem; height: 36rem; }
.full-btn::after { content: ''; position: absolute; inset: -8rem; }   /* 46rem target (the inset counts from inside the 3rem rim), 44+ CSS px on a phone */
.full-btn svg {
  display: block; width: 18rem; height: 18rem;
  fill: none; stroke: var(--cream); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 1.5rem 0 var(--ink));
}
.full-btn .i-exit, html.is-full .full-btn .i-enter { display: none; }
html.is-full .full-btn .i-exit { display: block; }
/* above the logo column in landscape (the logo box tops out at ~54rem at the
   peak of its float, in its transparent corner: do not move the button
   lower); the wallet's row in portrait */
#menu > .full-btn {
  position: absolute; z-index: 2;
  top: calc(12rem + var(--sa-t)); inset-inline-start: calc(14rem + var(--sa-x));
}
html.arcade .full-btn, html.arcade .a2hs-hint { display: none !important; }
/* the Settings head has no room for the version tag and the button together
   (English overlapped at 4:3 and 16:10): the button takes the version's
   place, and below 3:2 the player block drops its "PLAYER" label */
html:not(.arcade) #screen-settings .sheet-head:has(> #set-full:not(.hidden)) .set-version { display: none; }
@media (orientation: landscape) and (max-aspect-ratio: 3/2) {
  html:not(.arcade) #screen-settings .sheet-head:has(> #set-full:not(.hidden)) .set-player-k { display: none; }
}

/* The Home Screen hint (an iOS / iPadOS browser tab: no Fullscreen API
   there, so full screen is the Home Screen app; js/ui.js showHomeHint). The
   menu only (it is a child of #menu), one line, until the × is pressed.
   Landscape: top centre in the sky between the columns, leaving 90rem at each
   end for the coin chip; it ends at ~41rem, above the floating logo. The
   menu toast uses the same slot and covers it for its 2.2 s (z 24). */
.a2hs-hint {
  position: absolute; z-index: 2;
  top: calc(14rem + var(--sa-t)); left: 50%; transform: translateX(-50%);
  max-width: min(340rem, calc(100% - 2 * (14rem + var(--sa-x)) - 180rem));
  display: flex; align-items: center; gap: 8rem;
  padding-block: 3rem; padding-inline: 12rem 6rem;
  background: rgba(4, 6, 13, .78);
  border: 1.5rem solid rgba(226, 210, 178, .35); border-radius: 14rem;
  color: var(--cream); text-shadow: 0 1.5rem 0 var(--ink);
  white-space: nowrap;
}
/* one line in every measured case; the ellipsis is only a safety net for a
   font that sets wider than the measured ones */
.a2hs-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; font: 600 max(8.5rem, 11px)/1.25 var(--ui); }
.a2hs-t[lang="ar"] { font: 700 max(9rem, 11px)/1.25 var(--ar); }
.a2hs-hint .x {
  position: relative; flex: none; width: 18rem; height: 18rem; border-radius: 50%;
  background: rgba(255, 255, 255, .16); color: var(--cream); font: 700 11rem/1 var(--ui);
}
/* target >= 44 CSS px: 18rem + 2 x max(14rem, 22px - 9rem) */
.a2hs-hint .x::after { content: ''; position: absolute; inset: calc(-1 * max(14rem, 22px - 9rem)); }

/* ========================================================= TOUCH TARGETS
   The smallest rem: 1rem is 1.04 px on an iPhone SE held upright and 0.95 to
   0.99 px in a phone's browser tab held sideways (a 341-356 px tall stage),
   where the 36-41rem buttons came to 34-41 CSS px. On a touch screen every
   tap target keeps at least 44 CSS px: the aprons grow to 48 px where the rem
   would leave them short (an apron's inset counts from the padding box,
   inside the 3rem rim), and the settings tile gets a px floor. Mouse and
   cabinet sizes (1rem >= 1.5 px) are untouched. */
@media (hover: none) and (pointer: coarse) {
  .back-btn::after, .full-btn::after { inset: min(-8rem, calc((100% - 48px) / 2)); }
  .char-arrow::after { inset: min(-8rem, calc((100% - 48px) / 2)); }
  .signin .seg button::after, .set-player-acts button::after { inset: min(-5rem, calc((100% - 48px) / 2)) 0; }
  .signin .seg button { min-width: max(44rem, 46px); }
  #signin-go { position: relative; }
  #signin-go::after { content: ''; position: absolute; inset: min(0px, calc((100% - 48px) / 2)) 0; }
  .nav-tile { min-height: max(42rem, 44px); }
}

/* ================================================================ SPONSORS
   The strip under Settings (index.html, 2026-10-07): a caption over the
   sponsors' logos. A quiet plate, not a plaque: the logos are white artwork
   made for dark grounds, so they sit on translucent teal-lo with the bronze
   as a hairline; no rim, no scrolls, no drop face, nothing that reads as a
   button (they are not links).

   What it shows is decided by real pixels, not rem, because legibility is:
   the plate is an inline-size container, and in landscape the lockup shows
   where the plate's content is at least 465 CSS px wide, so the lockup is
   34+ px tall and its smallest line, "AL Daham Real Estate", reads (a
   window ~890+ px tall: 1440x900, 1600x900, 1080p and up, a 16:9 cabinet).
   Narrower, the three logos are laid out on their own: two rows in
   landscape (Al Daham up to 44 px, then Tathmeer | xpand), whose column is
   narrow but has height to spare under Settings; one row with CSS dividers
   in portrait, whose column is wide but whose height belongs to the
   runner. Tathmeer and xpand stand at 0.8 of their
   size in the lockup relative to Al Daham: that balances the heavy Tathmeer
   wordmark and gives the height to Al Daham, whose small line is the one
   that has to read. Measured off the artwork (tools/make_sponsors.py), with
   Al Daham's height --sp-a and --sp-n = 0.8 of it: Tathmeer 0.763 and xpand
   0.765 of --sp-n, the dividers 0.696, sat where the lockup sits them (Al
   Daham 0.04 and Tathmeer 0.052 of --sp-n above xpand's foot). A row is
   then 11.52 --sp-a + 2.4rem wide and 1.032 --sp-a tall. The order is the
   lockup's in both languages.

   How tall it may be, --sp-room, is one number per layout:
   - Landscape: the strip HANGS in the flexible gap under Settings, its
     plate overflowing its flex item downward, so the column's centred
     cluster (pick, PLAY, Settings) keeps its exact place: the 1920x1080
     menu is unchanged. That gap is (332rem - B - 185rem) / 2 (the column
     less the cluster, shared by two gaps); the plate starts 9.5rem below
     Settings (its 5rem drop face and air) and may run to the column's end:
     64rem - B/2. Where that leaves Al Daham under 40 px (a window under
     ~610 px tall; a phone held sideways, 1rem ~1 px, would get ~14 px),
     the strip takes what it lacks into the column's flow, --sp-x: the two
     gaps share it, so the plate gains half of it and the cluster rises by
     half, at most until the runner pick is 40rem under the column's top
     edge (the coin chip): 67rem - B in the flow, 97.5rem - B of room. One
     formula, no breakpoint, so the column glides while a window's height
     is dragged. The runner stands in the middle third, clear of both
     columns. On a cabinet PRESS START takes the bottom of the column
     (37.5rem and its gap): 37.75rem - B/2, hung.
   - Portrait: in the bottom cluster's flow, so the cluster grows upward
     towards the runner. The front of the runner's feet is at 62.53 % of H
     (the menu camera, js/main.js FRAME_TALL), the runner pick at
     H - 14 - B - 182rem; with the column's 8rem gap and 4rem clear of the
     feet that leaves .3747 H - 208rem - B. Where the menu is under 640rem
     tall inside its padding (a 9:16 phone; a container query on #menu,
     which sees both H and the insets) the caption moves to the start of the
     row and the plate's padding thins to 2rem; under 9:15.5 (a Safari tab
     with its toolbars) the menu's bottom margin also gives 8rem of its 14
     to the strip (6rem stay, so the plate does not sit on the toolbar).
     Even so the height is the runner's there: Al Daham is ~20 px at 9:15
     and ~21 px on a 9:16 iPhone SE, so its small Latin line (0.22 of its
     height) is ~4.5 px, the edge of legible; its mark, its Arabic name and
     the two wordmarks read well. (On taller phones the row is 30-33 px,
     the line 6.5-7 px.) A vertical
     cabinet has no room under Settings at all (PRESS START is there): its
     strip takes the top row.
   The plate's fixed parts (border, padding, caption) are 21rem with the
   caption on top (25 with two rows), 7rem with it at the side; across, 15rem
   in portrait and 11rem in landscape. */
.sponsors { flex: none; min-width: 0; }
.sp-plate {
  container: sponsors / inline-size;
  padding: 3rem 6rem 4rem;
  background: linear-gradient(rgba(23, 46, 57, .58), rgba(23, 46, 57, .8));   /* --teal-lo */
  border: 1.5rem solid rgba(246, 200, 140, .2);
  border-radius: 10rem;
  box-shadow: 0 0 0 1rem rgba(22, 10, 4, .4);
}
/* the caption: the current language first, the other after it, between two
   fading hairlines; an 8rem line whatever the fonts' metrics */
.sp-cap {
  display: flex; align-items: center; justify-content: center; gap: 5rem;
  height: 8rem; margin-bottom: 3rem; white-space: nowrap;
}
.sp-cap::before, .sp-cap::after {
  content: ''; flex: 1 1 0; max-width: 34rem; height: 1rem;
  background: linear-gradient(90deg, rgba(246, 200, 140, 0), rgba(246, 200, 140, .4));
}
.sp-cap::after, :root[dir='rtl'] .sp-cap::before { transform: scaleX(-1); }   /* each fades outward */
:root[dir='rtl'] .sp-cap::after { transform: none; }
.sp-cap span { font: 700 8.5rem/8rem var(--ar); color: rgba(247, 233, 203, .78); }
.sp-cap em {
  font: 400 6.5rem/8rem var(--display); font-style: normal;
  letter-spacing: .14em; color: rgba(226, 210, 178, .55);
}
.sp-cap span[lang="en"] { font: 400 7rem/8rem var(--display); letter-spacing: .14em; }
.sp-cap em[lang="ar"] { font: 700 8rem/8rem var(--ar); }
/* a phone (1rem ~1.04-1.19 px): the caption keeps 10 / 7.5 px at least */
@media (hover: none) and (pointer: coarse) {
  .sp-cap span, .sp-cap em[lang="ar"] { font-size: max(8.5rem, 10px); }
  .sp-cap em, .sp-cap span[lang="en"] { font-size: max(6.5rem, 7.5px); }
}

/* the logos. The gaps are the dividers' margins (0.32 of --sp-n),
   not flex gaps, so every length resolves inside the container */
.sp-lockup { display: none; width: 100%; height: auto; margin-inline: auto; }
.sp-each, .sp-pair { display: flex; align-items: flex-end; justify-content: center; }
.sp-each img { display: block; flex: none; width: auto; }
.sp-div {
  display: block; flex: none; width: max(1px, 1.2rem); border-radius: 1rem;
  background: rgba(255, 255, 255, .6);
}
/* before container units (iOS 15): one plain row at 15rem */
.sp-each { --sp-a: 15rem; --sp-n: calc(var(--sp-a) * .8); }
.sp-a { height: var(--sp-a); margin-bottom: calc(var(--sp-n) * .04); }
.sp-t { height: calc(var(--sp-n) * .763); margin-bottom: calc(var(--sp-n) * .052); }
.sp-x { height: calc(var(--sp-n) * .765); }
.sp-div { height: calc(var(--sp-n) * .696); margin: 0 calc(var(--sp-n) * .32) calc(var(--sp-n) * .02); }

/* ---- landscape: hung under Settings (the cluster keeps its place) */
html:not(.portrait) .menu-end .sponsors { height: 0; margin-top: -9rem; }   /* cancels the column's 9rem gap */
html:not(.portrait) .menu-end .sp-plate { margin-top: 9.5rem; }
/* 4rem at the sides: a 1440x900 or 1600x900 screen's plate is then 465+ px
   inside, wide enough for the lockup */
html:not(.portrait) .sp-plate { padding-inline: 4rem; }
/* --sp-x, the part in the flow: twice what the hang room lacks for a 40 px
   Al Daham on two rows (25rem of plate + 1.652 x Al Daham), from none to
   the coin chip's limit. Zero on any window 610+ px tall. */
html:not(.portrait):not(.arcade) .sponsors {
  --sp-x: clamp(0rem, 2 * (40px * 1.652 + 25rem - 64rem + var(--sa-b) / 2), 67rem - var(--sa-b));
  --sp-room: calc(64rem - var(--sa-b) / 2 + var(--sp-x) / 2);
}
html:not(.portrait):not(.arcade) .menu-end .sponsors { height: var(--sp-x); }
html.arcade:not(.portrait) .sponsors { --sp-room: calc(37.75rem - var(--sa-b) / 2); }

/* ---- portrait: in the bottom cluster's flow */
html.portrait #menu { container: menu / size; }
html.portrait .sponsors { --sp-room: calc(var(--stage-h) * .3747 - 208rem - var(--sa-b)); }
@media (min-aspect-ratio: 29/50) {
  html.portrait:not(.arcade) #menu { padding-bottom: calc(6rem + var(--sa-b)); }
  html.portrait .sponsors { --sp-room: calc(var(--stage-h) * .3747 - 200rem - var(--sa-b)); }
}
/* a short menu: the caption at the start of the row, two short lines (the
   same rules for a vertical cabinet's top row, below), and 2rem of padding
   over and under the row. Its negative block margins let it reach into that
   padding, so the logos' row, not the two lines of type, sets the plate's
   height (7rem + the row). */
@container menu (max-height: 639.9rem) {
  html.portrait .sp-plate { display: flex; align-items: center; padding-block: 2rem; }
  html.portrait .sp-cap {
    flex: none; flex-direction: column; align-items: flex-start; gap: 3rem;
    height: auto; margin: -1.5rem 0; margin-inline-end: 7rem; padding-inline-end: 7rem;
    border-inline-end: max(1px, 1.2rem) solid rgba(246, 200, 140, .3);
  }
  html.portrait .sp-cap::before, html.portrait .sp-cap::after { display: none; }
  html.portrait .sp-cap span, html.portrait .sp-cap em { line-height: 1; letter-spacing: .08em; }
  html.portrait .sp-each { flex: 1; min-width: 0; }
}
/* A vertical cabinet (?arcade held upright): PRESS START under Settings
   already reaches the runner's toes, so there is no room under it. The strip
   takes the top row instead, above the floating logo (its top at 43rem + T
   at the float's peak), out of the flow, so nothing else moves. Beside the
   coin chip, it leaves the chip room for nine characters ("9,999,999",
   113rem, and a 10rem gap): a cabinet's wallet is shared by all its players
   and only grows (js/main.js bankRun), and the strip should not shrink as it
   does. Past that the chip draws over the strip's end, never under it. */
html.arcade.portrait .sponsors {
  position: absolute; z-index: 1;
  top: calc(14rem + var(--sa-t)); inset-inline-start: calc(14rem + var(--sa-x));
  width: calc(100% - 2 * (14rem + var(--sa-x)) - 123rem);
  --sp-room: 28rem;
}
html.arcade.portrait .menu-end .wallet { z-index: 2; }
html.arcade.portrait .sp-plate { display: flex; align-items: center; padding-block: 2rem; }
html.arcade.portrait .sp-cap {
  flex: none; flex-direction: column; align-items: flex-start; gap: 3rem;
  height: auto; margin: -1.5rem 0; margin-inline-end: 7rem; padding-inline-end: 7rem;
  border-inline-end: max(1px, 1.2rem) solid rgba(246, 200, 140, .3);
}
html.arcade.portrait .sp-cap::before, html.arcade.portrait .sp-cap::after { display: none; }
html.arcade.portrait .sp-cap span, html.arcade.portrait .sp-cap em { line-height: 1; letter-spacing: .08em; }
html.arcade.portrait .sp-each { flex: 1; min-width: 0; }

@supports (width: 1cqi) {
  /* landscape, two rows: Tathmeer | xpand at 0.8 of Al Daham where the room
     sets the size, as large as the column allows where its width does; Al
     Daham takes the rest of the room, up to the column's width. Never past
     44 px: a desktop's hang room would grow the plate to PLAY's height, and
     the lockup on a 1080p screen is ~41 px. */
  html:not(.portrait) .sp-each {
    flex-direction: column; align-items: center; row-gap: 4rem;
    --sp-n: max(8rem, min(calc((100cqi - 1.2rem) / 7.646), calc((var(--sp-room) - 25rem) / 1.652 * .8), 35.2px));
    --sp-a: max(8rem, min(calc(100cqi / 4.882), calc(var(--sp-room) - 25rem - var(--sp-n) * .815), 44px));
  }
  html:not(.portrait) .sp-div1 { display: none; }
  html:not(.portrait) .sp-a { margin-bottom: 0; }
  /* a cabinet's plate under 465 px: one row (the room above PRESS START is
     too short for two) */
  html.arcade:not(.portrait) .sp-each {
    flex-direction: row; align-items: flex-end;
    --sp-a: max(8rem, min(calc((100cqi - 2.4rem) / 11.52), calc((var(--sp-room) - 21rem) / 1.032)));
    --sp-n: calc(var(--sp-a) * .8);
  }
  html.arcade:not(.portrait) .sp-div1 { display: block; }
  html.arcade:not(.portrait) .sp-a { margin-bottom: calc(var(--sp-n) * .04); }
  /* wide enough for the lockup (34 px x its 13.676:1): its full width, never
     taller than the room */
  @container sponsors (min-width: 465px) {
    html:not(.portrait) .sp-lockup { display: block; max-width: calc((var(--sp-room) - 21rem) * 13.676); }
    html:not(.portrait) .sp-each { display: none; }
  }
  /* portrait: one row, as wide as the plate and as tall as the room allows
     (the floor only matters in a window shorter than any phone's) */
  html.portrait .sp-each {
    --sp-a: max(8rem, min(calc((100cqi - 2.4rem) / 11.52), calc((var(--sp-room) - 21rem) / 1.032)));
  }
  @container menu (max-height: 639.9rem) {
    /* the row is its own container here (the caption takes the plate's start) */
    html.portrait .sp-each {
      container: sp-row / inline-size;
      --sp-a: max(8rem, min(calc((100cqi - 2.4rem) / 11.52), calc((var(--sp-room) - 7rem) / 1.032)));
    }
  }
  html.arcade.portrait .sp-each {
    container: sp-row / inline-size;
    --sp-a: max(8rem, min(calc((100cqi - 2.4rem) / 11.52), calc((var(--sp-room) - 7rem) / 1.032)));
  }
}

/* ============================================================== PORTRAIT
   html.portrait: a window taller than wide (a phone held upright, a narrow
   desktop window, a vertical cabinet). Same markup, one column: logo and
   board on top, the runner in the middle band, PLAY at the bottom. Sizes are
   unchanged (1rem is the same physical size); only placement moves.
   _integration/DUAL_ASPECT_SPEC.md §4, §5, §10.8 and the Fast Start /
   Continue appendix. Last in the file: every rule here is html.portrait-
   scoped, so it never reaches a landscape window. */

/* ---- menu: top cluster, the runner band (38-61 %), bottom cluster */
html.portrait #menu { flex-direction: column; justify-content: space-between; gap: 0; }
html.portrait #menu::before {
  background: linear-gradient(rgba(6,12,16,.82) 0, rgba(8,15,20,.5) 22%, rgba(9,18,23,0) 36%,
    rgba(9,18,23,0) 64%, rgba(8,15,20,.45) 76%, rgba(6,12,16,.8) 100%);
}
html.portrait .menu-col { width: 100%; max-width: none; gap: 8rem; }
html.portrait .menu-gap { display: none; }
html.portrait .menu-start { padding-top: 36rem; }   /* the wallet's row */
html.portrait .menu-logo { width: 236rem; margin: 0 auto; }
/* a window shorter than 9:15.5 (a Safari tab with the bottom toolbar): 18rem back */
@media (min-aspect-ratio: 29/50) { html.portrait .menu-logo { width: 200rem; } }
/* the wallet leaves the column for the top inline-end corner of the menu.
   hcRise keeps a transform (fill-mode both), which kept .menu-end the
   containing block and dropped the wallet behind PLAY: the column fades in
   instead. */
html.portrait .menu-end { position: static; }
html.portrait #menu .menu-end { animation-name: hcFade; }
html.portrait .menu-end .wallet { top: calc(14rem + var(--sa-t)); inset-inline-end: calc(14rem + var(--sa-x)); }
html.portrait .menu-end .menu-settings { min-width: 60%; }
html.portrait .arcade-ctrls { display: none !important; }   /* a vertical cabinet: .arcade-start stays under Settings */
html.portrait .toast { max-width: 220rem; }

/* the leaderboard plaque, compact: head ("الكل ›"), the "you" row and the
   total distance, about 236x57rem. The five rows would end the top cluster
   at ~48 % of a 9:16 stage; a tap still opens the full board. The landed
   plaque places its children in explicit grid rows, so they go back to
   auto-placement here (the hidden rows and message take no cell). */
html.portrait:not(.arcade) .menu-board {
  width: min(100%, 236rem); height: auto;
  grid-template-rows: 13rem 13rem 11rem; row-gap: 2rem;
  padding: 5rem 12rem 6rem;
}
html.portrait:not(.arcade) .menu-board > * { grid-row: auto; }
html.portrait:not(.arcade) .menu-board .lb-rows,
html.portrait:not(.arcade) .menu-board .lb-msg { display: none; }
html.portrait:not(.arcade) .menu-board .lb-you { border-top: 0; padding-top: 0; margin-top: 0; }
/* a vertical arcade cabinet: no "you" row there, so the top three rows */
html.arcade.portrait .menu-logo { width: 200rem; }
html.arcade.portrait .menu-board { height: auto; width: min(100%, 236rem); grid-template-rows: 15rem 39rem; }
html.arcade.portrait .menu-board .lb-rows { grid-template-rows: repeat(3, 13rem); }
html.arcade.portrait .menu-board .lb-row:nth-child(n+4) { display: none; }

/* the full-screen button keeps its corner (the wallet's row; the centred
   logo, 62-298rem across, never reaches it). The Home Screen hint: centred,
   full width, one line at 8rem + T; the wallet beside it would leave it a
   234rem row, too narrow for one line, so the wallet hides while it shows
   (coins still show in the HUD and on game over). It ends at 35rem + T,
   8rem above the logo at the top of its float. */
html.portrait .a2hs-hint { top: calc(8rem + var(--sa-t)); max-width: calc(100% - 2 * (14rem + var(--sa-x))); }
html.portrait #menu.a2hs-on .wallet { visibility: hidden; }

/* ---- run HUD */
/* The top row keeps the landscape grid (1fr auto 1fr): a 1fr track never
   shrinks below its content, so the worst case (999,999 beside the Arabic
   best chip at 9,999 / 999,999) still clears the chips by the 8rem gap and
   sits 20-25rem off centre, while a typical run's plaque stays dead centre
   on the vanishing point (measured at 375, 393 and 430 px, both languages;
   the spec's auto 1fr auto put it 25-37rem off centre in every run). The
   plaque gives up 8rem of its minimum. */
html.portrait .hud-score { justify-self: center; min-width: 128rem; }
/* "+N" under the plaque, centred; [dir] beats :root[dir='rtl'] .score-float */
html.portrait[dir] .score-float { left: 0; right: 0; top: calc(100% + 10rem); margin: 0; text-align: center; }
html.portrait .combo { margin-top: 26rem; }   /* the float's lane */
html.portrait .near-pop { top: calc(140rem + var(--sa-t)); }
html.portrait .combo.hidden + .near-pop { top: calc(100rem + var(--sa-t)); }
html.portrait .world-name { top: calc(84rem + var(--sa-t)); }
html.portrait .fs-badge { top: calc(84rem + var(--sa-t)); }   /* the same band; never together (the dash ends 100 m before a door) */
/* the pill's top edge at ~82 % of H; the runner's feet are at 78-79 %, so it
   must not sit any higher */
html.portrait #run-hint { bottom: calc(82rem + var(--sa-b)); max-width: 300rem; }
html.portrait .hud-mission { width: auto; flex: 1 1 auto; max-width: 262rem; }
/* the in-run toast: top centre under the combo band, above the horizon */
html.portrait #hud:not(.hidden) ~ .toast {
  left: 50%; right: auto; transform: translateX(-50%);
  top: calc(170rem + var(--sa-t)); max-width: 300rem; animation-name: hcToast;
}
/* after the rule above: over game over and the Continue card, the top edge */
html.portrait #gameover:not(.hidden) ~ .toast,
html.portrait #continue:not(.hidden) ~ .toast { top: calc(8rem + var(--sa-t)); }

/* ---- settings: one column; it scrolls a little at 9:16 (touch-action pan-y) */
html.portrait .sheet-body.cols { grid-template-columns: 1fr; gap: 8rem; }
/* the player block wraps to its own full-width line under back and title */
html.portrait #screen-settings .sheet-head { flex-wrap: wrap; row-gap: 8rem; }
html.portrait .set-player {
  order: 3; flex-basis: 100%;
  display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 6rem 8rem;
}
/* the name is a <bdi> (its own direction): on its own wide cell it keeps the
   page's side, as the board's name cells do, not its script's */
html.portrait .set-player-n { max-width: none; text-align: right; }
html.portrait[dir='ltr'] .set-player-n { text-align: left; }
html.portrait .set-player-acts { grid-column: 1 / -1; display: flex; }
html.portrait .set-player-acts > button { flex: 1; min-height: 40rem; }

/* ---- the full board: one 332rem column, the name keeps ~140rem */
html.portrait #lb-list, html.portrait #lb-pin { width: 100%; }
html.portrait .lb-row.lb-big { grid-template-columns: 28rem 1fr 54rem 66rem; gap: 6rem; }

/* ---- sign-in: the row stacks (name, email, GO full width) and ends in the
   top third, above the keyboard; the logo is centred in what is left, the
   loading line last */
html.portrait .signin-row { grid-template-columns: 1fr; gap: 10rem; align-items: stretch; }
html.portrait .signin[data-mode="rename"] .signin-row { grid-template-columns: 1fr; }
html.portrait #signin-go { width: 100%; min-width: 0; }
html.portrait .signin-note { margin-inline: auto; max-width: 300rem; }
/* the full-width GO's drop face reaches 7.5rem below its box (and paints over
   the line on touch, where #signin-go is position: relative for its apron):
   10rem clears it with 2.5rem of air. Only the error, the note and the logo
   move; the fields and GO still end in the top 40 % (§5.1) */
html.portrait .signin-err { margin-top: 10rem; }
html.portrait .signin-logo { width: 236rem; margin: auto; }
html.portrait .signin-load { margin-top: 12rem; }

/* ---- game over: one column, hung high; the dim eases off over the runner */
html.portrait #gameover {
  background: linear-gradient(rgba(5,11,14,.78) 0, rgba(5,11,14,.72) 48%, rgba(5,11,14,.3) 62%, rgba(5,11,14,.2));
}
/* The budget: the panel ends above the fallen runner's head and at <= 52 % of
   H from 9:16 up. With the landed panel (new best, the rank line, a two-line
   tip) the spec's spacing ended 0.7rem above the head at 9:16 and 16rem
   below it at 9:15, so portrait hangs it 4rem higher (the tab then just meets
   the dimmed score plaque, whose number the panel repeats) and trims its
   gaps: 21rem shorter, ~50 % of H at 9:16, 5rem clear of the head at 9:15.
   The keyboard's "Press ENTER" line (a desktop window taller than wide) is
   dropped here: it put the panel 26rem over the head, and Enter still works. */
html.portrait .hc-dialog.go { margin-top: 66rem; width: 100%; grid-template-columns: 1fr; gap: 0; padding-bottom: 12rem; }
html.portrait .hc-dialog.go #new-best { margin-bottom: 6rem; }
html.portrait .hc-dialog.go .stat { padding-block: 7rem; }
html.portrait .go-actions { display: flex; flex-wrap: wrap; gap: 9rem; margin-top: 10rem; }
html.portrait .go-actions > .dialog-row { flex: 1; margin-top: 0; }
html.portrait .go-actions .hc-btn { padding-block: 9rem; }   /* 52rem: still 57 px on an iPhone */
html.portrait .go-actions > .dialog-hint { display: none; }

/* ---- the Continue card: as the game-over panel, ending above the head */
html.portrait #continue {
  background: linear-gradient(rgba(5,11,14,.78) 0, rgba(5,11,14,.72) 48%, rgba(5,11,14,.3) 62%, rgba(5,11,14,.2));
}
html.portrait .hc-dialog.cont { margin-top: 70rem; width: 100%; }

/* ---- loading and failure */
html.portrait .loading-screen { padding: calc(18rem + var(--sa-t)) calc(20rem + var(--sa-x)) calc(18rem + var(--sa-b)); }
html.portrait .load-logo { margin-top: 70rem; width: 260rem; }
html.portrait .city-silhouette { height: 250rem; }
html.portrait .load-bottom { width: 100%; }
html.portrait .fail-screen .boot-msg { max-height: 360rem; }
