/*
 * pano-player.css — styling for the shared pseudo-360 room player (pano-player.js).
 * Chapter-agnostic; a chapter shell links this + the Pannellum CSS.
 */
:root { --bg:#06111a; --panel:#0b1b28; --line:rgba(255,255,255,.14); --amber:#ffd88c; --ink:#e8eef2; }
* { box-sizing:border-box; }
html, body { margin:0; height:100%; background:var(--bg); color:var(--ink); font:15px/1.55 system-ui, sans-serif; overflow:hidden; }
.screen { position:fixed; inset:0; display:none; }
.screen.active { display:block; }

/* screen 1 */
#screen1 { display:none; overflow:auto; }
#screen1.active { display:flex; align-items:center; justify-content:center; }
.intro { max-width:720px; padding:32px; position:relative; z-index:1; display:flex; gap:26px; align-items:center; flex-wrap:wrap; }
.intro .introtext { flex:1; min-width:260px; }
/* landing-screen cover thumbnail (scenario.cover), to the LEFT of the text; hidden until src set in JS */
#s1cover { display:none; width:180px; flex:none; aspect-ratio:1/1; object-fit:cover; border-radius:14px; margin:0; box-shadow:0 8px 30px rgba(0,0,0,.5); border:1px solid var(--line); }
.intro h1 { margin:0; font-size:26px; color:var(--amber); }
.intro .sub { opacity:.65; margin:4px 0 18px; font-size:13px; letter-spacing:.04em; text-transform:uppercase; }
.intro p { opacity:.9; }
.intro .x500 { margin-top:20px; display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.intro input { background:#0a1620; color:inherit; border:1px solid var(--line); border-radius:8px; padding:9px 11px; font:inherit; }
button { background:var(--amber); color:#10202a; border:0; border-radius:8px; padding:9px 15px; font:inherit; font-weight:600; cursor:pointer; }
button:disabled { opacity:.5; cursor:default; }
button.ghost { background:transparent; color:var(--ink); border:1px solid var(--line); font-weight:400; }

/* inter-room "loading" card — landing-screen look, no x500 gate; fades in over the room */
#loading { position:fixed; inset:0; z-index:30; display:flex; align-items:center; justify-content:center;
  background:var(--bg); opacity:0; visibility:hidden; transition:opacity .35s ease, visibility .35s ease; }
#loading.open { opacity:1; visibility:visible; }
#loading .loadcard { max-width:560px; padding:32px; position:relative; z-index:1; }

/* ambient particles behind the card on the entry screens (scenario.ambient: fireflies | snow | none) */
.particles { position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0; }
/* fireflies — warm, drifting, pulsing */
.firefly { position:absolute; border-radius:50%;
  background:radial-gradient(circle, rgba(255,244,190,.95) 0%, rgba(255,216,140,.55) 45%, rgba(255,216,140,0) 72%);
  box-shadow:0 0 9px 2px rgba(255,216,140,.42); will-change:transform, opacity; }
@keyframes ffPulse { 0%,100%{ opacity:.10; } 50%{ opacity:.95; } }
@keyframes ffFloat  { 0%{transform:translate(0,0)} 33%{transform:translate(14px,-20px)} 66%{transform:translate(-10px,-34px)} 100%{transform:translate(0,0)} }
@keyframes ffFloat2 { 0%{transform:translate(0,0)} 33%{transform:translate(-18px,-14px)} 66%{transform:translate(8px,-28px)} 100%{transform:translate(0,0)} }
@keyframes ffFloat3 { 0%{transform:translate(0,0)} 33%{transform:translate(10px,15px)} 66%{transform:translate(20px,-12px)} 100%{transform:translate(0,0)} }
/* snow — pale flakes falling with a gentle horizontal sway (one transform anim = fall + drift) */
.snowflake { position:absolute; top:0; border-radius:50%;
  background:radial-gradient(circle, rgba(255,255,255,.95) 0%, rgba(222,236,255,.7) 55%, rgba(222,236,255,0) 75%);
  box-shadow:0 0 4px 1px rgba(255,255,255,.35); will-change:transform; }
@keyframes snowFall  { 0%{transform:translate(0,-8vh)} 25%{transform:translate(12px,22vh)} 50%{transform:translate(-6px,52vh)} 75%{transform:translate(10px,82vh)} 100%{transform:translate(0,112vh)} }
@keyframes snowFall2 { 0%{transform:translate(0,-8vh)} 25%{transform:translate(-14px,26vh)} 50%{transform:translate(8px,56vh)} 75%{transform:translate(-10px,84vh)} 100%{transform:translate(2px,112vh)} }
@keyframes snowFall3 { 0%{transform:translate(0,-8vh)} 33%{transform:translate(9px,34vh)} 66%{transform:translate(16px,70vh)} 100%{transform:translate(4px,112vh)} }
@media (prefers-reduced-motion: reduce){ .firefly, .snowflake { animation:none !important; opacity:.4; } }
/* embers — warm rising sparks (reuses ffFloat/ffPulse; airship / fire ambience) */
.ember { position:absolute; border-radius:50%;
  background:radial-gradient(circle, rgba(255,214,150,.95) 0%, rgba(255,150,70,.6) 45%, rgba(255,120,50,0) 72%);
  box-shadow:0 0 8px 2px rgba(255,140,60,.45); will-change:transform, opacity; }
@media (prefers-reduced-motion: reduce){ .ember { animation:none !important; opacity:.4; } }
/* leaves — green leaves blowing across on the wind, tumbling as they go (single transform anim = drift + spin) */
.leaf { position:absolute; top:0; border-radius:0 100% 0 100%;
  background:radial-gradient(circle at 32% 30%, rgba(168,214,120,.95) 0%, rgba(104,176,74,.9) 52%, rgba(64,132,52,.7) 100%);
  box-shadow:0 0 3px 1px rgba(70,140,50,.28); will-change:transform; }
@keyframes leafBlow  { 0%{transform:translate(-16vw,-8vh) rotate(0deg)} 33%{transform:translate(22vw,16vh) rotate(150deg)} 66%{transform:translate(56vw,32vh) rotate(255deg)} 100%{transform:translate(98vw,58vh) rotate(400deg)} }
@keyframes leafBlow2 { 0%{transform:translate(-18vw,-4vh) rotate(0deg)} 33%{transform:translate(18vw,28vh) rotate(-120deg)} 66%{transform:translate(52vw,40vh) rotate(-230deg)} 100%{transform:translate(96vw,78vh) rotate(-350deg)} }
@keyframes leafBlow3 { 0%{transform:translate(-14vw,-10vh) rotate(0deg)} 33%{transform:translate(26vw,10vh) rotate(180deg)} 66%{transform:translate(60vw,44vh) rotate(300deg)} 100%{transform:translate(100vw,66vh) rotate(470deg)} }
@media (prefers-reduced-motion: reduce){ .leaf { animation:none !important; opacity:.4; } }

/* horizon heel — a slow nautical roll of the whole view (opt-in: scenario.heel). The roll oscillates
   ±1.3° around a BASE list angle `--heel-base` (default 0). For a PROGRESSIVE heel the player script
   grows --heel-base with world-state (e.g. a ship listing further as puzzles are solved). */
@keyframes heelRoll { 0%,100%{ transform:rotate(calc(var(--heel-base,0deg) - 1.3deg)) scale(1.045); } 50%{ transform:rotate(calc(var(--heel-base,0deg) + 1.3deg)) scale(1.045); } }
#pano.heel { animation:heelRoll 15s ease-in-out infinite; transform-origin:50% 55%; }
@media (prefers-reduced-motion: reduce){ #pano.heel { animation:none !important; transform:none; } }

/* sickness dim — a VIGNETTE that deepens (edges close in, tunnel-vision) as an infection worsens and
   clears at the cure (opt-in: scenario.sickness). Opacity is driven by pano-player.js updateEnvironment();
   a slow opacity fade, not vestibular motion, so it stays under prefers-reduced-motion. The gradient keeps
   the centre (where the puzzle is) readable while the edges darken. */
#sickness { position:absolute; inset:0; pointer-events:none; z-index:7; opacity:0; transition:opacity 1.4s ease;
  background:radial-gradient(128% 100% at 50% 46%, rgba(0,0,0,0) 32%, rgba(6,10,16,.6) 76%, rgba(2,4,8,.92) 100%); }

/* story-motif HUD — a small in-room emblem that tracks the narrative and RESOLVES at the boss (opt-in:
   scenario.hud={kind}). kind "infection" = a lesion that spreads then fades; "vitals" = a heartbeat that
   is weak+erratic then steadies+strengthens at the cure. Generalise with more kinds. */
#motifHud { position:absolute; top:12px; right:14px; z-index:19; width:42px; height:42px; pointer-events:none;
  display:none; transition:opacity 1.4s ease, transform 1.1s ease; }
#motifHud .lesion { position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 55% 45%, rgba(150,40,90,.95) 0%, rgba(90,20,70,.7) 45%, rgba(60,15,60,0) 72%);
  box-shadow:0 0 10px 3px rgba(150,40,110,.5); animation:lesionPulse 2.6s ease-in-out infinite; }
@keyframes lesionPulse { 0%,100%{ opacity:.55; } 50%{ opacity:1; } }
.motif-infection.resolved { opacity:0 !important; transform:scale(.3) !important; }   /* lesion heals away */
#motifHud .vitals { position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle, rgba(120,230,150,.9) 0%, rgba(60,180,110,.5) 45%, rgba(60,180,110,0) 72%);
  box-shadow:0 0 10px 3px rgba(90,220,140,.4); animation:vitalsBeat 2s ease-in-out infinite; }
@keyframes vitalsBeat { 0%,100%{ opacity:.35; transform:scale(.9); } 12%{ opacity:1; transform:scale(1.08); }
  26%{ opacity:.4; transform:scale(.92); } 40%{ opacity:.7; transform:scale(1); } }
.motif-vitals.resolved .vitals { animation-duration:1.15s; box-shadow:0 0 15px 5px rgba(90,240,150,.6); } /* steadies + strengthens */
@media (prefers-reduced-motion: reduce){ #motifHud .lesion, #motifHud .vitals { animation:none !important; } }

/* fx — a small library of setting-matched environment overlays over the room (opt-in: scenario.fx / room.fx,
   an array). Deliberately gentle, never a horror strobe. */
#fxLayer { position:absolute; inset:0; pointer-events:none; z-index:6; overflow:hidden; }
#fxLayer > div { position:absolute; inset:0; }
/* "flicker" — a warm fluorescent luminance BREATHING (cozy night hum, not a scary strobe) */
.fx-flicker { background:radial-gradient(120% 90% at 50% 0%, rgba(255,240,210,.06), rgba(255,240,210,0) 70%);
  animation:fxBreathe 7.5s ease-in-out infinite; }
@keyframes fxBreathe { 0%,100%{ opacity:.35; } 50%{ opacity:1; } 82%{ opacity:.72; } 88%{ opacity:.9; } }
/* "frost" — cold creeping in at the edges (Alaska); atmospheric, not creepy */
.fx-frost { box-shadow:inset 0 0 120px 40px rgba(200,225,255,.14), inset 0 0 44px 12px rgba(220,240,255,.1);
  animation:fxFrost 9s ease-in-out infinite; }
@keyframes fxFrost { 0%,100%{ opacity:.7; } 50%{ opacity:1; } }
@media (prefers-reduced-motion: reduce){ .fx-flicker, .fx-frost { animation:none !important; } }

/* tactile dial — a brass gauge face with a needle that swings to the chosen sigil */
.dialface { position:relative; width:150px; height:88px; margin:4px auto 12px;
  border-radius:150px 150px 10px 10px / 88px 88px 10px 10px;
  background:radial-gradient(120% 150% at 50% 100%, #3a2c14 0%, #241a0c 72%); border:2px solid #8a6a2a;
  box-shadow:inset 0 4px 14px rgba(0,0,0,.6); }
.dialface .needle { position:absolute; left:50%; bottom:8px; width:3px; height:66px;
  background:linear-gradient(#ffd88c,#a8791f); transform-origin:50% 100%;
  transform:translateX(-50%) rotate(0deg); transition:transform .5s cubic-bezier(.5,1.4,.5,1); border-radius:2px; }
.dialface .hub { position:absolute; left:50%; bottom:4px; width:10px; height:10px; margin-left:-5px; border-radius:50%; background:#c99a3a; }
@media (prefers-reduced-motion: reduce){ .dialface .needle { transition:none !important; } }
#loading #loadTitle { margin:0 0 10px; font-size:24px; color:var(--amber); }
#loading #loadText { opacity:.9; white-space:pre-wrap; margin:0; }
#loading #loadBtn { margin-top:20px; }

/* screen 2 — the room. Dark bg + fade so swapping the panorama (solve / enter room) never flashes
   white in the frame before the new scene paints; it fades up from black instead. */
#pano { position:fixed; inset:0; background:var(--bg); opacity:1; transition:opacity .35s ease; }
.pnlm-container, .pnlm-render-container { background:var(--bg) !important; }
.pnlm-controls-container, .pnlm-load-box { display:none !important; }
#hud { position:fixed; left:50%; top:12px; transform:translateX(-50%); z-index:6; background:rgba(9,18,26,.82); border:1px solid var(--line); border-radius:999px; padding:6px 15px; font-size:12px; pointer-events:none; }
#hud b { color:var(--amber); }

/* pannellum hotspot markers */
.hsmark { width:30px; height:30px; margin:-15px 0 0 -15px; border-radius:50%; cursor:pointer;
  background:rgba(255,216,140,.28); border:2px solid var(--amber);
  box-shadow:0 0 0 4px rgba(255,216,140,.12), 0 0 16px rgba(255,216,140,.6);
  animation:pulse 1.8s ease-in-out infinite; }
.hsmark.clue { background:rgba(150,200,255,.28); border-color:#9fd1ff; box-shadow:0 0 0 4px rgba(150,200,255,.12),0 0 16px rgba(150,200,255,.55); }
.hsmark.door.locked { background:rgba(180,180,180,.18); border-color:rgba(200,200,200,.5); box-shadow:none; animation:none; }
.hsmark.door.open { background:rgba(120,255,170,.30); border-color:#7dffaa; box-shadow:0 0 0 4px rgba(120,255,170,.14),0 0 20px rgba(120,255,170,.7); }
.hsmark.puzzle.done { opacity:.5; box-shadow:none; animation:none; }

/* --- Three-phase portals (SCENARIO.stonePortals) --- */
/* Awakened forward-door marker: shift the locked-grey dot to a starry blue. */
.hsmark.door.awakened { background:rgba(120,160,255,.30); border-color:#9fb8ff;
  box-shadow:0 0 0 4px rgba(120,160,255,.14),0 0 18px rgba(120,160,255,.7); animation:none; }
/* Dim starfield that kindles inside the arch when awakened (analysis solved, keypad pending). */
.portalglow { width:150px; height:150px; margin:-75px 0 0 -75px; border-radius:50%; pointer-events:none;
  background:
    radial-gradient(circle at 30% 38%, rgba(190,215,255,.95) 0 1px, transparent 2px),
    radial-gradient(circle at 63% 30%, rgba(205,225,255,.85) 0 1px, transparent 2px),
    radial-gradient(circle at 50% 58%, rgba(170,200,255,.95) 0 1.3px, transparent 2px),
    radial-gradient(circle at 40% 74%, rgba(215,230,255,.8) 0 1px, transparent 2px),
    radial-gradient(circle at 72% 66%, rgba(195,215,255,.85) 0 1px, transparent 2px),
    radial-gradient(circle at 24% 60%, rgba(205,225,255,.75) 0 1px, transparent 2px),
    radial-gradient(circle at 58% 44%, rgba(220,235,255,.7) 0 1px, transparent 2px),
    radial-gradient(ellipse at center, rgba(45,75,140,.5), rgba(12,22,48,.18) 58%, transparent 74%);
  animation:portalTwinkle 3.6s ease-in-out infinite; }
@keyframes portalTwinkle { 0%,100% { opacity:.5 } 50% { opacity:.95 } }

/* --- Stone keypad (lock mode:"stones") --- */
.stonelock .slots { display:flex; justify-content:center; gap:8px; flex-wrap:wrap; margin:6px 0 12px; }
.stonelock .slot { width:20px; height:34px; border-radius:5px; border:1px solid rgba(255,216,140,.35);
  background:rgba(0,0,0,.32); position:relative; }
.stonelock .slot.s-stand::after { content:""; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:8px; height:26px; border-radius:4px; background:#e7d7b0; }
.stonelock .slot.s-fall::after { content:""; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:26px; height:8px; border-radius:4px; background:#8fa0b4; }
.stonelock .keys { display:flex; justify-content:center; gap:10px; margin-top:4px; }
.stonelock .key { width:58px; height:54px; border-radius:10px; border:1px solid rgba(255,216,140,.4);
  background:rgba(0,0,0,.35); color:#ffd88c; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; font-size:20px; }
.stonelock .key:hover { background:rgba(255,216,140,.14); }
.stonelock .key:disabled { opacity:.4; cursor:default; }
.stonelock .stone { display:inline-block; border-radius:5px; }
.stonelock .stone.stand { width:10px; height:30px; background:#e7d7b0; }
.stonelock .stone.fall { width:30px; height:10px; background:#8fa0b4; }
/* NB: never animate `transform` here — Pannellum positions each hotspot via
   transform: translate(), and a transform animation overrides that, snapping the
   marker to the corner. Pulse opacity only. */
@keyframes pulse { 0%,100%{ opacity:.72; } 50%{ opacity:1; } }

/* modal */
/* flex-start + overflow so the modal can grow taller than the viewport (for big plots) and the
   OVERLAY scrolls — centring here would clip the top of an over-tall box beyond reach */
#modal { position:fixed; inset:0; z-index:20; display:none; align-items:flex-start; justify-content:center; background:rgba(3,8,13,.72); padding:12px; overflow-y:auto; }
#modal.open { display:flex; }
/* near-full-screen; no max-height so the box grows with a large plot instead of clipping it */
.mbox { position:relative; background:var(--panel); border:1px solid var(--line); border-radius:14px; width:97vw; max-width:1700px; overflow:visible; padding:16px 18px; }
.mbox .mclose { position:absolute; top:8px; right:10px; z-index:2; background:transparent; border:0; color:var(--ink); opacity:.55; font-size:20px; line-height:1; cursor:pointer; padding:4px 8px; }
.mbox .mclose:hover { opacity:1; }
/* test-play only — the "Solve" shortcut, tucked just left of the ✕ close (see isTestPlay in the player) */
.mbox #devSolveBtn { position:absolute; top:8px; right:44px; z-index:2; font-size:12px; padding:5px 10px; opacity:.8; }
.mbox #devSolveBtn:hover { opacity:1; }
/* Modal titles are suppressed in the player + test player (2026-07-28, Lucas): the header text is
   hidden but the strip is kept (min-height) so the ✕ close / ⏭ Solve buttons never overlap the body.
   openModal still writes #mtitle.textContent (harmless) — only the display is turned off here, so
   restoring titles is a one-line CSS revert. */
.mtitle { visibility:hidden; min-height:20px; margin-bottom:8px; }
/* weight the split toward the console (left); MC/question pane (right) is narrow (~2/3 of a 3:2 split) */
.qa { display:grid; grid-template-columns:minmax(0,11fr) minmax(0,4fr); gap:16px; }
@media (max-width:760px){ .qa { grid-template-columns:1fr; } }

/* webr console block */
#console-holder { display:none; }
.console { display:flex; flex-direction:column; gap:8px; }
#webr-status { font-size:12px; opacity:.8; min-height:16px; }
#code-input { width:100%; min-height:46vh; background:#08131c; color:#e8eef2; border:1px solid var(--line); border-radius:8px; padding:10px; font:13px/1.45 ui-monospace,Menlo,Consolas,monospace; resize:vertical; }
/* no max-height: a plot renders at full height and you scroll the modal, not this little pane */
#webr-output { background:#08131c; border:1px solid var(--line); border-radius:8px; padding:8px; min-height:60px; }
.webr-out { margin:0; font:12px/1.4 ui-monospace,Menlo,Consolas,monospace; white-space:pre-wrap; color:#cfe6d8; }
.webr-out.err { color:#ff9b9b; } .webr-out.muted { opacity:.5; }
.webr-plot { max-width:100%; border-radius:6px; margin-top:6px; }

/* question card */
.qcard .qprompt { margin-bottom:10px; }
.qopt { display:block; padding:8px 10px; border:1px solid var(--line); border-radius:8px; margin:6px 0; cursor:pointer; }
.qopt:hover { border-color:var(--amber); }
.qopt input { margin-right:8px; }
.qfeedback { font-size:13px; margin:8px 0; min-height:18px; }
.qfeedback.ok { color:#7dffaa; } .qfeedback.no { color:#ffcf8c; } .qfeedback.out { color:#ff9b9b; }
.attempts { opacity:.6; font-size:12px; }
code { background:#0a1620; padding:1px 5px; border-radius:4px; }

/* grid-select (matrix selector, mechanic #15) — the data-free grouping escape */
.gridsel { border-collapse:separate; border-spacing:6px; margin:6px auto 4px; }
.gridsel th { font-size:12px; font-weight:600; color:var(--amber); padding:2px 6px; text-align:center; }
.gridsel th.rowlab { text-align:right; white-space:nowrap; color:#e8eef2; }
.gridsel .cell { width:30px; height:30px; border-radius:7px; border:1px solid rgba(255,216,140,.4); background:rgba(0,0,0,.3); cursor:pointer; }
.gridsel .cell:hover { border-color:var(--amber); }
.gridsel .cell.on { background:var(--amber); border-color:var(--amber); box-shadow:0 0 8px rgba(255,216,140,.6); }

/* look-around arrows (no dragging; side-to-side only) */
.arrow { position:fixed; top:50%; transform:translateY(-50%); z-index:6; width:3.2rem; height:3.2rem; padding:0; border-radius:50%; border:0; cursor:pointer; font-size:1.8rem; line-height:1; color:#10202a; background:rgba(240,245,250,.92); box-shadow:0 2px 14px rgba(0,0,0,.5); }
.arrow:hover { background:#fff; }
.arrow.l { left:1.2rem; } .arrow.r { right:1.2rem; }
#toast { position:fixed; left:50%; bottom:80px; transform:translateX(-50%); z-index:8; background:rgba(9,18,26,.94); border:1px solid var(--line); border-radius:10px; padding:8px 14px; font-size:13px; opacity:0; transition:opacity .25s; pointer-events:none; }
#toast.show { opacity:1; }
#done { position:fixed; inset:0; z-index:20; display:none; align-items:center; justify-content:center; background:rgba(3,8,13,.8); }
#done.open { display:flex; }
#done .card { background:var(--panel); border:1px solid var(--line); border-radius:14px; padding:26px 28px; max-width:460px; text-align:center; }
#done h2 { color:var(--amber); margin:0 0 10px; }

/* exit debrief — the standalone, opt-in "how this world worked" screen (scenario.debrief). Its own
   overlay above #done (z-index 21, so it opens on top of the escape-done card), scrollable and
   left-aligned for a longer read; not the code/finish card. */
#debrief { position:fixed; inset:0; z-index:21; display:none; align-items:flex-start; justify-content:center; background:rgba(3,8,13,.86); padding:24px 12px; overflow-y:auto; }
#debrief.open { display:flex; }
#debrief .card { background:var(--panel); border:1px solid var(--line); border-radius:14px; padding:26px 28px; max-width:620px; text-align:left; }
#debrief h2 { color:var(--amber); margin:0 0 12px; }
#debriefBody p { margin:0 0 12px; }
.dbsec { margin:0 0 14px; }
.dbsec h3 { color:var(--amber); font-size:15px; margin:0 0 4px; }
#debrief #debriefClose { margin-top:6px; }
/* room-by-room gallery: each built room's scene (a 2:1 panorama thumbnail) with its text alongside. */
.dbintro { margin:0 0 18px; opacity:.92; }
.dbroom { display:flex; gap:14px; align-items:flex-start; margin:0 0 18px; padding:0 0 16px; border-bottom:1px solid var(--line); }
.dbroom:last-child { border-bottom:0; padding-bottom:0; }
.dbshot { width:240px; flex:none; aspect-ratio:2/1; object-fit:cover; border-radius:8px; border:1px solid var(--line); background:#0a1620; }
.dbtext { flex:1; min-width:0; }
.dbtext h3 { color:var(--amber); font-size:15px; margin:0 0 4px; }
.dbpuzzle { font-size:13px; opacity:.62; font-style:italic; margin:0 0 6px; }
.dbtext p { margin:0 0 8px; }
@media (max-width:560px) { .dbroom { flex-direction:column; } .dbshot { width:100%; } }

/* pick-a-point-on-the-plot puzzle (escape map). Transparent clickable hit-boxes overlaid on the chart
   image at each point's fraction coords; hover shows an amber ring, the correct pick a green one. */
.mapcard { max-width:760px; }
.mapinstr { font-size:13px; opacity:.82; margin:0 0 10px; }
.mapholder { position:relative; width:100%; }
.mapimg { width:100%; display:block; border-radius:8px; border:1px solid var(--line); }
.mappts { position:absolute; inset:0; }
.mappt { position:absolute; padding:0; margin:0; background:transparent; border:1px solid transparent; border-radius:50%; cursor:pointer; transition:box-shadow .12s, background .12s; }
.mappt:hover { border-color:rgba(255,216,140,.85); background:rgba(255,216,140,.14); box-shadow:0 0 10px rgba(255,216,140,.5); }
.mappt.hit { border-color:#7dffaa; background:rgba(120,255,170,.22); box-shadow:0 0 0 3px rgba(120,255,170,.35),0 0 16px rgba(120,255,170,.7); }

/* field-notebook collage board — a draggable snap-grid of collected image fragments (132px cells; keep
   NB_CELL in pano-player.js in sync). Tiles are 120px, centred in each cell by a 6px margin. */
#nbBoard { position:relative; margin:0 auto; border-radius:8px;
  background:
    repeating-linear-gradient(0deg,  transparent 0 131px, rgba(255,255,255,.06) 131px 132px),
    repeating-linear-gradient(90deg, transparent 0 131px, rgba(255,255,255,.06) 131px 132px),
    rgba(0,0,0,.18); }
.nbtile { position:absolute; width:120px; height:120px; margin:6px; object-fit:cover; border-radius:8px;
  border:1px solid var(--line); box-shadow:0 3px 10px rgba(0,0,0,.4); background:#08131c;
  cursor:grab; user-select:none; -webkit-user-drag:none; touch-action:none; }
.nbtile.dragging { cursor:grabbing; box-shadow:0 8px 22px rgba(0,0,0,.55); }
.nbtile.overlay { opacity:.82; background:transparent; box-shadow:none; }
  /* mask-overlay puzzles (Alaska): coloured-glass filters, opaque enough that a single stacked filter
     clearly covers a cell — the one cell left as a clear window through ALL of them is the answer (see
     escape_grids/). background MUST be transparent (not the tile's default dark fill) or the clear
     windows show the tile's own background instead of the board stacked beneath; box-shadow off so a
     mostly-transparent sheet doesn't cast a frame. */

/* test-play inline editing — active only when .tpedit is applied (the test player only; a real student
   run never adds the class, so this is invisible in normal play). */
.tpedit { cursor: text; border-radius: 4px; outline: 1px dashed rgba(255,216,140,.45); outline-offset: 3px;
          transition: outline-color .15s, background .15s; }
.tpedit:hover { outline-color: rgba(255,216,140,.85); }
.tpedit:focus { outline: 1px solid #ffd88c; background: rgba(255,216,140,.07); }
.tpedit.tpsaving { outline-color: #79b8ff; }
.tpedit.tpok { outline-color: #8fe6a8; }
.tpedit.tperr { outline-color: #ff9a9a; }

/* close-X on the full-screen finish + submission cards (mirrors the modal's .mclose) */
#done .card, #submitPrep .subcard { position: relative; }
#done .card .mclose, #submitPrep .subcard .mclose { position: absolute; top: 8px; right: 10px; z-index: 2;
  background: transparent; border: 0; color: var(--ink); opacity: .55; font-size: 20px; line-height: 1; cursor: pointer; }
#done .card .mclose:hover, #submitPrep .subcard .mclose:hover { opacity: 1; }

/* submission-preparation screen — a landing-style full-screen with a scrollable card */
#submitPrep { position: fixed; inset: 0; z-index: 25; display: none; align-items: center; justify-content: center;
  background: rgba(3,8,13,.86); overflow: auto; }
#submitPrep.open { display: flex; }
#submitPrep .subintro { position: relative; z-index: 1; padding: 28px; width: 100%; max-width: 1000px; display: flex; justify-content: center; }
#submitPrep .subcard { position: relative; background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 26px 28px; width: 100%; max-width: 960px; max-height: 88vh; overflow: auto; text-align: left; }
#submitPrep .subcard h2 { color: var(--amber); margin: 0 0 12px; text-align: center; }
/* two-tab header inside the submission card: Prepare submission | How this world worked */
#submitPrep .subtabs { display: flex; gap: 6px; margin: 2px 34px 16px 0; border-bottom: 1px solid var(--line); }
#submitPrep .subtab { background: transparent; color: var(--ink); border: 0; border-bottom: 2px solid transparent;
  border-radius: 0; font: inherit; font-weight: 600; opacity: .6; padding: 6px 10px; cursor: pointer; }
#submitPrep .subtab.on { color: var(--amber); border-bottom-color: var(--amber); opacity: 1; }
#submitPrep .subtab:hover { opacity: 1; }
#submitPrep .lbl { font-size: 13px; opacity: .75; margin-bottom: 6px; }
#submitPrep .idrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
#submitPrep #subX500 { background: #0a1620; color: inherit; border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 11px; font: inherit; flex: 1; min-width: 180px; }
#subCodeVal { font: 700 20px/1.3 ui-monospace, Menlo, Consolas, monospace; letter-spacing: 1.5px; color: #ffd88c;
  background: rgba(0,0,0,.35); padding: 10px 14px; border-radius: 8px; user-select: all; word-break: break-all; }
#submitPrep .subactions { display: flex; gap: 10px; justify-content: center; margin-top: 18px; flex-wrap: wrap; }
#subWork { margin-top: 16px; }
#subWork .swroom { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 14px; }
#subWork .swroom h3 { color: var(--amber); font-size: 14px; margin: 0 0 8px; }
#subWork pre { background: #0a1620; border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; overflow: auto;
  font: 12px/1.45 ui-monospace, Menlo, Consolas, monospace; white-space: pre-wrap; word-break: break-word; margin: 0 0 10px; }
#subWork img { max-width: 100%; border-radius: 8px; display: block; }
#subWork .swnone { opacity: .55; font-size: 12px; margin: 0 0 8px; }
#subWork .swintro { opacity: .8; font-size: 12.5px; margin: 2px 0 4px; }
#subWork .swq { font-size: 12.5px; opacity: .92; margin: 0 0 8px; line-height: 1.5;
  background: rgba(255,216,140,.06); border-left: 2px solid rgba(255,216,140,.5); padding: 7px 10px; border-radius: 0 6px 6px 0; }
#subWork .swqlbl { color: var(--amber); font-weight: 600; }
#subWork .swcode { width: 100%; min-height: 90px; resize: vertical; background: #0a1620; color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px;
  font: 12px/1.45 ui-monospace, Menlo, Consolas, monospace; }
#subWork .swbtnrow { display: flex; align-items: center; gap: 10px; margin: 8px 0 10px; }
#subWork .swrun { margin: 0; }
#subWork .swstat { font-size: 12px; opacity: .8; }
