/* Doctor's Orders — glassmorphic medical-monitor theme, 9:16 portrait shell */
:root {
  --bg: #0A0E14;
  --bg2: #0d1826;
  --card: rgba(18, 28, 38, .55);
  --card-solid: rgba(13, 22, 34, .92);
  --accent: #2DD4BF;
  --accent-cyan: #22D3EE;
  --glass-border: rgba(45, 212, 191, .25);
  --glass-border-hi: rgba(45, 212, 191, .5);
  --ok: #22C55E;
  --warn: #FBBF24;
  --crit: #FB4D6A;
  --ink: #EAF6FB;
  --muted: #93A7BA;
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, monospace;
  --hud-h: 48px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body, #app { height: 100%; }
body {
  background: radial-gradient(120% 100% at 50% 0%, var(--bg2) 0%, var(--bg) 60%);
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  /* The .stage below is the ONLY scroll surface — every screen scrolls internally
     (#app > div, see below). Hidden here so a host WebView that doesn't support
     page-level scroll (see .path-chart-screen's old comment) never has to. */
  overflow: hidden;
}

/* Subtle ambient glow behind everything — off under reduced-motion */
body::before {
  content: '';
  position: fixed;
  inset: -10% -10% auto -10%;
  height: 60vh;
  background: radial-gradient(50% 50% at 50% 0%, rgba(45, 212, 191, .10), transparent 70%);
  pointer-events: none;
  z-index: 0;
  animation: bg-glow 9s ease-in-out infinite alternate;
}
@keyframes bg-glow { from { opacity: .55; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; opacity: .8; }
}

/* Fixed full-bleed background photo, behind the glass UI. Scrim baked into the
   linear-gradient layer that main.js prepends to the image URL so text stays legible. */
.app-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Stage — the actual playable/touchable frame: capped to the viewport (100dvh, not
   min-height, so it can never grow past the WebView) and centered to a 9:16 max-width, same
   scale-to-viewport idea as music-mogul's #game-root/#game-scaler. #app-bg stays a separate
   position:fixed full-bleed layer behind this (untouched by the stage's own width cap) so the
   background photo/scrim still bleeds edge-to-edge on wider viewports. #hud-bar/#pause-overlay
   are positioned children of THIS (not #app), so #app's own per-screen innerHTML rebuilds
   (see Renderer.js/each screen's render()) never wipe them out — see Hud.js. overflow:hidden
   is the safety net: every screen scrolls internally (#app > div, below), so nothing here
   should ever need to escape the stage box, but a stray overflow shouldn't leak past it either. */
.stage {
  position: relative;
  z-index: 1;
  height: 100dvh;
  max-width: min(100vw, calc(100dvh * 9 / 16));
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
}

#app {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  justify-content: center;
}

/* 9:16 portrait shell — every screen's root div lands here. height:100% (of the bounded
   .stage, via #app) + overflow-y:auto makes EVERY screen its own scroll container by
   default — a screen taller than the stage scrolls to its last item instead of just
   overflowing uncontained (the old `flex:1` on a `min-height:100dvh` #app let tall content
   grow the whole page instead of scrolling, and relied on the host allowing page-level
   scroll, which not every embedding host does). padding-top reserves space for the
   persistent HUD bar (--hud-h, see Hud.js/.game-hud) on top of the usual safe-area gutter;
   .splash-screen below overrides padding to 0 (HUD is hidden on SPLASH). */
#app > div {
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding:
    calc(20px + var(--hud-h) + var(--sat, env(safe-area-inset-top, 0px)))
    max(16px, var(--sar, env(safe-area-inset-right, 0px)))
    calc(20px + var(--sab, env(safe-area-inset-bottom, 0px)))
    max(16px, var(--sal, env(safe-area-inset-left, 0px)));
  max-width: min(100vw, calc(100dvh * 9 / 16));
  width: 100%;
  margin: 0 auto;
}

h1 { font-size: 26px; text-align: center; }
h2 { font-size: 19px; font-weight: 700; }
h3 { font-size: 15px; font-weight: 700; }
p { color: var(--muted); line-height: 1.5; font-size: 14px; }

.eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--accent);
}
.mono { font-family: var(--font-mono); }

/* ---------- Glass grammar ---------- */
.glass-card {
  background: var(--card);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--glass-border);
  border-radius: 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, .35), 0 0 20px rgba(45, 212, 191, .06);
  position: relative;
  overflow: hidden;
  padding: 16px;
}
.glass-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 50%;
  background: linear-gradient(180deg, rgba(255, 255, 255, .06), transparent);
  pointer-events: none;
}

/* PRIORITY FIX — Close/action buttons cropped in small confirm/overlay cards (e.g. the
   Hospital upgrade confirm card). .glass-card's overflow:hidden exists ONLY to clip
   ::before's sharp top corners to the card's own rounded corners — it has no other job
   (background/border/shadow/radius all paint correctly without it). But it also clips ANY
   real content that ends up flush with the card's bottom edge, and these compact
   flex-column cards put an actual actionable button (Close/Purchase, Resume/Quit,
   Administer/Prepare) as the LAST child, right against that rounded bottom corner — so on
   any layout pass where the card's flex-computed box is a hair tighter than its content
   (WebKit's overflow+border-radius+backdrop-filter clip rect is a known trouble spot on
   flex items), the button's bottom edge gets shaved off. Fix: stop clipping content on
   these specific small cards, and give ::before its OWN matching top radius instead, so the
   shine still looks right without depending on the parent's overflow to trim it. */
.hospital-confirm,
.pause-card,
.protocol-tracker,
.test-row {
  overflow: visible;
}
.hospital-confirm::before,
.pause-card::before,
.protocol-tracker::before,
.test-row::before {
  border-radius: 16px 16px 0 0;
}

/* ---------- Buttons ---------- */
button {
  font: inherit;
  color: var(--ink);
  background: var(--card);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--glass-border);
  border-radius: 14px;
  padding: 14px 16px;
  cursor: pointer;
  text-align: left;
}
button:active { transform: scale(.96); }
button:disabled,
button.used {
  opacity: .35;
  pointer-events: none;
}

.btn-primary, .btn-secondary {
  align-self: center;
  min-width: 220px;
  text-align: center;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 13px;
}
.btn-primary {
  border-color: var(--glass-border-hi);
  color: var(--accent);
}
.btn-secondary {
  color: var(--ink);
}

/* ---------- Stat pills ---------- */
.stat-pills-row, .vitals-pills {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.stat-pill {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: rgba(45, 212, 191, .07);
  border: 1px solid var(--glass-border);
  border-radius: 10px;
  padding: 6px 10px;
  flex: 1;
  min-width: 64px;
}
.stat-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
}
.stat-value {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

/* Inline SVG stat icons (CareerHomeScreen's rating/lives-saved/deaths — see statIcons.js),
   glued to the front of .stat-value the same way the token wallet icon already is. Color
   lives on the wrapping span (currentColor) so one shared icon shape can read as
   accent/warn/ok/crit depending on which stat it's paired with. */
.stat-icon { width: 16px; height: 16px; display: block; }
.stat-icon-accent, .stat-icon-warn, .stat-icon-ok, .stat-icon-crit {
  display: inline-flex;
  vertical-align: -3px;
  margin-right: 4px;
}
.stat-icon-accent { color: var(--accent); }
.stat-icon-warn { color: var(--warn); }
.stat-icon-ok { color: var(--ok); }
.stat-icon-crit { color: var(--crit); }

/* ---------- Vitals bar ---------- */
.vitals-bar-track {
  height: 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--glass-border);
  overflow: hidden;
}
.vitals-bar-fill {
  height: 100%;
  border-radius: 6px;
  transition: width .3s ease;
}
.vitals-ok { background: var(--ok); box-shadow: 0 0 10px var(--ok); }
.vitals-warn { background: var(--warn); box-shadow: 0 0 10px var(--warn); }
.vitals-crit { background: var(--crit); box-shadow: 0 0 10px var(--crit); }

/* ---------- Splash (poster loading screen, boots before the menu) ---------- */
/* #app > .splash-screen (id + class) outranks the base #app > div (id + type) rule, so
   this overrides its padding/max-width without an !important. */
#app > .splash-screen {
  /* Inset to the iOS safe area, and paint the poster INSIDE that inset
     (background-origin: content-box). Was `padding: 0`, which let the poster run under
     the notch and the home indicator. */
  padding: var(--sat, env(safe-area-inset-top, 0px)) 0 var(--sab, env(safe-area-inset-bottom, 0px));
  justify-content: flex-end;
  align-items: center;

  /* `contain`, NOT `cover`. cover scales the poster until it FILLS the box and crops
     whatever overflows — on a tall phone that sliced the lettering baked into the artwork.
     contain fits the whole poster inside the safe area, so nothing is ever cut off; the
     leftover strip shows --bg, which is what the poster's own edges blend to anyway. */
  background-size: contain;
  background-origin: content-box;
  background-color: var(--bg);
  background-position: center;
  background-repeat: no-repeat;
  cursor: pointer;
}
.splash-tap-prompt {
  margin-bottom: calc(48px + var(--sab, env(safe-area-inset-bottom, 0px)));
  padding: 12px 30px;
  border-radius: 999px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  font-size: 13px;
  color: var(--accent);
  text-shadow: 0 0 12px rgba(45, 212, 191, .5);
  animation: splash-pulse 1.6s ease-in-out infinite;
}
@keyframes splash-pulse {
  0%, 100% { opacity: .55; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
  .splash-tap-prompt { animation: none; opacity: 1; }
}

/* ---------- Title ---------- */
/* flex-start, not center: with a save-game (Continue/New Game + How to Play + Field
   Manual = 4 stacked buttons) plus the up-to-28vh doctor avatar, this screen can exceed a
   short device's viewport — a centered flex column with overflow content clips/strands the
   TOP of the content (the classic flexbox justify-content:center + overflow:auto bug; every
   browser's default flex fallback alignment is "unsafe", including current WebKit/iOS) since
   scrolling can't reach content pushed off the start edge. Same fix already applied to
   .path-chart-screen / .week-report-screen below for the same reason. */
.title-screen { justify-content: flex-start; }
.title-hero { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.title-hero h1 { margin-top: 4px; }
.title-tagline { color: var(--ink); }
.title-doctor-avatar {
  max-height: 28vh;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, .45));
}
.title-menu { display: flex; flex-direction: column; gap: 10px; align-items: stretch; }

/* ---------- Doctor Select (horizontal carousel) ---------- */
/* flex-start, not center — the portrait alone can run up to 55vh; centered + overflowing
   content can clip/strand the top of the screen on a short device (same justify-content
   overflow issue as .title-screen above). */
.doctor-select-screen { justify-content: flex-start; }
.doctor-carousel { display: flex; align-items: center; gap: 8px; }
.doctor-carousel-arrow {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  line-height: 1;
  padding: 0;
  border-radius: 999px;
  color: var(--accent);
}
.doctor-carousel-stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  min-height: 0;
}
.doctor-carousel-portrait {
  max-height: 55vh;
  max-width: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, .45));
}
.doctor-carousel-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
}
.doctor-carousel-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--glass-border);
}
.doctor-carousel-dot-active {
  background: var(--accent);
  box-shadow: 0 0 8px rgba(45, 212, 191, .7);
}

/* ---------- How to Play ---------- */
.how-to-play-screen { justify-content: center; }
.how-to-card { display: flex; flex-direction: column; gap: 10px; }
.how-to-list {
  color: var(--ink);
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 14px;
  line-height: 1.5;
}

/* ---------- Reference (Field Manual) — ReferenceScreen.js ---------- */
/* This screen had no dedicated rules at all — .reference-card fell back to bare .glass-card
   (fine) but .reference-label rendered as an inline span directly butted against its value
   text with no separating space/margin (e.g. "PresentationSudden onset chest pain…"),
   unreadable rather than merely unstyled. Not an overflow/crop bug, but "content fully
   visible" in spirit — pure additions, nothing here changes any existing layout. */
.reference-list { display: flex; flex-direction: column; gap: 10px; }
.reference-card { display: flex; flex-direction: column; gap: 4px; }
.reference-name { color: var(--ink); font-weight: 700; font-size: 15px; }
.reference-specialty { color: var(--accent); font-size: 11px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; }
.reference-row { font-size: 13px; line-height: 1.4; }
.reference-label { color: var(--muted); font-weight: 700; text-transform: uppercase; font-size: 10.5px; letter-spacing: .5px; margin-right: 6px; }
.reference-dontmiss .reference-label { color: var(--crit); }
/* Medication is the field a player is actually trying to memorise — give it the accent
   so it is findable while scrolling, without competing with the red don't-miss line. */
.reference-med .reference-label { color: var(--accent); }

/* Rarity pills. One tier shown at a time — 36 cards in a single column meant scrolling
   past everything to reach anything, and mid-exam that scroll costs real clock time. */
.reference-intro { color: var(--muted); font-size: 12.5px; margin: -2px 0 4px; }
.reference-pills { display: flex; gap: 6px; margin: 2px 0 6px; }
/* STACKED, so it cannot overflow at ANY width. Measured, not eyeballed — and measured at
   more than one size, which is what the first two attempts got wrong:
     side-by-side label+badge overflowed by 2px at 390px; after trimming the badge border,
     letter-spacing and padding it fit at 390 but STILL overflowed by 6px at 320.
   Tuning numbers until one viewport passes is not a fix. Text cannot shrink to fit, so as
   long as label and count share a line the widest label ("UNCOMMON") sets a hard floor on
   pill width — and a 3-up row at 320px is below it.
   Putting the count on its own line removes the coupling entirely: each line only has to
   fit the longer of one word or one number, so the layout survives any viewport and any
   future tier name. `border-radius: 14px` rather than 999px because a two-line pill is no
   longer a lozenge. */
.reference-pill {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 7px 4px;
  border-radius: 14px;
  background: var(--card);
  border: 1px solid var(--glass-border);
  color: var(--muted);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: .2px;
  text-transform: uppercase;
  line-height: 1.25;
  overflow: hidden; /* belt and braces: clip rather than spill past the rounded edge */
}
.reference-pill-count {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0;
  opacity: .9;
}
/* Active pill takes its tier's colour, so the manual reads as three graded shelves rather
   than three arbitrary tabs: teal = daily, amber = watch for, red = will kill you. */
.reference-pill-active { color: var(--ink); border-color: currentColor; }
.reference-pill-common.reference-pill-active   { color: var(--accent); background: rgba(45, 212, 191, .12); }
.reference-pill-uncommon.reference-pill-active { color: #f0b429;       background: rgba(240, 180, 41, .12); }
.reference-pill-rare.reference-pill-active     { color: var(--crit);   background: rgba(251, 77, 106, .12); }
.reference-tier-blurb { color: var(--muted); font-size: 12px; margin: 0 0 2px; }

/* ---------- Disclaimer ---------- */
.disclaimer-screen { justify-content: center; }
.disclaimer-card { display: flex; flex-direction: column; gap: 8px; }

/* ---------- Intake ---------- */
.intake-card { display: flex; flex-direction: column; gap: 10px; }
.intake-portrait {
  align-self: center;
  max-height: 34vh;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, .45));
}
.intake-complaint, .intake-history { color: var(--ink); }
.emergency-tag {
  align-self: flex-start;
  background: rgba(251, 77, 106, .15);
  border: 1px solid var(--crit);
  color: var(--crit);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 1.5px;
  padding: 3px 10px;
  border-radius: 999px;
  text-transform: uppercase;
}
.intake-hint { border-left: 3px solid var(--warn); display: flex; flex-direction: column; gap: 4px; }
.returning-tag {
  align-self: flex-start;
  background: rgba(251, 191, 36, .15);
  border: 1px solid var(--warn);
  color: var(--warn);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 1px;
  padding: 3px 10px;
  border-radius: 999px;
  text-transform: uppercase;
}

/* ---------- Exam: monitor header ---------- */
/* The whole EXAM screen (monitor + optional chart drawer + action grid/subview) can run
   taller than the 9:16 shell once the chart drawer is open with several entries — every
   screen scrolls internally by default now (#app > div, above), so no per-screen override
   is needed here anymore (plan1.5-refine1 #3). */
/* gap 10 -> 6: the monitor card is the tallest block on the exam screen and every row in
   it is compact, so the inter-row gap was the cheapest vertical space to reclaim without
   shrinking any actual content. Part of getting the whole screen above the fold. */
.exam-monitor { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
/* Header row for the running W/D/rating/saved/deaths strip + the patient portrait thumbnail.
   The portrait used to be position:absolute (top:10/right:10, its own z-index) floating on
   top of this strip's text — on a narrow card the centered text ran directly underneath the
   opaque portrait square and got visually cropped. Now the portrait is a normal flex sibling
   with its own reserved column, so the two can never overlap regardless of text length. */
.exam-monitor-head { display: flex; align-items: flex-start; gap: 10px; }
.career-hud-strip {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  letter-spacing: .5px;
  color: var(--muted);
  text-align: center;
}
/* View Chart + Reference share one row at equal width. flex:1 on both is what keeps them
   the same size regardless of label length ("Close Chart" is wider than "View Chart"). */
.exam-tool-row { display: flex; gap: 8px; }
.exam-tool-row > .btn-secondary { flex: 1; min-width: 0; }

/* 72px -> 48px: on the incoming-patient screen the portrait pushed Begin Exam below the
   fold, so the primary action needed a scroll to reach. The image is a thumbnail, not
   content — shrinking it is the cheapest way to buy that vertical space back. */
.exam-portrait-inset {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 12px;
  object-fit: cover;
  border: 1px solid var(--glass-border-hi);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
}
/* 48px -> 40px with a tight line-height. Still the largest element on the card (it is the
   thing under time pressure) but ~20px shorter, which is most of what let the action grid
   clear the fold. line-height is explicit because the default on a 40px mono glyph adds
   noticeable dead space above and below the digits. */
.exam-clock {
  font-family: var(--font-mono);
  font-size: 40px;
  line-height: 1.05;
  font-weight: 700;
  text-align: center;
  color: var(--accent);
  text-shadow: 0 0 16px rgba(45, 212, 191, .55);
  letter-spacing: 2px;
}
.exam-emergency .exam-clock {
  color: var(--crit);
  text-shadow: 0 0 16px rgba(251, 77, 106, .6);
  animation: clock-pulse .6s ease-in-out infinite alternate;
}
@keyframes clock-pulse { from { opacity: 1; } to { opacity: .5; } }
/* Per-beat flash from the heart-monitor cadence. Shared by EXAM's full clock and
   REASONING's compact one (MonitorDriver drives both the same way — plan1.5-refine1 #1).

   ⚠️ MUST NOT use the `animation` shorthand. This rule and `.exam-emergency .exam-clock`
   target the SAME element, so an `animation` here REPLACES the emergency `clock-pulse`
   outright, and removing the class 180ms later restarts `clock-pulse` from 0%.
   `beatIntervalFor()` accelerates the cadence as the clock drops, so from ~10s the beat
   lands about every 250ms and clock-pulse is re-started before it ever completes a cycle
   — the digits render mid-scale AND mid-opacity-fade at once, which reads on screen as
   two overlapping counters. (The old comment here claimed 250ms was "well clear of the
   180ms flash"; that is true of the class toggling and irrelevant to the property
   collision, which is what actually broke.) Emergency patients only, because that is the
   only case where clock-pulse is applied.

   transform + transition compose with an animation on a different property instead of
   cancelling it, so both effects run and neither restarts the other. */
.exam-clock.beat-pulse, .reasoning-clock.beat-pulse {
  transform: scale(1.1);
  transition: transform .18s ease-out, text-shadow .18s ease-out;
  text-shadow: 0 0 26px rgba(45, 212, 191, .9);
}
@media (prefers-reduced-motion: reduce) {
  .exam-emergency .exam-clock { animation: none; }
  /* beat-pulse is transform/transition now, not an animation — neutralise both. */
  .exam-clock.beat-pulse, .reasoning-clock.beat-pulse { transform: none; transition: none; }
}

/* ---------- Reasoning: compact monitor header (same shared clock as EXAM) ---------- */
.reasoning-monitor { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.reasoning-clock {
  font-family: var(--font-mono);
  font-size: 30px;
  font-weight: 700;
  text-align: center;
  color: var(--accent);
  text-shadow: 0 0 12px rgba(45, 212, 191, .45);
  letter-spacing: 1.5px;
}

.ecg { width: 100%; height: 56px; display: block; overflow: visible; }
.ecg-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 4px rgba(45, 212, 191, .6));
  transform-box: fill-box;
  /* Constant scroll speed across every rhythm tier — only the PQRST points (swapped in
     JS by ExamScreen as time runs down) change shape/density, like a monitor's fixed
     sweep speed showing complexes closer together as heart rate rises. Swapping points
     under a continuously-running transform never restarts or jumps the animation, which
     is what caused the old per-tier animation-duration swap to visibly stutter. */
  animation: ecg-scroll 6s linear infinite;
}
/* Rhythm-tier tint, driven by time remaining (ecg-tachy/ecg-critical classes set in
   ExamScreen) — for emergency patients the effective tier also floors at "tachy" from the
   start (ExamScreen's ecgKindFor), so the trace is never calm-teal at the top of their
   shorter clock; exam-emergency itself just reflects the patient's ER status/clock color. */
.ecg-line.ecg-tachy { stroke: color-mix(in srgb, var(--accent) 65%, var(--warn) 35%); }
.ecg-line.ecg-critical {
  stroke: var(--crit);
  filter: drop-shadow(0 0 5px rgba(251, 77, 106, .65));
}
@keyframes ecg-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .ecg-line { animation: none; }
}

/* ---------- Exam: action grid ---------- */
.action-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.action-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  min-height: 96px;
}
.action-card-wide { grid-column: 1 / -1; flex-direction: row; align-items: center; gap: 12px; min-height: auto; }
.action-icon { font-size: 22px; line-height: 1; }
.action-card-wide .action-icon { font-size: 26px; }
.action-title {
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 13px;
  color: var(--ink);
}
.action-desc { font-size: 11.5px; color: var(--muted); }
/* Starting capability limits (§4) — a locked card is `disabled` (reuses button:disabled's
   grayed-out/non-interactive style above) plus this small badge. */
.action-lock-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--warn);
}
.nurse-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--glass-border-hi);
  flex-shrink: 0;
}
.nurse-card-body { display: flex; flex-direction: column; gap: 2px; }

.nurse-hint-panel { border-left: 3px solid var(--accent-cyan); display: flex; flex-direction: column; gap: 4px; }
.nurse-hint-panel p:last-child { color: var(--ink); }

/* ---------- Exam: sub-view ---------- */
.exam-subview { display: flex; flex-direction: column; gap: 10px; }
.back-btn {
  align-self: flex-start;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  font-size: 12px;
  padding: 8px 14px;
  color: var(--accent);
}
.subview-title { color: var(--ink); }
.subview-list { display: flex; flex-direction: column; gap: 10px; }
.subview-empty { font-style: italic; padding: 4px 2px; }

.test-row { display: flex; flex-direction: column; gap: 8px; }
.test-order-btn { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.test-label { font-weight: 700; text-transform: uppercase; letter-spacing: .4px; font-size: 13px; }
.test-cost { color: var(--muted); font-size: 12px; flex-shrink: 0; }
.accent-panel {
  border-left: 3px solid var(--accent);
  background: rgba(45, 212, 191, .08);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--ink);
}
/* Post-pick feedback only (never shown before a choice is made, so it can't spoil the
   4-option decision) — waste/misleading picks get a visually distinct reveal panel. */
.accent-panel-waste { border-left-color: var(--muted); background: rgba(147, 167, 186, .08); }
.accent-panel-misleading { border-left-color: var(--warn); background: rgba(251, 191, 36, .08); }
.test-pending { color: var(--warn); font-style: italic; padding: 4px 2px; font-size: 13px; }

/* ---------- Differential / Treatment ---------- */
.dx-options, .treatment-options { display: flex; flex-direction: column; gap: 10px; }
.dx-option-btn, .treatment-option-btn { width: 100%; font-weight: 600; }

/* ---------- Protocol Tracker (ordered-treatment regimen — TreatmentScreen.js) ---------- */
/* Heart-monitor themed: each step is a glass row keyed done/available/locked, with a
   distinct "Prep required" chip on craft steps until they're prepared. */
.protocol-tracker { display: flex; flex-direction: column; gap: 8px; padding: 10px; }
.protocol-step {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, .03);
}
.protocol-step-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.protocol-step-label { font-weight: 700; font-size: 13px; color: var(--ink); }

.protocol-step-locked { opacity: .5; }
.protocol-step-available {
  border-color: var(--glass-border-hi);
  background: rgba(45, 212, 191, .08);
  box-shadow: 0 0 14px rgba(45, 212, 191, .12);
}
.protocol-step-done {
  border-color: rgba(34, 197, 94, .4);
  background: rgba(34, 197, 94, .07);
}

.protocol-craft-chip {
  align-self: flex-start;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--warn);
  border: 1px solid rgba(251, 191, 36, .4);
  border-radius: 6px;
  padding: 2px 6px;
}

.protocol-status {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  flex-shrink: 0;
}
.protocol-status-done { color: var(--ok); }
.protocol-status-locked { color: var(--muted); }

.protocol-admin-btn, .protocol-prepare-btn {
  flex-shrink: 0;
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
}
.protocol-admin-btn { border-color: var(--glass-border-hi); color: var(--accent); }
.protocol-prepare-btn { border-color: rgba(251, 191, 36, .5); color: var(--warn); }

/* Last administered step's note — a contraindication reads in --crit with a shake, a clean
   administration reads muted. */
.protocol-note {
  border-left: 3px solid var(--ok);
  background: rgba(34, 197, 94, .08);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--ink);
  font-size: 13px;
}
.protocol-note-error {
  border-left-color: var(--crit);
  background: rgba(251, 77, 106, .1);
  animation: protocol-note-shake .4s ease-in-out;
}
@keyframes protocol-note-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  50% { transform: translateX(5px); }
  75% { transform: translateX(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .protocol-note-error { animation: none; }
}

/* ---------- Outcome ---------- */
.outcome-screen { justify-content: center; }
.outcome-card { display: flex; flex-direction: column; gap: 8px; }
.outcome-card h2 { color: var(--ink); }
.outcome-portrait {
  align-self: center;
  max-height: 30vh;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, .45));
}
/* Affective feedback only (never spoils the exact diagnosis — see OutcomeScreen.js): a
   wrong dx / patient death shakes + glitches the portrait, paired with sfx-wrong; a correct
   dx gets a small positive pulse, paired with sfx-correct. */
.outcome-portrait-wrong { animation: portrait-shake .6s ease-in-out; }
.outcome-portrait-correct { animation: portrait-correct-pulse .6s ease-out; }
@keyframes portrait-shake {
  0% { transform: translateX(0) rotate(0deg) skewX(0deg); }
  10% { transform: translateX(-10px) rotate(-3deg) skewX(-4deg); }
  20% { transform: translateX(9px) rotate(3deg) skewX(4deg); }
  30% { transform: translateX(-8px) rotate(-2deg) skewX(-6deg); }
  40% { transform: translateX(7px) rotate(2deg) skewX(3deg); }
  50% { transform: translateX(-6px) rotate(-3deg) skewX(-3deg); }
  60% { transform: translateX(5px) rotate(1deg) skewX(4deg); }
  70% { transform: translateX(-4px) rotate(-1deg) skewX(-2deg); }
  80% { transform: translateX(3px) rotate(1deg) skewX(2deg); }
  90% { transform: translateX(-2px) rotate(0deg) skewX(-1deg); }
  100% { transform: translateX(0) rotate(0deg) skewX(0deg); }
}
@keyframes portrait-correct-pulse {
  0% { transform: scale(1); }
  30% { transform: scale(1.08); }
  60% { transform: scale(.97); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .outcome-portrait-wrong, .outcome-portrait-correct { animation: none; }
}

/* ---------- Path Chart (case-end fork chart; also reused read-only for case review) ---------- */
/* Long cases (several rounds) run taller than the 9:16 shell — every screen scrolls
   internally by default now (#app > div, above), so the chart (SVG spine + per-round detail
   cards + result + button) always reaches its last item + the footer button. justify-content
   starts at the top (not centered) so a scrolled screen opens on "Case start", not
   mid-chart (plan1.5-refine1 #3). */
.path-chart-screen { justify-content: flex-start; }

/* DEFINITIVE SCROLL FIX (path-chart / case-review / reference / any tall screen):
   #app > div is a flex column with overflow-y:auto. Flex was SHRINKING tall children to fit
   height:100% instead of letting them overflow — and a .glass-card child (.path-chart-card)
   with overflow:hidden then CLIPPED its own bottom, hiding the last reasoning rounds with
   nothing for the container to scroll. flex-shrink:0 on every screen's direct children makes
   tall content overflow so the container's overflow-y:auto scrolls to the very end; and the
   path-chart card shows all its rounds (with the shine still rounded on top). */
#app > div > * { flex-shrink: 0; }
.path-chart-card { overflow: visible; }
.path-chart-card::before { border-radius: 16px 16px 0 0; }
.path-chart-card { display: flex; flex-direction: column; gap: 10px; }
.path-chart-card h2 { color: var(--ink); }
.path-chart-empty { font-style: italic; }

.path-chart-legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 11px; color: var(--muted); }
.legend-item { display: flex; align-items: center; gap: 5px; }
.legend-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.legend-dot-correct { background: var(--accent); box-shadow: 0 0 6px rgba(45, 212, 191, .7); }
.legend-dot-ok { background: var(--ok); box-shadow: 0 0 6px rgba(34, 197, 94, .7); }
.legend-dot-wrong { background: var(--crit); box-shadow: 0 0 6px rgba(251, 77, 106, .7); }
/* The "other" (not chosen, not correct) options in a round's chip list — plan1.5-refine1 #2. */
.legend-dot-muted { background: transparent; border: 1px solid var(--muted); opacity: .6; box-shadow: none; }

.path-chart-svg-wrap { width: 100%; }
.path-chart-svg { width: 100%; height: auto; display: block; overflow: visible; }

/* Connective lines: EKG-style trace. .path-line is the base stroke shared by every
   variant — kind-specific classes only change color/glow. stroke-dasharray/offset are set
   inline by PathChartScreen (actual path length isn't known until measured in JS). */
.path-line { fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.path-line-ok { stroke: var(--ok); filter: drop-shadow(0 0 5px rgba(34, 197, 94, .6)); }
.path-line-correct { stroke: var(--accent); filter: drop-shadow(0 0 5px rgba(45, 212, 191, .55)); }
.path-line-wrong, .path-line-wrong-final { stroke: var(--crit); filter: drop-shadow(0 0 5px rgba(251, 77, 106, .6)); }

.path-node { stroke-width: 2; }
.path-node-start, .path-node-end { fill: var(--card-solid); stroke: var(--accent); }
.path-node-ok { fill: var(--ok); stroke: var(--accent); }
.path-node-wrong { fill: var(--crit); stroke: var(--crit); }
.path-node-correct-ghost { fill: rgba(45, 212, 191, .18); stroke: var(--accent); stroke-dasharray: 2 2; }

.path-node-mark { font-size: 10px; font-weight: 800; text-anchor: middle; font-family: var(--font-sans); pointer-events: none; }
.path-node-mark-ok, .path-node-mark-wrong { fill: #fff; }
.path-node-mark-correct-ghost { fill: var(--accent); font-size: 8px; }

.path-chart-label { font-size: 10px; fill: var(--muted); font-family: var(--font-mono); }

.path-chart-rounds { display: flex; flex-direction: column; gap: 10px; }
.path-round-card {
  border-left: 3px solid var(--glass-border);
  background: rgba(255, 255, 255, .03);
  border-radius: 10px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.path-round-ok { border-left-color: var(--ok); }
.path-round-wrong { border-left-color: var(--crit); }
.path-round-title { color: var(--ink); font-size: 12.5px; font-weight: 700; }
.path-round-pick, .path-round-correct-pick { font-size: 12.5px; color: var(--muted); }
.path-round-pick span, .path-round-correct-pick span { color: var(--ink); font-weight: 600; }
.path-round-explanation { font-size: 12.5px; color: var(--ink); line-height: 1.45; }

/* Per-round option chips — ALL options shown as small dot-nodes (plan1.5-refine1 #2):
   the player's chosen option highlighted (green/red), the correct option outlined teal,
   everything else muted. */
.path-round-options { display: flex; flex-wrap: wrap; gap: 6px; }
.path-round-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--glass-border);
  color: var(--muted);
}
.path-opt-correct { border-color: var(--accent); color: var(--ink); }
.path-opt-chosen-correct { border-color: var(--ok); background: rgba(34, 197, 94, .12); color: var(--ink); font-weight: 700; }
.path-opt-chosen-wrong { border-color: var(--crit); background: rgba(251, 77, 106, .12); color: var(--ink); font-weight: 700; }
.path-opt-muted { opacity: .6; }
.path-round-option[aria-expanded="true"] { border-color: var(--accent-cyan); box-shadow: 0 0 8px rgba(34, 211, 238, .35); }

/* Per-pill detail line (case-review polish) — hidden until a pill is tapped; see
   PathChartScreen._toggleOptionDetail. */
.path-round-detail { margin-top: 4px; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--glass-border); background: rgba(255, 255, 255, .04); display: flex; flex-direction: column; gap: 4px; }
.path-round-detail-label { color: var(--ink); font-size: 12.5px; font-weight: 700; }
.path-round-detail-status { font-size: 11px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; }
.path-round-detail-status-ok { color: var(--ok); }
.path-round-detail-status-wrong { color: var(--crit); }
.path-round-detail-status-correct { color: var(--accent); }
.path-round-detail-status-muted { color: var(--muted); }
.path-round-detail-explanation { color: var(--muted); font-size: 12px; line-height: 1.4; }

.path-chart-result {
  text-align: center;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 13px;
  padding: 8px;
  border-radius: 10px;
  border: 1px solid var(--glass-border);
}
.result-correct { color: var(--ok); background: rgba(34, 197, 94, .08); border-color: var(--ok); }
.result-wrong { color: var(--crit); background: rgba(251, 77, 106, .08); border-color: var(--crit); }

/* ---------- Exam: patient chart ---------- */
/* Bottom-sheet OVERLAY, not a navigation — _chartOpen only swaps this drawer in/out of
   ExamScreen's own re-render (see ExamScreen.js's _draw/_chartHtml); the exam-monitor's
   countdown/ECG (MonitorDriver) keeps ticking underneath, untouched. position:absolute
   here resolves against #app (position:relative, fills .stage, since .exam-screen itself
   has no position set) — same anchoring idea as .pause-overlay below. z-index sits above
   ordinary exam content but below .game-hud (40) so Back/Pause/Mute stay reachable. */
.chart-overlay {
  position: absolute;
  inset: 0;
  z-index: 38;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(6, 10, 18, .6);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.chart-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  height: 80%;
  max-height: 80vh;
  border-left: 3px solid var(--accent-cyan);
  border-radius: 20px 20px 0 0;
  /* Close button sits near this rounded top corner — same clip risk .glass-card's
     overflow:hidden caused for other cards' edge buttons (see the .hospital-confirm/
     .pause-card/.protocol-tracker/.test-row fix above); same fix here. */
  overflow: visible;
}
.chart-panel::before { border-radius: 20px 20px 0 0; }
.chart-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.chart-close-btn {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--glass-border);
  color: var(--accent);
}
.chart-close-icon { width: 18px; height: 18px; display: block; }
.chart-time { color: var(--ink); font-size: 12px; }
/* Fills the rest of the sheet and scrolls through every gathered finding — flex:1 (not a
   vh cap) since the sheet itself is now already height-bounded to 80% of the stage. */
.chart-rows { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.chart-row { display: flex; align-items: flex-start; gap: 8px; }
.chart-tag {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--accent-cyan);
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  padding: 2px 8px;
}
.chart-text { color: var(--ink); font-size: 13px; line-height: 1.4; }

/* ---------- Career home (day intro) / Week report / Month review / Game over ---------- */
/* Month review / game over are short, bounded cards — safe to stay centered. Career home is
   NOT: with the History panel toggled open (up to 40vh) plus the HUD card and season
   banner, it can exceed a short device's viewport, and centered + overflowing content
   clips/strands the top of the screen (same justify-content overflow issue as .title-screen
   above), so it gets its own flex-start rule instead of sharing this one. */
.month-review-screen, .game-over-screen { justify-content: center; }
.career-home-screen { justify-content: flex-start; }
.career-hud, .week-report-card, .month-review-card, .game-over-card { display: flex; flex-direction: column; gap: 10px; }
.career-hud h2, .week-report-card h2, .month-review-card h2, .game-over-card h2 { color: var(--ink); }

/* Week report is a scrollable report card (multiple cards, scrolling internally by default
   now — #app > div, above) with a fixed bottom banner-ad slot — unlike the other
   center-justified screens above, so it gets its own layout rules (specificity via
   #app > .week-report-screen, same trick as #app > .splash-screen). */
.week-report-screen { justify-content: flex-start; }
/* Reserves room for BOTH docked fixed footers below (.week-report-actions, ~118px, then
   .report-banner-ad, 50px) so scrolling to the end of the report clears each of them —
   see .week-report-actions for why they're docked instead of plain scrollable content. */
#app > .week-report-screen { padding-bottom: calc(50px + 118px + var(--sab, env(safe-area-inset-bottom, 0px))); }

.report-grade-row { display: flex; align-items: center; gap: 12px; }
.report-grade-badge {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  font-size: 22px;
  font-weight: 800;
  border: 1px solid var(--glass-border-hi);
  background: rgba(45, 212, 191, .12);
  color: var(--accent);
}
.report-grade-A, .report-grade-B { color: var(--ok); border-color: var(--ok); background: rgba(34, 197, 94, .12); }
.report-grade-C { color: var(--warn); border-color: var(--warn); background: rgba(251, 191, 36, .12); }
.report-grade-D, .report-grade-F { color: var(--crit); border-color: var(--crit); background: rgba(251, 77, 106, .12); }
.report-grade-sub { font-size: 12px; margin: 2px 0 0; }

.report-patient-list { display: flex; flex-direction: column; gap: 8px; max-height: 40vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.report-patient-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--glass-border); }
.report-patient-row:last-child { border-bottom: none; }
.report-patient-row-clickable { cursor: pointer; transition: opacity .15s ease; }
.report-patient-row-clickable:active { opacity: .6; }
.report-patient-mark { flex-shrink: 0; width: 18px; text-align: center; font-weight: 800; }
.report-mark-ok { color: var(--ok); }
.report-mark-wrong { color: var(--crit); }
.report-patient-info { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.report-patient-name { color: var(--ink); font-size: 13px; }
.report-patient-dx { color: var(--muted); font-size: 11px; }
.report-dx-given { color: var(--ink); }
.report-dx-correct { color: var(--accent); }

.report-tips-list { display: flex; flex-direction: column; gap: 6px; list-style: none; }
.report-tips-list li { position: relative; padding-left: 18px; color: var(--ink); font-size: 12.5px; line-height: 1.45; }
.report-tips-list li::before { content: '\203A'; position: absolute; left: 2px; color: var(--accent); font-weight: 800; }

/* Docked action bar (Continue / View Full History) — fixed just above .report-banner-ad,
   same width-capping trick. MUST be fully opaque (not var(--card-solid)'s .92 alpha): at
   .92 alpha, bright --ink text scrolling underneath still shows through faintly, which
   read as the report's tips/history text overlapping the button labels. Docking these
   (rather than leaving them as normal scrollable content) also means Continue is always
   reachable without needing to scroll past the patient list + tips card first. */
.week-report-actions {
  position: fixed;
  left: 50%;
  /* Flush against .report-banner-ad's actual rendered top edge (its min-height, 50px) —
     NOT the old 74px constant below (that included slack meant as blank trailing margin,
     which read as a gap here, letting card content peek through between the two bars). */
  bottom: calc(50px + var(--sab, env(safe-area-inset-bottom, 0px)));
  transform: translateX(-50%);
  width: 100%;
  max-width: min(100vw, calc(100dvh * 9 / 16));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: rgb(13, 22, 34);
  border-top: 1px solid var(--glass-border);
  z-index: 21;
}

/* Fixed bottom banner-ad slot — centered to the 9:16 shell width (not full viewport width)
   so it lines up with the rest of the game UI on wide viewports. Dev/standalone shows a
   labeled placeholder bar; the RN host renders its own native banner into this space once
   show_banner is requested (see WeekReportScreen.render/leave). */
.report-banner-ad {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 100%;
  max-width: min(100vw, calc(100dvh * 9 / 16));
  min-height: 50px;
  padding-bottom: var(--sab, env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--card-solid);
  border-top: 1px solid var(--glass-border);
  z-index: 20;
}
.report-banner-ad-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
}
.career-rank-line { color: var(--accent); font-size: 12.5px; font-weight: 700; letter-spacing: .3px; }
.month-review-rank-line, .month-review-tokens-line { color: var(--ink); }
.month-review-rank-line strong, .month-review-tokens-line strong { color: var(--accent); }
.token-icon { width: 14px; height: 14px; vertical-align: -2px; margin-right: 3px; }

/* Rank pill (career-home) — tappable, styled like .stat-pill but visibly interactive */
/* The rank pill has always opened the rank detail, but nothing said so — it read as a
   label, not a control. The hint is inline (not a second line) so the pill keeps its
   height, and lowercase/dimmed so it reads as an affordance rather than part of the rank. */
.rank-pill-hint {
  margin-left: 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .2px;
  text-transform: lowercase;
  opacity: .65;
}
.rank-pill {
  align-self: flex-start;
  background: rgba(45, 212, 191, .12);
  border: 1px solid var(--glass-border-hi);
  border-radius: 999px;
  padding: 5px 14px;
  color: var(--accent);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .3px;
  text-align: center;
  box-shadow: 0 0 10px rgba(45, 212, 191, .25);
  animation: rank-pill-glow 2.4s ease-in-out infinite;
}
@keyframes rank-pill-glow {
  0%, 100% { box-shadow: 0 0 10px rgba(45, 212, 191, .25); }
  50% { box-shadow: 0 0 16px rgba(45, 212, 191, .5); }
}
@media (prefers-reduced-motion: reduce) {
  .rank-pill { animation: none; }
}

/* ---------- Rank Info (progression explainer) ---------- */
.rank-info-card { display: flex; flex-direction: column; gap: 8px; }
.rank-info-card h3 { color: var(--ink); }
.rank-ladder-list { display: flex; flex-direction: column; gap: 6px; }
.rank-ladder-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 8px;
  border-radius: 10px;
  border: 1px solid var(--glass-border);
  color: var(--muted);
  font-size: 13px;
}
.rank-ladder-current {
  border-color: var(--accent);
  color: var(--ink);
  background: rgba(45, 212, 191, .08);
  box-shadow: 0 0 10px rgba(45, 212, 191, .2);
}
.rank-ladder-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rank-ladder-title { font-weight: 600; }
.rank-ladder-req { font-size: 10.5px; color: var(--muted); }
.rank-ladder-you {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--accent);
}

/* ---------- Case history (shared by day intro + week report) ---------- */
.history-panel { display: flex; flex-direction: column; gap: 8px; max-height: 40vh; overflow-y: auto; }
.history-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--glass-border);
}
.history-row:last-child { border-bottom: none; }
.history-row-clickable { cursor: pointer; transition: opacity .15s ease; }
.history-row-clickable:active { opacity: .6; }
.history-name { color: var(--ink); font-size: 13px; }
.history-meta { color: var(--muted); font-size: 11px; }
.history-outcome {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--glass-border);
}
.history-saved { color: var(--ok); border-color: var(--ok); }
.history-harmed { color: var(--warn); border-color: var(--warn); }
.history-died { color: var(--crit); border-color: var(--crit); }

/* ---------- Hospital (Plan 2 upgrade map) ---------- */
.hospital-screen { gap: 12px; }
.hospital-hud { display: flex; flex-direction: column; gap: 8px; }
.hospital-hud h2 { color: var(--ink); }

.hospital-map-frame {
  position: relative;
  padding: 0;
  overflow: hidden;
  border-radius: 16px;
}
.hospital-map-img { width: 100%; height: auto; display: block; }
.hospital-room-layer { position: absolute; inset: 0; }

.hospital-room {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  border-radius: 10px;
  padding: 6px;
  border: 1px solid transparent;
  background: rgba(6, 10, 20, .35);
}
.hospital-room-name {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--ink);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .85);
}
.hospital-room-badge { font-size: 10px; font-weight: 700; }

.hospital-room-locked {
  background: rgba(6, 10, 20, .68);
  filter: grayscale(1);
  opacity: .55;
}
.hospital-room-locked .hospital-room-badge { color: var(--muted); }

.hospital-room-available {
  border-color: var(--accent);
  box-shadow: 0 0 14px rgba(45, 212, 191, .55), inset 0 0 10px rgba(45, 212, 191, .15);
  animation: hospital-room-pulse 2.2s ease-in-out infinite;
}
.hospital-room-available .hospital-room-badge { color: var(--accent); }
@keyframes hospital-room-pulse {
  0%, 100% { box-shadow: 0 0 14px rgba(45, 212, 191, .55), inset 0 0 10px rgba(45, 212, 191, .15); }
  50% { box-shadow: 0 0 22px rgba(45, 212, 191, .85), inset 0 0 14px rgba(45, 212, 191, .25); }
}
@media (prefers-reduced-motion: reduce) {
  .hospital-room-available { animation: none; }
}

.hospital-room-owned {
  border-color: var(--ok);
  box-shadow: 0 0 10px rgba(34, 197, 94, .45);
}
.hospital-room-owned .hospital-room-badge { color: var(--ok); }

.hospital-confirm { display: flex; flex-direction: column; gap: 8px; }
.hospital-confirm-cost { color: var(--accent); font-size: 13px; }
.hospital-confirm-actions { display: flex; gap: 10px; justify-content: center; }
.hospital-confirm-actions .btn-primary,
.hospital-confirm-actions .btn-secondary { min-width: 0; flex: 1; }

/* ---------- Seasonal condition banner (career-home) ---------- */
.season-banner {
  align-self: flex-start;
  background: rgba(45, 212, 191, .1);
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
}

/* ---------- ER Surprise Event ---------- */
.event-card { display: flex; flex-direction: column; gap: 10px; }
.event-card-head { display: flex; align-items: center; gap: 12px; }
/* Inline SVG category icon (see eventIcons.js — same convention as statIcons.js), replacing
   the old emoji. Wrapping span keeps the existing idle pulse animation. */
.event-pulse-icon {
  display: flex;
  color: var(--accent);
  animation: event-pulse 1.4s ease-in-out infinite;
}
.event-icon { width: 28px; height: 28px; display: block; }
@keyframes event-pulse {
  0%, 100% { transform: scale(1); opacity: .85; }
  50% { transform: scale(1.15); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .event-pulse-icon { animation: none; }
}
.event-severity { font-size: 11px; letter-spacing: 3px; color: var(--muted); }
.event-severity-3 { color: var(--crit); }
.event-severity-2 { color: var(--warn); }
.event-severity-1 { color: var(--accent); }
.event-prompt { color: var(--ink); }
.event-options { display: flex; flex-direction: column; gap: 10px; }
/* Option cards are chosen BLIND — no rating/note shown here, only the label + a shared
   neutral icon (see EventScreen.js's _optionHtml/EVENT_OPTION_ICON) — so this is a simple
   row, not the old label-over-rating-over-note column stack. */
.event-option-btn { display: flex; flex-direction: row; align-items: center; gap: 10px; width: 100%; min-height: auto; }
.event-option-icon { width: 18px; height: 18px; flex-shrink: 0; color: var(--muted); }
.event-rating-pos { color: var(--ok); }
.event-rating-neg { color: var(--crit); }

/* ---------- ER Surprise Event — result reveal (after picking) ---------- */
/* The animated +N/-N — a one-shot pop-in (scale + fade), modeled on .portrait-correct-pulse's
   finite (non-looping) shape rather than .event-pulse-icon's ambient loop. */
.event-result-delta {
  align-self: center;
  font-size: 34px;
  font-weight: 800;
  letter-spacing: .5px;
  animation: event-delta-pop .5s ease-out;
}
@keyframes event-delta-pop {
  0% { opacity: 0; transform: scale(.5) translateY(8px); }
  60% { opacity: 1; transform: scale(1.15) translateY(0); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .event-result-delta { animation: none; }
}
.event-result-note { font-size: 12.5px; color: var(--muted); }
.event-result-rating { font-size: 13px; color: var(--ink); align-self: center; }
.hospital-purchase-error { color: var(--crit); font-size: 12.5px; text-align: center; }

/* ---------- Persistent HUD (top bar) — see Hud.js ---------- */
/* Positioned within .stage (not #app), so per-screen innerHTML rebuilds never touch it.
   Hidden on SPLASH (Hud.render toggles .game-hud-visible); reserves --hud-h of top padding
   on every other screen (#app > div, above) so it never overlaps content. */
.game-hud {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: calc(var(--hud-h) + var(--sat, env(safe-area-inset-top, 0px)));
  padding: var(--sat, env(safe-area-inset-top, 0px)) 10px 0;
  /* Solid/opaque bar (not a fade-to-transparent gradient) so screen content scrolling
     upward is fully hidden behind it — items stay strictly UNDER the HUD, never bleeding
     into it. A hairline + blur keep the glass feel. */
  background: rgba(9, 13, 20, .97);
  border-bottom: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.game-hud.game-hud-visible { display: flex; }

.hud-right { display: flex; align-items: center; gap: 8px; }

/* Centre "Back" — one level up inside the game (the left door LEAVES the game).
   height/line-height are pinned so this text button matches the 40px round icon buttons
   exactly and CANNOT grow the 48px bar; the HUD reserves --hud-h of page padding, so a
   taller bar would silently overlap every screen's first row. */
.hud-navback-btn {
  height: 40px;
  line-height: 1;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--glass-border);
  color: var(--accent);
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.hud-navback-btn[hidden] { display: none; }

.hud-btn {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--glass-border);
  color: var(--accent);
  text-align: center;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.hud-btn[hidden] { display: none; }

/* ---------- Pause overlay — see Hud.js ---------- */
/* Fills .stage below the HUD bar (lower z-index, not a smaller box — the HUD bar itself
   sits on top so Back/Mute/Resume-via-Pause stay reachable while paused). */
.pause-overlay {
  position: absolute;
  inset: 0;
  z-index: 35;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(6, 10, 18, .72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.pause-overlay[hidden] { display: none; }

.pause-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: 280px;
  text-align: center;
}
.pause-card h2 { color: var(--ink); }
.pause-card .btn-primary, .pause-card .btn-secondary { width: 100%; }

/* ---- Med School tutorial (STATES.MED_SCHOOL) ------------------------------------- */
.medschool-screen { display: flex; flex-direction: column; gap: 14px; padding: 12px; }
.medschool-head { display: flex; align-items: center; gap: 10px; }
.medschool-head .eyebrow { flex: 1; margin: 0; }
.medschool-dots { display: flex; gap: 6px; }
.medschool-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.22); }
.medschool-dot-active { background: var(--accent-cyan); }
.medschool-dot-done { background: rgba(255,255,255,0.55); }
.medschool-clock { font-size: 1.05rem; color: var(--ink); }
.medschool-nurse { display: flex; gap: 10px; align-items: flex-start; }
.medschool-nurse .nurse-avatar { width: 44px; height: 44px; border-radius: 50%; flex: 0 0 auto; }
.medschool-nurse .nurse-hint-panel { flex: 1; padding: 10px 12px; }
.medschool-case { padding: 12px 14px; }
.medschool-presentation { font-size: 1.02rem; line-height: 1.45; margin: 0; }
.medschool-question { font-weight: 600; margin: 0; }
.medschool-options { display: flex; flex-direction: column; gap: 8px; }
.medschool-option { text-align: left; }
.medschool-verdict { margin: 0 0 6px; }
.medschool-dontmiss { margin: 0; opacity: 0.85; font-size: 0.92rem; }
.medschool-skip-btn {
  background: none; border: none; color: rgba(255,255,255,0.5);
  text-decoration: underline; font-size: 0.85rem; padding: 6px; cursor: pointer;
}

/* ---- Orientation cards (STATES.ORIENTATION) -------------------------------------- */
.orientation-screen { display: flex; flex-direction: column; gap: 14px; padding: 12px; }
.orientation-card { padding: 14px; display: flex; flex-direction: column; gap: 10px; }
.orientation-wing-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.orientation-wing-row { display: flex; align-items: flex-start; gap: 10px; }
.orientation-wing-info { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.orientation-wing-title { font-weight: 600; }
.orientation-wing-room { opacity: 0.6; font-weight: 400; }
.orientation-wing-desc { font-size: 0.88rem; opacity: 0.8; }
.orientation-wing-gate { flex: 0 0 auto; font-size: 0.78rem; opacity: 0.85; text-align: right; line-height: 1.3; }
.orientation-nav { display: flex; align-items: center; gap: 12px; }
.orientation-dots { display: flex; gap: 6px; flex: 0 0 auto; }
.orientation-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.22); }
.orientation-dot-active { background: var(--accent-cyan); }
.orientation-nav .btn-primary { flex: 1; }

/* ---- Multi-lab dependencies (engine/DependencyEngine.js) ------------------------- */
/* Shown only at pre-clinical ranks — a Resident is expected to know what a test needs. */
.test-requires {
  margin: 4px 0 0; font-size: 0.8rem; opacity: 0.7; font-style: italic;
  border-left: 2px solid rgba(34,211,238,0.4); padding-left: 8px;
}
/* An action re-opened because its prerequisite has since landed. Distinct from a fresh
   order so the player can see the clock cost coming a second time. */
.test-order-btn.test-reorder { border-color: var(--accent-cyan); }
.test-order-btn.test-reorder .test-label { color: var(--accent-cyan); }
