/* ==========================================================================
   CaNaMED Session 3 platform - stylesheet
   Design system: deliberate Franco-Japanese blue + amber identity, a neutral
   ramp, layered shadows, an 8px spacing scale, a type scale, and a small,
   consistent motion language. System fonts only (a strict CSP blocks CDNs),
   single file, no build step. Accessibility is designed in: WCAG-AA contrast,
   a strong surface-independent focus ring, a 1rem text floor, a prose width
   cap, and prefers-reduced-motion / prefers-contrast support.
   ========================================================================== */

/* ==========================================================================
   DESIGN TOKENS moved to tokens.css (loaded before this file) — the single
   source of truth for colour, type, space, radius, shadow, motion and
   z-index. Do not redefine tokens here; do not write raw hex/px values in
   this file — reference a token. (Clinical Editorial restyle, 2026-07.)
   ========================================================================== */
/* the four workshop "chapters" each get their own accent colour */
#room-main[data-stage="0"] { --stage-accent: var(--meeting);     --stage-accent-50: var(--meeting-50); }

/* WCAG 2.4.1 (Bypass Blocks, Level A). Hidden off-screen at rest;
   becomes visible the moment a keyboard user focuses it. Sits above the
   header so the focus ring doesn't get cropped. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 8px;
  z-index: 9999;
  background: var(--ink, #2a2a2a);
  color: #fff;
  padding: 10px 14px;
  border-radius: 6px;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
  transition: top 120ms;
}
.skip-link:focus,
.skip-link:focus-visible {
  top: 8px;
  outline: 3px solid var(--focus, #f59e0b);
  outline-offset: 2px;
}

/* a screen-reader-only utility (needed by the reworked lists / state labels) */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-sans);
  /* a near-invisible blue+amber wash so the page never reads as flat grey */
  background:
    radial-gradient(900px 620px at 88% -12%, var(--nagoya-50) 0%, transparent 55%),
    radial-gradient(700px 520px at 5% 108%, var(--caen-50) 0%, transparent 60%),
    var(--bg);
  background-attachment: fixed;
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.005em;
}

.hidden { display: none !important; }

/* ---------- inline SVG icon system (2026-07-17) ----------
   Replaces the pictographic UI emojis (inconsistent across OSes; fought the
   Clinical Editorial voice). Symbols live in the inline sprite at the top of
   index.html; referenced as <svg class="ic"><use href="#ic-name"/></svg>.
   Em-sized so icons track the surrounding text size; the slight negative
   vertical-align seats the 24-grid glyph on the text baseline. */
.ic {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  flex: none;
}
/* The sprite itself never renders (symbols only draw via <use>). A class,
   not an inline style attribute: the production CSP is style-src 'self'. */
.ic-sprite { display: none; }

/* ---------- typography base ----------
   Clinical Editorial: headings h1-h3 carry the serif display voice
   (Source Serif 4); h4+ and all UI chrome stay in the humanist sans.
   Serif at display sizes wants tighter default tracking than the sans. */
h1, h2, h3 {
  font-family: var(--font-serif);
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
}
h4 { letter-spacing: -0.015em; }
p { margin: 0.6em 0; }
/* the letterspaced small-caps "kicker" — the editorial signature used for
   section/form labels (e.g. the splash "Session code" label) */
.kicker {
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  font-size: var(--fs-1);
  font-weight: var(--fw-semibold);
  color: var(--muted);
}
/* cap prose width for second-language readers - long lines are hard to track */
.vignette p, .instructions p, .instructions-open p, .info-list,
.safety-note p, .privacy-note p, .micro-framework p {
  max-width: 70ch;
}

/* ---------- header ----------
   The page masthead only, hence `body > header`: a bare `header` also took a
   dialog's title row and the chart's tally for mastheads. */
body > header {
  background: linear-gradient(105deg, var(--masthead-a), var(--masthead-b));
  color: var(--masthead-ink);
  box-shadow: inset 0 -3px 0 var(--caen-500), var(--sh-md);
}
.header-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--s-4) var(--s-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}
body > header h1 { margin: 0; font-size: 1.6rem; font-weight: 700; letter-spacing: -0.02em; }
body > header .sub { margin: 4px 0 0; font-size: 0.9rem; opacity: 0.92; }

.header-right-wrap {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-shrink: 0;
}
#header-right { flex-shrink: 0; }
.mode-badge {
  font-size: 0.78rem;
  padding: 5px 10px;
  border-radius: var(--r-sm);
  white-space: nowrap;
}
.mode-badge.shared { background: rgba(0,0,0,.22); border: 1px solid rgba(255,255,255,.55); }
.mode-badge.solo { background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.4); }

.conn-badge {
  font-size: 0.74rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  white-space: nowrap;
}
.conn-ok { background: rgba(0,0,0,.3); color: #fff; border: 1px solid rgba(255,255,255,.6); }
.conn-lost { background: var(--key); color: #fff; border: 1px solid var(--key); }

/* ---------- layout ---------- */
main {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--s-5);
}
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  margin-bottom: var(--s-4);
  box-shadow: var(--sh-sm);
}
.card h2 { margin: 0 0 var(--s-2); font-size: 1.375rem; font-weight: 700; line-height: 1.25; }
.card h3 {
  margin: 0 0 var(--s-3);
  font-size: 1.05rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.card h4 { margin: var(--s-4) 0 var(--s-1); font-size: 0.98rem; color: var(--ink); }
.ico { font-size: 1.15rem; }
.hint { color: var(--muted); font-size: 0.92rem; }
.empty { color: var(--muted); font-style: italic; font-size: 0.92rem; }

.columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-4);
  /* `align-items: start` is load-bearing for the sticky-right-column
     pattern below — without it a grid cell stretches to match the
     tallest sibling, and a child with position:sticky inside a stretched
     cell never has anything to be sticky to. */
  align-items: start;
}
@media (max-width: 960px) { .columns { grid-template-columns: 1fr; } }

/* Sticky right-column — sim 2026-05-19 finding (24 students):
   "Sticky right-column (Findings + Decisions + Discussion) so the case
   panels can scroll without losing the working answers." Pinned at
   viewport ≥960px (where the two-column layout is in effect); on
   narrow viewports the layout already stacks so stickiness is a no-op. */
@media (min-width: 961px) {
  .columns > .col-right {
    position: sticky;
    /* Small breathing margin from the top of the viewport while sticky. The
       global header is not pinned, and the stage card no longer pins either
       (2026-06-02), so the right column tucks near the top edge when the body
       is scrolled. */
    top: 12px;
    /* Cap height so the inner scrollable region (when content overflows)
       can scroll independently of the case panels in col-left.
       Falls back to auto if the calc unit isn't supported. */
    max-height: calc(100vh - 80px);
    overflow-y: auto;
    /* Restore native momentum-scroll on touchpads/Macs */
    -webkit-overflow-scrolling: touch;
  }
  /* Module B (Round-4): the instructional/reference column is content-heavy
     and the group-answers sidebar is lighter, so give col-left more room
     than Module A's even 1fr 1fr split. Below 961px the base .columns rule
     stacks them to a single column (sticky becomes a no-op). */
  .columns.modB-columns { grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr); }
}

/* Progressive right-column reveal (2026-06-01). While none of Module A's
   right-column tabs (Decide together / Debate / Our final answers) have become
   actionable yet, revealModARightCol() adds .rcol-collapsed so the case work
   (col-left) gets the full width instead of sitting beside an empty grid track.
   Scoped to #stage-1 so it can never affect Module B or the wrap-up. The
   per-tab `hidden` reveal reuses the existing `.rcol-tab[hidden]{display:none}`
   rule further down. */
#stage-1 .columns.rcol-collapsed { grid-template-columns: 1fr; }
#stage-1 .columns.rcol-collapsed > .col-right { display: none; }

/* ── Branched-scenario format ──────────────────────────────────────────────
   The branched-format layout + components — the épuré body[data-format] rules,
   the .dec-documents evidence panel, the .branched-* rationale/final deliverable
   and the admin .ct-* choice tree — live in the LAZILY-loaded branched.css
   (injected in-room by applyScenario → CanamedLoader.ensureBranchedStyles), so
   they stay out of the eager splash CSS budget. Standard PBL/roleplay sessions
   never request it. Only the SHARED .dec-branch dark overrides remain here,
   because standard Module-B chained branches show .dec-branch too (its base
   style lives further down in this file). */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) .dec-branch { background: #15212c; }
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) .dec-branch p { color: var(--ink, #f1eee6); }
}


#join-btn, #start-session-btn, #prev-btn, #next-btn, .answer-add-btn, #advance-all-btn, #wrapup-download-btn{
  background: var(--primary);
  color: var(--on-primary);
  border: 1px solid transparent;
  border-radius: var(--r-btn);
  font: inherit;
  font-weight: 600;
  font-size: 0.875rem;
  padding: 9px 18px;
  cursor: pointer;
  box-shadow: var(--sh-sm);
  transition: background var(--dur-fast) var(--ease-soft),
              box-shadow var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-soft);
}
#join-btn:hover, #start-session-btn:hover, #prev-btn:hover, #next-btn:hover,
.answer-add-btn:hover, #wrapup-download-btn:hover,
#advance-all-btn:hover{
  background: var(--primary-hover);
}
#prev-btn:disabled, #next-btn:disabled{
  background: var(--n-100);
  color: var(--muted);
  border-color: var(--line-strong);
  box-shadow: none;
  cursor: not-allowed;
}
#join-btn { width: 100%; padding: 12px; font-size: 0.95rem; margin-top: var(--s-1); }
#start-session-btn { padding: 11px 20px; font-size: 0.95rem; }


#leave-btn, .reset-btn, #waiting-leave {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-btn);
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 8px 16px;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-soft),
              background var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-soft);
}
#leave-btn:hover, .reset-btn:hover, #waiting-leave:hover {
  border-color: var(--n-300);
  background: var(--n-25);
}
.reset-btn { width: 100%; color: var(--muted); }
.reset-btn:hover { border-color: var(--key); color: var(--key); }
#waiting-leave { max-width: 200px; }

/* danger */
#purge-session-btn {
  background: var(--card);
  border: 1px solid var(--key);
  color: var(--key);
  border-radius: var(--r-btn);
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 9px 18px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft);
}
#purge-session-btn:hover { background: var(--key-50); }

/* press feedback (respecting reduced motion, set below) */
button:active:not(:disabled) { transform: translateY(1px); }

/* ---------- inputs ---------- */
input, select, textarea { font-size: 16px; }   
.lobby-card input, .lobby-card select,
.answer-add input,
.inline-label select, .answer-edit {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font: inherit;
  background: var(--card);
  transition: border-color var(--dur-fast) var(--ease-soft),
              box-shadow var(--dur-fast) var(--ease-soft);
}
.lobby-card input:focus, .lobby-card select:focus, .answer-add input:focus, .inline-label select:focus,
.answer-edit:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--nagoya-50);
}
.lobby-card input.err, input.err {
  border-color: var(--key);
  border-width: 2px;
}

/* ---------- lobby ---------- */
#lobby {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--s-7) var(--s-5);
  display: flex;
  justify-content: center;
}
.lobby-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  padding: var(--s-6) var(--s-6);
  width: 100%;
  max-width: 440px;
}
.lobby-card h2 { margin: 0 0 6px; }
.lobby-mode {
  font-size: 0.9rem;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  margin: var(--s-2) 0 var(--s-4);
}
.lobby-mode.shared { background: var(--ok-50); color: var(--ok-strong, #176b3a); }
.lobby-mode.solo { background: var(--caen-50); color: var(--caen-ink); }
.lobby-mode.broken {
  background: var(--key-50);
  color: #8a2a22;
  border: 1px solid #f0b3ac;
  font-weight: 600;
}
.lobby-card label {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
  margin-bottom: var(--s-3);
}
.lobby-card input, .lobby-card select {
  display: block;
  width: 100%;
  margin-top: 5px;
  padding: 10px 12px;
  font-weight: 400;
}
.lobby-hint { font-size: 0.86rem; color: var(--muted); margin: var(--s-3) 0 0; }

.lobby-section {
  border-top: 1px solid var(--line);
  margin-top: 6px;
  padding-top: var(--s-4);
}
.lobby-section h3 { margin: 0 0 var(--s-3); font-size: 0.95rem; }
.link-btn {
  background: none;
  border: none;
  color: var(--primary);
  font-size: 0.86rem;
  cursor: pointer;
  padding: 6px 0;
  min-height: 40px;
  text-decoration: underline;
}

/* ---------- presence / chips ---------- */
.presence-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: var(--s-3);
}
.chip {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 3px 11px 3px 8px;
  font-size: 0.85rem;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.chip.me { border-color: var(--primary); background: var(--nagoya-50); font-weight: 600; }
.dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}
.mini-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-right: var(--s-2);
}

/* ---------- accent / vignette cards ---------- */
.vignette { border-left: 4px solid var(--primary); }
.vignette p { margin: var(--s-2) 0; }

/* ---------- request buttons ---------- */
.btn-group { display: flex; flex-direction: column; gap: var(--s-2); }
.req-btn {
  text-align: left;
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 11px 13px;
  cursor: pointer;
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  min-height: 44px;
  transition: border-color var(--dur-fast) var(--ease-soft),
              background var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-soft);
}
.req-btn:hover { border-color: var(--primary); background: var(--nagoya-50); }
.req-btn:active:not(:disabled):not(.done) {
  transform: translateY(1px);
  background: #ddeefb;
}
.req-btn.done {
  background: var(--surface-sunk);
  color: var(--muted);
  cursor: default;
  border-color: var(--line);
}
.req-btn.done:hover { border-color: var(--line); background: var(--surface-sunk); }
.req-btn.done::before { content: "\2713  "; color: var(--ok); font-weight: 700; }
.req-btn.key-btn { border-left: 3px solid var(--key); }
.req-btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---------- findings ---------- */
.count {
  background: var(--primary);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  border-radius: var(--r-pill);
  padding: 2px 9px;
  margin-left: auto;
}
#prompts-list { margin: 0; padding-left: 20px; }

/* ---------- prompts / compare ---------- */
.prompts-card { border-left: 4px solid var(--caen-line); }
#prompts-list li { margin-bottom: var(--s-3); }
.compare-card { border-left: 4px solid var(--key); }
.compare-card ul { margin: var(--s-2) 0; padding-left: 20px; }
@keyframes prompt-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

/* Mobile: stack the controls. */
@media (max-width: 480px) {
  .prompt-controls { flex-direction: column; align-items: stretch; }
  .prompt-skip { margin-left: 0; text-align: center; }
  .prompt-next { width: 100%; }
  .prompt-prev { width: 100%; text-align: center; }
}

/* ---------- module content: guidelines, recap tables, roleplay aids ---------- */
.guidelines-card, .recap-card { background: var(--surface-sunk); }
.guidelines-card > summary, .recap-card > summary {
  cursor: pointer;
  font-size: 0.98rem;
  list-style: none;
}
.guidelines-card > summary::-webkit-details-marker,
.recap-card > summary::-webkit-details-marker { display: none; }
.guidelines-card > summary::before, .recap-card > summary::before {
  content: "\25B8  ";
  color: var(--muted);
}
.guidelines-card[open] > summary::before, .recap-card[open] > summary::before {
  content: "\25BE  ";
}
.ref-list { margin: var(--s-3) 0 0; padding-left: 20px; font-size: 0.92rem; }
.ref-list li { margin-bottom: var(--s-2); }
.ref-list a, .recap-table a { color: var(--nagoya-700); word-break: break-word; }

.recap-card h4:first-of-type { margin-top: var(--s-3); }
/* The recap card's prose lists sit among full-width tables, so the global
   .info-list 70ch readability cap (see typography base) leaves them squeezed
   against the left while the tables span the card — fixed 2026-06-03 (user
   report: "the management list is constrained on the left side"). Let the list
   use the card's full width to match the adjacent recap tables. */
.recap-card .info-list { max-width: none; }
.recap-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
  margin: var(--s-1) 0 var(--s-2);
}
.recap-table th, .recap-table td {
  border: 1px solid var(--line-strong);
  padding: 7px 9px;
  text-align: left;
  vertical-align: top;
}
.recap-table th { background: var(--n-100); font-weight: 600; }
.recap-table tbody tr:nth-child(even) { background: var(--n-25); }
.recap-card { overflow-x: auto; }   /* tables stay readable on a phone */

.safety-note { background: var(--nagoya-50); border-left: 4px solid var(--primary); }
.safety-note p { margin: 6px 0; font-size: 0.95rem; }
.safety-note p:first-child { margin-top: 0; }

.spikes-strip {
  background: var(--n-100);
  border-radius: var(--r-md);
  padding: 11px 13px;
  margin: var(--s-3) 0;
  font-size: 0.9rem;
  line-height: 1.7;
}
.phrases-box {
  background: var(--caen-50);
  border: 1px solid #ecd9a0;
  border-radius: var(--r-md);
  padding: 11px 13px;
  margin: var(--s-3) 0;
  font-size: 0.92rem;
}

/* Observer's SPIKES observation checklist (Module B) — a private,
   per-tab scratchpad the observer fills during the scene. */
.observer-checklist-card { margin: var(--s-3) 0; }
.observer-checklist-summary {
  cursor: pointer;
  font-weight: 600;
  display: flex;
  gap: 8px;
  align-items: baseline;
}
.observer-checklist-summary::-webkit-details-marker { display: none; }
.observer-spikes-list {
  list-style: none;
  margin: var(--s-2) 0;
  padding: 0;
  display: grid;
  gap: 6px;
}
.observer-spikes-list li { margin: 0; }
.observer-spikes-list label {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  font-size: 0.92rem;
  line-height: 1.5;
  cursor: pointer;
}
.observer-spikes-list input[type="checkbox"] {
  width: 20px;
  height: 20px;
  margin-top: 1px;
  flex: 0 0 auto;
}
.observer-note-label {
  display: block;
  margin: var(--s-2) 0 4px;
  font-size: 0.88rem;
  font-weight: 600;
}
.observer-note {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  font: inherit;
  font-size: 0.92rem;
  padding: 7px 9px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--card);
  color: var(--ink);
}
.micro-framework {
  background: var(--surface-sunk);
  border-left: 4px solid var(--ok);
  border-radius: var(--r-md);
  padding: 11px 15px;
  margin: var(--s-3) 0;
}
.micro-framework p { margin: 0 0 4px; font-size: 0.95rem; }
.micro-framework .info-list { margin-top: 4px; }

/* Natural-flow refactor (2026-05-18): components introduced when the
   Module A + Module B instruction walls were replaced by progressive,
   contextual cues. See ARCHITECTURE/script-js-map.md "Phase stepper". */

/* Phase stepper — slim 4-step horizontal nav at the top of each stage.
   Each step is rank + label + time-budget. Current step is highlighted;
   completed steps get a subtle check. Wraps on narrow viewports. */
/* Global "you are here" session stepper (UX-overload fix 2026-06-01).
   A calm 4-segment pill row at the top of the stage card. Future stages are
   muted, completed stages get a filled dot, and the CURRENT stage is the one
   primary-coloured pill that also shows its name — so the eye gets the
   session position at a glance and the Module A phase-stepper below clearly
   reads as module sub-progress. */
.global-stage-progress {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  list-style: none;
  margin: 8px 0 4px;
  padding: 0;
}
.gsp-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 4px;
  border-radius: var(--r-pill);
  background: var(--surface-sunk, #f3f0e8);
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.2;
}
.gsp-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5em;
  height: 1.5em;
  border-radius: 50%;
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.gsp-step.is-done { color: var(--ink); }
.gsp-step.is-done .gsp-num {
  background: var(--meeting, #74a2b3);
  border-color: var(--meeting, #74a2b3);
  color: #fff;
}
.gsp-step.is-current {
  background: var(--primary);
  color: #fff;
}
.gsp-step.is-current .gsp-num {
  background: #fff;
  border-color: #fff;
  color: var(--primary);
}
/* the room is open further than the stage being viewed (student pressed Back) */
.gsp-step.is-live .gsp-num { box-shadow: 0 0 0 2px var(--primary); }
/* keep the bar compact: only the current segment shows its name inline */
.gsp-name { display: none; }
.gsp-step.is-current .gsp-name { display: inline; }
/* Generic per-phase visibility toggle (Module B): a section/prompt not in the
   current phase is removed from the flow entirely. */
.is-phase-hidden { display: none !important; }
/* Generic per-role visibility toggle (Module B, 2026-06-03): role-specific
   guidance (physician framework, observer checklist, patient/family guides)
   shows only for the participant holding that role. Independent of the phase
   toggle above — an element is visible only when it carries NEITHER class. */
.is-role-hidden { display: none !important; }

/* Pinned banners at the top of a panel — used for the Caen+Nagoya rule
   on the Discussion panel and the Module-B ground rule. */
.banner-rule {
  background: var(--meeting-50);
  border-left: 4px solid var(--meeting);
  padding: 10px 14px;
  border-radius: var(--r-sm);
  margin: 0 0 var(--s-3);
  font-size: 0.92rem;
  line-height: 1.55;
}
.banner-rule.banner-compare { background: var(--caen-50); border-left-color: var(--caen-line); }

/* Pause/Explore/Explain/Realign — collapsed-by-default reference card.
   The per-role "how to play your part" guides (patient + family, 2026-06-03)
   share the same collapsible-card chrome; only the left-accent colour differs
   so each role's card reads as its own thing. */
.micro-framework-card,
.role-guide-card {
  background: var(--surface-sunk);
  border-left: 4px solid var(--ok);
  padding: 10px 14px;
}
.micro-framework-card summary,
.role-guide-card summary {
  cursor: pointer;
  font-size: 0.95rem;
  color: var(--ink);
  list-style: none;
}
.micro-framework-card summary::-webkit-details-marker,
.role-guide-card summary::-webkit-details-marker { display: none; }
.micro-framework-card summary::before,
.role-guide-card summary::before {
  content: "\25B8\00A0";
  color: var(--muted);
}
.micro-framework-card[open] summary::before,
.role-guide-card[open] summary::before { content: "\25BE\00A0"; }
.micro-framework-card .info-list,
.role-guide-card .info-list { margin-top: var(--s-2); }

/* "What to do next" coach card — the persistent (but dismissible)
   guidance under the phase stepper. Designed to be eye-catching
   without being noisy: warm caen-amber border-left + soft surface +
   ample horizontal padding so the text is easy to scan in passing.
   Warm caen-amber so the single student-facing callout stays distinct. */
.next-step-coach {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: 0 0 var(--s-3);
  padding: 12px 14px;
  background: var(--caen-50);
  border-left: 4px solid var(--caen-line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-sm);
  font-size: 0.95rem;
  line-height: 1.5;
}
.next-step-coach.hidden { display: none; }
.next-step-icon {
  flex: 0 0 auto;
  font-size: 1.3rem;
  line-height: 1;
  padding-top: 1px;
}
.next-step-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.next-step-text {
  margin: 0;
  color: var(--ink);
}
.next-step-text strong { color: var(--caen-ink); }
.next-step-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.next-step-actions button {
  background: var(--primary);
  color: #fff;
  border: none;
  border-radius: var(--r-pill);
  padding: 7px 14px;
  font: inherit;
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease-soft);
}
.next-step-actions button:hover { background: var(--primary-hover); transform: translateY(-1px); }
.next-step-actions button:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.next-step-dismiss {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  border-radius: 4px;
  align-self: flex-start;
}
.next-step-dismiss:hover { color: var(--ink); background: rgba(0,0,0,.06); }
.next-step-dismiss:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* Dark-mode tweak: the warm caen-amber is too dim on the dark page,
   so use the meeting-blue surface for better readability. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) .next-step-coach {
    background: var(--meeting-50);
    border-left-color: var(--meeting);
  }
}
html[data-theme="dark"] .next-step-coach {
  background: var(--meeting-50);
  border-left-color: var(--meeting);
}


/* ── Chart-metaphor framing for Module A (pedagogist's strongest
   claim, 2026-05-18 specialist panel): the case-work isn't a "menu
   of buttons", it's the consultation note the team is writing. The
   visual treatment evokes a clinical note: warm paper-like surface,
   a clear chart header with the patient's name, and "chart sections"
   (History / Examination / Investigations) instead of generic cards. */
.consultation-note {
  background:
    linear-gradient(180deg, transparent 0, transparent calc(100% - 14px),
      var(--note-stripe) calc(100% - 14px), var(--note-stripe) 100%);
  background-color: var(--note-bg);
  color: var(--note-ink);
  border: 1px solid var(--note-line);
  border-radius: var(--r-lg, 14px);
  padding: var(--s-4) var(--s-4) var(--s-5);
  box-shadow: var(--sh-md);
  margin-bottom: var(--s-3);
  position: relative;
}
.consultation-note::before {
  /* A subtle red margin line, like a paper chart. */
  content: "";
  position: absolute;
  left: 38px;
  top: 16px;
  bottom: 16px;
  width: 1px;
  background: var(--note-margin-line);
  pointer-events: none;
}
/* The chart header now holds only the contribution tally (the title + team-note
   moved to the vignette, 2026-07-15). No title → no separating border; and an
   empty tally (before anyone contributes) collapses to nothing. */
.consultation-note-head { margin-bottom: var(--s-2); }
.consultation-note-head:empty { margin: 0; }

/* Fused patient-intro "shared chart" note (2026-07-15). Lives on the
   high-contrast vignette card now, not the muted cream paper. Themed surface +
   --ink text read cleanly in light, dark AND high-contrast; a team-teal accent
   marks it as the how-you-work-together line, distinct from the patient prose. */
.vignette-shared-note {
  margin: var(--s-3) 0 0;
  padding: 10px 14px;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--surface-sunk);
  border-left: 3px solid var(--meeting);
  border-radius: var(--r-sm);
}
.vignette-shared-note strong { color: var(--ink); }
.vignette-shared-note em { font-style: italic; }
.consultation-note-icon {
  font-size: 1.3rem;
  line-height: 1;
}
/* Chart sections (History / Examination / Investigations) — each is a
   <details open> so a student can collapse a section they're done with,
   trimming the chart's scroll-height without losing the gameplay
   options. Default-open so first-time users see the full chart. */
.chart-section {
  background: transparent;
  margin-bottom: var(--s-4);
  padding: 8px 0 0;
}
.chart-section:last-child { margin-bottom: 0; }
@keyframes chart-section-reveal {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
/* Browser-native <details> collapse hides non-summary children, but the
   .btn-group's explicit display:flex (defined above) wins specificity
   against the implicit shadow-slot. Force-hide non-summary children
   when the section is collapsed. Scoped to `details.chart-section` so it
   does NOT match the workup tab PANELS (<section class="chart-section
   chart-tab-panel">), which carry no `open` attr and would otherwise have
   their contents — incl. the mounted chat — hidden by this :not([open]). */
details.chart-section:not([open]) > *:not(summary):not(.chart-section-h) {
  display: none !important;
}
/* Narrow screens: stack icon over a wrapping label so all three tabs still
   fit one row on a 375px phone (matches the .rcol-tab mobile treatment). */
@media (max-width: 720px) {
  .chart-tab {
    flex-direction: column;
    gap: 3px;
    padding: 8px 6px;
    font-size: 0.72rem;
    line-height: 1.15;
    text-align: center;
  }
  .chart-tab-label { white-space: normal; }
}

/* --- Module A per-bullet progress checklist --- */
.modA-bullet-progress {
  list-style: none;
  padding: 0;
  margin: 0 0 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  font-size: 0.9rem;
  color: var(--muted);
}
.modA-bullet-progress li {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.modA-bullet-progress li::before {
  content: "☐";
  font-size: 1.1em;
  color: var(--muted);
}
.modA-bullet-progress li.is-done {
  color: var(--ink);
  font-weight: 600;
}
.modA-bullet-progress li.is-done::before {
  content: "☑";
  color: var(--success, #2e7d32);
}

/* Glossed buttons get a tiny dotted underline as a "hover for more"
   cue — sim 2026-05-19 feature for low-English students. Title
   attribute carries the EN+JP explanation; this is just the visual
   handle so the user knows hovering / long-pressing is useful. */
/* Glossary marker: a REAL child <span.glossary-marker> (with role=img +
   aria-label) is now rendered by _annotateButtonWithGlossary so the gloss
   is keyboard/touch/SR-discoverable, not hover-only (round4-a11y Rec 5).
   The decorative ::after "ⓘ" is therefore suppressed when the real marker
   is present to avoid a double badge; kept as a fallback if JS didn't add
   the span (e.g. an externally-supplied button). */
.req-btn.has-glossary:not(:has(.glossary-marker))::after {
  content: " ⓘ";
  font-size: 0.85em;
  color: var(--muted);
  margin-left: 4px;
  text-decoration: none;
}
.req-btn.has-glossary:not(:has(.glossary-marker)):hover::after {
  color: var(--primary, #1763a6);
}
.glossary-marker {
  font-size: 0.85em;
  margin-left: 2px;
  line-height: 1;
  /* The 📖 is now a real tap target INSIDE the reveal button: tapping it opens
     the plain-language gloss (the click handler stopPropagation()s so the
     reveal itself doesn't fire). Padding gives touch a comfortable hit area
     without making the span an interactive element (it stays a <span>, so the
     button keeps a single interactive node — valid HTML). */
  padding: 3px 5px;
  border-radius: var(--r-sm, 6px);
  cursor: help;
}
.glossary-marker:hover { background: rgba(0, 0, 0, 0.06); }
/* Visual gloss popover (touch + keyboard). Screen-reader users get the same
   text via the button's aria-description, so the popover is aria-hidden to
   avoid a double announcement. Dismissible (Escape / outside tap / scroll),
   per WCAG 1.4.13 content-on-focus. */
.gloss-pop {
  position: fixed;
  z-index: 60;
  max-width: min(280px, 90vw);
  padding: 8px 10px;
  /* the ONLY surface that renders FR + JA glosses: Latin face first so
     EN/FR glyphs match the UI, then the system JP stack so 日本語 renders
     cleanly (Hiragino / Yu Gothic / Noto Sans JP) — zero font download */
  font-family: var(--font-cjk);
  font-size: 0.8rem;
  line-height: var(--lh-snug);
  white-space: pre-line;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}
.gloss-pop[hidden] { display: none; }

/* Reading-aid popover (lang-reader.js). Inherits the .gloss-pop frame; this
   only adds the term/definition typographic split. Decorative + pointer-driven
   (aria-hidden), so it never double-announces for screen-reader users. */
.reader-pop { white-space: normal; max-width: min(300px, 92vw); }
.reader-pop .reader-term { color: var(--primary, #1763a6); font-weight: 700; }
.reader-pop .reader-def { color: var(--ink); }
/* "Word help" toggle in the settings cog: checkbox + sub-hint line. */
.global-settings-check {
  width: 18px;
  height: 18px;
  accent-color: var(--primary, #1763a6);
  cursor: pointer;
  flex: 0 0 auto;
}
.global-settings-hint {
  margin: -2px 0 2px;
  font-size: 0.78rem;
  color: var(--muted, #6a665d);
  line-height: 1.35;
}

/* History sub-grouping (round4-a11y Rec 4): the dense History group is
   split into a short visible cluster + a collapsed "More questions"
   <details>, cutting the at-once button count for the A2/B1 cohort. Both
   sub-groups inherit the .btn-group flow; the more-toggle is a quiet,
   full-width affordance, not another loud button. */
.history-sub-primary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2, 6px);
}
.history-sub-more {
  margin-top: var(--s-2, 6px);
}
.history-sub-summary {
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
  padding: 4px 2px;
  list-style: revert;
}
.history-sub-more[open] .history-sub-summary {
  margin-bottom: var(--s-2, 6px);
}
.history-sub-more .btn-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2, 6px);
}

/* Clinical-category clusters (2026-05-26): History / Examination reveal
   buttons are grouped under a small category heading so the dense button wall
   reads as a few labelled clinical sections. Display-only — item indices are
   unchanged. Each category is a collapsible <details> (2026-05-27 dry-run): the
   summary is the clinical label, and its buttons live in .req-category-items so
   students can close categories they're done with. */
.req-category {
  margin-top: var(--s-3, 10px);
}
.req-category:first-child { margin-top: 0; }
.req-category-label {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
  padding: var(--s-1, 4px) 0;
  list-style: revert;   /* keep the native disclosure triangle (global reset hides it) */
}
.req-category-label:hover { color: var(--ink, #2a2a2a); }
.req-category-label:focus-visible {
  outline: 2px solid var(--primary, #1763a6);
  outline-offset: 2px;
  border-radius: var(--r-sm, 6px);
}
.req-category-items {
  display: flex;
  flex-direction: column;
  gap: var(--s-2, 6px);
  margin-top: var(--s-2, 6px);
}

/* Inline citation badges on revealed findings — sim 2026-05-19
   feature for `checks_evidence` personas. Sits between the answer
   body and the author byline; visually de-emphasized but tappable. */
.req-inline-cite {
  display: inline-block;
  margin: 0 6px;
  padding: 1px 8px;
  background: var(--surface-sunk, #f3f0e8);
  border: 1px solid var(--line, #e2dccf);
  border-radius: 999px;
  font-size: 0.78rem;
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

/* --- Observer button + body-level state --- */
.observer-btn {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line-strong, #c9bd9c);
  border-radius: var(--r-pill, 999px);
  padding: 6px 12px;
  font-weight: 500;
  cursor: pointer;
  min-height: 44px;
}
.observer-btn.is-active {
  background: var(--surface-sunk, #f3f0e8);
  font-weight: 600;
}
body[data-observer="1"] .decisions-card,
body[data-observer="1"] #modA-bullet-progress {
  /* soften participation prompts but keep them readable */
  opacity: 0.65;
}
body[data-observer="1"] #modA-next-step,
body[data-observer="1"] #modB-next-step {
  /* coach prompts can be muted when in observer mode */
  filter: grayscale(0.4);
}

/* --- End-of-session poll --- */
.endpoll-card {
  border-left: 4px solid var(--accent, #e08a1e);
}
.endpoll-card h3 { margin-top: 0; }
#endpoll-submit { min-height: 44px; }
.endpoll-thanks {
  color: var(--success, #2e7d32);
  font-weight: 600;
  margin-top: 8px;
}

/* --- Counter-bullet replies under group answers --- */
.entry-disagree { color: var(--meeting, #8a2017); }
.entry-agree { color: #2e7d4f; }
.reply-arrow {
  color: var(--muted);
  margin-right: 4px;
}
.counter-bullet-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
  padding: 8px;
  background: var(--surface-sunk, #f9f7f1);
  border: 1px dashed var(--meeting, #8a2017);
  border-radius: var(--r-sm, 8px);
}
.counter-bullet-form textarea {
  resize: vertical;
  padding: 6px 8px;
  border: 1px solid var(--line, #e2dccf);
  border-radius: var(--r-sm, 8px);
}
.counter-bullet-form button {
  align-self: flex-start;
  min-height: 38px;
}

/* --- collapsible info cards (Module B safety-note, future Module A
   vignette, etc.). Default-open <details> with the heading-as-summary
   pattern: read once at the start, then a single click moves the card
   out of the way. Sim 2026-05-18: Module B was 4.5× viewport tall
   because every informational block stayed permanently expanded. --- */
.collapsible-card > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  /* Match the card padding so the click-target covers the whole row,
     not just the heading line. */
}
.collapsible-card > summary::-webkit-details-marker { display: none; }
.collapsible-card > summary::marker { content: ""; }
.collapsible-card > summary::before {
  content: "▸";
  display: inline-block;
  width: 1em;
  font-size: 0.85em;
  color: var(--muted);
  transition: transform 0.15s ease;
  margin-right: 4px;
}
.collapsible-card[open] > summary::before {
  transform: rotate(90deg);
}
.collapsible-card:not([open]) > *:not(summary) {
  display: none !important;
}

/* ── Working hypotheses panel (PBL 7-jump scaffold step) ── */
.chart-hypotheses {
  background: rgba(46, 159, 223, 0.06);
  border-radius: var(--r-md);
  padding: 10px 14px;
  margin-bottom: var(--s-3);
}
.hypothesis-list {
  list-style: none;
  margin: 6px 0 8px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.hypothesis-list:empty { display: none; }
.hypothesis-list li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--card, #fff);
  border: 1px solid var(--primary, #2e9fdf);
  border-radius: var(--r-pill, 999px);
  padding: 5px 10px 5px 12px;
  font-size: 0.88rem;
  color: var(--ink);
  font-weight: 500;
}
.hypothesis-list li .by {
  font-size: 0.74rem;
  color: var(--muted);
  font-weight: 400;
}
.hypothesis-list li .del {
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1rem;
  padding: 0 2px;
  border-radius: 3px;
}
.hypothesis-list li .del:hover { color: var(--key, #c47a5a); }
.hypothesis-empty {
  font-style: italic;
  color: var(--muted);
  font-size: 0.88rem;
  margin: 4px 0 8px;
}
.hypothesis-add {
  display: flex;
  gap: 8px;
  align-items: center;
}
.hypothesis-add input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font: inherit;
  font-size: 0.92rem;
}
.hypothesis-add input:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
  border-color: var(--primary);
}
.hypothesis-add button {
  flex: 0 0 auto;
  background: var(--primary);
  color: #fff;
  border: none;
  border-radius: var(--r-pill);
  padding: 7px 14px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.hypothesis-add button:hover { background: var(--primary-hover); }

/* Reference toolbar (2026-06-02) — the three reference lookups (historical
   context / guidelines / recap) as a single row of buttons at the TOP of each
   module; each button toggles its panel below (accordion — one open at a time;
   wireReferenceToolbars() in script.js). Replaced a stack of cards that used to
   sit below the workflow. */
/* Floating reference bar (user 2026-06-25): the row of three lookup buttons
   (Historical context / Guidelines / Recap) pins to the top of the viewport so
   it's always reachable while students scroll the whole module. The section is a
   full-width band directly inside the stage (above .columns), so its sticky range
   spans the entire module scroll — the buttons stay on screen throughout. Opaque
   themed background + a soft hairline shadow keep content scrolling underneath
   visually separated. Stacks ABOVE page content + the right-column sticky tabs
   (z 5) but BELOW the global pills (z 1500), banners (z 80) and modals. */
.reference-section {
  position: sticky;
  top: 0;
  z-index: 30;
  margin: 0 0 var(--s-4);
  padding: var(--s-2) 0;
  background: var(--bg);
  box-shadow: 0 4px 10px -6px rgba(0, 0, 0, 0.25);
}
.reference-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
/* While the bar is pinned and a panel is open, cap the panel so a long reference
   text can't blanket the screen — it scrolls inside the floating bar instead,
   leaving the student's own work visible below. */
.reference-section .reference-panel:not([hidden]) {
  max-height: min(58vh, 520px);
  overflow: auto;
}
.reference-btn {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: 0.92rem;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.reference-btn:hover { background: var(--n-100); }
/* Active (open) button — accent ring + accent text on the card surface. Avoids
   white-on-(--primary), which fails contrast in the dark theme (--primary→cyan). */
.reference-btn[aria-expanded="true"] {
  background: var(--card);
  border-color: var(--primary);
  color: var(--primary);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.reference-btn-emoji { font-size: 1.05em; line-height: 1; }
/* "Your role" tab — highlighted once a role is held/assigned (showRoleObjective
   toggles .has-role). A warm amber ring + dot says "this is yours" without
   colliding with the blue open-state ring (the dot always shows when a role is
   held; the amber ring only when the tab is closed, so an open tab keeps its
   blue accent). Amber is hardcoded (theme-independent) like the attention dot. */
.reference-btn--role.has-role::after {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #e0a86b;
  box-shadow: 0 0 0 2px var(--card);
  flex: 0 0 auto;
}
.reference-btn--role.has-role:not([aria-expanded="true"]) {
  border-color: #e0a86b;
  box-shadow: inset 0 0 0 1px #e0a86b;
}
/* Your-role panel — a transparent container (no .card) so the moved role cards
   keep their own boxes; stack them with a consistent gap. */
.role-panel { display: flex; flex-direction: column; gap: var(--s-3); }
.reference-panel { margin-top: var(--s-3); }
.reference-panel[hidden] { display: none; }
/* Beautiful line-breaking for the reference prose (user request 2026-06-02).
   The dense historical paragraphs wrapped purely by line-width, cutting phrases
   at awkward mid-clause points; text-wrap:pretty lets the engine choose nicer
   break points and avoids lone last-line words. hyphens:none stops mid-word
   hyphenation. Progressive enhancement — engines without text-wrap:pretty keep
   the previous normal wrapping, so nothing regresses. Scoped to the prose +
   did-you-know callouts; the citation list keeps its own per-entry layout. */
.reference-panel p,
.reference-panel .info-list li {
  text-wrap: pretty;
  hyphens: none;
}
@media (max-width: 520px) {
  .reference-btn { flex-basis: 100%; }   /* stack to full-width buttons on phones */
}

/* Floating "back to top" button (2026-06-02) — the stage card no longer pins to
   the top, so this gives a one-tap return once the user has scrolled down. Shown
   via the .is-visible class toggled by wireBackToTop(). The ink/card inverse
   keeps AA contrast in light, dark and high-contrast themes. */
.back-to-top {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 60;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--ink);
  color: var(--card);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--sh-md);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 160ms ease;
}
.back-to-top.is-visible { opacity: 1; visibility: visible; transform: none; }
@media (max-width: 720px) {
  .back-to-top { bottom: 84px; }   /* clear the sticky bottom coach / mobile tab bar */
}
@media (prefers-reduced-motion: reduce) {
  .back-to-top { transition: opacity 160ms ease, visibility 160ms ease; transform: none; }
}

/* Sticky-bottom coach card on mobile (per mobile-first specialist
   review 2026-05-18): on small viewports the coach lived at the top
   and disappeared as soon as the student scrolled to tap a button —
   exactly the moment they most needed it. Pin it to the bottom of
   the viewport on narrow screens so 'what to do next' is always
   thumb-reachable. Desktop layout keeps the inline placement. */
@media (max-width: 720px) {
  .next-step-coach {
    position: sticky;
    bottom: 0;
    z-index: 50;
    margin-left: -1px;
    margin-right: -1px;
    border-radius: var(--r-md) var(--r-md) 0 0;
    box-shadow: 0 -2px 12px rgba(0,0,0,.10);
  }
  /* Add a touch of bottom padding to stage content so the sticky
     coach doesn't obscure the last items. */
  .stage:not(.hidden) {
    padding-bottom: 100px;
  }
}

/* A brief "synthesis unlocked — Discussion is open" banner that
   appears at the top of the Discussion panel when the platform
   auto-switches there after the clinical synthesis is revealed. It
   fades after a few seconds via the .auto-fade class. */
.synthesis-unlocked-banner {
  background: var(--ok-50);
  border-left: 4px solid var(--ok);
  border-radius: var(--r-sm);
  padding: 10px 14px;
  margin: 0 0 var(--s-3);
  font-size: 0.92rem;
  color: var(--ink);
  font-weight: 500;
  animation: synthesis-banner-in 280ms var(--ease-out);
}
.synthesis-unlocked-banner.auto-fade {
  animation: synthesis-banner-out 600ms var(--ease-out) forwards;
  animation-delay: 6s;
}
.synthesis-unlocked-banner strong { color: var(--ok-strong); }
.synthesis-unlocked-banner.hidden { display: none; }
@keyframes synthesis-banner-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}
@keyframes synthesis-banner-out {
  to { opacity: 0; transform: translateY(-6px); pointer-events: none; }
}

/* Structured Group-answers form (per-bullet sections). One labelled
   section per bullet question; each holds its own list + input row. */
.answers-card-bulleted { display: flex; flex-direction: column; gap: var(--s-3); }
.answer-bullet { display: flex; flex-direction: column; gap: 6px; }

/* ---------- saved confirmation ---------- */
.copy-ok { color: var(--ok); font-size: 0.86rem; margin-left: 10px; font-weight: 600; }

/* ---------- footer ---------- */
footer {
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--s-4) var(--s-5) var(--s-6);
  color: var(--muted);
  font-size: 0.82rem;
}

/* ---------- stage card / controls ---------- */
.stage-card { border-left: 4px solid var(--ok); box-shadow: var(--sh-md); }
.stage-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  /* containing block for the leaderboard dropdown panel below */
  position: relative;
}
.stage-controls { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; row-gap: 6px; }
/* The "Stage X of 4" indicator is now sr-only (hidden visually, 2026-06-02), so
   it no longer occupies the left of the row. The participant controls used to
   grab the right edge with `margin-left:auto`, but since 2026-07-15 the inline
   Live-leaderboard disclosure owns the right edge (see .leaderboard-inline
   below), so the score chip + controls + Details toggle now cluster together on
   the left of the single-line header. */
.stage-controls--participant { margin-left: 0; }

/* ── Control-row clarity (UX-overload #5) ──────────────────────────────────
   Scoped to the PARTICIPANT row (.stage-controls--participant); the admin
   stage-controls shares only the base class and is untouched.

   Desktop: the "More" wrapper + its menu are display:contents, so Teams /
   observe / leave flow inline in the control row exactly as before, and the
   toggle button is hidden. (Plain div + button, not <details>: a closed
   <details> hides its content even when the element is display:contents on
   WebKit / Android, which would hide the buttons on desktop too.) */
.stage-controls--participant .stage-overflow { display: contents; }
.stage-controls--participant .stage-overflow-menu { display: contents; }

@media (max-width: 720px) {
  /* A compact "··· More" chip that reveals Teams / observe / leave on tap
     (initStageOverflow toggles .is-open). */
  .stage-controls--participant .stage-overflow { display: block; position: relative; }
  /* the menu is collapsed by default, shown as a popover when open */
  .stage-controls--participant .stage-overflow-menu { display: none; }
}

.presence-line {
  margin-top: var(--s-3);
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  flex-wrap: wrap;
}
.presence-label {
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.info-list { margin: var(--s-2) 0; padding-left: 22px; }
.info-list li { margin-bottom: 6px; }

.instructions { background: var(--surface-sunk); }
.instructions summary { cursor: pointer; font-size: 0.98rem; }
.instructions p { margin: var(--s-2) 0; font-size: 0.95rem; }
.instructions-open h3 { font-size: 1rem; margin: var(--s-4) 0 6px; }
.instructions-open h3:first-child { margin-top: 0; }

/* ---------- collaborative group-answers ---------- */
.answers-card { border-left: 4px solid var(--caen-line); }
.answers-list { list-style: none; margin: 0 0 var(--s-3); padding: 0; }
.answers-empty { color: var(--muted); font-style: italic; font-size: 0.92rem; }
.entry-act {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.82rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 4px 6px;
  min-height: 40px;
}
.entry-act:hover { color: var(--key); }
.answer-add { display: flex; gap: var(--s-2); }
.answer-add input { flex: 1; padding: 10px 12px; }
.typing-line { font-size: 0.82rem; color: var(--muted); font-style: italic; margin: 4px 0; }

/* ---------- contribution tally ---------- */
.contrib-tally {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
}
.contrib-tally:empty { display: none; }
.contrib-label {
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-right: 2px;
}
.contrib-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 3px 10px;
  font-size: 0.8rem;
}
.view-btn { background: var(--ink) !important; color: var(--bg) !important; }
.view-btn:hover { filter: brightness(1.12); } /* theme-proof (no #000) */
.call-badge {
  background: var(--key);
  color: var(--key-50);
  font-size: 0.74rem;
  font-weight: 700;
  border-radius: var(--r-pill);
  padding: 2px 9px;
  margin-left: 10px;
  box-shadow: var(--sh-sm);
}
.call-detail {
  display: block;
  font-size: 0.84rem;
  color: #9a2a20;
  font-weight: 600;
  margin-top: 4px;
}
.call-detail .call-msg { font-weight: 400; font-style: italic; color: var(--ink); }

/* ---------- room setup / waiting room / prestart ---------- */
.inline-label { font-weight: 600; font-size: 0.92rem; margin-right: var(--s-3); }
.inline-label select { padding: 9px 11px; margin-left: 6px; }
.prestart-person {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.95rem;
}
.prestart-person:last-child { border-bottom: none; }

/* Sticky-presence status dot — replaces the previous binary "in
   pool/not in pool" with a 3-state online / blip / gone signal so the
   admin sees who's actually in the room despite Firebase onDisconnect
   firing on every micro network gap (2026-05-18 user report). */
.prestart-status-dot {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
  background: var(--muted);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.06);
}
.prestart-status-dot-online {
  background: var(--ok, #2a9d8f);
  box-shadow: 0 0 0 2px rgba(42, 157, 143, 0.18);
}
.prestart-status-dot-blip {
  background: var(--caen-line, #e7b800);
  box-shadow: 0 0 0 2px rgba(231, 184, 0, 0.18);
  animation: prestart-blip-pulse 1.6s ease-in-out infinite;
}
.prestart-status-dot-gone {
  background: transparent;
  border: 1.5px dashed var(--muted);
}
@keyframes prestart-blip-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}
.prestart-person.prestart-person-blip { color: var(--ink); opacity: 0.92; }
.prestart-person.prestart-person-gone {
  color: var(--muted);
  font-style: italic;
}
.prestart-status-age {
  margin-left: auto;
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.prestart-remove-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
  border-radius: 50%;
  width: 22px;
  height: 22px;
  padding: 0;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  margin-left: 4px;
}
.prestart-remove-btn:hover {
  border-color: var(--key, #c47a5a);
  color: var(--key, #c47a5a);
  background: rgba(196, 122, 90, 0.06);
}
.prestart-remove-btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* ---------- stage nav / call-a-facilitator / teams ---------- */
#prev-btn, #next-btn, .call-btn, #leave-btn, .teams-btn { min-height: 44px; }
.call-btn {
  background: var(--card);
  border: 1px solid var(--caen-line);
  color: var(--caen-ink);
  border-radius: var(--r-sm);
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  padding: 9px 14px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-soft);
}
.call-btn:hover { background: var(--caen-50); }
.call-btn.pending {
  background: var(--caen-500);
  color: #4a3a10;
  border-color: var(--caen-line);
  animation: call-pulse 1.8s var(--ease-soft) infinite;
}
.call-btn.pending:hover { background: var(--caen-500); }

.teams-btn {
  background: var(--card);
  color: var(--nagoya-700);
  border: 1px solid var(--primary);
  border-radius: var(--r-sm);
  padding: 8px 14px;
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  display: inline-block;
  transition: background var(--dur-fast) var(--ease-soft);
}
.teams-btn:hover { background: var(--nagoya-50); }
#teams-btn-waiting { margin-top: var(--s-3); }

/* ---------- admin "More tools" menu: functional collapse state ----------
   The facilitator dashboard's decorative styling lives in the LAZILY-loaded
   admin.css (CanamedLoader.ensureAdminStyles), but the overflow menu's
   open/closed mechanics must ship with the shell: otherwise the closed menu's
   tools flash visible until admin.css arrives (and e2e reveals #admin-app
   without going through the loader). Panel decoration stays in admin.css. */
.admin-overflow { position: relative; display: inline-block; }
.admin-overflow-menu { display: none; }
.admin-overflow.is-open .admin-overflow-menu { display: flex; }

/* ---------- super-admin: purge ---------- */
.purge-row { margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px solid var(--line); }
.purge-row .hint { margin-top: 6px; }

/* ---------- privacy note / anon toggle ---------- */
.privacy-note {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 10px 13px;
  margin: var(--s-4) 0 0;
  font-size: 0.86rem;
  color: var(--muted);
}
.privacy-note summary { cursor: pointer; font-weight: 600; color: var(--ink); }
.privacy-note p { margin: var(--s-2) 0 0; }

/* ---------- late-joiner banner ---------- */
.late-banner {
  background: var(--nagoya-50);
  border: 1px solid #b9d8ee;
  border-left: 4px solid var(--primary);
  border-radius: var(--r-md);
  padding: 13px 15px;
  margin-bottom: var(--s-4);
  font-size: 0.95rem;
}
.late-banner button {
  background: none;
  border: none;
  color: var(--nagoya-700);
  text-decoration: underline;
  cursor: pointer;
  font-size: 0.88rem;
  padding: 2px 0;
  min-height: 40px;
}

/* ---------- room view layout: main + admin sidebar ---------- */
#app { display: flex; gap: var(--s-4); align-items: flex-start; }
#room-main { flex: 1; min-width: 0; }
body.admin-room #app { max-width: 1380px; }
body.admin-room .columns { grid-template-columns: 1fr; }
@media (max-width: 960px) {
  #app { flex-direction: column; }
}


.answer-text, .chip, #findings-log .a{
  overflow-wrap: break-word;
  word-break: normal;
}
.header-inner > div:first-child { min-width: 0; }


#waiting:not(.hidden), #app:not(.hidden){
  animation: view-in var(--dur-view) var(--ease-out);
}
@keyframes view-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* stage navigation: the revealed .stage slides in */
.stage:not(.hidden) { animation: stage-in var(--dur-base) var(--ease-out); }
@keyframes stage-in {
  from { opacity: 0; transform: translateX(10px); }
  to   { opacity: 1; transform: translateX(0); }
}
/* newly revealed findings flash in so the whole room notices */
@keyframes finding-in {
  from { background: #fff3cf; }
  to   { background: var(--surface-sunk); }
}
/* "call a facilitator" pending: a calm steady pulse - it stays "live" */
@keyframes call-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(231,184,0,0); }
  50%      { box-shadow: 0 0 0 4px rgba(231,184,0,0.35); }
}
/* breathe trough raised from 0.74 to 0.92 so rendered contrast stays >= 4.5:1
   throughout the cycle on the darkened #5a4519 foreground (WCAG AA) */
@keyframes breathe { 0%, 100% { opacity: 1; } 50% { opacity: 0.92; } }
/* connection lost: one short attention shake */
.conn-badge.conn-lost { animation: shake 0.4s var(--ease-soft); }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-3px); }
  75% { transform: translateX(3px); }
}

/* ==========================================================================
   FOCUS - a single strong, surface-independent ring
   ========================================================================== */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 3px;
}
body > header :focus-visible, .mode-badge:focus-visible, .conn-badge:focus-visible,
.view-btn:focus-visible{
  outline-color: #fff;
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 680px) {
  main, #lobby, footer { padding-left: 14px; padding-right: 14px; }
  .header-inner { padding-left: 14px; padding-right: 14px; }
  .card { padding: var(--s-4); }
}
@media (max-width: 520px) {
  /* let the badges drop below the title instead of squeezing it on a phone */
  .header-inner { flex-wrap: wrap; }
  .header-right-wrap { width: 100%; }
  .answer-add { flex-direction: column; align-items: stretch; }
}
@keyframes pop { 0% { transform: scale(1); } 40% { transform: scale(1.18); } 100% { transform: scale(1); } }
.team-name-card { border-left: 4px solid var(--caen-line); }

/* --- historical context cards --- */
.history-card { background: var(--surface-sunk); }
.history-card > summary {
  cursor: pointer;
  font-size: 0.98rem;
  list-style: none;
}
.history-card > summary::-webkit-details-marker { display: none; }
.history-card > summary::before { content: "\25B8  "; color: var(--muted); }
.history-card[open] > summary::before { content: "\25BE  "; }
/* No max-width here (2026-06-02): the 70ch cap was a leftover from when the
   reference cards lived in a narrow sidebar column. Now that the panel is a
   full-width accordion at the top of the module, the cap squeezed the historical
   prose into a ~574px column with empty space beside it, forcing many short,
   phrase-cutting line breaks while the citation list below spanned the full
   width. Letting the prose use the full panel width (like the citations) means
   fewer, cleaner wraps. */
.history-card p { font-size: 0.95rem; }
.didyouknow {
  background: var(--caen-50);
  border-left: 3px solid var(--caen-line);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  font-size: 0.92rem;
}
.pts-btn {
  background: var(--caen-50) !important;
  color: var(--caen-ink) !important;
  border: 1px solid var(--caen-line) !important;
  box-shadow: none !important;
}
.pts-btn:hover { background: #fbeec4 !important; }
.pts-tag {
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 7px 11px;
  min-height: 36px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft);
}
.pts-tag:hover:not(:disabled) { background: var(--nagoya-50); border-color: var(--primary); }
.pts-tag:disabled { opacity: 0.5; cursor: not-allowed; }
.pts-undo {
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.8rem;
  text-decoration: underline;
  cursor: pointer;
  min-height: 36px;
  margin-left: auto;
}
.pts-undo:hover { color: var(--key); }

/* --- celebration: confetti burst --- */
.burst {
  position: fixed;
  left: 50%;
  top: 36%;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 9999;
}
.confetti {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 2px;
  opacity: 0;
  animation: confetti-fly 1100ms var(--ease-out) forwards;
  animation-delay: var(--d);
}
@keyframes confetti-fly {
  0% { opacity: 1; transform: translate(0, 0) rotate(0); }
  100% { opacity: 0; transform: translate(var(--x), 150px) rotate(var(--r)); }
}

/* --- toast --- */
.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translate(-50%, 140%);
  background: var(--n-900);
  color: #fff;
  font-size: 0.92rem;
  font-weight: 600;
  padding: 11px 18px;
  border-radius: var(--r-pill);
  box-shadow: var(--sh-lg);
  z-index: 9998;
  max-width: 90vw;
  text-align: center;
  transition: transform var(--dur-base) var(--ease-out);
}
.toast.show { transform: translate(-50%, 0); }

/* --- a one-shot "stamp" when a finding button is completed --- */
@keyframes done-stamp {
  0% { transform: scale(1); }
  45% { transform: scale(0.96); }
  100% { transform: scale(1); }
}
.req-btn.done { animation: done-stamp 260ms var(--ease-out); }

/* ==========================================================================
   "WOW" REDESIGN LAYER  -  hero, glass surfaces, display type, SVG imagery,
   richer light. Appended last so it overrides the base rules above.
   ========================================================================== */

/* --- a warm, visible blue+amber wash so the page never reads as flat grey --- */
body {
  background:
    radial-gradient(1200px 780px at 88% -8%, rgba(46,159,223,.30), transparent 58%),
    radial-gradient(900px 680px at -4% 108%, rgba(224,138,30,.24), transparent 60%),
    var(--bg);
  background-attachment: fixed;
}

/* --- the signature lobby hero (two columns: dark SVG aside + join card) --- */
#lobby {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: 0;
  align-items: stretch;
  max-width: 980px;
  padding: var(--s-7) var(--s-5);
}
.hero-aside {
  background:
    radial-gradient(420px 300px at 18% 8%, rgba(46,159,223,.55), transparent 60%),
    radial-gradient(360px 320px at 92% 96%, rgba(231,184,0,.30), transparent 62%),
    linear-gradient(155deg, #0c2f52, #08243f);
  border-radius: var(--r-lg) 0 0 var(--r-lg);
  padding: var(--s-7) var(--s-6);
  color: #fff;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s-2);
}
.hero-aside::after {
  content: "";
  position: absolute;
  inset: -40%;
  background: radial-gradient(closest-side, rgba(231,184,0,.16), transparent);
  animation: drift 16s var(--ease-soft) infinite alternate;
}
.hero-aside > * { position: relative; }
.hero-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--caen-500);
  margin: 0;
}
/* serif display voice (a <p> — outside the global h1-h3 serif rule) */
.hero-title {
  font-family: var(--font-serif);
  font-size: var(--fs-7);
  line-height: var(--lh-tight);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  margin: 0.1em 0;
}
.hero-tag { font-size: 0.88rem; opacity: 0.82; margin: 0; }
.lobby-card {
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  max-width: none;
  box-shadow: var(--sh-lg);
  animation: view-in var(--dur-view) var(--ease-out) 120ms both;
}
@media (max-width: 760px) {
  #lobby { grid-template-columns: 1fr; max-width: 460px; }
  .hero-aside { border-radius: var(--r-lg) var(--r-lg) 0 0; padding: var(--s-6); }
  .hero-title { font-size: var(--fs-6); }
  .lobby-card { border-radius: 0 0 var(--r-lg) var(--r-lg); }
}

/* --- material depth: SOLID surfaces (glass-on-white fails contrast + perf) --- */
.stage-card, .leaderboard-card {
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh-md);
}
.card { transition: box-shadow var(--dur-base) var(--ease-soft),
                    transform var(--dur-base) var(--ease-soft); }
.card:hover { box-shadow: var(--sh-lg); }
/* vignettes are plain editorial cards — the stage card above carries the
   stage accent and the serif heading carries the hierarchy (the old 4px
   gradient side-stripe + corner watermark were decoration, removed) */
.vignette { border-left: none; position: relative; }

/* --- typography with more presence --- */
.card h2 { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.025em; line-height: 1.18; }
.card h3 { font-size: 1.08rem; font-weight: 700; }
h2, h3 { text-wrap: balance; }

/* --- primary CTA hover glow (fill comes from the primary button role:
   solid --primary/--on-primary — a gradient's light end broke AA in light) --- */
#join-btn, #start-session-btn {
  box-shadow: 0 4px 14px color-mix(in srgb, var(--nagoya-600) 32%, transparent);
}
#join-btn:hover, #start-session-btn:hover {
  box-shadow: 0 6px 20px color-mix(in srgb, var(--nagoya-600) 46%, transparent);
}
.req-btn:hover {
  box-shadow: 0 0 0 3px var(--nagoya-50), 0 4px 12px rgba(46,159,223,.18);
}

/* --- inline-SVG imagery --- */
.ico { width: 1.3rem; height: 1.3rem; flex-shrink: 0; }
.patient-av {
  width: 56px; height: 56px;
  float: right;
  margin: 0 0 var(--s-2) var(--s-3);
  filter: drop-shadow(var(--sh-sm));
}
.empty-art { width: 72px; height: 54px; display: block; margin: 0 auto 6px; }
.wrap-art { width: 120px; height: 70px; display: block; margin: 0 0 var(--s-2); }

/* --- "wow" motion --- */
@keyframes mark-in {
  from { opacity: 0; transform: scale(0.7) rotate(-12deg); }
  to { opacity: 1; transform: none; }
}
@keyframes draw {
  from { stroke-dashoffset: 145; }
  to { stroke-dashoffset: 0; }
}
@keyframes pop-disc {
  0% { transform: scale(0); }
  70% { transform: scale(1.15); }
  100% { transform: scale(1); }
}
@keyframes drift {
  from { transform: translate(-6%, -4%); }
  to { transform: translate(6%, 5%); }
}
@keyframes lb-rise {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

/* ==========================================================================
   ACCESSIBILITY MEDIA QUERIES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  /* "finished, not dead": kill movement & infinite loops, but keep gentle
     opacity fades so views/stages still feel intentional, and keep instant
     state colours so feedback still lands. */
  *, *::before, *::after {
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }
  /* no looping background motion - vestibular safety */
  .hero-aside::after, .stage-wait:not(:empty), .call-btn.pending,
  .lb-bar span::after, .motif-drift {
    animation: none !important;
  }
  /* one-shot enters become a short, still cross-fade */
  #waiting:not(.hidden), #app:not(.hidden), #admin-app:not(.hidden),
  .stage:not(.hidden), .leaderboard-card[open] .lb-row, .obj-row,
  .card, .lobby-card {
    animation: rm-fade 140ms ease both !important;
  }
  /* celebrations: no confetti / no transform spectacle, but the colour-state
     change still happens instantly (handled by burst()/reducedMotion() in JS) */
  .confetti, .score-chip.pop, .req-btn.done, .toast {
    animation: none !important;
  }
  button:active:not(:disabled) { transform: none; }
}
@keyframes rm-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-contrast: more) {
  .card, .req-btn, .recap-table th, .recap-table td { border-width: 2px; }
}

/* ==========================================================================
   REWORKED SCORING UI  -  live objectives panel, cooperative leaderboard,
   participation dots, formative toast, "order matters" cue, wrap-up recap.
   Appended last so it overrides earlier scoring styles.
   ========================================================================== */

/* --- the live "how points work" objectives panel --- */
.objectives {
  margin-top: var(--s-3);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
}
.obj-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
  font-size: 0.95rem;
}
.obj-head strong { letter-spacing: -0.01em; }
.obj-tally {
  font-weight: 800;
  font-size: 0.82rem;
  /* --ok on --ok-50 = 4.16:1, fails WCAG AA 4.5:1; use --ok-strong */
  color: var(--ok-strong, #176b3a);
  background: var(--ok-50);
  border-radius: var(--r-pill);
  padding: 2px 10px;
  white-space: nowrap;
}
.obj-list { display: flex; flex-direction: column; }
.obj-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 6px 4px;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.obj-row:last-child { border-bottom: none; }
.obj-mark {
  flex-shrink: 0;
  width: 1.5em; height: 1.5em;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-weight: 800;
  font-size: 0.8rem;
  color: var(--muted);
  background: var(--surface-sunk);
  border: 1px solid var(--line-strong);
}
.obj-row.done { color: var(--ink); }
.obj-row.done .obj-mark {
  color: #fff;
  background: var(--ok);
  border-color: var(--ok);
  animation: obj-pop 320ms var(--ease-out);
}
@keyframes obj-pop { 0% { transform: scale(0.5); } 60% { transform: scale(1.18); } 100% { transform: scale(1); } }
.obj-label { flex: 1; }
.obj-row.done .obj-label { font-weight: 600; }
.obj-pts {
  flex-shrink: 0;
  font-weight: 800;
  font-size: 0.8rem;
  color: var(--muted);
}
.obj-row.done .obj-pts { color: var(--ok); }
.obj-note {
  margin: var(--s-2) 0 0;
  font-size: 0.84rem;
  color: var(--muted);
}

/* Live-update feedback (2026-06-03, user: "the score doesn't feel like it
   updates live"). The board re-renders silently on every score change; without
   a cue the change is easy to miss. A room whose total just went UP gets a
   one-shot ring pulse, and the cohort "Together" number pops — so the liveness
   is visible. Classes are applied by renderLeaderboard() to freshly-built nodes,
   so the animation plays once on insert. */
@keyframes lb-bump {
  0%, 100% { box-shadow: 0 0 0 0 rgba(34,160,90,0); }
  20%, 60% { box-shadow: 0 0 0 2px var(--ok); }
}
@keyframes lb-together-pop {
  0%, 100% { transform: none; }
  30% { transform: scale(1.12); }
}

/* Attention "blink" for the Module B per-role section that appears when a
   student picks a role (2026-06-03 user request: "must clignoter so they
   understand they have to click on it and read it"). Triggered by
   flashRoleSections() on a deliberate role pick / swap. ~2 pulses, well under
   the 3-flash/sec seizure threshold. */
/* The inset shadow paints a translucent tint OVER the background but UNDER the
   text (so content stays readable in light AND dark themes), and the outset ring
   draws a thick primary halo that isn't clipped (cards have no overflow:hidden).
   3 pulses ≈ a clear "clignote" without crossing the 3-flash/sec seizure line. */
@keyframes role-attention-flash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(46,159,223,0), inset 0 0 0 0 rgba(46,159,223,0); }
  35%, 70% { box-shadow: 0 0 0 5px var(--primary), inset 0 0 0 9999px rgba(46,159,223,.16); }
}
.attention-flash {
  animation: role-attention-flash 0.7s ease-in-out 3;
  border-radius: var(--r-md, 10px);
  position: relative;
  z-index: 1;                 /* lift the halo above neighbours while it pulses */
}

@media (prefers-reduced-motion: reduce) {
  .lb-bumped, .lb-shared-bumped .lb-shared-head span, .attention-flash {
    animation: none;
  }
}

/* --- participation dots (no per-person numbers) ---
   The done/not-done meaning is carried for SR users by a .sr-only status
   span in renderContrib(); the dot is a redundant VISUAL cue. Empty state =
   thick high-contrast hollow ring (shape, not colour); enlarged to 14px so
   the hollow-vs-filled distinction stays legible on a shared projector at
   the back of a lecture room (round4-a11y Rec 2). */
.contrib-dot {
  width: 14px; height: 14px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
  background: transparent;
  border: 2.5px solid var(--caen-ink);
  box-sizing: border-box;
}
.contrib-dot.on { border-color: transparent; }
.contrib-chip.acted { border-color: var(--line-strong); font-weight: 600; }

/* --- formative two-line toast --- */
.toast-msg { font-weight: 800; }
.toast-sub {
  margin-top: 3px;
  font-weight: 400;
  font-size: 0.84rem;
  opacity: 0.92;
}
/* the penalty toast - calm amber, never alarming red; a mistake is a lesson */
.toast.loss {
  background: #7c2d12;
  color: #fff;
  border: 1px solid #c2410c;
}
.toast.loss .toast-sub { opacity: 0.95; }

/* --- "Points lost - why" section under the objectives panel --- */
.obj-penalties {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed var(--line-strong, #d8c9b6);
}
.obj-pen-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
.obj-pen-head strong {
  font-size: 0.92rem;
  color: #9a3412;
  letter-spacing: -0.01em;
}
.obj-pen-tally {
  font-weight: 800;
  color: #b91c1c;
  font-variant-numeric: tabular-nums;
}
.obj-pen-list { display: flex; flex-direction: column; gap: 8px; }
.obj-pen-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 10px;
  padding: 8px 10px;
  background: #fef3ec;
  border: 1px solid #f0d2bf;
  border-radius: var(--r-sm, 8px);
}
.obj-pen-title { font-weight: 700; color: #7c2d12; }
.obj-pen-pts {
  font-weight: 800;
  color: #b91c1c;
  font-variant-numeric: tabular-nums;
}
.obj-pen-why {
  grid-column: 1 / -1;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--ink-soft, #5b4f40);
}
.recap-lessons { margin: 4px 0 0; padding-left: 18px; }
.recap-lessons li { margin: 4px 0; font-size: 0.86rem; line-height: 1.45; }

/* ===================== TEAM DECISIONS - Kahoot-style voting ================= */
.decisions-card { border-left: 4px solid var(--nagoya-500, #2e9fdf); }
.decisions-card.hidden { display: none; }
/* Module B "vote together" card disappears once the team has committed every
   decision (renderDecisions adds .decisions-locked). User request 2026-06-02. */
.decisions-card.decisions-locked { display: none; }
.decision {
  margin-top: 14px;
  padding: 14px;
  background: var(--surface-sunk, #f3f0e8);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
}
.decision + .decision { margin-top: 12px; }
.decision.committed.correct { border-color: var(--ok); background: var(--ok-50, #e7f4ec); }
/* User request 2026-05-18: wrong committed decisions must be visibly
   RED, not the previous neutral amber. The amber was hard to read as
   'wrong' — students saw a slightly different background and didn't
   register it as a fail signal. Switching to a clear red (matches
   the existing --key palette for negative actions). */
.decision.committed.wrong {
  border-color: #c0392b;
  background: #fdecea;
}

/* Locked decision (per 2026-05-18 specialist panel — students must
   gather case info BEFORE the platform reveals the vote menu, to
   prevent premature closure / pattern-match-and-anchor). Renders as
   a slim placeholder: title + 🔒 + "ready when…" hint. NO vote
   controls — locked decisions cannot be voted on, only previewed. */
.decision.decision-locked {
  background: transparent;
  border-style: dashed;
  border-color: var(--line);
  opacity: 0.85;
  padding: 10px 14px;
}
.decision-locked-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 6px;
}
.decision-locked-icon {
  flex: 0 0 auto;
  font-size: 1.05rem;
  line-height: 1.3;
  opacity: 0.7;
}
.decision-locked-title {
  margin: 0;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--muted);
  line-height: 1.4;
}
.decision-locked-hint {
  margin: 0;
  font-size: 0.84rem;
  color: var(--muted);
  font-style: italic;
  padding-left: 26px;
  line-height: 1.45;
}
@keyframes rcol-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.25); opacity: 0.7; }
}
.rcol-panel[hidden] { display: none; }
@keyframes rcol-fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
/* On narrow screens, keep the verb label VISIBLE (UX-overload fix
   2026-06-01). It used to be `display:none`, leaving each tab as an
   aria-hidden emoji with NO accessible name (WCAG 4.1.2 failure on the
   primary Module A nav) and forcing L2 students to guess what 🗳️/💬/📝
   mean. Instead, stack the icon over a smaller, wrapping label so all
   three tabs still fit one row on a 375px phone. */
@media (max-width: 720px) {
  .rcol-tab {
    flex-direction: column;
    gap: 3px;
    padding: 8px 6px;
    font-size: 0.72rem;
    line-height: 1.15;
    text-align: center;
  }
  .rcol-tab-label { white-space: normal; }
}

/* ── Mobile sticky bottom tab bar (Module A) ───────────────────────────────
   A body-level mirror of the right-column .rcol-tabs, for thumb reach on
   phones (the canonical tab strip is sticky at the TOP of the right column,
   which on a long Module A scroll drifts far above the thumb). It can't live
   inside the right column: #app / #stage-1 carry the stage-transition
   transform and would become the containing block for a position:fixed bar,
   pinning it to the stage bottom instead of the viewport — hence the
   body-level <nav id="mobile-rcol-tabbar"> mirrored by updateMobileTabbar().
   Desktop: never shown. Mobile (<=720px): shown only when JS clears [hidden]
   (Module A is the visible stage and no text field is focused). */
.mobile-rcol-tabbar { display: none; }
@media (max-width: 720px) {
  .mobile-rcol-tabbar:not([hidden]) {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 50;
    display: flex;
    gap: 4px;
    /* clear the iOS home indicator / gesture bar */
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px));
    background: var(--card);
    border-top: 1px solid var(--line-strong);
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.08);
  }
  /* when the bar is up, pad the room so its last content clears the bar */
  body.mtabbar-on #app { padding-bottom: 4.25rem; }
  .mtab {
    position: relative;
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 44px;          /* WCAG 2.5.5 touch target */
    padding: 4px 2px;
    font: inherit;
    font-weight: 600;
    font-size: 0.7rem;
    line-height: 1.1;
    text-align: center;
    color: var(--ink);
    background: transparent;
    border: 0;
    border-radius: var(--r-sm);
    cursor: pointer;
  }
  .mtab-icon { font-size: 1.2rem; line-height: 1; }
  .mtab-label { white-space: normal; }
  .mtab.is-active { color: var(--primary); font-weight: 800; }
  .mtab.is-locked { opacity: 0.5; }
  .mtab-badge {
    position: absolute;
    top: 1px;
    right: 18%;
    min-width: 1.35em;
    height: 1.35em;
    padding: 0 5px;
    font-size: 0.62rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: #fff;
    background: var(--primary);
    border-radius: var(--r-pill);
    line-height: 1.35em;
    box-shadow: 0 0 0 2px var(--card);
  }
  .mtab-badge[hidden] { display: none; }
  /* Progressive reveal (2026-06-02): a mirrored tab whose canonical source is
     not yet revealed is [hidden] — but .mtab sets display:flex, which would
     beat the UA [hidden] default, so hide it explicitly. */
  .mtab[hidden] { display: none; }
}

/* ===================== CANAMED SPLASH (top-level gate) =====================
   When body.locked is set, the partnership identity (header, lobby, footer)
   is hidden and the generic CANAMED splash carries the page. This prevents a
   pre-unlock flash of the partnership branding even if the JS is slow to run. */
body.locked > header,
body.locked > #lobby,
body.locked > #waiting,
body.locked > #app,
body.locked > footer { display: none !important; }

#splash.hidden { display: none; }
/* Editorial split front page: masthead strip on top, navy statement panel
   left, join panel right; ≤900px stacks JOIN-first (students hold a code). */
#splash {
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "masthead masthead"
    "about    card";
  background: var(--bg);
  /* The least a column is of the viewport: caps the two display texts below. */
  --splash-col: 38vw;
}
.splash-masthead {
  grid-area: masthead;
  margin: 0;
  padding: var(--s-4) clamp(var(--s-5), 5vw, var(--s-7)) var(--s-2);
  font-size: var(--fs-0);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 4px double var(--line-strong);
}
@media (max-width: 900px) {
  #splash {
    /* minmax(0,1fr), NOT 1fr: bare `1fr` is `minmax(auto,1fr)`, so the track's
       floor is the card's min-content — set by the create form's <select>s,
       whose min-content is their longest <option>. That floored the page at
       528px on a 412px phone; the browser then zooms out to fit and taps land
       off-target (the PR #172 failure). Guarded by splash-overflow.spec.js. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "masthead"
      "card"
      "about";
    --splash-col: 86vw;
  }
}
.splash-card {
  grid-area: card;
  background: var(--card);
  padding: clamp(var(--s-6), 6vh, var(--s-8)) clamp(var(--s-5), 5vw, var(--s-8));
  text-align: center;
  animation: splash-in .55s var(--ease-out, ease) both;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* cap the content column, not the panel (R2-35: iPhone SE stays usable) */
.splash-card > *:not(.splash-mark) {
  width: 100%;
  max-width: 480px;
}
@media (max-width: 360px) {
  .splash-card { padding: var(--s-6) var(--s-4); }
}
@keyframes splash-in {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.splash-mark {
  width: 84px; height: 84px;
  margin: 0 auto 14px;
  filter: drop-shadow(var(--sh-sm));
}
.splash-mark .arc-n { stroke: var(--nagoya-500, #2e9fdf); }
.splash-mark .arc-c { stroke: var(--caen-500, #e08a1e); }
.splash-mark .heart { fill: var(--n-700, #38434f); }
/* the wordmark: letterspaced all-caps SERIF — a university-press signature,
   not a tech logo. Weight/tracking tuned for Source Serif 4 at display size.
   Capped to its column: the word is 5.94em and cannot break; 80px is the
   card's 480px. */
.splash-title {
  margin: 0 0 8px;
  font-size: min(clamp(var(--fs-7), 6vw, calc(var(--fs-7) * 1.28)), calc(var(--splash-col) / 5.94), 80px);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.12em;
  color: var(--ink);
}
.splash-tag {
  margin: 0 auto 26px;
  max-width: 38ch;
  color: var(--muted);
  font-size: var(--fs-2);
  line-height: var(--lh-body);
}
/* Compact language switcher under the splash tagline. R2-42 promoted
   the EN/FR/JA pill buttons to a native <select> covering all 8 supported
   languages, since the pill row was growing past the iPhone SE viewport
   once we added es/pt/de/ko/zh. The select keeps the row to a single tap
   target while still being keyboard- and screen-reader-friendly. The
   legacy button styles below remain in case any view re-introduces them.*/
.splash-lang-switcher {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  margin: -14px auto 22px;
  flex-wrap: wrap;
  max-width: 100%;
}
.splash-lang-select {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--line, #d6d2c9);
  background: transparent;
  color: var(--ink, #2a2a2a);
  /* R2-36 / WCAG 2.5.8: 24px is the AA target-size floor, but for a
     primary tap target in a coarse-pointer context we want 32-44px. */
  padding: 6px 28px 6px 12px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  min-height: 32px;
  max-width: 100%;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted, #6a665d) 50%),
    linear-gradient(135deg, var(--muted, #6a665d) 50%, transparent 50%);
  background-position:
    calc(100% - 14px) 50%,
    calc(100% - 9px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.splash-lang-select:focus-visible {
  outline: 2px solid var(--primary, #2e9fdf);
  outline-offset: 2px;
}
/* form label as an editorial kicker (letterspaced caps) */
.splash-label {
  display: block;
  margin: 0 0 8px;
  color: var(--muted);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-1);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
}
.splash-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
  flex-wrap: wrap;
  justify-content: center;
}
#splash-code {
  flex: 1 1 220px;
  /* R2-35: min-width 200px combined with the surrounding gap + button
     overflows the splash-card at 320px viewports (iPhone SE). Lower the
     minimum so the input shrinks to fit before forcing the row to wrap. */
  min-width: 0;
  padding: 12px 14px;
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-md);
  outline: none;
  transition: border-color var(--dur-base, .18s) var(--ease-out, ease),
              box-shadow var(--dur-base, .18s) var(--ease-out, ease);
}
#splash-code:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent); }
#splash-code.err { border-color: var(--key); box-shadow: 0 0 0 3px color-mix(in srgb, var(--key) 14%, transparent); }
#splash-code.shake { animation: splash-shake .35s var(--ease-out, ease); }
@keyframes splash-shake {
  10%,90% { transform: translateX(-2px); }
  20%,80% { transform: translateX(3px); }
  30%,50%,70% { transform: translateX(-5px); }
  40%,60% { transform: translateX(5px); }
}
#splash-enter {
  font: inherit;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2);
  color: var(--on-primary);
  background: var(--primary);
  border: none;
  padding: 12px 24px;
  border-radius: var(--r-btn);
  cursor: pointer;
  box-shadow: var(--sh-sm);
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              transform var(--dur-base, .18s) var(--ease-out, ease);
}
#splash-enter:hover { background: var(--primary-hover); transform: translateY(-1px); }
.splash-hint {
  min-height: 1.4em;
  margin: 10px 0 0;
  font-size: 0.88rem;
  color: var(--muted);
}
.splash-hint.err { color: var(--key); }
.splash-hint.ok { color: var(--ok); }
/* quiet editorial action rows under the join form: hairline rule (not the
   dashed AI default), roomy line-height, no underline soup — links read as
   calm actions and earn their underline on hover */
.splash-foot {
  margin: var(--s-6) 0 0;
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--fs-1);
  line-height: 1.5;
}
/* the secondary actions under the join form read as a modern stacked action
   list: each .splash-link becomes a full-width quiet pill row (2026-07-18,
   user request — the underlined text rows read as dated). CSS-only: the
   i18n strings (arrows included) are untouched. */
.splash-foot .splash-link,
.splash-my-sessions-row .splash-link {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--line-strong);
  background: var(--card);
  border-radius: var(--r-btn);
  padding: 10px var(--s-4);
  margin: var(--s-2) 0 0;
  text-align: center;
  transition: border-color var(--dur-fast) var(--ease-soft),
              background var(--dur-fast) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft);
}
.splash-foot .splash-link:hover,
.splash-my-sessions-row .splash-link:hover {
  border-color: var(--primary);
  background: var(--nagoya-50);
  color: var(--primary-hover);
  text-decoration: none;
}
/* the <br> separator between the two buttons is obsolete in the row layout */
.splash-foot br { display: none; }
/* "Don't have a code yet?" becomes a quiet lead-in line above its row */
.splash-foot > span[data-i18n] { display: block; }
.splash-view[hidden] { display: none; }

/* --- the inline "link" button used as a low-emphasis text action (toggles) --- */
.splash-link {
  font: inherit;
  font-size: var(--fs-1);
  font-weight: var(--fw-semibold);
  background: none;
  border: none;
  padding: var(--s-1) var(--s-2);
  color: var(--primary);
  cursor: pointer;
  text-decoration: none;
  border-radius: var(--r-sm);
}
.splash-link:hover {
  color: var(--primary-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.splash-link:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* --- the bigger "label" used by Create / Created views --- */
.splash-label-big {
  font-family: var(--font-serif);
  font-size: var(--fs-4);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.008em;
  line-height: var(--lh-snug);
  margin-bottom: 6px;
}
.splash-sublabel {
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.5;
  margin: 0 0 16px;
}
.splash-sublabel .opt { color: var(--muted); font-weight: 400; }

/* --- create form fields --- */
.splash-field {
  display: flex;
  flex-direction: column;
  text-align: left;
  margin-bottom: 12px;
}
.splash-field label {
  font-size: 0.86rem;
  font-weight: 600;
  margin-bottom: 5px;
  color: var(--ink);
}
.splash-field .opt {
  font-weight: 400;
  color: var(--muted);
}
/* Role toggle (student / facilitator) in the profile forms */
.splash-rolelabel {
  font-size: 0.86rem;
  font-weight: 600;
  margin-bottom: 5px;
  color: var(--ink);
}
.splash-role-toggle { display: flex; gap: 8px; }

/* ── S3b — the section picker on the create form ─────────────────────────────
   An ordered list of the sections this session will run. Deliberately plain:
   the create form is a dense utility surface, and this row has to stay legible
   at mobile widths with four controls on it. Tokens only, no raw hex/px. */
.splash-section-list {
  list-style: none;
  min-width: 0;
  margin: var(--s-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.splash-section-row {
  display: flex;
  align-items: center;
  /* WRAP, or the row's content floor (a long section title + the type chip +
     three controls) pushes the page wider than a phone viewport. That overflow
     does not merely scroll: mobile zooms the page out to fit it and every hit
     test in the form lands a few pixels off, which is how the reorder buttons
     became untappable on Pixel 7 (same failure as PR #172). */
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--card);
  /* …and CLIP the inline axis, which flex-wrap alone does not cover once the
     row contains a <select>. A select is sized by its longest <option> and can
     never wrap, and section titles are long ("Section 1 — Chronic Pain & the
     clinical case"): `max-width: 100%` on the select clamps its BOX, but WebKit
     still counts the unclamped min-content in its ANCESTORS' scrollable
     overflow. So every element's rect fitted while the document measured 525px
     in a 320px viewport, and the splash scrolled sideways on iPhone/iPad/WebKit
     while Chromium and Firefox were clean. Same phantom width, same fix, as the
     author page's `.field-row.select-row` (scenario-author.css) — `clip`, not
     `hidden`, so the row never becomes a scroll container.

     No `overflow-clip-margin` and no padding-inline dance, which the author
     page needed only because its rows have no padding of their own: the clip
     edge is the PADDING box, and this row already carries `padding: var(--s-2)`
     — more than the 3px a focus ring needs — so the ring survives on engines
     that do not support overflow-clip-margin. (It also has to be omitted rather
     than tokenised: a raw px here fails the picker's no-raw-px style test, and
     the space scale has no value that MEANS "focus-ring clearance".) */
  overflow-x: clip;
}
/* prefix is fixed-width; the section SELECT takes the flexible space. */
.splash-section-name { flex: 0 0 auto; min-width: 0; white-space: nowrap; }
/* min-width:0 + max-width stop a long option label overflowing a phone. */
.splash-section-pick { flex: 1 1 8ch; min-width: 0; max-width: 100%; }
.splash-section-type {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: var(--fs-0);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* The three row controls read as one group, set apart from the label they act
   on — without the gap they crowd the type chip and the ↑↓ pair stops being
   scannable as a pair. */
.splash-section-row .splash-link {
  flex: 0 0 auto;
  padding: var(--s-1);
  min-width: var(--s-5);
  line-height: 1;
  text-align: center;
}
.splash-section-row .splash-section-up { margin-left: var(--s-2); }
.splash-section-row .splash-section-remove { margin-left: var(--s-1); }
.splash-section-row .splash-link[disabled] { opacity: 0.35; cursor: default; }
/* Keep option text short all the same: section-picker.js groups with <optgroup>. */
.splash-case-add { display: grid; gap: var(--s-2); margin-top: var(--s-2); }
.splash-case-add select { min-width: 0; }
.splash-case-parts { display: grid; gap: var(--s-1); }
.splash-case-part { display: flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.splash-case-part input { margin: 0; }
.splash-case-part-name { flex: 1 1 auto; min-width: 0; }
.splash-section-add { display: flex; gap: var(--s-2); margin-top: var(--s-2); }
.splash-section-add select { flex: 1 1 auto; min-width: 0; }

.splash-role-opt {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 12px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--card);
  border: 2px solid var(--border, #d5dde4);
  border-radius: var(--r-md, 10px);
  cursor: pointer;
}
.splash-role-opt:has(input:checked) {
  border-color: var(--accent, #e08a1e);
  background: var(--accent-soft, rgba(224, 138, 30, 0.12));
}
.splash-role-opt input { margin: 0; }
#splash-prof-student-fields[hidden],
#account-student-fields[hidden] { display: none !important; }
.splash-field input {
  padding: 10px 12px;
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color var(--dur-base, .18s) var(--ease-out, ease),
              box-shadow var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-field input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(46,159,223,0.18);
}
#splash-create-submit, #splash-go-admin {
  font: inherit;
  font-weight: 700;
  font-size: 0.95rem;
  color: #fff;
  background: var(--primary);
  border: none;
  padding: 11px 18px;
  border-radius: var(--r-sm);
  cursor: pointer;
  box-shadow: var(--sh-sm);
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              transform var(--dur-base, .18s) var(--ease-out, ease);
}
#splash-create-submit:hover, #splash-go-admin:hover {
  background: var(--primary-hover); transform: translateY(-1px);
}

/* --- the big "your code is …" display in the Created view --- */
.splash-code-display {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 16px 0 6px;
  padding: 18px 20px;
  background: linear-gradient(180deg, rgba(46,159,223,0.10), rgba(46,159,223,0.04));
  border: 1.5px solid var(--primary);
  border-radius: var(--r-md);
}
#splash-shown-code {
  font-family: var(--font-mono);
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  font-weight: 800;
  letter-spacing: 0.12em;
  color: var(--primary);
  text-transform: uppercase;
  user-select: all;
}
/* QR + caption beneath the code on the created-success view. The image
   loads lazily from api.qrserver.com; an onerror in script.js hides the
   wrapper if the request fails (network blocked / CSP mismatch). */
.splash-qr {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin: 14px 0 4px;
}
.splash-qr img {
  width: 180px;
  height: 180px;
  border-radius: 8px;
  background: #fff;
  padding: 6px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
}
.splash-qr-caption {
  font-size: 0.85rem;
  color: var(--muted, #6a665d);
  margin: 0;
}
#splash-copy-code {
  font: inherit;
  font-weight: 700;
  font-size: 0.86rem;
  color: var(--primary);
  background: var(--card);
  border: 1.5px solid var(--primary);
  padding: 8px 14px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              color var(--dur-base, .18s) var(--ease-out, ease);
}
#splash-copy-code:hover { background: var(--primary); color: #fff; }
.splash-ok { color: var(--ok); margin-right: 6px; }

/* D21 — one-time recovery code block on the created-success view. Amber
   "important, write this down" framing distinguishes it from the blue
   session-code block: the session code is read aloud to a room, the
   recovery code is a SECRET shown exactly once. */
.splash-recovery {
  margin: 18px 0 4px;
  padding: 16px 18px;
  background: linear-gradient(180deg, rgba(231,184,0,0.14), rgba(231,184,0,0.05));
  border: 1.5px solid var(--warn, #E7B800);
  border-radius: var(--r-md);
  text-align: center;
}
.splash-recovery-title {
  margin: 0 0 8px;
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--warn-strong, #8a6d00);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.splash-recovery-display {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 4px 0 8px;
}
#splash-recovery-code {
  font-family: var(--font-mono);
  font-size: clamp(1.2rem, 3.4vw, 1.7rem);
  font-weight: 800;
  letter-spacing: 0.10em;
  color: var(--warn-strong, #8a6d00);
  user-select: all;
}
#splash-recovery-copy {
  font: inherit;
  font-weight: 700;
  font-size: 0.86rem;
  color: var(--warn-strong, #8a6d00);
  background: var(--card);
  border: 1.5px solid var(--warn, #E7B800);
  padding: 8px 14px;
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              color var(--dur-base, .18s) var(--ease-out, ease);
}
#splash-recovery-copy:hover { background: var(--warn, #E7B800); color: #1a1a1a; }
.splash-recovery-warn {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--muted, #6a665d);
}

/* --- the small "Session ABC-DEF" badge that appears on the lobby once
       a session has been locked in via the splash --- */
.session-badge {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 12px;
  background: var(--ok-50, #e7f4ec);
  border: 1px solid var(--ok);
  border-radius: var(--r-pill);
  /* --ok on --ok-50 = 4.16:1, fails WCAG AA 4.5:1; use --ok-strong */
  color: var(--ok-strong, #176b3a);
  font-size: 0.86rem;
  font-weight: 700;
  margin: 0 0 12px;
}
.session-badge #session-badge-code {
  font-family: var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ok-strong, #176b3a);
}
.session-badge[hidden] { display: none; }
/* the [hidden] attribute can be overridden by stronger label/display rules -
   force it here so the manual session-code input vanishes once the splash has
   already locked in the code */
#session-input-label[hidden] { display: none !important; }

/* --- "Today's content: <scenario>" line on the lobby so the room knows
       what they will work on before joining --- */
.scenario-line {
  margin: 0 0 12px;
  padding: 8px 12px;
  background: rgba(46,159,223,0.06);
  border-left: 3px solid var(--primary);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: 0.9rem;
  line-height: 1.5;
}
.scenario-line strong { color: var(--primary); font-weight: 700; }
.scenario-line-summary { color: var(--muted); }
.scenario-line[hidden] { display: none; }

/* --- the splash create form's scenario picker + custom-JSON textarea --- */
.splash-field select {
  padding: 10px 12px;
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-sm);
  outline: none;
  cursor: pointer;
  /* WebKit counts a <select>'s longest option toward its ancestors' scroll
     width, whatever sizing the select is given. Paint containment stops that. */
  contain: paint;
  transition: border-color var(--dur-base, .18s) var(--ease-out, ease),
              box-shadow var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-field select:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(46,159,223,0.18);
}
.splash-scenario-desc {
  margin: 6px 0 0;
  font-size: 0.84rem;
  color: var(--muted);
  line-height: 1.45;
  text-align: left;
}
.splash-field textarea {
  padding: 10px 12px;
  font: 0.86rem var(--font-mono);
  color: var(--ink);
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-sm);
  outline: none;
  resize: vertical;
  min-height: 120px;
  transition: border-color var(--dur-base, .18s) var(--ease-out, ease),
              box-shadow var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-field textarea:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(46,159,223,0.18);
}
.splash-field code {
  font: 0.85rem var(--font-mono);
  background: var(--surface-sunk, #f3f0e8);
  padding: 1px 5px;
  border-radius: 4px;
}
.splash-field[hidden] { display: none !important; }

/* --- the "session closed" banner participants see when an admin ends it ---
   sticky at the top so it stays visible while students scroll through their
   work; the room interactions stay readable (the data is saved either way) */
.closed-banner {
  position: sticky;
  top: 0;
  z-index: 80;
  background: linear-gradient(180deg, #e7f4ec, #d3ecda);
  border-bottom: 2px solid var(--ok);
  box-shadow: 0 4px 12px rgba(30, 132, 73, 0.12);
  animation: closed-banner-in .45s var(--ease-out, ease) both;
}
.closed-banner.hidden { display: none; }
.closed-banner-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 14px 22px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.closed-banner-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--ok);
  color: #fff;
  font-weight: 800;
  flex: 0 0 30px;
}
.closed-banner-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.closed-banner-text strong { color: var(--ok); font-size: 1rem; }
.closed-banner-sub { color: var(--ink); font-size: 0.9rem; }
@keyframes closed-banner-in {
  from { transform: translateY(-100%); opacity: 0; }
  to { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .closed-banner { animation: none; }
}

/* When the participant has been kicked to the "session ended" full-page
   screen, hide the rest of the chrome (header, footer) so the screen is
   undivided and unambiguous - the workshop is over. */
body.session-ended-shown > header,
body.session-ended-shown > footer { display: none !important; }
body.session-ended-shown { overflow: hidden; }

/* --- the full-page "Thank you for taking part" screen --- */
.session-ended {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 36px 18px;
  background:
    radial-gradient(900px 600px at 20% -10%, rgba(30,132,73,0.18), transparent 60%),
    radial-gradient(900px 700px at 110% 110%, rgba(46,159,223,0.14), transparent 60%),
    var(--bg);
  animation: ended-fade .4s var(--ease-out, ease) both;
}
.session-ended.hidden { display: none; }
@keyframes ended-fade {
  from { opacity: 0; transform: scale(0.985); }
  to { opacity: 1; transform: none; }
}
.session-ended-card {
  width: 100%;
  max-width: 520px;
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xl, 24px);
  box-shadow: var(--sh-lg);
  padding: 40px 32px 32px;
  text-align: center;
}
.session-ended-mark {
  width: 88px; height: 88px;
  margin: 0 auto 18px;
  display: block;
}
.session-ended-title {
  margin: 0 0 12px;
  font-size: clamp(1.7rem, 4vw, 2.1rem);
  font-weight: 800;
  letter-spacing: -0.012em;
  color: var(--ink);
}
.session-ended-msg {
  margin: 0 auto 14px;
  max-width: 38ch;
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.55;
}
.session-ended-by {
  margin: 0 auto 22px;
  font-size: 0.88rem;
  color: var(--muted);
}
#session-ended-leave {
  margin-top: 8px;
  padding: 12px 26px;
  font: inherit;
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--primary);
  background: var(--card);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-pill);
  cursor: pointer;
  box-shadow: none;
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              border-color var(--dur-base, .18s) var(--ease-out, ease),
              color var(--dur-base, .18s) var(--ease-out, ease);
}
#session-ended-leave:hover { border-color: var(--primary); }
/* the end-of-session questionnaire link on the "session ended" screen -
   this is the primary action (the workshop's last task), so it gets the
   filled brand-blue treatment; "Return to CANAMED" is the secondary action */
.ended-quiz-btn {
  display: inline-block;
  margin: 4px auto 14px;
  padding: 12px 26px;
  font: inherit;
  font-weight: 700;
  font-size: 0.95rem;
  color: #fff;
  background: var(--primary);
  border: none;
  border-radius: var(--r-pill);
  text-decoration: none;
  box-shadow: var(--sh-sm);
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              transform var(--dur-base, .18s) var(--ease-out, ease);
}
.ended-quiz-btn:hover { background: var(--primary-hover); transform: translateY(-1px); }
.ended-quiz-btn.hidden { display: none; }

/* the pre-session questionnaire card on the Welcome stage - calm but
   visible, so the room fills it in before the workshop begins */
.prequiz-card {
  border-left: 4px solid var(--primary);
  background: linear-gradient(180deg, rgba(46,159,223,0.04), transparent 60%), var(--card);
}
.prequiz-card.hidden { display: none; }
.prequiz-card h3 { color: var(--primary); }
#prequiz-btn {
  display: inline-block;
  margin-top: 8px;
  padding: 10px 22px;
  font: inherit;
  font-weight: 700;
  font-size: 0.92rem;
  color: #fff;
  background: var(--primary);
  border: none;
  border-radius: var(--r-pill);
  text-decoration: none;
  box-shadow: var(--sh-sm);
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              transform var(--dur-base, .18s) var(--ease-out, ease);
}
#prequiz-btn:hover { background: var(--primary-hover); transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) {
  .session-ended { animation: none; }
}

/* in-platform pre/post knowledge tests — calm cards, gentle highlight so the
   student notices them in the Welcome / Wrap-up stages. The body is built
   procedurally by render(Pre|Post)Test and shows one question at a time. */
.pretest-card, .posttest-card {
  border-left: 4px solid var(--secondary, #2e9fdf);
  background: linear-gradient(180deg, rgba(46,159,223,0.04), transparent 60%), var(--card);
}
.pretest-card.hidden, .posttest-card.hidden { display: none; }
/* primary on white = 2.94:1 (fails AA 4.5:1); use primary-hover for darker text */
.pretest-card h3, .posttest-card h3 { color: var(--primary-hover, #0c3f6e); }
.pretest-actions, .posttest-actions {
  display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2);
}
.test-body { margin-top: var(--s-3); }
.test-body.hidden { display: none; }
.test-progress {
  font-size: 0.9rem; color: var(--ink-muted, #5a6b7a); margin: 0 0 var(--s-2);
}
.test-question {
  font-weight: 700; font-size: 1.04rem; margin: 0 0 var(--s-3); line-height: 1.4;
}
.test-options {
  display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-3);
}
.test-option {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: 10px 14px; border: 2px solid var(--border, #d5dde4);
  border-radius: var(--r-md, 10px); background: var(--card, #fff);
  cursor: pointer; text-align: left; font: inherit; color: inherit;
  transition: border-color var(--dur-base, .18s) var(--ease-out, ease),
              background var(--dur-base, .18s) var(--ease-out, ease);
}
.test-option:hover:not(:disabled) { border-color: var(--secondary, #2e9fdf); }
.test-option[aria-pressed="true"] {
  border-color: var(--secondary, #2e9fdf);
  background: rgba(46,159,223,0.07);
}
.test-option:disabled { cursor: default; opacity: 0.9; }
.test-option.correct {
  border-color: #1e8449; background: rgba(30,132,73,0.07);
}
.test-option.incorrect {
  border-color: #c0392b; background: rgba(192,57,43,0.07);
}
.test-feedback {
  margin: var(--s-2) 0 var(--s-3); padding: 10px 14px;
  border-radius: var(--r-md, 10px); font-size: 0.96rem; line-height: 1.45;
}
.test-feedback.correct { background: rgba(30,132,73,0.10); color: #14532d; }
.test-feedback.incorrect { background: rgba(192,57,43,0.10); color: #7a1d12; }
.test-feedback strong { display: block; margin-bottom: 2px; }
.test-controls {
  display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: flex-end;
}
.test-result {
  text-align: center; padding: var(--s-3) 0;
}
.ghost-btn {
  background: transparent; border: 1px solid var(--border, #d5dde4);
  color: var(--ink, #1a2530); padding: 8px 16px; border-radius: var(--r-pill, 999px);
  font: inherit; font-size: 0.9rem; cursor: pointer; min-height: 40px;
}
.ghost-btn:hover { background: rgba(0,0,0,0.04); }
.splash-providers {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  margin: 16px auto 4px;
  max-width: 320px;
}
.splash-provider-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 11px 22px 11px 14px;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  border-radius: var(--r-sm);
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              border-color var(--dur-base, .18s) var(--ease-out, ease),
              transform var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-provider-btn:hover { transform: translateY(-1px); }
.splash-provider-btn:active { transform: none; }
.splash-provider-logo { width: 18px; height: 18px; flex: 0 0 18px; }
.splash-provider-tip {
  margin-top: 8px;
  font-size: 0.82rem;
  text-align: center;
}
.splash-or {
  text-align: center;
  margin: 14px auto 8px;
  max-width: 320px;
  font-size: 0.82rem;
  opacity: 0.65;
  letter-spacing: 0.03em;
}
.splash-email-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 320px;
  margin: 0 auto;
}
.splash-email-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  align-items: center;
}
.splash-email-actions button[type="submit"] { flex: 1 1 auto; }

/* Mode toggle (Sign in / Create account) — segmented tab control. */
.splash-email-mode {
  display: flex;
  gap: 0;
  margin: 0 0 4px;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid rgba(0,0,0,0.12);
}
.splash-email-mode-btn {
  flex: 1 1 0;
  padding: 8px 10px;
  font: inherit;
  font-size: 0.88rem;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: inherit;
  opacity: 0.7;
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              opacity var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-email-mode-btn:hover { opacity: 1; }
.splash-email-mode-btn.is-active {
  background: var(--primary, #1763a6);
  color: #fff;
  opacity: 1;
  font-weight: 600;
}

/* Password-strength meter (only visible in sign-up mode). */
.splash-pwd-strength {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.splash-pwd-strength-bar {
  height: 6px;
  background: rgba(0,0,0,0.08);
  border-radius: 3px;
  overflow: hidden;
}
.splash-pwd-strength-bar > span {
  display: block;
  height: 100%;
  width: 0%;
  background: #c44;
  transition: width var(--dur-base, .18s) var(--ease-out, ease),
              background var(--dur-base, .18s) var(--ease-out, ease);
}
.splash-pwd-strength-bar > span[data-score="0"] { background: #c44; }
.splash-pwd-strength-bar > span[data-score="1"] { background: #d97; }
.splash-pwd-strength-bar > span[data-score="2"] { background: #db4; }
.splash-pwd-strength-bar > span[data-score="3"] { background: #8b3; }
.splash-pwd-strength-bar > span[data-score="4"] { background: #4a3; }
.splash-pwd-strength-label {
  margin: 0;
  font-size: 0.82rem;
  opacity: 0.85;
}
.splash-pwd-strength-hint {
  margin: 0;
  font-size: 0.75rem;
  opacity: 0.65;
  line-height: 1.35;
}

/* Google: white card per their brand guidelines */
.splash-google-btn {
  color: #3c4043;
  background: #fff;
  border: 1.5px solid #dadce0;
}
.splash-google-btn:hover { background: #f8f9fa; border-color: #c4c7c8; }

/* Microsoft: white card with thin grey border per their brand guidelines */
.splash-microsoft-btn {
  color: #5e5e5e;
  background: #fff;
  border: 1.5px solid #8c8c8c;
}
.splash-microsoft-btn:hover { background: #f3f3f3; border-color: #6b6b6b; }

/* Apple: black, per their brand guidelines */
.splash-apple-btn {
  color: #fff;
  background: #000;
  border: 1.5px solid #000;
}
.splash-apple-btn:hover { background: #1a1a1a; border-color: #1a1a1a; }

/* --- header user chip (shown when signed in) --- */
.user-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 5px;
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--r-pill);
  cursor: pointer;
  max-width: 220px;
  overflow: hidden;
  transition: background var(--dur-base, .18s) var(--ease-out, ease),
              border-color var(--dur-base, .18s) var(--ease-out, ease);
}
.user-chip:hover { background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.3); }
.user-chip.hidden { display: none; }
.user-chip-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  font-weight: 800;
  font-size: 0.78rem;
  flex: 0 0 26px;
}
.user-chip-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
@media (max-width: 720px) {
  .user-chip-name { display: none; }
  .user-chip { padding: 4px; }
}

/* --- My-account dialog --- */
.account-dialog {
  width: min(560px, 92vw);
  max-height: 88vh;
  padding: 0;
  border: none;
  border-radius: var(--r-lg);
  background: var(--card);
  box-shadow: var(--sh-lg);
  color: var(--ink);
}
.account-dialog::backdrop {
  background: rgba(12, 22, 32, 0.55);
  backdrop-filter: blur(2px);
}
.account-dialog-inner {
  display: flex;
  flex-direction: column;
  max-height: 88vh;
  overflow: auto;
  padding: 22px 26px 26px;
}
.account-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.account-dialog-head h2 {
  margin: 0;
  font-size: 1.2rem;
  letter-spacing: -0.01em;
}
.account-dialog-close {
  font-size: 1.6rem;
  line-height: 1;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--muted);
  padding: 4px 10px;
  border-radius: var(--r-sm);
}
.account-dialog-close:hover { color: var(--ink); background: var(--surface-sunk, #f3f0e8); }
.account-email-row {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 0.9rem;
}
.account-email-row strong {
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.86rem;
  /* An address breaks only after a hyphen; a long one scrolled the dialog. */
  overflow-wrap: anywhere;
}
.account-fieldset {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: 14px 16px 16px;
  margin: 0 0 14px;
  background: var(--card);
  /* The UA default, min-inline-size: min-content, keeps a fieldset as wide as
     its widest descendant — the University <select>, i.e. its longest option —
     which pushed the profile form out of a phone-width dialog. */
  min-width: 0;
}
.account-fieldset legend {
  padding: 0 6px;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--ink);
}
/* Enlarged text on a phone: the role options stack, a bare domain breaks. */
.account-fieldset .hint { margin: 0 0 10px; overflow-wrap: anywhere; }
.account-fieldset .splash-role-toggle { flex-wrap: wrap; }
.account-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 10px;
}
.account-actions button {
  font: inherit;
  font-weight: 600;
  font-size: 0.86rem;
  color: var(--primary);
  background: var(--card);
  border: 1.5px solid var(--primary);
  padding: 7px 14px;
  border-radius: var(--r-pill);
  cursor: pointer;
}
.account-actions button:hover { background: var(--primary); color: #fff; }
.account-actions #account-save-btn {
  color: #fff;
  background: var(--primary);
  border-color: var(--primary);
}
.account-actions #account-save-btn:hover { background: var(--primary-hover); }
.account-danger-zone { border-color: #f0d2bf; background: #fef9f4; }
.account-danger-zone legend { color: #9a3412; }
.account-danger-btn {
  color: #9a3412 !important;
  border-color: #f0d2bf !important;
  background: #fff !important;
}
.account-danger-btn:hover {
  background: #9a3412 !important;
  color: #fff !important;
  border-color: #9a3412 !important;
}

/* --- history list inside the dialog --- */
.account-history {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 240px;
  overflow-y: auto;
}
/* Session code, date + scenario name, withdraw button. The row wraps instead of
   shrinking: under default flex shrinking a long scenario name squeezed the
   other two, breaking the code at its hyphen and the button label over lines. */
.account-history-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-1) var(--s-3);
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  /* An authored name, or one word of the button label, can be too wide. */
  overflow-wrap: anywhere;
}
.account-history-row:last-child { border-bottom: none; }
.account-history-code {
  font-family: var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--primary);
  font-weight: 800;
}
.account-history-meta {
  /* Takes what the other two leave, down to this floor; below it the button
     wraps to the next line instead. At 0 enlarged text left a 25px column, and
     at 12ch a long name still ran to nine lines. */
  flex: 1 1 20ch;
  color: var(--muted);
  font-size: 0.84rem;
}
/* A phone is too narrow for all three side by side: the date + scenario name
   goes last, on its own line at full width. The button shares the first line
   with the code where its label fits and drops below it where it does not. */
@media (max-width: 720px) {
  .account-history-meta { order: 1; flex-basis: 100%; }
}

/* --- splash-foot extra link --- */
.splash-foot-or { color: var(--muted); }

/* --- consent block in the join form: two checkboxes (workshop + research)
       persisted with the notice version. The Join button stays disabled
       until the workshop box is ticked. --- */
.consent-block {
  margin: 16px 0 12px;
  padding: 14px 16px;
  background: rgba(46, 159, 223, 0.04);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
}
.consent-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 12px;
  cursor: pointer;
  font-size: 0.92rem;
  line-height: 1.5;
}
.consent-row:last-of-type { margin-bottom: 4px; }
/* R2-36 / WCAG 2.5.8 (Target Size — Minimum, 24x24 CSS px): the consent
   checkboxes were 17px which fails AA on touch devices. Bumped to 24px
   with an enlarged label hit-area (padding on .consent-row below) so the
   tap target meets the 24x24 floor with comfortable margins. The native
   checkbox itself is now 24x24; the surrounding label adds extra padding
   for an even more forgiving tap target on small screens (R2-34 / R2-37). */
.consent-row input[type="checkbox"] {
  margin-top: 2px;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  cursor: pointer;
  /* Modern browsers respect accent-color for native form controls. */
  accent-color: var(--primary, #2e9fdf);
}
.consent-row strong { color: var(--ink); }
.consent-version {
  margin: 8px 0 0;
  font-size: 0.78rem;
  color: var(--muted);
}
.consent-version a { color: var(--primary); }

/* --- anti-coercion / fairness banner on the Welcome stage --- */
.grade-note {
  border-left: 4px solid var(--ok);
  background: linear-gradient(180deg, rgba(30, 132, 73, 0.06), transparent 60%), var(--card);
}
.grade-note p { margin: 0; }
.grade-note strong { color: var(--ok); }
#join-btn:disabled {
  background: var(--n-100);
  color: var(--muted);
  border-color: var(--line-strong);
  cursor: not-allowed;
  box-shadow: none;
}

/* --- in-splash signed-in indicator (chip is hidden behind body.locked) --- */
.splash-signed-in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 0 14px;
  padding: 6px 14px;
  font-size: 0.84rem;
  color: var(--muted);
  background: var(--surface-sunk, #f3f0e8);
  border-radius: var(--r-pill);
}
.splash-signed-in[hidden] { display: none; }
/* the name may be an e-mail address, which has no break opportunity */
.splash-signed-in strong {
  color: var(--ink); font-weight: 700;
  min-width: 0; overflow-wrap: anywhere;
}

/* --- saved-session banner: visible escape hatch when localStorage resume
   data is present. User-reported regression (2026-05-18): "once connected
   to a session in a browser, it is not possible to disconnect from this
   session." Same visual language as .splash-signed-in but wraps gracefully
   for the longer text (name + code + clear button). The clear button has
   a 44px hit target so it works on mobile devices. --- */
.splash-saved-session {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 0 14px;
  padding: 8px 14px;
  font-size: 0.84rem;
  color: var(--muted);
  background: var(--surface-sunk, #f3f0e8);
  border-radius: var(--r-pill);
  text-align: center;
}
.splash-saved-session[hidden] { display: none; }
.splash-saved-session strong { color: var(--ink); font-weight: 700; }
#splash-saved-session-clear {
  font-weight: 600;
  min-height: 44px;
  padding: 6px 10px;
}

/* Lobby escape hatch — companion to the splash banner, shown above the
   join form whenever the user reached the lobby via an unlocked session
   (i.e. there is something to "switch away from"). 44px hit target. */
.lobby-switch-session {
  margin: 0 0 12px;
  text-align: left;
}
.lobby-switch-session[hidden] { display: none; }
#lobby-switch-session-btn {
  font-weight: 600;
  min-height: 44px;
  padding: 6px 10px;
}

/* --- "My open sessions" reaper UI ---
   The link on the splash entry view + the list view that lets a
   facilitator close sessions they created previously and forgot to end.
   User-reported gap (2026-05-18): no way to close abandoned sessions. */
.splash-my-sessions-row {
  margin: 8px 0 4px;
  text-align: center;
}
.splash-my-sessions-row[hidden] { display: none; }
#splash-go-my-sessions {
  font-weight: 600;
  min-height: 44px;
  padding: 6px 10px;
}
.splash-my-sessions-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 8px 0 12px;
  text-align: left;
}
.splash-my-sessions-empty {
  color: var(--muted);
  font-size: 0.9rem;
  text-align: center;
  margin: 16px 0;
}
.splash-my-sessions-empty[hidden] { display: none; }
.my-session-row {
  border: 1px solid var(--border, #e2dccf);
  border-radius: var(--r-card, 10px);
  padding: 10px 12px;
  background: var(--surface-sunk, #f9f7f1);
}
.my-session-code {
  font-weight: 700;
  font-size: 1.05rem;
  margin: 0 0 2px;
  letter-spacing: 0.05em;
}
.my-session-label {
  color: var(--ink, #1c1c1c);
  font-size: 0.95rem;
  margin: 0 0 2px;
}
.my-session-when, .my-session-status {
  color: var(--muted);
  font-size: 0.84rem;
  margin: 0;
}
.my-session-status { margin-top: 4px; }
.my-session-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
  align-items: center;
}
.my-session-close {
  /* Danger affordance — match the rest of the platform's "close session"
     buttons (warm red border, clear visual weight). */
  min-height: 44px;
  padding: 6px 14px;
  border: 1px solid #c0392b;
  background: #fff5f3;
  color: #8a2017;
  border-radius: var(--r-pill, 999px);
  font-weight: 600;
  cursor: pointer;
}
.my-session-close:hover:not([disabled]) {
  background: #ffe9e4;
}
.my-session-close[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}
.my-session-forget {
  font-size: 0.84rem;
  min-height: 44px;
}

@media (prefers-reduced-motion: reduce) {
  .splash-card { animation: none; }
  #splash-code.shake { animation: none; }
  /* Round-2 a11y: kill the connection-lost attention shake for
     vestibular-sensitive users — the conn-badge color/text change
     is enough on its own. */
  .conn-badge.conn-lost { animation: none; }
}

/* statement panel: the lobby hero's navy language, theme-constant
   via the masthead tokens */
.splash-about {
  grid-area: about;
  margin: 0;
  text-align: left;
  background:
    radial-gradient(420px 300px at 18% 8%, color-mix(in srgb, var(--nagoya-500) 50%, transparent), transparent 60%),
    radial-gradient(360px 320px at 92% 96%, color-mix(in srgb, var(--caen-500) 28%, transparent), transparent 62%),
    linear-gradient(155deg, var(--masthead-a), var(--masthead-b));
  color: var(--masthead-ink);
  padding: clamp(var(--s-6), 7vh, var(--s-9)) clamp(var(--s-5), 5vw, var(--s-8));
  display: flex;
  flex-direction: column;
  justify-content: center;
  animation: splash-fade .8s var(--ease-out, ease) .2s both;
}
@keyframes splash-fade {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.splash-mission {
  margin: 0 0 var(--s-4);
  font-family: var(--font-serif);
  /* Capped like the wordmark; longest word 6.16em. */
  font-size: min(clamp(var(--fs-6), 3.4vw, calc(var(--fs-7) * 1.15)), calc(var(--splash-col) / 6.4));
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  line-height: var(--lh-tight);
  color: var(--masthead-ink);
  max-width: 16ch;
}
.splash-lede {
  margin: 0 0 clamp(28px, 5vh, 48px);
  max-width: 48ch;
  color: color-mix(in srgb, var(--masthead-ink) 86%, transparent);
  font-size: var(--fs-2);
  line-height: 1.65;
}
.splash-pillars {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 4vw, 48px);
}
@media (max-width: 760px) {
  .splash-pillars {
    grid-template-columns: 1fr;
    gap: 28px;
  }
}
.splash-pillar-ico {
  width: 30px;
  height: 30px;
  color: var(--caen-500);
  margin-bottom: 14px;
  display: block;
}
.splash-pillar h3 {
  margin: 0 0 8px;
  font-size: var(--fs-2);
  font-weight: var(--fw-bold);
  letter-spacing: -0.005em;
  color: var(--masthead-ink);
}
.splash-pillar p {
  margin: 0;
  color: color-mix(in srgb, var(--masthead-ink) 78%, transparent);
  font-size: 0.92rem;
  line-height: 1.6;
}
.splash-credits {
  margin: clamp(32px, 5vh, 48px) 0 0;
  color: color-mix(in srgb, var(--masthead-ink) 66%, transparent);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}
@media (prefers-reduced-motion: reduce) {
  .splash-about { animation: none; }
}

/* --- "order matters" cue on imaging buttons --- */
.req-btn.warn {
  border-color: var(--caen-line);
  border-left-width: 4px;
  background: var(--caen-50);
}
/* the warning is a REAL DOM node (a sibling), not ::after content - generated
   content is not reliably read by screen readers, and this is a key learning cue */
.req-warn-note {
  display: block;
  margin: -2px 0 2px;
  padding: 0 2px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--caen-ink);
}
.req-warn-note::before { content: "\26A0  "; }   /* warning sign */

/* --- Inline reveal under each button — the patient's answer appears
   DIRECTLY under the tapped button on EVERY viewport (per 2026-05-18
   specialist panel: "Inline-reveal answers under the clicked button.
   Action and result fuse spatially. Mobile is fixed for free — no
   scroll-up-to-see-result"). Originally mobile-only (Bug 2 user-
   feedback-2); promoted to all viewports because the right-column log
   is too far away even on desktop for many students. The right-column
   "What we're finding" log still aggregates everything chronologically
   — useful for the team to see teammates' contributions across panels. */
.req-inline-reveal {
  display: block;
  margin: 6px 0 10px;
  padding: 8px 12px;
  background: var(--surface-sunk, #f6f4ef);
  border-left: 3px solid var(--ok, #2a9d8f);
  border-radius: var(--r-sm, 6px);
  font-size: 0.92rem;
  color: var(--ink, #2a2a2a);
  line-height: 1.45;
  animation: req-reveal-in 200ms var(--ease-out, ease-out);
}
.req-inline-reveal::before {
  content: "→ ";
  color: var(--ok, #2a9d8f);
  font-weight: 700;
}
/* Author byline inside the inline-reveal chip — replaces the
   "revealed by [name]" line that used to live in the (now removed)
   Findings tab. Muted italic so the answer text stays the visual
   focus. */
.req-inline-by {
  color: var(--muted);
  font-style: italic;
  font-size: 0.84rem;
  margin-left: 4px;
}
/* Contribution tally relocated 2026-05-18 to the chart header (was
   inside the removed Findings panel). Tighter top spacing + a themed
   surface: a white wash was a grey slab on the dark chart. */
.contrib-tally-header {
  margin: 8px 0 0;
  padding: 6px 8px;
  background: var(--card);
  border-radius: var(--r-sm);
}

/* Transient "Generating QR code…" placeholder shown while the lazy-
   loaded qrcode.js library finishes fetching. Prevents the previous
   session's QR from lingering visible during the load gap. */
.splash-qr-placeholder {
  margin: 0;
  padding: 60px 12px;
  text-align: center;
  font-size: 0.92rem;
  color: var(--muted);
  font-style: italic;
}

/* Empty-input flash used by the team-name save button (and reusable
   elsewhere) — pulses the input border + tints the background so the
   user gets visible feedback when they click Save on an empty field
   instead of the previous silent failure. */
.input-empty-flash {
  animation: input-empty-flash-anim 600ms var(--ease-out, ease-out);
}
@keyframes input-empty-flash-anim {
  0%   { box-shadow: 0 0 0 0 rgba(228, 138, 30, 0.0); background-color: var(--card, #fff); }
  30%  { box-shadow: 0 0 0 3px rgba(228, 138, 30, 0.45); background-color: rgba(228, 138, 30, 0.08); }
  100% { box-shadow: 0 0 0 0 rgba(228, 138, 30, 0.0); background-color: var(--card, #fff); }
}

/* "Every click is a team decision" warning — pinned to the top of the
   chart so it's visible the whole case-work phase. Caen-amber border
   matches the chart's paper theme; strong colour pulls the eye without
   being alarming. */
/* "Every click is a team decision" note pinned in the chart header.
   UX-overload fix (2026-06-01): demoted from a filled amber banner to quiet
   muted hint text (no fill, no border) so it no longer competes with the
   next-step coach as a second "alarm" in the Stage-1 first viewport. The
   message is preserved verbatim — just calmer. Muted text reads fine in
   light/dark/high-contrast{
  margin: 10px 0 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted);
}
@keyframes req-reveal-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* --- wrap-up "your team did well" recap --- */
.team-recap-card {
  background: linear-gradient(135deg, var(--nagoya-50), var(--caen-50));
  border: 1px solid var(--line);
}
#team-recap h3 { margin: 0 0 var(--s-2); font-size: 1.2rem; }
.recap-total { font-size: 1rem; margin: 0 0 var(--s-2); }
.recap-wins {
  list-style: none;
  margin: var(--s-1) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.recap-wins li {
  padding: 6px 12px;
  background: var(--card);
  border-radius: var(--r-sm);
  font-size: 0.92rem;
  font-weight: 600;
}
.recap-wins li::before { content: "✓ "; color: var(--ok); font-weight: 800; }

/* ==========================================================================
   DESIGN v3  -  "Two arcs, one circle". The cohesive modern layer: brand mark,
   per-stage chapters, editorial type, inline-SVG imagery, the meeting motif,
   animated disclosure, a sticky command bar, tiered celebration. Appended last.
   ========================================================================== */

/* --- editorial typography: real weight contrast + a dramatic scale --- */
h1, h2, h3, h4 { text-wrap: balance; }
body > header h1 { font-size: 1.7rem; font-weight: 800; letter-spacing: -0.025em; }
.card h2 { font-size: 1.55rem; font-weight: 800; letter-spacing: -0.025em; line-height: 1.16; }
.card h3 { font-size: 1.1rem; font-weight: 700; }
.card h4 { font-weight: 700; }
/* numbers as a graphic device - tabular, heavier */
.score-num, .obj-tally, .lb-pts, .lb-rank, .count, .findings-count,
.lb-shared-head span { font-variant-numeric: tabular-nums; }

/* --- the brand mark "Meeting Ring" --- */
.brand-mark { display: block; }
.brand-lockup {
  display: flex; align-items: center; gap: 10px;
}
.brand-lockup .brand-mark { width: 38px; height: 38px; flex-shrink: 0; }
.brand-lockup h1 { line-height: 1.05; }
.brand-mark .arc-c { stroke: var(--caen-500); }
.brand-mark .arc-n { stroke: var(--nagoya-500); }
.brand-mark .heart { fill: var(--caen-500); }
/* on dark surfaces (header, lobby hero) the arcs read brighter */
body > header .brand-mark .arc-c, .hero-aside .brand-mark .arc-c { stroke: #f0ad3c; }
body > header .brand-mark .arc-n, .hero-aside .brand-mark .arc-n { stroke: #5cb8ea; }
body > header .brand-mark .heart, .hero-aside .brand-mark .heart { fill: #fff; }
/* draw-on: both arcs arrive together, then the heart fades in */
.brand-mark .arc-c, .brand-mark .arc-n {
  stroke-dasharray: 150;
  animation: mark-draw 760ms var(--ease-out) 120ms both;
}
.brand-mark .heart { animation: mark-heart 420ms var(--ease-spring) 760ms both; }
@keyframes mark-draw { from { stroke-dashoffset: 150; } to { stroke-dashoffset: 0; } }
@keyframes mark-heart { from { opacity: 0; transform: scale(0); } to { opacity: 1; transform: scale(1); } }

/* --- header: a crossing blue->meeting->amber underline --- */
body > header {
  background: radial-gradient(620px 220px at 85% -70%, rgba(224,138,30,.22), transparent),
              linear-gradient(135deg, var(--masthead-a), var(--masthead-b));
  border-bottom: 3px solid transparent;
  border-image: linear-gradient(90deg, var(--nagoya-500), var(--meeting), var(--caen-500)) 1;
  box-shadow: 0 8px 26px rgba(8,36,63,.28);
}

/* --- the warm-paper card system: solid, real depth, no glass --- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh-sm);
}
/* the vignette accent bar now carries the CURRENT STAGE's colour */

/* --- per-stage "chapter" identity --- */
/* No longer pinned (2026-06-02): the stage card used to stick to the top of the
   viewport on scroll, which the user found intrusive. It now scrolls away with
   the page; the floating .back-to-top button covers the "jump back up" need. */
.stage-card {
  border-top: 4px solid var(--stage-accent);
  padding: var(--s-3) var(--s-5);
}

/* --- inline-SVG spot illustrations (the answer to "should we add images?") --- */
/* geometric spot illustrations — the icon set's stroke language at figure
   scale; colours are tokens, so both themes come for free */
.spot-illustration .spot-arc-a,
.spot-illustration .spot-arc-b,
.spot-illustration .spot-base,
.spot-illustration .spot-trace { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.spot-illustration .spot-arc-a { stroke: var(--nagoya-500); stroke-width: 9; opacity: .45; }
.spot-illustration .spot-arc-b { stroke: var(--caen-500); stroke-width: 9; opacity: .45; }
.spot-illustration .spot-trace { stroke: var(--primary); stroke-width: 3; }
.spot-illustration .spot-base { stroke: var(--line-strong); stroke-width: 2; }
.spot-illustration .spot-dot { fill: var(--caen-500); }
.spot-illustration .spot-meet { fill: var(--meeting); }
.wrap-art .wrap-arc-a,
.wrap-art .wrap-arc-b { fill: none; stroke-linecap: round; }
.wrap-art .wrap-arc-a { stroke: var(--nagoya-500); stroke-width: 8; }
.wrap-art .wrap-arc-b { stroke: var(--caen-500); stroke-width: 8; }
.wrap-art .wrap-apex { fill: var(--meeting); }
.session-ended-mark .ended-ring { fill: var(--ok-50); stroke: var(--ok); stroke-width: 3; }
.session-ended-mark .ended-check { fill: none; stroke: var(--ok-strong); stroke-width: 6; stroke-linecap: round; stroke-linejoin: round; }
.spot-illustration {
  display: block;
  width: 100%;
  max-width: 200px;
  height: auto;
  margin: 0 0 var(--s-3);
  float: right;
  margin-left: var(--s-4);
}
@media (max-width: 520px) {
  .spot-illustration { float: none; margin: 0 auto var(--s-3); }
}


/* --- reference cards read as a quiet library, not as task steps --- */
.history-card, .guidelines-card, .recap-card {
  background: var(--surface-sunk);
  border-color: var(--line-strong);
  box-shadow: none;
}
.history-card > summary, .guidelines-card > summary, .recap-card > summary {
  color: var(--muted);
}

/* --- animated disclosure: content eases in when a <details> opens --- */
details.card[open] > *:not(summary) {
  animation: detail-open var(--dur-base) var(--ease-out);
}
@keyframes detail-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes card-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes bar-sheen { to { transform: translateX(100%); } }

/* --- objectives + leaderboard rendered as real lists (a11y) --- */
.obj-list[role="list"], #leaderboard[role="list"] { list-style: none; margin: 0; padding: 0; }

/* --- richer confetti: shape variety --- */
.confetti.c-circle { border-radius: 50%; }
.confetti.c-tri {
  border-radius: 0;
  background: none !important;
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 9px solid var(--cf, #2e9fdf);
}

/* --- the sound toggle in the header --- */
.sound-toggle {
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.4);
  color: #fff;
  border-radius: var(--r-pill);
  width: 34px; height: 34px;
  font-size: 1rem;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.sound-toggle:hover { background: rgba(255,255,255,.24); }


.pts-tag, .pts-undo{ min-height: 44px; }

/* --- request-finding buttons: a touch more presence --- */
.req-btn { font-weight: 600; }
.req-btn:hover { transform: translateY(-1px); }
.req-btn.done { color: var(--ink); }

/* --- gradient primary buttons stay, but unify the press feel --- */
button:active:not(:disabled), .req-btn:active:not(:disabled):not(.done) {
  transform: scale(0.97);
}

/* --- footer: small mark + tagline --- */
.footer-brand {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 6px;
}
.footer-brand .brand-mark { width: 28px; height: 28px; }
.footer-brand strong { color: var(--ink); }
.footer-tag { font-style: italic; }

/* --- lobby hero: make the mark the showpiece --- */
.hero-aside .brand-mark { width: 104px; height: 104px; margin-bottom: var(--s-3); }

@media (max-width: 680px) {
  body > header h1 { font-size: 1.4rem; }
  .card h2 { font-size: 1.35rem; }
}

/* WCAG 2.5.5 / 2.5.8 (Target Size, Minimum). On touch-primary devices,
   guarantee a 44×44 CSS-pixel touch target on every primary interactive
   element. We don't expand inline `.splash-link` (those are inline text
   nuggets where 44px would visually break the layout) — they keep an
   adequate hit area via padding alone. */
@media (pointer: coarse), (max-width: 680px) {
  button:not(.splash-link):not(.splash-lang-switcher button),
  input[type="text"]:not(.no-min-touch),
  input[type="password"]:not(.no-min-touch),
  select,
  .lobby-card label { min-height: 44px; }
  /* Comfortable checkbox hit-area: the label wraps the box and is the
     real tap target, so making the row at least 44px high covers it. */
  .consent-row { min-height: 44px; padding: 4px 0; }
}

/* "Clone last workshop" row at the top of the create-session view.
   Only shown when localStorage holds a prior workshop summary. */
.splash-clone-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 12px;
  margin: 0 0 12px;
  background: rgba(46,159,223,0.06);
  border: 1px dashed var(--primary, #2e9fdf);
  border-radius: 8px;
  font-size: 0.92rem;
}
.splash-clone-meta {
  color: var(--muted, #6a665d);
  font-size: 0.85rem;
}

/* GDPR Art. 15 self-export row in the waiting room footer. Discreet
   underline-link look — present but never competing with the workshop
   chrome. Visible to ALL participants (anonymous + identified). */
.data-rights-row {
  margin: 14px 0 4px;
  text-align: center;
  font-size: 0.85rem;
  color: var(--muted, #6a665d);
}

/* ==========================================================================
   DARK THEME — the token palette lives in tokens.css (forced
   [data-theme="dark"] block + the prefers-color-scheme auto block). Only
   component-level dark rules remain in this file.
   ========================================================================== */
/* Bug 4 follow-up (user-feedback-2): the splash footer links
   `#splash-go-create` / `#splash-go-account` resolved to `var(--primary)`
   which axe-core under the dark theme saw as #1763a6 on the #16202b
   card background — 2.64:1, well under the 4.5:1 AA floor. Pinning the
   colour to `--nagoya-500` (#5cb8e8 in dark mode) keeps the brand blue
   identity and brings the contrast to ~7.4:1. The override is also
   applied inside the prefers-color-scheme media query so users on
   data-theme="auto" with a dark OS get the same accessible colour.
   Light theme is untouched. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) #splash-go-create,
  html:not([data-theme="light"]) #splash-go-account {
    color: var(--nagoya-500);
  }
  html:not([data-theme="light"]) #splash-go-create:hover,
  html:not([data-theme="light"]) #splash-go-account:hover {
    color: #88cdee;
  }
  html:not([data-theme="light"]) #splash-go-create:focus-visible,
  html:not([data-theme="light"]) #splash-go-account:focus-visible {
    outline-color: var(--nagoya-500);
  }
}
html[data-theme="dark"] #splash-go-create,
html[data-theme="dark"] #splash-go-account {
  color: var(--nagoya-500);
}
html[data-theme="dark"] #splash-go-create:hover,
html[data-theme="dark"] #splash-go-account:hover {
  color: #88cdee;
}
html[data-theme="dark"] #splash-go-create:focus-visible,
html[data-theme="dark"] #splash-go-account:focus-visible {
  outline-color: var(--nagoya-500);
}

/* PR #3 follow-up: in dark mode, --primary is var(--nagoya-500) =
   #5cb8e8 (bright cyan) so that text-on-dark links (e.g. .privacy-note
   a, splash-go-create) hit ~6.5:1. But solid-primary BUTTONS like
   #splash-enter — `color: #fff; background: var(--primary)` — invert
   that pattern: white text on bright cyan = 2.21:1 (axe fail). Flip
   the text colour to the dark page background so the contrast goes
   the other way (#0e1620 on #5cb8e8 = ~9.5:1). Same fix applies to
   #splash-create-submit / #splash-go-admin / #splash-copy-code:hover
   which are also visible after the create-session step. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) #splash-enter,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) #splash-create-submit,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) #splash-go-admin,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) #splash-copy-code:hover {
    color: #0e1620;
  }
}
html[data-theme="dark"] #splash-enter,
html[data-theme="dark"] #splash-create-submit,
html[data-theme="dark"] #splash-go-admin,
html[data-theme="dark"] #splash-copy-code:hover {
  color: #0e1620;
}

/* Bug 4 follow-up (user-feedback-2): form controls have no explicit
   text colour anywhere in the cascade, so they fall back to the UA
   default (black on most platforms). On a dark surface (--card =
   #16202b) that's #000 on #16202b = 1.27:1 — invisible. The lobby
   `#name-input` typed-text was the worst offender. Force ink-on-card
   on every native control under the dark theme; the same override is
   mirrored inside the prefers-color-scheme media query for users on
   data-theme="auto" with a dark OS. Light theme stays untouched
   (controls keep --ink dark text on --card white). High-contrast
   theme already pins its own border + colour rules, so we don't
   touch it here. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) input,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) textarea,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) select {
    color: var(--ink);
    background-color: var(--card);
    border-color: var(--line-strong);
  }
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) input::placeholder,
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) textarea::placeholder {
    color: var(--muted);
    opacity: 1;
  }
}
html[data-theme="dark"] input,
html[data-theme="dark"] textarea,
html[data-theme="dark"] select {
  color: var(--ink);
  background-color: var(--card);
  border-color: var(--line-strong);
}
html[data-theme="dark"] input::placeholder,
html[data-theme="dark"] textarea::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* Bug 4 follow-up (PR #3 follow-up): the lobby's privacy disclosure
   (`.privacy-note`) contains inline <a> links (lobby.privacy.p5 + p6)
   injected via data-i18n-html. Because the i18n payload uses bare
   <a href> without a class, the links fall through to the browser
   default link colour #0000ee, which only hits ~2.01:1 contrast on
   the dark `--surface-sunk` background (#0a1119) — well under the
   4.5:1 AA floor (axe-core fail). Force `--primary` (= --nagoya-500
   = #5cb8e8 = ~6.5:1) in dark + high-contrast. Mirrored in the
   prefers-color-scheme block for data-theme="auto" with dark OS. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="high-contrast"]) .privacy-note a {
    color: var(--primary);
  }
}
html[data-theme="dark"] .privacy-note a {
  color: var(--primary);
}

/* The theme selector control in admin prefs / nav */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--muted);
}
.theme-toggle select {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 0.85rem;
  font-family: inherit;
  cursor: pointer;
}

/* ==========================================================================
   CSP-hardening helpers (PR sec/csp-tighten-no-unsafe-inline)
   Classes that replaced inline style="..." attributes so the CSP can drop
   style-src 'unsafe-inline'. Behaviour-preserving — purely a security
   refactor.
   ========================================================================== */
.splash-qr-img { width: 180px; height: 180px; }
.placeholder-inline { font-style: italic; }
.hint-block-small { display: block; font-size: 0.78rem; margin-top: 2px; }

/* ==========================================================================
   HIGH-CONTRAST THEME — for low-vision users + prefers-contrast: more
   ==========================================================================
   Activates two ways:
   1. <html data-theme="high-contrast"> set by the theme picker
   2. @media (prefers-contrast: more) html:not([data-theme="auto"]) overrides
   The high-contrast palette is high-saturation black/white/yellow on a clean
   white surface, with text strictly black and interactive states explicit.
   Pure WCAG-AAA-targeted (7:1 minimum text contrast). Loses brand colour
   identity intentionally — accessibility first.
   ========================================================================== */
@media (prefers-contrast: more) {
  html:not([data-theme="light"]):not([data-theme="dark"]) {
    --bg: #ffffff;
    --surface-sunk: #f5f5f5;
    --card: #ffffff;
    --line: #000000;
    --line-strong: #000000;
    --ink: #000000;
    --muted: #1f1f1f;
    --primary: #0033a0;
    --primary-hover: #001f5c;
    --key: #c00000;
    --key-50: #ffe5e5;
    --ok: #006400;
    --ok-50: #e5ffe5;
    --focus: #b75d00;
    --nagoya-700: #001f5c;
    --nagoya-600: #0033a0;
    --nagoya-50:  #e6ecf7;
    --caen-line:  #8b4500;
    --caen-ink:   #5a2c00;
    --caen-50:    #fff3e0;
    --meeting:    #1f1f1f;
    --meeting-50: #f5f5f5;
    /* Shadows replaced by hairline borders — flat, predictable */
    --sh-sm: 0 0 0 1px #000000;
    --sh-md: 0 0 0 1px #000000;
    --sh-lg: 0 0 0 2px #000000;
  }
}
html[data-theme="high-contrast"] {
  --bg: #ffffff;
  --surface-sunk: #f5f5f5;
  --card: #ffffff;
  --line: #000000;
  --line-strong: #000000;
  --ink: #000000;
  --muted: #1f1f1f;
  /* Consultation-note tokens in high-contrast: hard white surface,
     black borders, full-black ink. Removes the warm-paper effect
     intentionally — high-contrast prioritises legibility. */
  --note-bg:    #ffffff;
  --note-line:  #000000;
  --note-stripe: rgba(0, 0, 0, 0.04);
  --note-margin-line: #000000;
  --note-ink:   #000000;
  --note-accent-ink: #000000;
  --primary: #0033a0;
  --primary-hover: #001f5c;
  --key: #c00000;
  --key-50: #ffe5e5;
  --ok: #006400;
  --ok-50: #e5ffe5;
  --focus: #b75d00;
  --nagoya-700: #001f5c;
  --nagoya-600: #0033a0;
  --nagoya-50:  #e6ecf7;
  --caen-line:  #8b4500;
  --caen-ink:   #5a2c00;
  --caen-50:    #fff3e0;
  --meeting:    #1f1f1f;
  --meeting-50: #f5f5f5;
  --sh-sm: 0 0 0 1px #000000;
  --sh-md: 0 0 0 1px #000000;
  --sh-lg: 0 0 0 2px #000000;
}
/* In high-contrast: every button/input gets a visible 2px black border so
   keyboard-only and low-vision users can clearly see hit-targets without
   relying on shadow or colour fills. */
html[data-theme="high-contrast"] button,
html[data-theme="high-contrast"] input,
html[data-theme="high-contrast"] select,
html[data-theme="high-contrast"] textarea {
  border: 2px solid #000000 !important;
  outline-offset: 2px;
}
html[data-theme="high-contrast"] :focus-visible {
  outline: 3px solid #b75d00 !important;
  outline-offset: 2px !important;
}

/* Per-student debrief card (inside session-ended). */
.student-debrief {
  text-align: left;
  margin-top: var(--s-4);
  padding: var(--s-3);
  background: var(--n-25);
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 0.92rem;
}
.student-debrief h2 {
  margin: 0 0 var(--s-2);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--ink);
}
.student-debrief .sd-score {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ok);
  margin: 0 0 var(--s-2);
}
.student-debrief .sd-row {
  margin: var(--s-2) 0;
}
.student-debrief .sd-row strong {
  display: block;
  font-size: 0.82rem;
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 2px;
}
.student-debrief .sd-row ul {
  margin: 0;
  padding-left: var(--s-3);
}
.student-debrief .sd-row li { margin: 2px 0; }
.student-debrief .sd-closing {
  margin-top: var(--s-3);
  font-style: italic;
  color: var(--muted);
}

/* ==========================================================================
   First-time facilitator onboarding tour
   - dimmed overlay + spotlight punch-out on the anchor element
   - speech-bubble next to the anchor (flipped if it would clip)
   - vanilla DOM, no external library; respects prefers-reduced-motion
   ========================================================================== */
.canamed-tour-root {
  position: fixed;
  inset: 0;
  z-index: 9000;
  pointer-events: none;  /* children opt back in */
}
.canamed-tour-overlay {
  position: absolute;
  inset: 0;
  background: rgba(12, 32, 43, 0.42);
  backdrop-filter: blur(1.5px);
  pointer-events: auto;
  cursor: pointer;
  animation: canamedTourFade var(--dur-base, 260ms) var(--ease-soft, ease-out);
}
.canamed-tour-spotlight {
  position: absolute;
  pointer-events: none;
  border-radius: var(--r-md, 12px);
  box-shadow:
    0 0 0 3px rgba(46, 159, 223, 0.85),
    0 0 0 9999px rgba(12, 32, 43, 0.42);
  transition: left var(--dur-base, 260ms) var(--ease-soft, ease-out),
              top var(--dur-base, 260ms) var(--ease-soft, ease-out),
              width var(--dur-base, 260ms) var(--ease-soft, ease-out),
              height var(--dur-base, 260ms) var(--ease-soft, ease-out);
}
.canamed-tour-spotlight--hidden { display: none; }
.canamed-tour-bubble {
  position: absolute;
  pointer-events: auto;
  max-width: 340px;
  min-width: 240px;
  background: var(--card, #fff);
  color: var(--ink, #16202b);
  border: 1px solid var(--line-strong, #d4cfc2);
  border-radius: var(--r-md, 12px);
  box-shadow: var(--sh-lg, 0 4px 10px rgba(0,0,0,.12), 0 26px 60px rgba(0,0,0,.2));
  padding: 18px 20px 14px;
  font-size: 0.95rem;
  line-height: 1.45;
  animation: canamedTourPop var(--dur-base, 260ms) var(--ease-out, ease-out);
}
.canamed-tour-bubble--center { /* no arrow */ }
/* triangular arrows pointing at the anchor */
.canamed-tour-bubble::before {
  content: "";
  position: absolute;
  width: 0; height: 0;
  border: 8px solid transparent;
}
.canamed-tour-bubble--top::before {
  bottom: -16px; left: 50%; transform: translateX(-50%);
  border-top-color: var(--card, #fff);
}
.canamed-tour-bubble--bottom::before {
  top: -16px; left: 50%; transform: translateX(-50%);
  border-bottom-color: var(--card, #fff);
}
.canamed-tour-bubble--left::before {
  right: -16px; top: 50%; transform: translateY(-50%);
  border-left-color: var(--card, #fff);
}
.canamed-tour-bubble--right::before {
  left: -16px; top: 50%; transform: translateY(-50%);
  border-right-color: var(--card, #fff);
}
.canamed-tour-bubble--center::before { display: none; }
.canamed-tour-title {
  margin: 0 0 6px;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--primary, #1763a6);
}
.canamed-tour-body {
  margin: 0 0 10px;
  color: var(--ink, #16202b);
}
.canamed-tour-progress {
  margin: 0 0 12px;
  font-size: 0.78rem;
  color: var(--muted, #5b6b7a);
}
.canamed-tour-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.canamed-tour-spacer { flex: 1 1 auto; }
.canamed-tour-btn {
  font: inherit;
  cursor: pointer;
  border-radius: var(--r-sm, 9px);
  padding: 7px 14px;
  border: 1px solid var(--line-strong, #d4cfc2);
  background: var(--n-25, #fbf9f4);
  color: var(--ink, #16202b);
  transition: background var(--dur-fast, 160ms) var(--ease-soft, ease),
              border-color var(--dur-fast, 160ms) var(--ease-soft, ease);
}
.canamed-tour-btn:hover {
  background: var(--n-50, #f1eee6);
  border-color: var(--n-300, #b3ab99);
}
.canamed-tour-btn:focus-visible {
  outline: 2px solid var(--focus, #11161b);
  outline-offset: 2px;
}
.canamed-tour-btn--skip {
  background: transparent;
  border-color: transparent;
  color: var(--muted, #5b6b7a);
  padding-left: 4px;
  padding-right: 4px;
}
.canamed-tour-btn--skip:hover {
  background: transparent;
  color: var(--ink, #16202b);
  text-decoration: underline;
}
.canamed-tour-btn--next {
  background: var(--primary, #1763a6);
  color: #fff;
  border-color: var(--primary, #1763a6);
}
.canamed-tour-btn--next:hover {
  background: var(--primary-hover, #0c3f6e);
  border-color: var(--primary-hover, #0c3f6e);
}
@keyframes canamedTourFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes canamedTourPop {
  from { opacity: 0; transform: translateY(4px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .canamed-tour-overlay,
  .canamed-tour-bubble { animation: none; }
  .canamed-tour-spotlight { transition: none; }
}
/* tiny "Show tour again" link inside the create-session form */
.canamed-tour-reopen {
  background: none;
  border: none;
  padding: 4px 0;
  color: var(--primary, #1763a6);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  text-decoration: underline;
}
.canamed-tour-reopen:hover { color: var(--primary-hover, #0c3f6e); }
.canamed-tour-reopen:focus-visible {
  outline: 2px solid var(--focus, #11161b);
  outline-offset: 2px;
}

/* ==========================================================================
   Admin dashboard search / filter
   - shown only when roomCount > 5 (toggled via .hidden on the wrapper)
   - matches against room name + any participant name within the room
   - filter state survives re-renders (kept in script.js module scope)
   ========================================================================== */
.dashboard-filter {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
}
.dashboard-filter-input {
  flex: 1 1 auto;
  font: inherit;
  padding: 7px 12px;
  border: 1px solid var(--line-strong, #d4cfc2);
  border-radius: var(--r-sm, 9px);
  background: var(--card, #fff);
  color: var(--ink, #16202b);
  min-width: 0;  /* allow shrink in flex parent */
}
.dashboard-filter-input:focus-visible {
  outline: 2px solid var(--focus, #11161b);
  outline-offset: 1px;
  border-color: var(--primary, #1763a6);
}
.dashboard-filter-clear {
  font: inherit;
  cursor: pointer;
  padding: 6px 12px;
  border: 1px solid var(--line-strong, #d4cfc2);
  border-radius: var(--r-sm, 9px);
  background: var(--n-25, #fbf9f4);
  color: var(--ink, #16202b);
}
.dashboard-filter-clear:hover {
  background: var(--n-50, #f1eee6);
}
.dashboard-filter-clear:focus-visible {
  outline: 2px solid var(--focus, #11161b);
  outline-offset: 2px;
}
.dashboard-filtered-out { display: none !important; }
.dashboard-empty-filter {
  padding: 14px;
  text-align: center;
  color: var(--muted, #5b6b7a);
  font-style: italic;
}

/* ==========================================================================
   Service Worker offline banner
   ==========================================================================
   Shown when navigator.onLine flips to false (browser detects no network).
   aria-live="polite" so screen readers announce reconnect events without
   interrupting the user. Positioned at the very top of the page so it's
   immediately visible without scrolling.
   ========================================================================== */
.canamed-offline-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9500;
  padding: 8px 16px;
  text-align: center;
  font-size: 0.9rem;
  font-weight: 600;
  background: var(--key, #c0392b);
  color: #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,.2);
  pointer-events: none;          /* don't block underlying clicks */
}
.canamed-offline-banner[hidden] { display: none; }
.canamed-offline-banner.offline { animation: canamedOfflineSlide var(--dur-base, 260ms) var(--ease-out, ease-out); }
@keyframes canamedOfflineSlide {
  from { transform: translateY(-100%); }
  to   { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .canamed-offline-banner.offline { animation: none; }
}

/* ------------------------------------------------------------------
 * Facilitator UX batch — additions from SIMULATION_FACILITATOR.md
 * ------------------------------------------------------------------ */

/* Required-field marker on the create-session form. The asterisk is
   visual-only (aria-hidden) — screen readers receive aria-required="true"
   on the input itself, which is the canonical, redundant signal. */
.splash-field .req {
  color: var(--err, #b91c1c);
  font-weight: 700;
  margin-left: 4px;
}

/* Inline help text below a field — same colour as splash-sublabel but
   tighter margin so it sits under the input visually attached to it. */
.splash-field-hint {
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 6px 0 0;
}

/* Per-cohort live count chips in the waiting-room card. Visible as soon
   as the prestart panel renders; chips are coloured from the COHORTS
   registry (orgs.js) so a Lyon × Tokyo deployment gets two purple/blue
   chips automatically. The list is empty (and hidden via :empty) when
   no one has joined yet. */
.prestart-cohort-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin: 0 0 10px;
}
.prestart-cohort-row:empty { display: none; }
.prestart-cohort-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--line-strong, #cbd5e1);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
}
.prestart-cohort-chip-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--primary);
}
.prestart-cohort-chip-count {
  font-variant-numeric: tabular-nums;
}

/* Expected-total input next to the live count.  */
.prestart-expected-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin: 4px 0 12px;
}
.prestart-expected-label {
  font-size: 0.85rem;
  color: var(--muted);
}
.prestart-expected-input {
  width: 72px;
  padding: 4px 8px;
  font: inherit;
  font-size: 0.9rem;
  border: 1px solid var(--line-strong, #cbd5e1);
  border-radius: var(--r-sm, 4px);
  background: var(--card);
  color: var(--ink);
}
.prestart-expected-hint {
  font-size: 0.8rem;
  color: var(--muted);
}
.count-expected {
  margin-left: 8px;
  font-size: 0.85em;
  font-weight: 500;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.count-expected.full {
  color: var(--ok, #1e8449);
  font-weight: 700;
}

/* Pre-start "Test alerts" button row. Sits below the Start button so the
   eye-flow is: "Start button (primary), then test alerts hint, then
   waiting room". */
.prestart-test-alerts-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: 10px;
}
.prestart-test-alerts-btn {
  padding: 6px 12px;
  font: inherit;
  font-size: 0.85rem;
  border: 1px solid var(--line-strong, #cbd5e1);
  border-radius: var(--r-sm, 4px);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
}
.prestart-test-alerts-btn:hover {
  border-color: var(--primary);
  background: rgba(46,159,223,0.06);
}
.prestart-test-alerts-hint {
  font-size: 0.8rem;
  color: var(--muted);
  flex: 1 1 240px;
}
.prestart-test-alerts-status {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ok, #1e8449);
}
.prestart-test-alerts-status.err {
  color: var(--err, #b91c1c);
}

/* ----- Branded in-page modal (replaces native confirm) ----- */
.canamed-modal {
  width: min(520px, 92vw);
  border: none;
  border-radius: var(--r-md, 10px);
  padding: 0;
  background: transparent;
  color: var(--ink);
}
.canamed-modal::backdrop {
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(2px);
}
.canamed-modal-form {
  background: var(--card, #ffffff);
  border-radius: var(--r-md, 10px);
  padding: 22px 24px 18px;
  box-shadow: 0 12px 36px rgba(15, 23, 42, 0.25);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.canamed-modal-title {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--ink);
}
.canamed-modal-message {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  white-space: pre-line;
}
.canamed-modal-detail {
  margin: 0;
  padding: 10px 12px;
  background: var(--card-alt, #f1f5f9);
  border: 1px solid var(--line, #e2e8f0);
  border-radius: var(--r-sm, 4px);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.5;
  max-height: 220px;
  overflow: auto;
  white-space: pre-wrap;
}
.canamed-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 6px;
}
.canamed-modal-cancel,
.canamed-modal-confirm {
  font: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  padding: 8px 16px;
  border-radius: var(--r-sm, 4px);
  cursor: pointer;
}
.canamed-modal-cancel {
  background: var(--card, #ffffff);
  color: var(--ink);
  border: 1px solid var(--line-strong, #cbd5e1);
}
.canamed-modal-cancel:hover { background: var(--card-alt, #f1f5f9); }
.canamed-modal-confirm {
  background: var(--primary, #2e9fdf);
  color: #fff;
  border: 1px solid var(--primary, #2e9fdf);
}
.canamed-modal-confirm:hover {
  background: var(--primary-hover, #1c87c2);
}
.canamed-modal-confirm.danger {
  background: var(--err, #b91c1c);
  border-color: var(--err, #b91c1c);
}
.canamed-modal-confirm.danger:hover {
  background: #991b1b;
}

/* ============================================================
 * <dialog> polyfill (R2-34) — applied by dialogShow() in script.js
 * when the native showModal() method is missing (iOS Safari 15.0-15.3).
 *
 * Strategy: when .dialog-polyfill is on the element, force position:fixed,
 * centred, on top of everything, with a dark backdrop drawn via a
 * pseudo-element on the body. We can't use ::backdrop here (it only fires
 * with showModal()), so the backdrop is rendered as a fixed pseudo on
 * the body.dialog-polyfill-open container. Scrolling the body is locked
 * while at least one polyfill dialog is open, matching native semantics.
 * ============================================================ */
body.dialog-polyfill-open {
  overflow: hidden;
}
body.dialog-polyfill-open::before {
  content: "";
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  z-index: 9998;
  pointer-events: none;
}
dialog.dialog-polyfill {
  /* Cancel browser-default <dialog> display:none even without [open] */
  display: block;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  z-index: 9999;
  max-height: 90vh;
  overflow: auto;
  /* The element already carries its own .account-dialog / .canamed-modal
     styling for width + padding + border-radius, so we don't double-style
     it here — we only handle positioning + stacking. */
}

/* ----- Global language switcher (always-visible) -----
   A small fixed-position language picker pinned to the top-right of every
   screen (lobby, waiting room, in-room, admin dashboard, session-ended).
   User feedback: a participant who already passed the splash had no way to
   change the UI language. The splash still has its own larger switcher; we
   hide this one while splash is on screen so the two don't compete. */
.global-lang-switcher {
  position: fixed;
  top: 8px;
  right: 10px;
  z-index: 1500;
  pointer-events: auto;
}
/* When splash is the visible top-level surface, the splash's own dropdown
   is the obvious tap target — hide the floating one to avoid duplication.
   Splash flips body.locked off when dismissed; we use that as the signal. */
body.locked .global-lang-switcher { display: none; }
/* If JS hasn't yet decided whether splash should show (or the page boots
   directly into the lobby), reveal the switcher as soon as the splash is
   hidden. The check above is sufficient. */
.global-lang-select {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--line, #d6d2c9);
  background: var(--bg, #fff);
  color: var(--ink, #2a2a2a);
  padding: 5px 24px 5px 10px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  min-height: 32px;
  max-width: 140px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted, #6a665d) 50%),
    linear-gradient(135deg, var(--muted, #6a665d) 50%, transparent 50%);
  background-position:
    calc(100% - 12px) 50%,
    calc(100% - 7px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.global-lang-select:focus-visible {
  outline: 2px solid var(--primary, #2e9fdf);
  outline-offset: 2px;
}
@media (max-width: 480px) {
  .global-lang-switcher { top: 6px; right: 6px; }
  .global-lang-select { font-size: 0.75rem; padding: 4px 22px 4px 8px; }
}

/* ----- Bug 6 (user-feedback-2): participant settings widget -----
   A small cog button pinned next to the global language switcher; clicking
   opens an inline panel with the theme picker and "Restart tour" link.
   Hidden behind body.locked exactly like the language switcher so it
   doesn't appear over the splash gate. */
.global-settings {
  position: fixed;
  top: 8px;
  right: 158px;            /* sit immediately left of the language pill */
  z-index: 1500;
  pointer-events: auto;
}
body.locked .global-settings { display: none; }
.global-settings-btn {
  appearance: none;
  border: 1px solid var(--line, #d6d2c9);
  background: var(--bg, #fff);
  color: var(--ink, #2a2a2a);
  padding: 5px 8px;
  min-width: 32px;
  min-height: 32px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.global-settings-btn:focus-visible {
  outline: 2px solid var(--primary, #2e9fdf);
  outline-offset: 2px;
}
.global-settings-btn:hover {
  border-color: var(--ink, #2a2a2a);
}
.global-settings-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 240px;
  max-width: calc(100vw - 24px);
  background: var(--card, #fff);
  border: 1px solid var(--line-strong, #c0bcb3);
  border-radius: 10px;
  padding: 14px 14px 12px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 12px;
  color: var(--ink, #2a2a2a);
}
.global-settings-panel[hidden] { display: none; }
.global-settings-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--ink);
}
.global-settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.88rem;
  color: var(--ink);
}
.global-settings-select {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  padding: 4px 24px 4px 10px;
  border-radius: 6px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  min-height: 28px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted, #6a665d) 50%),
    linear-gradient(135deg, var(--muted, #6a665d) 50%, transparent 50%);
  background-position:
    calc(100% - 12px) 50%,
    calc(100% - 7px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.global-settings-link,
.global-settings-close {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: inherit;
  font-size: 0.85rem;
  border-radius: 6px;
  padding: 6px 10px;
  cursor: pointer;
  text-align: left;
}
.global-settings-link:hover,
.global-settings-close:hover {
  border-color: var(--ink);
  background: var(--surface-sunk, transparent);
}
.global-settings-close { align-self: flex-end; }
@media (max-width: 480px) {
  .global-settings { top: 6px; right: 140px; }
  .global-settings-btn { min-width: 28px; min-height: 28px; padding: 4px 6px; }
  .global-settings-panel { min-width: 220px; right: -6px; }
}

/* Mobile: tighten paddings + collapse the textarea + button to a column
   so the send button stays reachable above the fold. */
@media (max-width: 600px) {
  .moda-chat-panel { padding: 8px; gap: 6px; }
  .moda-chat-transcript { max-height: 240px; }
  .moda-chat-form { flex-direction: column; align-items: stretch; }
  .moda-chat-form button { padding: 10px; }
}

/* Honour reduced-motion: kill the auto-scroll smooth behaviour. The JS uses
   plain scrollTop = scrollHeight (no smooth) so nothing to disable here,
   but keep the rule explicit for future maintenance. */
@media (prefers-reduced-motion: reduce) {
  .moda-chat-transcript { scroll-behavior: auto; }
}

/* High-contrast theme: trade subtle accents for the on-token. */
body.theme-high-contrast .moda-chat-panel,
body[data-theme="high-contrast"] .moda-chat-panel {
  border-color: var(--ink);
  background: var(--card);
}
body.theme-high-contrast .moda-chat-bub-assistant,
body[data-theme="high-contrast"] .moda-chat-bub-assistant {
  background: var(--surface-sunk);
  border: 1px solid var(--ink);
}

/* ==========================================================================
   2026-06-02 user-requested polish (Module A/B)
   - red "inappropriate choice" reveal buttons (was green for all)
   - green "points gained" award toast
   - in-chat points-scored chips
   - Group-answers "call a facilitator to advance" completion CTA
   Appended last so these override the base button / toast rules above.
   ========================================================================== */

/* (#3) An inappropriate exam/investigation (one the case did not need): the
   revealed button + its inline answer turn RED instead of the usual green ✓,
   so students see at a glance the move cost points. Higher specificity than
   `.req-btn.done` / `.req-btn.done::before` so it wins regardless of order. */
/* Hardcoded #c0392b / #fdecea (NOT the --key token) so the red treatment is
   identical in light + dark — matches the existing wrong-decision styling
   (.decision.committed.wrong). The dark-theme --key is a light salmon, which
   would give poor contrast on the light "wrong" background. */
.req-btn.done.wrong-choice {
  background: #fdecea;
  color: #c0392b;
  border-color: #c0392b;
}
.req-btn.done.wrong-choice:hover {
  background: #fdecea;
  border-color: #c0392b;
}
.req-btn.done.wrong-choice::before {
  content: "\2717  ";              /* ✗ */
  color: #c0392b;
  font-weight: 700;
}
.req-inline-reveal.wrong .req-inline-answer { color: #c0392b; }
.req-inline-reveal.wrong { border-left: 3px solid #c0392b; padding-left: 8px; }

@media (prefers-contrast: more) {
  .req-btn.done.wrong-choice { border-width: 2px; }
}

/* (#2) Award toast — calm green, mirrors `.toast.loss` (amber) for losses. */
.toast.gain {
  background: var(--ok-strong, #176b3a);
  color: #fff;
  border: 1px solid var(--ok, #1e8449);
}
.toast.gain .toast-sub { opacity: 0.95; }

/* (#5/#6) Completion CTA inside the Group-answers card — appears once every
   bullet has an entry, inviting the room to ping the facilitator to advance. */
.answers-complete-cta {
  margin-top: var(--s-2, 14px);
  padding: 12px 14px;
  border: 1px solid #1e8449;
  border-radius: var(--r-sm, 8px);
  background: #e6f4ec;            /* hardcoded so it stays high-contrast in dark mode */
}
.answers-complete-cta.hidden { display: none; }
.answers-complete-msg {
  margin: 0 0 8px;
  font-weight: 700;
  color: #176b3a;
}
.call-next-btn {
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--r-sm, 8px);
  border: 1px solid #1e8449;
  background: #1e8449;
  color: #fff;
}
.call-next-btn:hover { background: #176b3a; }
.call-next-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.call-next-btn.called {
  background: #e6f4ec;
  color: #176b3a;
  cursor: default;
}

