/* Safe-area insets as resolved lengths (registered so JS can read them computed) and the phone stage scale. */
@property --sai-t { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --sai-b { syntax: '<length>'; inherits: true; initial-value: 0px; }
:root {
  --sai-t: env(safe-area-inset-top, 0px); --sai-b: env(safe-area-inset-bottom, 0px); --stage-scale: 1;
  --navy: #0f1b2d; --navy2: #172740; --steel: #2b3644; --bakelite: #1c1a19; --bakelite2: #2a2624;
  --brass: #c9a24a; --brass2: #f2c14e; --paper: #efe6cf; --paper2: #e3d7b6; --ink: #2a2118; --ink2: #5a4b36;
  --red: #b3372f; --green: #3c9a5f; --amber: #ffb347; --glow: #ffd27a;
  --font: "Courier New", Courier, monospace; --head: Georgia, "Times New Roman", serif;
  /* Type scale. Phone values here; the desktop layer raises them. Nothing readable goes below --fs-xs. */
  --fs-xs: 13px; --fs-sm: 14px; --fs-base: 16px; --fs-md: 18px; --fs-lg: 20px; --fs-xl: 26px; --fs-2xl: 34px;
  --cap-h: 36px; /* room caption bar height; props are laid out above it */
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; overflow: hidden; }
body { display: flex; flex-direction: row; align-items: stretch; justify-content: center; height: 100vh; height: 100dvh;
  background: #05080d radial-gradient(900px 600px at 50% -10%, #1a2a40, #05080d 70%);
  color: var(--paper); font-family: var(--font); font-size: var(--fs-base); line-height: 1.4; -webkit-user-select: none; user-select: none; }
button { font-family: inherit; }
/* Pinch zoom stays available; double-tap zoom is off on the controls so a fast tap on a button or the key is never a zoom. */
button, .key, .prop, .sw, .band, .sc, .mn, .item, .dlg-portrait, .dlg-pad button, #keydeck, #dialogue, .dlg-pad { touch-action: manipulation; }
.key { touch-action: none; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
button.sc { font: inherit; color: inherit; text-align: inherit; }
kbd { background: #333; border: 1px solid #666; border-bottom-width: 3px; padding: 0 5px; border-radius: 3px; font-family: var(--font); color: #eee; }
.hidden { display: none !important; }
.muted { color: #9aa4b1; }
.tiny { font-size: var(--fs-sm); color: #9aa4b1; }

/* Gate */
/* Fixed outside the stage; sized to the small viewport (every browser bar shown) and padded past the
   safe areas, so the card is never under a toolbar or a notch. fitGate() in app.js scales the card
   down on screens shorter than it; the inner scroll is the last resort. */
.gate { position: fixed; inset: 0; height: 100vh; height: 100svh; background: rgba(5, 9, 15, .96); z-index: 50; display: flex; align-items: safe center; justify-content: center; padding: max(12px, env(safe-area-inset-top, 0px)) max(16px, env(safe-area-inset-right, 0px)) max(12px, env(safe-area-inset-bottom, 0px)) max(16px, env(safe-area-inset-left, 0px)); overflow-y: auto; }
.gate-card { text-align: center; max-width: 560px; width: 100%; margin: auto; background: var(--navy2); border: 2px solid var(--brass); padding: 20px; box-shadow: 0 0 60px #000; }
.gate-card h1 { font-family: var(--head); letter-spacing: 3px; margin: 14px 0 4px; font-size: var(--fs-2xl); color: var(--brass2); }
.gate-card .sub { letter-spacing: 2px; font-size: var(--fs-xs); color: #b9c3d0; margin: 0 0 14px; }
.gate-art { height: min(200px, 24vh); height: min(200px, 22svh); }
#gate-install { margin-top: 10px; }
/* Title menu: one column of buttons; the primary one pulses. Panels swap in place of the menu. */
.gate-panel { display: grid; gap: 10px; justify-items: stretch; max-width: 360px; margin: 0 auto; }
.gate-menu .btn.big { font-size: var(--fs-lg); padding: 12px 18px; }
.gate-menu .btn.small, .gate-back { justify-self: center; }
.gate .list { text-align: left; }
/* No network at the gate: the shell is cached but the station is not reachable, so the notice
   reads as a signal problem rather than a broken app. */
.gate-offline { color: var(--brass); }
.gate .list .item { background: #142238; border-color: var(--brass); color: var(--paper); }
.gate .list .item:hover { border-color: var(--brass2); background: #1b2e4a; }
.gate .item small { color: #b9c3d0; }
.gate .item .btn { margin-left: auto; text-decoration: none; }
.gate-label { display: grid; gap: 6px; text-align: left; font-size: var(--fs-sm); color: #b9c3d0; }
.gate .answer { text-transform: none; font-size: var(--fs-lg); padding: 8px; color: var(--ink); }
.gate.busy .gate-panel { pointer-events: none; opacity: .6; }
.gate-load { display: block; text-align: center; }
.gate-who { display: block; font-size: var(--fs-sm); letter-spacing: 1px; opacity: .85; }

/* Viewport probe: the small viewport (every browser toolbar shown), read by fitStage in app.js. */
#vprobe { position: fixed; left: 0; top: 0; width: 100vw; width: 100svw; height: 100vh; height: 100svh; visibility: hidden; pointer-events: none; z-index: -1; }
/* Landscape phones: ask for portrait. */
.rotate { display: none; position: fixed; inset: 0; z-index: 55; background: #05080d; color: var(--brass2); align-items: center; justify-content: center; text-align: center; font-family: var(--head); letter-spacing: 3px; }
.rotate b { display: block; font-size: 22px; margin-bottom: 8px; }
.rotate small { display: block; font-family: var(--font); font-size: 14px; letter-spacing: 1px; color: #9aa4b1; }
@media (pointer: coarse) and (orientation: landscape) and (max-height: 500px) { .rotate { display: flex; } }

/* Header */
#top { flex: none; display: flex; align-items: center; gap: 16px; padding: 8px 16px; padding-top: max(8px, env(safe-area-inset-top)); background: linear-gradient(#22334b, #101a2a); border-bottom: 2px solid var(--brass); flex-wrap: nowrap; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .eagle { font-size: 26px; color: var(--brass2); }
.brand b { display: block; font-family: var(--head); letter-spacing: 2px; color: var(--brass2); font-size: var(--fs-base); }
.brand small { display: block; font-size: var(--fs-xs); letter-spacing: 1px; color: #9aa4b1; }
.who { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.who-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
.abbr { flex: none; font-style: normal; font-family: var(--head); font-size: var(--fs-xs); letter-spacing: 1px; padding: 1px 5px; border: 1px solid var(--brass); color: var(--brass2); line-height: 1.3; }
.who-text { min-width: 0; }
.who-text b { display: block; font-size: var(--fs-base); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.who-text span { font-size: var(--fs-sm); color: var(--brass2); }
.xp { display: flex; align-items: center; gap: 8px; font-size: var(--fs-xs); color: #9aa4b1; margin-top: 3px; white-space: nowrap; }
.xp-bar { width: 140px; height: 8px; background: #0a111c; border: 1px solid #3a4a60; }
#xpbar { height: 100%; background: linear-gradient(90deg, var(--brass), var(--brass2)); width: 0; transition: width .6s; }
.loc small { display: block; font-size: var(--fs-xs); letter-spacing: 2px; color: #9aa4b1; }
.loc span { font-size: var(--fs-sm); white-space: nowrap; }
.ctl { display: flex; align-items: center; gap: 8px; }
.tog { width: 36px; height: 36px; border-radius: 50%; border: 2px solid var(--brass); background: #111; color: #666; cursor: pointer; font-size: 18px; }
.tog.on { color: var(--brass2); box-shadow: 0 0 8px var(--brass); }
.vol { font-size: var(--fs-xs); color: #9aa4b1; display: flex; align-items: center; gap: 4px; }
.vol input { width: 80px; }
#btn-room.on { border-color: var(--brass2); color: var(--brass2); }
#btn-sound { display: none; font-size: 15px; } /* the phone's one sound button (music and effects); desktop keeps ♪ */

/* ---- App shell (shared by phone and desktop). #app is the game canvas; #main holds
   the task panel, the instructor dialogue, the receiver drawer and the key deck. ---- */
#app { position: relative; width: 100%; height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; background: var(--navy); box-shadow: 0 0 90px #000; }
#main { display: flex; flex-direction: column; flex: 1; min-height: 0; padding: 0; gap: 0; width: 100%; }
#station { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scroll-behavior: smooth; border: 0; border-radius: 0; box-shadow: none; padding: 0; }
@media (prefers-reduced-motion: reduce) { #station { scroll-behavior: auto; } }
#content:has(.scene) { height: 100%; }
#content:not(:has(.scene)) { min-height: 100%; padding: 14px; }
#content .art.wide { aspect-ratio: 16 / 5; }
/* The key deck is the desk itself: oak boards under a lamp, no frame. The sending strip is a paper slip, the meter an instrument on it. */
#keydeck { flex: none; display: flex; flex-direction: column; gap: 6px; align-items: stretch; padding: 6px 10px; padding-bottom: max(6px, env(safe-area-inset-bottom));
  border: 0; border-top: 3px solid #5e412b; border-radius: 0;
  background: linear-gradient(180deg, rgba(255,230,180,.07), rgba(0,0,0,0) 14%, rgba(0,0,0,.38)),
    repeating-linear-gradient(91deg, rgba(0,0,0,.12) 0 2px, transparent 2px 9px, rgba(255,220,160,.045) 9px 12px, transparent 12px 27px),
    repeating-linear-gradient(89.5deg, transparent 0 41px, rgba(0,0,0,.09) 41px 43px),
    linear-gradient(180deg, #4d3524, #3c2819 55%, #2b1b10);
  box-shadow: inset 0 8px 16px rgba(0,0,0,.45); }
#keydeck .pad { box-shadow: 0 2px 5px rgba(0,0,0,.55); }
#keydeck { position: relative; }
/* The code sheet from the desk: a small ? pinned beside the key (phones; desktop has CODE SHEET in the header). */
.deck-sheet { display: none; position: absolute; right: 6px; bottom: 14px; width: 32px; height: 32px; padding: 0; border-radius: 50%; border: 2px solid var(--brass); background: #111; color: var(--brass2); font: bold var(--fs-md) var(--font); line-height: 1; cursor: pointer; z-index: 2; box-shadow: 0 2px 6px rgba(0,0,0,.6); }
.deck-sheet:active { filter: brightness(1.3); }
#rx-open { display: none; }
.scene { margin: 0; height: 100%; aspect-ratio: auto; }
.chart { margin: 0; }
/* The receiver (dial, bands, meters) is a drawer. On the phone it replaces the task panel. */
#radio.drawer { display: none; flex: 1; min-height: 0; overflow-y: auto; border-radius: 0; margin: 0; border-left: 0; border-right: 0; }
body:not(.simple) #radio.drawer { display: block; animation: scene .25s ease-out both; }
#radio .dial-wrap { max-width: min(92vw, 46dvh, 460px); margin: 0 auto; }
#radio .dial-row { margin-top: 4px; }
.drawer-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
/* Overlays live inside the canvas. */
.sheet, .modal, #toast { position: absolute; }
#toast { top: 60px; }
.gate { position: fixed; }

/* Panels */
.panel { border-radius: 8px; padding: 14px; }
.bakelite { background: linear-gradient(160deg, #2c2826, #171514); border: 2px solid #3a332f; box-shadow: inset 0 0 40px #000, 0 6px 18px #000; }
.paper { background: var(--paper); color: var(--ink); border: 2px solid #b8a67f; box-shadow: 0 6px 18px #000; -webkit-user-select: text; user-select: text; }
.paper h2, .paper h3 { font-family: var(--head); margin: 0 0 8px; color: var(--ink); }
.paper h2 { font-size: var(--fs-xl); letter-spacing: 1px; }
.paper h3 { font-size: var(--fs-lg); }
.paper .stamp { display: inline-block; border: 2px solid var(--red); color: var(--red); padding: 1px 8px; font-weight: bold; letter-spacing: 2px; transform: rotate(-3deg); font-size: var(--fs-sm); margin-bottom: 8px; }
.paper .muted { color: var(--ink2); }
.paper .brief { white-space: pre-wrap; }
.paper hr { border: 0; border-top: 1px dashed #b8a67f; margin: 10px 0; }

/* Art placeholders */
.art { background: #1d2a3d; position: relative; overflow: hidden; background-size: cover; background-position: center; }
.art.wide { aspect-ratio: 16 / 9; width: 100%; }
.art::after { content: attr(data-label); position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 12px; font-family: var(--head); letter-spacing: 2px; color: #7f95b3; font-size: var(--fs-sm); background: repeating-linear-gradient(135deg, rgba(255,255,255,.02) 0 12px, transparent 12px 24px); }
.art.ready::after { content: none; }

/* Dial */
.dial-row { display: flex; gap: 12px; align-items: stretch; flex-wrap: wrap; }
.dial-wrap { flex: none; width: 100%; max-width: 230px; }
#dial { width: 100%; height: auto; cursor: grab; touch-action: none; }
#dial.dragging { cursor: grabbing; }
#dial:focus-visible, .item:focus-visible, .mn:focus-visible { outline: 3px solid var(--brass2); outline-offset: 2px; }
#dial:focus-visible { border-radius: 50%; }
.dial-face { fill: #0d0f12; stroke: var(--brass); stroke-width: 3; }
.tick { stroke: #6b6560; stroke-width: 1; }
.tick.major { stroke: var(--brass2); stroke-width: 2; }
.scale { fill: var(--brass2); font-size: 11px; font-family: var(--font); }
.knob-body { fill: url(#kg); fill: #2b2724; stroke: #0a0a0a; stroke-width: 2; }
.knob-grip { fill: #17130f; }
.knob-cap { fill: var(--brass); }
.pointer { stroke: var(--red); stroke-width: 3; stroke-linecap: round; }
.slot { fill: #3b4a60; }
.slot.open { fill: var(--green); filter: drop-shadow(0 0 3px var(--green)); }
.slot.locked { fill: var(--red); }
.dial-side { flex: 1; display: flex; flex-direction: column; gap: 8px; min-width: 150px; }
.readout { background: #060a08; border: 2px solid #3a332f; color: #86ff9a; font-size: var(--fs-xl); padding: 6px 8px; text-align: right; text-shadow: 0 0 8px #3cff5a; font-family: var(--font); letter-spacing: 2px; }
.lamp-row { display: flex; align-items: center; gap: 8px; font-size: var(--fs-xs); letter-spacing: 2px; color: #9aa4b1; }
.lamp { width: 16px; height: 16px; border-radius: 50%; background: #3a1c1c; border: 2px solid #555; }
.lamp.lit { background: var(--green); box-shadow: 0 0 12px var(--green); }
.lamp.lit.locked-out { background: var(--red); box-shadow: 0 0 12px var(--red); }
.bands { display: flex; flex-direction: column; gap: 4px; }
.band { text-align: left; background: #0f0d0c; border: 1px solid #3a332f; color: #c9c2b5; padding: 5px 8px; cursor: pointer; font-size: var(--fs-sm); letter-spacing: 1px; }
.band:hover { border-color: var(--brass); }
.band.active { border-color: var(--brass2); color: var(--brass2); background: #1a1612; }
.band.locked { color: #6b6560; }
.band.locked::after { content: " 🔒"; }
.band b { font-weight: normal; }

/* Receiver switches: BAND and MODE rows of toggle positions. */
.rx-switches { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 10px; }
.rx-row { display: flex; align-items: center; gap: 10px; }
.rx-row label { flex: none; width: 4.2em; font-size: var(--fs-xs); letter-spacing: 2px; color: #9aa4b1; }
.sw-row { display: flex; gap: 6px; flex: 1; }
.sw { flex: 1; min-width: 0; padding: 8px 4px; font-family: var(--font); font-size: var(--fs-sm); letter-spacing: 1px; color: #c9c2b5; background: linear-gradient(#2c2826, #171514); border: 2px solid #3a332f; border-radius: 6px; cursor: pointer; box-shadow: 0 3px 0 #0a0a0a; }
.sw.on { color: #1a1408; background: linear-gradient(#e8cf7f, #a7853a); border-color: var(--brass2); box-shadow: 0 1px 0 #0a0a0a, 0 0 10px rgba(242,193,78,.5); transform: translateY(2px); }
.sw.off { opacity: .4; cursor: not-allowed; }
.sw:not(.off):hover { border-color: var(--brass); }

/* Meters */
.meters { display: flex; gap: 12px; margin-top: 12px; align-items: flex-end; flex-wrap: wrap; }
.meter { text-align: center; font-size: var(--fs-xs); letter-spacing: 2px; color: #9aa4b1; }
.meter-face { width: 90px; height: 50px; background: #f5efdc; border: 3px solid #3a332f; border-radius: 90px 90px 0 0; position: relative; overflow: hidden; }
.meter-face::before { content: ""; position: absolute; right: 0; top: 0; width: 28%; height: 100%; background: linear-gradient(90deg, transparent, rgba(179,55,47,.5)); }
.needle { position: absolute; left: 50%; bottom: 0; width: 2px; height: 46px; background: #222; transform-origin: bottom; transform: rotate(-80deg); transition: transform .25s; }
#swr-meter.hidden { display: none; }
.knobs { display: flex; gap: 10px; font-size: var(--fs-xs); letter-spacing: 1px; color: #9aa4b1; flex: 1; flex-wrap: wrap; }
.knobs label { display: flex; flex-direction: column; gap: 3px; }
.knobs span { color: var(--brass2); font-size: var(--fs-sm); }
.knobs input { width: 90px; }

/* Key deck: a one-line sending strip, the rhythm meter, then the key across the full width. */
.key-wrap { text-align: center; flex: none; display: flex; flex-direction: column; align-items: center; }
.keylabel { width: 100%; font-size: var(--fs-xs); letter-spacing: 3px; color: var(--brass2); margin-bottom: 4px; font-weight: bold; }
.keyhelp { font-size: var(--fs-xs); letter-spacing: 1px; color: #d8dee6; font-weight: normal; margin-top: 6px; }
.keyhelp kbd { color: var(--brass2); border-color: var(--brass); }
/* The straight key: a J-38 hand key drawn in SVG, sitting on the desk with no box round it.
   The lever pivots on its trunnion when pressed and springs back with a little overshoot, the
   front contact closes with a spark, a gleam runs down the brass now and then, and a pool of
   lamp light pulses on the desk under it while it waits for the first press. */
.key { width: 100%; max-width: 520px; aspect-ratio: 2 / 1; position: relative; cursor: pointer; touch-action: none; user-select: none; -webkit-user-select: none; background: none; border: 0; }
.keysvg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.k-pool { opacity: 0; transition: opacity .5s; }
.key.idle .k-pool { animation: pool 1.6s ease-in-out infinite; }
@keyframes pool { 0%, 100% { opacity: .2; } 50% { opacity: .85; } }
.k-shadow { fill: #000; opacity: .55; }
.k-lever { transform-box: view-box; transform-origin: 118px 80px; transition: transform .16s cubic-bezier(.3, 1.8, .5, 1); }
.k-spring { transform-box: view-box; transform-origin: 58px 113px; transition: transform .16s cubic-bezier(.3, 1.8, .5, 1); }
.k-knob { transition: filter .08s; }
.k-gleam { animation: gleam 9s linear infinite; }
@keyframes gleam { 0%, 80% { transform: translateX(-80px); } 100% { transform: translateX(300px); } }
.k-spark { opacity: 0; transform-box: fill-box; transform-origin: center; transition: opacity .04s; mix-blend-mode: screen; }
.key.down .k-lever { transform: rotate(3deg); transition: transform .04s ease-out; }
.key.down .k-spring { transform: scaleY(.8); transition: transform .04s ease-out; }
.key.down .k-knob { filter: brightness(.8); }
.key.down .k-spark { opacity: 1; animation: spark .18s ease-out; }
.key.down .k-shadow { opacity: .7; }
@keyframes spark { 0% { transform: scale(.4); opacity: 1; } 60% { transform: scale(1.4); opacity: .9; } 100% { transform: scale(1); opacity: 1; } }
.key-press { position: absolute; left: 12px; top: 8px; font-size: var(--fs-sm); letter-spacing: 3px; color: var(--glow); text-shadow: 0 0 6px var(--amber); opacity: 0; transition: opacity .3s; }
.key.idle .key-press { opacity: 1; }
.key:hover .k-knob { filter: brightness(1.2); }
@media (prefers-reduced-motion: reduce) { .key.idle .k-pool { animation: none; opacity: .55; } .k-gleam { animation: none; } .key.down .k-spark { animation: none; } .k-lever, .k-spring { transition-timing-function: ease-out; } }
/* Sending strip: what the decoder made of your keying, newest on the right. */
.pad { flex: none; min-width: 0; background: var(--paper); color: var(--ink); border: 2px solid #b8a67f; padding: 4px 8px; display: flex; align-items: center; gap: 8px; }
.pad-label { font-size: var(--fs-xs); letter-spacing: 2px; color: var(--ink2); flex: none; }
.pad-text { flex: 1; min-width: 0; font-size: var(--fs-xl); letter-spacing: 3px; white-space: nowrap; overflow: hidden; line-height: 1.2; padding: 2px 0; }
.elements { font-size: var(--fs-md); color: var(--red); letter-spacing: 2px; min-width: 3ch; flex: none; }
#wpm { font-size: var(--fs-xs); letter-spacing: 1px; color: var(--ink2); flex: none; white-space: nowrap; }
.pad .btn { flex: none; }
/* Rhythm meter: every element you send as a bar against the ideal 1:3 timing. */
.rhythm { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 2px 4px; background: #0d0f12; border: 1px solid #3a332f; border-radius: 6px; }
.rhythm-bars { flex: 1; display: flex; align-items: flex-end; gap: 3px; height: 38px; min-width: 0; overflow: hidden; position: relative; }
.rhythm-bars::before { content: ""; position: absolute; left: 0; right: 0; bottom: 12.5%; border-top: 1px dashed rgba(242,193,78,.5); pointer-events: none; }
.rhythm-bars::after { content: ""; position: absolute; left: 0; right: 0; bottom: 37.5%; border-top: 1px dashed rgba(242,193,78,.5); pointer-events: none; }
.rbar { flex: 0 0 9px; border-radius: 2px 2px 0 0; background: #6b6560; min-height: 3px; transition: height .12s; }
.rbar.gap { flex-basis: 5px; opacity: .55; }
.rbar.good { background: var(--green); } .rbar.fair { background: var(--amber); } .rbar.bad { background: var(--red); }
.rhythm-coach { flex: 0 1 46%; min-width: 0; font-size: var(--fs-sm); line-height: 1.25; color: var(--paper); }
.rhythm-coach:empty { display: none; }
.rhythm-coach.good { color: #a8f0c0; }

/* Buttons and content */
.btn { background: linear-gradient(#3a4a60, #1b2634); color: #fff; border: 2px solid var(--brass); padding: 8px 14px; letter-spacing: 2px; cursor: pointer; font-size: var(--fs-base); }
.btn:hover { background: linear-gradient(#4a5c76, #26344a); }
.btn:disabled { opacity: .5; cursor: default; }
.btn.big { font-size: var(--fs-lg); padding: 12px 24px; }
.btn.small { font-size: var(--fs-sm); padding: 4px 10px; letter-spacing: 1px; }
.btn.red { border-color: var(--red); }
.btn.gold { background: linear-gradient(#c9a24a, #8a6a20); color: #1a1408; }
.actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.list { display: grid; gap: 6px; }
.item { display: flex; gap: 10px; align-items: center; padding: 8px; border: 1px solid #b8a67f; background: #f6efdc; cursor: pointer; }
.item:hover { border-color: var(--ink); }
/* Pressed states that work on touch as well as hover: a state change only, no motion. */
.prop:active .art { filter: brightness(1.25); outline: 2px solid var(--brass2); outline-offset: -2px; }
.item:active:not(.locked) { border-color: var(--brass); filter: brightness(1.04); }
.item.locked { opacity: .5; cursor: default; }
.item .art { width: 64px; height: 44px; flex: none; }
.item .art::after { font-size: 10px; letter-spacing: 0; padding: 2px; }
.item b { display: block; }
.item small { color: var(--ink2); font-size: var(--fs-sm); }
.item .score { margin-left: auto; font-weight: bold; color: var(--green); white-space: nowrap; }
.item .score.partial { color: var(--amber); }
.item .tag { font-size: var(--fs-xs); letter-spacing: 1px; border: 1px solid var(--ink2); padding: 0 4px; color: var(--ink2); }
.copybox { width: 100%; font-family: var(--font); font-size: var(--fs-lg); letter-spacing: 3px; padding: 8px; border: 2px solid #b8a67f; background: #fffaf0; color: var(--ink); text-transform: uppercase; min-height: 60px; resize: vertical; }
.copybox:focus { outline: 2px solid var(--brass); }
.answer { width: 100%; font-family: var(--font); font-size: var(--fs-base); padding: 6px; border: 2px solid #b8a67f; background: #fffaf0; text-transform: uppercase; }
.progress { height: 6px; background: #d9cfae; margin: 8px 0; }
.progress > div { height: 100%; background: var(--green); width: 0; transition: width .1s linear; }
.mnemonics { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 6px; }
.mn { border: 1px solid #b8a67f; padding: 6px; background: #f6efdc; cursor: pointer; }
.mn b { font-size: var(--fs-xl); display: block; }
.mn .code { font-size: var(--fs-lg); color: var(--red); letter-spacing: 2px; }
.mn small { display: block; color: var(--ink2); font-size: var(--fs-sm); }
.target { font-size: var(--fs-xl); letter-spacing: 3px; padding: 8px; background: #fffaf0; border: 2px dashed #b8a67f; margin: 8px 0; word-break: break-all; }
.target .done { color: var(--green); }
.target .bad { color: var(--red); text-decoration: underline; }
.feedback { padding: 8px; margin: 8px 0; border-left: 4px solid var(--green); background: #e8f2e8; }
.feedback.bad { border-color: var(--red); background: #f5e6e4; }
.df { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.compass { width: 140px; height: 140px; border-radius: 50%; border: 3px solid var(--ink); background: radial-gradient(#fffaf0, #e3d7b6); position: relative; flex: none; }
.compass .loop { position: absolute; left: 50%; top: 50%; width: 6px; height: 120px; margin: -60px 0 0 -3px; background: var(--ink); border-radius: 3px; transform-origin: center; }
.compass .n { position: absolute; top: 4px; left: 50%; transform: translateX(-50%); font-weight: bold; }
.sig { height: 14px; background: #d9cfae; border: 1px solid var(--ink2); }
.sig > div { height: 100%; background: linear-gradient(90deg, var(--green), var(--amber), var(--red)); }
.swr-box { border: 2px solid #b8a67f; padding: 8px; background: #f6efdc; margin: 8px 0; }
.swr-val { font-size: var(--fs-xl); font-weight: bold; }
.swr-val.ok { color: var(--green); }
.swr-val.bad { color: var(--red); }
.qso-line { padding: 6px 8px; margin: 4px 0; border-left: 4px solid var(--ink2); background: #f6efdc; }
.qso-line.them { border-color: var(--red); }
.qso-line.you { border-color: var(--green); }
.qso-line.active { outline: 2px solid var(--brass); }

/* Modal + toast */
.modal { position: fixed; inset: 0; background: rgba(0,0,0,.75); z-index: 40; display: flex; align-items: center; justify-content: center; padding: 16px; }
.modal-card { max-width: 640px; width: 100%; max-height: 92vh; max-height: 92dvh; overflow: auto; padding: 14px; }
#toast { position: fixed; left: 50%; top: 70px; transform: translateX(-50%); display: flex; flex-direction: column; gap: 6px; z-index: 60; pointer-events: none; }
.toast-msg { background: var(--navy2); color: var(--brass2); border: 2px solid var(--brass); padding: 8px 14px; letter-spacing: 1px; font-size: var(--fs-sm); box-shadow: 0 4px 16px #000; animation: toast 4s forwards; }
@keyframes toast { 0% { opacity: 0; transform: translateY(-10px); } 10% { opacity: 1; transform: none; } 85% { opacity: 1; } 100% { opacity: 0; } }
.df-note { margin-top: 8px; display: block; }
.df-note .feedback { display: block; }

/* Orders of the Day */
.steps { list-style: none; padding: 0; margin: 10px 0; }
.steps li { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid #b8a67f; margin-bottom: 4px; background: #f6efdc; }
.steps li.done { opacity: .6; }
.steps li.done .mark { color: var(--green); font-weight: bold; }
.steps li.next { border: 2px solid var(--brass); background: #fff6dc; }
.steps li.next .mark { color: var(--red); }
.steps li.locked { opacity: .5; }
.steps .lbl { flex: 1; }
.steps .xp { font-size: var(--fs-xs); color: var(--ink2); white-space: nowrap; }
.mn.active { outline: 3px solid var(--brass); background: #fff6dc; }
.btn.begin { width: 100%; text-align: center; }

/* Code sheet drawer */
.sheet { position: fixed; right: 0; top: 0; bottom: 0; width: min(440px, 100%); background: var(--paper); color: var(--ink); border-left: 3px solid var(--brass); z-index: 45; overflow: auto; padding: 12px; box-shadow: -8px 0 30px #000; }
.sheet-head { display: flex; align-items: center; gap: 10px; justify-content: space-between; margin-bottom: 10px; }
.sheet-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px; }
.sc { border: 1px solid #b8a67f; background: #f6efdc; padding: 6px; text-align: center; cursor: pointer; }
.sc b { display: block; font-size: var(--fs-lg); }
.sc span { color: var(--red); letter-spacing: 2px; font-size: var(--fs-base); }
.sc-break { grid-column: 1 / -1; height: 6px; }

/* Campaign map */
.loc-card { display: flex; gap: 10px; border: 1px solid #b8a67f; background: #f6efdc; padding: 8px; margin-top: 8px; }
.loc-card .art { width: 160px; min-height: 90px; flex: none; }
.loc-card.locked { opacity: .55; }
.loc-text { flex: 1; min-width: 0; }
.loc-text small { display: block; color: var(--ink2); margin-bottom: 6px; }
.list.mini .item { padding: 5px 8px; }

/* Stations */
.st-controls { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin: 8px 0; font-size: var(--fs-sm); }
.st-controls select, .settings select { font-family: var(--font); font-size: var(--fs-sm); padding: 4px; }
.chk { display: flex; align-items: center; gap: 6px; }
.st-now { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); letter-spacing: 1px; margin: 6px 0; }
.st-log { margin-top: 8px; }
.st-line { display: grid; grid-template-columns: 20px 1fr 1fr; gap: 8px; padding: 3px 6px; font-size: var(--fs-md); letter-spacing: 2px; border-bottom: 1px dashed #d9cfae; }
.st-line.ok .mark { color: var(--green); } .st-line.bad .mark, .st-line.bad .got { color: var(--red); }
.st-stats { display: flex; gap: 14px; flex-wrap: wrap; font-size: var(--fs-xs); letter-spacing: 1px; color: var(--ink2); margin-top: 8px; }
.st-stats b { color: var(--ink); font-size: var(--fs-base); }

/* Multiplayer world */
.net-top { display: grid; grid-template-columns: 1.3fr 1fr; gap: 12px; }
.map-wrap { position: relative; border: 2px solid var(--ink); background: #b9c8cf; }
.worldmap { width: 100%; height: auto; display: block; }
.worldmap .sea { fill: #9db7c4; }
.worldmap .land { fill: #d8cfae; stroke: #6b5f45; stroke-width: .4; }
.worldmap .lake { fill: #9db7c4; stroke: #6b5f45; stroke-width: .3; }
.worldmap .lane { fill: none; stroke: #6b5f45; stroke-width: .4; stroke-dasharray: 1.5 1.2; }
.worldmap .lbl { font-size: 3px; font-family: var(--head); fill: #4a3f2c; letter-spacing: .5px; }
.map-dots { position: absolute; inset: 0; pointer-events: none; }
.dot { position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; background: #6b6560; border: 2px solid #fff; }
.dot.on { background: var(--red); box-shadow: 0 0 10px var(--red); }
.dot span { position: absolute; left: 16px; top: -4px; font-size: var(--fs-xs); font-weight: bold; color: var(--ink); background: rgba(255,250,240,.85); padding: 0 3px; }
.net-side { display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-sm); }
.net-side select { font-family: var(--font); font-size: var(--fs-sm); max-width: 100%; }
.net-you .on { color: var(--green); font-weight: bold; } .net-you .off { color: var(--red); font-weight: bold; }
.roster { max-height: 280px; overflow: auto; }
.op { padding: 4px 6px; border-left: 3px solid var(--ink2); margin-bottom: 4px; background: #f6efdc; }
.op.me { border-color: var(--brass); }
.op small { color: var(--ink2); }
.op-copy { font-size: var(--fs-base); letter-spacing: 2px; min-height: 16px; color: var(--red); }
.net-log { margin-top: 10px; max-height: 200px; overflow: auto; background: #fffaf0; border: 2px solid #b8a67f; padding: 6px; font-size: var(--fs-md); letter-spacing: 1px; }
.nl.sys { color: var(--ink2); font-size: var(--fs-sm); letter-spacing: 0; }
.net-map-fold { margin-top: 10px; } .net-map-fold .map-wrap { margin-top: 8px; }

/* Honour board */
.leaderboard .tabs { margin: 0 0 10px; }
.lb-table table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
.lb-table th { text-align: left; font-size: var(--fs-xs); letter-spacing: 2px; color: var(--ink2); border-bottom: 2px solid #b8a67f; padding: 4px 6px; }
.lb-table td { padding: 6px; border-bottom: 1px dashed #d9cfae; }
.lb-table td:first-child { font-weight: bold; width: 2.5em; }
.lb-table td:last-child { text-align: right; font-weight: bold; }
.lb-table td small { color: var(--ink2); font-weight: normal; }
.lb-table tr.you td { background: #fff6dc; color: var(--ink); }
.lb-table tr:nth-child(1) td:first-child { color: var(--brass); }
/* Key locker */
.key-cards { display: grid; gap: 10px; margin: 10px 0; }
.key-card { display: flex; gap: 12px; border: 2px solid #b8a67f; background: #f6efdc; padding: 10px; animation: rise .4s ease-out both; animation-delay: calc(var(--i, 0) * 60ms); }
.key-card .art { width: 140px; aspect-ratio: 1; flex: none; border: 2px solid #b8a67f; }
.key-card.locked { opacity: .6; } .key-card.locked .art { filter: grayscale(1) brightness(.7); }
.key-card.fitted { border-color: var(--brass); background: #fff6dc; }
.key-text { flex: 1; min-width: 0; } .key-text b { font-family: var(--head); font-size: var(--fs-lg); }
.key-text .era { display: block; color: var(--ink2); letter-spacing: 1px; font-size: var(--fs-xs); margin: 2px 0 6px; }
.key-text p { margin: 0 0 6px; } .key-text .how { display: block; color: var(--ink2); font-size: var(--fs-sm); }
.key-text .actions { margin-top: 8px; } .stamp.fit { border-color: var(--green); color: var(--green); }
.keyer-speed label { display: flex; align-items: center; gap: 10px; }
/* The stations board on the desk: a red on-air lamp on its frame. */
.prop.stations .art { border-color: #8a6a20; box-shadow: 0 10px 22px rgba(0,0,0,.65), inset 0 0 0 1px rgba(255,230,160,.25), 0 0 14px rgba(179,55,47,.45); }
/* Practice nets in the wardroom */
.prop.ainet .art { border-color: #6b5a30; }
.nl.ai b { color: var(--red); }
.net-side .chk { font-size: var(--fs-sm); }

/* Settings */
.s-toggles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 6px 0 4px; }
.s-toggles .btn { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 8px; opacity: .6; filter: grayscale(.6); }
.s-toggles .btn.on { opacity: 1; filter: none; border-color: var(--brass2); box-shadow: 0 0 10px rgba(242,193,78,.45); }
.s-toggles .s-ico { font-size: var(--fs-xl); line-height: 1.1; }
.s-toggles b { font-size: var(--fs-base); letter-spacing: 2px; }
.s-toggles small { font-size: var(--fs-sm); color: var(--brass2); letter-spacing: 1px; }
.settings { display: flex; flex-direction: column; gap: 10px; }
.settings label { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.settings .answer { width: auto; flex: 1; min-width: 160px; }

/* Distress watch */
.sos-btn { display: block; margin: 14px auto 8px; width: 150px; height: 150px; border-radius: 50%; border: 5px solid #f2c14e; cursor: pointer; font-family: var(--font); color: #fff;
  background: radial-gradient(circle at 35% 30%, #e0584c, var(--red) 55%, #5e1a14); box-shadow: 0 8px 18px rgba(0,0,0,.5), inset 0 -6px 10px rgba(0,0,0,.4); transition: transform .06s; }
.sos-btn:disabled { filter: grayscale(.6) brightness(.7); cursor: default; }
.sos-btn:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 3px 8px rgba(0,0,0,.5), inset 0 -3px 6px rgba(0,0,0,.4); }
.sos-btn b { display: block; font-size: 40px; line-height: 1; letter-spacing: 2px; }
.sos-btn .code { display: block; font-size: var(--fs-sm); letter-spacing: 1px; color: #ffe6a3; white-space: nowrap; margin-top: 4px; }
.sos-btn small { display: block; font-size: var(--fs-xs); letter-spacing: 2px; margin-top: 4px; }
.sos-btn.hit { animation: sosflash .6s; }
.sos-btn.miss { animation: sosshake .4s; }
@keyframes sosflash { 0% { box-shadow: 0 0 0 0 rgba(60,154,95,.9); } 100% { box-shadow: 0 0 0 30px rgba(60,154,95,0); } }
@keyframes sosshake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-8px); } 75% { transform: translateX(8px); } }
.watch-stats { text-align: center; letter-spacing: 2px; font-size: var(--fs-sm); color: var(--ink2); margin-bottom: 8px; }
.watch-stats b { color: var(--ink); font-size: var(--fs-md); }
.watch-log { max-height: 130px; overflow-y: auto; font-size: var(--fs-sm); border-top: 1px dashed #b8a67f; padding-top: 6px; }
.watch-log div { padding: 2px 0; color: var(--ink2); }
.watch-log .good { color: var(--green); }
.watch-log .bad { color: var(--red); }

/* Scene transition */
.scene-in { animation: scene .25s ease-out both; }
@keyframes scene { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .scene-in { animation: none; } }

/* Campaign chart: the level path */
.chart { position: relative; padding: 0 0 30px; min-height: 100%; isolation: isolate; }
.chart-bg { position: absolute; inset: 0; z-index: 0; opacity: .5; background-size: cover; background-position: center top;
  background-color: #e3d7b6; background-image: repeating-linear-gradient(0deg, rgba(90,75,54,.12) 0 1px, transparent 1px 44px), repeating-linear-gradient(90deg, rgba(90,75,54,.12) 0 1px, transparent 1px 44px); }
.chart-bg::after { content: none; }
.chart-path { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: visible; }
.chart-path .lane { fill: none; stroke: #6b5f45; stroke-width: 3; stroke-dasharray: 8 7; stroke-linecap: round; opacity: .8; }
.chart-path .lane.done { stroke: var(--brass); stroke-dasharray: none; stroke-width: 4; opacity: 1; filter: drop-shadow(0 0 3px rgba(242,193,78,.6)); }
.chapter { position: relative; z-index: 1; }
.ch-banner { position: sticky; top: 0; z-index: 3; display: flex; gap: 12px; align-items: center; background: rgba(239,230,207,.94); border-top: 2px solid #b8a67f; border-bottom: 2px solid #b8a67f; padding: 8px 14px; backdrop-filter: blur(2px); }
.ch-banner .art { width: 150px; aspect-ratio: 16 / 9; flex: none; border: 2px solid #b8a67f; }
.ch-text small { display: block; font-size: var(--fs-xs); letter-spacing: 3px; color: var(--ink2); }
.ch-text b { display: block; font-family: var(--head); font-size: var(--fs-lg); line-height: 1.15; margin: 2px 0 6px; }
.ch-text .stamp { margin: 0; font-size: var(--fs-xs); transform: rotate(-2deg); }
.chapter.locked .ch-banner { background: rgba(214,204,178,.94); }
.chapter.locked .ch-banner .art { filter: grayscale(1) brightness(.65); }
.chapter.locked .ch-text b { color: var(--ink2); }
.nodes { list-style: none; margin: 0; padding: 26px 0 30px; display: flex; flex-direction: column; align-items: center; gap: 38px; --amp: 130px; }
.node { position: relative; z-index: 1; display: flex; align-items: center; gap: 6px; transform: translateX(calc(var(--x) * var(--amp))); max-width: calc(100% - 2 * var(--amp) - 20px); }
.nb { display: flex; align-items: center; gap: 10px; border: 3px solid #b8a67f; background: #f6efdc; color: var(--ink); padding: 6px 14px 6px 6px; border-radius: 40px; cursor: pointer; font-family: var(--font); text-align: left; box-shadow: 0 5px 14px rgba(0,0,0,.35); transition: transform .12s, box-shadow .12s; max-width: 100%; }
.nb:hover { transform: translateY(-2px); box-shadow: 0 8px 18px rgba(0,0,0,.4); border-color: var(--brass); }
.badge { width: 50px; height: 50px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: var(--fs-xl); font-weight: bold; border: 3px solid var(--brass); background: radial-gradient(circle at 35% 30%, #fff3c4, var(--brass) 70%); color: #1a1408; box-shadow: inset 0 -3px 5px rgba(0,0,0,.25); }
.nb .lbl { min-width: 0; }
.nb .lbl b { display: block; font-size: var(--fs-base); letter-spacing: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 280px; }
.nb .lbl small { display: block; color: var(--ink2); font-size: var(--fs-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 280px; }
.nb .score { color: var(--green); font-size: var(--fs-sm); }
.det { flex: none; padding: 4px 7px; }
.node.done .badge { background: radial-gradient(circle at 35% 30%, #ffe6a3, var(--brass) 55%, #8a6a20); color: #1a1408; }
.node.done .nb { opacity: .88; }
.node.current .nb { border-color: var(--brass2); background: #fff6dc; transform: scale(1.06); box-shadow: 0 8px 22px rgba(0,0,0,.45); }
.node.current .badge { background: radial-gradient(circle at 35% 30%, #ff8a7a, var(--red) 55%, #5e1a14); color: #fff; border-color: var(--brass2); animation: beacon 1.6s ease-in-out infinite; }
@keyframes beacon { 0%, 100% { box-shadow: inset 0 -3px 5px rgba(0,0,0,.25), 0 0 0 0 rgba(255,179,71,.7); } 50% { box-shadow: inset 0 -3px 5px rgba(0,0,0,.25), 0 0 0 14px rgba(255,179,71,0); } }
.node.locked .nb { opacity: .6; filter: grayscale(.6); cursor: not-allowed; box-shadow: none; }
.node.locked .badge { font-size: var(--fs-md); }
.node.quest .badge { border-radius: 10px; transform: rotate(45deg); background: radial-gradient(circle at 35% 30%, #d9e6ff, #3a4a60 70%); color: #fff; border-color: #9aa4b1; }
.node.quest .badge::after { content: "◇"; transform: rotate(-45deg); }
.node.quest .badge { font-size: 0; }
.node.quest .badge::after { font-size: var(--fs-xl); }
.node.quest.live .badge { background: radial-gradient(circle at 35% 30%, #a8f0c0, var(--green) 70%); }
.node.shake { animation: sosshake .4s; }
.chart-end { position: relative; z-index: 1; text-align: center; color: var(--ink2); padding: 0 12px; }
@media (prefers-reduced-motion: reduce) { .node.current .badge { animation: none; box-shadow: 0 0 0 5px rgba(255,179,71,.45); } .nb, .nb:hover { transition: none; transform: none; } }
.chart-nav { position: relative; z-index: 2; display: flex; align-items: center; gap: 8px; padding: 10px 14px 0; }
/* Heat row: one cell per learned character, green / amber / red by the weaker of sending and copying, grey untried. */
.heat { position: relative; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 8px 14px 0; }
.heat small { width: 100%; font-size: var(--fs-xs); letter-spacing: 2px; color: var(--ink2); }
.hc { width: 26px; height: 26px; display: grid; place-items: center; font-weight: bold; font-size: var(--fs-sm); border: 1px solid #b8a67f; border-radius: 4px; color: var(--ink); background: #d9cfae; }
.hc.good { background: #bfe3c9; border-color: var(--green); }
.hc.fair { background: #ffe0a3; border-color: var(--amber); }
.hc.bad { background: #f3b8b2; border-color: var(--red); }
.hc.none { color: var(--ink2); }

/* Result stamp card and XP flight */
.result-card { position: relative; border: 2px dashed #b8a67f; background: #fffaf0; padding: 14px 14px 10px; margin: 10px 0; text-align: center; }
.result-card .stamp.big { font-size: var(--fs-xl); padding: 4px 16px; border-width: 3px; margin-bottom: 8px; animation: stampin .4s cubic-bezier(.2, 1.4, .4, 1) both; }
.result-card.pass .stamp.big { border-color: var(--green); color: var(--green); }
.result-card .rc-row { font-size: var(--fs-sm); letter-spacing: 2px; color: var(--ink2); display: flex; justify-content: center; gap: 18px; align-items: baseline; }
.result-card .rc-row b { color: var(--ink); font-size: var(--fs-md); }
.result-card .rc-xp { color: var(--green); font-weight: bold; font-size: var(--fs-base); }
.result-card.fail .rc-xp { color: var(--ink2); }
@keyframes stampin { from { opacity: 0; transform: rotate(-16deg) scale(1.9); } 70% { opacity: 1; } to { opacity: 1; transform: rotate(-4deg) scale(1); } }
.xpfly { position: fixed; z-index: 70; transform: translate(-50%, -50%); background: var(--navy2); color: var(--brass2); border: 2px solid var(--brass); padding: 6px 12px; font-weight: bold; letter-spacing: 2px; font-size: var(--fs-base); box-shadow: 0 4px 16px #000; pointer-events: none; will-change: transform; transition: transform .75s cubic-bezier(.2, .8, .2, 1), opacity .75s; }
.xp-bar.bump { animation: bump .4s ease-out; }
#pabbr.bump { animation: bump .6s ease-out; }
@keyframes bump { 30% { transform: scale(1.18); box-shadow: 0 0 14px var(--brass2); } 100% { transform: none; } }
/* Promotion ceremony */
.modal-card.ceremony { text-align: center; }
.modal-card.ceremony #modal-art { width: 200px; aspect-ratio: 1; height: auto; border-radius: 50%; margin: 0 auto 12px; border: 4px solid var(--brass); box-shadow: 0 0 40px var(--brass); animation: stampin .6s cubic-bezier(.2, 1.4, .4, 1) both; }
.modal-card.ceremony h2 { font-size: var(--fs-xl); }
.modal-card.ceremony #modal-actions { display: flex; justify-content: center; }
.gear-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin: 8px 0; }
.gear-card { border: 2px solid #b8a67f; background: #f6efdc; padding: 8px; animation: rise .45s ease-out both; animation-delay: calc(200ms + var(--i) * 160ms); }
.gear-card .art { aspect-ratio: 16 / 9; width: 100%; margin-bottom: 6px; }
.gear-card b { display: block; font-size: var(--fs-base); } .gear-card small { display: block; color: var(--ink2); font-size: var(--fs-sm); }
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .result-card .stamp.big, .xp-bar.bump, #pabbr.bump, .modal-card.ceremony #modal-art, .gear-card { animation: none; } }

/* Rooms: point-and-click scenes */
.scene { position: relative; overflow: hidden; background: #1c1a19; isolation: isolate; --px: 0; --py: 0; user-select: none; }
.scene-bg { position: absolute; inset: -3%; background-size: cover; background-position: center; transform: translate(calc(var(--px) * -10px), calc(var(--py) * -6px)); transition: transform .15s ease-out; filter: saturate(.85); }
.scene-bg::after { font-size: var(--fs-xl); color: #4a5a70; background: radial-gradient(ellipse at 50% 80%, #3a4a5e, #141c28 70%); }
.scene-bg.ready::after { content: none; }
.scene::before { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: radial-gradient(ellipse at 50% 30%, rgba(255,210,122,.16), transparent 55%), linear-gradient(to bottom, rgba(0,0,0,.25), transparent 30%, transparent 70%, rgba(0,0,0,.45)); }
.scene-lamp { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: radial-gradient(ellipse at 50% 0%, rgba(255,200,110,.28), transparent 45%); animation: lampflicker 5s infinite; mix-blend-mode: screen; }
@keyframes lampflicker { 0%, 100% { opacity: 1; } 47% { opacity: .92; } 50% { opacity: .75; } 53% { opacity: .95; } 80% { opacity: .9; } }
.scene-props { position: absolute; inset: 0 0 var(--cap-h) 0; z-index: 2; }
.prop { position: absolute; width: calc(var(--w) * 1%); padding: 0; border: 0; background: none; cursor: pointer; font-family: var(--font); color: var(--paper); text-align: center;
  transform: translate(calc(var(--px) * var(--depth) * -18px), calc(var(--py) * var(--depth) * -10px)); transition: transform .15s ease-out, filter .15s; }
.prop .art { width: 100%; aspect-ratio: 1; border: 3px solid #5a4a30; box-shadow: 0 10px 22px rgba(0,0,0,.65), inset 0 0 0 1px rgba(255,230,160,.25); background-color: #23303f; }
.prop .art::after { font-size: var(--fs-xs); padding: 6px; letter-spacing: 1px; }
.prop:hover .art, .prop:focus-visible .art { border-color: var(--brass2); box-shadow: 0 12px 26px rgba(0,0,0,.7), 0 0 18px rgba(242,193,78,.55); }
.prop:hover { filter: brightness(1.12); }
.prop:active { transform: translate(calc(var(--px) * var(--depth) * -18px), calc(var(--py) * var(--depth) * -10px + 2px)); }
.prop .plate { display: block; margin: 5px auto 0; width: fit-content; max-width: 100%; background: linear-gradient(#e8cf7f, #a7853a); color: #1a1408; border: 1px solid #5a4620; padding: 2px 5px; font-size: var(--fs-xs); letter-spacing: .5px; font-weight: bold; box-shadow: 0 2px 4px rgba(0,0,0,.6); white-space: normal; overflow-wrap: anywhere; overflow: hidden; line-height: 1.25; }
.prop .plate small { display: block; font-size: var(--fs-xs); letter-spacing: 0; font-weight: normal; }
.prop.locked .art { filter: grayscale(1) brightness(.55); }
.prop.locked .plate { background: linear-gradient(#8a8a8a, #4a4a4a); color: #ddd; }
.prop-badge { position: absolute; top: -8px; right: -8px; min-width: 26px; height: 26px; border-radius: 13px; background: var(--red); color: #fff; font-weight: bold; font-size: var(--fs-sm); display: grid; place-items: center; border: 2px solid #fff3c4; box-shadow: 0 3px 8px rgba(0,0,0,.6); animation: badgebounce 1.8s ease-in-out infinite; }
@keyframes badgebounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
.prop-text { position: absolute; left: 50%; top: 58%; transform: translate(-50%, -50%) rotate(-3deg); background: #fffaf0; color: var(--red); border: 2px solid var(--red); padding: 1px 6px; font-weight: bold; font-size: var(--fs-sm); letter-spacing: 2px; white-space: nowrap; }
.prop.decor { cursor: default; pointer-events: none; }
.prop.porthole .art { border-radius: 50%; border: 5px solid #8a7238; box-shadow: 0 8px 20px rgba(0,0,0,.7), inset 0 0 20px rgba(0,0,0,.6); background-size: 130% auto; animation: seapan 14s ease-in-out infinite alternate; }
@keyframes seapan { from { background-position: 0% 50%; } to { background-position: 100% 45%; } }
.prop.clock .art { border-radius: 50%; }
.hands { position: absolute; inset: 0; width: 100%; height: auto; aspect-ratio: 1; pointer-events: none; }
.hands line { stroke: #1a1408; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; transform-box: view-box; filter: drop-shadow(0 1px 1px rgba(0,0,0,.6)); }
.hands .mh { stroke-width: 3; }
.hands circle { fill: var(--brass2); }
.scene-caption { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; min-height: var(--cap-h); display: flex; align-items: center; background: rgba(18,16,15,.92); border-top: 1px solid var(--brass); padding: 4px 12px; font-size: var(--fs-sm); letter-spacing: 1px; color: var(--paper); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scene-caption b { color: var(--brass2); margin-right: 8px; letter-spacing: 2px; flex: none; }
.scene-caption span { overflow: hidden; text-overflow: ellipsis; }
/* CONTINUE TRAINING: on the caption bar after LATER, until the chief is back. */
.scene-caption .resume-chief { display: none; margin-left: auto; flex: none; font-size: var(--fs-base); padding: 3px 8px; border-color: var(--brass2); color: var(--brass2); background: linear-gradient(#3a4a60, #1b2634); }
body.chief-away:not(.training) .scene-caption .resume-chief { display: inline-block; }
@media (prefers-reduced-motion: reduce) { .scene-lamp, .prop-badge, .prop.porthole .art { animation: none; } .scene-bg, .prop { transition: none; } }
/* Inbox */
.inbox .slips { display: flex; flex-direction: column; gap: 10px; margin: 12px 0; }
.slip { position: relative; text-align: left; border: 1px solid #b8a67f; background: #fffaf0; color: var(--ink); padding: 10px 14px; font-family: var(--font); font-size: var(--fs-base); cursor: pointer; transform: rotate(calc(var(--r) * .4deg)); box-shadow: 0 3px 8px rgba(0,0,0,.25); background-image: repeating-linear-gradient(0deg, transparent 0 22px, rgba(90,75,54,.12) 22px 23px); }
.slip:hover { transform: rotate(0) translateY(-2px); box-shadow: 0 6px 14px rgba(0,0,0,.3); border-color: var(--brass); }
.slip-kind { display: block; font-size: var(--fs-xs); letter-spacing: 3px; color: var(--ink2); }
.slip b { display: block; font-family: var(--head); font-size: var(--fs-md); margin: 2px 0; }
.slip small { display: block; color: var(--ink2); font-size: var(--fs-sm); }
.slip-xp { position: absolute; right: 14px; bottom: 10px; color: var(--green); font-weight: bold; font-size: var(--fs-sm); }
.slip-new { position: absolute; right: 12px; top: 8px; font-size: var(--fs-xs); transform: rotate(8deg); }

/* Instructor dialogue */
.dlg { flex: none; display: flex; gap: 10px; align-items: stretch; background: linear-gradient(#2c2826, #171514); border-top: 2px solid var(--brass); padding: 8px 10px; color: var(--paper); min-height: 118px; cursor: pointer; animation: scene .25s ease-out both; }
.dlg-portrait { width: 72px; flex: none; aspect-ratio: 1; border: 2px solid var(--brass); border-radius: 6px; background-color: #23303f; align-self: flex-start; }
.dlg-portrait::after { font-size: var(--fs-xs); padding: 2px; letter-spacing: 0; }
.dlg-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.dlg-name { font-size: var(--fs-xs); letter-spacing: 3px; color: var(--brass2); margin-bottom: 4px; }
.dlg-text { font-size: var(--fs-lg); line-height: 1.35; flex: 1; min-height: calc(1.35em * 2); }
.dlg-goal { margin: 6px 0 2px; font-size: var(--fs-2xl); line-height: 1.2; letter-spacing: 4px; font-weight: bold; color: #9aa4b1; word-break: break-word; }
.dlg-goal.long { font-size: var(--fs-xl); letter-spacing: 3px; }
.dlg-goal.xlong { font-size: var(--fs-lg); letter-spacing: 2px; }
.dlg-goal span.done { color: var(--green); }
.dlg-goal span.next { color: var(--brass2); text-decoration: underline; text-underline-offset: 6px; }
.dlg-goal span.filled { color: var(--paper); }
.dlg-goal span.wrong { color: var(--red); text-decoration: line-through; }
/* The copy keypad: every learned character, a ? for a miss and a backspace. */
.dlg-pad { display: grid; grid-template-columns: repeat(auto-fill, 38px); gap: 4px; margin: 6px 0 2px; justify-content: start; }
.dlg-pad button { width: 38px; height: 38px; padding: 0; font-family: var(--font); font-size: var(--fs-lg); font-weight: bold; color: var(--paper); background: linear-gradient(#3a4a60, #1b2634); border: 2px solid var(--brass); border-radius: 6px; cursor: pointer; box-shadow: 0 3px 0 #0a0a0a; }
.dlg-pad button:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 #0a0a0a; }
.dlg-pad button:disabled { opacity: .5; cursor: default; }
.dlg-pad button.miss { color: var(--amber); }
.dlg-pad button.back { color: #9aa4b1; font-size: var(--fs-md); }
.cs-char.wrong { border-color: var(--red); } .cs-char.wrong b { color: var(--red); }
.copy-slots { font-size: var(--fs-xl); letter-spacing: 3px; padding: 8px; background: #fffaf0; border: 2px dashed #b8a67f; margin: 8px 0; word-break: break-all; min-height: 2em; }
.copy-slots span.next { color: var(--brass); text-decoration: underline; }
.copy-slots span.filled { color: var(--ink); }
.copy-pad .dlg-pad { justify-content: center; }
.copy-pad .dlg-pad button { color: var(--ink); background: #f6efdc; border-color: #b8a67f; box-shadow: 0 3px 0 #b8a67f; }
.dlg-actions { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.dlg-later { margin-left: auto; opacity: .7; }
/* The code strip: each character the player must send or copy, with its pattern. Elements light
   as the code plays (.lit), as the player keys them (.keyed / .wrong), and the character ticks off (.done). */
.dlg-strip { display: flex; flex-wrap: wrap; gap: 6px 12px; margin: 6px 0 2px; }
.cs-char { display: inline-flex; flex-direction: column; align-items: center; min-width: 2.2em; padding: 3px 6px; border: 1px solid #3a332f; border-radius: 6px; background: rgba(0,0,0,.35); transition: border-color .15s, background .15s; }
.cs-char b { font-size: var(--fs-xl); line-height: 1.05; color: var(--paper); }
.cs-code { display: flex; gap: 3px; margin-top: 2px; font-style: normal; font-weight: bold; font-size: var(--fs-lg); line-height: 1; color: #9aa4b1; letter-spacing: 0; }
.cs-code i { font-style: normal; display: inline-block; min-width: .6em; text-align: center; transition: color .08s, transform .08s; }
.cs-code i.lit { color: var(--glow); text-shadow: 0 0 8px var(--amber); transform: scale(1.2); }
.cs-code i.keyed { color: var(--green); }
.cs-code i.wrong { color: var(--red); }
.cs-char.playing { border-color: var(--brass2); }
.cs-char.next { border-color: var(--brass2); background: rgba(242,193,78,.12); }
.cs-char.next b { color: var(--brass2); }
.cs-char.done { opacity: .55; } .cs-char.done b { color: var(--green); }
.dlg-strip.long .cs-char b { font-size: var(--fs-lg); } .dlg-strip.long .cs-code { font-size: var(--fs-md); }
.dlg-strip.xlong .cs-char { min-width: 1.6em; padding: 2px 4px; } .dlg-strip.xlong .cs-char b { font-size: var(--fs-md); } .dlg-strip.xlong .cs-code { font-size: var(--fs-sm); }
.dlg-repeat { opacity: .85; }
.dlg.waiting .dlg-text { color: #9aa4b1; font-style: italic; }
body.training .scene-bg { filter: saturate(.6) brightness(.55); }
body.training .scene-props { opacity: .45; }
body.training .scene-props .prop:hover { opacity: 1; }

/* ============================================================================
   PHONE LAYER: portrait game canvas, full screen. Anything that is not a
   wide landscape screen (see the desktop query below) gets this. On a wide
   monitor the same portrait canvas would be letterboxed, which is exactly the
   "looks like mobile on desktop" problem the desktop layer fixes.
   ============================================================================ */
@media not all and (min-width: 1000px) and (min-aspect-ratio: 1/1) and (min-height: 620px) {
  /* Nothing on the stage goes below 14px: at the smallest scale (360x640, 0.82) that is still 11.5px on the glass. */
  :root { --fs-xs: 14px; }
  /* The stage: a fixed 390x780 portrait picture. app.js scales and centres it in the visible viewport
     (letterboxed), keeping the safe-area insets and the full-screen gesture bar clear.
     Budget top to bottom: header 56, room (and the chief's 240 over it), key deck about 205. */
  body { display: block; }
  #app { position: absolute; left: 0; top: 0; width: 390px; height: 780px; transform-origin: 0 0; }
  #top { gap: 8px; padding: 4px 8px; height: 56px; }
  .brand, .brand b, .brand small, .loc, .vol, #btn-sheet, #btn-music, .who-text span { display: none; }
  .who { gap: 8px; }
  .xp { margin-top: 1px; }
  .xp-bar { width: 90px; }
  .ctl { gap: 5px; }
  .ctl .btn.small { padding: 4px 6px; font-size: var(--fs-xs); letter-spacing: 0; }
  .tog { width: 32px; height: 32px; font-size: 16px; }
  .sheet { width: 100%; }
  .key-card { flex-direction: column; } .key-card .art { width: 100%; aspect-ratio: 16 / 7; }
  /* The receiver replaces the task panel. */
  body:not(.simple) #station { display: none; }
  /* The receiver must fit the stage without scrolling: compact head and switches, a 240px dial,
     band buttons as a row of chips, and no meters (decoration the phone has no room for). */
  #radio.drawer { padding: 8px 10px; overflow: hidden; }
  .drawer-head { margin-bottom: 4px; }
  #radio .rx-switches { margin: 2px 0 6px; gap: 4px; } #radio .sw { padding: 5px 4px; }
  #radio .dial-wrap { max-width: 240px; }
  #radio .dial-row { gap: 6px; } #radio .dial-side { gap: 5px; min-width: 0; }
  #radio .readout { font-size: var(--fs-md); padding: 3px 6px; letter-spacing: 1px; }
  #radio .bands { flex-direction: row; flex-wrap: wrap; gap: 4px; } #radio .band { padding: 3px 6px; font-size: var(--fs-xs); } #radio .band .band-name { display: none; }
  #radio .meters { display: none; }
  /* Key deck: the sending strip and the meter on one row, the key below at a fixed size; no keyboard hint (touch has no SPACE bar). */
  #keydeck { padding: 4px 8px 8px; gap: 4px; flex-direction: row; flex-wrap: wrap; align-items: center; }
  .key-wrap { width: 100%; }
  .key { width: 300px; }
  body:not(.simple) .key { width: 220px; }
  .keylabel, .keyhelp { display: none; }
  .deck-sheet { display: grid; place-items: center; }
  #btn-sound { display: inline-block; }
  .pad { flex: 1 1 52%; min-width: 0; padding: 3px 6px; gap: 6px; }
  .pad-label, #wpm { display: none; }
  .pad-text { font-size: var(--fs-lg); }
  .rhythm { flex: 1 1 40%; min-height: 34px; min-width: 0; } .rhythm-bars { height: 28px; } .rhythm-coach { display: none; }
  .prop .plate, .prop .plate small { font-size: 14px; letter-spacing: 0; } .prop .plate { padding: 2px 3px; }
  /* The main screen: bigger plates so every option reads at a glance. */
  .room-radio .prop .plate { font-size: 17px; }
  .scene-caption { font-size: var(--fs-xs); }
  /* While the chief is up the room is a backdrop: props at half size, no plates. */
  body.training .prop { width: calc(var(--w) * 0.52%); }
  body.training .prop .plate { display: none; }
  /* The chief's panel is a fixed 240 (420 while a copy is written on the pad, when the key deck steps aside);
     speeches are paged to about 90 characters (four lines) and the text scrolls as the backstop. */
  .dlg { min-height: 0; padding: 6px 8px; }
  .dlg:not(.hidden) { height: 240px; }
  body.copying .dlg:not(.hidden) { height: 420px; }
  body.copying #keydeck { display: none; }
  /* The distress watch is answered on its SOS button; the key steps aside. */
  body.watching #keydeck { display: none; }
  .dlg-text { flex: none; max-height: calc(1.35em * 4); overflow-y: auto; }
  .dlg-pad { grid-template-columns: repeat(auto-fill, 44px); gap: 6px; margin-top: 10px; } .dlg-pad button { width: 44px; height: 44px; }
  .dlg-body { overflow-y: auto; min-height: 0; }
  /* No buttons on the stage: tapping the chief repeats him, ROOM in the header sends him away, and the chief clears the strip himself. */
  .dlg-actions, #pad-clear { display: none; }
  .dlg-portrait { width: 64px; cursor: pointer; }
  .nodes { --amp: 52px; gap: 28px; }
  .ch-banner { padding: 6px 10px; gap: 8px; } .ch-banner .art { width: 96px; } .ch-text b { font-size: var(--fs-md); }
  .nb .lbl b, .nb .lbl small { max-width: 190px; }
  .badge { width: 42px; height: 42px; font-size: var(--fs-lg); }
  .loc-card { flex-direction: column; } .loc-card .art { width: 100%; }
  .net-top { grid-template-columns: 1fr; }
  .prop .art { border-width: 2px; }
  /* Overlays live on the stage, under the same transform. The gate stays fixed outside #app. */
  #app .modal, #app .sheet, #app #toast { position: absolute; }
  .modal-card { max-height: 92%; }
  body.can-full:not(.is-full) #btn-full { display: inline-block; }
  .gate { padding: max(8px, env(safe-area-inset-top, 0px)) max(8px, env(safe-area-inset-right, 0px)) max(8px, env(safe-area-inset-bottom, 0px)) max(8px, env(safe-area-inset-left, 0px)); } .gate-card { padding: 12px; } .gate-card h1 { font-size: var(--fs-xl); margin: 8px 0 2px; } .gate-card p { margin: 6px 0; }
}
/* Short phones (with the browser bars, most of them): smaller art, tighter menu, the save note dropped. */
@media (pointer: coarse) and (max-height: 760px) {
  .gate-art { height: min(120px, 17svh); }
  .gate-card h1 { margin-top: 6px; }
  .gate-card .sub { margin-bottom: 6px; }
  .gate-panel { gap: 7px; }
  .gate-menu .btn.big { padding: 9px 14px; }
  .gate-card p.tiny:not(#gate-tip) { display: none; }
  #gate-install { margin-top: 6px; }
}
#btn-full { display: none; color: var(--brass2); }

/* ============================================================================
   DESKTOP LAYER: wide landscape screens. The room scene sits on the left at
   16:9 (the room art's native shape); the console rail on the right stacks the
   instructor, the receiver drawer and the Morse key, so the key never moves.
   ============================================================================ */
@media (min-width: 1000px) and (min-aspect-ratio: 1/1) and (min-height: 620px) {
  :root { --fs-xs: 14px; --fs-sm: 15px; --fs-base: 17px; --fs-md: 19px; --fs-lg: 23px; --fs-xl: 30px; --fs-2xl: 40px; --cap-h: 40px; }
  #app { width: min(100vw, 1600px); }
  #main { display: grid; grid-template-columns: minmax(0, 1fr) clamp(360px, 33vw, 520px); grid-template-rows: auto minmax(0, 1fr) auto; gap: 10px; padding: 10px; }
  #station { grid-column: 1; grid-row: 1 / 4; border: 2px solid #3a332f; border-radius: 8px; box-shadow: inset 0 0 40px #000, 0 6px 18px #000; }
  #dialogue { grid-column: 2; grid-row: 1; border: 2px solid var(--brass); border-radius: 8px; min-height: 160px; }
  body.simple #dialogue { grid-row: 1 / 3; align-self: start; }
  #radio.drawer { grid-column: 1; grid-row: 1 / 4; border: 2px solid #3a332f; border-radius: 8px; padding: 20px 28px; }
  #radio .rx-switches { flex-direction: row; gap: 24px; margin: 8px 0 14px; } #radio .rx-row { flex: 1; } #radio .rx-row label { width: 3.5em; }
  /* The receiver opens over the room: a bakelite panel with a big dial, the switches and bands beside it. The rail keeps the chief and the key. */
  body:not(.simple) #station { display: none; }
  #keydeck { grid-column: 2; grid-row: 3; flex-direction: column; align-items: stretch; padding: 10px; gap: 10px; }
  .key { width: 100%; }
  .rhythm { min-height: 56px; } .rhythm-bars { height: 48px; }
  /* Scene: exactly 16:9, centred in the task panel with dark bars where needed. */
  #content:has(.scene) { container-type: size; display: grid; place-items: center; background: #0a0e14; }
  .scene { width: min(100cqw, calc(100cqh * 16 / 9)); max-width: 100%; height: auto; min-height: 0; aspect-ratio: 16 / 9; }
  /* Everything else is a readable centred column. */
  #content:not(:has(.scene)) { padding: 20px max(24px, calc((100% - 900px) / 2)) 28px; }
  #radio .dial-row { flex-direction: row; align-items: flex-start; gap: 24px; }
  #radio .dial-wrap { flex: none; width: min(56%, 58dvh); max-width: none; }
  #radio .dial-side { flex: 1; gap: 12px; }
  #radio .readout { font-size: var(--fs-2xl); }
  #radio .band { padding: 8px 10px; font-size: var(--fs-base); }
  #radio .meters { margin-top: 18px; }
  .dlg-portrait { width: 96px; }
  .dlg-goal { letter-spacing: 6px; }
  /* Simple view: the receiver stays folded away (the RECEIVER prop and the header open it). A tuner strip
     in the rail was tried on 2026-09-15 and reverted: at 1366x768 it wraps to two rows and the key deck
     then covers the chief's panel and the copy keypad. See BACKLOG "Desktop: the receiver rail". */
  body.simple #radio.drawer { display: none; }
  /* Plates scale with the panel so the narrow end of the desktop layer (1024x768) keeps two-line door labels inside the scene. */
  .room-radio .prop .plate { font-size: clamp(13px, 2cqw, 18px); }
}
@media (min-width: 1000px) and (min-aspect-ratio: 1/1) and (min-height: 620px) and (max-width: 1099px) { .loc { display: none; } }

/* ---- Motion: small, cheap, and all switched off under prefers-reduced-motion ---- */
/* Lists and cards rise in one after another. */
.list .item, .slips .slip, .steps li, .nodes .node, .mnemonics .mn, .loc-card, .st-line, .op { animation: rise .4s ease-out both; animation-delay: calc(var(--i, 0) * 45ms); }
.list > :nth-child(1), .slips > :nth-child(1), .steps > :nth-child(1), .nodes > :nth-child(1), .mnemonics > :nth-child(1) { --i: 0; }
.list > :nth-child(2), .slips > :nth-child(2), .steps > :nth-child(2), .nodes > :nth-child(2), .mnemonics > :nth-child(2) { --i: 1; }
.list > :nth-child(3), .slips > :nth-child(3), .steps > :nth-child(3), .nodes > :nth-child(3), .mnemonics > :nth-child(3) { --i: 2; }
.list > :nth-child(4), .slips > :nth-child(4), .steps > :nth-child(4), .nodes > :nth-child(4), .mnemonics > :nth-child(4) { --i: 3; }
.list > :nth-child(5), .slips > :nth-child(5), .steps > :nth-child(5), .nodes > :nth-child(5), .mnemonics > :nth-child(5) { --i: 4; }
.list > :nth-child(6), .slips > :nth-child(6), .steps > :nth-child(6), .nodes > :nth-child(6), .mnemonics > :nth-child(6) { --i: 5; }
.list > :nth-child(7), .slips > :nth-child(7), .steps > :nth-child(7), .nodes > :nth-child(7), .mnemonics > :nth-child(7) { --i: 6; }
.list > :nth-child(8), .slips > :nth-child(8), .steps > :nth-child(8), .nodes > :nth-child(8), .mnemonics > :nth-child(8) { --i: 7; }
.list > :nth-child(n+9), .slips > :nth-child(n+9), .steps > :nth-child(n+9), .nodes > :nth-child(n+9), .mnemonics > :nth-child(n+9) { --i: 8; }
.st-line, .op { animation-duration: .25s; animation-delay: 0s; }
/* Props settle into the room: the framed art drops in with a little bounce; the button itself keeps its parallax transform. */
.prop .art, .prop .plate { animation: propsettle .5s cubic-bezier(.2, 1.2, .3, 1) both; animation-delay: calc(var(--i, 0) * 40ms); }
@keyframes propsettle { from { opacity: 0; transform: translateY(-14px) scale(.96); } to { opacity: 1; transform: none; } }
.prop .plate { animation-name: rise; animation-duration: .35s; animation-delay: calc(var(--i, 0) * 40ms + 120ms); }
/* Drifting dust in the lamp light. */
.scene-dust { position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: .55; mix-blend-mode: screen;
  background-image: radial-gradient(1.5px 1.5px at 20% 30%, rgba(255,230,170,.9), transparent 60%), radial-gradient(1px 1px at 60% 20%, rgba(255,230,170,.8), transparent 60%), radial-gradient(1.5px 1.5px at 80% 60%, rgba(255,230,170,.7), transparent 60%), radial-gradient(1px 1px at 35% 70%, rgba(255,230,170,.8), transparent 60%), radial-gradient(1px 1px at 50% 45%, rgba(255,230,170,.6), transparent 60%);
  background-size: 220px 180px, 300px 260px, 260px 220px, 340px 300px, 200px 240px; animation: dust 28s linear infinite; }
@keyframes dust { from { background-position: 0 0, 0 0, 0 0, 0 0, 0 0; } to { background-position: -120px 180px, 90px 260px, -80px 220px, 60px 300px, -40px 240px; } }
/* The chief: portrait breathes while he speaks, a caret blinks at the end of the line. */
.dlg.speaking .dlg-portrait { animation: speak .5s ease-in-out infinite alternate; box-shadow: 0 0 14px rgba(242,193,78,.45); }
@keyframes speak { from { transform: translateY(0) scale(1); } to { transform: translateY(-1.5px) scale(1.015); } }
.dlg.speaking .dlg-text::after { content: "▌"; color: var(--brass2); animation: tapblink .6s infinite; margin-left: 1px; }
.dlg-goal span.next { animation: goalpulse 1.1s ease-in-out infinite; display: inline-block; }
@keyframes goalpulse { 50% { transform: translateY(-2px); text-shadow: 0 0 10px rgba(242,193,78,.7); } }
.dlg-goal span.done { animation: goaldone .3s ease-out; display: inline-block; }
@keyframes goaldone { 40% { transform: scale(1.25); } }
/* Decoded characters pop onto the pad; the element buffer ticks. */
.pad-text .pop { display: inline-block; animation: pop .25s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes pop { from { transform: scale(1.6) translateY(-4px); color: var(--red); } to { transform: none; } }
.elements:not(:empty) { animation: tick .12s ease-out; }
@keyframes tick { from { transform: translateX(2px); } to { transform: none; } }
/* XP bar shimmer and header glints. */
#xpbar { position: relative; overflow: hidden; }
#xpbar::after { content: ""; position: absolute; top: 0; bottom: 0; width: 40%; left: -40%; background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent); animation: shimmer 3.2s ease-in-out infinite; }
@keyframes shimmer { 0%, 60% { left: -40%; } 100% { left: 110%; } }
/* Buttons press, modals pop, the gate breathes. */
.btn:active:not(:disabled) { transform: translateY(1px); filter: brightness(1.1); }
.btn.gold:not(:disabled) { background-size: 200% 100%; }
.modal:not(.hidden) .modal-card { animation: popin .28s cubic-bezier(.2, 1.3, .4, 1) both; }
@keyframes popin { from { opacity: 0; transform: translateY(18px) scale(.96); } to { opacity: 1; transform: none; } }
.gate-art { animation: kenburns 24s ease-in-out infinite alternate; background-size: 110% auto !important; }
@keyframes kenburns { from { background-position: 30% 40%; } to { background-position: 70% 60%; } }
.gate .btn.primary:not(:disabled) { animation: startpulse 1.6s ease-in-out infinite; }
@keyframes startpulse { 50% { box-shadow: 0 0 22px 4px rgba(242,193,78,.55); transform: scale(1.03); } }
/* Receiver: the lock lamp pulses when on a band; needles already move with the audio level. */
.lamp.lit { animation: lamppulse 1.8s ease-in-out infinite; }
@keyframes lamppulse { 50% { box-shadow: 0 0 4px var(--green); } }
/* Net: a dot pings when that operator keys. */
.dot.tx::after { content: ""; position: absolute; inset: -3px; border-radius: 50%; border: 2px solid var(--red); animation: ping .6s ease-out infinite; }
@keyframes ping { from { transform: scale(.6); opacity: 1; } to { transform: scale(2.2); opacity: 0; } }
.inbox .slip.fresh .slip-new { animation: stampin .5s cubic-bezier(.2, 1.4, .4, 1) both; animation-delay: .4s; }
@media (prefers-reduced-motion: reduce) {
  .list .item, .slips .slip, .steps li, .nodes .node, .mnemonics .mn, .loc-card, .st-line, .op, .prop .art, .prop .plate, .scene-dust, .dlg.speaking .dlg-portrait, .dlg-goal span.next, .dlg-goal span.done, .pad-text .pop, .elements:not(:empty), #xpbar::after, .modal:not(.hidden) .modal-card, .gate-art, .gate .btn.primary:not(:disabled), .lamp.lit, .dot.tx::after, .inbox .slip.fresh .slip-new { animation: none; }
}
