:root {
  --bg: #f2efe6;
  --panel: #fffdf8;
  --panel-2: #eae5d6;
  --border: #d6cfba;
  --text: #2b2a26;
  --text-dim: #6b6960;
  --accent: #b5791f;
  --safety: #b3352f;
  --quality: #2f5fa0;
  --programme: #b5791f;
  --commercial: #1f7a54;
  --environmental: #2e7d32;
  --information: #6a4fb3;
  --ok: #2f8f5b;
  --bad: #b3352f;
  --warn: #b5791f;
  --radius: 10px;
  --opening-sky: #7b8a99;
  --opening-sky-low: #9aa6ad;
  --opening-ground: #4a4844;
  --opening-silhouette: #2b2a28;
}

[data-theme="dark"] {
  --bg: #0f1620;
  --panel: #17212e;
  --panel-2: #1e2a3a;
  --border: #2a3a4d;
  --text: #e8edf3;
  --text-dim: #9fb0c2;
  --accent: #f5a623;
  --safety: #e5484d;
  --quality: #5b8def;
  --programme: #f5a623;
  --commercial: #2fb380;
  --environmental: #3fb950;
  --information: #a78bfa;
  --ok: #2fb380;
  --bad: #e5484d;
  --warn: #f5a623;
  --opening-sky: #3a4552;
  --opening-sky-low: #5a6570;
  --opening-ground: #24211e;
  --opening-silhouette: #14120f;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}

#app {
  max-width: 720px;
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18px 20px;
  border-bottom: 1px solid var(--border);
}

.brand {
  font-weight: 700;
  letter-spacing: 0.5px;
  font-size: 16px;
}

.score-panel {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 14px;
  font-weight: 700;
  display: flex;
  gap: 8px;
  align-items: baseline;
}

.score-label {
  font-size: 11px;
  color: var(--text-dim);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

#score-value { color: var(--accent); font-size: 16px; }

.header-controls {
  display: flex;
  align-items: center;
  gap: 10px;
}

.theme-toggle {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  width: 34px;
  height: 34px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.theme-toggle:hover {
  border-color: var(--accent);
}

#game-root {
  flex: 1;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* World mode (Čaks, 2026-09-19 — "the image is not content inside the UI,
   the image is the UI"): the beige page chrome around the scene (header,
   #app's 720px column, #game-root's 20px padding) was still visible even
   after the box lost its border — the scene was borderless but still
   floating in a page. This removes the page around it: #app goes full
   width, the padding collapses, the topbar shrinks to a thin unobtrusive
   strip, and the scene itself grows to fill the real viewport instead of
   being capped to a 16:9 box. Toggled via body.world-mode, set only while
   a world scene (Site Office / Block A) is showing — every other screen
   (lists, decisions, debrief) keeps the normal reading-column layout. */
body.world-mode #app { max-width: 100%; }
body.world-mode #game-root { padding: 0; gap: 0; }
body.world-mode .topbar { padding: 8px 16px; }
body.world-mode .opening-screen {
  aspect-ratio: auto;
  width: 100vw;
  height: calc(100vh - 52px);
  max-height: none;
}
/* Real desktop bug fix (2026-09-27): .scene-hero (the SEE/NOTICE/ACT/RESULT/
   Consult hero screens) never had this override — on any viewport wider
   than #app's normal 720px reading-column cap, it sat as a small fixed-
   16:10 box with a huge empty page around it. Same full-viewport treatment
   as .opening-screen above, now that the JS side also flags these screens
   as world-mode (see renderSeePhase() and siblings in script.js). */
body.world-mode .scene-hero {
  aspect-ratio: auto;
  width: 100vw;
  height: calc(100vh - 52px);
  max-height: none;
}
body.world-mode #footer-note { display: none; }

/* The opening screen is a single fixed-aspect-ratio card — on a narrow/tall
   viewport it's short, and left flex-start it just pins to the top with the
   leftover space dumped below. Center it instead so that space is even. */
#game-root:has(.opening-screen) {
  justify-content: center;
}

#loading {
  color: var(--text-dim);
  text-align: center;
  padding: 60px 0;
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  animation: card-fade-in 0.28s ease;
}

/* ---- Motion layer: signals activity, not decoration. Safe to delete this
   block entirely — nothing else depends on it. ---- */
@keyframes card-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

@keyframes pulse-signal {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.15); }
}

.svg-badge-circle.pending {
  animation: pulse-signal 1.8s ease-in-out infinite;
  transform-box: fill-box;
  transform-origin: center;
}

.observation-row:not(.done) {
  position: relative;
  padding-left: 24px;
}

.observation-row:not(.done)::before {
  content: "";
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--bad);
  animation: pulse-signal 1.8s ease-in-out infinite;
}
/* ---- end motion layer ---- */

.site-walk-id {
  font-size: 11px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 8px;
}

.scenario-media {
  width: 100%;
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 14px;
  background: var(--panel-2);
  border: 1px solid var(--border);
}

.scenario-media img,
.scenario-media video {
  width: 100%;
  display: block;
  max-height: 320px;
  object-fit: cover;
}

.scenario-media.empty {
  display: none;
}

/* Visual-First vertical slice (2026-09-19) — proof of concept that the
   scene image can BE the gameplay surface instead of a caption above a
   text card. Reuses the opening screen's proven hero+scrim language
   (.opening-hero/.opening-overlay) rather than inventing a new visual
   system. Deliberately scoped to one scenario at a time via a render path,
   not a global replacement of .scenario-media — see VISUAL_HERO_IDS. */
.scene-hero {
  /* No card frame — matches .opening-screen's "image is the UI" treatment. */
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  max-height: 86vh;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-color: var(--panel-2);
}

.scene-hero-eyebrow {
  position: absolute;
  top: 14px;
  left: 18px;
  font-size: 11px;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.6);
  text-transform: uppercase;
  letter-spacing: 1.5px;
  opacity: 0.85;
}

.scene-hero-scrim {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 12px;
  padding: 20px;
  background: linear-gradient(180deg, rgba(0,0,0,0.05) 0%, transparent 30%, transparent 55%, rgba(0,0,0,0.82) 100%);
}

.scene-hero-caption {
  color: #fff;
  text-shadow: 0 1px 4px rgba(0,0,0,0.75);
  font-size: 16px;
  line-height: 1.5;
  max-width: 640px;
}

.scene-hero-caption .scene-hero-context {
  opacity: 0.88;
  font-size: 14px;
  margin-top: 6px;
}

.scene-action-panel {
  margin-top: 14px;
}

/* World-mode Location/Event screens (Čaks, 2026-09-20): "Look around" /
   "Back" must live ON the hero image, never in a panel below it — a beige
   footer under the photo breaks "the image is the game world" the same way
   a card frame did. Nesting .scene-action-panel inside .opening-overlay's
   bottom group (see renderScaffoldLocation/renderBlockALocation/
   renderDeliveryLocation) puts it in the overlay instead of normal
   document flow; these overrides just make the buttons legible against a
   photo, since .btn-secondary's transparent/dim style assumes a plain
   page background. */
.opening-overlay-bottom {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* §21 fix (2026-09-22): baseCardHtml()/renderResultPhase() now mount
     dynamically-sized content here (Classify's checkbox grid, Detect's
     options + separate Continue button, Result's five result-sections) —
     content .opening-screen's fixed-height/overflow:hidden shell was never
     designed to hold. Without this, a Continue button could render fully
     off-screen and unreachable (real regression caught live-testing the
     Codex-flagged scenarios). Scrolls internally instead of clipping. */
  max-height: 62vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.opening-overlay .scene-action-panel {
  margin-top: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  /* Buttons always win over a hotspot that happens to sit under them (2026-10-09, found on a
     real iPhone test: on a phone the Goods-In "Block Delivery" hotspot box covered "Look around"
     and the tap opened the delivery note instead of the driver's call). The panel itself ignores
     taps, so hotspots in the gaps between buttons stay reachable. */
  position: relative;
  z-index: 7;
  pointer-events: none;
}
.opening-overlay .scene-action-panel > * { pointer-events: auto; }
.opening-overlay .scene-action-panel .btn-secondary,
.scene-hero-scrim .scene-action-panel .btn-secondary {
  background: rgba(0,0,0,0.45);
  color: #fff;
  border-color: rgba(255,255,255,0.5);
}

/* End Shift (F6.1/G, 2026-09-27): needs to read as "finish today's
   workday", not "X to exit" and not "the correct next click" - so it's
   neither the top-right corner button it used to be (that position reads
   as dismiss/close on every other UI pattern) nor styled like .btn-primary
   (which would wrongly suggest it's the recommended action). Warm amber
   outline against the semi-transparent nav-button fill keeps it visually
   distinct from plain "Back"/location .btn-secondary buttons it now sits
   next to, without competing with them for attention. */
.opening-overlay .scene-action-panel .btn-end-shift {
  background: rgba(0,0,0,0.45);
  color: #fff;
  border: 1px solid var(--accent);
}
.scene-hero-scrim .scene-action-panel {
  margin-top: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* World-mode object drill-down (Office Computer/Phone/Drawing, Čaks
   2026-09-20): the underlying item-list content/logic is unchanged
   (pendingDeskItems/deskItemRowHtml/beginDeskItem) — only the presentation
   moves from a separate plain-card page into an overlay panel on top of
   the office photo, so clicking a physical object never drops the player
   out of the world into old page chrome. */
.world-panel {
  background: rgba(15,15,15,0.72);
  backdrop-filter: blur(2px);
  border-radius: 12px;
  padding: 14px 16px;
  max-height: 46vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.world-panel .site-walk-id { color: rgba(255,255,255,0.75); }
.world-panel .situation-text { color: #fff; }

.scene-action-panel .phase-label {
  font-size: 13px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text);
  margin-bottom: 12px;
}

@media (max-width: 480px) {
  .scene-hero { aspect-ratio: 3 / 4; background-position: 62% center; }
  .scene-hero-caption { font-size: 14px; }
  .scene-hero-caption .scene-hero-context { font-size: 13px; }
}

/* Contact visual identity block (Visual Gameplay System v0.1) — small,
   inline portrait so the player recognises WHO is speaking before reading
   the message. Graceful by design: contactVisualHtml() returns "" when no
   asset exists for a role, so this class simply never appears — no broken
   image, no layout shift, callers/contacts without a visual just show text. */
.contact-visual {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  border: 1px solid var(--border);
  background: var(--panel-2);
}

.contact-visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.contact-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}

.situation-text {
  font-size: 16px;
  line-height: 1.5;
  margin-bottom: 14px;
}

.observation-box {
  background: var(--panel-2);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  padding: 12px 14px;
  font-size: 14px;
  color: var(--text-dim);
}

.observation-box strong {
  color: var(--text);
}

.briefing-list {
  margin: 8px 0 0;
  padding-left: 18px;
}
.briefing-list li {
  margin-bottom: 6px;
  line-height: 1.4;
}
.briefing-list li:last-child {
  margin-bottom: 0;
}
/* Shift-briefing card on a phone held sideways (short viewport): compact text
   so the Start shift button stays in view without scrolling the card. */
@media (max-height: 520px) and (orientation: landscape) {
  .scene-dialogue.briefing-card { font-size: 12px; line-height: 1.25; padding: 6px 10px; }
  .scene-dialogue.briefing-card .briefing-list { margin-top: 4px; }
  .scene-dialogue.briefing-card .briefing-list li { margin-bottom: 1px; line-height: 1.25; }
}

.phase-label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--accent);
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.timer-bar-wrap {
  height: 4px;
  background: var(--panel-2);
  border-radius: 2px;
  overflow: hidden;
  margin-bottom: 14px;
}

.timer-bar {
  height: 100%;
  background: var(--warn);
  width: 100%;
  transition: width 0.2s linear, background 0.3s;
}

.timer-bar.urgent { background: var(--bad); }

.option-grid {
  display: grid;
  gap: 10px;
}

.option-btn {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 13px 16px;
  border-radius: 8px;
  text-align: left;
  font-size: 14px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}

.option-btn:hover {
  border-color: var(--accent);
  background: #24344a;
  color: #eef3fa;
}

.option-btn.selected {
  border-color: var(--accent);
  background: #2a3d54;
  color: #eef3fa;
}

/* Čaks (2026-09-20): an already-called contact was functionally disabled
   but visually identical to a live option — read as a duplicate, still-
   active "WHO DO YOU CALL?" choice. Make "done" state unmistakable. */
.option-btn:disabled {
  cursor: not-allowed;
  opacity: 0.55;
  background: transparent;
  border-style: dashed;
}

.option-btn:disabled:hover {
  border-color: var(--border);
  background: transparent;
  color: var(--text);
}

/* Experiment A (2026-10-25): at zero attention the decision options are shown but inert. They are information (what the choices
   would be), so they must stay readable on a photo: keep the panel background, only dim and dash them. */
.option-btn.locked:disabled,
.option-btn.locked:disabled:hover {
  background: var(--panel-2);
  color: var(--text);
  opacity: 0.8;
  border-style: dashed;
  cursor: not-allowed;
}

.checkbox-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-dim);
  padding: 8px 14px;
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
}

.chip.active {
  border-color: var(--accent);
  color: #eef3fa;
  background: #2a3d54;
}

.btn-primary {
  background: var(--accent);
  color: #1a1305;
  border: none;
  padding: 12px 20px;
  border-radius: 8px;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  margin-top: 6px;
}

.btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.btn-secondary {
  background: transparent;
  color: var(--text-dim);
  border: 1px solid var(--border);
  padding: 12px 20px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
}

.result-banner {
  border-radius: 8px;
  padding: 14px 16px;
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 4px;
}

.result-banner.ok { background: rgba(47,179,128,0.15); border: 1px solid var(--ok); color: var(--ok); }
.result-banner.bad { background: rgba(229,72,77,0.15); border: 1px solid var(--bad); color: var(--bad); }
.result-banner.partial { background: rgba(245,166,35,0.15); border: 1px solid var(--warn); color: var(--warn); }

.result-section {
  border-top: 1px solid var(--border);
  padding-top: 12px;
  margin-top: 12px;
  font-size: 14px;
  line-height: 1.55;
}

.result-section .label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  font-weight: 700;
  margin-bottom: 4px;
}

/* UI Readability QA — SMS-029 (2026-09-26, Andris's own live screenshot:
   "es kā cilvēks ne visu varu izlasīt"). Root cause: this DETECT/CLASSIFY/
   ACT resolution screen's .result-section blocks were built assuming a
   plain, opaque .card background (var(--text) is dark-on-light by
   default) — never actually tested against a real photographic
   .opening-hero, because no non-VISUAL_HERO_IDS scenario had a dedicated
   image until SMS-029/SMS-023 today. Over a busy photo, dark text on no
   background (plus each section's border-top line) reads as illegible
   noise. Fix is a controlled readability layer, NOT a rebuild: same
   "dark scrim + white text" treatment .scene-dialogue already uses
   elsewhere for exactly this reason, applied only inside .result-panel —
   theme-independent (fixed colours, not var(--text)) since it must stay
   readable over any photo regardless of light/dark site theme. World
   Mode image stays the primary visual layer; this only wraps the
   resolution/debrief text that sits on top of it. */
.result-panel {
  background: rgba(0,0,0,0.68);
  border-radius: 10px;
  padding: 14px 16px;
  color: #fff;
}
.result-panel .result-section {
  border-top-color: rgba(255,255,255,0.22);
  font-size: 15px;
  line-height: 1.6;
}
.result-panel .result-section .label {
  color: rgba(255,255,255,0.65);
}
.result-panel .tag {
  border-color: rgba(255,255,255,0.4);
  color: rgba(255,255,255,0.85);
}
.result-panel .tag.verified { border-color: var(--ok); color: var(--ok); }
.result-panel .tag.needs-review { border-color: var(--warn); color: var(--warn); }

.consequence-row {
  display: flex;
  gap: 14px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.consequence-pill {
  font-size: 12px;
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
}

.consequence-pill.neg { color: var(--bad); border-color: var(--bad); }

.tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
}

.tag.verified { border-color: var(--ok); color: var(--ok); }
.tag.needs-review { border-color: var(--warn); color: var(--warn); }

.type-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  margin-right: 6px;
  margin-bottom: 6px;
  border: 1px solid var(--border);
  color: var(--text-dim);
}

.footer-note {
  padding: 14px 20px;
  font-size: 11px;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
  text-align: center;
}

.footer-note .tag { vertical-align: middle; }

.footer-links {
  margin-top: 8px;
  font-size: 11px;
}

.footer-links a {
  color: var(--accent);
  text-decoration: none;
  padding: 4px 2px;
}

.footer-links a:hover,
.footer-links a:focus {
  text-decoration: underline;
}

/* Meters */
.meter-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 16px;
  margin: 14px 0;
}

.meter-label {
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 4px;
  display: flex;
  justify-content: space-between;
}

.meter-value { color: var(--text); font-weight: 700; }

.meter-track {
  height: 6px;
  background: var(--panel-2);
  border-radius: 3px;
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  background: var(--ok);
  transition: width 0.3s;
}

.meter-fill.warn { background: var(--warn); }
.meter-fill.danger { background: var(--bad); }

.slots-remaining {
  font-size: 13px;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
  padding-top: 10px;
}

.slots-remaining strong { color: var(--accent); }

/* Observation list */
.observation-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.observation-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
}

.observation-row.done {
  opacity: 0.55;
}

.observation-num {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--border);
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.observation-text {
  flex: 1;
  font-size: 13.5px;
}

.btn-investigate {
  flex: none;
  background: var(--accent);
  color: #1a1305;
  border: none;
  padding: 8px 14px;
  border-radius: 6px;
  font-weight: 700;
  font-size: 12.5px;
  cursor: pointer;
}

.btn-investigate:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* ---- Opening screen: the world, not a dashboard ----
   Hero is a placeholder gradient/silhouette until Džimītis delivers the
   real illustration — swap it in later via a single background-image line
   on .opening-hero (or set --hero-image), no structural change needed. */
.opening-screen {
  /* Čaks, 2026-09-19: "THE IMAGE IS THE UI" — this is the world viewport,
     not a photo inside a card. No border, no radius, no shadow: the scene
     itself is the surface the player interacts with. */
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 86vh;
  overflow: hidden;
  background: var(--opening-sky);
}

/* Mobile World Walk landscape hint (Čaks, 2026-09-22, after a real iPhone
   portrait test showed Scaffold's hotspot floating with no visible scaffold
   under it — hotspot placement is correct for the image, portrait just
   crops too much of the world to read the geography). Recommend, never
   force: hidden entirely outside "narrow + portrait" so desktop/tablet and
   already-landscape phones never see it, and it vanishes the instant the
   device is rotated (a plain CSS media query, not a resize listener). */
.landscape-hint {
  display: none;
}
@media (max-width: 768px) and (orientation: portrait) {
  .landscape-hint {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    position: absolute;
    top: 10px;
    left: 10px;
    right: 10px;
    z-index: 5;
    background: rgba(0,0,0,0.6);
    border: 1px solid rgba(255,255,255,0.3);
    border-radius: 10px;
    padding: 8px 10px;
    color: #fff;
    font-size: 12px;
    line-height: 1.3;
    text-shadow: 0 1px 3px rgba(0,0,0,0.5);
  }
  .landscape-hint button {
    background: transparent;
    border: none;
    color: #fff;
    font-size: 15px;
    line-height: 1;
    padding: 2px 4px;
    flex-shrink: 0;
  }
}

/* Evidence photo view (Čaks's decision, 2026-09-27): mobile portrait's
   .opening-overlay-bottom text panel covers roughly two-thirds of the
   hero image (measured live on SMS-031: panel top at 33% of hero height)
   - fine for ambient/illustrative screens, but a real problem for
   evidence-required scenarios (§M/§13/§14/PFP sub-tier) where the player
   is specifically meant to read information out of the image. Mobile-only
   (desktop/tablet already show plenty of image around the overlay, no
   toggle needed there) toggle button that hides the text panel so the
   full evidence image is briefly viewable, per Čaks's chosen flow:
   scenario view -> [View Photo] -> full image -> [Back] -> same state. */
.evidence-view-toggle {
  display: none;
}
@media (max-width: 768px) and (orientation: portrait) {
  .evidence-view-toggle {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 6;
    background: rgba(0,0,0,0.55);
    color: #fff;
    border: 1px solid rgba(255,255,255,0.5);
    border-radius: 20px;
    padding: 8px 14px;
    min-height: 44px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    font-size: 13px;
    font-weight: 600;
  }
  .opening-screen.evidence-view-active .opening-overlay-bottom {
    display: none;
  }
  /* Čaks's follow-up check (2026-09-27): "View Photo" hid the text panel
     but the hero image still used background-size:cover, which center-
     crops horizontally to fill a tall portrait viewport — measured on
     SMS-031's 1024x572 source in a 375x760 container, only ~28% of the
     image WIDTH was actually visible. "View Photo" is meant to show
     evidence, not a crop of it, so evidence-view switches to `contain`
     (whole image visible, letterboxed) instead of `cover`. Only inside
     .evidence-view-active, so the normal always-cropped-to-fill hero
     look is unchanged everywhere else (including this same screen before
     the toggle is pressed). */
  .opening-screen.evidence-view-active .opening-hero {
    background-size: contain;
    background-repeat: no-repeat;
    background-color: #000;
  }
}

.opening-hero {
  position: absolute;
  inset: 0;
  background-image: var(--hero-image, none);
  background-size: cover;
  background-position: center;
}

/* Andris, live playtest (2026-09-20): renderConsultPhase()'s consulted-
   contact response text sits directly over the scene image now (fixing
   the "beige background" violation) — but a bare <div class="scene-dialogue">
   with no background scrim isn't legible against a busy image like Block
   A's blockwork. The existing `.opening-signals.scene-dialogue` rule only
   fires on mobile AND requires the .opening-signals class too, so it
   never applied here. This gives .scene-dialogue its own always-on scrim,
   independent of .opening-signals, so any world-mode dialogue text stays
   readable regardless of what's behind it. */
.scene-dialogue {
  background: rgba(0,0,0,0.55);
  border-radius: 8px;
  padding: 10px 12px;
  color: #fff;
}
.scene-dialogue .contact-header strong { color: #fff; }

/* Presentation types (2026-09-27): "no textbox as the default information
   container" — email/SMS get a shell that actually reads as that medium,
   not the same generic caption as everything else. Both still sit inside
   the existing World Mode overlay (no new screen, no new engine). */
.medium-email {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  color: var(--text);
}
.medium-email-header {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  font-size: 13px;
}
.medium-email-field { margin: 2px 0; }
.medium-email-label {
  display: inline-block;
  width: 56px;
  color: var(--text-dim);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.5px;
}
.medium-email-body {
  padding: 10px 12px;
  line-height: 1.4;
}

.medium-sms {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.medium-sms-sender {
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.7);
  padding-left: 4px;
}
.medium-sms-bubble {
  background: #fff;
  color: var(--text);
  border-radius: 16px 16px 16px 4px;
  padding: 10px 14px;
  max-width: 85%;
  box-shadow: 0 2px 6px rgba(0,0,0,0.25);
}

/* Same visual family as .world-call-banner/.incoming-call-hero (accent
   border, dark scrim) so a "call" desk item reads as the same medium as a
   live incoming call elsewhere in the game, not a coincidentally similar
   but different-looking thing. */
.medium-call {
  background: rgba(0,0,0,0.55);
  border: 2px solid var(--accent);
  border-radius: 10px;
  padding: 10px 14px;
  color: #fff;
}
.medium-call-header {
  font-weight: 700;
  font-size: 14px;
  margin-bottom: 6px;
  opacity: 0.9;
}
.medium-call-transcript {
  font-style: italic;
}

/* EvidenceReport v1 (Čaks, 2026-09-20): the first free-text/numeric input
   in the game, sitting on a World Mode scene like .scene-dialogue — same
   dark scrim treatment so it reads against a busy photo, not a floating
   plain form. */
.evidence-report-form {
  background: rgba(0,0,0,0.55);
  border-radius: 8px;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.evidence-report-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
}
.evidence-report-field input,
.evidence-report-field textarea {
  background: rgba(255,255,255,0.92);
  border: 1px solid rgba(255,255,255,0.5);
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 14px;
  font-family: inherit;
  color: var(--text);
  resize: vertical;
}
.evidence-report-field input:focus,
.evidence-report-field textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* World Mode video calls (CALL-05/09/13) — same full-bleed box as
   .opening-hero, but an actual <video> element instead of a CSS
   background-image, which can't play video. */
.opening-hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}

.opening-hero.has-real-art::before,
.opening-hero.has-real-art::after {
  display: none;
}

.opening-hero::before {
  /* Dawn sky gradient placeholder */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--opening-sky) 0%, var(--opening-sky-low) 55%, var(--opening-ground) 55%, var(--opening-ground) 100%);
}

.opening-hero::after {
  /* Abstract skyline silhouette — crane + partial building, kept deliberately simple */
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0; height: 46%;
  background:
    linear-gradient(180deg, transparent, var(--opening-silhouette) 85%),
    repeating-linear-gradient(90deg, var(--opening-silhouette) 0 2px, transparent 2px 34px);
  background-blend-mode: normal;
  opacity: 0.9;
  clip-path: polygon(0 100%, 0 55%, 8% 55%, 8% 30%, 10% 30%, 10% 55%, 30% 55%, 30% 40%, 33% 40%, 33% 8%, 34% 8%, 34% 40%, 58% 40%, 58% 62%, 62% 62%, 62% 38%, 90% 38%, 90% 62%, 100% 62%, 100% 100%);
}

.opening-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 22px;
  background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, transparent 30%, transparent 70%, rgba(0,0,0,0.45) 100%);
}

.opening-time {
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}

.opening-time .clock { font-size: 28px; font-weight: 700; letter-spacing: 0.5px; }
.opening-time .place { font-size: 13px; text-transform: uppercase; letter-spacing: 1.5px; opacity: 0.85; margin-top: 2px; }

/* "What is this?" line for a first-time/external visitor (Čaks, pilot
   launch checklist §8) — unlike .opening-signals (ambient flavour, hidden
   on mobile), this stays visible at every viewport size since a stranger
   arriving from a LinkedIn/CIOB link needs it most on their phone. */
.opening-tagline {
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  opacity: 0.95;
  margin-top: 8px;
  max-width: 340px;
  line-height: 1.4;
}

/* Voluntary-support link (Buy Me a Coffee): a plain link under the tagline, no popup, no widget, never in the action area. */
.opening-support { margin-top: 6px; font-size: 12.5px; opacity: 0.95; line-height: 1.4; }
.opening-support a { color: #ffe08a; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
.opening-support span { opacity: 0.8; }

.opening-signals {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
  font-size: 13px;
}

/* Incoming-call banner on a World Mode location (Walkway) — same visual
   language as .incoming-call, adapted for the full-bleed hero scrim instead
   of a boxed card. */
.world-call-banner {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: rgba(0,0,0,0.5);
  border: 2px solid var(--accent);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 15px;
  font-weight: 600;
}
.world-call-banner .scene-action-panel { margin-top: 0; }

/* Opening-screen hotspots — reuses the same .photo-hotspot dot/pulse
   language as the Block A photo-view prototype (one interaction verb,
   third context) instead of a dashboard button row. */
.opening-hotspot {
  flex-direction: column;
  gap: 4px;
}

.opening-hotspot-label {
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.7);
  white-space: normal;
  max-width: min(120px, 34vw);
  text-align: center;
  line-height: 1.25;
  background: rgba(0,0,0,0.4);
  padding: 3px 9px;
  border-radius: 999px;
}

/* Small phones: the 16:9 hero box gets short in absolute px, so fixed-size
   overlay text/padding crowds the hotspots. Ease that off, and drop the
   ambient signals row (flavour only, not needed to act) rather than let it
   collide with the Messages hotspot underneath it. */
@media (max-width: 480px) {
  .opening-overlay { padding: 12px; }
  .opening-time .clock { font-size: 20px; }
  .opening-time .place { font-size: 10px; }
  .opening-tagline { font-size: 11px; max-width: 220px; }
  /* .opening-signals hides on mobile because it's ambient flavour there
     (missed-call counts etc.) — but the same class reused for a call/event
     screen's actual dialogue is real content, never decorative, so it must
     never disappear. Scope the hide to when it's NOT carrying dialogue. */
  .opening-signals:not(.scene-dialogue) { display: none; }
  .opening-signals.scene-dialogue {
    font-size: 13px;
    background: rgba(0,0,0,0.45);
    padding: 8px 10px;
    border-radius: 8px;
  }
  .opening-hotspot-label { font-size: 10px; max-width: min(96px, 30vw); padding: 2px 7px; }
  .photo-hotspot-dot { width: 18px; height: 18px; }
}
/* ---- end opening screen ---- */

/* Pane tabs — keeps Desk and Site Walk both reachable without scrolling
   past one to find the other, especially on narrow/mobile viewports. */
.pane-tabs {
  display: flex;
  gap: 8px;
  margin-bottom: -4px;
}

.pane-tab {
  flex: 1;
  background: var(--panel);
  border: 1px solid var(--border);
  border-bottom: none;
  color: var(--text-dim);
  padding: 10px 12px;
  border-radius: var(--radius) var(--radius) 0 0;
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.pane-tab.active {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--accent);
}

.pane-tab-count {
  background: var(--bad);
  color: #fff;
  border-radius: 999px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Realistic-view prototype (pilot: Block A) — same click logic as the SVG
   map, different renderer. Placeholder styling is deliberately honest about
   being a placeholder, not a fake CSS attempt at "realistic". */
.view-mode-toggle {
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 5px 10px;
  border-radius: 6px;
  cursor: pointer;
}

.view-mode-toggle:hover {
  border-color: var(--accent);
  color: var(--text);
}

.site-photo-scene {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 8px;
  overflow: hidden;
  background: repeating-linear-gradient(45deg, var(--panel-2), var(--panel-2) 12px, var(--panel) 12px, var(--panel) 24px);
  background-size: cover;
  background-position: center;
  border: 1px dashed var(--border);
}

.site-photo-scene.has-real-art {
  border: 1px solid var(--border);
}

.site-photo-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.6;
  padding: 20px;
}

.site-photo-placeholder span {
  font-weight: 400;
  font-size: 11px;
  opacity: 0.8;
}

.photo-hotspot {
  position: absolute;
  transform: translate(-50%, -50%);
  background: none;
  border: none;
  padding: 0;
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
}

.photo-hotspot-dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--panel);
  border: 2px solid var(--border);
  box-shadow: 0 0 0 6px rgba(0,0,0,0.06);
}

.photo-hotspot.clickable {
  cursor: pointer;
}

.photo-hotspot.clickable .photo-hotspot-dot {
  border-color: var(--accent);
  background: var(--accent);
  animation: pulse-signal 1.8s ease-in-out infinite;
  transform-box: fill-box;
}

.photo-hotspot.quiet .photo-hotspot-dot {
  opacity: 0.3;
}

.photo-hotspot-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bad);
  color: #fff;
}

/* World-scene hotspots (Site Office, 2026-09-19 — Čaks: "the player sees the
   computer, they click the computer" — no visible button chrome by default,
   the object in the photo IS the affordance). Label only appears on
   hover/focus (desktop) or stays a faint tap-hint on touch, since there's
   no hover there. The DOT itself stays genuinely findable (Andris, live
   playtest on his own screen: the previous near-invisible white ring
   against varied photo backgrounds read as "weak, hard to see" — a real
   usability regression, not just an aesthetic note) — a bright white ring
   with a dark outline for contrast on any background colour, plus a slow
   pulse so it's noticeable without reading as a loud UI button.
   Specificity note: this must beat .photo-hotspot.clickable .photo-hotspot-dot
   (3 classes) — .opening-hotspot.world-hotspot (2 classes on the same
   element) brings it to equal specificity, and later source order wins. */
.opening-hotspot.world-hotspot .photo-hotspot-dot {
  width: 26px;
  height: 26px;
  background: rgba(255,255,255,0.85);
  border: 2px solid rgba(0,0,0,0.55);
  box-shadow: 0 0 0 4px rgba(255,255,255,0.25), 0 1px 4px rgba(0,0,0,0.4);
  animation: pulse-signal 2.2s ease-in-out infinite;
  opacity: 1;
  transition: opacity 0.15s ease, background 0.15s ease;
}

.world-hotspot:hover .photo-hotspot-dot,
.world-hotspot:focus-visible .photo-hotspot-dot {
  opacity: 1;
  background: rgba(255,255,255,0.16);
}

.world-hotspot .opening-hotspot-label {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}

.world-hotspot:hover .opening-hotspot-label,
.world-hotspot:focus-visible .opening-hotspot-label {
  opacity: 1;
  transform: translateY(0);
}

/* No hover on touch — keep a faint always-visible label so the object is
   still findable, just far quieter than the old always-on pill. */
@media (hover: none) {
  .world-hotspot .opening-hotspot-label { opacity: 0.75; transform: none; }
  .world-hotspot .photo-hotspot-dot { opacity: 0.65; }
}

.photo-hotspot-badge.done {
  background: var(--ok);
}

.site-photo-caption {
  font-size: 11px;
  color: var(--text-dim);
  text-align: center;
  margin-top: 8px;
  font-style: italic;
}

/* Incoming call — the living-world interrupt. Sits above everything else
   but blocks nothing; the player can keep working while it rings. §21 fix
   (2026-09-22): now a compact .scene-hero variant (world visual behind the
   Answer/Ignore/Let-it-ring decision) instead of a flat beige card — capped
   shorter than a full scene since it's a banner on top of the shift hub,
   not a full-screen moment. */
.incoming-call-hero {
  aspect-ratio: unset;
  height: 180px;
  max-height: 30vh;
  border-radius: var(--radius, 12px);
  margin-bottom: 14px;
  border: 2px solid var(--accent);
}

.incoming-call-name {
  font-size: 17px;
  font-weight: 700;
}

/* Window chrome — desk (Windows-taskbar feel) vs site walk (view through a window) */
.window-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
  padding: 8px 14px;
  margin: -20px -20px 16px -20px;
  border-radius: var(--radius) var(--radius) 0 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.window-dots {
  display: flex;
  gap: 5px;
}

.window-dots i {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--border);
}

.desk-pane {
  position: relative;
}

.desk-scene {
  aspect-ratio: 21 / 8;
  border-radius: 8px;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  border: 1px solid var(--border);
  margin: 12px 0;
}

.desk-sub {
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 10px;
}

/* Desk Office UX v2 — categorised doors instead of a flat list. Badge shows
   a pending count only, never which items are inside or how urgent they
   are (see deskPaneHtml() comment). Grid sizes down to 2 columns on narrow
   viewports via the general .card max-width rather than a media query here. */
.desk-category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
}

.desk-category-door {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 10px;
  cursor: pointer;
  font-family: inherit;
  color: var(--text);
}

.desk-category-door:hover {
  border-color: var(--accent);
}

.desk-category-door-icon {
  font-size: 26px;
}

.desk-category-door-label {
  font-size: 13px;
  font-weight: 600;
  text-align: center;
}

.desk-category-door-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 20px;
  height: 20px;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
}

.desk-category-door-badge.done {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
}

.desk-category-back {
  margin-bottom: 10px;
}

.desk-category-heading {
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 10px;
}

.window-pane {
  position: relative;
  overflow: hidden;
}

.window-pane::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(120, 170, 220, 0.12), transparent 45%);
  pointer-events: none;
}

/* Site plan — blueprint-style SVG, not colored UI boxes. Line art only;
   fill/stroke entirely via theme variables so it re-themes automatically. */
.site-plan-wrap {
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--panel-2);
}

.site-plan-svg {
  display: block;
  width: 100%;
  height: auto;
}

.site-plan-ground {
  fill: var(--panel-2);
}

.svg-zone .zone-shape {
  fill: none;
  stroke: var(--text-dim);
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
  transition: stroke 0.2s, opacity 0.2s;
}

.svg-zone .zone-shape.thin {
  stroke-width: 1.5;
}

.svg-zone .zone-shape.hatch {
  stroke-width: 1;
  opacity: 0.55;
}

.svg-zone .zone-shape.dashed {
  stroke-dasharray: 5 5;
  fill: none;
}

.svg-zone .zone-shape.mass {
  fill: var(--panel-2);
  fill-opacity: 0.9;
}

.svg-zone .zone-shape.fill-solid {
  fill: var(--text-dim);
  fill-opacity: 0.22;
}

.svg-zone .zone-shape.shadow {
  fill: var(--border);
  fill-opacity: 0.35;
  stroke: none;
}

.svg-zone .zone-label-text {
  fill: var(--text-dim);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.svg-zone.quiet {
  opacity: 0.3;
}

.svg-zone.done {
  opacity: 0.55;
}

.svg-zone.clickable .zone-shape {
  stroke: var(--accent);
}

.svg-zone.clickable .zone-label-text {
  fill: var(--text);
}

.svg-zone.clickable:hover .zone-shape {
  stroke: var(--ok);
}

.svg-zone .zone-hit {
  fill: transparent;
}

.svg-zone.clickable .zone-hit {
  cursor: pointer;
}

.svg-badge-circle.pending {
  fill: var(--bad);
}

.svg-badge-circle.done {
  fill: var(--ok);
}

.svg-badge-text {
  fill: #fff;
  font-size: 15px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
  pointer-events: none;
}

/* ---- Feedback widget v1 — ambient, never a modal ---- */
.feedback-widget {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 500;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.feedback-tab {
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 18px;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

.feedback-tab:hover {
  border-color: var(--accent);
}

.corner-tabs {
  display: flex;
  gap: 8px;
}

.corner-panel {
  width: 260px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.2);
}

.feedback-title {
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 10px;
  color: var(--text);
}

.feedback-reactions {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.feedback-react {
  text-align: left;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  cursor: pointer;
}

.feedback-react:hover {
  border-color: var(--accent);
}

.feedback-react.selected {
  border-color: var(--accent);
  background: var(--panel);
  font-weight: 700;
}

.feedback-text {
  width: 100%;
  margin-top: 10px;
  box-sizing: border-box;
  min-height: 56px;
  resize: vertical;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 8px;
  padding: 8px;
  font-size: 12px;
  font-family: inherit;
}

.feedback-submit {
  margin-top: 8px;
  width: 100%;
  background: var(--accent);
  color: #1a1305;
  border: none;
  border-radius: 8px;
  padding: 8px;
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}

.feedback-submit:disabled {
  opacity: 0.5;
  cursor: default;
}

.feedback-thanks {
  font-size: 12px;
  color: var(--ok);
  font-weight: 700;
  text-align: center;
  padding: 6px 0;
}

.feedback-error {
  font-size: 12px;
  color: var(--danger, #e05252);
  font-weight: 700;
  text-align: center;
  padding: 6px 0;
}

@media (max-width: 480px) {
  .corner-panel { width: calc(100vw - 32px); }
  /* Clears the fixed feedback/ideas tabs (bottom:16px, ~44px tall each,
     stacked) so the last world-mode action button's text/tap-target never
     sits underneath them — found via real mobile QA, 2026-09-20. */
  body.world-mode .opening-overlay-bottom { padding-bottom: 60px; }
  /* Scenario screens (Look closer / Decide / result) use .scene-hero-scrim with only 20px of bottom padding, so
     the last answer and "Back" ended up under the feedback tabs (found 2026-10-09 with scripts/qa_hit_audit.js
     smsHitAuditScenarios). Same 60px clearance as above. */
  .scene-hero-scrim { padding-bottom: 60px; }
}

/* ---- Walk transition on the real Walkway photo (2026-09-27) ----
   Replaces the retired separate Nav Slice map (2026-09-26 POC) per real-
   device feedback: navigation now happens directly on the actual site
   photo, using the exact same hotspot positions already defined for
   enterX()/renderX() below — one navigation surface, not two. */
.walkway-sprite {
  position: absolute;
  transform: translate(-50%, -100%);
  transform-origin: 50% 100%;
  font-size: 30px;
  line-height: 1;
  transition: left 0.45s ease, top 0.45s ease;
  filter: drop-shadow(0 3px 4px rgba(0,0,0,0.5));
  z-index: 4;
  cursor: pointer;
  animation: walkway-sprite-idle 2.4s ease-in-out infinite;
  -webkit-user-select: none;
  user-select: none;
}

/* While walking to a hotspot he is a visual only: a tap must reach the hotspot underneath, never him. */
.walkway-sprite.walking {
  animation: walkway-sprite-bob 0.4s ease-in-out infinite;
  pointer-events: none;
}

/* Idle yawn (Andris, 2026-10-09): purely cosmetic, never touches game state. */
.walkway-sprite.yawning {
  animation: walkway-sprite-yawn 1.9s ease-in-out 1;
}

@keyframes walkway-sprite-idle {
  0%, 100% { transform: translate(-50%, -100%) translateY(0); }
  50% { transform: translate(-50%, -100%) translateY(-3px); }
}

@keyframes walkway-sprite-bob {
  0%, 100% { transform: translate(-50%, -100%) scale(1); }
  50% { transform: translate(-50%, -100%) scale(1.08); }
}

@keyframes walkway-sprite-yawn {
  0%, 100% { transform: translate(-50%, -100%) scale(1, 1); }
  30%, 70% { transform: translate(-50%, -100%) scale(0.9, 1.25); }
}

.walkway-sprite-thought {
  position: absolute;
  transform: translate(-50%, -100%);
  background: rgba(255,255,255,0.96);
  border: 1px solid #b9b3a0;
  border-radius: 10px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: #1c1c1c;
  text-align: center;
  width: max-content;
  max-width: min(260px, 72vw);
  z-index: 6;
  pointer-events: none;
}

/* Reduced motion: no walking animation, no bobbing, no yawn stretch (the game also skips the walk delay). */
@media (prefers-reduced-motion: reduce) {
  .walkway-sprite,
  .walkway-sprite.walking,
  .walkway-sprite.yawning { animation: none; transition: none; }
}

/* Persistent attention counter (Čaks's condition, 2026-10-09): visible on every screen once the shift has started.
   Never colour-only: the icon and the number change as well (🧠 -> ⚠️ at 1 left -> ⛔ at 0). */
.attention-chip {
  display: flex;
  align-items: baseline;
  gap: 6px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 10px;
  font-weight: 700;
  white-space: nowrap;
}
.attention-chip-icon { font-size: 13px; line-height: 1; }
.attention-chip-label {
  font-size: 11px;
  color: var(--text-dim);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.attention-chip-text { font-size: 16px; color: var(--text); }
.attention-chip[data-level="low"] { background: #ffd27a; border-color: #b36b00; }
.attention-chip[data-level="low"] .attention-chip-label,
.attention-chip[data-level="low"] .attention-chip-text { color: #3a2300; }
.attention-chip[data-level="empty"] { background: #c62828; border-color: #7f1010; }
.attention-chip[data-level="empty"] .attention-chip-label,
.attention-chip[data-level="empty"] .attention-chip-text { color: #fff; }

/* One-line header on phones once the shift HUD (attention counter + score) is showing: the brand name steps aside
   (the emoji stays), labels shrink away, so nothing wraps and nothing is pushed off-screen. The world screens
   assume a 52px header. Checked at 320 to 1886 px wide (scripts/gate_header.js). */
@media (max-width: 640px) {
  body.has-hud .topbar { gap: 8px; }
  body.has-hud .brand-name { display: none; }
  body.has-hud .header-controls { gap: 8px; min-width: 0; }
  body.has-hud .score-label { display: none; }
  body.has-hud .score-panel { padding: 6px 10px; }
  body.has-hud .attention-chip { padding: 6px 8px; gap: 5px; }
  body.has-hud #score-value { font-size: 15px; white-space: nowrap; }
}
/* The Multiplayer button adds about 106 px to the header, so the same compact one-line form is needed up to 740 px wide when it is present
   (found by the header gate at 667x375, 2026-10-18; without the button the header already fit). */
@media (min-width: 641px) and (max-width: 740px) {
  body.has-hud:has(#mp-team-btn) .topbar { gap: 8px; }
  body.has-hud:has(#mp-team-btn) .brand-name { display: none; }
  body.has-hud:has(#mp-team-btn) .header-controls { gap: 8px; min-width: 0; }
  body.has-hud:has(#mp-team-btn) .score-label { display: none; }
  body.has-hud:has(#mp-team-btn) .score-panel { padding: 6px 10px; }
  body.has-hud:has(#mp-team-btn) .attention-chip { padding: 6px 8px; gap: 5px; }
  body.has-hud:has(#mp-team-btn) #score-value { font-size: 15px; white-space: nowrap; }
}
@media (max-width: 359px) {
  body.has-hud .attention-chip-label { display: none; }
}

/* Very short landscape phones (568x320 and below): the no-attention call banner (with its explanation note) and End Shift
   no longer fit one above the other, so End Shift ended up below the screen. Put them side by side instead. */
@media (orientation: landscape) and (max-height: 340px) {
  .opening-overlay-bottom:has(.attention-empty-note) {
    display: flex;
    flex-direction: row-reverse;   /* End Shift on the left, the call banner to its right */
    align-items: flex-end;
    gap: 12px;
    padding-right: 120px;   /* keep clear of the feedback/ideas tabs in the bottom-right corner */
  }
  .opening-overlay-bottom:has(.attention-empty-note) > .opening-signals { flex: 1 1 auto; min-width: 0; }
  .opening-overlay-bottom:has(.attention-empty-note) > .scene-action-panel { flex: 0 0 auto; }
}

/* Attention-budget notice: short, non-blocking, readable over any World Mode scene. */
.attention-toast {
  position: fixed;
  left: 50%;
  top: 38%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(88vw, 420px);
  padding: 10px 14px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.82);
  color: #fff;
  font-size: 14px;
  line-height: 1.35;
  text-align: center;
  z-index: 60;
  pointer-events: none;
}

/* One-line "how it works" under the morning briefing list. */
.briefing-howto { margin-top: 4px; font-size: 0.85em; opacity: 0.92; }
@media (max-height: 520px) and (orientation: landscape) {
  .briefing-howto { margin-top: 3px; font-size: 11.5px; line-height: 1.25; }
  /* the shared 62vh cap + 60px bottom padding would push the Start shift button into a scroll area here */
  body.world-mode .opening-overlay-bottom.briefing-bottom { max-height: 84vh; padding-bottom: 12px; }
}

/* Compound on short landscape screens (2026-10-09, found by scripts/qa_hit_audit.js): the PPE store
   hotspot (top:40%) lay under the Plot B hotspot (top:29%), because 11% of a ~300 px high hero is only
   ~33 px while each hotspot box is ~50 px, so a tap on the PPE dot opened Plot B. Landscape only:
   portrait and the hotspot positions on the artwork stay as they were. */
@media (max-height: 520px) and (orientation: landscape) {
  #plot-b-btn { top: 22% !important; }
}

/* Attention is used up (2026-10-09, reported on the live site): the Open buttons used to be `disabled`, so
   a tap did nothing and nothing said why. They now stay tappable (the attention gate shows its explanation)
   and look locked; the Office list also carries a visible note and an End Shift button. */
.btn-investigate.locked {
  opacity: 0.45;
  cursor: not-allowed;
}
.attention-empty-note {
  margin: 0 0 10px;
  padding: 9px 12px;
  border-left: 4px solid var(--accent);
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 13.5px;
  line-height: 1.35;
}

/* Detect / Classify cards on phones (2026-10-09, scripts/qa_hit_audit.js): the panel was capped at 62vh, so on
   long scenarios (e.g. SMS-023, SMS-029) "Continue" sat below the visible area and nothing showed that the panel
   scrolls. These cards may use more of the screen; the image is still one tap away via "View Photo". */
@media (max-width: 480px) {
  .opening-overlay-bottom.phase-card { max-height: 84vh; }
}
/* Short phones (iPhone SE class, 667px high): the same cards are ~630-650px of content, so tighten spacing and
   type slightly so they still fit without scrolling (checked with smsHitAuditScenarios at 375x667). */
@media (max-width: 480px) and (max-height: 700px) {
  .opening-overlay-bottom.phase-card { max-height: 90vh; gap: 8px; }
  .phase-card .scene-dialogue { font-size: 14px; padding: 8px 10px; }
  .phase-card .observation-box { font-size: 13px; padding: 8px 12px; }
  .phase-card .option-btn { padding: 10px 14px; font-size: 13.5px; }
  .phase-card #phase-body > .btn-primary { margin-top: 8px !important; padding: 10px 18px; }
}

/* Detect / Classify / Act cards on short landscape screens (2026-10-09): the panel was ~233px high with ~450px of
   content, so "Continue" sat far below the screen. Two columns: the situation on the left, the choices on the
   right, so everything fits at 667x375 / 740x360 / 812x375 (the game itself suggests landscape on phones). */
@media (orientation: landscape) and (max-height: 520px) {
  .opening-overlay-bottom.phase-card {
    max-height: 80vh;
    padding-bottom: 8px;
    padding-right: 56px; /* the feedback tabs stand in a column at the right edge here (see the end of this file) */
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 12px;
    row-gap: 6px;
    align-content: start;
  }
  .phase-card .scene-dialogue,
  .phase-card .observation-box,
  .phase-card .phase-label { grid-column: 1; }
  .phase-card #phase-body { grid-column: 2; grid-row: 1 / span 3; }
  .phase-card .scene-dialogue { font-size: 13px; padding: 6px 9px; }
  .phase-card .observation-box { font-size: 12px; padding: 6px 9px; }
  .phase-card .option-btn { padding: 6px 10px; font-size: 12.5px; }
  .phase-card #phase-body .option-grid { gap: 6px; }
  .phase-card #phase-body > .btn-primary { margin-top: 6px !important; padding: 8px 16px; }
}

/* Explains why "Send" is disabled on the evidence report form (no silent dead buttons). */
.evidence-report-hint { font-size: 12px; color: #fff; opacity: 0.9; margin-top: 4px; }

/* A short instruction the player has to notice (e.g. the Site Office "Grab your PPE" line): always visible (the plain
   .opening-signals caption is hidden on phones as ambient flavour) and big enough to read on a wide desktop screen
   (Andris, 2026-10-09: "too small, I only saw it after I walked to the site walk"). */
.opening-signals.scene-dialogue.scene-hint {
  display: block;
  width: fit-content;
  max-width: min(92vw, 780px);
  font-size: clamp(15px, 1.35vw, 24px);
  font-weight: 600;
  line-height: 1.3;
  padding: 10px 14px;
  background: rgba(0, 0, 0, 0.62);
  border-left: 4px solid #f5b43c;
}
@media (max-width: 480px) {
  /* above the feedback/ideas tabs (bottom-right) instead of running under them */
  .opening-signals.scene-dialogue.scene-hint { margin-bottom: 64px; max-width: calc(100vw - 32px); }
}

/* ---- Scenario screens: three real layout faults behind the six scripts/gate_scenarios.js failures (2026-10-18) ----
   The same six failures were already present at the deployed commit 9629602. Each cause is fixed here; the audit itself
   (scripts/qa_hit_audit.js) is unchanged.
   1. .scene-hero-scrim is bottom-aligned (justify-content: flex-end) and could not scroll, so on a short screen a long
      list of answers grew UPWARD under the header and the first answers could not be reached (SMS-021 at 740x360, 667x375).
      The first child now carries margin-top:auto, which keeps the bottom alignment while the content is short and lets
      the scrim scroll from its top once the content is taller than the screen.
   2. On a short landscape phone the two feedback tabs (44px, side by side, bottom-right) lay over the last answer
      (SMS-006, SMS-014). They now stand in a column at the right edge and the panels keep a right margin of their own.
   3. On a large desktop the Resolution panel (62vh) scrolls, and "Back to Site Walk" sat below its visible area
      (SMS-043 at 1886x940). The button row now sticks to the bottom of the panel. */
.scene-hero-scrim {
  justify-content: flex-start;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.scene-hero-scrim > :first-child { margin-top: auto; }

@media (orientation: landscape) and (max-height: 520px) {
  .feedback-widget { right: 6px; bottom: 6px; }
  .corner-tabs { flex-direction: column; gap: 4px; }
  .feedback-tab { width: 40px; height: 40px; font-size: 16px; }
  /* Question and answers side by side, so a six-answer decision still fits a 375px-high screen without scrolling. */
  .scene-hero-scrim {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
    column-gap: 14px;
    align-items: end;
    padding: 8px 56px 8px 14px;
  }
  .scene-hero-scrim > :first-child { margin-top: 0; }
  .scene-hero-scrim .scene-hero-caption { font-size: 13px; line-height: 1.35; max-width: none; }
  .scene-hero-scrim .scene-hero-caption .scene-hero-context { font-size: 12px; margin-top: 4px; }
  .scene-hero-scrim .scene-action-panel { margin-top: 0; align-self: end; }
  .scene-hero-scrim .scene-action-panel .phase-label { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.75); margin-bottom: 4px; }
  .scene-hero-scrim .timer-bar-wrap { margin-bottom: 6px; }
  .scene-hero-scrim .option-grid { gap: 5px; }
  .scene-hero-scrim .option-btn { padding: 6px 10px; font-size: 12.5px; line-height: 1.25; }
}

@media (min-width: 481px) and (min-height: 521px) {
  /* Resolution screen only: the panel gets more of a tall screen, and its button row stays on the bottom edge of the
     panel (on a dark bar, so the text that scrolls under it stays readable) however long the text is. */
  .opening-overlay-bottom:has(> .result-panel) { max-height: 78vh; }
  .opening-overlay-bottom:has(> .result-panel) > .scene-action-panel:last-child {
    position: sticky;
    bottom: 0;
    padding-top: 10px;
    background: linear-gradient(to top, rgba(0,0,0,0.86) 70%, rgba(0,0,0,0));
  }
}

/* ---- Inspect and mark evidence (Stage 1, single player, 2026-10-18) ----
   The picture sits in .inspect-pic and the hotspots in .inspect-hot-layer: two layers with exactly the picture's box
   (set by layoutInspection in script.js), so a hotspot stays on its object at every screen size. The rest of the screen
   is the same photo, blurred. HUD, text panel, buttons and the little site manager are the normal World Mode ones. */
.inspect-screen .inspect-fill { filter: blur(24px) brightness(0.62); transform: scale(1.12); }
.inspect-pic, .inspect-hot-layer { position: absolute; pointer-events: none; }
.inspect-pic img { display: block; width: 100%; height: 100%; user-select: none; -webkit-user-drag: none; }
.inspect-hot-layer > * { pointer-events: auto; }
.inspect-hot-layer .opening-hotspot { transform: translate(-50%, -13px); }   /* the dot, not the middle of dot and label, sits on the point */
.inspect-chip {
  display: inline-block;
  margin-top: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.4);
  font-size: 12px;
  text-shadow: none;
  letter-spacing: normal;
  text-transform: none;
}
.inspect-bottom { max-width: 760px; }
.inspect-text { box-sizing: border-box; }
.inspect-tray { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 8px; color: #fff; }
.inspect-tray-hint { flex: 1 1 100%; font-size: 12px; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7); }
.inspect-crop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 4px;
  background: rgba(0, 0, 0, 0.45);
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-radius: 8px;
  color: #fff;
  font-size: 11px;
  cursor: pointer;
}
.inspect-crop .inspect-crop-img { display: block; height: 46px; background-repeat: no-repeat; border-radius: 4px; }
.inspect-crop[aria-pressed="true"] { border-color: var(--accent); background: rgba(0, 0, 0, 0.7); }
.inspect-crop:disabled { opacity: 0.55; cursor: not-allowed; }
.inspect-crop.locked .inspect-crop-img { background-image: none !important; background: rgba(255, 255, 255, 0.12); display: flex; align-items: center; justify-content: center; min-width: 60px; }
.inspect-crop.locked .inspect-crop-img::after { content: "🔒"; font-size: 16px; }
/* Short landscape screens (phones held sideways): the text panel becomes a column on the right, the HUD sits top-left, and the
   picture gets the rest. A stacked HUD + text + buttons would leave the picture a few pixels high. */
@media (orientation: landscape) and (max-height: 520px) {
  .inspect-screen .opening-overlay {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(250px, 46%);
    grid-template-rows: auto 1fr;
    column-gap: 10px;
    padding: 8px 10px;
  }
  .inspect-screen .opening-time { grid-column: 1; grid-row: 1; }
  .inspect-screen .opening-time .clock { font-size: 18px; }
  .inspect-screen .opening-time .place { font-size: 10px; margin-top: 0; letter-spacing: 1px; }
  .inspect-chip { margin-top: 3px; font-size: 11px; padding: 2px 8px; }
  .inspect-screen .opening-overlay-bottom.inspect-bottom { grid-column: 2; grid-row: 1 / span 2; align-self: end; max-width: none; max-height: 100%; gap: 6px; padding: 0 56px 0 0; }
  .inspect-text { font-size: 12px; padding: 6px 9px; }
  .inspect-crop .inspect-crop-img { height: 30px; }
  .inspect-crop { font-size: 10px; padding: 3px; }
  .inspect-tray { gap: 6px; }
  .inspect-bottom .scene-action-panel .btn-primary, .inspect-bottom .scene-action-panel .btn-secondary { padding: 7px 12px; font-size: 13px; }
}

/* ---- Multiplayer (2026-10-18): menu, lobby, Team panel, shared team scene. All text from the server is set with textContent. ---- */
.mp-overlay { align-items: center; justify-content: center; }
.mp-card { background: rgba(14, 14, 14, 0.82); color: #fff; border-radius: 12px; padding: 18px 20px; width: min(440px, 100%); max-height: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; box-sizing: border-box; pointer-events: auto; }
.mp-card h2 { margin: 0; font-size: 20px; }
.mp-lead { margin: 0; font-size: 13.5px; line-height: 1.4; opacity: 0.92; }
.mp-small { font-size: 12px; opacity: 0.85; }
.mp-input { background: #fff; color: #111; border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; font-size: 15px; min-width: 0; flex: 1 1 auto; box-sizing: border-box; width: 100%; }
.mp-code { text-transform: uppercase; letter-spacing: 2px; }
.mp-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mp-or { font-size: 12px; opacity: 0.8; text-align: center; }
.mp-error { min-height: 1.2em; margin: 0; color: #ffb4a8; font-size: 13px; }
.mp-codebox { display: flex; flex-direction: column; gap: 2px; background: rgba(255,255,255,0.08); border-radius: 8px; padding: 8px 12px; }
.mp-label { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; opacity: 0.8; }
.mp-bigcode { font-size: 28px; letter-spacing: 4px; font-family: ui-monospace, Consolas, monospace; }
.mp-members { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.mp-member { display: flex; align-items: center; gap: 8px; font-size: 14px; flex-wrap: wrap; }
.mp-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; flex: 0 0 auto; border: 1px solid rgba(255,255,255,0.7); }
.mp-dot.off { opacity: 0.35; }
.mp-badge { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; border: 1px solid var(--accent); border-radius: 999px; padding: 1px 6px; }
.mp-away, .mp-loc { font-size: 11px; opacity: 0.75; margin-left: auto; }
.mp-kick { background: transparent; border: 1px solid rgba(255,255,255,0.4); color: #fff; border-radius: 6px; width: 28px; height: 28px; cursor: pointer; }
.mp-chat { background: rgba(255,255,255,0.07); border-radius: 8px; padding: 8px; height: 140px; overflow-y: auto; font-size: 13px; display: flex; flex-direction: column; gap: 3px; }
.mp-msg { overflow-wrap: anywhere; }
.mp-who { font-weight: 700; }
.mp-empty { opacity: 0.7; font-size: 12.5px; }
.mp-back { align-self: flex-start; }
.mp-team-btn { width: auto !important; padding: 0 10px; font-size: 13px; white-space: nowrap; }
.mp-team-btn.mp-off { opacity: 0.55; }
/* Narrow phones: the header button shows only the icon before a session, otherwise it squeezes the brand name onto two lines (opening-screen header was 59 px at 360/375 and 80 px at 320, found 2026-10-18). The opening-screen Multiplayer hotspot stays the main entry. */
@media (max-width: 400px) { .mp-team-btn .mp-label { display: none; } }
/* Very small phones (320 px): not even the icon button fits beside the full brand name, so the name steps aside, the same way the shift HUD already does at 640 px and below. */
@media (max-width: 340px) { body:has(#mp-team-btn) .brand-name { display: none; } }
/* Short landscape screens: the support link moves to the free top-right corner so it never reaches the Site Office hotspot. */
@media (orientation: landscape) and (max-height: 420px) { .opening-support { position: absolute; top: 12px; right: 16px; margin: 0; text-align: right; text-shadow: 0 1px 3px rgba(0,0,0,0.6); } }
.mp-drawer { position: fixed; top: 56px; right: 8px; width: min(360px, calc(100vw - 16px)); max-height: calc(100vh - 70px); overflow-y: auto; z-index: 600; background: rgba(14, 14, 14, 0.94); color: #fff; border-radius: 12px; padding: 12px; box-sizing: border-box; display: flex; flex-direction: column; gap: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.5); }
.mp-dhead { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.mp-x { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,0.4); border-radius: 6px; width: 30px; height: 30px; cursor: pointer; }
.mp-tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.mp-tab { background: rgba(255,255,255,0.1); color: #fff; border: 1px solid transparent; border-radius: 8px; padding: 6px 10px; font-size: 12.5px; cursor: pointer; }
.mp-tab.on { border-color: var(--accent); background: rgba(255,255,255,0.2); }
.mp-pane { display: flex; flex-direction: column; gap: 8px; }
.mp-here { position: absolute; left: 12px; top: 84px; z-index: 5; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; background: rgba(0,0,0,0.55); color: #fff; border-radius: 999px; padding: 4px 10px; font-size: 12px; max-width: calc(100% - 24px); }
.mp-chip { display: inline-flex; align-items: center; gap: 5px; }
.mp-board { display: flex; gap: 6px; flex-wrap: wrap; }
.mp-evi { background: rgba(0,0,0,0.5); color: #fff; border: 1px solid rgba(255,255,255,0.4); border-radius: 999px; padding: 3px 10px; font-size: 12px; cursor: pointer; display: inline-flex; gap: 6px; align-items: center; }
.mp-dots { display: inline-flex; gap: 3px; margin-right: 6px; }
.mp-pin { position: absolute; transform: translate(-50%, -50%); width: 22px; height: 22px; border-radius: 50%; color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.6); pointer-events: none; }
.mp-vote { position: absolute; right: 8px; bottom: 8px; width: min(420px, calc(100% - 16px)); max-height: calc(100% - 70px); overflow-y: auto; z-index: 8; background: rgba(14,14,14,0.94); color: #fff; border-radius: 12px; padding: 12px; box-sizing: border-box; display: flex; flex-direction: column; gap: 8px; }
.mp-opt { display: flex; align-items: center; gap: 8px; }
.mp-vrow { display: flex; align-items: center; gap: 8px; }
@media (orientation: landscape) and (max-height: 520px) {
  .mp-chat { height: 90px; }
  .mp-card { padding: 12px 14px; gap: 6px; }
  .mp-here { top: 64px; }
}
/* Multiplayer: buttons readable on the dark cards, and the vote panel clear of the feedback tabs */
.mp-card .btn-secondary, .mp-drawer .btn-secondary, .mp-vote .btn-secondary { background: rgba(255, 255, 255, 0.14); color: #fff; border: 1px solid rgba(255, 255, 255, 0.55); }
.mp-card .btn-secondary:hover, .mp-drawer .btn-secondary:hover { background: rgba(255, 255, 255, 0.24); }
.mp-card button:disabled { opacity: 0.5; cursor: not-allowed; }
.mp-vote { bottom: 68px; background: rgb(14, 14, 14); }
@media (orientation: landscape) and (max-height: 520px) {
  .mp-vote { bottom: 8px; right: 56px; width: min(420px, calc(100% - 72px)); }
}
.opening-time .mp-here { position: static; display: inline-flex; margin-top: 6px; text-shadow: none; }
@media (orientation: landscape) and (max-height: 520px) { .opening-time .mp-here { margin-top: 3px; font-size: 11px; padding: 2px 8px; } }

/* Multiplayer avatars: the same little site manager for every player, a ring and a name in the player's colour. They never take taps. */
.mp-avatar { position: absolute; transform: translate(-50%, -100%); z-index: 4; pointer-events: none; display: flex; flex-direction: column; align-items: center; transition: left 0.45s ease, top 0.45s ease; }
.mp-av-emoji { font-size: 30px; line-height: 1; filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.5)); border-radius: 50%; box-shadow: 0 0 0 3px var(--mp-color, #fff); background: rgba(0, 0, 0, 0.25); padding: 1px 2px; }
.mp-av-name { margin-top: 3px; font-size: 11px; font-weight: 700; color: #fff; background: var(--mp-color, #444); border-radius: 999px; padding: 1px 7px; white-space: nowrap; max-width: 110px; overflow: hidden; text-overflow: ellipsis; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); }
.walkway-sprite.mp-self { border-radius: 50%; box-shadow: 0 0 0 3px var(--mp-color, #fff); }
@media (prefers-reduced-motion: reduce) { .mp-avatar { transition: none; } }

/* The Multiplayer entry on the opening screen: bigger than the other hotspots, always readable (Andris, 2026-10-18). */
.opening-hotspot[data-action="multiplayer"] { min-width: 120px; min-height: 96px; }
.opening-hotspot[data-action="multiplayer"] .photo-hotspot-dot { width: 46px; height: 46px; border-width: 3px; }
.opening-hotspot[data-action="multiplayer"] .opening-hotspot-label { font-size: 16px; padding: 7px 16px; max-width: none; white-space: nowrap; opacity: 1; transform: none; background: rgba(0, 0, 0, 0.62); }
@media (hover: none) { .opening-hotspot[data-action="multiplayer"] .photo-hotspot-dot { opacity: 1; } }
.mp-warn { color: #ffd59a; }

/* Phones: 100vh includes the browser toolbar, so a full-height scene pushed the page down and hid the header (and the Team button) (found on a real iPhone, 2026-10-18).
   100dvh follows the visible area. Browsers without dvh keep the old rule. */
@supports (height: 100dvh) {
  body.world-mode .opening-screen, body.world-mode .scene-hero { height: calc(100dvh - 52px); }
}
/* the player's own figure carries a small "You" tag in the player's colour */
.walkway-sprite.mp-self::after { content: "You"; position: absolute; left: 50%; top: 100%; transform: translateX(-50%); margin-top: 2px; font-size: 10px; font-weight: 700; line-height: 1; color: #fff; background: var(--mp-color, #444); border-radius: 999px; padding: 2px 6px; white-space: nowrap; pointer-events: none; }

/* Walkway: where the teammates are (a name tag above the hotspot of their location). Never takes taps. */
.mp-there { position: absolute; transform: translate(-50%, -100%); z-index: 4; display: flex; flex-direction: column; align-items: center; gap: 2px; pointer-events: none; margin-top: -6px; }
.mp-there-tag { font-size: 11px; font-weight: 700; color: #fff; border-radius: 999px; padding: 2px 8px; white-space: nowrap; max-width: 150px; overflow: hidden; text-overflow: ellipsis; border: 1px solid rgba(255, 255, 255, 0.85); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5); }

/* Multiplayer entry: a clear, informative call to action (Andris, 2026-10-18: "you want your friends to join") */
.opening-hotspot[data-action="multiplayer"] .opening-hotspot-label { display: flex; flex-direction: column; align-items: center; gap: 1px; font-size: 19px; padding: 9px 20px; border: 2px solid rgba(255, 255, 255, 0.85); border-radius: 16px; background: rgba(0, 0, 0, 0.7); }
.opening-hotspot[data-action="multiplayer"] .opening-hotspot-label small { font-size: 13px; font-weight: 600; opacity: 0.95; }
.opening-hotspot[data-action="multiplayer"] .photo-hotspot-dot { width: 54px; height: 54px; }

/* Speech bubble above an avatar that is talking (multiplayer chat). Never takes taps. */
.mp-bubble { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 6px; background: #fff; color: #111; font-size: 12.5px; font-weight: 500; line-height: 1.25; border-radius: 12px; padding: 6px 10px; width: max-content; max-width: 180px; text-align: left; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45); pointer-events: none; z-index: 6; }
.mp-bubble::after { content: ""; position: absolute; left: 50%; top: 100%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: #fff; }

/* "is writing": three dots in a bubble over the avatar (not a sent message) */
.mp-typing { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 6px; background: #fff; border-radius: 12px; padding: 7px 10px; display: flex; gap: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45); pointer-events: none; z-index: 6; }
.mp-typing i { width: 6px; height: 6px; border-radius: 50%; background: #666; animation: mp-dot 1.1s infinite ease-in-out; }
.mp-typing i:nth-child(2) { animation-delay: 0.18s; } .mp-typing i:nth-child(3) { animation-delay: 0.36s; }
@keyframes mp-dot { 0%, 80%, 100% { opacity: 0.25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .mp-typing i { animation: none; opacity: 0.7; } }

/* ---------- Illustrative-image caption (Andris 2026-10-18 requirement, shipped 2026-10-20; Čaks: one short line on the picture) ----------
   Every picture the game shows gets the same small caption in its top-right corner, drawn as a pseudo-element of the picture itself, so it sits
   BELOW the overlay, the living-scene layer and every hotspot (DOM order, no z-index) and can never block a tap. Real scene pictures are
   .opening-hero.has-real-art (its skyline ::after is switched off for real art above, so ::after is free for this), scenario scenes .scene-hero,
   office scenes .desk-scene, .site-photo-scene, inspection pictures .inspect-pic and scenario media. On small screens the text shortens to one
   short line so it stays clear of hotspot dots near the top (Block A crane, Walkway scaffold); the full sentence is also in the footer note. */
:root { --caption-text: "Illustrative image. Real sites and conditions vary."; }
@media (max-width: 480px), (max-height: 430px) { :root { --caption-text: "Illustrative image"; } }
.desk-scene.has-real-art, .scenario-media { position: relative; }
.opening-hero.has-real-art::after,
.scene-hero::after,
.site-photo-scene.has-real-art::after,
.desk-scene.has-real-art::after,
.scenario-media:not(.empty)::after,
.inspect-pic::after {
  display: block;
  content: var(--caption-text);
  position: absolute;
  top: 8px;
  right: 8px;
  left: auto;
  bottom: auto;
  width: auto;
  height: auto;
  max-width: calc(100% - 16px);
  white-space: nowrap;
  padding: 3px 8px;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.25;
  text-align: right;
  border-radius: 8px;
  clip-path: none;
  opacity: 1;
  pointer-events: none;
}
/* Landscape phones, Walkway only: the Scaffold dot sits in the top-right corner, so the short caption is centred at the top of this one scene (found by scripts/gate_caption.js; every other scene keeps the corner). */
@media (max-height: 430px) {
  .opening-screen[data-world-location="walkway"] .opening-hero.has-real-art::after {
    right: auto;
    left: 50%;
    transform: translateX(-50%);
  }
}

/* Living hotspots (2026-10-25, guideline P2): presentation states for the material-shortage chain. They only restyle the dot and add
   a badge; sizes and positions do not change, so hit-testing is unchanged. Meaning is never colour alone: each state has its own
   badge symbol (! new, X escalated, tick dealt with) and the label or aria-label says it in words. One restrained pulse for 'active',
   a one-off arrival animation the first time it is drawn, nothing animated for escalated or resolved. */
.opening-hotspot.world-hotspot.hs-active .photo-hotspot-dot { background: rgba(245,166,35,0.95); border-color: #6b4200; animation: hs-pulse 1.8s ease-in-out infinite; }
.opening-hotspot.world-hotspot.hs-new .photo-hotspot-dot { animation: hs-arrive 0.7s ease-out 1, hs-pulse 1.8s 0.7s ease-in-out infinite; }
.opening-hotspot.world-hotspot.hs-escalated .photo-hotspot-dot { background: rgba(229,72,77,0.95); border-color: #5c0f12; box-shadow: 0 0 0 5px rgba(229,72,77,0.35); animation: none; }
.opening-hotspot.world-hotspot.hs-resolved .photo-hotspot-dot { background: rgba(47,143,91,0.9); border-color: #14472d; animation: none; }
.world-hotspot .hs-badge { position: absolute; top: -9px; right: -9px; min-width: 20px; height: 20px; padding: 0 3px; border-radius: 999px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; line-height: 1; color: #fff; background: #8a5a00; border: 2px solid rgba(255,255,255,0.92); pointer-events: none; }
.world-hotspot.hs-escalated .hs-badge { background: #b3352f; }
.world-hotspot.hs-resolved .hs-badge { background: #2f8f5b; }
/* The three states are always labelled with a colour pill (not hover-only): a new or escalated problem must be noticeable without
   hovering, and the pill carries the symbol and words as well as the colour. (Concept direction 2026-10-25: colourful, little text.) */
.world-hotspot.hs-active .opening-hotspot-label, .world-hotspot.hs-escalated .opening-hotspot-label, .world-hotspot.hs-resolved .opening-hotspot-label { opacity: 1; transform: none; text-shadow: none; box-shadow: 0 2px 6px rgba(0,0,0,0.35); }
.world-hotspot.hs-active .opening-hotspot-label { background: #b87400; }
.world-hotspot.hs-escalated .opening-hotspot-label { background: #b3352f; }
.world-hotspot.hs-resolved .opening-hotspot-label { background: #2a8a55; }
/* Short landscape phones: the Block A shortage pill would sit under the "Look around" button, so only its dot and symbol badge show there
   (the aria-label still says it in words). Everywhere else the pill is shown. */
@media (max-height: 420px) {
  .world-hotspot.hs-short-nolabel .opening-hotspot-label { display: none; }
  /* and its dot moves above the bottom button row, where "Look around" would otherwise cover it (found by gate_locations at 568x320) */
  .world-hotspot.hs-short-nolabel { left: 12% !important; top: 66% !important; }
}
/* Header score feedback (2026-10-25): a short ring when the world really changed the score (e.g. an ignored event escalated). The
   notice names the numbers in words, so the colour is never the only signal. */
.score-panel.score-hit { animation: score-hit 1s ease-out 1; }
.score-panel.score-gain { animation: score-gain 1s ease-out 1; }
@keyframes score-hit { 0% { box-shadow: 0 0 0 0 rgba(229,72,77,0.7); } 40% { box-shadow: 0 0 0 7px rgba(229,72,77,0.45); } 100% { box-shadow: 0 0 0 0 rgba(229,72,77,0); } }
@keyframes score-gain { 0% { box-shadow: 0 0 0 0 rgba(47,143,91,0.7); } 40% { box-shadow: 0 0 0 7px rgba(47,143,91,0.45); } 100% { box-shadow: 0 0 0 0 rgba(47,143,91,0); } }
@media (prefers-reduced-motion: reduce) { .score-panel.score-hit, .score-panel.score-gain { animation: none; } }
@keyframes hs-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }
@keyframes hs-arrive { 0% { transform: scale(0.2); opacity: 0; } 70% { transform: scale(1.4); opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .opening-hotspot.world-hotspot.hs-active .photo-hotspot-dot, .opening-hotspot.world-hotspot.hs-new .photo-hotspot-dot { animation: none; }
}

/* URGENT NOTICES (Čaks's GO 2026-10-25): a real fired world event nobody has dealt with, shown on every world screen except its own place.
   Meaning is never colour alone: warning symbol, the place in words, the event in words and the trip time. The strip itself ignores taps (only the
   notices take them) so it can never block a hotspot; scripts/gate_urgent.js hit-tests it on every world screen at three viewports. */
.urgent-strip { position: fixed; top: 58px; left: 50%; transform: translateX(-50%); z-index: 45; display: flex; flex-direction: column; align-items: center; gap: 6px; width: max-content; max-width: min(94vw, 560px); pointer-events: none; }
.urgent-pill { pointer-events: auto; display: inline-flex; align-items: center; gap: 8px; max-width: 100%; box-sizing: border-box; padding: 6px 12px; border-radius: 999px; border: 2px solid #b36b00; background: #ffd27a; color: #3a2300; font: 600 13px/1.2 inherit; cursor: pointer; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35); animation: urgent-in 0.35s ease-out; }
.urgent-pill:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
.urgent-icon { font-size: 14px; line-height: 1; }
.urgent-where { font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; font-size: 11px; white-space: nowrap; }
.urgent-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.urgent-go { margin-left: 2px; padding: 2px 8px; border-radius: 999px; background: #3a2300; color: #ffd27a; font-size: 11px; white-space: nowrap; }
.urgent-more { pointer-events: none; font-size: 12px; color: #fff; background: rgba(0, 0, 0, 0.62); border-radius: 999px; padding: 2px 10px; }
.urgent-strip.compact .urgent-text { display: none; }
.urgent-strip.single .urgent-pill:nth-of-type(n+2) { display: none; }
@keyframes urgent-in { from { transform: translateY(-8px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .urgent-pill { animation: none; } }
@media (max-height: 420px) { .urgent-strip { top: 50px; } .urgent-pill { padding: 3px 10px; font-size: 12px; } .urgent-icon { display: none; } }

/* HELP: "What can I do now?" (interface literacy for school-age players, 2026-10-26): plain words, never a hint about evidence. */
.help-panel { max-height: calc(100dvh - 120px); overflow: auto; }
.help-list { margin: 8px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.4; }
.help-list li { margin-bottom: 6px; }

/* PACE QUESTION: one small non-modal question when attention first reaches zero (2026-10-26). */
.pace-prompt { position: fixed; right: 8px; bottom: 64px; z-index: 46; max-width: min(260px, calc(100vw - 16px)); padding: 10px 12px; background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); font-size: 13px; line-height: 1.35; }
.pace-q { margin-bottom: 8px; }
.pace-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.pace-btns button { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; font: inherit; }
@media (orientation: landscape) and (max-height: 520px) { .pace-prompt { right: 56px; bottom: 6px; max-width: min(260px, calc(100vw - 72px)); } }
.pace-prompt.compact { padding: 6px 8px; max-width: min(230px, calc(100vw - 16px)); } .pace-prompt.compact .pace-long { display: none; } .pace-prompt.compact .pace-q { margin-bottom: 4px; } .pace-prompt.compact .pace-btns button { padding: 4px 10px; }

/* Drawing sheet and viewer (2026-10-26, PILOT-39: the drawing against the built wall).
   The sheet lies on the office backdrop between the clock and the text panel and takes whatever height is left (min-height 0, so on a short
   landscape phone it can shrink to nothing: the "Read the drawing" button in the action panel is always there). The viewer is a modal dialog in
   <body>, outside #app, so a re-render of the screen cannot wipe it. */
.doc-sheet {
  flex: 1 1 0;
  min-height: 0;
  margin: 8px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: none;
  border: 0;
  padding: 0;
  cursor: zoom-in;
}
.doc-sheet img {
  max-width: 100%;
  max-height: 100%;
  background: #fff;
  box-shadow: 0 10px 28px rgba(0,0,0,0.55);
  transform: rotate(-0.6deg);
  pointer-events: none;
}
.doc-sheet:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.doc-viewer {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  background: #1b1b1b;
  color: #fff;
  overscroll-behavior: contain;
}
.doc-viewer-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  background: #000;
}
.doc-viewer-title { font-weight: 700; font-size: 14px; }
.doc-viewer-tools { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.doc-viewer-btn {
  min-width: 44px;
  min-height: 44px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.5);
  background: rgba(255,255,255,0.1);
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  padding: 0 12px;
  cursor: pointer;
}
.doc-viewer-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.doc-viewer-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  background: #2a2a2a;
}
.doc-viewer-stage:active { cursor: grabbing; }
.doc-viewer-stage img {
  position: absolute;
  left: 0;
  top: 0;
  max-width: none;
  transform-origin: 0 0;
  user-select: none;
  -webkit-user-drag: none;
  background: #fff;
}
.doc-viewer-help { flex: 0 0 auto; padding: 6px 12px; font-size: 12px; opacity: 0.8; background: #000; }
@media (max-height: 420px) {
  .doc-viewer-bar { padding: 4px 8px; }
  .doc-viewer-help { display: none; }
}

/* The drawing stays one tap away on the wall screen: a pill under the clock, outside the scrolling text panel, so on a short screen it never pushes an option out of view. */
.doc-reopen-pill {
  display: inline-flex;
  align-items: center;
  margin-top: 6px;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 22px;
  border: 1px solid rgba(255,255,255,0.5);
  background: rgba(0,0,0,0.55);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  text-shadow: none;
  cursor: pointer;
}
.doc-reopen-pill:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
