/* Game menu screens of the browser edition (menu.js): a 1:1 simplified port of the desktop game's UI.
   Every size is a design pixel of the game's 1920x1080 reference: calc(N * var(--u)), --u set by ui/kit.js.
   Ported sources:
     assets/ui/tokens.json 1-381           avionics palette, gaps, fonts, kit 9-slice parts, menu / modal / settings sizes
     assets/ui/menu_v2/slices.json         panel 80, screen 48, key 44, link 30, cta 44 (drawn at texel x 0.5)
     assets/ui/screens/menu.json 1-215     home panel anchors and sizes (margin = screen_margin 20 + panel inset)
     assets/ui/screens/news.json 1-50, assets/ui/news/manifest.json      NEWS key and panel
     assets/ui/screens/hangar.json 1-100, assets/ui/hangar_v4/manifest.json   hangar column (native texel insets)
     assets/ui/screens/run_pause.json 1-87, assets/ui/pause_v2/manifest.json  RUN PAUSED card (source_scale 2)
     assets/ui/screens/run_results.json 1-73                                 result column
     crates/aura_ui_kit/src/settings_kit.rs 249-613, aura_flight_ui settings.rs 1750-2643   settings page
   9-slice art is CSS border-image (slice = texel inset, width = drawn inset). */

@font-face { font-family: 'Russo One'; src: url('assets/fonts/RussoOne-Regular.ttf') format('truetype'); font-display: block; }

.gm {
  /* Palette: ui/tokens.css (:root), inherited. */
  font-family: 'Russo One', sans-serif; color: var(--text); font-size: max(var(--font-min), calc(17 * var(--u))); line-height: 1.2;
  user-select: none; -webkit-user-select: none;
  /* ── size / spacing system (design px of the 1920x1080 reference, scaled by --u) ──
     Spacing scale  tokens.json gap xs 4 / s 8 / m 12 / l 22; modal.key_gap 14 (icon | divider | legend gap).
     Key heights    button.height 46 / primary_height 56 / small_height 38; settings action_height 54, tab_height 40,
                    toggle / slot / mute 38; modal key_height 62 / 76; keycap_height 28.
     Kit paddings   kit.key pad 20 x 6, kit.link pad 12 x 4, kit.screen pad 10, kit.panel pad 16; settings keys use
                    half the part pad (settings_kit KeyLook::soft / action_key: pad_x * 0.5).
     Hairlines      2 design px rules (menu_kit::separator), never thinner than one device pixel, snapped to whole px. */
  --gap-xs: calc(4 * var(--u)); --gap-s: calc(8 * var(--u)); --gap-m: calc(12 * var(--u)); --gap-l: calc(22 * var(--u));
  --key-gap: calc(14 * var(--u));                 /* tokens modal.key_gap / menu.key_gap */
  --key-pad-x: calc(20 * var(--u)); --key-pad-y: calc(6 * var(--u));   /* kit.key pad */
  --link-pad-x: calc(12 * var(--u)); --link-pad-y: calc(4 * var(--u)); /* kit.link pad */
  --screen-pad: calc(10 * var(--u));              /* kit.screen pad */
  --panel-pad: calc(16 * var(--u));               /* kit.panel pad */
  --btn-h: calc(46 * var(--u)); --btn-h-primary: calc(56 * var(--u)); --btn-h-small: calc(38 * var(--u));
  --keycap-h: calc(28 * var(--u)); --keycap-pad: calc(9 * var(--u)); --keycap-font: max(var(--font-min), calc(14 * var(--u)));
  --hair: max(1px, round(calc(2 * var(--u)), 1px));   /* menu_kit::separator 2 px, pixel-snapped */
  --hair-1: 1px;                                   /* settings row_divider 1 */
  --focus-w: max(1px, round(calc(2 * var(--u)), 1px)); /* settings focus_outline 2 */
  --focus-off: calc(2 * var(--u));                 /* settings focus_offset 2 */
  --disabled-alpha: .4;                            /* modal.disabled_alpha */
  --hit-min: max(var(--touch-min), calc(40 * var(--u)));       /* minimum hit height of a pointer target */
}
.gm .hidden:not(.ui-keep) { display: none !important; }
/* Browser button chrome reset at zero specificity: every kit class below wins over it (a `.gm button` reset used to
   beat the single-class kit rules and strip their padding, so icons sat on the key edges). */
:where(.gm) button { font: inherit; color: inherit; background: none; border: 0; margin: 0; padding: 0; cursor: pointer;
  outline: none; box-sizing: border-box; line-height: 1.2; -webkit-tap-highlight-color: transparent; }
:where(.gm) button:disabled { cursor: default; opacity: var(--disabled-alpha); }
/* Keyboard Tab focus draws the settings focus outline (focus_outline 2, offset 2, cyan); arrow focus uses .is-focus art. */
:where(.gm) button:focus-visible { outline: var(--focus-w) solid var(--cyan); outline-offset: var(--focus-off); }

/* Tinted kit glyph: white PNG used as a mask over the tone colour (menu_kit icon_bundle). */
.gm-ico { display: inline-block; flex: none; -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat; }
.gm-img { display: inline-block; flex: none; object-fit: contain; }
/* Key divider: 2 px, divider colour (menu_kit::separator). */
.gm-sep { display: inline-block; flex: none; width: var(--hair); background: var(--divider); }
.gm-rule-h { display: block; flex: none; height: var(--hair); background: var(--divider); width: 100%; }
.gm-rule-v { display: block; flex: none; width: var(--hair); align-self: stretch; background: var(--divider); }

/* ── JETS design system (operator 2026-11-01: "i said the plane choosing [JETS panel] <- All uis like this on web now") ──
   The JETS jet-selection page is the reference look of every web screen. Its recipes are defined once here and shared
   by selector lists: the ui/kit.js component classes (.jt-*; the JETS page is built from them since UI
   loop iteration 7, its gm-hangar-* classes are layout only) and the legacy kit classes other screens are built from
   (menu.js kitKey / titlePlate / oledPanel / homeKey, pause rows, result keys, language cells, settings chips), so no
   screen carries a look of its own and the brushed-metal 9-slice kit art is no longer drawn.
     component     recipe classes                                   look
     panel         .jt-panel                                         smoked dark glass, 16 radius, hairline, cyan edge light
     header        .gm-softkeys (+ -slot, .gm-softkey, -label, tape) cyan icon tile │ › TITLE │ tick ruler (ui/kit.js
                                                                     jetsHeader)
     data block    .jt-data                                          inset LCD glass; .gm-hangar-spec-title small caps + rule
     row           .jt-row                                           glass card, chevron; hover / arrow focus = cyan corner
                                                                     brackets; selected = lit cyan slab + backlight bar
     secondary     .jt-btn                                           grey glass key; hover / focus = cyan edge and legend;
                   (.jt-btn.jt-solo: free-standing, panel glass fill)
     primary       .jt-go                                            green slab with shine
     chip          .jt-chip                                          small pill; selected = cyan slab
     key cap       .jt-cap                                           cyan hairline cap
     table         .jt-table (.jt-table-head + .jt-row lines)        ui/kit.js table()
   Builders: ui/kit.js (panel, dataBlock, row, button, chip, keycap, table, jetsHeader) is the only code that sets the
   .jt-* classes. Every screen is converted (UI loop iteration 11): the former aliases (gm-unit, gm-mfd, gm-pause-title,
   gm-screen, gm-pause-row, gm-key, gm-kitkey, gm-result-key, gm-soft, gm-slot, gm-keycap, gm-pause-chip) became layout
   classes of kit-built nodes (gm-results-housing, gm-pause-head / -box / -line / -cap, gm-result-line, gm-pagekey,
   gm-chipkey, gm-bind-slot, gm-capkey); only the :active / .is-pressed / hover-spring rules still list a dead alias. Colours, alpha steps and
   radii come from ui/tokens.css only (ui/tokens_lint.test.mjs).
   Tokens (on :root and .gm so every screen and the page shell keys read them; mobile.css retunes --hu per column):
     --hu          one panel design pixel (= --u; floored on phones)   --hg-glass-top / -bot   panel glass fill
     --hg-edge     edge-light colour     --hg-line   hairlines           --hg-lit   selected cyan     --hg-go   primary green */
/* The palette lives on :root in ui/tokens.css, so recipe classes used outside a .gm screen (page shell keys, the
   multiplayer map panel and Tab scoreboard) resolve the same colours. */
:root, .gm {
  --hu: var(--u);
  --hg-glass-top: var(--glass-a74); --hg-glass-bot: var(--night-a86);
  --hg-edge: var(--cyan); --hg-line: var(--tick-a13); --hg-lit: var(--cyan); --hg-go: var(--green);
}
/* Panel. */
.jt-panel {
  position: relative; box-sizing: border-box; border-radius: calc(var(--r-panel) * var(--hu)); border: 1px solid var(--hg-line);
  background:
    radial-gradient(120% 40% at 0% 0%, var(--cyan-a10), transparent 60%),
    linear-gradient(180deg, var(--hg-glass-top), var(--hg-glass-bot));
  box-shadow: inset 0 1px 0 var(--light-a10), inset 0 0 0 1px var(--black-a35), 0 calc(18 * var(--hu)) calc(48 * var(--hu)) var(--black-a45);
  isolation: isolate;
}
/* Edge light: a 1 px cyan-to-violet gradient ring that fades out down the panel (mask-composite ring trick). */
.jt-panel::before {
  content: ''; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none; z-index: -1;
  background: linear-gradient(160deg, var(--cyan-a85), var(--violet-a45) 28%, var(--cyan-a08) 55%, transparent 80%);
  -webkit-mask: linear-gradient(var(--black) 0 0) content-box, linear-gradient(var(--black) 0 0); -webkit-mask-composite: xor;
  mask: linear-gradient(var(--black) 0 0) content-box exclude, linear-gradient(var(--black) 0 0);
}
/* Desktop glass blur on the free-standing panels (the JETS column keeps its own blur on every device); phones skip it:
   one blurred column is the phone budget (mobile perf pass). */
@media (hover: hover) and (pointer: fine) {
  .jt-panel { -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%); }
}
/* Data block (LCD glass inset). */
.jt-data {
  box-sizing: border-box; border-radius: calc(var(--r-card) * var(--hu));
  background: linear-gradient(180deg, var(--black-a42), var(--deep-a38));
  box-shadow: inset 0 0 0 1px var(--cyan-a16), inset 0 calc(10 * var(--hu)) calc(24 * var(--hu)) var(--black-a35);
}
/* Row card. */
.jt-row {
  position: relative; isolation: isolate; box-sizing: border-box; border-radius: calc(var(--r-card) * var(--hu)); color: var(--h-text);
  background: linear-gradient(180deg, var(--light-a04), var(--light-a01));
  box-shadow: inset 0 0 0 1px var(--light-a05);
  transition: background-color .18s, box-shadow .18s, transform .12s;
}
/* Hover / arrow focus: HUD corner brackets only (eight gradient strokes), clearly lighter than the selected slab. */
.jt-row::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0;
  --b: calc(16 * var(--hu)); --w: max(1px, calc(2 * var(--hu))); --c: var(--cyan-a90);
  background:
    linear-gradient(var(--c), var(--c)) left top / var(--b) var(--w) no-repeat, linear-gradient(var(--c), var(--c)) left top / var(--w) var(--b) no-repeat,
    linear-gradient(var(--c), var(--c)) right top / var(--b) var(--w) no-repeat, linear-gradient(var(--c), var(--c)) right top / var(--w) var(--b) no-repeat,
    linear-gradient(var(--c), var(--c)) left bottom / var(--b) var(--w) no-repeat, linear-gradient(var(--c), var(--c)) left bottom / var(--w) var(--b) no-repeat,
    linear-gradient(var(--c), var(--c)) right bottom / var(--b) var(--w) no-repeat, linear-gradient(var(--c), var(--c)) right bottom / var(--w) var(--b) no-repeat;
  transition: opacity .15s; }
.jt-row:is(:hover, .is-focus)::after { opacity: 1; }
.jt-row:is(:hover, .is-focus) { background-color: var(--cyan-a04); }
/* Selected: lit slab, cyan backlight bar on the left edge, light sweep layer (span.jt-sweep, animated below). */
.jt-row.is-selected {
  background: linear-gradient(90deg, var(--cyan-a26), var(--cyan-a08) 55%, var(--cyan-a03));
  box-shadow: inset 0 0 0 1px var(--cyan-a55), inset 0 1px 0 var(--light-a16), 0 0 calc(22 * var(--hu)) var(--cyan-a16);
}
.jt-row.is-selected::before { content: ''; position: absolute; left: 0; top: 18%; bottom: 18%; width: calc(4 * var(--hu));
  border-radius: 0 calc(var(--r-bar) * var(--hu)) calc(var(--r-bar) * var(--hu)) 0; background: var(--hg-lit); box-shadow: 0 0 calc(12 * var(--hu)) var(--hg-lit); }
:is(.jt-row, .gm-pause-row):is(:active, .is-pressed) { transform: scale(.985); background-color: var(--black-a25); }
/* Light sweep of the lit row: the band (105deg, light-a10 at 45 %) on a layer 2.5x the row wide, moved by transform only so
   the compositor runs it (the former background-position keyframes repainted the row every frame: A51 menu frame 50 ms ->
   33 ms without it). Same path as before: background-position -200% -> 250% of a 250 % layer = layer x +3W -> -3.75W =
   translateX(120%) -> translateX(-150%) of its own width; held off-row for the first 55 %. Clipped by the span (row radius);
   z-index -1 inside the row's isolation keeps it under the content and the backlight bar, over the slab fill. */
.jt-sweep { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; pointer-events: none; z-index: -1; display: none; }
.jt-row.is-selected > .jt-sweep { display: block; }
.jt-sweep::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 250%;
  background: linear-gradient(105deg, transparent 35%, var(--light-a10) 45%, transparent 55%); transform: translateX(120%); }
@keyframes gm-hangar-sweep { 0%, 55% { transform: translateX(120%); } 100% { transform: translateX(-150%); } }
@media (prefers-reduced-motion: no-preference) {
  .jt-row.is-selected > .jt-sweep::before { animation: gm-hangar-sweep 4.2s ease-in-out infinite; }
}
/* Secondary key (grey glass). */
.jt-btn {
  box-sizing: border-box; border-radius: calc(var(--r-card) * var(--hu)); color: var(--h-text);
  background: linear-gradient(180deg, var(--light-a07), var(--light-a02));
  box-shadow: inset 0 0 0 1px var(--light-a16), inset 0 1px 0 var(--light-a12);
  transition: color .15s, box-shadow .15s, transform .17s ease-out;
}
.jt-btn:is(:hover, .is-focus) { color: var(--hg-lit); box-shadow: inset 0 0 0 1px var(--cyan-a60), inset 0 1px 0 var(--light-a12); }
:is(.jt-btn, .gm-key, .gm-kitkey):is(:active, .is-pressed) { background: var(--black-a30); }
/* Free-standing key (not inside a panel: page shell MUTE / FULLSCREEN, the lives pill, the in-flight account chip): the
   panel glass fill under the key's hairline, so it reads over bright sky as well as inside a panel. */
.jt-btn.jt-solo { background: linear-gradient(180deg, var(--hg-glass-top), var(--hg-glass-bot));
  box-shadow: inset 0 0 0 1px var(--hg-line), inset 0 1px 0 var(--light-a10), 0 calc(6 * var(--hu)) calc(18 * var(--hu)) var(--black-a35); }
.jt-btn.jt-solo:is(:hover, .is-focus) { box-shadow: inset 0 0 0 1px var(--cyan-a60), inset 0 1px 0 var(--light-a10), 0 calc(6 * var(--hu)) calc(18 * var(--hu)) var(--black-a35); }
/* Primary key (green slab, shine pass, double chevron on the JETS FLY). */
.jt-go {
  position: relative; overflow: hidden; border-radius: calc(var(--r-card) * var(--hu)); color: var(--ink);
  background: linear-gradient(180deg, var(--go-top) 0%, var(--hg-go) 48%, var(--go-bot) 100%);
  box-shadow: 0 0 0 1px var(--green-a70), inset 0 1px 0 var(--light-a65), inset 0 calc(-4 * var(--hu)) 0 var(--go-shade-a28),
    0 calc(8 * var(--hu)) calc(28 * var(--hu)) var(--green-a30);
  transition: box-shadow .18s, filter .18s, transform .1s;
}
/* Shine pass: the band on a layer 2.5x the key wide, moved by transform only (compositor; the former background-position
   keyframes repainted the key every frame). Same path: background-position -150% -> 250% of a 250 % layer = layer x
   +2.25W -> -3.75W = translateX(90%) -> translateX(-150%); the key's overflow: hidden clips it. */
.jt-go::after { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 250%;
  pointer-events: none; background: linear-gradient(105deg, transparent 35%, var(--light-a45) 47%, transparent 58%); transform: translateX(90%); }
.jt-go:is(:hover, .is-focus) { filter: brightness(1.08) saturate(1.1); color: var(--ink);
  box-shadow: 0 0 0 1px var(--go-hi-a95), inset 0 1px 0 var(--light-a70), inset 0 calc(-4 * var(--hu)) 0 var(--go-shade-a28),
    0 calc(10 * var(--hu)) calc(36 * var(--hu)) var(--green-a50); }
:is(.jt-go, .gm-kitkey.role-primary, .gm-result-key.is-primary):is(:active, .is-pressed) { transform: translateY(calc(2 * var(--hu))); filter: brightness(.92);
  box-shadow: 0 0 0 1px var(--green-a70), inset 0 calc(3 * var(--hu)) calc(8 * var(--hu)) var(--go-shade-a45); }
@keyframes gm-hangar-shine { 0%, 70% { transform: translateX(90%); } 100% { transform: translateX(-150%); } }
@media (prefers-reduced-motion: no-preference) {
  .jt-go::after { animation: gm-hangar-shine 3.6s ease-in-out infinite; }
}
/* Disabled primary key (a primary that cannot act yet): the secondary glass instead of the green
   slab, muted legend, no glow, no shine, no hover / press response. Full opacity: the grey frame itself reads as disabled. */
:is(.jt-go, .gm-kitkey.role-primary):disabled, :is(.jt-go, .gm-kitkey.role-primary):disabled:is(:hover, .is-focus, :active, .is-pressed) {
  opacity: 1; cursor: default; filter: none; transform: none; color: var(--h-muted);
  background: linear-gradient(180deg, var(--light-a05), var(--light-a02));
  box-shadow: inset 0 0 0 1px var(--light-a10); }
.jt-go:disabled::after { animation: none; background: none; }
/* Free-standing chip (HUD toasts, kill feed lines over the world): the panel glass fill under the chip hairline. */
.jt-chip.jt-solo { background: linear-gradient(180deg, var(--hg-glass-top), var(--hg-glass-bot)); }
/* Table (ui/kit.js table): caption row of small caps, then one .jt-row per line; columns are the caller's grid. */
.jt-table { display: flex; flex-direction: column; gap: calc(4 * var(--hu)); }
.jt-table-head { color: var(--h-muted); font-size: max(var(--font-min), calc(13 * var(--hu))); letter-spacing: .12em; }
/* Chip. */
.jt-chip {
  box-sizing: border-box; border-radius: calc(var(--r-chip) * var(--hu)); color: var(--h-text);
  background: var(--light-a05); box-shadow: inset 0 0 0 1px var(--hg-line);
  transition: color .15s, box-shadow .15s, background-color .15s, transform .17s ease-out;
}
button.jt-chip:is(:hover, .is-focus) { color: var(--hg-lit); box-shadow: inset 0 0 0 1px var(--cyan-a60); }
:is(.jt-chip, .gm-soft).is-selected, :is(.jt-chip, .gm-soft).is-selected:is(:hover, :active, .is-pressed) { color: var(--ice);
  background: linear-gradient(90deg, var(--cyan-a30), var(--cyan-a12));
  box-shadow: inset 0 0 0 1px var(--cyan-a65), 0 0 calc(14 * var(--hu)) var(--cyan-a18);
  text-shadow: 0 0 calc(10 * var(--hu)) var(--cyan-a50); }
.jt-chip.is-selected.is-focus { box-shadow: inset 0 0 0 max(1px, calc(2 * var(--hu))) var(--ice-2-a90), 0 0 calc(14 * var(--hu)) var(--cyan-a25); }
/* Key cap. */
.jt-cap {
  border-radius: calc(var(--r-cap) * var(--hu)); background: var(--cyan-a08); box-shadow: inset 0 0 0 1px var(--cyan-a40); color: var(--cyan);
}
.jt-go .jt-cap { background: var(--go-shade-a14); box-shadow: inset 0 0 0 1px var(--go-shade-a45); color: var(--ink); }
/* BACK legend chevron (JETS footer): cyan ‹ before the legend on every BACK key (ui/kit.js jetsBackKey, menu.js kitKey back). */
.gm-hangar-back-chev { color: var(--cyan); font-size: 1.5em; line-height: .8; transform: translateY(-.04em); flex: none; }
.jt-go .gm-hangar-back-chev { color: inherit; }
/* Sub-line on the green slab in the legend ink (the grey --text2 sub read as pale green on green, BUY key; UI_UNIFORMITY.md R1). */
.jt-go .gm-kitkey-sub { color: var(--ink); opacity: .8; }
@media (prefers-reduced-motion: reduce) {
  .jt-row, .gm-pause-row, .jt-btn, .gm-key, .gm-kitkey, .jt-go,
  .jt-chip, .gm-soft, .gm-slot { transition: none; }
}

/* ── kit parts (menu_v2, texel x 0.5) ── */
/* The former kit-part aliases (panel, screen, key) are layout classes of kit-built nodes now: .gm-results-housing,
   .gm-results-screen and .gm-result-line below. */
/* Hover grow / press squash of the keys (juice.rs spring_buttons: growth capped at 4 px, set by ui/button_spring.js). Pointer
   devices only, so a touch tap never leaves a stuck grown key. Transform only: no layout, composited. */
@media (hover: hover) {
  .gm-key:hover, .gm-home-key:hover, .gm-soft:hover { transform: scale(var(--spring-hover, 1)); }
  .gm-key:is(:active, .is-pressed), .gm-home-key:is(:active, .is-pressed), .gm-soft:is(:active, .is-pressed) { transform: scale(var(--spring-press, 1)); }
}
/* Settings / controls value rows are frames holding keys, not keys: a press on a chip inside one must not sink the whole
   row (the ancestor row matches :active too). No transform, so no extra layer or row repaint on a chip press. */
.jt-row:is(.gm-vrow, .gm-bind-row):is(:active, .is-pressed) { transform: none; background-color: transparent; }
/* Chip key layout (tabs, arrows, On/Off toggles, bus chips): kit chip button + padding and inline-flex. */
.gm-chipkey {
  box-sizing: border-box;
  padding: var(--link-pad-y) var(--link-pad-x);
  display: inline-flex; align-items: center; justify-content: center; gap: calc(7 * var(--u)); flex: none; white-space: nowrap;
}

/* ── home (menu.json; web two-column layout, operator 2026-10-28) ──
   Left column: logo, READY TO FLY? unit with START FLYING, SETTINGS and the language key. Right column: the news,
   open by default, starting below the shell's FULLSCREEN key. The middle stays free for the world behind the menu.
   Portrait phones stack both columns and the home scrolls (news below, collapsible).
     --home-x / --home-y   screen_margin 20 + panel inset 16 / 12 (menu.json title and settings anchors: 36 / 32)
     --home-main-w         left column (menu.json play panel 720, trimmed so both columns fit 1366 x 768 at --u 0.71)
     --home-news-w         news column width
     --home-news-top       FULLSCREEN key top 20 + key height + gap l 22 */
#menu.gm { background: transparent; }
.gm-home {
  --home-x: calc(36 * var(--u)); --home-y: calc(32 * var(--u));
  --home-main-w: calc(620 * var(--u)); --home-news-w: calc(640 * var(--u));
  --home-news-top: calc(20 * var(--u) + max(var(--touch-min), var(--btn-h)) + 22 * var(--u));
  position: absolute; inset: 0; box-sizing: border-box; padding: var(--home-y) var(--home-x);
  display: grid; grid-template-columns: minmax(0, var(--home-main-w)) minmax(0, 1fr) minmax(0, var(--home-news-w));
  grid-template-rows: minmax(0, 1fr); column-gap: var(--gap-l);
}
.gm-home-main { grid-column: 1; display: flex; flex-direction: column; gap: calc(18 * var(--u)); min-height: 0; max-height: 100%;
  overflow-y: auto; scrollbar-width: none; }
/* Title: the validated Jet Combat Sim wordmark (assets/flight/ui/jetcombat/logo_jetcombatsim_wordmark.webp, 1400 x 136,
   transparent), drawn at its own aspect over the column width with breathing room above and below. */
.gm-p-title { width: min(calc(600 * var(--u)), 100%); flex: none; padding: calc(10 * var(--u)) 0 calc(6 * var(--u)); }
.gm-logo { display: block; width: 100%; height: auto; aspect-ratio: 1400 / 136; object-fit: contain;
  filter: drop-shadow(0 calc(2 * var(--u)) calc(6 * var(--u)) var(--black-a55)); }
.gm-p-play, .gm-p-settings { flex: none; }
.gm-text-title { font-size: max(var(--font-min), calc(44 * var(--u))); color: var(--text); }
.gm-text-body { font-size: max(var(--font-min), calc(17 * var(--u))); color: var(--muted); }
.gm-spacer-l { height: calc(22 * var(--u)); flex: none; }
/* Home play panel header (JETS header, menu.js jetsHeader) and its hint line under the ruler. */
#menu .gm-home-head { margin-bottom: calc(10 * var(--u)); }
#menu .gm-home-hint { margin-bottom: calc(14 * var(--u)); }
.gm-row { display: flex; gap: calc(12 * var(--u)); }
.gm-row > * { flex: 1 1 0; }
.gm-home-key { box-sizing: border-box; padding: var(--key-pad-y) var(--key-pad-x); display: flex; align-items: center;
  justify-content: center; gap: var(--key-gap); width: 100%; min-width: 0; min-height: var(--hit-min); overflow: hidden;
  color: var(--white); }
/* Home account row (operator 2026-11-01 "login integrated into the main menu ui"): a JETS row card (ui/kit.js row) at
   the top of the play panel: star glyph │ GUEST PILOT / pilot name over its sub line │ LOG IN / PILOT chip │ chevron.
   Guests get the selected chip (kit chip .is-selected, the sign-in action); signed-in pilots the plain chip. */
#menu .gm-home-account { flex: none; width: 100%; display: grid; align-items: center; text-align: start;
  min-height: max(var(--hit-min), calc(76 * var(--u)));
  grid-template-columns: calc(40 * var(--u)) minmax(0, 1fr) auto calc(16 * var(--u)); gap: 0 calc(14 * var(--u));
  grid-template-rows: 1fr auto auto 1fr;
  padding: calc(8 * var(--u)) calc(14 * var(--u)) calc(8 * var(--u)) calc(16 * var(--u)); margin-bottom: calc(14 * var(--u)); }
/* No words wrapper (UI-LATENCY #5): star │ name over sub line (middle tracks) │ chip │ chevron, side items full height. */
#menu .gm-home-account > .gm-hangar-plane { grid-column: 1; grid-row: 1 / -1; }
#menu .gm-home-account > .gm-hangar-name { grid-column: 2; grid-row: 2; }
#menu .gm-home-account > .gm-hangar-sub { grid-column: 2; grid-row: 3; margin-top: calc(4 * var(--hu)); }
#menu .gm-home-account > .gm-home-account-cta { grid-column: 3; grid-row: 1 / -1; }
#menu .gm-home-account > .gm-hangar-chev { grid-column: 4; grid-row: 1 / -1; }
#menu .gm-home-account .gm-hangar-plane { margin: calc(4 * var(--u)) 0; background: linear-gradient(180deg, var(--ice-2), var(--hg-lit)); }
#menu .gm-home-account .gm-hangar-name { font-size: max(var(--font-min), calc(24 * var(--u))); }
#menu .gm-home-account .gm-hangar-sub { font-size: max(var(--font-min), calc(15 * var(--u))); }
.gm-home-account-cta { font-size: max(var(--font-min), calc(17 * var(--u))); letter-spacing: .1em; padding: calc(7 * var(--u)) calc(14 * var(--u)); white-space: nowrap; }
/* Menu screens over the home (RANKING, PILOT, ACCOUNT, BUY): the home panels hide so the screen's glass column sits over
   the world alone, like the JETS page, instead of over a second menu. */
body.mo-screen-open #menu .gm-home { visibility: hidden; }
.gm-home-key-label { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; line-height: 1.1; }
.gm-home-key.is-stacked { flex-direction: column; justify-content: center; gap: var(--gap-xs); }
/* Hero home key (MULTIPLAYER, or START FLYING in solo-only builds): ui/kit.js row(.is-selected), the lit cyan slab of
   the selected JETS row; START FLYING (secondary), RANKING / PILOT / BUY and SETTINGS: kit button('secondary').
   Play card: kit panel; below, the layout of its START FLYING row and service row. */
.gm-home-play, .gm-home-tools { padding: calc(16 * var(--u)); display: flex; flex-direction: column; gap: calc(8 * var(--u)); }
.gm-home-solo.is-secondary { display: flex; margin-top: calc(12 * var(--u)); flex: none; }
.gm-home-service { display: flex; gap: calc(12 * var(--u)); margin-top: calc(12 * var(--u)); flex: none; }
.gm-home-service > * { flex: 1 1 0; }
/* START FLYING wraps onto two lines instead of an ellipsis: translations run up to twice the English length
   ("COMMENCER À VOLER"), and the 200 design px key holds two lines of its 52 px legend. */
.gm-home-key.is-hero .gm-home-key-label { white-space: normal; text-align: center; overflow-wrap: anywhere; line-height: 1.05; }
/* Tools card (SETTINGS key over the language row): the same kit panel as the play card, a wider key gap. */
.gm-home-tools { gap: calc(12 * var(--u)); }

/* MULTIPLAYER primary key (operator 2026-11-01): icon | divider | caption over the lives gauge. The gauge is the JETS
   card gauge language (.gm-hangar-bar: unlit segments always drawn, lit ones up to --fill) with one segment per life
   (--segs = the lives cap), the "N / 5" readout in the card's lit cyan and the next-life line under it.
     cyan = lives held · amber = last life (low_at) · red = none · green = unlimited (∞) */
.gm-home-multi { justify-content: flex-start; padding-inline: calc(34 * var(--u)) calc(28 * var(--u)); }
.gm-home-mp-words { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: calc(14 * var(--u)); }
.gm-home-key.is-hero .gm-home-mp-words .gm-home-key-label { text-align: start; }
.gm-home-lives { display: flex; flex-direction: column; align-items: flex-start; text-align: start; gap: calc(6 * var(--u)); min-width: 0; --lit: var(--cyan); --lit-dim: var(--cyan-a55);
  --bar-lit: var(--lit); --bar-dim: var(--lit-dim); }
.gm-home-lives.is-low { --lit: var(--amber); --lit-dim: var(--amber-a55); }
.gm-home-lives.is-empty { --lit: var(--red); --lit-dim: var(--red-a55); }
.gm-home-lives.is-unlimited { --lit: var(--green); --lit-dim: var(--green-a55); }
.gm-home-lives-line { display: flex; align-self: stretch; align-items: center; gap: calc(16 * var(--u)); min-width: 0; }
/* The bar is the JETS card gauge (.gm-hangar-bar recipe) with one segment per life: segment pitch = width / --segs. */
.gm-hangar-bar.gm-home-lives-bar { --segs: 5; flex: 0 1 calc(240 * var(--u)); min-width: calc(90 * var(--u)); height: calc(16 * var(--u));
  --bar-off: var(--cyan-a12); --seg-pitch: calc(100% / var(--segs)); --seg-gap: calc(5 * var(--u)); }
.gm-home-lives-count { font-size: max(var(--font-min), calc(32 * var(--u))); line-height: 1; color: var(--lit); white-space: nowrap; font-variant-numeric: tabular-nums;
  text-shadow: 0 0 calc(10 * var(--u)) var(--lit-dim); }
.gm-home-lives-count:empty { display: none; }
.gm-home-lives-sub { font-size: max(var(--font-min), calc(18 * var(--u))); line-height: 1.1; letter-spacing: .06em; color: var(--text2); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

/* Language key (language_picker.rs spawn_language_key): a JETS row card (ui/kit.js row, laid out like the account row):
   flag (key_flag_width, 3:2) │ LANGUAGE caption (row sub line) over the native name (row name) │ row chevron; a click
   opens the LANGUAGE picker card below. Look = the row recipe; only the grid and the type sizes are set here. */
#menu .gm-home-lang { flex: none; width: 100%; display: grid; align-items: center; text-align: start; cursor: pointer;
  min-height: var(--hit-min); grid-template-columns: auto minmax(0, 1fr) calc(16 * var(--u)); gap: 0 calc(16 * var(--u));
  grid-template-rows: 1fr auto auto 1fr;
  padding: calc(8 * var(--u)) calc(14 * var(--u)) calc(8 * var(--u)) calc(var(--key-pad-x) + 2 * var(--u)); }
/* No words wrapper (UI-LATENCY #5): flag │ LANGUAGE caption over the native name (middle tracks) │ chevron. */
#menu .gm-home-lang > .gm-lang-flag { grid-column: 1; grid-row: 1 / -1; }
#menu .gm-home-lang > .gm-lang-caption { grid-column: 2; grid-row: 2; }
#menu .gm-home-lang > .gm-lang-name { grid-column: 2; grid-row: 3; margin-top: calc(4 * var(--hu)); }
#menu .gm-home-lang > .gm-hangar-chev { grid-column: 3; grid-row: 1 / -1; }
.gm-lang-flag { flex: none; display: block; width: calc(var(--lp-key-flag, 30) * var(--u) * var(--lp-k, 1));
  aspect-ratio: 3 / 2; object-fit: cover; }
.gm-lang-flag.is-empty { visibility: hidden; }
#menu .gm-home-lang .gm-lang-caption { font-size: max(var(--font-min), calc(14 * var(--u))); letter-spacing: .08em; line-height: 1; }
#menu .gm-home-lang .gm-lang-name { font-size: max(var(--font-min), calc(26 * var(--u))); line-height: 1.15; }

/* LANGUAGE picker (language_picker.rs build_language_picker, assets/ui/screens/language_picker.json as --lp-* design px):
   black wash `dim`, kit panel card `card_width` (cap max_width_percent / max_height_percent), globe + title header,
   grid of `columns` link keys `cell_height` tall with `cell_gap`: flag `flag_width` (3:2), native name `native_font`
   in white over the English name `english_font` in text_2; the current language outlined 2 px in `selected_tone`.
   Portrait phones: two columns and --lp-k 1.6 (the portrait home scales its keys the same way, 26 → 40). The wash is
   fixed to the window: the portrait home scrolls, and an absolute wash would scroll away with it. */
#menu .gm-langpick, #settings .gm-langpick { position: fixed; inset: 0; z-index: 5; display: flex; align-items: center; justify-content: center;
  box-sizing: border-box; padding: calc(12 * var(--u)); isolation: isolate; }
/* Wash: black token at the layout's `dim` opacity (--lp-dim, set with the card's layout variables). */
#menu .gm-langpick::before, #settings .gm-langpick::before { content: ''; position: absolute; inset: 0; z-index: -1; background: var(--black); opacity: var(--lp-dim, .6);
  pointer-events: none; }
.gm-langpick-card { --lp-k: 1; width: min(calc(var(--lp-card) * var(--u)), var(--lp-maxw)); max-height: var(--lp-maxh);
  display: flex; flex-direction: column; padding: calc(16 * var(--u));
  gap: calc(var(--lp-gap) * var(--u)); overflow-y: auto; scrollbar-width: none; overscroll-behavior: contain; }
.gm-langpick-header { display: flex; align-items: center; gap: var(--key-gap); flex: none; padding: 0 calc(4 * var(--u)); }
.gm-langpick-grid { display: grid; grid-template-columns: repeat(var(--lp-cols), minmax(0, 1fr)); gap: calc(var(--lp-cell-gap) * var(--u)); }
.gm-langpick-cell {
  min-height: max(var(--hit-min), calc(var(--lp-cell) * var(--u) * var(--lp-k))); min-width: 0;
  display: flex; align-items: center; gap: calc(12 * var(--u)); padding: 0 calc(12 * var(--u)); overflow: hidden; text-align: start;
}
.gm-langpick-flag { flex: none; display: block; width: calc(var(--lp-flag) * var(--u) * var(--lp-k)); aspect-ratio: 3 / 2; object-fit: cover; }
.gm-langpick-names { display: flex; flex-direction: column; gap: calc(2 * var(--u)); min-width: 0; }
.gm-langpick-native { font-size: max(var(--font-min), calc(var(--lp-native) * var(--u) * var(--lp-k))); color: var(--white); line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-langpick-english { font-size: max(var(--font-min), calc(var(--lp-english) * var(--u) * var(--lp-k))); color: var(--text2); line-height: 1.1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── news column (news.js createNewsColumn) ──
   JETS layer (UI loop iteration 6): the column is a kit panel (.jt-panel), each card a kit row (.jt-row: glass card,
   corner-bracket hover / arrow focus), tag / date / unread count kit chips (.jt-chip; the count is the lit chip), the
   collapse key a kit button chip. Only layout, type sizes and the tag accent (tag chip legend, Read more, ▲ pin) stay
   here; the list scrolls with fades on hidden edges. */
.gm-newscol {
  grid-column: 3; align-self: start; margin-top: calc(var(--home-news-top) - var(--home-y));
  max-height: calc(100% - var(--home-news-top) + var(--home-y)); min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
}
.gm-newscol-head { display: flex; align-items: center; gap: calc(12 * var(--hu)); flex: none;
  padding: calc(14 * var(--hu)) calc(14 * var(--hu)) calc(12 * var(--hu)) calc(18 * var(--hu));
  border-bottom: 1px solid var(--hg-line); }
.gm-newscol.is-collapsed .gm-newscol-head { border-bottom-color: transparent; }
/* NEWS title = JETS header (news.js jetsHeader): it takes the free width, the head row keeps the hairline under it. */
.gm-newscol-title.gm-softkeys { flex: 1 1 auto; min-width: 0; padding-bottom: 0; border-bottom: 0; }
.gm-newscol-count { min-width: calc(30 * var(--hu)); height: calc(30 * var(--hu)); padding: 0 calc(9 * var(--hu)); flex: none;
  display: grid; place-items: center; font-size: max(var(--font-min), calc(15 * var(--hu))); line-height: 1; font-variant-numeric: tabular-nums; }
.gm-newscol-toggle { width: max(var(--touch-min), var(--btn-h)); height: max(var(--touch-min), var(--btn-h)); flex: none; display: grid; place-items: center; }
.gm-newscol-chev { width: calc(14 * var(--hu)); height: calc(14 * var(--hu)); border-left: var(--hair) solid var(--hg-lit); border-top: var(--hair) solid var(--hg-lit);
  transform: translateY(25%) rotate(45deg); transition: transform .2s; }
.gm-newscol.is-collapsed .gm-newscol-chev { transform: translateY(-25%) rotate(225deg); }
.gm-newscol-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: calc(8 * var(--hu)); padding: calc(14 * var(--hu));
  scrollbar-width: thin; scrollbar-color: var(--cyan-a35) transparent;
  --fade-t: 0px; --fade-b: 0px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, var(--black) var(--fade-t), var(--black) calc(100% - var(--fade-b)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, var(--black) var(--fade-t), var(--black) calc(100% - var(--fade-b)), transparent 100%);
}
.gm-newscol-list.fade-top { --fade-t: calc(36 * var(--hu)); }
.gm-newscol-list.fade-bottom { --fade-b: calc(48 * var(--hu)); }
.gm-newscol.is-collapsed .gm-newscol-list { display: none; }

/* Card = kit row; the tag colour only tints the tag chip legend, the Read more line and the pin mark. */
.gm-nc-card {
  --tag: var(--cyan);
  flex: none; display: flex; gap: calc(16 * var(--hu)); align-items: flex-start;
  padding: calc(16 * var(--hu)) calc(18 * var(--hu));
  text-decoration: none; outline: none;
}
.gm-nc-card.tag-update { --tag: var(--green); }
.gm-nc-card.tag-event { --tag: var(--amber); }
.gm-nc-card.tag-community { --tag: var(--lilac); }
.gm-nc-card.tag-dev { --tag: var(--cyan); }
/* Every card opens the news modal (news.js newsCard: a focusable role=button block, never an <a>). */
.gm-nc-card { cursor: pointer; }
/* WHAT: Tab focus on a news card draws the kit's cyan focus outline (same as buttons).
   WHY: the card has outline:none, so keyboard users saw no focus at all (WCAG 2.4.7). */
.gm-nc-card:focus-visible { outline: var(--focus-w) solid var(--cyan); outline-offset: var(--focus-off); }
.gm-nc-thumb { flex: none; width: calc(132 * var(--hu)); aspect-ratio: 16 / 10; object-fit: cover; border-radius: calc(var(--r-inner) * var(--hu));
  box-shadow: inset 0 0 0 1px var(--hg-line); background: var(--night); }
.gm-nc-main { display: flex; flex-direction: column; gap: calc(8 * var(--hu)); min-width: 0; flex: 1 1 auto; }
/* A card without a thumbnail is its own text column (UI-LATENCY #5 lean DOM, no main wrapper): same gap and stretch. */
.gm-nc-card:not(.has-thumb) { flex-direction: column; align-items: stretch; gap: calc(8 * var(--hu)); }
.gm-nc-meta { display: flex; align-items: center; flex-wrap: wrap; gap: calc(8 * var(--hu)); }
.gm-nc-tag, .gm-nc-date { font-size: max(var(--font-min), calc(13 * var(--hu))); line-height: 1; padding: calc(6 * var(--hu)) calc(10 * var(--hu)); letter-spacing: .08em; }
.gm-nc-tag { color: var(--tag); }
.gm-nc-date { color: var(--text2); font-variant-numeric: tabular-nums; }
.gm-nc-dot { width: calc(9 * var(--hu)); height: calc(9 * var(--hu)); border-radius: 50%; background: var(--hg-lit); box-shadow: 0 0 calc(8 * var(--hu)) var(--hg-lit); }
.gm-nc-card.is-pinned .gm-nc-date::before { content: '▲ '; color: var(--tag); }
.gm-nc-title { margin: 0; font: inherit; font-size: max(var(--font-min), calc(23 * var(--hu))); line-height: 1.2; color: var(--white); }
.gm-nc-body { margin: 0; font-family: var(--font-body); font-weight: 450;
  font-size: max(var(--font-min), calc(16 * var(--hu))); line-height: 1.45; color: var(--text2);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; line-clamp: 4; overflow: hidden; }
.gm-nc-more { display: inline-flex; align-items: center; gap: calc(6 * var(--hu)); font-size: max(var(--font-min), calc(15 * var(--hu))); color: var(--tag); letter-spacing: .04em; }
.gm-nc-arrow { transition: transform .15s; }
.gm-nc-card:hover .gm-nc-arrow, .gm-nc-card.is-focus .gm-nc-arrow { transform: translateX(calc(4 * var(--hu))); }
body.is-rtl .gm-nc-arrow { transform: scaleX(-1); }

/* ── news modal (news.js createNewsModal, operator 2026-11-02) ──
   Fixed black wash over the menu (fixed like the language picker: the portrait home scrolls), one kit panel card:
   JETS header, tag + date chips, title, then the body (image + text) as the only scrolling part, and the key row
   (OPEN PAGE for an https url | CLOSE). The card is sized in px bounds, not only --u: on a portrait phone --u is
   width / 1080 (0.36 at 390 px) and design-px sizes alone would draw 9 px text. Safe areas pad the wash. */
#menu .gm-newsmodal { position: fixed; inset: 0; z-index: 6; display: flex; align-items: center; justify-content: center;
  box-sizing: border-box; isolation: isolate;
  padding: max(12px, env(safe-area-inset-top, 0px)) max(12px, env(safe-area-inset-right, 0px))
           max(12px, env(safe-area-inset-bottom, 0px)) max(12px, env(safe-area-inset-left, 0px)); }
#menu .gm-newsmodal::before { content: ''; position: absolute; inset: 0; z-index: -1; background: var(--black); opacity: .75; pointer-events: none; }
/* Near-opaque card glass (the RUN PAUSED card's stops): the home keys and news column must not read through the post. */
#menu .gm-newsmodal { --hg-glass-top: var(--night-a94); --hg-glass-bot: var(--night-a97); }
.gm-newsmodal-card { --tag: var(--cyan);
  width: min(max(calc(860 * var(--u)), 340px), 100%); max-height: 100%; min-height: 0; box-sizing: border-box;
  display: flex; flex-direction: column; gap: max(10px, calc(14 * var(--u)));
  padding: max(14px, calc(22 * var(--u))) max(14px, calc(26 * var(--u))); overflow: hidden; }
.gm-newsmodal-card.tag-update { --tag: var(--green); }
.gm-newsmodal-card.tag-event { --tag: var(--amber); }
.gm-newsmodal-card.tag-community { --tag: var(--lilac); }
.gm-newsmodal-head { display: flex; align-items: center; gap: var(--key-gap); flex: none; }
.gm-newsmodal-card .gm-nc-meta { flex: none; gap: 8px; }
.gm-newsmodal-card .gm-nc-tag, .gm-newsmodal-card .gm-nc-date { font-size: max(12px, calc(15 * var(--u))); padding: 6px 10px; }
.gm-newsmodal-title { flex: none; margin: 0; font: inherit; font-size: max(19px, calc(32 * var(--u))); line-height: 1.2; color: var(--white);
  overflow-wrap: anywhere; }
.gm-newsmodal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y;
  display: flex; flex-direction: column; gap: max(10px, calc(14 * var(--u)));
  scrollbar-width: thin; scrollbar-color: var(--cyan-a35) transparent; }
.gm-newsmodal-img { flex: none; display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: calc(var(--r-inner) * var(--u)); box-shadow: inset 0 0 0 1px var(--hg-line); background: var(--night); }
.gm-newsmodal-text { margin: 0; font-family: var(--font-body); font-weight: 450; font-size: max(15px, calc(21 * var(--u)));
  line-height: 1.5; color: var(--text2); overflow-wrap: anywhere; }
.gm-newsmodal-actions { flex: none; display: flex; gap: max(8px, calc(12 * var(--u))); }
.gm-newsmodal-key { flex: 1 1 0; min-width: 0; height: max(var(--touch-min), 48px, calc(64 * var(--u)));
  display: flex; align-items: center; justify-content: center; gap: 8px; padding: 0 12px; }
.gm-newsmodal-legend { font-size: max(14px, calc(22 * var(--u))); letter-spacing: .06em; line-height: 1.1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.gm-newsmodal-ext { flex: none; font-size: max(14px, calc(22 * var(--u))); line-height: 1; }
body.is-rtl .gm-newsmodal-ext { transform: scaleX(-1); }
/* The menu-time loading pill paints above the menu layer; it hides while a post is open (it covered the modal keys on
   landscape phones). Same visibility rule as the phone settings page: a join or jet download pill stays. */
body.gm-news-open .bgload:not(.bgload--join):not(.jetload) { visibility: hidden; }

/* Loading skeleton: kit row placeholders (no pointer response) with sweeping shine lines. */
.gm-nc-skel { flex: none; display: flex; flex-direction: column; gap: calc(10 * var(--hu)); padding: calc(18 * var(--hu)) calc(20 * var(--hu)); pointer-events: none; }
.gm-nc-skel-line { display: block; height: calc(14 * var(--hu)); width: 92%; border-radius: calc(var(--r-chip) * var(--hu));
  background: linear-gradient(90deg, var(--light-a05) 0, var(--light-a14) 40%, var(--light-a05) 80%) 0 0 / 300% 100%;
  animation: gm-nc-shine 1.4s linear infinite; }
.gm-nc-skel-line.is-meta { width: 38%; height: calc(22 * var(--hu)); }
.gm-nc-skel-line.is-title { width: 70%; height: calc(24 * var(--hu)); }
.gm-nc-skel-line.is-short { width: 54%; }
@keyframes gm-nc-shine { from { background-position: 100% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .gm-nc-skel-line { animation: none; } .gm-nc-arrow { transition: none; } }

/* Empty / failed feed. */
.gm-nc-empty { display: flex; flex-direction: column; align-items: center; gap: calc(12 * var(--hu)); padding: calc(36 * var(--hu)) calc(16 * var(--hu)); text-align: center; }
.gm-nc-empty-icon { opacity: .45; }
.gm-nc-empty p { margin: 0; font-size: max(var(--font-min), calc(18 * var(--hu))); color: var(--text2); }

/* Portrait phones: one column that scrolls; the news follows the play block, its list grows with its cards. */
body.gm-portrait #menu .gm-home { display: flex; flex-direction: column; gap: calc(28 * var(--u)); overflow-y: auto; overscroll-behavior: contain;
  padding: calc(var(--safe-t, 0px) + var(--home-news-top)) calc(var(--safe-r, 0px) + 40 * var(--u)) calc(var(--safe-b, 0px) + 40 * var(--u)) calc(var(--safe-l, 0px) + 40 * var(--u)); }
body.gm-portrait #menu .gm-home-main { flex: none; max-height: none; overflow: visible; }
body.gm-portrait #menu .gm-p-title { width: min(calc(760 * var(--u)), 100%); }
/* Portrait home panels share the column: the older absolute-layout rule below (.gm-portrait .gm-p-play, 100vw - 72u)
   made the play panel wider than SETTINGS and NEWS and pushed it past the right margin (2026-11-01 phone capture). */
body.gm-portrait #menu .gm-p-play, body.gm-portrait #menu .gm-p-settings { width: auto; max-width: 100%; top: auto; max-height: none; }
body.gm-portrait #menu .gm-newscol { margin-top: 0; max-height: none; flex: none; }
body.gm-portrait #menu .gm-newscol-list { overflow: visible; -webkit-mask-image: none; mask-image: none; }
body.gm-portrait #menu .gm-text-body { font-size: max(var(--font-min), calc(32 * var(--u))); }
body.gm-portrait #menu .gm-nc-card { padding: calc(26 * var(--u)) calc(28 * var(--u)); gap: calc(14 * var(--u)); }
body.gm-portrait #menu :is(.gm-nc-main, .gm-nc-card:not(.has-thumb)) { gap: calc(14 * var(--u)); }
body.gm-portrait #menu .gm-nc-title { font-size: max(var(--font-min), calc(44 * var(--u))); }
body.gm-portrait #menu .gm-nc-body { font-size: max(var(--font-min), calc(36 * var(--u))); }
body.gm-portrait #menu .gm-nc-tag, body.gm-portrait #menu .gm-nc-date, body.gm-portrait #menu .gm-nc-more { font-size: max(var(--font-min), calc(28 * var(--u))); }
body.gm-portrait #menu .gm-nc-thumb { width: calc(200 * var(--u)); }
body.gm-portrait #menu .gm-newscol-title .gm-softkey-label { font-size: max(var(--font-min), calc(36 * var(--u))); }
body.gm-portrait #menu .gm-newscol-count { min-width: calc(52 * var(--u)); height: calc(52 * var(--u)); padding: 0 calc(14 * var(--u)); font-size: max(var(--font-min), calc(28 * var(--u))); }
body.gm-portrait #menu .gm-lang-caption { font-size: max(var(--font-min), calc(24 * var(--u))); }
body.gm-portrait #menu .gm-lang-name { font-size: max(var(--font-min), calc(40 * var(--u))); }
body.gm-portrait #menu .gm-lang-flag { --lp-k: 1.6; }
body.gm-portrait #menu .gm-langpick-card, body.gm-portrait #settings .gm-langpick-card { --lp-k: 1.6; --lp-cols: 2 !important; }

/* ── hangar JETS page (web glass pass, operator hangar UI request: megamix of the eight concepts) ──
   Smoked-glass MFD column over the live terrain: JETS key + heading tape, six jet rows sharing the free height, the
   PUBLISHED PERFORMANCE card of the viewed jet (aero_profiles.json figures only), Back (secondary) | FLY (hero).
   Row states:  rest      clear glass, white silhouette and name, dim chevron
                hover / arrow focus (.is-focus)   cyan HUD corner brackets + chevron nudge, no fill
                selected (.is-selected)          lit cyan slab, backlight bar, cyan silhouette, slow light sweep
                keyboard Tab (:focus-visible)    the kit's 2 px cyan outline, offset outside the row
                pressed (:active / .is-pressed)  slab sinks (scale .985, darker); .is-pressed = ui/press_feedback.js
   Local tokens (all on .gm-hangar-col so mobile.css can retune them in one place):
     --hu          one panel design pixel; = --u on desktop, floored on phones so text and keys stay finger-sized
     --hg-glass-*  smoked-glass fill (top / bottom) of the column        --hg-edge    edge-light colour (tokens cyan)
     --hg-line     hairline colour of separators and borders             --hg-lit     selected-row cyan (tokens cyan)
     --hg-go       FLY green (tokens avionics.green)                     --hg-row-min / --hg-row-max   row height range
     --pick        index 0..5 of the viewed jet (menu.js), drives the heading-tape pointer
   Performance: the only backdrop-filter of the screen is on this column; nothing filters the 3D canvas. */
#menu .gm-hangar { position: absolute; inset: 0; pointer-events: none; }
/* Glass, edge light, rows, card, keys and chips: ui/kit.js components (jetsColumn / jetsRow / jetsSpec / jetsChip /
   jetsBackKey / jetsFlyKey) drawn by the recipes at the top of this file. Layout only below. */
.gm-hangar-col {
  --hg-row-min: calc(58 * var(--hu)); --hg-row-max: calc(112 * var(--hu));
  --pick: 2;
  pointer-events: auto; position: absolute; left: calc(14 * var(--u)); top: calc(14 * var(--u)); bottom: calc(14 * var(--u));
  max-height: calc(100 * var(--dvh) - 28 * var(--u)); overflow: hidden;
  width: clamp(calc(380 * var(--hu)), 25vw, calc(560 * var(--hu)));
  padding: calc(18 * var(--hu)) calc(18 * var(--hu)) calc(18 * var(--hu));
  display: flex; flex-direction: column; gap: calc(12 * var(--hu));
  -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%);
}

/* Header: JETS key and label, then the heading tape. */
.gm-softkeys { display: flex; flex: none; align-items: center; gap: calc(12 * var(--hu));
  padding-bottom: calc(12 * var(--hu)); border-bottom: 1px solid var(--hg-line); }
/* The tile and the title sit directly in the header (UI-LATENCY #5 lean DOM: no slot wrapper): the header gap is the old
   slot's 12 units (tile │ title), the tape keeps its 14 through a 2-unit margin, and the title shrinks (ellipsis) when a
   long translation fills the header. */
.gm-softkeys > .gm-softkey-label { flex: 0 1 auto; }
.gm-softkeys > .gm-hangar-tape { margin-inline-start: calc(2 * var(--hu)); }
.gm-softkey {
  width: calc(50 * var(--hu)); height: calc(50 * var(--hu)); border-radius: calc(var(--r-card) * var(--hu)); flex: none;
  display: grid; place-items: center;
  background: linear-gradient(180deg, var(--cyan-a16), var(--cyan-a04));
  box-shadow: inset 0 0 0 1px var(--cyan-a45), inset 0 1px 0 var(--light-a18), 0 0 calc(16 * var(--hu)) var(--cyan-a18);
}
.gm-softkey-glyph { width: 62%; height: 62%; background: var(--h-text); -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat; }
.gm-softkey.is-active .gm-softkey-glyph { background: var(--hg-lit); }
.gm-softkey-label { display: flex; align-items: center; gap: calc(8 * var(--hu)); line-height: 1; min-width: 0;
  font-size: max(var(--font-min), calc(28 * var(--hu))); letter-spacing: .14em; color: var(--white); white-space: nowrap; }
/* Cyan chevron before JETS (wordmark direction: flat letterforms, cyan chevron). */
.gm-softkey-label::before { content: ''; flex: none; width: calc(10 * var(--hu)); height: calc(16 * var(--hu)); background: var(--hg-lit);
  clip-path: polygon(0 0, 45% 0, 100% 50%, 45% 100%, 0 100%, 55% 50%); }
.gm-softkey-label.is-active { color: var(--white); }
/* Heading tape: minor ticks every 8, major every 40 design px, fading at both ends; the pointer follows --pick. */
.gm-hangar-tape { position: relative; flex: 1 1 auto; min-width: calc(40 * var(--hu)); height: calc(26 * var(--hu)); align-self: center;
  background:
    repeating-linear-gradient(90deg, var(--cyan-a55) 0 1px, transparent 1px calc(40 * var(--hu))) bottom / 100% calc(14 * var(--hu)) no-repeat,
    repeating-linear-gradient(90deg, var(--tick-a32) 0 1px, transparent 1px calc(8 * var(--hu))) bottom / 100% calc(7 * var(--hu)) no-repeat,
    linear-gradient(var(--tick-a25), var(--tick-a25)) bottom / 100% 1px no-repeat;
  -webkit-mask: linear-gradient(90deg, transparent, var(--black) 14%, var(--black) 86%, transparent);
  mask: linear-gradient(90deg, transparent, var(--black) 14%, var(--black) 86%, transparent); }
.gm-hangar-tape::after { content: ''; position: absolute; top: 0; width: calc(12 * var(--hu)); height: calc(9 * var(--hu));
  left: calc((var(--pick, 2) + .5) / 6 * 100% - 6 * var(--hu)); background: var(--hg-lit);
  clip-path: polygon(0 0, 100% 0, 50% 100%); filter: drop-shadow(0 0 4px var(--cyan-a80));
  transition: left .28s cubic-bezier(.2, .8, .2, 1); }

/* Jet rows: share the page height (flex 1 1 0) between --hg-row-min and --hg-row-max. */
.gm-hangar-page { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
  gap: calc(6 * var(--hu)); justify-content: flex-start; justify-content: safe center; scrollbar-width: none; }
.gm-hangar-row {
  flex: 1 1 0; min-height: var(--hg-row-min); max-height: var(--hg-row-max); width: 100%;
  display: grid; grid-template-columns: calc(92 * var(--hu)) minmax(0, 1fr) calc(22 * var(--hu)); align-items: center;
  grid-template-rows: 1fr auto auto 1fr; gap: 0 calc(16 * var(--hu)); padding: 0 calc(18 * var(--hu)) 0 calc(16 * var(--hu)); text-align: left;
}
/* Row text without a wrapper (UI-LATENCY #5 lean DOM): name and sub line sit in the middle column, centred as one block
   between two flexible tracks (the old flex column's place); the glyph and the chevron span the whole row height. */
.gm-hangar-row > .gm-hangar-plane { grid-column: 1; grid-row: 1 / -1; }
.gm-hangar-row > .gm-hangar-chev { grid-column: 3; grid-row: 1 / -1; }
.gm-hangar-row > .gm-hangar-name { grid-column: 2; grid-row: 2; }
.gm-hangar-row > .gm-hangar-sub { grid-column: 2; grid-row: 3; margin-top: calc(4 * var(--hu)); }
.gm-hangar-plane { align-self: stretch; margin: calc(8 * var(--hu)) 0; flex: none;
  background: linear-gradient(180deg, var(--light), var(--steel));
  -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat;
  transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
.gm-hangar-name { font-size: max(var(--font-min), calc(25 * var(--hu))); line-height: 1.05; color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-hangar-sub { font-family: system-ui, 'Segoe UI', Roboto, sans-serif; font-weight: 600; font-size: max(var(--font-min), calc(14 * var(--hu))); letter-spacing: .04em;
  color: var(--h-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-hangar-sub:empty { display: none; }
/* Chevron: drawn, not a ">" glyph, with its own padding column (also the chevron of every JETS row elsewhere). */
.gm-hangar-chev { justify-self: end; flex: none; width: calc(11 * var(--hu)); height: calc(11 * var(--hu)); margin-right: calc(4 * var(--hu));
  border-top: calc(3 * var(--hu)) solid var(--h-muted); border-right: calc(3 * var(--hu)) solid var(--h-muted);
  transform: rotate(45deg); opacity: .7; transition: transform .2s, border-color .2s, opacity .2s; }
.jt-row:is(:hover, .is-focus) .gm-hangar-chev { border-color: var(--hg-lit); opacity: 1; transform: translateX(calc(3 * var(--hu))) rotate(45deg); }
.gm-hangar-row.is-selected .gm-hangar-plane { background: linear-gradient(180deg, var(--ice-2), var(--hg-lit)); transform: scale(1.06); }
/* Selected row name: the JETS jet name, and the caption of the home hero key (a selected kit row). */
.jt-row.is-selected :is(.gm-hangar-name, .gm-home-key-label) { color: var(--ice); text-shadow: 0 0 calc(14 * var(--hu)) var(--cyan-a55); }
.jt-row.is-selected .gm-hangar-sub { color: var(--ice-2-a85); }
.jt-row.is-selected .gm-hangar-chev { border-color: var(--hg-lit); opacity: 1; }

/* PUBLISHED PERFORMANCE card: LCD glass, segmented gauges relative to the six jets, Mach and load-limit chips. */
.gm-hangar-spec, .jt-data { flex: none; display: flex; flex-direction: column; gap: calc(10 * var(--hu));
  padding: calc(14 * var(--hu)) calc(16 * var(--hu)); }
.gm-hangar-spec-title { font-size: max(var(--font-min), calc(13 * var(--hu))); letter-spacing: .18em; color: var(--h-muted); display: flex; align-items: center; gap: calc(10 * var(--hu)); min-width: 0; }
.gm-hangar-spec-name { order: 2; color: var(--hg-lit); letter-spacing: .08em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.gm-hangar-spec-title::after { content: ''; order: 3; flex: 1; height: 1px; background: linear-gradient(90deg, var(--hg-line), transparent); }
.gm-hangar-spec-body { display: flex; flex-direction: column; gap: calc(9 * var(--hu)); }
.gm-hangar-gauge { display: grid; grid-template-columns: calc(104 * var(--hu)) minmax(0, 1fr) calc(128 * var(--hu)); align-items: center; gap: calc(12 * var(--hu)); }
.gm-hangar-gauge-label { font-size: max(var(--font-min), calc(14 * var(--hu))); letter-spacing: .1em; color: var(--text2); }
.gm-hangar-gauge-value { font-size: max(var(--font-min), calc(19 * var(--hu))); color: var(--hg-lit); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
  text-shadow: 0 0 calc(10 * var(--hu)) var(--cyan-a45); }
/* Segmented bar: unlit segments always drawn, lit ones up to --fill (0..1 of the roster maximum). Shared gauge recipe:
   --seg-pitch / --seg-gap (segment period and gap), --bar-off (unlit), --bar-dim / --bar-lit (lit ramp); the defaults
   are the JETS card values, the home lives gauge sets one segment per life and the lives colour. */
.gm-hangar-bar { position: relative; height: calc(9 * var(--hu)); background: var(--bar-off, var(--cyan-a10));
  -webkit-mask: repeating-linear-gradient(90deg, var(--black) 0 calc(var(--seg-pitch, calc(9 * var(--hu))) - var(--seg-gap, calc(2 * var(--hu)))),
    transparent calc(var(--seg-pitch, calc(9 * var(--hu))) - var(--seg-gap, calc(2 * var(--hu)))) var(--seg-pitch, calc(9 * var(--hu))));
  mask: repeating-linear-gradient(90deg, var(--black) 0 calc(var(--seg-pitch, calc(9 * var(--hu))) - var(--seg-gap, calc(2 * var(--hu)))),
    transparent calc(var(--seg-pitch, calc(9 * var(--hu))) - var(--seg-gap, calc(2 * var(--hu)))) var(--seg-pitch, calc(9 * var(--hu)))); }
.gm-hangar-bar::before { content: ''; position: absolute; inset: 0 auto 0 0; width: calc(var(--fill, 0) * 100%);
  background: linear-gradient(90deg, var(--bar-dim, var(--cyan-a55)), var(--bar-lit, var(--hg-lit))); transition: width .35s cubic-bezier(.2, .8, .2, 1); }
.gm-hangar-chips { display: flex; gap: calc(8 * var(--hu)); flex-wrap: wrap; }
.gm-hangar-chip { font-size: max(var(--font-min), calc(14 * var(--hu))); letter-spacing: .06em; color: var(--white); padding: calc(5 * var(--hu)) calc(10 * var(--hu)); }
.gm-hangar-chips:empty { display: none; }

/* Back (secondary ghost key) | FLY (hero, two thirds of the width). */
.gm-hangar-actions { flex: none; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2.1fr); grid-template-rows: 100%;
  align-items: stretch; gap: calc(12 * var(--hu)); height: clamp(44px, calc(76 * var(--hu)), calc(13 * var(--dvh))); }
.gm-hangar-actions > * { height: 100%; min-height: 0; }
/* MULTIPLAYER (full width) and RANKING / PILOT / BUY THE GAME: their own block under BACK / FLY, inside the column. The
   three service legends wrap onto two lines instead of being cut ("BUY THE GA…" at 1280x800, operator capture). */
.gm-hangar-extra { flex: none; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: calc(8 * var(--hu)); }
.gm-hangar-extra:empty { display: none; }
#menu .gm-hangar-extra .gm-hangar-multi { height: clamp(34px, calc(52 * var(--hu)), calc(9 * var(--dvh))); min-width: 0; padding: 0 calc(8 * var(--hu));
  font-size: max(var(--font-min), calc(17 * var(--hu))); line-height: 1.05; text-align: center; }
/* The legend is the key's own text (UI-LATENCY #5 lean DOM, no span): it wraps like the former span did. */
#menu .gm-hangar-extra .gm-hangar-multi { overflow-wrap: normal; white-space: normal; }
.gm-hangar-back { font-size: max(var(--font-min), calc(21 * var(--hu))); letter-spacing: .06em; display: flex; align-items: center; justify-content: center; }
.gm-hangar-fly { display: flex; align-items: center; justify-content: center; gap: calc(14 * var(--hu));
  font-size: max(var(--font-min), calc(36 * var(--hu))); letter-spacing: .16em; }
.gm-hangar-fly-label { position: relative; z-index: 1; padding-left: .16em; }
/* Double chevron after FLY, slides forward on hover / focus. */
.gm-hangar-fly-chev { position: relative; z-index: 1; width: calc(26 * var(--hu)); height: calc(20 * var(--hu)); background: currentColor;
  clip-path: polygon(0 0, 22% 0, 52% 50%, 22% 100%, 0 100%, 30% 50%, 0 0, 48% 0, 70% 0, 100% 50%, 70% 100%, 48% 100%, 78% 50%, 48% 0);
  transition: transform .2s cubic-bezier(.2, .8, .2, 1); }
.gm-hangar-fly:is(:hover, .is-focus) .gm-hangar-fly-chev { transform: translateX(calc(6 * var(--hu))); }
.gm-hangar-fly:disabled .gm-hangar-fly-chev { transform: none; opacity: .45; }
@media (prefers-reduced-motion: reduce) {
  .gm-hangar-plane, .gm-hangar-chev, .gm-hangar-tape::after, .gm-hangar-bar::before, .gm-hangar-fly-chev { transition: none; }
}

/* Short windows: the card yields its height to the rows first. */
@media (max-height: 600px) { .gm-hangar-spec { display: none; } }
/* Short landscape (~400-520 px): smaller header, no heading tape, tighter gaps, rows down to 40 px; the list scrolls. */
@media (max-height: 520px) and (orientation: landscape) {
  .gm-hangar-col { --hg-row-min: 40px; gap: 6px; padding: 8px 10px; top: 8px; bottom: 8px; max-height: calc(100 * var(--dvh) - 16px); }
  .gm-hangar-tape { display: none; }
  .gm-softkeys { padding-bottom: 4px; }
  .gm-softkey { width: 30px; height: 30px; }
  .gm-softkey-label { font-size: 18px; }
  .gm-hangar-actions { height: 44px; }
  /* WHAT: Service keys at least the touch target (40 px mouse, 44 px touch). WHY: Responsive UI: 34 px keys were under the
     44 px touch floor on phones in landscape. */
  #menu .gm-hangar-extra .gm-hangar-multi { height: var(--touch-min); font-size: 13px; }
}

.gm-news-key-label { white-space: nowrap; line-height: 1; }
.gm-news-thumb { width: calc(136 * var(--u)); height: calc(136 * var(--u)); border-radius: calc(var(--r-inner) * var(--u)); }

/* Page keys (menu.js kitKey: BACK, controls, multiplayer screen actions): kit secondary / primary key + this layout class.
   Renamed from .gm-kitkey (multi/ui.css follows). */
.gm-pagekey {
  box-sizing: border-box;
  padding: var(--key-pad-y) var(--key-pad-x); display: flex; align-items: center; justify-content: center; gap: var(--key-gap);
  flex: none; min-width: 0; overflow: hidden;
}
.gm-kitkey-words { flex: 1 1 0; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: calc(2 * var(--u)); min-width: 0; }
.gm-kitkey-legend { white-space: nowrap; line-height: 1.1; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* A legend without a sub-line sits directly in its key (UI-LATENCY #5): it takes the words column's place and alignment. */
.gm-pagekey > .gm-kitkey-legend { flex: 1 1 0; min-width: 0; text-align: start; }
.gm-kitkey-sub { font-size: max(var(--font-min), calc(14 * var(--u))); color: var(--text2); }
/* BACK legend (kitKey back: the ‹ chevron precedes it) in capitals with the JETS footer spacing. UI_UNIFORMITY.md R2: the
   JETS page said "BACK", settings, controls and every multiplayer screen said "Back" (common.back is "Back" in the
   game catalogue; the JETS key upper-cases it the same way). */
.gm-hangar-back-chev + .gm-kitkey-legend { text-transform: uppercase; letter-spacing: .06em; }
.gm-capkey {
  flex: none; height: calc(28 * var(--u)); box-sizing: border-box; display: inline-flex; align-items: center;
  padding: 0 var(--keycap-pad); font-size: var(--keycap-font); line-height: 1; color: var(--cyan); white-space: nowrap; justify-content: center;
}

/* ── settings / controls page (settings.rs, settings_kit.rs) ── */
/* The page is a JETS glass panel over the dimmed world (no opaque black page any more). */
#settings.gm { background: var(--night-a55); overflow-y: auto; z-index: 15; }
/* settings_kit page_root_node: page_pad_x 24, page_pad_top 14, page_pad_bottom 10, page_gap 10;
   page width 82 % clamped to 720-1120 (controls 780-1080). */
.gm-page { min-height: 100%; display: flex; justify-content: center; box-sizing: border-box; padding: 0 calc(24 * var(--u)); }
.gm-page-col {
  width: clamp(min(calc(720 * var(--u)), 100%), 82vw, calc(1120 * var(--u))); max-width: 100%; box-sizing: border-box;
  padding: calc(14 * var(--u)) 0 calc(10 * var(--u)); display: flex; flex-direction: column; gap: calc(10 * var(--u));
}
/* Page column as a JETS panel: panel padding and a margin off the window edges. */
.gm-page-col.jt-panel { padding: calc(18 * var(--hu)); gap: calc(12 * var(--hu)); margin: calc(14 * var(--u)) 0; align-self: flex-start; }
.gm-page-col > * { flex: none; }
/* Fixed settings / controls pages (operator 2026-11-02 "scroll should be virtualized in the html component"): the layer
   and the page never scroll; the column is capped at the window (its 14 u margins off), title, tabs and footer keep their
   size and the list panel takes the rest; only the ui/kit.js ScrollList inside it scrolls. */
html body #settings.gm.gm-fixed { overflow: hidden; }
.gm-page.is-fixed { height: 100%; min-height: 0; }
.gm-page.is-fixed > .gm-page-col { max-height: calc(100% - 28 * var(--u)); min-height: 0; }
.gm-page-col > .gm-oled-panel.is-fill { flex: 1 1 auto; min-height: 0; }
.gm-oled.is-list { max-height: none; overflow: hidden; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* Virtualized scroll list: its own scroll box (wheel, touch drag, bar) with the kit's thin cyan bar inside the panel;
   the body's top / bottom padding stands in for the detached rows (ScrollList.render). */
.gm-vscroll { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; overflow-anchor: none; overscroll-behavior: contain;
  touch-action: pan-y; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: var(--cyan-a35) transparent;
  padding-right: calc(6 * var(--u)); }
.gm-vscroll::-webkit-scrollbar { width: calc(6 * var(--u)); }
.gm-vscroll::-webkit-scrollbar-track { background: var(--cyan-a05); border-radius: calc(var(--r-bar) * var(--hu)); }
.gm-vscroll::-webkit-scrollbar-thumb { background: var(--cyan-a35); border-radius: calc(var(--r-bar) * var(--hu)); }
.gm-vscroll::-webkit-scrollbar-thumb:hover { background: var(--cyan-a55); }
.gm-vscroll-body { display: flex; flex-direction: column; gap: calc(4 * var(--u)); box-sizing: border-box; }
/* Group titles keep one margin wherever they sit: a detached first item makes a later title the first child, and a
   :first-child margin would change its measured height as it scrolls. */
.gm-vscroll-body > .gm-group, .gm-vscroll-body > .gm-group:first-child { margin-top: calc(10 * var(--u)); }
/* Reload notice above the footer keys: a reload-only graphics row changed (menu.js refreshGraphics). */
.gm-reload-note { display: flex; flex-wrap: wrap; align-items: center; gap: calc(8 * var(--u)) calc(14 * var(--u));
  color: var(--amber); font-size: max(var(--font-min), calc(15 * var(--u))); }
.gm-reload-note.hidden, .gm-reload-note .hidden { display: none; }
.gm-preset-value { min-width: calc(200 * var(--u)); text-align: center; }
/* Page title = JETS header (menu.js titlePlate ─▶ ui/kit.js jetsHeader); long titles end in an ellipsis, the tape yields first. */
.jt-title .gm-softkey-label { overflow: hidden; text-overflow: ellipsis; }
/* tab_key: soft key, auto width, height 40, padding link pad_x 12, gap 6; the active tab rests on the focus cut. */
.gm-tabs { display: flex; flex-wrap: wrap; gap: calc(6 * var(--u)); }
.gm-tab { height: calc(40 * var(--u)); font-size: max(var(--font-min), calc(16 * var(--u))); padding: 0 var(--link-pad-x); }
/* oled_panel: housing pad 16, gap 8, header 18 cyan with screen pad_x 10, OLED screen pad 10. */
.gm-oled-panel { gap: var(--gap-s); }
.gm-oled { max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--divider) transparent; }
.gm-oled-body { display: flex; flex-direction: column; gap: calc(4 * var(--u)); }
/* group_title: label 14 cyan over a 2 px divider, 3 px apart, group_gap 12 above. */
.gm-group { margin-top: calc(14 * var(--u)); margin-bottom: calc(4 * var(--u)); color: var(--hg-lit); }
.gm-group:first-child { margin-top: calc(4 * var(--u)); }
/* Settings value row = kit row card (div): layout only. Arrow focus and hover are the row recipe's corner brackets. */
.gm-vrow {
  min-height: calc(50 * var(--u)); display: flex; align-items: center; gap: calc(12 * var(--u));
  padding: calc(4 * var(--u)) calc(12 * var(--u)); box-sizing: border-box;
}
.gm-vrow-label { flex: 1 1 auto; min-width: calc(120 * var(--u)); font-size: max(var(--font-min), calc(19 * var(--u))); line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gm-vrow-value { width: calc(360 * var(--u)); max-width: 62%; flex: none; display: flex; align-items: center; justify-content: flex-end; gap: var(--gap-s); }
.gm-vrow-value.is-wide { width: auto; max-width: none; }
.gm-arrow { width: calc(34 * var(--u)); height: calc(34 * var(--u)); padding: 0; }
.gm-arrow::before { content: ''; width: calc(16 * var(--u)); height: calc(16 * var(--u)); background: var(--cyan);
  -webkit-mask: url('assets/ui/menu_v2/icons/chevron.png') center / contain no-repeat; mask: url('assets/ui/menu_v2/icons/chevron.png') center / contain no-repeat; }
.gm-arrow.is-left::before { transform: scaleX(-1); }
/* Cycle row at an end of its range (menu.js optionRow): the arrow that cannot step further dims (settings.rs
   dec_enabled / inc_enabled), it stays focusable and clickable (a click there does nothing). */
.gm-arrow.is-end { opacity: .35; }
.gm-cycle-value { width: calc(240 * var(--u)); min-width: 0; flex: 0 1 auto; white-space: nowrap; text-align: center; font-size: max(var(--font-min), calc(18 * var(--u))); color: var(--cyan); }
/* toggle_chips: soft keys 92 x 38, font 16, no padding, link legend; the shown state is selected (focus cut, cyan). */
.gm-toggle { width: calc(92 * var(--u)); height: calc(38 * var(--u)); font-size: max(var(--font-min), calc(16 * var(--u))); padding: 0; color: var(--link); }
/* WHAT: Value-row chips keep the 92 u width as a floor and grow to their word. WHY: "Frequent" (Radio chatter) was 47 px of
         text in a 43 px chip at 700 x 500, where 92 u is 43 px. */
.gm-vrow-value .gm-toggle { width: auto; min-width: calc(92 * var(--u)); padding: 0 calc(6 * var(--u)); white-space: nowrap; }
/* Bus rows (settings.rs BusStepper): readout bus_readout_width 56, font 17, cyan, right; meter height 12, radius 6,
   track #272c2e, amber fill (bus_meter_tone). The web meter is a range input drawn as that meter (audio/panel.js). */
.gm-readout { width: calc(56 * var(--u)); flex: none; text-align: right; font-size: max(var(--font-min), calc(17 * var(--u))); line-height: 1; color: var(--cyan); font-variant-numeric: tabular-nums; white-space: nowrap; }
.gm-meter { -webkit-appearance: none; appearance: none; flex: 1 1 auto; min-width: 0; margin: 0; height: calc(34 * var(--u)); background: transparent; cursor: pointer; }
.gm-meter::-webkit-slider-runnable-track { height: calc(12 * var(--u)); border-radius: calc(var(--r-chip) * var(--u));
  background: linear-gradient(90deg, var(--cyan-a55) 0, var(--cyan) var(--fill, 50%), var(--cyan-a10) var(--fill, 50%) 100%); }
.gm-meter::-moz-range-track { height: calc(12 * var(--u)); border-radius: calc(var(--r-chip) * var(--u)); background: var(--cyan-a10); }
.gm-meter::-moz-range-progress { height: calc(12 * var(--u)); border-radius: calc(var(--r-chip) * var(--u)); background: var(--cyan); }
.gm-meter::-webkit-slider-thumb { -webkit-appearance: none; width: calc(6 * var(--u)); height: calc(22 * var(--u)); margin-top: calc(-5 * var(--u));
  border-radius: calc(var(--r-tick) * var(--u)); background: var(--white); border: 0; }
.gm-meter::-moz-range-thumb { width: calc(6 * var(--u)); height: calc(22 * var(--u)); border-radius: calc(var(--r-tick) * var(--u)); background: var(--white); border: 0; }
.gm-meter:focus-visible { outline: var(--focus-w) solid var(--cyan); outline-offset: var(--focus-off); }
/* Muted bus (audio/panel.js MUTE chip): the meter dims so the row reads silent; the level stays for unmute. */
.gm-meter.is-muted { opacity: .35; }
.gm-mute { flex: 0 0 auto; }
/* Page actions (action_key): action_height 54, font 20, glyph 22 | divider 22, legend centred, pad = kit key pad / 2,
   gap = key_gap / 2, action_gap 14 between keys. */
.gm-page-actions { display: flex; flex-wrap: wrap; gap: calc(14 * var(--u)); }
.gm-page-actions .gm-pagekey { padding: 0 calc(10 * var(--u)); gap: calc(7 * var(--u)); max-width: 100%; }
.gm-page-actions .gm-kitkey-words { align-items: center; }
.gm-page-actions .gm-pagekey > .gm-kitkey-legend { text-align: center; }
.gm-nav-hint { display: flex; flex-wrap: wrap; justify-content: center; gap: calc(4 * var(--u)) calc(22 * var(--u)); font-size: max(var(--font-min), calc(14 * var(--u))); color: var(--hint); }
/* Keyboard navigation hints: none on touch devices (no keys to press; UI_UNIFORMITY C, main.js body.is-touch). */
body.is-touch .gm-nav-hint { display: none; }
.gm-nav-item { display: inline-flex; align-items: center; gap: calc(6 * var(--u)); white-space: nowrap; }
.gm-bind-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: calc(12 * var(--u));
  min-height: calc(50 * var(--u)); padding: calc(4 * var(--u)) calc(12 * var(--u)); box-sizing: border-box; }
/* Binding lines are read-only kit rows: no hover brackets, no press response. */
.gm-bind-row { pointer-events: none; }
.gm-bind-head { color: var(--hint); font-size: max(var(--font-min), calc(14 * var(--u))); min-height: calc(32 * var(--u)); }
.gm-bind-label { font-size: max(var(--font-min), calc(19 * var(--u))); line-height: 1.2; min-width: 0; overflow-wrap: anywhere; }
.gm-bind-head .gm-bind-label { font-size: inherit; }
.gm-bind-keys { display: flex; flex-wrap: wrap; gap: calc(10 * var(--u)); min-width: 0; }
.gm-bind-slot {
  min-width: calc(150 * var(--u)); height: calc(38 * var(--u)); box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  padding: 0 calc(10 * var(--u)); font-size: max(var(--font-min), calc(16 * var(--u))); line-height: 1; white-space: nowrap; max-width: 100%;
}
/* ── Settings Controls tab (operator #11359, cards S30-S32; menu_settings_controls.js) ──
     [Graphics] [Controls] [Audio]                     tabs (.gm-tab, the shown one .is-selected)
     [Touch] [Gyro] [Keyboard] [Joystick]     → swipe    anchor strip (.gm-anchors, Controls only, one line; short
     ┌ list panel ───────────────────────────────────┐   landscape phones: footer row beside Back, mobile.css)
     │ GENERAL ─────────────                          │  plain group title
     │ ▾ GYRO ─────────────                           │  section header key (.gm-section-head), ▸ when collapsed
     │   Hold the phone how you play…   [CALIBRATE]   │  + result line (.gm-ctl-result) under it
     │ ▾ JOYSTICK                                     │
     │   Xbox Controller   (L) (R)  A B X Y LB RB …   │  live test (.gm-pad-test): stick dots + lit button chips
     │   Action            Gamepad                    │  two-column binding table (.gm-bind-row.is-two)
     └────────────────────────────────────────────────┘ */
/* WHAT: Anchor strip: one line of chips that scrolls sideways (no bar) between the tabs and the list panel. WHY: Four
         section anchors must stay one row on a 360 px phone and never push the list down by wrapping. */
.gm-anchors { display: flex; flex-wrap: nowrap; gap: calc(6 * var(--u)); overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  touch-action: pan-x; overscroll-behavior-x: contain; flex: none; }
.gm-anchors::-webkit-scrollbar { display: none; }
.gm-anchor { flex: none; height: calc(34 * var(--u)); font-size: max(var(--font-min), calc(15 * var(--u))); padding: 0 var(--link-pad-x); }
/* WHAT: Section header key: the group title look on a full-width button, a chevron that points down when open and right
         when collapsed, the row focus tint on hover / arrow focus. WHY: A collapsed section stays reachable and reads as
         openable; the button has no native chrome. */
.gm-section-head { width: 100%; box-sizing: border-box; min-height: calc(40 * var(--u)); margin: 0; padding: 0 calc(6 * var(--u)); border: 0;
  background: transparent; font: inherit; font-size: max(var(--font-min), calc(14 * var(--u))); letter-spacing: .18em; text-align: left;
  color: var(--hg-lit); cursor: pointer; border-radius: calc(var(--r-chip) * var(--u)); }
.gm-section-head:is(:hover, .is-focus) { background-color: var(--cyan-a04); box-shadow: inset 0 0 0 1px var(--cyan-a35); }
.gm-section-head:focus-visible { outline: var(--focus-w) solid var(--cyan); outline-offset: var(--focus-off); }
.gm-section-title { order: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gm-section-chev { order: 4; flex: none; width: calc(9 * var(--u)); height: calc(9 * var(--u)); margin: 0 calc(6 * var(--u));
  border-right: 2px solid var(--cyan); border-bottom: 2px solid var(--cyan); transform: rotate(45deg); transition: transform .15s ease; }
.gm-section-head.is-collapsed { color: var(--h-muted); }
.gm-section-head.is-collapsed .gm-section-chev { transform: rotate(-45deg); }
/* WHAT: Note rows (empty section) are muted, wrap and take no row tint. WHY: Text, not a setting. */
.gm-ctl-note { pointer-events: none; min-height: calc(40 * var(--u)); }
.gm-ctl-note .gm-vrow-label { white-space: normal; color: var(--hint); font-size: max(var(--font-min), calc(16 * var(--u))); }
/* WHAT: The calibration row wraps its result line onto a full-width line under label and key. WHY: Plain-language result
         ("Done: this angle is now level flight") right under the key the player tapped. */
.gm-vrow.gm-ctl-row { flex-wrap: wrap; }
.gm-ctl-result { flex: 1 1 100%; min-width: 0; color: var(--cyan); font-size: max(var(--font-min), calc(15 * var(--u))); line-height: 1.25;
  overflow-wrap: anywhere; }
.gm-ctl-result:empty { display: none; }
.gm-vrow .gm-vrow-label { min-width: 0; }
.gm-ctl-key { width: auto; min-width: calc(140 * var(--u)); padding: 0 calc(14 * var(--u)); }
/* WHAT: Joystick live test: pad name, then two stick wells with a dot each and the 16 button chips, lit while held.
   WHY: Card S31 "a live axis/button test". The dot is a third of its well: translate(100 %) of the dot reaches the rim. */
.gm-pad-test { flex-wrap: wrap; row-gap: calc(8 * var(--u)); pointer-events: none; }
.gm-pad-test .gm-pad-name { white-space: normal; overflow-wrap: anywhere; color: var(--hint); }
.gm-pad-test.is-live .gm-pad-name { color: var(--hg-lit); }
.gm-pad-live { display: flex; flex-wrap: wrap; align-items: center; gap: calc(10 * var(--u)); min-width: 0; flex: 1 1 calc(360 * var(--u)); }
.gm-pad-stick { position: relative; flex: none; width: calc(48 * var(--u)); height: calc(48 * var(--u)); border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--cyan-a35); background: var(--cyan-a05); }
.gm-pad-dot { position: absolute; left: calc(50% - 8 * var(--u)); top: calc(50% - 8 * var(--u)); width: calc(16 * var(--u));
  height: calc(16 * var(--u)); border-radius: 50%; background: var(--cyan); will-change: transform; }
.gm-pad-buttons { display: flex; flex-wrap: wrap; gap: calc(4 * var(--u)); min-width: 0; flex: 1 1 0; }
.gm-pad-btn { min-width: calc(30 * var(--u)); height: calc(24 * var(--u)); padding: 0 calc(4 * var(--u)); justify-content: center;
  font-size: max(var(--font-min), calc(12 * var(--u))); }
/* WHAT: Two-column binding tables (Action | Keyboard / mouse, Action | Gamepad) and their group titles.
   WHY: Each section lists only its own device's inputs; the third column of the former page is gone. */
.gm-bind-row.is-two { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.gm-bind-group { color: var(--h-muted); font-size: max(var(--font-min), calc(13 * var(--u))); letter-spacing: .14em;
  padding: calc(8 * var(--u)) calc(12 * var(--u)) 0; }

/* ── RUN PAUSED (run_pause.json: art at source_scale 2) ── */
/* z-index 35: above the multiplayer HUD layers (overlay 30, roster/lobby cards 31) that covered the title plate in the
   lobby; below the multiplayer screens (40) and the room map panel (60), which stays clickable while paused.
   Operator 2026-11-02 pause polish: a flat dark scrim (no blur) over the frozen world, so ring gates, contrails and
   the jet stay scenery behind the card; the card is one panel at a near-opaque fill, centred in the layer. */
#pause.gm { background: var(--night-a75); z-index: 35; display: flex; align-items: center; justify-content: center;
  --hg-glass-top: var(--night-a94); --hg-glass-bot: var(--night-a97); }
/* Settings opened over a pause card kept underneath (menu.js show, phone canvas-off page, UI-LATENCY #5): above the card,
   still under the paused shell keys (shell.css body.shell-paused #shell z 36, later in the page). */
#settings.gm.gm-over-pause { z-index: 36; }
/* The card: header, then the stats block and the actions column on one grid; margin auto keeps it centred and lets a
   short window scroll the layer instead of clipping the top. No backdrop blur: the fill is opaque and the scrim flat. */
#pause .gm-pause-card { flex: none; margin: auto; width: min(calc(1120 * var(--u)), calc(100vw - 24px)); box-sizing: border-box;
  padding: calc(20 * var(--u)) calc(24 * var(--u)) calc(24 * var(--u)); display: flex; flex-direction: column; gap: calc(18 * var(--u));
  -webkit-backdrop-filter: none; backdrop-filter: none; }
.gm-pause-head .gm-softkey-label { font-size: max(var(--font-min), calc(40 * var(--hu))); }
.gm-mfds { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: calc(20 * var(--u)); align-items: stretch; }
.gm-pause-box { min-width: 0; box-sizing: border-box; display: flex; flex-direction: column; }
/* Stats block: score, rule, aircraft, facts, mode chip spread over the actions column's height (no dead box). */
.gm-mfd-stats { justify-content: space-between; gap: calc(12 * var(--u)); padding: calc(20 * var(--u)) calc(28 * var(--u)); }
.gm-pause-score-caption { font-size: max(var(--font-min), calc(21 * var(--u))); line-height: 1; letter-spacing: .14em; color: var(--text2); }
.gm-pause-score { display: flex; align-items: center; gap: calc(14 * var(--u)); min-width: 0; }
.gm-pause-score-value { font-size: max(var(--font-min), calc(64 * var(--u))); line-height: 1; color: var(--white); font-variant-numeric: tabular-nums; white-space: nowrap; }
.gm-pause-plane { display: flex; align-items: center; gap: calc(14 * var(--u)); min-width: 0; }
.gm-pause-plane-name { font-size: max(var(--font-min), calc(30 * var(--u))); line-height: 1.1; letter-spacing: .04em; color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-pause-facts { display: flex; align-items: stretch; gap: calc(20 * var(--u)); }
.gm-pause-fact { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: calc(6 * var(--u)); }
.gm-pause-fact-caption { font-size: max(var(--font-min), calc(21 * var(--u))); line-height: 1; letter-spacing: .12em; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-pause-fact-value { font-size: max(var(--font-min), calc(34 * var(--u))); line-height: 1; color: var(--white); font-variant-numeric: tabular-nums; }
/* Mode: one chip in the operator's wording ("ARCADE · Very Easy", menu.js modeLabel), the same text and case as the
   result column's mode pill (UI_UNIFORMITY.md R5: the pause card upper-cased it, the result card did not). */
.gm-pause-mode { align-self: flex-start; max-width: 100%; padding: calc(6 * var(--u)) calc(14 * var(--u)); font-size: max(var(--font-min), calc(21 * var(--u))); line-height: 1.2;
  letter-spacing: .08em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text2); }
/* Actions: four equal rows at one 12-unit pitch (three in the lobby, packed at the top). */
.gm-mfd-actions { gap: calc(12 * var(--u)); }
/* spawn_action_row: a slot filled by a JETS row card; icon 40, caption margin-left label_gap 30, key chip margin-left
   chip_gap 12, the chevron last so every row ends on the same column. */
.gm-pause-slot { position: relative; width: 100%; height: calc(88 * var(--u)); flex: none; }
/* width / height 100 %: the row is a <button>; Chrome 93 (A51) sizes an absolute button with inset 0 to its content
   (shrink-to-fit), so in landscape each row shrank to its words (UI-FLIGHT #2). Same box as inset 0 elsewhere. */
.gm-pause-line {
  position: absolute; inset: 0; width: 100%; height: 100%;
  padding: 0 calc(24 * var(--u)); display: flex; align-items: center; gap: 0; color: var(--white); text-align: left;
}
.gm-pause-caption { flex: 1 1 0; min-width: 0; margin-left: calc(30 * var(--u)); font-size: max(var(--font-min), calc(32 * var(--u))); line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-pause-cap {
  margin: 0 calc(12 * var(--u)); flex: none; line-height: 1; height: calc(40 * var(--u)); min-width: calc(64 * var(--u)); box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  padding: 0 calc(10 * var(--u)); font-size: max(var(--font-min), calc(21 * var(--u)));
}

/* ── result column (run_results.json: right third, dim 0.45) ── */
#results.gm { z-index: 12; }
.gm-results-dim { position: absolute; inset: 0; background: var(--black-a45); }
.gm-results-col { position: absolute; right: 0; top: 0; bottom: 0; width: 33.333vw; min-width: min(calc(560 * var(--u)), 100vw); box-sizing: border-box; padding: calc(24 * var(--u)); overflow-y: auto; }
.gm-results-inner { min-height: 100%; display: flex; flex-direction: column; justify-content: center; }
/* results.rs screen_box: padding kit screen pad + screen_pad (10 + 8 x 10 + 4), head_gap 8; stats stat_gap_y 6. */
.gm-results-screen { display: flex; flex-direction: column; gap: calc(8 * var(--u)); padding: calc(14 * var(--u)) calc(18 * var(--u)); box-sizing: border-box; }
.gm-results-screen:not(.is-centered) { gap: calc(6 * var(--u)); }
.gm-results-screen.is-centered { align-items: center; text-align: center; }
/* Result panel header = JETS header (menu.js jetsHeader); the panel and its data blocks follow the JETS recipes. */
.gm-results-housing { padding: calc(18 * var(--hu)); gap: calc(12 * var(--u)); display: flex; flex-direction: column; }
/* Mode pill = kit chip (lit); NEW BEST RUN! = kit chip tinted amber. */
.gm-result-mode { display: inline-block; padding: calc(2 * var(--u)) calc(14 * var(--u)); font-size: max(var(--font-min), calc(21 * var(--u))); line-height: 1.2; white-space: nowrap; }
.gm-hero { display: flex; flex-direction: column; align-items: center; gap: calc(2 * var(--u)); }
.gm-hero-caption { font-size: max(var(--font-min), calc(20 * var(--u))); color: var(--text2); }
/* The run score glows amber like the JETS lit names glow cyan (the phone draws it as a baked halo, ui/skin.css). */
.gm-hero-score { font-size: calc(84 * var(--u)); line-height: 1; color: var(--amber); font-variant-numeric: tabular-nums;
  text-shadow: 0 0 calc(22 * var(--u)) var(--amber-a55); }
.gm-result-badge { border: var(--hair) solid var(--amber); color: var(--amber); font-size: max(var(--font-min), calc(24 * var(--u))); padding: calc(2 * var(--u)) calc(16 * var(--u)); animation: gm-throb 1.05s ease-in-out infinite; }
/* badge_throb 0.07 at speed 6 rad/s (period ~1.05 s). */
@keyframes gm-throb { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.07); } }
/* WHAT: the pulsing badge holds still under the OS reduced-motion setting.
   WHY: an endless scale pulse is decorative motion (WCAG 2.3.3); the badge text carries the meaning. */
@media (prefers-reduced-motion: reduce) { .gm-result-badge { animation: none; } }
.gm-stat { display: flex; justify-content: space-between; align-items: baseline; gap: calc(20 * var(--u)); font-size: max(var(--font-min), calc(23 * var(--u))); line-height: 1.2; }
.gm-stat-caption { color: var(--text2); }
.gm-stat-value { color: var(--white); font-variant-numeric: tabular-nums; }
/* Result keys: rows_gap 6, key 58 / primary 72, key_gap 12, icon 34 | divider 36 | words | chevron 28. */
.gm-result-keys { display: flex; flex-direction: column; gap: calc(6 * var(--u)); }
.gm-result-line { box-sizing: border-box; padding: var(--key-pad-y) var(--key-pad-x); display: flex; align-items: center; justify-content: center;
  width: 100%; min-width: 0; min-height: var(--hit-min); overflow: hidden; height: calc(58 * var(--u)); text-align: left; gap: calc(12 * var(--u)); flex: none; }
.gm-result-line.is-primary { height: calc(72 * var(--u)); }
.gm-result-words { flex: 1 1 0; display: flex; flex-direction: column; justify-content: center; gap: calc(1 * var(--u)); min-width: 0; }
.gm-result-legend { font-size: max(var(--font-min), clamp(calc(20 * var(--u)), 1.6vw, calc(30 * var(--u)))); line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A legend without a sub-line sits directly in its key (UI-LATENCY #5): it takes the words column's place. */
.gm-result-line > .gm-result-legend { flex: 1 1 0; min-width: 0; }
.gm-result-line.is-primary .gm-result-sub { color: var(--ink-a72); }
.gm-result-sub { font-size: max(var(--font-min), calc(20 * var(--u))); line-height: 1.15; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── portrait phones: panels stack full width (web-only; the game has no portrait layout) ── */
.gm-portrait .gm-p-title { width: 70vw; }
.gm-portrait .gm-p-play { width: calc(100vw - 72 * var(--u)); top: calc(260 * var(--u)); max-height: 60vh; }
/* Portrait: the hangar column spans the width; its panel unit follows the column width (mobile.css sets the height). */
.gm-portrait .gm-hangar-col { --hu: calc((100vw - 28 * var(--u)) / 500); width: calc(100vw - 28 * var(--u)); top: auto; height: 62vh; }
.gm-portrait .gm-mfds { grid-template-columns: minmax(0, 1fr); }
.gm-portrait .gm-results-col { width: 100vw; }

/* Hangar BACK (operator 2026-10-29 pass 2): one glass frame like the service keys (the kit key art drew a dark outer
   pill around it), cyan ‹ chevron, upper-case legend, stretched to the FLY row's height. */
#menu .gm-hangar-backkey { height: 100%; min-height: 0; gap: calc(10 * var(--hu, var(--u))); padding: 0 calc(14 * var(--hu, var(--u)));
  font-size: max(var(--font-min), calc(24 * var(--hu, var(--u)))); letter-spacing: .08em; text-transform: uppercase; color: var(--white); }
.gm-hangar-back-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* Choose-jet download bar (#jetload, main.js jetBar; operator 2026-11-02 "optimistic with LOD first and showing progress
   bar"): the menu-time pill recipe (.jt-panel.bgload: JETS glass panel, segmented .gm-hangar-bar gauge) centred on the
   point main.js writes as --jl-x / --jl-y (under the jet in landscape, above the jet column in portrait), --jl-w wide
   (showcase.json preview.bar_width_px). The indeterminate sweep (.loader-bar--indet, loader.css) runs when the server
   sends no Content-Length. */
.bgload.jetload { left: var(--jl-x, 50%); top: var(--jl-y, 80%); bottom: auto; transform: translate(-50%, -50%);
  width: var(--jl-w, 220px); min-width: 0; box-sizing: border-box; }
.jetload .bgload-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jetload-bar { position: relative; overflow: hidden; }

/* ── Compact landscape (responsive UI, operator 2026-11-03 "the UI NOT being responsive enough") ──
   WHAT: Landscape windows narrower than 1200 px or shorter than 600 px (phones in landscape, small laptops, half-screen
         windows): the NEWS column takes at most a third of the window (34vw) next to the main column, and the menu-time
         loading pill moves from the bottom centre into the free middle column of the home, sized to that gap.
   WHY:  The 1920 x 1080 home (620 u main + 640 u news) only shrinks with --u; on a 667-915 px phone the news column and
         the bottom-centre pill (min 220 px) crowded the main keys (the pill covered BUY THE GAME at 667 x 375). The
         middle column is the part of the home kept free for the world, so the pill never covers a key there.
         Middle column = from home-x 36 u + main 620 u + gap 22 u to the news column start (window - 36 u - news - 22 u). */
@media (orientation: landscape) and (max-width: 1199.98px), (orientation: landscape) and (max-height: 599.98px) {
  body:not(.gm-portrait) #menu .gm-home { --home-news-w: min(calc(640 * var(--u)), 34vw); }
  /* Stacked service keys (RANKING / PILOT / BUY THE GAME): the caption wraps onto two centred lines instead of being
     cut at the 11 px font floor (it drew "UY THE GAM" in a 70 px key at 667 x 375). */
  body:not(.gm-portrait) #menu .gm-home-key.is-stacked .gm-home-key-label { white-space: normal; text-align: center; overflow-wrap: break-word; }
  body.gm-on-home:not(.gm-portrait) .bgload:not(.bgload--join):not(.jetload) {
    --mid-l: calc(678 * var(--u));
    --mid-r: calc(100vw - 58 * var(--u) - min(calc(640 * var(--u)), 34vw));
    left: calc((var(--mid-l) + var(--mid-r)) / 2); bottom: var(--safe-b, 16px);
    width: min(260px, calc(var(--mid-r) - var(--mid-l) - 12px)); min-width: 0; box-sizing: border-box; }
  body.gm-on-home:not(.gm-portrait) .bgload:not(.bgload--join):not(.jetload) .bgload-label {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* In-flight cursor capture prompt (ui/lock_prompt.js, operator 2026-11-03): one JETS panel centred over the view,
   "CLICK TO CAPTURE THE CURSOR" + "Esc releases it". It takes no pointer events, so the click lands on the canvas,
   where controls.js requests the lock inside that gesture. z 34: over the flight HUD and the multiplayer layers, under
   the pause card (35), which hides it anyway. Refused / no API: amber sub line; compact = a small top line after
   lock_prompt.compact_after_ms so keyboard flight goes on without a panel in the middle. */
.lock-prompt { position: fixed; inset: 0; z-index: 34; display: flex; align-items: center; justify-content: center;
  pointer-events: none; font-family: 'Russo One', sans-serif; user-select: none; -webkit-user-select: none; }
.lock-prompt .lock-prompt-card { display: flex; flex-direction: column; align-items: center; gap: calc(8 * var(--u));
  padding: calc(18 * var(--u)) calc(28 * var(--u)); max-width: calc(100vw - 32px); text-align: center; }
.lock-prompt-title { color: var(--cyan); font-size: max(var(--font-min), calc(26 * var(--u))); letter-spacing: .08em; }
.lock-prompt-sub { color: var(--h-muted); font-size: max(var(--font-min), calc(16 * var(--u))); letter-spacing: .04em; }
.lock-prompt.is-refused .lock-prompt-sub { color: var(--amber); }
.lock-prompt.is-compact { align-items: flex-start; padding-top: calc(72 * var(--u)); }
.lock-prompt.is-compact .lock-prompt-card { flex-direction: row; gap: calc(14 * var(--u)); padding: calc(8 * var(--u)) calc(16 * var(--u)); }
.lock-prompt.is-compact .lock-prompt-title { font-size: max(var(--font-min), calc(15 * var(--u))); }
.lock-prompt.is-compact .lock-prompt-sub { font-size: max(var(--font-min), calc(13 * var(--u))); }

/* FPS chip (ui/fps_counter.js; Settings > Graphics > HUD > Performance overlay): top-left, under the safe area, above
   the HUD and menus, never takes a touch. */
.gm-fps { position: fixed; z-index: 60; left: calc(var(--safe-l, 0px) + 8px); top: calc(var(--safe-t, 0px) + 8px);
  padding: 2px 8px; font: 14px/1.3 'Russo One', sans-serif; color: var(--cyan); background: var(--black-a42);
  pointer-events: none; }
/* Round 4 r21: a stacked service caption is never wider than its key, so menu.js fitLegend can measure the overflow and
   shrink it (360 px phone: captions were cut on both sides). */
.gm-home-service .gm-home-key.is-stacked .gm-home-key-label { max-width: 100%; }
/* Round 4 r23: on a portrait phone the four service keys (RANKING, PILOT, SKINS, BUY MILES) sit in a 2x2 grid of
   icon | caption keys. In one row of four each caption had 58 px: es CLASIFICACIÓN / COMPRAR MILLAS and fr CLASSEMENT /
   ACHETER DES MILLES needed 77-92 px even at 10 px. */
body.gm-portrait #menu .gm-home-service { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
body.gm-portrait #menu .gm-home-service .gm-home-key.is-stacked { flex-direction: row; justify-content: flex-start; gap: 10px;
  height: 48px !important; padding: 0 12px; }
/* r24: a long caption wraps to a second line inside its 48 px key instead of ending in an ellipsis (fr ACHETER DES
   MILLES needed 118 px at 10 px in a 104 px cell); a single long word still shrinks through menu.js fitService. */
body.gm-portrait #menu .gm-home-service .gm-home-key.is-stacked .gm-home-key-label { white-space: normal; overflow-wrap: normal;
  text-align: start; line-height: 1.1; }
/* r25: START FLYING caption sits left like its cost line and like MULTIPLAYER (it was centred over a left-aligned
   "1 MILE · YOU HAVE 3" line). */
#menu .gm-home-mp-words .gm-home-key-label { text-align: start; }
