/* =========================================================
   Circuit Grape theme (light + dark)
   Lilita One = headings (always uppercase), Quicksand = pop-ups and buttons, Poppins = body text.
   Green is only for a correct answer, coral for "not yet correct", red only for electrical hazards;
   one amber primary button per view.
   ========================================================= */
/* Fonts are served with the app (latin subset, SIL OFL: fonts/OFL-*.txt), so no request leaves the site */
@font-face { font-family: "Lilita One"; font-style: normal; font-weight: 400; font-display: swap; src: url(../fonts/lilita-one-400.woff2) format("woff2"); }
@font-face { font-family: "Quicksand"; font-style: normal; font-weight: 300 700; font-display: swap; src: url(../fonts/quicksand-var.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 400; font-display: swap; src: url(../fonts/poppins-400.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 500; font-display: swap; src: url(../fonts/poppins-500.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 600; font-display: swap; src: url(../fonts/poppins-600.woff2) format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 700; font-display: swap; src: url(../fonts/poppins-700.woff2) format("woff2"); }
:root {
  /* Brand ramps. The five Circuit Grape colours are unchanged; each one gains a tint and a
     deep shade, so chips, panels and pressed states take their depth from colour. */
  --lilac: #C77DFF; --grape: #7B2CBF; --grape-hi: #9442DB; --grape-deep: #561C8A; --plum: #2B0A3D;
  --lime: #B9E769;
  --amber: #FF9F1C; --amber-hi: #FFB547; --amber-edge: #D27600;
  --coral: #FF7B72;                        /* "not yet correct" -- encouraging, never alarming */
  --red: #D92B4B;                          /* real electrical hazards only, always with the hazard icon */

  /* Page and surfaces. Depth comes from lighter surfaces, a 1px top highlight and soft shadows. */
  --bg: #F6F2FA; --bg-glow: rgba(199, 125, 255, .24);
  --surface: #FFFFFF; --surface-2: #F1EAF8; --surface-3: #E9DFF4;
  --line: #E4D9EF; --line-strong: #D3C3E4;
  --text: #2B0A3D; --muted: #6E5C80;
  --stage: #0F0D13; --on-stage: #F1EDF5; --on-stage-muted: #A9A1B5;  /* the video stage is the same in both themes */
  --hi: inset 0 1px 0 rgba(255, 255, 255, .9);
  --shadow-1: 0 1px 2px rgba(43, 10, 61, .05), 0 8px 22px -10px rgba(43, 10, 61, .16);
  --shadow-2: 0 2px 6px rgba(43, 10, 61, .06), 0 28px 56px -20px rgba(43, 10, 61, .32);
  --shadow-knob: 0 1px 4px rgba(43, 10, 61, .35);   /* under a slider thumb or a switch knob */

  /* Semantic roles, one meaning each. A state is a pale tint with a stronger ink of the same
     hue, never a solid fill, so it stays readable on white and in dark mode. */
  --accent-text: #7B2CBF; --focus: #7B2CBF;
  --progress: #7B2CBF; --progress-track: #EBE1F5; --buf: #DEC9F6;   /* --buf: video loaded ahead of the playhead */
  --done-bg: #7B2CBF; --done-ink: #FFFFFF;
  --ok-ink: #3F7D26; --ok-tint: #EEF9DC; --ok-line: #3F7D26;        /* correct answers and reward moments only */
  --try-ink: #B23A2A; --try-tint: #FFECE9; --try-line: #E0574B;     /* not yet correct */
  --shown-ink: #9A5800; --shown-tint: #FFF0D9;                      /* the answer had to be revealed */
  --info-ink: #7B2CBF; --info-tint: #F4E8FF;                        /* hints and asides */
  --safety-ink: #B3203C; --safety-tint: #FDE6EA;                    /* safety warnings */
  --opt-bg: #FFFFFF; --opt-line: #9D88B8;   /* the outline of a tappable piece, kept at 3:1 unlike the decorative --line */
  --letter-bg: #F1EAF8; --letter-text: #6E5C80;
  --active-bg: #F7F0FF; --active-line: #7B2CBF;
  --outline-bg: #FFFFFF; --outline-line: #D3C3E4;
  --scrim: rgba(15, 13, 19, .62);
  --scrim-strong: rgba(15, 13, 19, .82);   /* no blur behind it: a stronger tint keeps the card clear */

  --f-display: "Lilita One", "Arial Rounded MT Bold", "Trebuchet MS", sans-serif;
  --f-ui: "Quicksand", "Nunito", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  --f-body: "Poppins", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  --pad: clamp(12px, 3vw, 32px);

  /* Text sizes. --ts is the reader's text size (Settings: A / A+ / A++); headings and the player bar grow less (--tsh).
     rem keeps the browser's own text-size setting. Nothing goes below 14px except tiny numbers on grid cells. */
  --ts: 1; --tsh: 1;
  --fs-cap:  calc(clamp(.875rem, .8rem + .2vw, 1rem) * var(--ts));          /* 14-16: labels, counters, chips, tags */
  --fs-sm:   calc(clamp(.9375rem, .86rem + .25vw, 1.0625rem) * var(--ts));  /* 15-17: instructions, secondary text */
  --fs-body: calc(clamp(1rem, .9rem + .4vw, 1.25rem) * var(--ts));          /* 16-20: body text, options, labels to drag */
  --fs-lead: calc(clamp(1.0625rem, .9rem + .65vw, 1.5rem) * var(--ts));     /* 17-24: prompts, big options */
  --fs-q:    calc(clamp(1.25rem, .95rem + 1.35vw, 2.125rem) * var(--tsh));  /* 20-34: the activity question */
  --fs-h2:   calc(clamp(1.375rem, 1rem + 1.5vw, 2.25rem) * var(--tsh));     /* 22-36: titles */

  /* Motion (playful). Durations: xs taps, s hovers, m most changes, l entrances, xl celebrations.
     --m scales every movement: 1 normally, 0 with reduced motion (fades stay, movement goes). */
  --m: 1;
  --d-xs: 100ms; --d-s: 180ms; --d-m: 280ms; --d-l: 460ms; --d-xl: 800ms;
  --e-out: cubic-bezier(.22, 1, .36, 1);     /* entrances: fast start, soft landing */
  --e-in: cubic-bezier(.55, 0, .75, .2);     /* exits: gentle start, quick finish */
  --e-io: cubic-bezier(.65, 0, .35, 1);      /* moves from A to B */
  --spring: cubic-bezier(.34, 1.56, .64, 1);     /* pops (15% overshoot where linear() is supported) */
  --spring-big: cubic-bezier(.3, 2.2, .5, 1);    /* rewards (33% overshoot) */
  --pop: var(--spring);
  color-scheme: light;
}
/* Real damped springs where the browser supports linear() easing (Chrome 113+, Safari 17.2+, Firefox 112+) */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    --spring: linear(0, 0.029, 0.104, 0.211, 0.337, 0.47, 0.602, 0.725, 0.835, 0.929, 1.006, 1.064, 1.106, 1.132, 1.145, 1.147, 1.141, 1.128, 1.111, 1.092, 1.073, 1.053, 1.036, 1.02, 1.007, 0.996, 0.988, 0.983, 0.98, 0.978, 0.978, 0.98, 0.982, 0.984, 0.987, 0.99, 0.993, 0.995, 0.998, 0.999, 1.001, 1.002, 1.003, 1.003, 1);
    --spring-big: linear(0, 0.091, 0.317, 0.603, 0.885, 1.113, 1.263, 1.329, 1.32, 1.258, 1.167, 1.071, 0.987, 0.927, 0.895, 0.89, 0.905, 0.933, 0.965, 0.995, 1.018, 1.032, 1.037, 1.034, 1.026, 1.016, 1.005, 0.996, 0.991, 0.988, 0.988, 0.99, 0.994, 0.997, 1, 1.003, 1.004, 1.004, 1.004, 1.003, 1.001, 1, 0.999, 0.999, 1);
  }
}
@media (prefers-reduced-motion: reduce) { :root { --m: 0; } }
:root[data-motion="reduce"] { --m: 0; }   /* the device setting or Settings > Reduce motion (set by boot.js / common.js) */
:root[data-text="l"] { --ts: 1.15; --tsh: 1.08; }
:root[data-text="xl"] { --ts: 1.3; --tsh: 1.15; }
/* Phones held sideways have little height: text grows less there (turn the phone upright for the full size) */
@media (max-height: 520px) and (orientation: landscape) {
  :root[data-text="l"] { --ts: 1.08; --tsh: 1.04; }
  :root[data-text="xl"] { --ts: 1.15; --tsh: 1.08; }
}
/* Dark mode uses violet-tinted neutrals and lets the accents carry the colour: each surface is a
   little lighter than the one behind it, rather than more purple. Only the roles change here. */
:root[data-theme="dark"] {
  --bg: #141119; --bg-glow: rgba(123, 44, 191, .30);
  --surface: #1D1924; --surface-2: #28232F; --surface-3: #332C3D;
  --line: #2E2838; --line-strong: #3D3549;
  --text: #EEEAF3; --muted: #A39BAE;
  --hi: inset 0 1px 0 rgba(255, 255, 255, .05);
  --shadow-1: 0 12px 30px -14px rgba(0, 0, 0, .7);
  --shadow-2: 0 30px 70px -24px rgba(0, 0, 0, .85);
  --shadow-knob: 0 1px 4px rgba(0, 0, 0, .6);

  --accent-text: #C77DFF; --focus: #C77DFF;
  --progress: #C77DFF; --progress-track: #2E2838; --buf: #4A4155;
  --done-bg: #C77DFF; --done-ink: #2B0A3D;
  --ok-ink: #B9E769; --ok-tint: rgba(185, 231, 105, .10); --ok-line: #B9E769;
  --try-ink: #FF8E85; --try-tint: rgba(255, 123, 114, .10); --try-line: #FF7B72;
  --shown-ink: #FFB547; --shown-tint: rgba(255, 159, 28, .12);
  --info-ink: #D9A8FF; --info-tint: rgba(199, 125, 255, .10);
  --safety-ink: #FF6B85; --safety-tint: rgba(217, 43, 75, .14);
  --opt-bg: #1D1924; --opt-line: #6E657E;
  --letter-bg: #2E2838; --letter-text: #D9D2E3;
  --active-bg: rgba(123, 44, 191, .16); --active-line: #A75BEB;
  --outline-bg: transparent; --outline-line: rgba(199, 125, 255, .55);
  --scrim: rgba(10, 8, 13, .74);
  --scrim-strong: rgba(10, 8, 13, .88);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  min-height: 100vh; min-height: 100dvh;
  /* One soft light source in the top-left corner, so a plain page is not a flat slab */
  background: radial-gradient(1100px 560px at 10% -12%, var(--bg-glow), transparent 65%), var(--bg);
  background-attachment: fixed; color: var(--text);
  font: var(--fs-body)/1.5 var(--f-body);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  transition: background-color .2s, color .2s;
}
button, input { font: inherit; color: inherit; }
button { cursor: pointer; -webkit-tap-highlight-color: transparent; }
a { -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.muted { color: var(--muted); }
svg { flex: none; }

/* ---------- Buttons ---------- */
/* One primary button per view: amber, with a darker bottom edge that shortens when pressed.
   Feedback colour lives in the message, never in the button -- there is no green "correct"
   button and no red "try again" button. Secondary actions are outlined; opting out is a text
   link. Every variant keeps a 48px tap target. */
.btn {
  display: inline-flex; text-decoration: none; align-items: center; justify-content: center; gap: .5em;
  min-height: 48px; padding: .72em 1.5em;
  border: 2px solid var(--outline-line); border-radius: 999px; background: var(--outline-bg); color: var(--text);
  font: 700 var(--fs-body)/1.15 var(--f-ui); text-align: center;
  box-shadow: var(--hi);
  transition: transform var(--d-s) var(--spring), box-shadow var(--d-s) var(--e-out), filter var(--d-s), border-color var(--d-s), background-color var(--d-s), color var(--d-s);
}
/* Press: pushed down into its own edge, quickly; release springs back. Movement is scaled by --m,
   so reduced motion keeps the colour change and drops the travel. */
.btn:active:not(:disabled) { transform: translateY(calc(2px * var(--m))) scale(calc(1 - .02 * var(--m))); transition-duration: var(--d-xs); }

.btn.primary {
  background: linear-gradient(180deg, var(--amber-hi), var(--amber)); border-color: transparent; color: var(--plum);
  padding-bottom: calc(.72em + 2px);
  box-shadow: inset 0 -3px 0 var(--amber-edge), inset 0 1px 0 rgba(255, 255, 255, .45), 0 8px 20px -10px rgba(255, 159, 28, .8);
}
.btn.primary:active:not(:disabled) { box-shadow: inset 0 -1px 0 var(--amber-edge), 0 4px 12px -8px rgba(255, 159, 28, .8); }
/* The one red button left: confirming that saved data is about to be deleted. Not a feedback
   state -- an irreversible action, so it keeps a colour of its own. */
.btn.danger {
  background: var(--red); border-color: transparent; color: #fff;
  padding-bottom: calc(.72em + 2px);
  box-shadow: inset 0 -3px 0 var(--safety-ink), inset 0 1px 0 rgba(255, 255, 255, .25);
}
.btn.danger:active:not(:disabled) { box-shadow: inset 0 -1px 0 var(--safety-ink); }
/* Opting out: a text link, so it never competes with the primary beside it. */
.btn.ghost {
  background: none; border-color: transparent; color: var(--muted); box-shadow: none;
  padding: .72em .6em; text-decoration: underline; text-underline-offset: 3px; font-weight: 600;
}
/* Disabled comes after the variants on purpose: a variant has the same specificity, so whichever
   is written last wins. Listed per variant as well, so re-ordering cannot quietly undo it.
   It must not rely on opacity alone -- a faded amber still reads as a live button. */
.btn:disabled, .btn.primary:disabled, .btn.danger:disabled, .btn.ghost:disabled {
  background: var(--surface-3); background-image: none; border-color: transparent; color: var(--muted);
  box-shadow: none; cursor: not-allowed; transform: none; text-decoration: none;
}
@media (hover: hover) {
  .btn:hover:not(:disabled) { border-color: var(--accent-text); }
  .btn:hover:not(:disabled):not(:active) { transform: translateY(calc(-1px * var(--m))); }
  .btn.primary:hover:not(:disabled) {
    border-color: transparent;
    box-shadow: inset 0 -3px 0 var(--amber-edge), inset 0 1px 0 rgba(255, 255, 255, .45), 0 10px 28px -8px rgba(255, 159, 28, .9);
  }
  .btn.danger:hover:not(:disabled) { border-color: transparent; filter: brightness(1.06); }
  .btn.ghost:hover:not(:disabled) { border-color: transparent; color: var(--text); }
}

/* Small pill (name, back link) */
.chip-btn {
  display: inline-flex; align-items: center; gap: .55em; min-height: 44px; max-width: 40vw; padding: 8px 16px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none;
  border: 2px solid var(--outline-line); border-radius: 999px; background: var(--outline-bg); color: var(--text);
  font: 700 calc(.875rem * var(--tsh))/1.1 var(--f-ui); box-shadow: var(--hi);
  transition: transform var(--d-s) var(--spring), box-shadow var(--d-s) var(--e-out), border-color var(--d-s);
}
.chip-btn:active { transform: translateY(calc(1px * var(--m))) scale(calc(1 - .03 * var(--m))); transition-duration: var(--d-xs); }
@media (hover: hover) {
  .chip-btn:hover { border-color: var(--accent-text); }
}
#nameBtn::before { content: ""; flex: none; width: .62em; height: .62em; border-radius: 50%; background: var(--accent-text); }
.chip-btn > span { overflow: hidden; text-overflow: ellipsis; }

/* Round icon button, on the page itself: an outlined circle like the other secondary controls */
.icon-btn {
  display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0;
  border: 1.5px solid var(--outline-line); border-radius: 50%; background: var(--outline-bg); color: var(--accent-text);
  box-shadow: var(--hi);
  transition: background-color var(--d-s), border-color var(--d-s), transform var(--d-s) var(--spring);
}
.icon-btn:active:not(:disabled) { transform: scale(calc(1 - .08 * var(--m))); transition-duration: var(--d-xs); }
@media (hover: hover) {
  .icon-btn:hover { border-color: var(--accent-text); background: var(--surface-2); }
}
.icon-btn svg { width: 24px; height: 24px; }

/* Counter chip, e.g. "AKTIVITI 2 / 5" */
.chip {
  display: inline-flex; align-items: center; gap: .7em; padding: .6em 1.15em;
  border: 2px solid var(--line); border-radius: 999px; background: var(--surface-2); color: var(--text);
  font: 700 var(--fs-cap)/1 var(--f-ui); letter-spacing: .14em; text-transform: uppercase; white-space: nowrap;
}
.chip::before { content: ""; flex: none; width: .62em; height: .62em; border-radius: 50%; background: var(--accent-text); }

.err {
  margin: 20px auto; max-width: 720px; padding: 14px 18px; border-radius: 18px;
  background: var(--try-tint); color: var(--try-ink); font: 600 var(--fs-sm)/1.45 var(--f-ui);
}

/* ---------- Page ---------- */
.app {
  min-height: 100vh; min-height: 100dvh;
  display: flex; flex-direction: column; align-items: center;
  padding: env(safe-area-inset-top) max(var(--pad), env(safe-area-inset-right)) max(16px, env(safe-area-inset-bottom)) max(var(--pad), env(safe-area-inset-left));
}
.top { width: 100%; max-width: 1280px; display: flex; align-items: center; gap: 12px; padding: 12px 0; }
.top .ep-title { flex: 1; }
.back { flex: none; }
.ep-title {
  margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 400 calc(clamp(1.1875rem, .9rem + 1.6vw, 2rem) * var(--tsh))/1.1 var(--f-display); text-transform: uppercase; letter-spacing: .01em;
}
.top-actions { display: flex; align-items: center; gap: 10px; flex: none; margin-left: auto; }
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; font: 600 var(--fs-cap)/1.2 var(--f-ui); color: var(--accent-text); }
.brand::before {
  content: ""; flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--grape) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M9 6.6v10.8a1 1 0 0 0 1.5.86l8.6-5.4a1 1 0 0 0 0-1.72l-8.6-5.4A1 1 0 0 0 9 6.6z'/%3E%3C/svg%3E") center / 60% no-repeat;
}
.brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- Player shell ---------- */
.player {
  width: min(100%, 1280px);
  width: min(100%, 1280px, max(480px, calc((100vh - 210px) * 16 / 9)));
  width: min(100%, 1280px, max(480px, calc((100dvh - 210px) * 16 / 9)));
  margin: 0 auto; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-radius: clamp(18px, 2.4vw, 28px);
  box-shadow: var(--hi), var(--shadow-2);
}
.stage { position: relative; aspect-ratio: 16 / 9; background: var(--stage); cursor: pointer; -webkit-user-select: none; user-select: none; }
.stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: var(--stage); }
.big-play {
  position: absolute; left: 50%; top: 50%; width: clamp(64px, 9vw, 108px); height: auto; aspect-ratio: 1; padding: 0 0 0 .4%;
  display: grid; place-items: center; border: 0; border-radius: 50%;
  background: linear-gradient(180deg, var(--amber-hi), var(--amber)); color: var(--plum); transform: translate(-50%, -50%);
  box-shadow: inset 0 -4px 0 var(--amber-edge), inset 0 1px 0 rgba(255, 255, 255, .45), 0 12px 34px -10px rgba(255, 159, 28, .85);
  transition: transform var(--d-s) var(--spring), box-shadow var(--d-s) var(--e-out);
  animation: big-play-in var(--d-l) var(--spring);   /* pops back each time the video pauses */
}
.big-play svg { width: 44%; height: 44%; }
@media (hover: hover) { .big-play:hover { transform: translate(-50%, -50%) scale(calc(1 + .06 * var(--m))); } }
.big-play:active { transform: translate(-50%, calc(-50% + 2px * var(--m))) scale(calc(1 - .03 * var(--m))); box-shadow: inset 0 -1px 0 var(--amber-edge), 0 6px 18px -10px rgba(255, 159, 28, .85); transition-duration: var(--d-xs); }
@keyframes big-play-in { from { opacity: 0; transform: translate(-50%, -50%) scale(calc(1 - .4 * var(--m))); } }

/* ---------- Start screen (title, description, safety disclaimer) ---------- */
/* Sizes follow the stage: cqw = stage width, cqh = stage height (full screen) or the viewport height (windowed). */
.stage { container-type: inline-size; }
body.fs .stage { container-type: size; }
/* Windowed: keeps a 16:9 minimum but grows taller when its content needs it. Clipping sits on the stage,
   because overflow on .ov-start itself would stop aspect-ratio from growing. */
.stage.has-start { aspect-ratio: auto; overflow: hidden; cursor: default; }
.ov-start {
  position: relative; z-index: 20; aspect-ratio: 16 / 9; display: flex;
  padding: clamp(16px, min(4.4cqw, 7cqh), 80px) clamp(16px, 5cqw, 96px);
  color: var(--text); cursor: default;
  /* Decoration in the scheme colours (no picture), painted as the background so it is sized from the screen and can
     never stick out and make the start screen scroll: a large circle off the top right, a smaller one off the
     bottom left and a small dot in the bottom right corner */
  --r1: max(29cqw, 20cqh); --r2: 12cqw; --r3: 3cqw;
  background:
    radial-gradient(circle var(--r1) at calc(116% - var(--r1)) calc(var(--r1) - 34%), color-mix(in srgb, var(--surface-3) 80%, transparent) calc(100% - 1px), transparent 100%),
    radial-gradient(circle var(--r2) at calc(var(--r2) - 7%) calc(112% - var(--r2)), var(--surface-2) calc(100% - 1px), transparent 100%),
    radial-gradient(circle var(--r3) at calc(100% - 7cqw) calc(100% - 6cqw), var(--surface-3) calc(100% - 1px), transparent 100%),
    var(--bg);
}
/* Full screen: fills the stage and scrolls only if the screen is really too small */
body.fs .ov-start { position: absolute; inset: 0; aspect-ratio: auto; overflow: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.start {
  position: relative; z-index: 1; width: 100%; max-width: 1320px; margin: auto; /* auto margins centre without clipping when it scrolls */
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: clamp(16px, min(3.4cqw, 5cqh), 64px); align-items: center;
}
.kicker { margin: 0; font: 600 calc(clamp(.875rem, min(1.3cqw, 2.2cqh), 1.125rem) * var(--ts))/1.3 var(--f-ui); color: var(--accent-text); }
.kicker:empty { display: none; }
.start-title { margin: .15em 0 .3em; font: 400 calc(clamp(1.625rem, min(5cqw, 9cqh), 5.25rem) * var(--tsh))/1 var(--f-display); text-transform: uppercase; }
.lead { margin: 0; max-width: 26em; font-size: calc(clamp(1rem, min(1.6cqw, 2.8cqh), 1.375rem) * var(--ts)); line-height: 1.55; }
.lead:empty { display: none; }
.facts { margin: clamp(8px, 2cqh, 20px) 0 0; font: 600 calc(clamp(.875rem, min(1.2cqw, 2.2cqh), 1.0625rem) * var(--ts))/1.3 var(--f-ui); color: var(--muted); }
.safety {
  padding: clamp(12px, min(1.8cqw, 3cqh), 26px); background: var(--surface);
  border: 2px solid var(--red); border-radius: clamp(18px, 2vw, 28px); box-shadow: var(--hi), var(--shadow-2);
}
.safety-head { display: flex; align-items: center; gap: 12px; }
.safety-k { margin: 0; font: 400 calc(clamp(1.0625rem, min(1.9cqw, 3.4cqh), 1.875rem) * var(--tsh))/1.05 var(--f-display); text-transform: uppercase; color: var(--safety-ink); }
.safety-list { margin: clamp(6px, 1.4cqh, 14px) 0 0; padding: 0 0 0 1.3em; font-size: calc(clamp(.9375rem, min(1.35cqw, 2.4cqh), 1.125rem) * var(--ts)); line-height: 1.5; }
.safety-list li + li { margin-top: .2em; }
.safety-list li::marker { color: var(--red); }
/* The general safety items, one tick each (player.js); the episode's own lines follow as a plain list */
.acks { display: flex; flex-direction: column; gap: clamp(6px, 1.2cqh, 10px); margin-top: clamp(8px, 2cqh, 18px); }
.ack {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  border-radius: 16px; background: var(--safety-tint); font: 700 calc(clamp(.9375rem, min(1.3cqw, 2.4cqh), 1.125rem) * var(--ts))/1.3 var(--f-ui); cursor: pointer;
}
.ack input {
  -webkit-appearance: none; appearance: none; flex: none; width: 28px; height: 28px; margin: 0;
  display: grid; place-items: center; border: 3px solid var(--red); border-radius: 9px; background: var(--surface);
  cursor: pointer; transition: background-color var(--d-s), border-color var(--d-s), transform var(--d-s) var(--spring);
}
.ack:active input { transform: scale(calc(1 - .12 * var(--m))); transition-duration: var(--d-xs); }
.ack input:checked { background: var(--grape); border-color: var(--grape); animation: m-pulse var(--d-m) var(--e-io); }
.ack input:checked::after {
  content: ""; width: 70%; height: 70%; animation: m-in-pop var(--d-m) var(--spring-big);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
.ack input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(10px, 2.4cqh, 24px); }
.btn svg { width: 1.15em; height: 1.15em; }
/* Narrow stage (phones, portrait tablets): one column, sized by width */
@container (max-width: 760px) {
  .start { grid-template-columns: 1fr; max-width: 560px; }
  .start-title { font-size: calc(clamp(1.625rem, min(8cqw, 7cqh), 3.5rem) * var(--tsh)); }
  .lead { font-size: calc(clamp(1rem, min(4cqw, 2.6cqh), 1.1875rem) * var(--ts)); }
  .safety-k { font-size: calc(clamp(1.0625rem, min(5cqw, 3.4cqh), 1.625rem) * var(--tsh)); }
  .safety-list, .ack { font-size: calc(clamp(.9375rem, min(3.7cqw, 2.4cqh), 1.0625rem) * var(--ts)); }
  .cta .btn { flex: 1 1 auto; }
}
/* Full screen on a portrait screen (phones, tablets held upright): stacked and larger */
@container (orientation: portrait) and (min-width: 761px) {
  .start { grid-template-columns: 1fr; max-width: 720px; }
  .start-title { font-size: calc(clamp(1.875rem, min(7.4cqw, 6cqh), 4.5rem) * var(--tsh)); }
  .lead { font-size: calc(clamp(1rem, min(2.8cqw, 2.2cqh), 1.5rem) * var(--ts)); }
  .safety-k { font-size: calc(clamp(1.25rem, min(3.6cqw, 3cqh), 2rem) * var(--tsh)); }
  .safety-list, .ack { font-size: calc(clamp(.9375rem, min(2.5cqw, 2cqh), 1.3125rem) * var(--ts)); }
  .kicker, .facts { font-size: calc(clamp(.875rem, min(2.1cqw, 1.8cqh), 1.1875rem) * var(--ts)); }
  .cta .btn { flex: 1 1 auto; font-size: calc(clamp(1rem, 2.4cqw, 1.375rem) * var(--ts)); min-height: 56px; }
}
/* Full screen on a short landscape screen (phones turned sideways, 200% zoom): compact, two columns when there is
   safety text: title, description and buttons on the left, the safety card with its tick box on the right */
@container (orientation: landscape) and (max-height: 520px) {
  body.fs .ov-start { padding: clamp(10px, 4cqh, 20px) clamp(16px, 4cqw, 40px); }
  .start { gap: clamp(8px, 3cqh, 16px) clamp(16px, 3cqw, 32px); max-width: 1100px; }
  .start:has(#sSafety:not([hidden])) {
    grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); grid-template-areas: "main side" "cta side"; align-items: center;
  }
  .start:has(#sSafety:not([hidden])) .start-side { display: contents; }
  .start-main { grid-area: main; align-self: end; }
  .safety { grid-area: side; padding: 10px 14px; }
  .start .cta { grid-area: cta; align-self: start; margin-top: 0; }
  .start:not(:has(#sSafety:not([hidden]))) .cta { margin-top: 12px; }
  .start-title { margin: .1em 0 .25em; font-size: calc(clamp(1.25rem, min(4.4cqw, 8cqh), 2.75rem) * var(--tsh)); }
  .lead { font-size: calc(clamp(.9375rem, min(2cqw, 5cqh), 1.125rem) * var(--ts)); line-height: 1.45; }
  .kicker, .facts { font-size: calc(.875rem * var(--ts)); }
  .facts { margin-top: 6px; }
  .safety-head { gap: 10px; }
  .safety .warn-badge { width: 30px; }
  .safety-k { font-size: calc(clamp(1.0625rem, 6cqh, 1.375rem) * var(--tsh)); }
  .safety-list { margin-top: 4px; }
  .safety-list, .ack { font-size: calc(clamp(.875rem, 4.6cqh, 1.0625rem) * var(--ts)); line-height: 1.4; }
  .safety-list li + li { margin-top: .1em; }
  .acks { gap: 6px; margin-top: 8px; }
  .ack { min-height: 44px; padding: 6px 12px; }
  .ack input { width: 26px; height: 26px; }
  .cta .btn { flex: 1 1 auto; min-height: 48px; padding-block: .5em; }
}
/* Full screen, when the start screen is still taller than the stage (.is-tall, player.js): the buttons stay in view,
   sticky at the bottom, and settle in place once scrolled to (the ticks are found by scrolling). Opaque backings
   (shadows, so the layout does not change) let the text slide under them. */
body.fs .ov-start.is-tall .cta { position: sticky; z-index: 1; bottom: 0; background: var(--bg); box-shadow: 0 -8px 0 4px var(--bg), 0 8px 0 4px var(--bg); }

/* Safety badge (shared by the start screen, the safety dialog and timed warnings) */
.warn-badge { flex: none; display: grid; place-items: center; width: clamp(32px, 3.4vw, 46px); aspect-ratio: 1; border-radius: 50%; background: var(--red); color: #fff; }
.warn-badge svg { width: 60%; height: 60%; stroke-width: 3.2; }
.safety-dlg .safety-k { font-size: calc(clamp(1.5rem, 3vw, 2.125rem) * var(--tsh)); }

/* ---------- Timed safety warning ---------- */
.warn {
  position: absolute; top: clamp(8px, 2vw, 28px); left: 50%; z-index: 15;
  display: flex; align-items: center; gap: clamp(10px, 1.4vw, 18px);
  width: max-content; max-width: min(860px, calc(100% - 20px));
  padding: clamp(8px, 1vw, 14px) clamp(6px, .8vw, 10px) clamp(8px, 1vw, 14px) clamp(10px, 1.2vw, 16px);
  background: var(--surface); color: var(--text); border: 2px solid var(--red); border-radius: clamp(14px, 1.8vw, 24px);
  box-shadow: var(--hi), var(--shadow-2); transform: translateX(-50%); overflow: hidden; cursor: default;
}
.warn::after { content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 4px; background: var(--red); transform-origin: left; }
.warn.run { animation: drop var(--d-l) var(--spring); }
.warn.m-ghost::after { display: none; }   /* the closing copy: no countdown bar */
.warn.run::after { animation: drain var(--dur, 8s) linear forwards; }
.warn.is-held::after { animation-play-state: paused; }   /* paused video, or pointer / focus on the warning */
.safety-sub { margin: 6px 0 0; color: var(--muted); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .1em; text-transform: uppercase; }
/* Captions (episode .vtt files, CC button in the bar) */
video::cue { background: rgba(18, 3, 26, .82); color: #fff; font-family: "Poppins", system-ui, sans-serif; line-height: 1.35; }
#ccBtn[aria-pressed="true"] { background: var(--grape); border-color: var(--grape); color: #fff; }
@keyframes drop { from { opacity: 0; transform: translate(-50%, calc(-30px * var(--m))); } }
@keyframes drain { to { transform: scaleX(0); } }
.warn .warn-badge { width: clamp(28px, 3vw, 44px); }
.warn-txt { min-width: 0; }
.warn-k { font: 400 calc(clamp(.9375rem, 1.6vw, 1.375rem) * var(--tsh))/1.1 var(--f-display); text-transform: uppercase; color: var(--safety-ink); }
.warn-t { margin-top: .15em; font-size: var(--fs-sm); line-height: 1.4; }
.warn-x { width: 40px; height: 40px; }
.warn-x svg { width: 20px; height: 20px; }

/* ---------- Controls ---------- */
.controls { position: relative; padding: 10px 18px 14px; background: var(--surface); border-top: 1px solid var(--line); color: var(--text); container-type: inline-size; }
.bar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; }
.bar button, .bar .time { flex: none; }
/* Wide: the groups disappear and every button sits in one row in this order */
.g-trans, .g-media, .g-tabs { display: contents; }
#playBtn { order: 1; } #backBtn { order: 2; } #fwdBtn { order: 3; } .time { order: 4; } .bar > .now { order: 5; }
#safetyBtn { order: 6; } #audioBtn { order: 7; } #ccBtn { order: 7; } #epsBtn { order: 8; } #nameBtn { order: 9; } #setBtn { order: 10; } #fsBtn { order: 11; }
.icon-btn .lbl { display: none; }
.controls .chip-btn { min-height: 44px; max-width: 200px; padding: 8px 14px; }
.eps-btn svg { flex: none; width: 20px; height: 20px; color: var(--accent-text); }
.controls button:disabled { opacity: .4; cursor: not-allowed; box-shadow: none; }
.play-btn {
  display: grid; place-items: center; flex: none; width: 54px; height: 54px; margin-right: 4px; padding: 0;
  border: 0; border-radius: 50%; background: linear-gradient(180deg, var(--grape-hi), var(--grape)); color: #fff;
  box-shadow: inset 0 -3px 0 var(--grape-deep), inset 0 1px 0 rgba(255, 255, 255, .18);
  transition: transform var(--d-s) var(--spring), box-shadow var(--d-s) var(--e-out), filter var(--d-s);
}
.play-btn:active:not(:disabled) { transform: translateY(calc(2px * var(--m))) scale(calc(1 - .04 * var(--m))); box-shadow: inset 0 -1px 0 var(--grape-deep); transition-duration: var(--d-xs); }
@media (hover: hover) { .play-btn:hover:not(:disabled) { filter: brightness(1.08); } }
.play-btn svg { width: 26px; height: 26px; }
/* In the bar: a quiet rounded square that fills in only when its feature is on */
.controls .icon-btn { width: 46px; height: 46px; border: 0; border-radius: 12px; background: transparent; box-shadow: none; color: var(--text); }
@media (hover: hover) { .controls .icon-btn:hover { background: var(--surface-2); border-color: transparent; } }
.controls .safety-btn { color: var(--safety-ink); }
.controls .icon-btn svg { width: 26px; height: 26px; }
.time { flex: none; margin: 0 6px; font: 700 calc(.9375rem * var(--tsh))/1 var(--f-ui); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bar > .now { flex: 1 1 auto; display: flex; justify-content: center; min-width: 0; }
.now:empty { visibility: hidden; }
/* The progress chip opens the activity list (player.js): a list icon instead of the dot */
.now-btn { min-height: 44px; cursor: pointer; transition: border-color var(--d-s), transform var(--d-s) var(--spring); }
@media (hover: hover) { .now-btn:hover { border-color: var(--accent-text); } }
.now-btn:active { transform: scale(calc(1 - .03 * var(--m))); transition-duration: var(--d-xs); }
.now-btn::before { width: 1.15em; height: 1.15em; border-radius: 0; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M9.5 6.5H20M9.5 12H20M9.5 17.5H20'/%3E%3Ccircle cx='4.8' cy='6.5' r='1.6' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.8' cy='12' r='1.6' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.8' cy='17.5' r='1.6' fill='%23000' stroke='none'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M9.5 6.5H20M9.5 12H20M9.5 17.5H20'/%3E%3Ccircle cx='4.8' cy='6.5' r='1.6' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.8' cy='12' r='1.6' fill='%23000' stroke='none'/%3E%3Ccircle cx='4.8' cy='17.5' r='1.6' fill='%23000' stroke='none'/%3E%3C/svg%3E") center / contain no-repeat; }

/* Seek bar: the range input supplies the thumb and input handling; the track, fill and marks are drawn underneath. */
/* 44px tall for touch, but it takes 32px of height (negative margins); the button row paints over the overlap */
.seek { position: relative; height: 44px; margin: -6px 4px; --r: 11px; --p: 0; --b: 0; touch-action: none; }
.track { position: absolute; left: 0; right: 0; top: 50%; height: 10px; margin-top: -5px; border-radius: 999px; background: var(--progress-track); overflow: hidden; transition: height .15s, margin .15s; }
.buf, .fill { position: absolute; top: 0; bottom: 0; left: 0; }
.buf { width: calc(var(--r) + (100% - 2 * var(--r)) * var(--b)); background: var(--buf); }
.fill { width: calc(var(--r) + (100% - 2 * var(--r)) * var(--p)); background: linear-gradient(90deg, var(--grape-hi), var(--progress)); }
.rail { position: absolute; left: var(--r); right: var(--r); top: 50%; height: 0; pointer-events: none; }
.lockzone {
  position: absolute; top: -5px; right: calc(-1 * var(--r)); height: 10px; border-radius: 0 999px 999px 0;
  background: repeating-linear-gradient(45deg, rgba(199, 125, 255, .45) 0 4px, transparent 4px 9px);
}
.marks { position: absolute; inset: 0; }
/* Activity markers are diamonds, not dots: shape tells the three states apart before colour does. */
.mark {
  position: absolute; top: -7px; width: 14px; height: 14px; margin-left: -7px; border-radius: 3px;
  background: var(--surface); border: 2.5px solid var(--progress); rotate: 45deg;
}
.mark.done { background: var(--lime); border-color: var(--ok-line); box-shadow: 0 0 10px 1px rgba(185, 231, 105, .55); }
.mark.shown { background: var(--amber); border-color: var(--amber-edge); }
.seek input {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer;
}
.seek input:focus-visible { outline: none; }
.seek input::-webkit-slider-runnable-track { height: 44px; background: transparent; border: 0; }
.seek input::-moz-range-track { height: 44px; background: transparent; border: 0; }
.seek input::-moz-range-progress { background: transparent; }
.seek input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; box-sizing: border-box; width: 22px; height: 22px; margin-top: 11px;
  border: 4px solid var(--surface); border-radius: 50%; background: var(--progress); box-shadow: 0 0 14px 2px rgba(199, 125, 255, .55);
  transition: transform .15s;
}
.seek input::-moz-range-thumb {
  box-sizing: border-box; width: 22px; height: 22px;
  border: 4px solid var(--surface); border-radius: 50%; background: var(--progress); box-shadow: 0 0 14px 2px rgba(199, 125, 255, .55);
}
.seek input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus); }
.seek input:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus); }
@media (hover: hover) {
  .seek:hover .track { height: 12px; margin-top: -6px; }
  .seek input:hover::-webkit-slider-thumb { transform: scale(1.12); }
}

/* ---------- Fake full screen ---------- */
body.fs { overflow: hidden; }
body.fs .top { display: none; }
body.fs .player {
  position: fixed; inset: 0; z-index: 20; width: auto; max-width: none; border: 0; border-radius: 0;
  display: flex; flex-direction: column; box-shadow: none; background: var(--stage);
}
body.fs .stage { flex: 1; aspect-ratio: auto; min-height: 0; }
.player-top { display: none; }
body.fs .player-top {
  display: flex; align-items: center; gap: 12px; flex: none;
  padding: max(10px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) 10px max(18px, env(safe-area-inset-left));
  background: var(--surface); color: var(--text); border-bottom: 1px solid var(--line);
}
.player-title {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 400 calc(clamp(1.0625rem, 2vw, 1.5rem) * var(--tsh))/1.15 var(--f-display); text-transform: uppercase;
}
.top-tools { display: flex; align-items: center; gap: 2px; flex: none; }
.top-tools:empty { display: none; }
/* While the start screen is up it shows the title itself, and the seek bar is locked anyway */
.app.is-start .player-top, .app.is-start .seek { display: none !important; }
body.fs .controls {
  border-top: 0;
  padding-bottom: max(14px, env(safe-area-inset-bottom));
  padding-left: max(18px, env(safe-area-inset-left)); padding-right: max(18px, env(safe-area-inset-right));
}

/* ---------- Controls over the video (full screen, once the start screen is gone) ----------
   The video fills the screen; the title row and the bar float over it on dark gradients (no box, no blur: a live blur
   is redrawn on every video frame). player.js sets .is-idle on .player after 3 s without the mouse moving while the
   video plays (never while paused, while a pop-up is open or while the pointer or keyboard focus is on the rows),
   and moves the bar's tabs into the title row on narrow screens (.tabs-top). Pop-ups and dialogs keep the theme. */
body.fs .app:not(.is-start) :is(.player-top, .controls) {
  /* Always light on dark, whatever the theme */
  --text: var(--on-stage); --muted: var(--on-stage-muted); --surface: #1D1924; --surface-2: rgba(255, 255, 255, .14);
  --line: rgba(255, 255, 255, .18); --accent-text: #C77DFF; --focus: #C77DFF; --safety-ink: #FF6B85;
  --progress: #C77DFF; --progress-track: rgba(255, 255, 255, .26); --buf: rgba(255, 255, 255, .45);
  --outline-line: rgba(255, 255, 255, .42); --outline-bg: rgba(15, 13, 19, .35); --hi: 0 0 transparent;
  color-scheme: dark;
  position: absolute; left: 0; right: 0; z-index: 30; background: none; border: 0; color: var(--text);
  transition: opacity var(--d-s) var(--e-out), translate var(--d-s) var(--e-out);
}
body.fs .app:not(.is-start) .player-top { top: 0; text-shadow: 0 1px 3px rgba(0, 0, 0, .5); }
body.fs .app:not(.is-start) .controls { bottom: 0; }
body.fs .app:not(.is-start) .time { text-shadow: 0 1px 3px rgba(0, 0, 0, .5); }
/* The gradients reach well into the picture, without catching clicks meant for the video (player.js lifts captions
   clear of the bar's 40px fade, which would dim them) */
body.fs .app:not(.is-start) :is(.player-top, .controls)::before {
  content: ""; position: absolute; left: 0; right: 0; z-index: -1; pointer-events: none;
}
body.fs .app:not(.is-start) .player-top::before {
  top: 0; height: calc(100% + 48px); background: linear-gradient(rgba(10, 8, 13, .8), rgba(10, 8, 13, .45) 50%, transparent);
}
body.fs .app:not(.is-start) .controls::before {
  bottom: 0; height: calc(100% + 40px); background: linear-gradient(to top, rgba(10, 8, 13, .88), rgba(10, 8, 13, .58) 60%, transparent);
}
/* Idle: both rows fade out (slower than they come back) and slide a little towards their edge; the cursor hides */
body.fs .app:not(.is-start) .player.is-idle :is(.player-top, .controls) {
  opacity: 0; pointer-events: none; transition-duration: var(--d-l);
}
body.fs .app:not(.is-start) .player.is-idle .player-top { translate: 0 calc(-10px * var(--m)); }
body.fs .app:not(.is-start) .player.is-idle .controls { translate: 0 calc(10px * var(--m)); }
body.fs .app:not(.is-start) .player.is-idle .stage { cursor: none; }
/* The safety warning drops in below the title row (--top-h: its height, from player.js), over the row's fade */
body.fs .app:not(.is-start) .warn { top: calc(var(--top-h, 56px) + 8px); z-index: 31; }
/* Narrow: the tabs sit in the title row as plain icons (names stay for screen readers), the bar is one row */
.player.tabs-top .bar { grid-template-areas: "time trans media"; }
.top-tools button {
  order: 0; display: grid; place-items: center; width: 44px; height: 44px; min-height: 0; max-width: none; padding: 0;
  border: 0; border-radius: 12px; background: transparent; box-shadow: none; color: var(--text);
}
@media (hover: hover) { .top-tools button:hover { background: var(--surface-2); } }
.top-tools button svg { width: 24px; height: 24px; }
.top-tools .safety-btn { color: var(--safety-ink); }
.top-tools .lbl, .top-tools .eps-btn > span { display: none; }
.top-tools #nameBtn > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.top-tools #nameBtn::before {
  width: 24px; height: 24px; border-radius: 0; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4.5 20.5a7.5 7.5 0 0 1 15 0'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4.5 20.5a7.5 7.5 0 0 1 15 0'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ---------- Interaction panel (covers the screen; the paused video shows through the scrim) ---------- */
.panel {
  position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center;
  --gap: clamp(24px, 3vw, 48px); /* blurred video shows through this gap on larger screens */
  padding: max(var(--gap), env(safe-area-inset-top)) max(var(--gap), env(safe-area-inset-right)) max(var(--gap), env(safe-area-inset-bottom)) max(var(--gap), env(safe-area-inset-left));
  background: var(--scrim-strong);
  color: var(--text); animation: fade var(--d-m) var(--e-out);
}
/* Behind the card: one blurred snapshot of the paused video (player.js). A live backdrop blur was redrawn on every
   frame of the activity and made games stutter on low-end devices. Without a snapshot the stronger tint shows. */
.panel.has-snap { background: none; }
.panel-snap { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.panel:not(.has-snap) .panel-snap { display: none; }
.panel-card {
  position: relative; width: min(100%, 1600px); height: 100%; display: flex; flex-direction: column;
  background: var(--surface); border: 2px solid var(--line); border-radius: clamp(20px, 3vw, 36px);
  box-shadow: var(--hi), var(--shadow-2); animation: pop var(--d-l) var(--spring) 40ms backwards;
}
/* Closing: player.js leaves a copy (.m-ghost) that fades while its card sinks; see UI.leave */
.panel-in {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  padding: clamp(18px, 3.2vw, 44px) clamp(16px, 3.6vw, 52px) clamp(14px, 2.6vw, 36px);
}
/* Card corner: settings + activity music */
.panel-tools { position: absolute; z-index: 2; top: clamp(14px, 2.4vw, 30px); right: clamp(14px, 2.4vw, 30px); display: flex; gap: 8px; }

.ix { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.ix-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0 0 clamp(12px, 1.6vw, 22px); }
/* Only the top row sits beside the card's corner buttons; the question below uses the full width */
.ix-meta { min-height: 44px; padding-right: 56px; }
.panel-card:has(.panel-audio:not([hidden])) .ix-meta { padding-right: 108px; }
/* Practice adds a close button to the corner */
.panel-card:has(.panel-x:not([hidden])) .ix-meta { padding-right: 108px; }
.panel-card:has(.panel-x:not([hidden])):has(.panel-audio:not([hidden])) .ix-meta { padding-right: 160px; }
.ix-att { font: 600 var(--fs-cap)/1.2 var(--f-ui); color: var(--muted); }
.ix-practice { font: 700 var(--fs-cap)/1.2 var(--f-ui); color: var(--info-ink); }   /* practice run: "Not counted" */
.ix-att:empty { display: none; }
.ix-q { margin: 0 0 .3em; font: 700 var(--fs-q)/1.25 var(--f-ui); outline: none; }
.ix-ins { margin: 0; color: var(--muted); font-size: var(--fs-sm); }
.ix-ins:empty { display: none; }
/* { hover, touch } instructions: mouse/trackpad screens see `hover`, touch screens see `touch` */
.on-touch { display: none; }
@media (hover: none) { .on-hover { display: none; } .on-touch { display: inline; } }

/* Temp image placeholder (item has no `img` yet). Uses theme tokens so it works in light and dark. */
.ph {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 10px; overflow: hidden;
  border: 3px dashed var(--line-strong); border-radius: 16px; background: var(--surface-2); color: var(--muted); text-align: center;
}
.ph svg { flex: none; width: clamp(34px, 3.4vw, 52px); height: auto; opacity: .85; }
.ph-t { font: 700 clamp(12px, 1.2vw, 15px)/1.2 var(--f-ui); color: var(--text); opacity: .8; max-width: 100%; overflow-wrap: break-word; }
.ph-s { font: 700 9px/1 var(--f-ui); letter-spacing: .16em; opacity: .7; }
.ph.compact { padding: 4px; gap: 0; }
.ph.compact svg { width: 60%; max-width: 44px; }

/* One picture beside the options (tf `media`) */
/* The picture takes the free height; options sit beside it (stacked under it on narrow cards) */
.ix-body > .ix-media { flex-grow: 1; }
.ix-media { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: clamp(12px, 1.6vw, 22px); align-items: center; }
.ix-media-pic { align-self: stretch; display: flex; min-height: 220px; }
.ix-media-pic > img, .ix-media-pic > .ph { flex: 1; width: 100%; min-height: 0; object-fit: contain; border-radius: 22px; background: var(--surface-2); }
.ix-media .opts { grid-template-columns: 1fr; }
.ix-media .opt { min-height: clamp(64px, 7vw, 96px); font-size: var(--fs-lead); }
@media (max-width: 700px) {
  .ix-media { grid-template-columns: 1fr; grid-template-rows: minmax(180px, 1fr) auto; align-items: stretch; }
  .ix-media-pic { min-height: 180px; }
}
.ix-body {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: auto;  padding: clamp(12px, 1.6vw, 22px) 4px 4px; margin: 0 -4px; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
}
/* The activity fills the card: short content is centred, tall content scrolls (auto margins collapse to 0 when it overflows) */
.ix-body > * { flex: 1 0 auto; margin-block: auto; }
.ix-body > .opts:not(.has-imgs) { flex-grow: 0; }
/* Text options in a large card: bigger pills */
.ix-body > .opts:not(.has-imgs) .opt { min-height: clamp(54px, 7vh, 96px); font-size: var(--fs-lead); }

.ix-foot {
  flex: none; display: flex; align-items: center; justify-content: flex-end; gap: clamp(12px, 2vw, 24px);
  margin-top: clamp(12px, 1.8vw, 24px); padding: clamp(10px, 1.2vw, 16px) clamp(10px, 1.2vw, 16px) clamp(10px, 1.2vw, 16px) clamp(14px, 1.8vw, 26px);
  border-radius: clamp(16px, 2vw, 30px); background: var(--bg); transition: background-color var(--d-m);
  position: relative; /* holds the leaving feedback copy */
}
.ix-foot.is-ok { background: var(--ok-tint); }
.ix-foot.is-bad { background: var(--try-tint); }
.ix-foot.is-done { background: var(--surface-2); }
.ix-fb { flex: 1; min-width: 0; display: none; align-items: center; gap: clamp(12px, 1.6vw, 20px); }
.ix-fb.open { display: flex; }
.ix-fb-icon { position: relative; flex: none; display: grid; place-items: center; width: clamp(40px, 4.2vw, 64px); aspect-ratio: 1; border-radius: 50%; }
.ix-fb-icon svg { width: 55%; height: 55%; stroke-width: 3.2; }
.ok .ix-fb-icon { background: var(--lime); color: var(--plum); }
.bad .ix-fb-icon { background: var(--coral); color: var(--plum); }
.done .ix-fb-icon { background: var(--grape); color: #fff; }
.ix-fb-txt { min-width: 0; max-height: 32vh; max-height: 32dvh; overflow-y: auto; }   /* a long explanation scrolls in the bar, the button stays */
.ix-fb-t { margin: 0; font: 400 var(--fs-h2)/1.05 var(--f-display); text-transform: uppercase; letter-spacing: .01em; }
.ok .ix-fb-t { color: var(--ok-ink); }
.bad .ix-fb-t { color: var(--try-ink); }
.done .ix-fb-t { color: var(--accent-text); }
.ix-fb-l { margin: .4em 0 0; font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .16em; text-transform: uppercase; color: var(--muted); }
.ix-fb-x { margin: .45em 0 0; font-size: var(--fs-sm); line-height: 1.5; }
.ix-fb-t:empty, .ix-fb-l:empty, .ix-fb-x:empty { display: none; }
.ix-act { flex: none; margin-left: auto; }
/* Activities with a Semak button (.ix-check, interactions.js): the bar has one height before and after the check, so the
   content above it never resizes. It fits the feedback (title, label and two lines of explanation; a longer explanation
   scrolls inside it). Before the check it holds the instruction beside the button. */
.ix-check .ix-foot {
  --fb-pad: clamp(10px, 1.2vw, 16px);
  --fb-text: calc(var(--fs-h2) * 1.05 + var(--fs-cap) * 1.6 + var(--fs-sm) * 3.45);   /* title + label + 2 lines */
  height: calc(var(--fb-text) + 2 * var(--fb-pad)); padding-block: var(--fb-pad);
}
.ix-check .ix-fb { align-self: stretch; min-height: 0; }
.ix-check .ix-fb-txt { max-height: 100%; }
.ix-foot > .ix-ins { flex: 1 1 auto; align-self: center; min-width: 0; }
.ix-foot:has(.ix-fb.open) > .ix-ins { display: none; }

/* Choice options (A, B, C ...) */
.opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); gap: clamp(8px, 1vw, 16px); }
.opt {
  position: relative; display: flex; align-items: center; gap: clamp(10px, 1.2vw, 16px);
  min-height: clamp(54px, 5.4vw, 72px); padding: 8px 50px 8px 8px; text-align: left;
  background: var(--opt-bg); color: var(--text); border: 3px solid var(--opt-line); border-radius: 36px;
  font: 600 var(--fs-body)/1.3 var(--f-ui);
  transition: border-color var(--d-s), background-color var(--d-s), transform var(--d-s) var(--spring), opacity var(--d-m);
}
.opt:active { transform: scale(calc(1 - .04 * var(--m))); transition-duration: var(--d-xs); }
/* Picture options: up to --cols per row (set from the option count), rows stretch to fill the card */
.opts.has-imgs {
  --cols: 4; --gap: clamp(8px, 1vw, 16px);
  grid-template-columns: repeat(auto-fit, minmax(max(180px, calc((100% - (var(--cols) - 1) * var(--gap)) / var(--cols))), 1fr));
  grid-auto-rows: minmax(150px, 1fr); gap: var(--gap);
}
.opt.has-img { flex-direction: column; align-items: stretch; gap: 10px; border-radius: 26px; padding: 10px; }
.opt.has-img > img, .opt.has-img > .ph { flex: 1 1 0; min-height: 72px; width: 100%; object-fit: contain; border-radius: 16px; background: var(--surface-2); }
.opt.has-img .opt-text { flex: none; display: flex; align-items: center; gap: clamp(10px, 1.2vw, 16px); }
.opt.has-img .opt-badge { top: 18px; margin-top: 0; right: 18px; }
@media (max-width: 600px) {
  .opts.has-imgs { --cols: 2; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: minmax(180px, 1fr); }
  .opt.has-img { gap: 8px; }
  .opt.has-img > img, .opt.has-img > .ph { min-height: 90px; }
  .opt.has-img .ph-s { display: none; }
}
/* Too narrow for two per row (small phones, or larger text on phones): one per row, the picture beside the label */
@media (max-width: 340px) {
  .opts.has-imgs { grid-template-columns: minmax(0, 1fr); grid-auto-rows: minmax(104px, auto); }
  .opt.has-img { flex-direction: row; align-items: center; }
  .opt.has-img > img, .opt.has-img > .ph { flex: 0 0 30%; align-self: stretch; min-height: 84px; }
  .opt.has-img .opt-text { flex: 1 1 0; min-width: 0; }
  .opt.has-img .opt-badge { top: 14px; left: 14px; right: auto; }   /* on the picture's corner */
}
@media (max-width: 400px) {
  :root:is([data-text="l"], [data-text="xl"]) .opts.has-imgs { grid-template-columns: minmax(0, 1fr); grid-auto-rows: minmax(104px, auto); }
  :root:is([data-text="l"], [data-text="xl"]) .opt.has-img { flex-direction: row; align-items: center; }
  :root:is([data-text="l"], [data-text="xl"]) .opt.has-img > img,
  :root:is([data-text="l"], [data-text="xl"]) .opt.has-img > .ph { flex: 0 0 30%; align-self: stretch; min-height: 84px; }
  :root:is([data-text="l"], [data-text="xl"]) .opt.has-img .opt-text { flex: 1 1 0; min-width: 0; }
  :root:is([data-text="l"], [data-text="xl"]) .opt.has-img .opt-badge { top: 14px; left: 14px; right: auto; }   /* on the picture's corner */
}

.opt-mark {
  flex: none; display: grid; place-items: center; width: clamp(36px, 3.4vw, 48px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--letter-text); font: 700 var(--fs-sm)/1 var(--f-ui);
  transition: background-color .15s, color .15s, border-radius .15s;
}
.opt-mark.box { border-radius: 30%; }
.opt-label { flex: 1; min-width: 0; overflow-wrap: anywhere; hyphens: auto; }   /* long single words break, never spill */
.opt-badge {
  position: absolute; right: 14px; top: 50%; width: 28px; height: 28px; margin-top: -14px; border-radius: 50%;
  display: grid; place-items: center; font: 700 15px/1 var(--f-ui);
}
.opt-badge:empty { display: none; }
@media (hover: hover) { .opts:not(.locked) .opt:hover { border-color: var(--lilac); } .opts:not(.locked) .opt:hover:not(:active) { transform: translateY(calc(-3px * var(--m))); } }
.opt.sel, .opts:not(.locked) .opt.sel:hover { border-color: var(--active-line); background: var(--active-bg); }
.opt.sel .opt-mark { background: var(--grape); color: #fff; }
.opt.ok { border-color: var(--ok-line); background: var(--ok-tint); }
.opt.ok .opt-mark, .opt.ok .opt-badge { background: var(--lime); color: var(--plum); }
.opt.bad { border-color: var(--try-line); background: var(--try-tint); animation: shake .42s; }
.opt.bad .opt-mark, .opt.bad .opt-badge { background: var(--coral); color: var(--plum); }
.opt.missed { border-color: var(--ok-line); border-style: dashed; }
.opt.missed .opt-mark, .opt.missed .opt-badge { background: var(--lime); color: var(--plum); }
.opts.locked .opt { cursor: default; }
.opts.locked .opt:active { transform: none; }
.opts.locked .opt:not(.ok):not(.bad):not(.missed) { opacity: .72; }   /* still readable (4.5:1) after a check */

/* Info list (scoreless) */
.info { display: grid; grid-template-columns: minmax(200px, 1fr) minmax(0, 1.5fr); grid-template-rows: 1fr auto; gap: clamp(12px, 1.6vw, 22px); align-items: start; }
.info-detail { align-self: stretch; }
.info-list { display: flex; flex-direction: column; gap: clamp(8px, .9vw, 12px); }
.info-item {
  position: relative; display: flex; align-items: center; gap: 12px; min-height: 58px; padding: 8px 44px 8px 8px; text-align: left;
  border: 3px solid var(--opt-line); border-radius: 30px; background: var(--opt-bg); color: var(--text);
  font: 700 var(--fs-body)/1.25 var(--f-ui);
  transition: border-color var(--d-s), background-color var(--d-s), transform var(--d-s) var(--spring);
}
.info-item:active { transform: scale(calc(1 - .04 * var(--m))); transition-duration: var(--d-xs); }
@media (hover: hover) { .info-item:hover { border-color: var(--lilac); } .info-item:hover:not(:active) { transform: translateY(calc(-3px * var(--m))); } }
.info-num {
  flex: none; display: grid; place-items: center; width: 38px; aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--accent-text); font: 400 18px/1 var(--f-display); text-transform: uppercase;
}
.info-item img { width: 40px; height: 40px; object-fit: cover; border-radius: 10px; }
.info-item.sel, .info-item.sel:hover { border-color: var(--active-line); background: var(--active-bg); }
.info-item.sel .info-num { background: var(--grape); color: #fff; }
.info-tick { position: absolute; right: 16px; color: var(--accent-text); font-weight: 700; opacity: 0; transition: opacity var(--d-s); }
.info-item.seen .info-tick { opacity: 1; }
.info-detail {
  min-height: 170px; padding: clamp(16px, 2vw, 28px);
  border: 2px solid var(--line); border-radius: clamp(18px, 2vw, 28px); background: var(--surface-2);
  font-size: var(--fs-body); line-height: 1.55;
}
.info-title { margin: 0 0 8px; font: 400 var(--fs-h2)/1.1 var(--f-display); text-transform: uppercase; }
.info-img { display: block; max-width: 100%; max-height: 220px; margin: 0 0 12px; border-radius: 14px; }
.info-detail p { margin: 0 0 8px; }
.info-count { grid-column: 1 / -1; margin: 0; font: 600 var(--fs-cap)/1.2 var(--f-ui); color: var(--muted); justify-self: start; transform-origin: left center; }

/* Explore (scoreless): picture tiles on the left, the description of the selected one on the right (under the grid when narrow).
   Uses the info pieces for the detail text, the count line and the tick; --cols tiles per row is set from the item count. */
.explore { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); grid-template-rows: 1fr auto; gap: clamp(12px, 1.6vw, 22px); align-items: start; }
.ex-grid { display: grid; grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr)); gap: clamp(8px, 1vw, 14px); }
.ex-tile {
  position: relative; min-width: 0; display: flex; flex-direction: column; align-items: stretch; gap: 8px; padding: 8px 8px 10px; text-align: center;
  border: 3px solid var(--opt-line); border-radius: clamp(18px, 2vw, 26px); background: var(--opt-bg); color: var(--text);
  font: 700 var(--fs-sm)/1.25 var(--f-ui);
  transition: border-color var(--d-s), background-color var(--d-s), transform var(--d-s) var(--spring);
}
.ex-tile:active { transform: scale(calc(1 - .04 * var(--m))); transition-duration: var(--d-xs); }
@media (hover: hover) { .ex-tile:hover { border-color: var(--lilac); } .ex-tile:hover:not(:active) { transform: translateY(calc(-3px * var(--m))); } }
.ex-tile.sel, .ex-tile.sel:hover { border-color: var(--active-line); background: var(--active-bg); }
.ex-tile.sel { animation: ix-pick var(--d-m) var(--e-out); }
.ex-pic { position: relative; display: flex; aspect-ratio: 3 / 2; }
.ex-pic > img, .ex-pic > .ph { flex: 1; width: 100%; min-height: 0; object-fit: contain; border-radius: clamp(12px, 1.4vw, 18px); background: var(--surface-2); }
.ex-label { min-width: 0; overflow-wrap: break-word; hyphens: auto; }
/* The tick sits on the picture's corner: not by colour alone, a ✓ once the item has been opened */
.ex-pic > .info-tick {
  top: -2px; right: -2px; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  background: var(--lime); color: var(--plum); font-size: 14px; line-height: 1;
}
.ex-tile.seen .info-tick { opacity: 1; animation: ix-badge var(--d-l) var(--spring-big); }
.ex-detail {
  align-self: stretch; min-height: 170px; padding: clamp(16px, 2vw, 28px);
  border: 2px solid var(--line); border-radius: clamp(18px, 2vw, 28px); background: var(--surface-2);
  font-size: var(--fs-body); line-height: 1.55;
}
.ex-detail > * { transform-origin: top center; animation: ix-swap var(--d-m) var(--e-out); }
.ex-detail p { margin: 0 0 8px; }
.ex-big { display: flex; height: clamp(140px, 30vh, 260px); margin: 0 0 12px; }
.ex-big > img, .ex-big > .ph { flex: 1; width: 100%; min-height: 0; object-fit: contain; border-radius: 16px; background: var(--surface); }
@media (max-width: 700px) {
  .explore { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .ex-grid { --cols: 2; }
  .ex-detail { min-height: 0; }
}
@media (max-height: 520px) and (orientation: landscape) {
  .ex-tile { padding: 6px 6px 8px; gap: 6px; }
  .ex-pic { aspect-ratio: 16 / 10; }
  .ex-big { height: 120px; }
  .ex-pic .ph-s { display: none; }
}

/* ---------- Shared activity pieces (style B: pills, letter circles, flat cards) ----------
   States used by every piece:
     hover = lilac line · is-picked = grape (selected) · is-over = drop target under the pointer
     is-ok = correct (green) · is-bad = wrong (red) · is-missed = answer shown (dashed green) · locked = .is-locked on a parent
   Pieces must not depend on their parent for looks: the drag ghost is a copy placed on <body>. */

/* Draggable label: a pill with a circle on the left (grip dots, a letter/number or a small picture) */
.token {
  position: relative; display: inline-flex; align-items: center; gap: 10px; max-width: 100%;
  min-height: clamp(48px, 4.6vw, 60px); padding: 6px 18px 6px 6px; text-align: left;
  background: var(--opt-bg); color: var(--text); border: 3px solid var(--opt-line); border-radius: 999px;
  font: 600 var(--fs-body)/1.25 var(--f-ui);
  touch-action: none; -webkit-user-select: none; user-select: none; cursor: grab;
  transition: border-color var(--d-s), background-color var(--d-s), opacity var(--d-s), transform var(--d-s) var(--spring);
}
.token-mark {
  flex: none; display: grid; place-items: center; width: clamp(34px, 3.2vw, 44px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--letter-text); font: 700 var(--fs-cap)/1 var(--f-ui); overflow: hidden;
}
.token-mark svg { width: 46%; height: auto; }
.token-mark img, .token-mark .ph { width: 100%; height: 100%; object-fit: cover; border: 0; border-radius: 0; padding: 0; }
.token-label { min-width: 0; overflow-wrap: break-word; hyphens: auto; }   /* a word longer than the line breaks with a hyphen where the browser can */
@media (hover: hover) { .token:hover:not(.is-picked, .is-ok, .is-bad, .is-missed) { border-color: var(--lilac); } }
.token:active { cursor: grabbing; }
.token.is-picked { border-color: var(--active-line); background: var(--active-bg); }
.token.is-picked .token-mark { background: var(--grape); color: #fff; }
.token.is-lifted { opacity: .35; border-style: dashed; }
.token.is-ok { border-color: var(--ok-line); background: var(--ok-tint); }
.token.is-ok .token-mark { background: var(--lime); color: var(--plum); }
.token.is-bad { border-color: var(--try-line); background: var(--try-tint); animation: shake .42s; }
.token.is-bad .token-mark { background: var(--coral); color: var(--plum); }
.token.is-missed { border-color: var(--ok-line); border-style: dashed; }
/* Not by colour alone: a ✓ / ✕ badge on the corner of a marked item (::before; ::after is the drop-target ring) */
.token.is-ok::before, .token.is-bad::before {
  content: "\2713"; position: absolute; z-index: 2; top: -9px; right: -7px; display: grid; place-items: center;
  width: 24px; height: 24px; border: 2px solid var(--opt-bg); border-radius: 50%;
  background: var(--lime); color: var(--plum); font: 700 14px/1 var(--f-ui); pointer-events: none;
  animation: m-in-pop var(--d-m) var(--spring) backwards;
}
.token.is-bad::before { content: "\2715"; background: var(--coral); color: var(--plum); }
.drag-ghost::before { content: none !important; }
.is-locked .token { cursor: default; pointer-events: none; } /* locked: no hover or presses, marks stay visible */
.is-locked .token:not(.is-ok):not(.is-bad):not(.is-missed) { opacity: .72; }

/* Drag ghost: the copy that follows the pointer */
.drag-ghost {
  position: fixed; left: 0; top: 0; z-index: 1000; margin: 0; pointer-events: none;
  transition: none; animation: none; /* the copy must not inherit the label's transitions, or it slides in from the corner */
  rotate: -2deg; scale: 1.04; border-color: var(--active-line); background: var(--active-bg);
  box-shadow: var(--shadow-2); will-change: translate, rotate;
}
.drag-ghost .token-mark { background: var(--grape); color: #fff; }
/* Gliding into place after a drop (drag.js FLIP): above its neighbours on the way */
.token.is-gliding, .dg-slot > .dg-part.is-gliding { z-index: 5; }

/* Bank: the tray that holds labels not yet placed. Also a drop zone (drop a label here to take it back). */
.bank {
  display: flex; flex-wrap: wrap; align-content: flex-start; gap: clamp(8px, .9vw, 12px);
  min-height: calc(clamp(48px, 4.6vw, 60px) + 2 * clamp(10px, 1.2vw, 16px) + 4px); padding: clamp(10px, 1.2vw, 16px);
  border: 2px solid var(--line); border-radius: clamp(22px, 2.4vw, 32px); background: var(--surface-2);
  transition: border-color .15s, background-color .15s;
}
.bank.is-over { border-color: var(--lilac); background: var(--active-bg); }
.bank-empty { margin: auto; color: var(--muted); font: 600 var(--fs-sm)/1.3 var(--f-ui); }
.bank:has(.token) .bank-empty { display: none; }

/* Drop box: dashed while empty, solid when filled. Holds one label (or several with .slot-many). */
.slot {
  position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px;
  min-height: calc(clamp(48px, 4.6vw, 60px) + 16px); padding: 5px;
  border: 3px dashed var(--line-strong); border-radius: clamp(22px, 2.4vw, 32px); background: var(--surface-2); color: var(--muted);
  font: 600 var(--fs-sm)/1.3 var(--f-ui); text-align: center;
  transition: border-color .15s, background-color .15s, transform var(--d-s) var(--spring);
}
/* Drop target invite: a lilac ring that springs out around the box under the pointer (opacity and transform only) */
.slot::after {
  content: ''; position: absolute; inset: -7px; border: 3px solid var(--lilac); border-radius: calc(clamp(22px, 2.4vw, 32px) + 7px);
  opacity: 0; transform: scale(calc(1 - .05 * var(--m))); pointer-events: none;
  transition: opacity var(--d-s) var(--e-out), transform var(--d-s) var(--spring);
}
.slot.is-over::after { opacity: .75; transform: none; }
.slot > .token { flex: 1 1 auto; justify-content: flex-start; }
.slot-hint { padding: 0 12px; }
.slot:has(.token) .slot-hint { display: none; }
.slot:has(.token) { border-style: solid; background: var(--opt-bg); }
.has-pick .slot, .is-dragging .slot { border-color: var(--lilac); }
.has-pick .slot { cursor: pointer; }
.slot.is-over { border-style: solid; border-color: var(--active-line); background: var(--active-bg); transform: scale(calc(1 + .03 * var(--m))); }
.slot.is-ok { border-style: solid; border-color: var(--ok-line); background: var(--ok-tint); }
.slot.is-bad { border-style: solid; border-color: var(--try-line); background: var(--try-tint); }
.slot.is-missed { border-color: var(--ok-line); }
.slot-answer { flex: 1 0 100%; color: var(--ok-ink); font: 700 var(--fs-cap)/1.3 var(--f-ui); }

/* Flip card (pairs): grape back with a "?", flat face with the content. The turn has no perspective: the card only narrows
   and widens again (with perspective its near half grows past the box and flashes a scroll bar under the bottom row). */
.flip { position: relative; aspect-ratio: 4 / 3; border: 0; padding: 0; background: none; color: var(--text); text-align: center; }
.flip-in { position: absolute; inset: 0; transform-style: preserve-3d; transition: transform .45s var(--pop); }
.flip.is-open .flip-in, .flip.is-ok .flip-in { transform: rotateY(180deg); }
.flip-back, .flip-face {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 10px;
  border-radius: clamp(18px, 2vw, 26px); backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.flip-back {
  background: linear-gradient(180deg, var(--grape-hi), var(--grape)); color: #fff; border: 3px solid var(--grape-deep);
  background-image: radial-gradient(circle at 20% 25%, rgba(199, 125, 255, .55) 0 10%, transparent 11%), radial-gradient(circle at 80% 78%, rgba(199, 125, 255, .4) 0 14%, transparent 15%);
  font: 400 clamp(30px, 4vw, 52px)/1 var(--f-display); text-transform: uppercase;
}
.flip-face { transform: rotateY(180deg); background: var(--opt-bg); border: 3px solid var(--opt-line); font: 600 var(--fs-sm)/1.3 var(--f-ui); }
.flip-face img, .flip-face .ph { flex: 1 1 0; min-height: 0; width: 100%; object-fit: contain; border-radius: 12px; }
@media (hover: hover) { .flip:not(.is-open):not(.is-ok):hover .flip-back { border-color: var(--lilac); } }
.flip.is-open .flip-face { border-color: var(--active-line); background: var(--active-bg); }
.flip.is-ok .flip-face { border-color: var(--ok-line); background: var(--ok-tint); }
.flip.is-bad .flip-face { border-color: var(--try-line); background: var(--try-tint); }
.flip.is-bad { animation: shake .42s; }

/* Grid cells (word search, crossword, maze). Set --cols on .cells. */
.cells { display: grid; grid-template-columns: repeat(var(--cols, 10), minmax(0, 1fr)); gap: clamp(3px, .45vw, 6px); touch-action: none; -webkit-user-select: none; user-select: none; }
.cell {
  position: relative; display: grid; place-items: center; aspect-ratio: 1; min-width: 0; padding: 0;
  border: 2px solid var(--opt-line); border-radius: clamp(6px, .8vw, 12px); background: var(--opt-bg); color: var(--text);
  font: 700 clamp(13px, 1.9vw, 24px)/1 var(--f-ui); text-transform: uppercase;
  transition: background-color .12s, border-color .12s, color .12s;
}
.cell-num { position: absolute; top: 2px; left: 4px; font: 700 clamp(8px, .7vw, 11px)/1 var(--f-ui); color: var(--muted); }
.cell.is-void { visibility: hidden; }
.cell.is-wall { background: var(--grape-deep); border-color: var(--grape-deep); }
.cell.is-hl { background: var(--surface-2); border-color: var(--surface-3); }
.cell.is-picked { background: var(--lilac); border-color: var(--grape); color: var(--plum); }
.cell.is-active { border-color: var(--active-line); background: var(--active-bg); box-shadow: inset 0 0 0 2px var(--active-line); }
.cell.is-ok { background: var(--lime); border-color: var(--ok-line); color: var(--plum); }
.cell.is-bad { background: var(--try-tint); border-color: var(--try-line); color: var(--try-ink); }
.cell.is-missed { border-color: var(--ok-line); border-style: dashed; color: var(--ok-ink); }

/* Connecting lines (connect, diagram): an SVG layer over the items; ports are the dots lines start from */
.links { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.link { fill: none; stroke: var(--progress); stroke-width: 4; stroke-linecap: round; }
.link.is-pending { stroke: var(--lilac); stroke-dasharray: 2 10; }
.link.is-ok { stroke: var(--ok-line); }
.link.is-bad { stroke: var(--try-line); }
.link.is-missed { stroke: var(--ok-line); stroke-dasharray: 10 8; }
.port {
  flex: none; width: 20px; height: 20px; border-radius: 50%; border: 3px solid var(--line-strong); background: var(--opt-bg);
  transition: background-color .15s, border-color .15s;
}
.is-linked > .port, .port.is-linked { border-color: var(--progress); background: var(--progress); }
.is-picked > .port { border-color: var(--progress); background: var(--lilac); }

/* Game header: lives, score and a time bar */
.hud { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.hud-item {
  display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 6px 14px;
  border: 2px solid var(--line); border-radius: 999px; background: var(--surface);
  font: 700 var(--fs-cap)/1 var(--f-ui);
}
.hud-item small { font-size: 1em; font-weight: 600; color: var(--muted); }
.hud-lives { gap: 4px; }
.hud-life { width: 22px; height: 22px; color: var(--grape); transition: opacity .2s, transform .2s; }
[data-theme="dark"] .hud-life { color: var(--lilac); }
.hud-life.is-lost { opacity: .25; transform: scale(.8); }
.hud-bar { flex: 1 1 160px; height: 12px; border-radius: 999px; background: var(--progress-track); overflow: hidden; }
.hud-bar > i { display: block; height: 100%; width: calc(var(--p, 1) * 100%); border-radius: inherit; background: var(--lilac); transition: width .2s linear; }

/* Start / end screen inside a game area */
.game-ov {
  position: absolute; inset: 0; z-index: 5; display: grid; place-items: center; padding: 16px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-radius: inherit; animation: fade .2s ease-out;
}
.game-ov-card {
  width: min(100%, 460px); display: flex; flex-direction: column; align-items: center; gap: 12px; padding: clamp(18px, 2.4vw, 32px);
  border: 2px solid var(--line); border-radius: clamp(20px, 2.4vw, 30px); background: var(--surface); text-align: center;
  box-shadow: var(--hi), var(--shadow-2); animation: pop .38s var(--pop);
}
.game-ov-title { margin: 0; font: 400 var(--fs-h2)/1.05 var(--f-display); text-transform: uppercase; color: var(--accent-text); }
.game-ov-text { margin: 0; font-size: var(--fs-sm); line-height: 1.5; }
div.game-ov-text { display: flex; flex-direction: column; gap: .5em; }
.game-ov-text > p { margin: 0; }
.game-ov.is-ok .game-ov-title { color: var(--ok-ink); }
.game-ov.is-bad .game-ov-title { color: var(--try-ink); }
/* A card taller than the game area scrolls; its button stays in sight at the bottom (a surface ring hides the text under it) */
/* Sticky over the card's own text, so it keeps a ring of card colour around it */
.game-ov-card > .btn { position: sticky; bottom: 6px; z-index: 1; flex: none; box-shadow: inset 0 -3px 0 var(--amber-edge), inset 0 1px 0 rgba(255, 255, 255, .45), 0 0 0 6px var(--surface); }
.game-ov-card > .btn:active:not(:disabled) { box-shadow: inset 0 -1px 0 var(--amber-edge), 0 0 0 6px var(--surface); }
/* Short screens: a compact card */
@media (max-height: 520px) and (orientation: landscape) {
  .game-ov { padding: 6px; }
  .game-ov-card { width: min(100%, 680px); gap: 6px; padding: 10px 18px; }   /* wider: fewer lines */
  .game-ov-card > .btn { min-height: 44px; padding-block: .45em; }
  .game-ov-title { font-size: calc(1.375rem * var(--tsh)); }
  .game-ov-text { font-size: var(--fs-cap); line-height: 1.35; }
}

/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .flip-in { transition-duration: .01s; }
:root[data-motion="reduce"] .token.is-bad, :root[data-motion="reduce"] .flip.is-bad { animation: none; }

/* ---------- Activity motion: choices, info, feedback and the generic piece states ----------
   Entrances are played from interactions.js. Pieces sit at the edges of the scrolling .ix-body, so they hop up or squash
   (barely wider, never lower by more than its 4px padding): growing would flash a scroll bar. A piece that stretches is
   pinned at its bottom edge, so a tall one (a picture card) grows upwards, where nothing scrolls. */
@keyframes ix-pick { 40% { transform: translateY(calc(-5px * var(--m))); } }
@keyframes ix-hop { 30% { transform: translateY(calc(-6px * var(--m))); } 60% { transform: translateY(calc(1px * var(--m))); } }
@keyframes ix-mark { from { transform: scale(calc(1 - .45 * var(--m))); } }
@keyframes ix-badge { from { opacity: 0; transform: scale(calc(1 - .9 * var(--m))) rotate(calc(-40deg * var(--m))); } }
@keyframes ix-jelly {
  25% { transform: scale(calc(1 + .008 * var(--m)), calc(1 - .09 * var(--m))); }
  45% { transform: scale(calc(1 - .004 * var(--m)), calc(1 + .035 * var(--m))); }
  65% { transform: scale(1, calc(1 - .015 * var(--m))); }
}
@keyframes ix-swap { from { opacity: 0; transform: scale(calc(1 - .03 * var(--m))); } }
@keyframes ix-fb-in { from { opacity: 0; transform: translateY(calc(14px * var(--m))); } }
@keyframes ix-fb-t { from { opacity: 0; transform: translateY(calc(6px * var(--m))) scale(calc(1 - .12 * var(--m))); } }
@keyframes ix-icon-in { from { opacity: 0; transform: scale(calc(1 - .8 * var(--m))) rotate(calc(-35deg * var(--m))); } }
/* Select: a quick springy nudge, the letter circle pops */
.opt.sel, .info-item.sel, .token.is-picked { animation: ix-pick var(--d-m) var(--e-out); }
.opt.sel .opt-mark, .info-item.sel .info-num, .token.is-picked .token-mark { animation: ix-mark var(--d-l) var(--spring-big); }
/* Result: correct = jelly + a ring from the tick, wrong = shake, answer shown = a gentle pulse after the shakes */
.opt.ok, .token.is-ok, .flip.is-ok { transform-origin: bottom center; animation: ix-jelly calc(var(--d-l) + 140ms) ease-out; }
.opt.bad, .token.is-bad, .flip.is-bad { animation: shake .42s; }
/* A full-width piece shaking sideways sticks out of the activity area for a moment: no sideways scrollbar meanwhile */
.ix-body:has(.opt.bad, .token.is-bad, .flip.is-bad, .cell.is-bad) { animation: ix-hold-x .5s linear; }
@keyframes ix-hold-x { from, to { overflow-x: hidden; } }
.opt.missed, .token.is-missed { animation: ix-hop var(--d-l) var(--e-io) var(--d-m); }
.opt-badge:not(:empty) { animation: ix-badge var(--d-l) var(--spring-big) backwards; }
.opt.missed .opt-badge { animation-delay: var(--d-m); }
.opt.ok .opt-badge::after, .ix-fb.ok .ix-fb-icon::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--lime); pointer-events: none;
  opacity: 0; animation: m-ring var(--d-xl) var(--e-out) var(--d-xs);
}
/* Info: the seen tick pops, the detail swaps in */
.info-item.seen .info-tick { animation: ix-badge var(--d-l) var(--spring-big); }
.info-detail > * { transform-origin: top center; animation: ix-swap var(--d-m) var(--e-out); }
/* Feedback bar: slides up, the icon bounces in (a wrong one also wiggles), the title pops */
.ix-fb.open { animation: ix-fb-in var(--d-m) var(--e-out); }
.ix-fb.open .ix-fb-icon { animation: ix-icon-in var(--d-l) var(--spring-big) backwards; }
.ix-fb.open.bad .ix-fb-icon { animation: ix-icon-in var(--d-l) var(--spring-big) backwards, m-wiggle 520ms ease-in-out var(--d-l); }
.ix-fb.open .ix-fb-t { transform-origin: left center; animation: ix-fb-t var(--d-l) var(--spring) 60ms backwards; }
.ix-fb.open .ix-fb-l, .ix-fb.open .ix-fb-x { animation: ix-fb-in var(--d-m) var(--e-out) 120ms backwards; }
/* While those lines move in, they stick out of the text box, which scrolls (a long explanation): without this hold
   it flashes scrollbars for a moment. The box clips until the entrance is over, then scrolls again. */
.ix-fb.open .ix-fb-txt { animation: ix-fb-hold calc(var(--d-l) + 120ms) linear; }
@keyframes ix-fb-hold { from, to { overflow: hidden; } }
/* The bar that leaves on Try again is a copy (interactions.js) */
.ix-fb-ghost { position: absolute; display: flex; align-items: center; gap: clamp(12px, 1.6vw, 20px); pointer-events: none; }
.ix-fb-ghost, .ix-fb-ghost * { animation: none !important; }
.ix-fb-ghost .ix-fb-icon::after { content: none; }
.ix-att { transform-origin: left center; }
/* Hover lift and press squash for tokens and flip cards (dragging and dropping are styled with drag.js) */
@media (hover: hover) {
  .token:hover:not(.is-picked, .is-ok, .is-bad, .is-missed, .is-lifted, :active) { transform: translateY(calc(-2px * var(--m))); }
  .flip:not(.is-open, .is-ok):hover:not(:active) { transform: translateY(calc(-3px * var(--m))); }
}
.token:active:not(.is-lifted, .is-fixed) { transform: scale(calc(1 - .04 * var(--m))); transition-duration: var(--d-xs); }
.flip { transition: transform var(--d-s) var(--spring); }
.flip:not(.is-open, .is-ok):active { transform: scale(calc(1 - .04 * var(--m))); transition-duration: var(--d-xs); }

/* ---------- Activity types ---------- */
/* ---------- match ---------- */
/* Bank of labels on top, target cards below filling the rest of the card (--cols cards per row, set from the target count).
   Phones: labels two per row. Short landscape screens: bank on the left, cards on the right (both scroll with .ix-body,
   so dragging near the top/bottom edge auto-scrolls). */
.match { position: relative; display: flex; flex-direction: column; gap: clamp(10px, 1.2vw, 18px); }
.match > .bank { flex: none; }
.match > .bank:not(:has(.token)) { align-content: center; }
.match-targets {
  --gap: clamp(8px, 1vw, 16px);
  flex: 1 0 auto; display: grid; gap: var(--gap); grid-auto-rows: minmax(min-content, 1fr);
  grid-template-columns: repeat(auto-fit, minmax(max(min(100%, 250px), calc((100% - (var(--cols, 2) - 1) * var(--gap)) / var(--cols, 2))), 1fr));
}
.match-target {
  min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: clamp(6px, .7vw, 10px);
  padding: clamp(10px, 1vw, 14px); border: 2px solid var(--line); border-radius: clamp(22px, 2.4vw, 32px); background: var(--surface);
}
.match-prompt { margin: 0; padding: 0 6px; font: 700 var(--fs-body)/1.3 var(--f-ui); color: var(--text); overflow-wrap: break-word; }
.match-target > .slot { flex: none; }
.match-target > .slot > .token { width: 100%; }

/* With pictures: picture on top of each card (takes the free height), a larger circle picture on the labels */
.match.has-pics .match-targets { grid-template-columns: repeat(auto-fit, minmax(max(min(100%, 200px), calc((100% - (var(--cols) - 1) * var(--gap)) / var(--cols))), 1fr)); }
.match.has-pics .match-target { justify-content: flex-start; }
.match.has-pics .match-prompt { text-align: center; }
.match-pic { flex: 1 1 0; min-height: clamp(72px, 11vh, 160px); display: flex; }
.match-pic > img, .match-pic > .ph { flex: 1; width: 100%; min-height: 0; object-fit: contain; border-radius: 16px; background: var(--surface-2); }
.token.has-pic .token-mark { width: clamp(40px, 3.8vw, 52px); }

/* Long words (match.js measures a hidden copy of the bank with .is-measure): .is-fit1 = smaller label text,
   .is-fit2 = one label per row on phones */
.bank.is-measure .token-label { overflow-wrap: normal; }
.match.is-fit1 .token { font-size: var(--fs-cap); }
.match.is-fit2 > .bank { grid-template-columns: minmax(0, 1fr); }
/* Narrow cards: the label in the box drops its circle (the card shows the picture) and uses the smaller text */
.match-target { container-type: inline-size; }
@container (max-width: 220px) {
  .match-target > .slot > .token { gap: 0; padding: 6px 12px; font-size: var(--fs-cap); }
  .match-target > .slot > .token .token-mark { display: none; }
}

/* Labels kept in place after Try again (correct): green, not draggable */
.token.is-fixed, .token.is-fixed:active { cursor: default; }

@media (max-width: 600px) {
  .match > .bank { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
  .match > .bank > .token { padding-right: 12px; }
  .match > .bank > .bank-empty { grid-column: 1 / -1; }
  .match.has-pics .match-targets { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .match.has-pics .match-target { padding: 8px; }
  .match-pic { min-height: 84px; }
  .match-pic .ph-s { display: none; }
  .match.has-pics .match-target > .slot > .token { padding-right: 10px; }
}
@media (max-height: 520px) and (orientation: landscape) {
  .match { display: grid; grid-template-columns: minmax(200px, 30%) minmax(0, 1fr); align-items: start; gap: 12px; }
  /* Side by side the boxes do not move when the bank empties, so the bank need not keep its height (set inline by match.js) */
  .match > .bank { flex-direction: column; flex-wrap: nowrap; align-items: stretch; min-height: 0 !important; }
  .match > .bank > .token { width: 100%; min-height: 46px; }
  .match.is-fit2 { grid-template-columns: minmax(200px, 40%) minmax(0, 1fr); }   /* long words: a wider bank */
  .match-targets { grid-template-columns: repeat(auto-fit, minmax(max(min(100%, 200px), calc((100% - var(--gap)) / 2)), 1fr)); grid-auto-rows: auto; }
  .match.has-pics .match-targets { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); }
  .match-target { padding: 8px 10px; gap: 6px; }
  .match-pic { min-height: 70px; }
  .match-pic .ph-s { display: none; }
}

/* ---------- label ---------- */
/* Names in a bank; boxes on the picture at their `at` point, each with a leader line to its part. label.js picks the
   layout from the free space (data-layout): side = bank beside the picture, top = bank above it, list = numbered pins on
   the picture and the boxes listed under it. --fig-w = the picture's width, --ratio = its width / height.
   The picture is the same in both themes (a dark diagram), so its lines, dots and pin rings are plain white. */
.label { --gap: clamp(10px, 1.2vw, 18px); min-width: 0; display: grid; gap: var(--gap); align-content: center; justify-content: center; }
.label[data-layout="side"] { grid-template-columns: var(--bank-w) var(--fig-w); align-items: center; }
.label > .bank:not(:has(.token)) { align-content: center; }
/* Beside the picture the names wrap in a column as wide as the picture leaves; smaller pills, so they fit its height */
.label[data-layout="side"] > .bank { gap: 8px; padding: 10px; }
.label[data-layout="side"] > .bank > .token { min-height: 44px; gap: 8px; padding: 4px 14px 4px 4px; font-size: var(--fs-sm); }
.label[data-layout="side"] > .bank > .token .token-mark { width: 32px; }
.label[data-layout="top"] { grid-template-columns: minmax(0, 1fr); justify-content: stretch; }
.label[data-layout="top"] .lbl-fig { width: var(--fig-w); justify-self: center; }
.lbl-fig { position: relative; min-width: 0; }
.lbl-pic { position: relative; aspect-ratio: var(--ratio, 16 / 9); overflow: hidden; border-radius: clamp(14px, 1.6vw, 22px); background: var(--surface-2); }
.lbl-pic > img { display: block; width: 100%; height: 100%; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
.lbl-lines { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.lbl-lines line { stroke: #fff; stroke-width: 2; stroke-linecap: round; opacity: .9; }
.lbl-dot { position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 2px rgba(0, 0, 0, .35); pointer-events: none; }
.lbl-pin, .lbl-num { display: none; }

/* Boxes on the picture: centred on --x / --y (translate, so the slot's own hover scale still works), the name without its
   circle and in the smaller text */
.label:not([data-layout="list"]) .lbl-slots { position: absolute; inset: 0; pointer-events: none; }
.label:not([data-layout="list"]) .lbl-slots > .slot {
  position: absolute; left: var(--x); top: var(--y); width: max(var(--w, 20%), 112px); translate: -50% -50%;
  min-height: 52px; padding: 3px; pointer-events: auto; box-shadow: var(--shadow-1);
}
/* The marked colours are see-through: an opaque backing (inside the box's own stacking context) hides the line under it */
.label:not([data-layout="list"]) .lbl-slots > .slot::before { content: ''; position: absolute; z-index: -1; inset: -3px; border-radius: inherit; background: var(--surface); }
.label:not([data-layout="list"]) .slot-hint { padding: 0 6px; text-wrap: balance; }
.label .slot:has(.slot-answer) .slot-hint { display: none; }   /* an empty box with its answer shown */
.label:not([data-layout="list"]) .slot > .token { width: 100%; min-height: 44px; gap: 0; padding: 4px 12px; justify-content: center; text-align: center; font-size: var(--fs-sm); }
.label:not([data-layout="list"]) .slot > .token .token-mark { display: none; }

/* List: each box is a numbered pin on the picture, and a numbered row under it */
.label[data-layout="list"] { grid-template-columns: minmax(0, 1fr); justify-content: stretch; }
.label[data-layout="list"] .lbl-fig { display: flex; flex-direction: column; gap: var(--gap); }
/* The picture stays in view while the boxes scroll under it, so the pin numbers can be read while placing names */
.label[data-layout="list"] .lbl-pic { position: sticky; top: 0; z-index: 6; }
.label[data-layout="list"] .lbl-pin {
  position: absolute; display: grid; place-items: center; width: 30px; height: 30px; margin: -15px 0 0 -15px; border-radius: 50%;
  border: 2px solid #fff; background: var(--grape); color: #fff; font: 700 14px/1 var(--f-ui); pointer-events: none;
}
.lbl-slots { min-width: 0; }
.label[data-layout="list"] .lbl-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr)); gap: clamp(8px, 1vw, 12px); }
.label[data-layout="list"] .slot { justify-content: flex-start; text-align: left; }
.label[data-layout="list"] .lbl-num {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px; margin-left: 4px; border-radius: 50%;
  background: var(--grape); color: #fff; font: 700 var(--fs-cap)/1 var(--f-ui);
}
.label[data-layout="list"] .slot > .token { flex: 1 1 0; min-width: 0; }
.label[data-layout="list"] .slot-answer { padding-left: 46px; }
@media (max-width: 600px) {
  .label[data-layout="list"] > .bank { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
  .label[data-layout="list"] > .bank > .token { padding-right: 12px; }
  .label[data-layout="list"] > .bank > .bank-empty { grid-column: 1 / -1; }
}
/* Short landscape screens: the picture on the left (stays in view while the right side scrolls), bank and boxes on the right */
@media (max-height: 520px) and (orientation: landscape) {
  .label[data-layout="list"] { grid-template-columns: min(50%, var(--fit-w, 50%)) minmax(0, 1fr); grid-template-areas: "pic bank" "pic slots"; align-items: start; align-content: start; }
  .label[data-layout="list"] .lbl-fig { display: contents; }
  .label[data-layout="list"] .lbl-pic { grid-area: pic; grid-row: 1 / span 2; }
  .label[data-layout="list"] > .bank { grid-area: bank; min-height: 0; }
  .label[data-layout="list"] .lbl-slots { grid-area: slots; grid-template-columns: minmax(0, 1fr); }
}

/* ---------- order ---------- */
/* Steps bank beside a numbered list of positions (stacked on narrow cards). Uses .token, .bank and .slot. */
.order {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); align-items: start;
  gap: clamp(14px, 2vw, 32px);
}
/* Row height follows the screen height too, so 4-5 steps fit a laptop card without scrolling.
   Set on the token's own class (not via .order) so the drag ghost on <body> looks the same. */
.order, .order-tok { --row: clamp(44px, min(4.6vw, 6vh), 60px); }
.order-tok { min-height: var(--row); padding-block: 4px; }
.order-tok .token-mark { width: calc(var(--row) - 14px); }
.order .slot { min-height: calc(var(--row) + 8px); padding: 1px; }
.order-col { display: flex; flex-direction: column; gap: clamp(6px, .8vw, 10px); min-width: 0; }
.order-h {
  margin: 0; padding-left: 4px; color: var(--muted);
  font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .14em; text-transform: uppercase;
}
/* Steps are sentences: one per row, full width */
.order-bank { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
.order-bank > .token { width: 100%; }
.order-list { display: flex; flex-direction: column; gap: clamp(8px, .8vw, 12px); margin: 0; padding: 0; list-style: none; }
.order-pos { display: flex; align-items: center; gap: clamp(10px, 1.2vw, 16px); }
.order-num {
  flex: none; display: grid; place-items: center; width: clamp(40px, 4vw, 56px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--accent-text); font: 400 clamp(19px, 2vw, 26px)/1 var(--f-display); text-transform: uppercase;
  transition: background-color .15s, color .15s;
}
.order-pos.is-filled .order-num { background: var(--grape); color: #fff; }
.order-pos.is-ok .order-num { background: var(--lime); color: var(--plum); }
.order-pos.is-bad .order-num { background: var(--coral); color: var(--plum); }
.order-slot { flex: 1 1 auto; min-width: 0; justify-content: flex-start; cursor: default; }
.order-slot .slot-hint { padding: 0 14px; }
.order-slot .slot-answer { padding: 2px 14px 4px; text-align: left; }
/* Locked: no presses on the positions either (the shared rule covers the steps) */
.order.is-locked .slot { pointer-events: none; }

/* Narrow cards: bank on top, positions below */
@media (max-width: 760px) {
  .order { grid-template-columns: minmax(0, 1fr); align-content: start; }
}
/* Short landscape phones: two columns, tighter rows */
@media (max-height: 520px) and (orientation: landscape) {
  .order { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: 14px; }
  .order, .order-tok { --row: 44px; }
  .order-num { width: 36px; font-size: 18px; }
  .order-h { display: none; }
}

/* ---------- sentence ---------- */
/* Word token: a compact pill without the circle, so the words read like a sentence (look does not depend on the parent) */
.token.is-word {
  gap: 0; justify-content: center; min-height: clamp(46px, 4.4vw, 64px); padding: 6px clamp(14px, 1.4vw, 22px);
  font-size: var(--fs-lead);
}

/* The sentence and the bank share the free height of the card (2 : 1) */
.sentence { display: flex; flex-direction: column; gap: clamp(12px, 1.6vw, 20px); }
.sentence-line {
  position: relative; flex: 2 0 auto; display: flex; flex-wrap: wrap; align-items: center; align-content: center; justify-content: center;
  gap: clamp(8px, .9vw, 12px); min-height: clamp(110px, 18vh, 240px); padding: clamp(14px, 1.8vw, 26px);
  border: 3px solid var(--line); border-radius: clamp(22px, 2.4vw, 32px); background: var(--surface);
  transition: border-color .15s, background-color .15s;
}
.sentence-line.is-empty { border-style: dashed; background: var(--bg); }
.is-dragging .sentence-line { border-color: var(--lilac); }
.sentence-line.is-over { border-style: solid; border-color: var(--active-line); background: var(--active-bg); }
.sentence-line.is-ok { border-color: var(--ok-line); background: var(--ok-tint); }
.sentence-line.is-bad { border-color: var(--try-line); }
.sentence-ph { display: none; margin: 0; color: var(--muted); font: 600 var(--fs-sm)/1.3 var(--f-ui); text-align: center; }
.sentence-line.is-empty .sentence-ph { display: block; }

/* Insertion caret while dragging over the sentence */
.sentence-caret {
  position: absolute; left: 0; top: 0; display: none; width: 4px; margin-top: -8px; padding-block: 8px; box-sizing: content-box;
  border-radius: 2px; background: var(--grape); pointer-events: none;
}
[data-theme="dark"] .sentence-caret { background: var(--lilac); }
.sentence-caret.is-on { display: block; }

.sentence-answer { flex: none; margin: 0; text-align: center; font-size: var(--fs-body); }
.sentence-bank { flex: 1 0 auto; justify-content: center; align-content: center; }
.sentence-sr { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Phones: less empty space above the words */
@media (max-width: 600px), (max-height: 520px) and (orientation: landscape) {
  .sentence-line { min-height: 96px; }
  .token.is-word { min-height: 44px; padding: 4px 14px; font-size: var(--fs-body); }
}
@media (max-height: 520px) and (orientation: landscape) {
  .sentence { gap: 10px; }
  .sentence-line { min-height: 72px; padding: 10px; }
}

/* ---------- connect ----------
   Two columns of .token pills with a .port dot on the inner side; lines (.links) run between the dots
   through the gap column. Hit lines (.cx-hits) sit under the items so a tap on the gap removes a link.
   All rows are as tall as the tallest one, so short and long labels line up. */
.cx { container-type: inline-size; display: flex; flex-direction: column; }
/* The grid fills the free height (equal rows), up to --cx-max per row; then it is centred */
.cx-grid {
  --cx-h: clamp(48px, 7vh, 88px);           /* smallest row */
  --cx-max: clamp(64px, 12vh, 128px);       /* tallest row when there is room */
  --cx-gap: clamp(8px, 1.6vh, 22px);
  --cx-pic: clamp(40px, 7vh, 88px);
  position: relative; flex: 1 0 auto; width: 100%; max-width: 1400px; margin: auto;
  max-height: calc(var(--rows, 5) * var(--cx-max) + (var(--rows, 5) - 1) * var(--cx-gap));
  display: grid; grid-template-columns: minmax(0, 1fr) clamp(30px, 16cqi - 30px, 220px) minmax(0, 1fr);
  grid-auto-rows: minmax(min-content, 1fr); row-gap: var(--cx-gap);   /* min-content: a long label is never cut by the cap */
}
.cx-item {
  z-index: 1; display: flex; width: 100%; min-height: var(--cx-h); gap: 12px;
  padding: 8px 14px 8px 20px; border-radius: clamp(24px, 2.4vw, 32px);
  font-size: var(--fs-body); cursor: pointer;
}
.cx-item[data-side="R"] { padding: 8px 20px 8px 14px; }
.cx-label { flex: 1 1 auto; hyphens: auto; }
.cx-item .port { width: 18px; height: 18px; }
.cx-pic { flex: none; align-self: stretch; min-width: var(--cx-pic); aspect-ratio: 1; max-height: 120px; border-radius: 16px; overflow: hidden; background: var(--surface-2); }
.cx-pic > img, .cx-pic > .ph { display: flex; width: 100%; height: 100%; object-fit: cover; border-width: 0; border-radius: 0; }

/* Lines: visible ones over the items, hit lines under them */
.cx-lines { z-index: 2; }
.cx-hits { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.cx-hit { fill: none; stroke: transparent; stroke-width: 26; pointer-events: stroke; cursor: pointer; }
.link.is-hot { stroke: var(--lilac); stroke-width: 6; }
[data-theme="dark"] .link.is-hot { stroke: var(--text); }

/* The item a line is being dragged from looks picked (not faded like a lifted label) */
.cx-item.is-lifted { opacity: 1; border-style: solid; border-color: var(--active-line); background: var(--active-bg); }
.cx-item.is-lifted > .port { border-color: var(--grape); background: var(--lilac); }
/* While an item is picked or dragged, the other side's items show they can take the line */
.cx-grid.from-L .cx-item[data-side="R"], .cx-grid.from-R .cx-item[data-side="L"] { border-color: var(--lilac); }
.cx-grid.from-L .cx-item[data-side="R"] > .port, .cx-grid.from-R .cx-item[data-side="L"] > .port { border-color: var(--lilac); }
.cx-item.is-target, .cx-grid .cx-item.is-target { border-color: var(--active-line); background: var(--active-bg); }
.cx-item.is-target > .port { border-color: var(--grape); background: var(--lilac); }
/* Target invite: a ring springs out (a pseudo-element, so the dots the lines are measured from do not move) */
.cx-item::after, .dg-slot > .dg-part::after {
  content: ''; position: absolute; inset: -7px; border: 3px solid var(--lilac); border-radius: inherit;
  opacity: 0; transform: scale(calc(1 - .05 * var(--m))); pointer-events: none;
  transition: opacity var(--d-s) var(--e-out), transform var(--d-s) var(--spring);
}
.cx-item.is-target::after, .dg-slot > .dg-part.is-over::after { opacity: .75; transform: none; }
/* Items hold the line ends, so they stay still (no hover lift / press squash / pick nudge); the dot reacts instead
   (it scales about its own centre, which is where the line ends) */
.cx-grid .cx-item:hover, .cx-grid .cx-item:active { transform: none; }
.cx-grid .cx-item.is-picked { animation: none; }
.cx-item > .port { transition: background-color .15s, border-color .15s, transform var(--d-s) var(--spring); }
@media (hover: hover) { .cx:not(.is-locked) .cx-item:hover > .port { transform: scale(calc(1 + .25 * var(--m))); } }
.cx-item.is-picked > .port, .cx-item.is-lifted > .port { animation: cx-port var(--d-l) var(--spring-big); }
.cx-item.is-target > .port { transform: scale(calc(1 + .3 * var(--m))); }
@keyframes cx-port { from { transform: scale(calc(1 + .8 * var(--m))); } }
/* Dots follow the result */
.cx-item.is-ok > .port { border-color: var(--ok-line); background: var(--ok-line); }
.cx-item.is-bad > .port { border-color: var(--try-line); background: var(--coral); }
.cx-item.is-missed > .port { border-color: var(--ok-line); background: var(--surface); }
.cx.is-locked .cx-item { cursor: default; }
.cx.is-locked .cx-hit { pointer-events: none; }

/* Narrow (phones): tighter pills and gap; pictures go above the label */
@container (max-width: 560px) {
  /* rows grow with their labels (no equal-height cap); the card body scrolls when they need more room */
  .cx-grid {
    --cx-pic: clamp(40px, 6.5vh, 64px); grid-template-columns: minmax(0, 1fr) 26px minmax(0, 1fr); --cx-gap: 10px;
    grid-auto-rows: minmax(var(--cx-h), auto); max-height: none;
  }
  .cx-item { gap: 8px; padding: 8px 8px 8px 12px; border-radius: 20px; font-size: var(--fs-sm); line-height: 1.25; }
  .cx-item[data-side="R"] { padding: 8px 12px 8px 8px; }
  .cx-item .port { width: 16px; height: 16px; }
  .cx-item:has(.cx-pic) { display: grid; align-items: center; row-gap: 6px; }
  .cx-item[data-side="L"]:has(.cx-pic) { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "pic port" "label port"; }
  .cx-item[data-side="R"]:has(.cx-pic) { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "port pic" "port label"; }
  .cx-item:has(.cx-pic) > .port { grid-area: port; }
  .cx-item:has(.cx-pic) > .cx-pic { grid-area: pic; width: 100%; min-width: 0; height: var(--cx-pic); max-height: none; align-self: center; aspect-ratio: auto; border-radius: 12px; }
  .cx-item:has(.cx-pic) > .cx-label { grid-area: label; }
}
@container (max-width: 360px) {
  /* large text on small phones: shrink towards 14px before long words have to break */
  .cx-item { font-size: min(var(--fs-cap), max(14px, 4.3cqi)); }
}
/* Short landscape phones: no row cap, the rows take what the labels need and the body scrolls */
@media (max-height: 520px) and (orientation: landscape) {
  .cx-grid { max-height: none; }
}

/* ---------- crossword ----------
   Shared .cells / .cell grid; crossword.js sets --cw-size (cell size) and --cw-gap from the free space.
   Wide cards (.is-wide): grid beside the clue list. Narrow cards: current clue (sticky) and grid on top, clue list below.
   One transparent input (.cw-in) sits on the current cell and receives the typing. */
.cw { --cw-size: 40px; --cw-gap: 4px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(14px, 2vw, 24px); min-width: 0; }
.cw.is-wide { flex-direction: row; justify-content: center; align-items: center; gap: clamp(24px, 3.5vw, 56px); }
.cw-main { flex: none; display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 100%; min-width: 0; }
.cw:not(.is-wide) .cw-main { width: 100%; }
.cw-board { max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; padding: 2px; }
.cw-grid {
  position: relative; width: max-content; gap: var(--cw-gap);
  grid-template-columns: repeat(var(--cols), var(--cw-size)); grid-auto-rows: var(--cw-size);
  touch-action: manipulation; cursor: pointer;
}
.cw-grid .cell {
  width: var(--cw-size); border-radius: calc(var(--cw-size) * .22);
  font-size: calc(var(--cw-size) * .5); padding-top: calc(var(--cw-size) * .08);
}
.cw-grid .cell-num { top: calc(var(--cw-size) * .05); left: calc(var(--cw-size) * .1); font-size: max(8px, calc(var(--cw-size) * .24)); }
.cw-grid .cell.is-active .cell-num { color: var(--accent-text); }
.cw-grid .cell.is-ok .cell-num { color: var(--plum); }
.cw-grid .cell.is-bad .cell-num { color: var(--try-ink); }
/* Selection shows strongly while typing, softly when the focus is elsewhere */
.cw:not(.is-focus) .cw-grid .cell.is-active { box-shadow: none; }
@media (hover: hover) {
  .cw:not(.is-locked) .cw-grid .cell:not(.is-void, .is-active, .is-hl, .is-ok, .is-bad):hover { border-color: var(--lilac); background: var(--surface-2); }
}
/* Wrong letter left visible (small, struck through) when the answer is shown */
.cw-was {
  position: absolute; right: calc(var(--cw-size) * .07); bottom: calc(var(--cw-size) * .03);
  font: 700 max(8px, calc(var(--cw-size) * .24))/1 var(--f-ui); color: var(--try-ink); text-decoration: line-through;
}
.cw-grid .cell.is-bad:has(.cw-was) .cw-l { color: var(--ok-ink); }
.cw-in {
  position: absolute; left: 0; top: 0; width: 40px; height: 40px; margin: 0; padding: 0; z-index: 2;
  border: 0; outline: none; background: transparent; color: transparent; caret-color: transparent;
  font-size: 16px; /* 16px keeps iOS from zooming on focus */ opacity: 0; pointer-events: none;
}
.cw-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Current clue (narrow cards only): number circle, direction and the clue */
.cw-cur { display: none; }
.cw:not(.is-wide).has-sel .cw-cur {
  position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 6px 12px 6px 6px; border: 2px solid var(--active-line); border-radius: 20px; background: var(--active-bg);
  font: 600 var(--fs-sm)/1.3 var(--f-ui);
  box-shadow: 0 -12px 0 var(--surface); /* covers the cells scrolling under it */
}
.cw-cur-n {
  flex: none; display: grid; place-items: center; width: 30px; aspect-ratio: 1; border-radius: 50%;
  background: var(--grape); color: #fff; font: 700 14px/1 var(--f-ui);
}
.cw-cur-b { min-width: 0; display: flex; flex-direction: column; }
.cw-cur-d { color: var(--accent-text); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .14em; text-transform: uppercase; }
.cw-cur-t { overflow-wrap: break-word; }

/* Clue lists: Across, then Down (two columns when the list is wide enough) */
.cw-clues {
  position: relative; flex: none; min-width: 0; align-content: start; width: 100%; max-width: 640px; align-self: center;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: clamp(12px, 1.6vw, 22px);
}
.cw.is-wide .cw-clues {
  flex: 1 1 0; max-width: 560px; grid-template-columns: 1fr; align-self: center;
  max-height: var(--cw-h, none); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
  padding: 4px; margin: -4px 0; /* room for the focus ring inside the scroll box */
}
.cw-h { margin: 0 0 8px 6px; color: var(--accent-text); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .16em; text-transform: uppercase; }
.cw-list { display: flex; flex-direction: column; gap: 5px; margin: 0; padding: 0; list-style: none; }
.cw-clue {
  display: flex; align-items: center; gap: 12px; width: 100%; min-height: 44px; margin: 0; padding: 4px 14px 4px 4px; text-align: left;
  border: 2px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--text);
  font: 600 var(--fs-body)/1.3 var(--f-ui);
  transition: border-color .15s, background-color .15s;
}
.cw-cn {
  flex: none; display: grid; place-items: center; width: clamp(30px, 2.6vw, 36px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--muted); font: 700 var(--fs-cap)/1 var(--f-ui);
  transition: background-color .15s, color .15s;
}
.cw-ct { min-width: 0; overflow-wrap: break-word; }
@media (hover: hover) { .cw:not(.is-locked) .cw-clue:not(.is-active):hover { border-color: var(--lilac); background: var(--surface-2); } }
.cw-clue.is-active { border-color: var(--active-line); background: var(--active-bg); }
.cw-clue.is-active .cw-cn { background: var(--grape); color: #fff; }
.cw-clue.is-ok .cw-cn { background: var(--lime); color: var(--plum); }
.cw-clue.is-bad .cw-cn { background: var(--coral); color: var(--plum); }
.cw-clue.is-ok { border-color: var(--ok-line); }
.cw-clue.is-bad { border-color: var(--try-line); }
.cw.is-locked .cw-grid, .cw.is-locked .cw-clue { cursor: default; }
.cw.is-locked .cw-cur { position: relative; } /* not sticky over the marked cells */
.cw.is-locked .cw-grid .cell.is-active:not(.is-ok, .is-bad, .is-missed) { box-shadow: none; }

/* Short landscape phones: tighter clue pills */
@media (max-height: 520px) and (orientation: landscape) {
  .cw-clue { min-height: 44px; padding: 3px 12px 3px 4px; font-size: var(--fs-sm); }
  .cw-cn { width: 28px; }
  .cw-h { margin-bottom: 4px; }
}
/* Short screens while typing (an on-screen keyboard is open): the activity header shrinks so the grid keeps some room */
@media (max-height: 460px) and (orientation: portrait), (max-height: 320px) {
  .ix:has(.cw.is-focus) .ix-meta, .ix:has(.cw.is-focus) .ix-ins { display: none; }
  .ix:has(.cw.is-focus) .ix-q { margin: 0; padding-right: 108px; overflow: hidden; font-size: var(--fs-sm); white-space: nowrap; text-overflow: ellipsis; }
  .panel-card:has(.panel-x:not([hidden])) .ix:has(.cw.is-focus) .ix-q { padding-right: 160px; }
  .ix:has(.cw.is-focus) .ix-body { padding-top: 6px; }
  .ix:has(.cw.is-focus) .ix-foot { margin-top: 6px; padding-block: 6px; }
}
/* Motion. Scale never goes above 1 on the cells: the board scrolls sideways, so growing cells would flash a scrollbar. */
.cw-grid .cell { transition: background-color var(--d-s) var(--e-out), border-color var(--d-s) var(--e-out), color var(--d-s); }
.cw-grid .cell.is-hl { transition-delay: calc(var(--hd, 0) * 28ms); }   /* the word highlight sweeps out from the cell */
.cw-enter .cw-grid { animation: m-in-pop var(--d-l) var(--e-out) both; }   /* one animation for the whole grid: a wave of 50+ cells is too slow on low-end devices */
.cw-enter .cw-clue { animation: m-in-drop var(--d-l) var(--e-out) calc(260ms + var(--k, 0) * 35ms) both; }
.cw-clue { transition: border-color .15s, background-color .15s, transform var(--d-xs) var(--e-out); }
.cw:not(.is-locked) .cw-clue:active { transform: scale(.97); }
.cw-clue.is-active .cw-cn { animation: m-in-pop var(--d-m) var(--spring); }
.cw-clue.is-ok .cw-cn { animation: m-jelly var(--d-l) ease-out; }
.cw-grid .cell.cw-win { animation: cw-squash var(--d-l) ease-out calc(var(--wj, 0) * 55ms) both; }
.cw-grid .cell.cw-win .cw-l { animation: m-jelly calc(var(--d-l) + 140ms) ease-out calc(var(--wj, 0) * 55ms) both; }
.cw-grid .cell.cw-no .cw-l { animation: cw-no .42s linear; }
@keyframes cw-squash {
  30% { transform: scale(calc(1 - .03 * var(--m)), calc(1 - .14 * var(--m))); }
  55% { transform: scale(calc(1 - .1 * var(--m)), calc(1 - .02 * var(--m))); }
  80% { transform: scale(calc(1 - .02 * var(--m)), calc(1 - .04 * var(--m))); }
}
@keyframes cw-no {
  15% { transform: translateX(calc(-5px * var(--m))); } 30% { transform: translateX(calc(4px * var(--m))); }
  45% { transform: translateX(calc(-3px * var(--m))); } 60% { transform: translateX(calc(2px * var(--m))); } 75% { transform: translateX(calc(-1px * var(--m))); }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .cw-clue, :root[data-motion="reduce"] .cw-cn, :root[data-motion="reduce"] .cw-grid .cell { transition: none; }

/* ---------- diagram ----------
   Parts bank beside the grid (above it on narrow cards). The grid (.dg-board) fills the free space: boxes grow up to
   --dg-cell-w × --dg-cell-h and the grid is centred. Placed parts are cards that fill their box; pipes (.links) run
   between them through the gaps, with an arrow head at the end that receives the water.
   Hit lines (.dg-hits) sit over the empty boxes and under the parts, so a tap on a pipe removes it. */
.dg {
  --dg-gap-x: clamp(34px, 5vw, 100px);
  --dg-gap-y: clamp(26px, 4.5vh, 76px);
  --dg-cell-w: clamp(110px, 15vw, 270px);
  --dg-cell-h: clamp(96px, 21vh, 220px);
  --dg-min-h: 60px;
  position: relative; display: grid; grid-template-columns: minmax(200px, 25%) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
  gap: clamp(12px, 1.6vw, 24px);
}
/* Fill the free height of the card (the boxes grow up to their largest size), never less than the content needs */
.ix-body > .dg { flex: 1 0 0; }
.dg-bank { flex-direction: column; flex-wrap: nowrap; align-items: stretch; align-content: stretch; }
.dg-bank > .dg-part { width: 100%; }
.dg-bank:not(:has(.dg-part)) { justify-content: center; }
.dg-bank > .bank-empty { margin: auto; text-align: center; }

.dg-board {
  position: relative; min-width: 0; display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, var(--dg-cell-w)));
  grid-template-rows: repeat(var(--rows), minmax(var(--dg-min-h), 1fr));
  justify-content: center; column-gap: var(--dg-gap-x); row-gap: var(--dg-gap-y);
  --dg-pad: clamp(22px, 2.6vw, 40px); padding: var(--dg-pad);
  /* Rows share the free height, up to --dg-cell-h each; then the grid is centred in its column */
  align-self: center; height: 100%;
  max-height: calc(var(--rows) * var(--dg-cell-h) + (var(--rows) - 1) * var(--dg-gap-y) + 2 * var(--dg-pad) + 4px);
  border: 2px solid var(--line); border-radius: clamp(22px, 2.4vw, 32px);
  background: var(--bg) radial-gradient(circle, var(--line) 1.3px, transparent 1.8px) 0 0 / 24px 24px;
}

/* Boxes: the shared .slot (dashed while empty). A filled box shows only its part card. */
.dg-slot { min-width: 0; min-height: 0; padding: 0; flex-wrap: nowrap; border-radius: clamp(18px, 2vw, 26px); }
.dg-slot .slot-hint { padding: 0 8px; }
.dg-slot:has(> .dg-part) { border: 0; padding: 0; background: none; }
.dg-slot.is-empty { cursor: default; }
.has-pick .dg-slot.is-empty, .is-dragging .dg-slot.is-empty { cursor: pointer; }

/* Part: a .token pill in the bank (and as the drag ghost); a card with the icon above the label on the grid */
.dg-part .token-mark { background: var(--surface-2); color: var(--accent-text); }
.dg-part .token-mark svg { width: 62%; }
.dg-part.has-pic .token-mark { background: var(--surface-2); }
.dg-part .token-label { overflow-wrap: anywhere; }
/* The card covers its box (absolutely, so its content does not set the row height: rows follow the free space) */
.dg-slot > .dg-part {
  position: absolute; inset: 0; z-index: 2; width: auto; min-width: 0; min-height: 0;
  flex-direction: column; justify-content: center; gap: clamp(4px, .8vh, 10px);
  padding: 8px 8px 10px; border-radius: inherit; text-align: center;
  font-size: calc(clamp(.875rem, 1.25vw, 1.125rem) * var(--ts)); line-height: 1.2; cursor: pointer;
}
/* Icon / picture size follows the box (the box is a size container) */
.dg-slot { container-type: size; }
.dg-slot > .dg-part .token-mark { width: clamp(28px, min(44cqh, 40cqw), 72px); }
.dg-slot > .dg-part.has-pic .token-mark { width: auto; height: clamp(28px, min(46cqh, 36cqw), 84px); aspect-ratio: 4 / 3; border-radius: 14px; }
/* Compact (set by diagram.js when the boxes are short and wide): icon beside the label */
.dg.is-compact { --dg-gap-y: 26px; }
.dg.is-compact .dg-slot > .dg-part { flex-direction: row; justify-content: flex-start; gap: 10px; padding: 6px 12px 6px 8px; text-align: left; }
.dg.is-compact .dg-slot > .dg-part .token-mark { width: clamp(30px, 62cqh, 48px); }
.dg.is-compact .dg-slot > .dg-part.has-pic .token-mark { width: auto; height: clamp(30px, 62cqh, 48px); }
/* Fixed parts (already on the grid) are flat soft cards: they can take pipes but cannot move */
.dg-part.is-fixed:not(.is-ok):not(.is-bad) { background: var(--surface-2); border-color: var(--surface-3); }
.dg-part.is-fixed .token-mark { background: var(--surface); }

/* Picking and pipe dragging */
.dg.from-part .dg-slot > .dg-part:not(.is-picked):not(.is-lifted):not(.is-over) { border-color: var(--lilac); }
.dg-slot > .dg-part.is-lifted { opacity: 1; border-style: solid; border-color: var(--active-line); background: var(--active-bg); }
.dg-slot > .dg-part.is-lifted .token-mark { background: var(--grape); color: #fff; }
.dg-slot > .dg-part.is-over, .dg-slot.is-over > .dg-part { border-color: var(--active-line); background: var(--active-bg); }
.dg-slot.is-over { transform: none; }   /* boxes stay still (pipes are measured from them); the ring invites */
/* Placed parts hold the pipe ends: no lift / squash / nudge (the icon circle still pops when picked) */
.dg-slot > .dg-part:hover, .dg-slot > .dg-part:active { transform: none; }
.dg-slot > .dg-part.is-picked { animation: none; }
.dg-slot::after { border-radius: calc(clamp(18px, 2vw, 26px) + 7px); }
/* Marks on the token's mark circle */
.dg-part.is-ok .token-mark { background: var(--lime); color: var(--plum); }
.dg-part.is-bad .token-mark { background: var(--coral); color: var(--plum); }

/* Pipes */
.dg-lines { z-index: 3; }
.dg-hits { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.dg-hit { fill: none; stroke: transparent; stroke-width: 26; stroke-linecap: round; stroke-linejoin: round; pointer-events: stroke; cursor: pointer; }
.dg-lines .link { stroke-width: 5; stroke-linejoin: round; }
.dg-under { fill: none; stroke: var(--bg); stroke-width: 11; stroke-linecap: round; stroke-linejoin: round; }
.dg-lines .dg-head { stroke-dasharray: none; }
.dg-pipe.is-hot .link { stroke: var(--lilac); }
[data-theme="dark"] .dg-pipe.is-hot .link { stroke: var(--text); }
.dg.is-locked .dg-hit { pointer-events: none; }
/* Correct answer: water runs through the pipes */
.dg-flow { fill: none; stroke: var(--surface); stroke-width: 1.8; stroke-linecap: round; stroke-dasharray: 1 11; animation: dg-flow .7s linear 8; }   /* flows for ~6 s, then rests (no endless repaint) */
@keyframes dg-flow { to { stroke-dashoffset: -12; } }

.dg-sr { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.dg.is-locked .dg-slot { cursor: default; }

/* Narrow cards (phones): bank on top (two parts per row), grid below */
@media (max-width: 760px) {
  .dg { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); --dg-gap-x: clamp(24px, 6vw, 40px); --dg-gap-y: 30px; --dg-min-h: 92px; }
  .dg-bank { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
  .dg-bank > .dg-part { padding-right: 12px; }
  .dg-bank > .bank-empty { grid-column: 1 / -1; }
  .dg-board { --dg-pad: 14px; }
  .dg-slot > .dg-part { padding: 6px 4px 8px; font-size: calc(.875rem * var(--ts)); }
}
/* Short landscape screens: bank on the left, smaller boxes */
@media (max-height: 520px) and (orientation: landscape) {
  .dg { grid-template-columns: minmax(180px, 28%) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); --dg-gap-y: 24px; --dg-gap-x: 32px; --dg-min-h: 56px; --dg-cell-h: 120px; --dg-cell-w: 220px; }
  .dg-bank { display: flex; gap: 8px; padding: 8px; }
  .dg-bank > .dg-part { min-height: 44px; padding-block: 3px; }
  .dg-bank > .dg-part .token-mark { width: 34px; }
  .dg-board { --dg-pad: 14px; }
  .dg-slot > .dg-part { gap: 3px; padding: 4px; font-size: calc(.875rem * var(--ts)); }
  .dg-slot > .dg-part .token-mark, .dg.is-compact .dg-slot > .dg-part .token-mark { width: 32px; }
  .dg-slot > .dg-part.has-pic .token-mark, .dg.is-compact .dg-slot > .dg-part.has-pic .token-mark { width: auto; height: 32px; }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .dg-flow { animation: none; display: none; }
:root[data-motion="reduce"] .dg-part.is-bad { animation: none; }

/* ---------- pairs ---------- */
/* Counter row on top, then the cards. pairs.js picks the columns and sets --cols, --card-w, --card-h and --fs
   from the free space; the cards are the shared .flip pieces. */
.pairs { display: flex; flex-direction: column; gap: clamp(8px, 1.2vw, 16px); min-width: 0; }
.pairs-bar {
  flex: none; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px clamp(14px, 2vw, 28px);
  font: 700 var(--fs-cap)/1.2 var(--f-ui); color: var(--muted);
}
.pairs-grid {
  --gap: clamp(8px, 1vw, 16px);
  flex: 1 0 auto; display: grid; gap: var(--gap); align-content: center; justify-content: center;
  grid-template-columns: repeat(var(--cols, 4), var(--card-w, minmax(0, 1fr)));
  grid-auto-rows: var(--card-h, auto);
}
.pairs-card.flip { aspect-ratio: auto; width: 100%; height: 100%; min-height: 0; }
.pairs .flip-face { padding: clamp(6px, .8vw, 12px); font-size: var(--fs, 16px); overflow: hidden; }
.pairs .flip-back { font-size: clamp(26px, calc(var(--card-h, 120px) * .32), 64px); }
.pairs-t { max-width: 100%; overflow-wrap: break-word; hyphens: auto; }
.pairs .flip-face.is-name .pairs-t { font-weight: 700; }
.pairs .flip-face.has-pic .pairs-t { flex: none; }
.pairs .flip-face .ph { border-width: 2px; padding: 4px; gap: 2px; }
.pairs .flip-face .ph svg { width: auto; height: 60%; max-height: 52px; min-height: 0; }
.pairs .flip-face .ph.is-tiny .ph-s, .pairs .flip-face .ph.is-tiny .ph-t { display: none; }
.pairs-card[aria-disabled="true"], .pairs.is-locked .pairs-card, .pairs.is-busy .pairs-card { cursor: default; }
/* Screen-reader announcements only */
.pairs-say { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* No lilac hover while two wrong cards are showing or after the end */
.pairs.is-busy .flip-back, .pairs.is-locked .flip-back { border-color: var(--grape-deep) !important; }
/* Motion (small amplitudes: the cards fill the card edge to edge, so nothing may grow far past its box; the jelly barely
   widens and the wiggle turns 1 degree at most, so even a 360px card stays inside the 4px edge of .ix-body).
   Pick: the card dips while it turns. Pair: jelly after the turn + a ring. Not a pair: wiggle, then it turns back.
   All found: a wave of hops over the deck (pairs.js adds .pr-win; --k = card index). */
.pairs-card.is-open:not(.is-bad) { animation: pr-dip var(--d-l) var(--e-io); }
.pairs-card.is-ok { animation: pr-jelly calc(var(--d-l) + 140ms) ease-out var(--d-m) both; }
.pairs-card.is-ok::after {
  content: ""; position: absolute; inset: 6px; border: 3px solid var(--ok-line); border-radius: clamp(18px, 2vw, 26px); pointer-events: none;
  opacity: 0; animation: pr-ring var(--d-xl) var(--e-out) var(--d-m);
}
.pairs-card.flip.is-bad { animation: pr-wiggle .52s ease-in-out var(--d-m) both; }
.pairs.pr-win .pairs-card { animation: pr-hop var(--d-l) var(--e-io) calc(var(--d-l) + min(var(--k, 0), 12) * 40ms) both; }
@keyframes pr-dip { 50% { transform: scale(calc(1 - .05 * var(--m))); } }
@keyframes pr-jelly {
  25% { transform: scale(calc(1 + .008 * var(--m)), calc(1 - .08 * var(--m))); }
  45% { transform: scale(calc(1 - .004 * var(--m)), calc(1 + .03 * var(--m))); }
  65% { transform: scale(1, calc(1 - .02 * var(--m))); }
}
@keyframes pr-ring { from { opacity: .8; transform: scale(calc(1 - .04 * var(--m))); } to { opacity: 0; transform: scale(calc(1 + .06 * var(--m))); } }
@keyframes pr-wiggle {
  20% { transform: rotate(calc(-1deg * var(--m))); } 40% { transform: rotate(calc(.8deg * var(--m))); }
  60% { transform: rotate(calc(-.6deg * var(--m))); } 80% { transform: rotate(calc(.3deg * var(--m))); }
}
@keyframes pr-hop { 40% { transform: translateY(calc(-8px * var(--m))); } 70% { transform: translateY(calc(1px * var(--m))); } }

/* ---------- words ----------
   Word search. Shared .cells / .cell grid; words.js sets --ws-size (cell size) and --ws-gap from the free space.
   Wide cards (.is-wide): grid beside the word list (one column when it fits: .is-col). Narrow cards: word chips above the grid.
   Current selection = .cell.is-picked (grape). Found words = a lime bar behind the letters (.ws-marks, under the grid);
   their cells (.is-ok) turn see-through so the bar shows. A wrong selection is .is-bad for a moment. */
.ws { --ws-size: 32px; --ws-gap: 3px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(12px, 2vw, 22px); min-width: 0; }
.ws.is-wide { flex-direction: row-reverse; gap: clamp(24px, 3.5vw, 56px); }
.ws-board { position: relative; flex: none; max-width: 100%; padding: 2px; }
.ws-marks { position: absolute; left: 2px; top: 2px; overflow: visible; pointer-events: none; }
.ws-mark { fill: var(--lime); stroke: var(--ok-line); stroke-width: 2; }
.ws-mark.is-new { transform-box: fill-box; transform-origin: 0 50%; animation: ws-stretch var(--d-l) var(--spring) both; }
@keyframes ws-stretch { from { opacity: .3; transform: scaleX(calc(1 - (1 - var(--s0, .2)) * var(--m))); } }
.ws-grid {
  position: relative; width: max-content; gap: var(--ws-gap);
  grid-template-columns: repeat(var(--cols), var(--ws-size)); grid-auto-rows: var(--ws-size);
  cursor: pointer; outline: none;
}
.ws-row { display: contents; }
.ws-grid .cell {
  width: var(--ws-size); border-radius: calc(var(--ws-size) * .24);
  font-size: max(.875rem, calc(var(--ws-size) * .5));   /* letters never under 14px */
}
.ws-grid .cell.is-ok { background: transparent; border-color: transparent; }
.ws-grid .cell.is-ok.is-picked { background: var(--lilac); border-color: var(--grape); }
.ws-grid .cell.ws-start { box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--grape); z-index: 1; }
[data-theme="dark"] .ws-grid .cell.ws-start { box-shadow: 0 0 0 3px var(--surface), 0 0 0 5px var(--lilac); }
.ws-grid:focus-visible .cell.is-kb { box-shadow: 0 0 0 3px var(--focus); z-index: 1; }
@media (hover: hover) {
  .ws:not(.is-locked) .ws-grid .cell:not(.is-picked, .is-ok, .is-bad):hover { border-color: var(--lilac); background: var(--surface-2); }
}
.ws.is-locked .ws-grid { cursor: default; }
/* Motion: letters wave in; a cell joining the selection pops (scale stays close to 1: centres stay put for dragging) */
.ws-enter .ws-grid { animation: m-in-pop var(--d-l) var(--e-out) both; }   /* one animation for the whole grid (100 cells) */
.ws-grid .cell.is-picked { animation: ws-pick var(--d-s) var(--spring); }
.ws-grid .cell.is-bad { animation: shake .42s; }
@keyframes ws-pick { from { transform: scale(calc(1 - .18 * var(--m))); } }

/* Word list: a count, then one pill per word with a tick circle (lime + check when found) */
.ws-side { flex: none; display: flex; flex-direction: column; align-items: center; gap: 10px; min-width: 0; width: 100%; max-width: 640px; }
.ws.is-wide .ws-side {
  align-items: stretch; width: var(--ws-list-w, 300px); max-width: none; max-height: var(--ws-h, none);
  overflow-y: auto; scrollbar-width: thin; padding: 2px;
}
.ws-count { margin: 0; color: var(--accent-text); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .16em; text-transform: uppercase; }
.ws.is-wide .ws-count { margin-left: 6px; }
.ws-list { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 8px; margin: 0; padding: 0; list-style: none; }
.ws.is-wide .ws-list { justify-content: flex-start; }
.ws.is-wide.is-col .ws-list { flex-direction: column; flex-wrap: nowrap; }
.ws-word {
  display: inline-flex; align-items: center; gap: 10px; min-height: clamp(38px, 3.6vw, 48px); padding: 4px 16px 4px 4px;
  border: 2px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--text);
  font: 700 var(--fs-body)/1.2 var(--f-ui); letter-spacing: .04em;
  transition: border-color .2s, background-color .2s;
}
.ws-tick {
  flex: none; display: grid; place-items: center; width: clamp(28px, 2.6vw, 36px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--plum); transition: background-color .2s;
}
.ws-tick svg { width: 58%; height: 58%; opacity: 0; transform: scale(.4); transition: opacity .2s, transform .3s var(--pop); }
.ws-word.is-ok { border-color: var(--ok-line); background: var(--ok-tint); }
.ws-word.is-ok .ws-tick { background: var(--lime); }
.ws-word.is-ok .ws-tick svg { opacity: 1; transform: none; }
/* Found: the tick jellies and a line strikes the word through */
.ws-word.is-ok .ws-tick { animation: m-jelly calc(var(--d-l) + 140ms) ease-out; }
.ws-wt { position: relative; }
.ws-word.is-ok .ws-wt::after {
  content: ""; position: absolute; left: -3px; right: -3px; top: calc(50% - 1px); height: 2px; border-radius: 2px;
  background: var(--ok-ink); transform-origin: 0 50%; animation: ws-strike var(--d-m) var(--e-out) 120ms both;
}
@keyframes ws-strike { from { opacity: 0; transform: scaleX(calc(1 - var(--m))); } }
.ws-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Phones: compact chips */
@media (max-width: 600px), (max-height: 520px) and (orientation: landscape) {
  .ws-list { gap: 5px 6px; }
  .ws-word { gap: 6px; min-height: 32px; padding: 2px 11px 2px 3px; font-size: var(--fs-cap); }
  .ws-tick { width: 24px; }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .ws-grid .cell.is-bad { animation: none; }
:root[data-motion="reduce"] .ws-word, :root[data-motion="reduce"] .ws-tick, :root[data-motion="reduce"] .ws-tick svg, :root[data-motion="reduce"] .ws-grid .cell { transition: none; }

/* ---------- branch ---------- */
/* Steps trail + attempt count, then a scene card: picture beside the text and choices (picture above on narrow cards).
   Endings tint the card: green = good ending, red = bad ending. Choices are the shared .opt pills. */
.ix-body > .br { display: flex; flex-direction: column; gap: clamp(8px, 1vw, 14px); }
.br-top { flex: none; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 16px; padding: 0 4px; }
.br-trail { display: flex; align-items: center; gap: 12px; min-width: 0; }
.br-dots { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; padding: 0; list-style: none; }
.br-dots li { width: 10px; height: 10px; border-radius: 50%; background: var(--grape); }
.br-dots li.is-cur { width: 16px; height: 16px; background: transparent; border: 3px solid var(--accent-text); }
.br-dots li.is-ok { background: var(--lime); border-color: var(--ok-line); }
.br-dots li.is-bad { background: var(--coral); border-color: var(--try-line); }
.br-step, .br-att, .br-label {
  margin: 0; color: var(--muted); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .14em; text-transform: uppercase;
}
.br-att { letter-spacing: 0; text-transform: none; font-size: var(--fs-cap); font-weight: 600; }

.br-scene {
  flex: 1 0 auto; display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(12px, 1.8vw, 28px);
  padding: clamp(10px, 1.4vw, 18px); border: 2px solid var(--line); border-radius: clamp(20px, 2.2vw, 30px); background: var(--surface);
  transition: background-color .2s, border-color .2s;
}
.br-scene.has-pic { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
.br-scene.is-ok { border-color: var(--ok-line); background: var(--ok-tint); }
.br-scene.is-bad { border-color: var(--try-line); background: var(--try-tint); }
/* Motion. A new scene slides in (picture, then text), its choices rise one after another; the old scene goes at once.
   Endings: the card settles in; good = icon pops then tadas (branch.js), bad = the heading shakes and Try again calls.
   Movements stay within the scene's padding (~12px) so the scroll area never grows mid-animation. */
.br-enter > * { animation: br-in var(--d-l) var(--e-out) both; }
.br-enter > .br-main { animation-delay: 60ms; }
@keyframes br-in { from { opacity: 0; transform: translateX(calc(12px * var(--m))); } }
.br-enter .br-opt { animation: br-up var(--d-l) var(--e-out) calc(160ms + min(var(--i, 0), 6) * 55ms) both; }
@keyframes br-up { from { opacity: 0; transform: translateY(calc(10px * var(--m))) scale(calc(1 - .03 * var(--m))); } }
.br-opts.locked .br-opt.sel { animation: br-pick var(--d-m) var(--spring); }
@keyframes br-pick { from { transform: scale(calc(1 - .05 * var(--m))); } }
.br-scene.br-enter.is-end { animation: br-settle var(--d-l) var(--e-out) both; }
@keyframes br-settle { from { opacity: .4; transform: scale(calc(1 - .04 * var(--m))); } }
.br-enter.is-end .br-end-icon { animation: m-in-pop var(--d-xl) var(--spring-big) 120ms both; }
.br-enter.is-bad .br-end-head { animation: shake .48s linear 300ms; }
.br-retry { animation: br-call calc(var(--d-xl) + 200ms) var(--e-io) 900ms 2; }
@keyframes br-call { 50% { transform: scale(calc(1 + .05 * var(--m))); } }

.br-pic { display: flex; min-height: 200px; }
.br-pic > img, .br-pic > .ph { flex: 1; width: 100%; min-height: 0; object-fit: contain; border-radius: clamp(14px, 1.6vw, 22px); background: var(--surface-2); }
/* Without a picture, keep the lines readable */
.br-scene:not(.has-pic) .br-main { width: 100%; max-width: 62rem; margin-inline: auto; }
.br-main { min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: clamp(12px, 1.4vw, 20px); padding: clamp(2px, .6vw, 8px); }
.br-text { margin: 0; font-size: calc(clamp(1rem, min(1.6vw, 2.6vh), 1.375rem) * var(--ts)); line-height: 1.5; outline: none; }
.br-text:focus-visible, .br-end-title:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; border-radius: 8px; }

/* Choices: one per row, large pills (no result badge, so no room kept for it) */
.br-opts { grid-template-columns: minmax(0, 1fr); }
.br-opt { min-height: clamp(52px, min(6vw, 8vh), 80px); padding-right: 22px; font-size: var(--fs-body); }
.br-opts.locked .opt.sel { opacity: 1; }
.br-opt:disabled { opacity: .45; cursor: not-allowed; }

/* Endings */
.br-end-head { display: flex; align-items: center; gap: clamp(12px, 1.4vw, 18px); }
.br-end-icon { flex: none; display: grid; place-items: center; width: clamp(44px, 4.4vw, 60px); aspect-ratio: 1; border-radius: 50%; }
.br-end-icon svg { width: 55%; height: 55%; }
.is-ok .br-end-icon { background: var(--lime); color: var(--plum); }
.is-bad .br-end-icon { background: var(--coral); color: var(--plum); }
.br-end-kind { margin: 0 0 .3em; color: var(--muted); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .14em; text-transform: uppercase; }
.br-end-title { margin: 0; font: 400 var(--fs-h2)/1.08 var(--f-display); text-transform: uppercase; outline: none; }
.is-ok .br-end-title { color: var(--ok-ink); }
.is-bad .br-end-title { color: var(--try-ink); }
.br-main .br-label { margin-bottom: -6px; }
.br-path { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; list-style: none; counter-reset: br; }
.br-path li {
  counter-increment: br; display: flex; align-items: center; gap: 10px;
  font: 600 var(--fs-sm)/1.3 var(--f-ui);
}
.br-path li::before {
  content: counter(br); flex: none; display: grid; place-items: center; width: 28px; aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--muted); font: 700 13px/1 var(--f-ui);
}
.br-path.is-safe li::before { background: var(--lime); color: var(--plum); }
.br-path.is-safe + .br-path.is-safe { padding-top: 8px; border-top: 2px dashed var(--line); }
.br-again { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.br-left { color: var(--muted); font-size: var(--fs-sm); }

.br-sr { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Narrow cards: picture above the text */
@media (max-width: 700px) {
  .br-scene.has-pic { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(150px, 1fr) auto; }
  .br-pic { min-height: 150px; }
  .br-pic .ph-s { display: none; }
  .br-again .btn { flex: 1 1 auto; }
}
/* Short landscape phones: picture beside, tighter */
@media (max-height: 520px) and (orientation: landscape) {
  .br-scene.has-pic { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); grid-template-rows: none; }
  .br-scene { padding: 8px; gap: 12px; }
  .br-pic { min-height: 0; }
  .br-pic .ph-s { display: none; }
  .br-text { font-size: var(--fs-body); }
  .br-opt { min-height: 48px; font-size: var(--fs-sm); }
  .br-main { gap: 10px; }
  .br-again .btn { flex: none; }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .br-enter > * { animation: none; }

/* ---------- maze ----------
   Maze walk. Shared .cells / .cell grid (walls .is-wall, visited path .is-hl, door being asked .is-active,
   opened door .is-ok, door opened with the answer shown .is-missed, end reached .is-ok), HUD (.hud), game screens (.game-ov)
   and answer pills (.opt). maze.js sets --mz-size (cell size) and --mz-gap from the free space and picks the layout:
   .is-wide = HUD and pad beside the maze (.is-short: side by side), otherwise HUD above and pad below.
   The worker (.mz-me, grape) moves over the grid with a transform. */
.ix-body > .mz {
  --mz-size: 32px; --mz-gap: 3px; --mz-btn: clamp(52px, 5vw, 64px);
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: "hud" "board" "pad";
  justify-content: center; align-content: center; justify-items: center; gap: clamp(10px, 1.6vw, 18px);
  min-width: 0; border-radius: clamp(18px, 2vw, 28px);
}
.mz.is-wide { grid-template-columns: max-content max-content; grid-template-areas: "board hud" "board pad"; align-items: end; column-gap: clamp(24px, 3vw, 48px); }
.mz.is-wide > .mz-pad { align-self: start; }
.mz.is-wide.is-short { grid-template-columns: max-content max-content max-content; grid-template-areas: "board hud pad"; align-items: center; column-gap: clamp(16px, 2.4vw, 32px); }
.mz.is-wide.is-short > .mz-pad { align-self: center; }

/* HUD */
.mz-hud { grid-area: hud; justify-content: center; }
.mz.is-wide .mz-hud { flex-direction: column; align-items: stretch; width: clamp(200px, 17vw, 250px); }
.mz.is-wide.is-short .mz-hud { width: 200px; }
.mz-hud .hud-item { justify-content: flex-start; }
.mz.is-wide .mz-hud .hud-item { min-height: 44px; }
.mz-hud .hud-item svg { width: 20px; height: 20px; color: var(--accent-text); }
.mz-hud-wrong.is-bad { border-color: var(--try-line); color: var(--try-ink); }
.mz-hud-wrong.is-bad small { color: var(--try-ink); }
.mz-pause { min-height: 44px; color: var(--accent-text); cursor: pointer; transition: border-color .15s, background-color .15s; }
.mz-pause span { color: var(--text); }
@media (hover: hover) { .mz-pause:hover { border-color: var(--lilac); background: var(--surface-2); } }

/* Board */
.mz-board { grid-area: board; position: relative; flex: none; }
.mz-grid {
  gap: var(--mz-gap); grid-template-columns: repeat(var(--cols), var(--mz-size)); grid-auto-rows: var(--mz-size);
  width: max-content; outline: none; border-radius: calc(var(--mz-size) * .3);
}
.mz-grid:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }
.mz-grid .cell { width: var(--mz-size); border-radius: calc(var(--mz-size) * .24); border-width: max(1px, min(2px, calc(var(--mz-size) * .06))); }
.mz-grid .cell:not(.is-wall) { cursor: pointer; }
.mz-grid .cell.is-hl:not(.mz-door, .mz-end) { background: var(--surface-2); border-color: var(--surface-3); }
@media (hover: hover) {
  .mz.is-on:not(.is-done) .mz-grid .cell:not(.is-wall, .is-ok, .is-missed, .is-active):hover { border-color: var(--lilac); }
}
.mz-grid .cell svg { width: 62%; height: 62%; }
.mz-door { color: var(--accent-text); border-color: var(--lilac); background: var(--active-bg); }
.mz-grid .cell.mz-door:not(.is-ok, .is-missed, .is-active) { border-style: solid; border-color: var(--lilac); background: var(--active-bg); }
.mz-dl {
  position: absolute; right: 6%; bottom: 4%; font: 700 max(12px, calc(var(--mz-size) * .28))/1 var(--f-ui); color: inherit;
}
.mz-door svg { width: 54%; height: 54%; margin-top: -8%; }
/* Small cells (maze.js .is-tiny): the door shows its letter only, centred */
.mz.is-tiny .mz-door svg { display: none; }
.mz.is-tiny .mz-dl { position: static; font-size: max(12px, calc(var(--mz-size) * .6)); }
.mz-grid .cell.mz-door.is-ok { color: var(--plum); }
.mz-grid .cell.mz-door.is-missed { background: var(--ok-tint); }
.mz-end { color: var(--accent-text); }
.mz-grid .cell.mz-end:not(.is-ok) { border-color: var(--lilac); border-style: dashed; }
.mz-grid .cell.mz-end.is-ok { color: var(--plum); }

/* The worker: a grape disc with a hard-hat face */
.mz-me {
  position: absolute; left: 0; top: 0; z-index: 2; width: var(--mz-size); height: var(--mz-size); pointer-events: none;
  display: grid; place-items: center; transition: transform var(--d-s) var(--e-io);
}
.mz.is-walk .mz-me:not(.no-anim) { transition: transform var(--d-xs) linear; }   /* steps close together: keep up, no slow start on each retarget */
.mz-me.no-anim, .mz.is-still .mz-me { transition: none; }
.mz-me-in {
  display: grid; place-items: center; width: 84%; height: 84%; border-radius: 50%;
  background: var(--grape); color: #fff; box-shadow: 0 0 0 2px var(--surface), var(--shadow-1);
}
[data-theme="dark"] .mz-me-in { background: var(--lilac); color: var(--plum); }
.mz-me-in svg { width: 72%; height: 72%; }
.mz-me.is-bump .mz-me-in { animation: shake .3s; }
.mz.is-done .mz-me-in { background: var(--lime); color: var(--plum); }

/* Finished: the pad is not needed any more (the footer takes its room) */
.mz.is-done > .mz-pad, .mz.is-done .mz-pause { display: none; }

/* Direction pad: 3 x 3, middle empty */
.mz-pad { grid-area: pad; display: grid; grid-template-columns: repeat(3, var(--mz-btn)); grid-template-rows: repeat(3, var(--mz-btn)); gap: 6px; }
.mz-dir { width: var(--mz-btn); height: var(--mz-btn); min-height: 0; padding: 0; border-radius: 30%; color: var(--accent-text); touch-action: manipulation; }
.mz-dir svg { width: 46%; height: 46%; }
.mz-up { grid-area: 1 / 2; }
.mz-left { grid-area: 2 / 1; }
.mz-left svg { transform: rotate(-90deg); }
.mz-right { grid-area: 2 / 3; }
.mz-right svg { transform: rotate(90deg); }
.mz-down { grid-area: 3 / 2; }
.mz-down svg { transform: rotate(180deg); }

/* Screens over the game area: start, pause, door question (the whole activity area; the card scrolls when it is taller) */
.mz > .game-ov { overflow: auto; }
.mz > .mz-scr { z-index: 7; }
.mz-scr .btn { min-width: 160px; }
.mz-scr .btn svg { width: 18px; height: 18px; }
.mz-ask { width: min(100%, 720px); display: grid; grid-template-columns: minmax(0, 1fr); align-items: start; gap: clamp(12px, 1.6vw, 20px); text-align: left; margin: auto; }
.mz.is-wide .mz-ask { width: min(100%, 1080px); grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); column-gap: clamp(20px, 2.6vw, 40px); align-items: center; }
.mz-ask-l, .mz-ask-r { min-width: 0; display: flex; flex-direction: column; gap: clamp(8px, 1vw, 14px); }
.mz-ask-head { display: flex; align-items: center; justify-content: space-between; gap: 6px 14px; }
.mz-back { min-height: 44px; padding: .5em 1em; margin-right: -.6em; }
.mz-ask-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px 14px; }
.mz-ask-foot .mz-msg { flex: 1 1 220px; }
.mz-ask-foot .mz-msg:empty { min-height: 0; }
.mz-ask-title { display: inline-flex; align-items: center; gap: 10px; }
.mz-ask-title svg { width: .9em; height: .9em; }
.mz-ask-tries { color: var(--muted); font: 600 var(--fs-cap)/1.2 var(--f-ui); }
.mz-ask-q { margin: 0; font: 700 var(--fs-lead)/1.35 var(--f-ui); }
.mz-opts { grid-template-columns: minmax(0, 1fr); }
.mz-opt { min-height: clamp(54px, 6vh, 72px); font-size: var(--fs-body); }
.mz-opt:disabled { cursor: default; }
.mz-opt.is-tried { animation: none; }
.mz-opts.locked .opt.ok { opacity: 1; }
.mz-msg { margin: 0; min-height: 1.4em; font: 700 var(--fs-sm)/1.4 var(--f-ui); }
.mz-msg.is-ok { color: var(--ok-ink); }
.mz-msg.is-bad { color: var(--try-ink); }
.mz-msg.is-shown { color: var(--text); font-weight: 600; }
.mz-msg.is-shown strong { color: var(--ok-ink); }
.mz-acts { display: flex; justify-content: flex-end; gap: 10px; margin-left: auto; }
.mz-acts:empty { display: none; }

.mz-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Phones */
@media (max-width: 600px) {
  .ix-body > .mz { --mz-btn: 56px; gap: 10px; }
  .mz-hud { gap: 6px; }
  .mz-hud .hud-item { padding: 6px 10px; font-size: var(--fs-cap); }
  .mz-acts .btn { flex: 1 1 auto; }
}
/* Short portrait phones: smaller pad so the maze keeps some height */
@media (max-width: 600px) and (max-height: 720px) and (orientation: portrait) {
  .ix-body > .mz { --mz-btn: 48px; gap: 8px; }
  .mz-pad { gap: 4px; }
}
/* Short landscape phones: smaller pad, answers side by side */
@media (max-height: 520px) and (orientation: landscape) {
  .ix-body > .mz { --mz-btn: 46px; }
  .mz-pad { gap: 4px; }
  .mz.is-wide .mz-hud { gap: 6px; }
  .mz.is-wide .mz-hud .hud-item { min-height: 44px; padding: 4px 12px; font-size: var(--fs-cap); }
  .mz > .game-ov { padding: 4px; place-items: start center; }
  .mz-ask { padding: 10px 14px; gap: 8px; }
  .mz-ask-title { font-size: calc(1.375rem * var(--tsh)); }
  .mz-ask-q { font-size: var(--fs-body); }
  .mz-opts { gap: 6px; }
  .mz-opt { min-height: 46px; font-size: var(--fs-cap); padding: 4px 40px 4px 6px; }
  .mz-opt .opt-mark { width: 30px; }
  .game-ov-card { gap: 8px; }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .mz-me, :root[data-motion="reduce"] .mz.is-walk .mz-me:not(.no-anim) { transition: none; }
:root[data-motion="reduce"] .mz-me.is-bump .mz-me-in, :root[data-motion="reduce"] .mz-opt.bad { animation: none; }

/* ---------- shooter ---------- */
/* Task + game header (.hud) on top, the field below taking the free height of the card. Items are flat cards (picture +
   label) that cross the field in lanes; the nozzle (grape) sits at the bottom centre and sprays lilac water.
   Green = a needed item hit, red = a wrong item / a lost life. The in-game card (.game-ov) covers the header and the field.
   Card sizes come from the field size (set by the script as --sh-card-h, --sh-card-w, --sh-pic, --sh-fs). */
.ix-body > .sh { flex: 1 0 0; }
.sh { position: relative; display: flex; flex-direction: column; gap: clamp(8px, 1.2vw, 14px); min-height: 250px; }
.sh-top { flex: none; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
.sh-task { flex: 1 1 300px; min-width: 0; margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
.sh-task-k { color: var(--muted); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .14em; text-transform: uppercase; }
.sh-task-t { font: 700 calc(clamp(1rem, min(1.7vw, 2.8vh), 1.375rem) * var(--ts))/1.3 var(--f-ui); }
.sh-hud { flex: 0 1 auto; min-width: 0; gap: 6px 8px; }
.sh-hud .hud-item { white-space: nowrap; }
.sh-hud .hud-lives.is-hurt { border-color: var(--try-line); background: var(--try-tint); animation: shake .42s; }
.sh-pause { justify-content: center; min-width: 44px; min-height: 44px; padding: 0 10px; color: var(--accent-text); cursor: pointer; }
.sh-pause svg { width: 20px; height: 20px; }
.sh-pause:disabled { opacity: .45; cursor: default; }
@media (hover: hover) { .sh-pause:not(:disabled):hover { border-color: var(--lilac); background: var(--surface-2); } }

/* Field */
.sh-field {
  position: relative; flex: 1 1 auto; min-height: 150px; overflow: hidden; outline: none;
  border: 2px solid var(--line); border-radius: clamp(18px, 2vw, 28px); background: var(--bg);
  touch-action: none; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.is-playing .sh-field { cursor: crosshair; }
.sh-field:focus-visible { border-color: var(--accent-text); box-shadow: 0 0 0 2px var(--focus); }
.sh-lanes > i { position: absolute; left: 0; right: 0; border-top: 2px dashed var(--line); }
.sh-lanes > i:nth-child(odd) { background: var(--surface-2); opacity: .6; }
.sh-lanes > i:first-child { border-top: 0; }
/* Ground strip for the nozzle base */
.sh-field::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: var(--sh-ground, 24px);
  border-top: 2px solid var(--line); background: var(--surface-3); pointer-events: none;
}
.sh-nozzle {
  position: absolute; left: 50%; bottom: 4px; z-index: 3; width: calc(var(--sh-noz, 60px) * .56); height: var(--sh-noz, 60px);
  margin-left: calc(var(--sh-noz, 60px) * -.28); transform-origin: 50% 100%; pointer-events: none; transition: transform .06s linear;
}
.sh-nozzle svg { display: block; width: 100%; height: 100%; overflow: visible; }
.sh-noz-tip { fill: var(--lilac); }
.sh-noz-body { fill: var(--grape); }
.sh-noz-grip { fill: var(--grape-deep); }
[data-theme="dark"] .sh-noz-grip { fill: var(--plum); stroke: var(--lilac); stroke-width: 2; }
.sh-fx { position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.sh-jet { fill: none; stroke: var(--lilac); stroke-width: 7; stroke-linecap: round; stroke-dasharray: 12 8; animation: sh-jet .32s linear forwards; }
.sh-splash { fill: none; stroke: var(--lilac); stroke-width: 4; transform-box: fill-box; transform-origin: center; animation: sh-splash .36s ease-out forwards; }
.is-still .sh-jet, .is-still .sh-splash { animation: none; opacity: .8; }
@keyframes sh-jet { from { stroke-dashoffset: 40; } to { stroke-dashoffset: 0; opacity: .2; } }
@keyframes sh-splash { from { transform: scale(.2); opacity: 1; } to { transform: scale(1.5); opacity: 0; } }

/* Item card: picture above the label (tall lanes) or beside it (.is-row: short lanes) */
.sh-item {
  position: absolute; top: 0; left: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  width: var(--sh-card-w, 130px); height: var(--sh-card-h, 120px); margin: 0; padding: 8px 8px 6px;
  border: 3px solid var(--line); border-radius: clamp(14px, 1.4vw, 20px); background: var(--surface); color: var(--text);
  font: 700 var(--sh-fs, 15px)/1.2 var(--f-ui); text-align: center; cursor: crosshair;
  box-shadow: var(--shadow-1); will-change: translate; touch-action: none;
  transition: border-color .12s, background-color .12s;
}
.sh-item[hidden] { display: none; }
.sh-pic { flex: none; display: flex; width: var(--sh-pic, 70px); height: var(--sh-pic, 70px); border-radius: 12px; overflow: hidden; background: var(--surface-2); pointer-events: none; }
.sh-pic > img, .sh-pic > .ph { width: 100%; height: 100%; object-fit: cover; border-radius: 12px; }
.sh-pic > .ph { border-width: 2px; }
.sh-label { min-width: 0; max-width: 100%; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow-wrap: break-word; pointer-events: none; }
.sh-field.is-row .sh-item { flex-direction: row; justify-content: flex-start; gap: 10px; padding: 5px 12px 5px 5px; text-align: left; border-radius: 999px; }
.sh-field.is-row .sh-pic { border-radius: 50%; }
.sh-field.is-row .sh-pic > img, .sh-field.is-row .sh-pic > .ph { border-radius: 50%; }
/* Lane number for the keyboard (mouse/keyboard screens only) */
.sh-key {
  position: absolute; top: -9px; left: -9px; display: none; place-items: center; width: 24px; height: 24px; border-radius: 50%;
  background: var(--grape); color: #fff; font: 700 13px/1 var(--f-ui); pointer-events: none;
}
@media (hover: hover) { .sh-key { display: grid; } .sh-item:hover { border-color: var(--lilac); } }
.sh-field.is-row .sh-key { top: -6px; left: -6px; }
.sh-item:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
/* Motion: a card comes in with a soft pop; a needed hit swells with a ring, then shrinks away (before HIT_GONE hides it).
   `scale` and `transform` only: `translate` stays the card's place. Paused game: the animations stop too. */
.sh-item { animation: sh-in var(--d-m) var(--e-out); }
.sh-item.is-ok { border-color: var(--ok-line); background: var(--ok-tint); animation: sh-pop var(--d-l) var(--e-out) forwards; }
.sh-item.is-ok::after { content: ""; position: absolute; inset: -3px; border: 3px solid var(--ok-line); border-radius: inherit; pointer-events: none; animation: m-ring var(--d-l) var(--e-out) forwards; }
.sh-item.is-bad { border-color: var(--try-line); background: var(--try-tint); animation: shake .42s; }
.sh-item.is-ok .sh-key { background: var(--lime); color: var(--plum); }
.sh-item.is-bad .sh-key { background: var(--coral); color: var(--plum); }
.sh.is-paused .sh-item, .sh.is-paused .sh-item::after, .sh.is-paused .sh-fx * { animation-play-state: paused; }
@keyframes sh-in { from { opacity: 0; scale: calc(1 - .15 * var(--m)); } }
@keyframes sh-pop {
  30% { scale: calc(1 + .12 * var(--m)); opacity: 1; }
  55% { scale: calc(1 + .06 * var(--m)); opacity: 1; }
  to { scale: calc(1 - .5 * var(--m)); opacity: 0; }
}
.sh-tag {
  position: absolute; top: 0; left: 50%; translate: -50% -55%; padding: 2px 10px; border-radius: 999px;
  background: var(--coral); color: var(--plum); font: 700 .875rem/1.3 var(--f-ui); white-space: nowrap; pointer-events: none;
}
.sh-item.is-ok .sh-tag { background: var(--lime); color: var(--plum); }

/* In-game card over the header and the field */
.sh-ov { z-index: 6; overflow: auto; border-radius: clamp(18px, 2vw, 28px); }
.sh-ov[hidden], .sh-ov .btn[hidden] { display: none; }
.sh-ov .game-ov-card { margin: auto; }
.sh-keys, .sh-left { color: var(--muted); font-size: max(.9em, .875rem); }   /* never under 14px */
@media (hover: none) { .sh-keys { display: none; } }
.sh-answers { margin: 0; padding: 0; list-style: none; text-align: left; display: flex; flex-direction: column; gap: 6px; }
.sh-answers li { padding: 6px 12px; border-radius: 14px; background: var(--surface-2); }
.sh-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; margin: 0; }

/* Phones: a smaller header */
@media (max-width: 600px) {
  .sh-hud .hud-item { min-height: 36px; padding: 4px 10px; font-size: var(--fs-cap); }
  .sh-hud .hud-life { width: 18px; height: 18px; }
  .sh-hud .sh-pause { min-width: 44px; min-height: 44px; }
}
/* Short landscape screens: header in one row, the activity instruction hidden while playing (the start card explains) */
@media (max-height: 520px) and (orientation: landscape) {
  .sh { gap: 6px; min-height: 200px; }
  .sh-task { flex: 1 1 auto; }
  .sh-task-k { display: none; }
  .sh-task-t { font-size: var(--fs-sm); }
  .sh-hud { gap: 4px; } /* task and header share one row when they fit (larger text sizes wrap) */
  .sh-hud .hud-item { min-height: 34px; padding: 4px 9px; font-size: var(--fs-cap); }
  .sh-hud .hud-life { width: 16px; height: 16px; }
  .sh-hud .sh-pause { min-width: 44px; min-height: 44px; }
  .ix:has(.sh) .ix-ins { display: none; }
  .ix:has(.sh) .ix-meta { margin-bottom: 4px; }
  .ix:has(.sh) .ix-q { font-size: calc(1rem * var(--tsh)); }
  .ix:has(.sh) .ix-body { padding-top: 8px; }
  .sh-ov { padding: 8px; }
  .sh-ov .game-ov-card { gap: 8px; padding: 12px 16px; }
  .sh-ov .game-ov-title { font-size: calc(1.375rem * var(--tsh)); }
  .sh-ov .game-ov-text { font-size: var(--fs-cap); line-height: 1.35; }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .sh-item, :root[data-motion="reduce"] .sh-item.is-ok::after, :root[data-motion="reduce"] .sh-item.is-bad, :root[data-motion="reduce"] .sh-hud .hud-lives.is-hurt { animation: none; }
:root[data-motion="reduce"] .sh-item.is-ok { animation: sh-pop var(--d-l) ease-out forwards; }   /* --m is 0: only the fade out */
:root[data-motion="reduce"] .sh-nozzle { transition: none; }

/* ---------- whack ---------- */
/* Game header (.hud pieces) on top, the field with six pipes below taking the free height. whack.js sets
   .is-row (one row of six: wide, short fields) or .is-tall (2 x 3: narrow fields) on the field, and --wm-fs
   (card font, fitted to the longest practice). Pipes: dark opening + pipe body in tokens; cards slide up out of them. */
.ix-body > .wm { display: flex; flex-direction: column; gap: clamp(8px, 1.2vw, 16px); min-width: 0; }
.wm-hud { flex: none; justify-content: space-between; }
.wm-prompt {
  flex: 1 1 260px; display: flex; align-items: center; gap: 10px; margin: 0; min-width: 0;
  font: 700 var(--fs-body)/1.25 var(--f-ui); color: var(--text);
}
.wm-prompt::before { content: ""; flex: none; width: .62em; height: .62em; border-radius: 50%; background: var(--lilac); }
.wm-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.wm-pause { width: 44px; height: 44px; }
.wm-pause svg { width: 20px; height: 20px; }
.wm-pause:disabled { opacity: .4; cursor: default; }

.wm-field {
  position: relative; flex: 1 0 200px; min-height: 200px; outline: none;
  border: 2px solid var(--line); border-radius: clamp(20px, 2.4vw, 30px); background: var(--bg);
  touch-action: none; -webkit-user-select: none; user-select: none; overflow: hidden;
}
.wm-field:focus-visible { outline: none; box-shadow: inset 0 0 0 3px var(--focus); border-radius: 14px; }   /* keyboard focus on the field: keys 1–6 hit the pipes */
.wm-grid {
  position: absolute; inset: clamp(10px, 1.6vw, 22px) clamp(10px, 2vw, 32px) clamp(6px, 1vw, 14px);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: clamp(8px, 1.4vw, 20px) clamp(10px, 2.4vw, 40px); max-width: 1100px; margin: 0 auto;
}
.wm-field.is-row .wm-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); grid-template-rows: minmax(0, 1fr); max-width: none; column-gap: clamp(6px, 1.2vw, 18px); }
.wm-field.is-tall .wm-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); column-gap: 10px; }
.wm-slot { position: relative; display: flex; align-items: flex-end; justify-content: center; min-width: 0; min-height: 0; }
.wm-box { position: relative; height: 100%; max-height: 300px; aspect-ratio: 1.45; max-width: 100%; }
.wm-field.is-tall .wm-box { aspect-ratio: 1.2; }
.wm-field.is-sq .wm-grid { column-gap: 8px; }
.wm-field.is-sq .wm-box { aspect-ratio: 1; }
.wm-field.is-row .wm-slot { align-items: center; }
.wm-field.is-row .wm-box { width: 100%; height: auto; aspect-ratio: .9; max-height: 100%; }

/* Pipe: opening (behind the card) and body + front rim (in front of it); 30% of the box at the bottom */
.wm-back, .wm-front { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: 30%; min-height: 36px; overflow: visible; pointer-events: none; }
.wm-back { z-index: 1; }
.wm-front { z-index: 3; }
.wm-hole { fill: var(--stage); stroke: var(--lilac); stroke-width: 4; }
.wm-body { fill: var(--buf); }
.wm-rim { fill: none; stroke: var(--lilac); stroke-width: 4; }
/* Card window: ends at the middle of the opening, so a hidden card is out of sight */
.wm-win { position: absolute; z-index: 2; top: 0; left: 5%; right: 5%; bottom: max(18%, 22px); overflow: hidden; }

.wm-card {
  appearance: none; position: absolute; left: 0; right: 0; bottom: 0; height: 94%; margin: 0;
  display: flex; align-items: center; justify-content: center; padding: 8px 8px 12px;
  border: 3px solid var(--buf); border-bottom-width: 0; border-radius: 16px 16px 6px 6px;
  background: var(--surface); color: var(--text); text-align: center;
  font: 700 var(--wm-fs, 16px)/1.22 var(--f-ui); overflow: hidden;
  transform: translateY(104%); visibility: hidden; transform-origin: 50% 100%;
  transition: transform var(--d-s) var(--e-in), visibility 0s var(--d-s), background-color .15s, border-color .15s;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
/* Up: rises out of the pipe with a springy stretch (keyframes, so it never overshoots out of the window); down: sinks back */
.wm-card.is-up { transform: translateY(0); visibility: visible; transition: transform var(--d-s) var(--e-in), visibility 0s, background-color .15s, border-color .15s; animation: wm-up var(--d-m) var(--e-out); }
@keyframes wm-up {
  from { transform: translateY(104%) scale(calc(1 - .1 * var(--m)), calc(1 + .1 * var(--m))); }
  60% { transform: translateY(-3%) scale(calc(1 - .03 * var(--m)), calc(1 + .03 * var(--m))); }
  82% { transform: translateY(1%) scale(calc(1 + .04 * var(--m)), calc(1 - .04 * var(--m))); }
}
.wm-front { transform-origin: 50% 100%; }   /* whack.js squashes the pipe as a card pops out */
[data-theme="dark"] .wm-card { background: var(--surface-2); }
.wm-t { display: block; width: 100%; overflow-wrap: break-word; hyphens: manual; }
.wm-measure { visibility: hidden !important; transform: none !important; transition: none; pointer-events: none; }
.wm-measure .wm-t { overflow-wrap: normal; }
@media (hover: hover) { .wm.is-running .wm-card.is-up:not(.is-hit):not(.is-bad):hover { border-color: var(--lilac); background: var(--surface-2); } }
.wm-card:focus-visible { outline: 3px solid var(--focus); outline-offset: -6px; }
.wm-card.is-hit, .wm-card.was-hit { border-color: var(--ok-line); background: var(--ok-tint); }
.wm-card.is-bad, .wm-card.was-bad { border-color: var(--try-line); background: var(--try-tint); }
.wm-card.is-bad .wm-t { animation: shake .42s; }
.wm-badge { position: absolute; top: 4px; right: 4px; display: none; place-items: center; width: 22px; height: 22px; border-radius: 50%; }
.wm-badge svg { width: 62%; height: 62%; }
.wm-card.is-hit .wm-badge, .wm-card.was-hit .wm-badge { display: grid; background: var(--lime); color: var(--plum); animation: m-in-pop var(--d-m) var(--spring-big); }
.wm-card.is-bad .wm-badge, .wm-card.was-bad .wm-badge { display: grid; background: var(--coral); color: var(--plum); animation: m-in-pop var(--d-m) var(--spring); }
/* A right tap squashes the card (`scale`, so the rise keeps running); a paused game stops the card animations */
.wm-card.is-up.is-hit { animation: wm-up var(--d-m) var(--e-out), wm-squash var(--d-l) var(--e-out); }
@keyframes wm-squash {
  25% { scale: calc(1 + .1 * var(--m)) calc(1 - .14 * var(--m)); }
  50% { scale: calc(1 - .05 * var(--m)) calc(1 + .06 * var(--m)); }
  75% { scale: calc(1 + .02 * var(--m)) calc(1 - .02 * var(--m)); }
}
.wm.is-paused .wm-card, .wm.is-paused .wm-t, .wm.is-paused .wm-badge { animation-play-state: paused; }
.wm:not(.is-running) .wm-card, .wm.is-ending .wm-card { cursor: default; }

/* Finished (won / no replays left): only the end card, so it is not cut when the footer opens */
.wm.is-locked .wm-field { flex: 1 0 auto; min-height: 0; display: grid; }
.wm.is-locked .wm-grid { display: none; }
.wm.is-locked .wm-ov { position: relative; inset: auto; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; }

/* Number key on the pipe body (keyboard screens only) */
.wm-key {
  position: absolute; z-index: 4; left: 50%; bottom: 0; height: 9.6%; min-height: 12px; display: none; align-items: center;
  translate: -50% 0; color: var(--text); opacity: .7; font: 700 clamp(12px, .9vw, 14px)/1 var(--f-ui); pointer-events: none;
}
@media (hover: hover) { .wm-key { display: flex; } }

/* In-game screens (start, paused, game over, won) */
.wm-ov { z-index: 6; touch-action: auto; overflow: auto; }
.wm-ov .game-ov-card { margin: auto; } /* taller than the field (large text): scrolls from the top instead of being cut */
.wm-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Phones: smaller header; the prompt on its own line */
@media (max-width: 600px) {
  .wm-hud { gap: 8px; }
  .wm-prompt { flex: 1 1 0; }
  .wm-stats { order: 2; flex: 1 1 100%; justify-content: space-between; gap: 6px; }
  .wm-stats .hud-item { min-height: 36px; padding: 5px 10px; }
  .wm-stats .hud-life { width: 19px; height: 19px; }
  .wm-card { padding: 6px 5px 10px; }
}
/* Short portrait phones: compact header, wider boxes and lower pipes, so the cards keep room for 2–3 lines */
@media (max-width: 600px) and (max-height: 760px) and (orientation: portrait) {
  .ix-body > .wm { gap: 6px; }
  .wm-hud { gap: 6px; }
  .wm-prompt { font-size: var(--fs-sm); }
  .wm-stats { gap: 4px; }
  .wm-stats .hud-item { min-height: 32px; padding: 3px 8px; gap: 5px; }
  .wm-stats .hud-life { width: 16px; height: 16px; }
  .wm-field { min-height: 170px; flex-basis: 170px; }
  .wm-grid { inset: 6px 8px 4px; row-gap: 6px; }
  .wm-field.is-tall .wm-box { aspect-ratio: 1.6; }
  .wm-back, .wm-front { min-height: 22px; }
  .wm-win { bottom: max(16%, 14px); }
}
/* Short landscape screens: tight header so the field keeps its height */
@media (max-height: 520px) and (orientation: landscape) {
  .ix-body > .wm { gap: 6px; }
  .wm-prompt { font-size: var(--fs-sm); }
  .wm-stats .hud-item { min-height: 34px; padding: 4px 10px; }
  .wm-field { min-height: 170px; flex-basis: 170px; }
  .wm-grid { inset: 8px 10px 4px; }
  .wm-card { padding: 5px 4px 9px; border-radius: 12px 12px 5px 5px; }
  /* as in shooter: the activity instruction hidden (the start card explains), a smaller question */
  .ix:has(.wm) .ix-ins { display: none; }
  .ix:has(.wm) .ix-q { font-size: calc(1rem * var(--tsh)); }
}
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"] .wm-card, :root[data-motion="reduce"] .wm-card.is-up { transition: background-color .15s, border-color .15s; }
:root[data-motion="reduce"] .wm-card.is-bad .wm-t, :root[data-motion="reduce"] .wm-badge, :root[data-motion="reduce"] .wm-card.is-up { animation: none !important; }

/* ---------- Dialogs ---------- */
.dlg {
  width: min(calc(100vw - 24px), 480px); max-width: none; max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px);
  padding: 0; overflow: auto; overscroll-behavior: contain;
  border: 2px solid var(--line); border-radius: clamp(20px, 3vw, 32px); background: var(--surface); color: var(--text);
  box-shadow: var(--hi), var(--shadow-2);
}
.dlg[open] { animation: pop var(--d-l) var(--spring); }
/* --scrim-strong, written out: ::backdrop cannot read custom properties before Safari 17.4 / Chrome 122 / Firefox 120,
   and a var() there would leave no backdrop at all */
.dlg::backdrop { background: rgba(15, 13, 19, .82); }
.dlg[open]::backdrop { animation: fade var(--d-m) var(--e-out); }
[data-theme="dark"] .dlg::backdrop { background: rgba(10, 8, 13, .88); }
/* Closing copies (UI.leave in common.js): a dialog box and its backdrop, the panel, pop-ups, the safety warning */
.m-ghost, .m-ghost * { pointer-events: none !important; animation: none !important; transition: none !important; }
.dlg-scrim { position: fixed; inset: 0; z-index: 999; background: var(--scrim-strong); }
.dlg.dlg-wide { width: min(calc(100vw - 24px), 840px); }
.dlg-box { padding: clamp(20px, 3.4vw, 40px); display: flex; flex-direction: column; gap: 14px; }
.dlg-title { margin: 0; font: 400 calc(clamp(1.625rem, 3.4vw, 2.5rem) * var(--tsh))/1.05 var(--f-display); text-transform: uppercase; }
.dlg-box p { margin: 0; }
.dlg-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; }
.field { display: flex; flex-direction: column; gap: 8px; font: 700 var(--fs-sm)/1.2 var(--f-ui); }
.field input {
  min-height: 50px; padding: 10px 18px; border: 2px solid var(--opt-line); border-radius: 18px; background: var(--opt-bg);
  font: 500 var(--fs-body)/1.3 var(--f-body); transition: border-color .15s;
}
.field input:focus { border-color: var(--focus); outline: none; }

/* Results */
.res { display: flex; flex-direction: column; gap: 16px; }
.res-head { display: flex; align-items: center; gap: clamp(12px, 2vw, 20px); }
.res-badge {
  flex: none; display: grid; place-items: center; width: clamp(54px, 7vw, 80px); aspect-ratio: 1; border-radius: 50%;
  background: var(--lime); color: var(--plum); animation: m-in-pop var(--d-xl) var(--spring-big) .1s both;
}
.res-badge svg { width: 56%; height: 56%; fill: currentColor; }
.res-head > div { min-width: 0; }
.res-meta { margin-top: 6px !important; color: var(--muted); font-size: var(--fs-sm); word-break: break-word; }
.stars { display: flex; flex-wrap: wrap; gap: 8px; }
.star { width: clamp(26px, 3vw, 38px); height: auto; aspect-ratio: 1; fill: var(--surface-3); stroke: var(--opt-line); stroke-width: 1.5; stroke-linejoin: round; }   /* entrance: Results.animate */
.star.on { fill: var(--lime); stroke: var(--ok-line); }
.res-wrap { overflow-x: auto; border: 2px solid var(--line); border-radius: 20px; -webkit-overflow-scrolling: touch; }
.res-table { width: 100%; min-width: 580px; border-collapse: collapse; font-size: var(--fs-sm); }
.res-table th, .res-table td { padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--line); }
.res-table tbody tr:last-child td { border-bottom: 0; }
.res-table th { background: var(--surface-2); color: var(--muted); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }
.res-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.st { font: 700 var(--fs-cap)/1.3 var(--f-ui); }
.st-passed { color: var(--ok-ink); }
.st-completed { color: var(--accent-text); }
.st-revealed { color: var(--shown-ink); }
.st-notdone { color: var(--muted); }
.res-tot { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.res-tot p { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 12px 16px; border-radius: 18px; background: var(--bg); color: var(--muted); font: 600 var(--fs-cap)/1.3 var(--f-ui); }
.res-tot strong { color: var(--accent-text); font: 400 calc(1.875rem * var(--tsh))/1 var(--f-display); text-transform: uppercase; }

/* Episodes list (from the bottom bar) */
.eps-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.eps-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
/* Rows, not cards: no outline until hovered (a tint) or current (tint + grape ring) */
.eps-item {
  display: flex; align-items: center; gap: 14px; padding: 10px 14px 10px 10px; text-decoration: none; color: inherit;
  border: 2px solid transparent; border-radius: 20px; background: transparent;
  transition: border-color var(--d-s), background-color var(--d-s), transform var(--d-s) var(--spring);
}
@media (hover: hover) { .eps-item:hover { background: var(--surface-2); } }
.eps-item:active { transform: scale(calc(1 - .02 * var(--m))); transition-duration: var(--d-xs); }
.eps-item.is-current, .eps-item.is-current:hover { border-color: var(--active-line); background: var(--active-bg); }
.eps .ep-num { width: 46px; font-size: 21px; }
.eps-item.is-current .ep-num { background: linear-gradient(180deg, var(--grape-hi), var(--grape)); color: #fff; box-shadow: inset 0 -2px 0 var(--grape-deep); }
.eps-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.eps-name { font: 700 var(--fs-body)/1.3 var(--f-ui); }
.eps-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 4px; color: var(--muted); font-size: var(--fs-cap); }
.tag.current { background: var(--grape); color: #fff; }

/* Activity list (the progress chip) and the Practise buttons it shares with the results table */
.acts-hint { margin: 0; font-size: var(--fs-sm); }
.acts-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.acts-item { display: flex; align-items: center; gap: 14px; padding: 8px 10px; border-radius: 20px; }
.acts-item:nth-child(odd) { background: var(--bg); }
.acts .ep-num { width: 46px; font-size: 21px; }
.acts .eps-txt .st { margin-top: 2px; }
.practise-btn svg { width: 20px; height: 20px; margin-left: 2px; }   /* the triangle's weight sits left of centre */
.res-table .res-play { width: 1%; padding-block: 6px; padding-inline: 4px 10px; }

/* ---------- Pop-ups: sound, settings ---------- */
.audio-pop, .pop {
  position: fixed; z-index: 60; width: min(310px, calc(100vw - 16px)); padding: 14px; display: flex; flex-direction: column; gap: 12px;
  border: 2px solid var(--line); border-radius: 22px; background: var(--surface); color: var(--text);
  box-shadow: var(--hi), var(--shadow-2); animation: pop var(--d-m) var(--spring);
}
.arow { display: flex; align-items: center; gap: 12px; }
.arow label { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; font: 600 var(--fs-cap)/1.2 var(--f-ui); color: var(--muted); }
.arow input { width: 100%; height: 28px; margin: 0; padding: 0; -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; }
.arow input::-webkit-slider-runnable-track { height: 8px; border-radius: 99px; background: linear-gradient(90deg, var(--progress) var(--v, 100%), var(--progress-track) var(--v, 100%)); }
.arow input::-moz-range-track { height: 8px; border-radius: 99px; background: linear-gradient(90deg, var(--progress) var(--v, 100%), var(--progress-track) var(--v, 100%)); }
.arow input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; box-sizing: border-box; width: 22px; height: 22px; margin-top: -7px;
  border: 4px solid var(--surface); border-radius: 50%; background: var(--progress); box-shadow: var(--shadow-knob);
}
.arow input::-moz-range-thumb {
  box-sizing: border-box; width: 22px; height: 22px;
  border: 4px solid var(--surface); border-radius: 50%; background: var(--progress); box-shadow: var(--shadow-knob);
}
.arow input:focus-visible { outline: none; }
.arow input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus); }
.arow input:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus); }

/* Settings: text size (A / A+ / A++), theme, reduce motion */
.set-pop { width: min(350px, calc(100vw - 16px)); max-height: calc(100vh - 16px); max-height: calc(100dvh - 16px); overflow: auto; gap: 14px; padding: 16px; }
.set-h { margin: 0; font: 400 calc(1.25rem * var(--tsh))/1.1 var(--f-display); text-transform: uppercase; }
.set-g { display: flex; flex-direction: column; gap: 6px; min-width: 0; margin: 0; padding: 0; border: 0; }
.set-l { padding: 0 0 0 4px; font: 600 var(--fs-cap)/1.2 var(--f-ui); color: var(--muted); }
/* Segmented choice: radio buttons in a soft track, the chosen one grape (like the play button) */
.seg { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 4px; padding: 4px; border-radius: 999px; background: var(--surface-2); }
.seg-o { position: relative; display: flex; min-width: 0; }
.seg-o input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.seg-f {
  flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; padding: 6px 8px;
  border-radius: 999px; color: var(--text); font: 700 var(--fs-cap)/1.1 var(--f-ui); white-space: nowrap; pointer-events: none;
  transition: background-color var(--d-s), color var(--d-s), box-shadow var(--d-s);
}
.seg-f svg { width: 18px; height: 18px; color: var(--accent-text); }
.seg-f > span { overflow: hidden; text-overflow: ellipsis; }
/* The A buttons show the sizes themselves, so they do not grow with the setting */
.seg-a { font-size: 15px; line-height: 1; }
.seg-w > span { white-space: normal; text-align: center; line-height: 1.15; }   /* word choices (game speed) may wrap */
/* Word labels (theme, game speed) shrink a little when the longest word would not fit its button (A++, narrow phones) */
.seg { container-type: inline-size; }
.seg-f:not(.seg-a) { font-size: min(var(--fs-cap), 5.5cqi); }
.set-d { margin: 6px 4px 0; color: var(--muted); font-size: var(--fs-cap); line-height: 1.3; }
.seg-o:nth-child(2) .seg-a { font-size: 19px; }
.seg-o:nth-child(3) .seg-a { font-size: 23px; }
@media (hover: hover) { .seg-o:hover input:not(:checked) + .seg-f { background: var(--surface); box-shadow: inset 0 0 0 2px var(--lilac); } }
.seg-o input:checked + .seg-f { background: linear-gradient(180deg, var(--grape-hi), var(--grape)); color: #fff; box-shadow: inset 0 -3px 0 var(--grape-deep), inset 0 1px 0 rgba(255, 255, 255, .18); }
.seg-o input:checked + .seg-f svg { color: #fff; }
.seg-o input:focus-visible + .seg-f { outline: 3px solid var(--focus); outline-offset: 2px; }
/* On / off switch */
.set-sw { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 4px 0; border-top: 1px solid var(--line); cursor: pointer; }
.set-sw-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.set-sw-k { font: 700 var(--fs-sm)/1.2 var(--f-ui); }
.set-sw-d { color: var(--muted); font-size: var(--fs-cap); line-height: 1.3; }
.set-sw input {
  -webkit-appearance: none; appearance: none; position: relative; flex: none; width: 54px; height: 32px; margin: 0;
  border-radius: 999px; background: var(--surface-3); box-shadow: inset 0 0 0 2px var(--opt-line); cursor: pointer;
  transition: background-color var(--d-s), box-shadow var(--d-s);
}
.set-sw input::after {
  content: ""; position: absolute; top: 4px; left: 4px; width: 24px; height: 24px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-knob); transition: transform var(--d-m) var(--spring);
}
.set-sw input:checked { background: linear-gradient(180deg, var(--grape-hi), var(--grape)); box-shadow: inset 0 -2px 0 var(--grape-deep); }
.set-sw input:checked::after { transform: translateX(22px); }
.set-sw input:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.set-clear {
  display: block; width: 100%; min-height: 44px; margin-top: 10px; padding: 10px 4px 0; border: 0; border-top: 1px solid var(--line);
  background: none; color: var(--muted); font: 700 var(--fs-sm)/1.2 var(--f-ui); text-align: left; text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
@media (hover: hover) { .set-clear:hover { color: var(--text); } }
.set-clear:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 8px; }
.dlg-box .dlg-note { margin: -4px 0 0; color: var(--muted); font-size: var(--fs-sm); }
/* Progress cannot be saved (storage full or blocked) */
.save-warn {
  position: fixed; z-index: 1000; left: 50%; top: max(12px, env(safe-area-inset-top)); transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px; width: min(560px, calc(100% - 24px)); padding: 10px 10px 10px 16px;
  border: 2px solid var(--line); border-radius: 18px; background: var(--surface); color: var(--text); box-shadow: var(--hi), var(--shadow-2);
  font: 600 var(--fs-sm)/1.4 var(--f-ui); animation: pop var(--d-l) var(--spring);
}
.save-warn span { flex: 1; }
.save-warn .icon-btn { flex: none; width: 44px; height: 44px; }

/* ---------- Episode menu ---------- */
.menu { width: 100%; max-width: 900px; padding: 0 0 40px; }
.hero { padding: clamp(8px, 3vw, 36px) 0 clamp(18px, 3vw, 32px); }
.hero-title { margin: 0 0 .25em; font: 400 calc(clamp(2.375rem, 7.4vw, 5.75rem) * var(--tsh))/.95 var(--f-display); text-transform: uppercase; }
.menu-h { margin: 0; font: 700 var(--fs-lead)/1.3 var(--f-ui); color: var(--muted); }
.ep-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: clamp(10px, 1.4vw, 16px); }
.ep-card {
  display: flex; align-items: center; gap: clamp(12px, 2vw, 20px); padding: 12px clamp(14px, 2vw, 22px) 12px 12px;
  text-decoration: none; color: inherit;
  border: 2px solid var(--line); border-radius: clamp(22px, 3vw, 32px); background: var(--surface); box-shadow: var(--hi), var(--shadow-1);
  transition: border-color var(--d-s), background-color var(--d-s), transform var(--d-s) var(--spring);
}
@media (hover: hover) { .ep-card:hover { border-color: var(--lilac); background: var(--surface-2); transform: translateY(calc(-2px * var(--m))); } }
.ep-card:active { transform: scale(calc(1 - .015 * var(--m))); transition-duration: var(--d-xs); }
.ep-num {
  flex: none; display: grid; place-items: center; width: clamp(46px, 5.6vw, 64px); aspect-ratio: 1; border-radius: 50%;
  background: var(--letter-bg); color: var(--letter-text); font: 400 clamp(20px, 2.4vw, 28px)/1 var(--f-display); text-transform: uppercase;
}
.ep-card.completed .ep-num { background: var(--done-bg); color: var(--done-ink); }
.ep-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ep-name { font: 700 var(--fs-lead)/1.3 var(--f-ui); }
.ep-sub, .ep-stat { color: var(--muted); font-size: var(--fs-sm); }
.ep-stat { font-variant-numeric: tabular-nums; }
.ep-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.tag {
  display: inline-flex; align-items: center; gap: .4em; padding: 5px 11px; border-radius: 999px;
  background: var(--surface-2); color: var(--accent-text); font: 700 var(--fs-cap)/1.2 var(--f-ui); letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
}
.tag.inprogress { background: var(--info-tint); color: var(--info-ink); }
.tag.completed { background: var(--surface-3); }
.tag.completed::before { content: "\2713"; }
/* Looks like a button; the whole card is the link. Outlined, except on the suggested next episode (menu.js .is-next):
   the one amber on the page */
.ep-go {
  display: inline-flex; align-items: center; gap: .4em; padding: 7px 16px; border-radius: 999px; white-space: nowrap;
  border: 2px solid var(--outline-line); background: var(--outline-bg); color: var(--text); font: 700 var(--fs-sm)/1.1 var(--f-ui); box-shadow: var(--hi);
}
.ep-go::after { content: "\203A"; font-size: 1.3em; line-height: .8; }
.ep-card.is-next .ep-go {
  border-color: transparent; padding-bottom: 9px; background: linear-gradient(180deg, var(--amber-hi), var(--amber)); color: var(--plum);
  box-shadow: inset 0 -3px 0 var(--amber-edge), inset 0 1px 0 rgba(255, 255, 255, .45), 0 8px 20px -10px rgba(255, 159, 28, .8);
}

/* ---------- Effects ---------- */
.fx-canvas { position: fixed; inset: 0; z-index: 1100; pointer-events: none; }   /* Motion.confetti / celebrate */
/* In the top layer (a popover, motion.js): drop the browser's popover box */
.fx-canvas[popover] { margin: 0; padding: 0; border: 0; background: transparent; overflow: visible; }
/* ---------- Motion keyframes. Every movement is multiplied by --m, so reduced motion keeps only the fades. ---------- */
@keyframes fade { from { opacity: 0; } }
@keyframes pop { from { opacity: 0; transform: translateY(calc(14px * var(--m))) scale(calc(1 - .08 * var(--m))); } }
/* Decaying shake: a firm "no" that settles quickly */
@keyframes shake {
  15% { transform: translateX(calc(-9px * var(--m))) rotate(calc(-1.5deg * var(--m))); }
  30% { transform: translateX(calc(7px * var(--m))) rotate(calc(1deg * var(--m))); }
  45% { transform: translateX(calc(-5px * var(--m))); }
  60% { transform: translateX(calc(3px * var(--m))); }
  75% { transform: translateX(calc(-1.5px * var(--m))); }
}
@keyframes m-in-pop { from { opacity: 0; transform: scale(calc(1 - .45 * var(--m))); } }
@keyframes m-in-rise { from { opacity: 0; transform: translateY(calc(22px * var(--m))) scale(calc(1 - .04 * var(--m))); } }
@keyframes m-in-drop { from { opacity: 0; transform: translateY(calc(-22px * var(--m))) scale(calc(1 - .04 * var(--m))); } }
@keyframes m-out-sink { to { opacity: 0; transform: translateY(calc(12px * var(--m))) scale(calc(1 - .06 * var(--m))); } }
@keyframes m-out-fade { to { opacity: 0; } }
/* Squash and stretch: the "yes!" of a correct answer */
@keyframes m-jelly {
  0% { transform: scale(1, 1); }
  25% { transform: scale(calc(1 + .18 * var(--m)), calc(1 - .14 * var(--m))); }
  45% { transform: scale(calc(1 - .1 * var(--m)), calc(1 + .1 * var(--m))); }
  65% { transform: scale(calc(1 + .05 * var(--m)), calc(1 - .04 * var(--m))); }
  100% { transform: scale(1, 1); }
}
@keyframes m-pulse { 50% { transform: scale(calc(1 + .07 * var(--m))); } }
@keyframes m-wiggle {
  20% { transform: rotate(calc(-7deg * var(--m))); } 40% { transform: rotate(calc(6deg * var(--m))); }
  60% { transform: rotate(calc(-4deg * var(--m))); } 80% { transform: rotate(calc(2deg * var(--m))); }
}
/* An expanding ring behind something that just succeeded (put on a ::after) */
@keyframes m-ring { from { opacity: .7; transform: scale(1); } to { opacity: 0; transform: scale(calc(1 + .9 * var(--m))); } }
@keyframes m-shimmer { from { transform: translateX(-120%) skewX(-18deg); } to { transform: translateX(220%) skewX(-18deg); } }
/* Reduced motion (device setting or Settings > Reduce motion; boot.js / common.js set data-motion for both) */
:root[data-motion="reduce"], :root[data-motion="reduce"] *, :root[data-motion="reduce"] *::before, :root[data-motion="reduce"] *::after { scroll-behavior: auto !important; }

/* ---------- Responsive ---------- */
/* Bottom bar layout by the bar's own width (also right in landscape and full screen) */
@container (max-width: 1060px) { .now .chip { display: none; } }
@container (max-width: 780px) {
  .play-btn { width: 48px; height: 48px; }
  .controls .icon-btn { width: 44px; height: 44px; }
  .controls .chip-btn { max-width: 140px; padding: 8px 12px; }
}
/* Narrow (phones, landscape phones, small windows): time | transport | sound + full screen, then a tab bar */
@container (max-width: 640px) {
  .bar {
    display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-areas: "time trans media" "tabs tabs tabs"; align-items: center; gap: 6px 4px;
  }
  .bar > .now { display: none; }
  .time { grid-area: time; justify-self: start; margin: 0; font-size: calc(.875rem * var(--tsh)); }
  .g-trans { grid-area: trans; display: flex; align-items: center; gap: 4px; }
  #backBtn { order: 1; } #playBtn { order: 2; margin: 0 2px; } #fwdBtn { order: 3; }
  .g-media { grid-area: media; display: flex; justify-content: flex-end; gap: 2px; }
  .g-tabs {
    grid-area: tabs; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 2px;
    margin: 2px 0 0; padding-top: 6px; border-top: 1px solid var(--line);
  }
  .controls .g-tabs > button {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    width: auto; height: auto; min-width: 0; max-width: none; min-height: 54px; padding: 6px 1px;
    border: 0; border-radius: 14px; background: transparent; box-shadow: none; color: var(--accent-text);
    font: 700 calc(.875rem * var(--tsh))/1.1 var(--f-ui);
  }
  .controls .g-tabs > button:active { transform: none; background: var(--surface-2); }
  .controls .g-tabs > .safety-btn { color: var(--safety-ink); }
  .g-tabs svg, .controls .g-tabs .icon-btn svg { width: 22px; height: 22px; }
  .g-tabs .lbl, .g-tabs button > span { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
  /* Name tab: person icon instead of the dot */
  #nameBtn::before {
    width: 22px; height: 22px; border-radius: 0; background: var(--accent-text);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4.5 20.5a7.5 7.5 0 0 1 15 0'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4.5 20.5a7.5 7.5 0 0 1 15 0'/%3E%3C/svg%3E") center / contain no-repeat;
  }
}
@container (max-width: 380px) {
  .play-btn { width: 44px; height: 44px; }
  .controls .icon-btn { width: 44px; height: 44px; }   /* touch size kept; the icon is drawn smaller */
  .controls .icon-btn svg { width: 23px; height: 23px; }
  .time { margin: 0 2px; font-size: calc(.875rem * var(--tsh)); }
  /* Too narrow for "0:20 / 2:00" beside the buttons (it ran into back 5 s): the total goes on a second line */
  .time { display: flex; flex-direction: column; line-height: 1.2; }
  .controls .chip-btn { padding: 8px 10px; font-size: calc(.875rem * var(--tsh)); }
}
@media (max-width: 640px) {
  .cta .btn { flex: 1 1 auto; }
  .top { flex-wrap: wrap; gap: 8px; padding: 8px 0; }
  .top .ep-title { order: 3; flex-basis: 100%; white-space: normal; font-size: calc(1.3125rem * var(--tsh)); }
  .chip-btn { max-width: 34vw; padding: 8px 12px; }
  .controls { padding: 6px 10px 10px; }
  .ix-meta { padding-right: 52px; }
  .panel-card:has(.panel-audio:not([hidden])) .ix-meta { padding-right: 100px; }
  .panel-card:has(.panel-x:not([hidden])) .ix-meta { padding-right: 100px; }
  .panel-card:has(.panel-x:not([hidden])):has(.panel-audio:not([hidden])) .ix-meta { padding-right: 148px; }
  .ix-foot { flex-direction: column; align-items: stretch; }
  .ix-act { width: 100%; margin: 0; }
  /* Stacked: the feedback (or the instruction) takes the room above the full-width button */
  .ix-check .ix-foot { height: calc(var(--fb-text) + 2 * var(--fb-pad) + 12px + max(48px, var(--fs-body) * 2.59 + 4px)); gap: 12px; }
  .ix-check .ix-fb { flex: 1 1 0; }
  .ix-foot > .ix-ins { display: flex; align-items: center; align-self: stretch; }
  .info { grid-template-columns: 1fr; grid-template-rows: none; }
  .info-detail { min-height: 0; }
  .dlg-actions .btn { flex: 1 1 auto; }
  .ep-card { flex-wrap: wrap; }
  .ep-side { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; width: 100%; padding-left: calc(clamp(46px, 5.6vw, 64px) + 12px); }
  .ep-go { margin-left: auto; }   /* stays on the right when the row wraps */
}
/* Phones: the activity card fills the screen */
@media (max-width: 600px), (max-height: 520px) and (orientation: landscape) {
  .panel { padding: 0; }
  .panel-card { width: 100%; height: 100%; max-height: none; border: 0; border-radius: 0; }
  .panel-in {
    padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) max(14px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  }
  .panel-tools { top: max(12px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right)); }
}
@media (max-width: 400px) {
  .back { padding: 8px 10px; }
  .opt { padding-right: 44px; }
  .ix-meta .chip { padding-inline: .8em; letter-spacing: .06em; }   /* stays clear of the corner buttons */
  .ep-side { padding-left: 0; }
  .tag { max-width: 100%; letter-spacing: .06em; white-space: normal; }
  .ep-go { max-width: 100%; white-space: normal; }
}
/* Short landscape screens (phones turned sideways) */
@media (max-height: 520px) and (orientation: landscape) {
  .top { padding: 4px 0; }
  .player { width: min(100%, calc((100vh - 150px) * 16 / 9)); width: min(100%, calc((100dvh - 150px) * 16 / 9)); }
  .controls { padding: 4px 12px 8px; }
  .seek { margin-block: -9px; }   /* takes 26px of height as before, still a 44px hit area */
  .bar { margin-top: 0; column-gap: 2px; }   /* 44px buttons still fit one row with every tab */
  .time { margin: 0 4px; }
  .play-btn { width: 44px; height: 44px; }
  .controls .icon-btn { width: 44px; height: 44px; }
  /* Activity frame: the content keeps as much height as possible. Top row (counter, attempts) across the card; the
     question (2 lines) and the instruction (1 line) beside the main button; tapping them shows them in full
     (.q-open, interactions.js). The footer returns under the content once there is feedback to show. */
  .ix {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto minmax(0, 1fr) auto;
    grid-template-areas: "meta meta" "q act" "ins act" "body body" "foot foot"; column-gap: 16px;
  }
  .ix-head { display: contents; }
  .ix-meta { grid-area: meta; margin-bottom: 4px; }
  .ix-q {
    grid-area: q; align-self: center; margin: 0; font-size: calc(1.125rem * var(--tsh)); line-height: 1.25;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; cursor: pointer;
  }
  .ix-ins { grid-area: ins; margin-top: 2px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; cursor: pointer; }
  .ix.q-open .ix-q { -webkit-line-clamp: none; }
  .ix.q-open .ix-ins { white-space: normal; }
  .ix-body { grid-area: body; padding-top: 8px; }
  .ix-foot { grid-area: foot; flex-direction: row; align-items: center; margin-top: 8px; padding-block: 8px; }
  .ix-fb { gap: 12px; }
  .ix-fb-icon { width: 36px; }
  .ix-fb-t { font-size: calc(1.375rem * var(--tsh)); }
  .ix-fb-l { margin-top: .25em; }
  .ix-fb-x { margin-top: .25em; line-height: 1.4; }
  .ix-act { width: auto; margin-left: auto; }
  .ix-foot:not(:has(.ix-fb.open)) { grid-area: act; align-self: center; margin: 0; padding: 0; background: none; }
  .ix-foot:not(:has(.ix-fb.open)) .ix-fb-ghost { display: none; }   /* the leaving bar copy stays out of the top row */
  .ix-foot:not(:has(.ix-fb.open)) .ix-act { min-height: 44px; padding: .5em 1.2em; }
  /* With a Semak button the bar stays under the content at one height (see .ix-check): the question gets the full width */
  .ix.ix-check { grid-template-areas: "meta meta" "q q" "ins ins" "body body" "foot foot"; }
  .ix-check .ix-foot, .ix-check .ix-foot:not(:has(.ix-fb.open)) {
    --fb-pad: 8px; --fb-text: calc(1.375rem * var(--tsh) * 1.05 + var(--fs-cap) * 1.45 + var(--fs-sm) * 3.05);
    grid-area: foot; align-self: stretch; margin: 8px 0 0; padding: 8px 10px 8px 14px;
  }
  .ix-check .ix-foot:not(:has(.ix-fb.open)) { background: var(--bg); }
  .ix-check .ix-foot { flex-direction: row; height: calc(var(--fb-text) + 2 * var(--fb-pad)); }
  .ix-check .ix-act { width: auto; min-height: 44px; padding: .5em 1.2em; }
  .ix-foot > .ix-ins { display: block; align-self: center; white-space: normal; }
  .opt { min-height: 48px; }
  .info { grid-template-columns: minmax(180px, 1fr) minmax(0, 1.5fr); }
}
