/* Shared components. Screens and activities build from these. */

/* ---------- buttons ---------- */
.btn {
  --btn-bg: var(--paper-light);
  --btn-fg: var(--ink);
  --btn-under: var(--paper-shade);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 56px;
  padding: 10px 22px;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--r);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-ui);
  font-size: var(--text-md);
  font-weight: 700;
  line-height: 1.15;
  text-decoration: none;
  letter-spacing: 0.01em;
  box-shadow: 0 var(--lift) 0 var(--btn-under);
  transform: translateY(0);
  transition: transform var(--t-micro) var(--out), box-shadow var(--t-micro) var(--out), background-color var(--t-micro);
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}
.btn:hover { --btn-bg: var(--btn-hover, var(--paper)); }
.btn:active, .btn.is-pressed {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--btn-under);
}
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}
.btn svg { flex: none; }

.btn-primary { --btn-bg: var(--yolk); --btn-hover: var(--yolk-hover); --btn-under: var(--yolk-deep); }
.btn-go { --btn-bg: var(--leaf); --btn-hover: var(--leaf-hover); --btn-fg: var(--paper-light); --btn-under: var(--leaf-deep); }
.btn-pond { --btn-bg: var(--pond); --btn-hover: var(--pond-hover); --btn-fg: var(--paper-light); --btn-under: var(--pond-deep); }
.btn-think { --btn-bg: var(--think-light); --btn-hover: var(--think-hover); --btn-under: var(--think); }
.btn-quiet {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  min-height: 48px;
  padding-inline: 12px;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
  text-decoration-color: var(--rule);
}
.btn-quiet:hover { --btn-bg: transparent; background: transparent; text-decoration-color: var(--yolk-deep); }
.btn-quiet:active { transform: translateY(1px); box-shadow: none; }

.btn-big {
  min-height: 72px;
  padding: 14px 32px;
  font-size: var(--text-lg);
  border-radius: var(--r-lg);
  --lift: 6px;
}
/* small, but still a full 48px touch target */
.btn-small { min-height: 48px; padding: 6px 14px; font-size: var(--text-sm); --lift: 3px; }
.btn-block { width: 100%; }

/* square icon button (never a pill) */
.btn-icon {
  width: 56px;
  min-width: 56px;
  height: 56px;
  min-height: 56px;
  padding: 0;
  border-radius: var(--r);
}
.btn-icon.btn-small { width: 48px; min-width: 48px; height: 48px; min-height: 48px; }

/* the "hear it" button: speaker with sound arcs */
.btn-hear { --btn-bg: var(--pond); --btn-hover: var(--pond-hover); --btn-fg: var(--paper-light); --btn-under: var(--pond-deep); }
.btn-hear.is-speaking svg .arc { animation: arc-pulse 0.9s var(--out) infinite; }
.btn-hear.is-speaking svg .arc2 { animation-delay: 0.15s; }
@keyframes arc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ---------- icons ---------- */
/* icon(name, { size }) sets --icon; screens can still size icons in their own CSS */
.icon { width: var(--icon, 26px); height: var(--icon, 26px); display: inline-block; vertical-align: middle; }
.icon-lg { width: 34px; height: 34px; }

/* ---------- sheets & wells ---------- */
.sheet {
  position: relative;
  background: var(--paper-light);
  border: var(--stroke-thin) solid var(--ink);
  border-radius: var(--r-lg);
  padding: clamp(16px, 3.5vw, 28px);
}
/* hero sheet: a second outline a hair out of register, as if printed in two passes */
.sheet-riso::before {
  content: "";
  position: absolute;
  inset: 0;
  border: var(--stroke-thin) solid var(--riso, var(--think));
  border-radius: inherit;
  transform: translate(3px, 3px);
  pointer-events: none;
  mix-blend-mode: multiply;
}
.well {
  background: var(--paper-deep);
  border-radius: var(--r-lg);
  box-shadow: inset 0 2px 0 var(--paper-shade);
  padding: 14px;
}

/* ---------- top bar ---------- */
.topbar {
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 72px;
  padding-block: 10px;
  margin-inline: calc(var(--gutter) * -1) calc(var(--gutter-r) * -1);
  padding-left: var(--gutter);
  padding-right: var(--gutter-r);
  background: var(--paper);
}
.topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: repeating-linear-gradient(90deg, var(--rule) 0 10px, transparent 10px 16px);
  opacity: 0;
  transition: opacity 0.2s;
}
.topbar.is-stuck::after { opacity: 1; }
.topbar-title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-variation-settings: var(--display-axes);
  font-size: var(--text-lg);
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar-inner { width: 100%; max-width: var(--maxw); margin-inline: auto; display: flex; align-items: center; gap: 10px; }

/* player chip */
.player-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 4px 14px 4px 4px;
  border: var(--stroke-thin) solid var(--ink);
  border-radius: var(--r);
  background: var(--paper-light);
  font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  cursor: pointer;
}
.player-chip .wordling { width: 40px; height: 40px; }

/* ---------- chips, tags, labels ---------- */
.label {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: 8px;
  border: var(--stroke-thin) solid var(--ink);
  background: var(--paper-light);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1.6;
  white-space: nowrap;
}
.tag-yolk { background: var(--yolk-light); }
.tag-leaf { background: var(--leaf-light); }
.tag-think { background: var(--think-light); }
.tag-pond { background: var(--pond-light); }

/* ---------- the word, big ---------- */
.word {
  font-family: var(--font-word);
  font-weight: 700;
  font-size: var(--word-size);
  line-height: 1.1;
  letter-spacing: var(--letter-space);
  color: var(--ink);
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  white-space: nowrap;
}
.word .letter {
  position: relative;
  display: inline-block;
  min-width: 0.3em;
}
/* tricky bit: a pink highlighter swash drawn behind the letters */
.word .letter.is-tricky::before {
  content: "";
  position: absolute;
  left: -0.03em;
  right: -0.03em;
  bottom: 0.12em;
  height: 0.42em;
  background: var(--think);
  border-radius: 0.12em 0.2em 0.1em 0.18em;
  z-index: -1;
  transform-origin: left center;
  animation: swash 280ms var(--out) both;
  animation-delay: var(--swash-delay, 0ms);
  mix-blend-mode: multiply;
}
.word { isolation: isolate; }
@keyframes swash { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.word .letter.is-drop { animation: letter-drop 360ms var(--out) both; animation-delay: var(--d, 0ms); }
@keyframes letter-drop { from { transform: translateY(-14px); opacity: 0; } to { transform: none; opacity: 1; } }
.word .letter.is-wiggle { animation: wiggle 420ms var(--out); }
@keyframes wiggle { 25% { transform: rotate(-7deg); } 60% { transform: rotate(5deg); } }
.word.is-small { font-size: clamp(1.8rem, 7vw, 3rem); }
.word .gap-blank {
  display: inline-block;
  width: 0.6em;
  border-bottom: 0.08em solid var(--think-ink);
  margin-inline: 0.04em;
}

/* chunks (spelling-voice pieces) */
.chunks {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-word);
  font-weight: 700;
  font-size: clamp(1.5rem, 5.5vw, 2.4rem);
}
.chunk {
  padding: 4px 14px 6px;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--r);
  background: var(--paper-light);
  box-shadow: 0 3px 0 var(--paper-shade);
  letter-spacing: var(--letter-space);
}
.chunk.is-tricky { background: var(--think-light); border-color: var(--think-ink); box-shadow: 0 3px 0 var(--think); }
.chunk.is-hop { animation: hop 360ms var(--spring); }
@keyframes hop { 40% { transform: translateY(-10px); } 100% { transform: none; } }

/* ---------- spelling input: worksheet lines, not boxes ---------- */
/* A word always stays on one line, however long ("occasionally" on a 360px phone): the
   letters shrink to fit the column. keyboard.js sets --len (letters to hold). --room is the
   column's width: the viewport as a fallback, the input's own width where container units
   work. An activity can still choose the size outright with --slot-size. */
.spell-input {
  --room: min(100vw - 40px, 720px);
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
}
@supports (width: 1cqi) {
  .spell-input { container-type: inline-size; }
  .spell-input > .spell-display { --room: 100cqi; }
}
.spell-display {
  --slot-gap: 6px;
  --slot-fit: calc((var(--room) - 8px - (var(--len, 8) - 1) * var(--slot-gap)) / (var(--len, 8) * 0.82));
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  justify-content: safe center;
  align-items: flex-end;
  gap: var(--slot-gap);
  min-height: calc(var(--word-size) * 1.2);
  padding-inline: 4px;
  font-family: var(--font-word);
  font-weight: 700;
  font-size: var(--slot-size, min(clamp(2rem, 8vw, 3.6rem), max(1rem, var(--slot-fit))));
  line-height: 1.15;
}
.spell-display.is-long { --slot-gap: 4px; }
/* the free line has no gaps between letters: it fits about one letter per 0.62em */
.spell-display.is-line { --slot-fit: calc((var(--room) - 32px) / (var(--len, 8) * 0.62)); }
.spell-display .slot {
  position: relative;
  display: inline-flex;
  justify-content: center;
  align-items: flex-end;
  min-width: 0.78em;
  height: 1.3em;
  padding-bottom: 0.08em;
  border-bottom: 3px solid var(--ink-soft);
  color: var(--ink);
}
.spell-display.is-line .slot { border-bottom-color: transparent; min-width: 0; padding-inline: 0.02em; }
.spell-display.is-line { gap: 0; border-bottom: 3px solid var(--ink-soft); min-width: min(16ch, 80%); padding-inline: 0.3em; }
.spell-display .slot.is-tricky { border-bottom-color: var(--think-ink); }
.spell-display .slot.is-space { border-bottom-style: dashed; }
/* Where the next letter goes: a softly lit slot whose line is drawn thick, blinking.
   (An upright bar inside a slot reads as l or I to early readers, so there isn't one.) */
.spell-display .slot.is-next { background: var(--yolk-light); border-radius: 8px 8px 0 0; }
.spell-display .slot.is-next::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 6px;
  border-radius: 3px;
  background: var(--ink);
  animation: caret 1.2s steps(1) infinite;
}
/* the free line (tests): an underscore after the last letter, not an upright bar */
.spell-display .caret {
  display: block;
  align-self: flex-end;
  flex: none;
  width: 0.5em;
  height: 6px;
  margin: 0 0 0.1em 0.06em;
  border-radius: 3px;
  background: var(--ink);
  animation: caret 1.2s steps(1) infinite;
}
@keyframes caret { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0.25; } }
.spell-display .slot-hint { color: var(--ink-faint); opacity: 0.55; }
.spell-display .slot.is-ghost { color: var(--think-ink); opacity: 0.35; }
.spell-display .slot.is-right { border-bottom-color: var(--leaf); color: var(--leaf); }
.spell-display .slot.is-wrong { border-bottom-color: var(--think-ink); }
.spell-display .slot.is-wrong::before {
  content: "";
  position: absolute;
  inset: 0.18em -0.06em 0.06em;
  background: var(--think);
  opacity: 0.55;
  border-radius: 0.15em;
  z-index: -1;
}
.spell-display { isolation: isolate; }
.spell-input.is-locked .spell-display .slot.is-next::after,
.spell-input.is-locked .spell-display .caret { display: none; }
.spell-input.is-locked .spell-display .slot.is-next { background: none; }
/* "not that one" (a refused letter, Check with nothing typed, no room left): the place
   blushes pink for a moment. Colour, not movement, so it shows with reduced motion. */
.spell-display.is-nudge .slot.is-next { background: var(--think-light); border-bottom-color: var(--think-ink); }
.spell-display.is-nudge .slot.is-next::after { background: var(--think-ink); }
.spell-display.is-nudge.is-nudge-all .slot { border-bottom-color: var(--think-ink); }
.spell-display.is-line.is-nudge { border-bottom-color: var(--think-ink); }
.spell-display.is-line.is-nudge .caret { background: var(--think-ink); }

/* ---------- keyboard ---------- */
.keyboard {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  margin-inline: auto;
  width: 100%;
  max-width: 720px;
  background: var(--paper-deep);
  border-radius: var(--r-lg);
  box-shadow: inset 0 2px 0 var(--paper-shade);
  touch-action: manipulation;
}
.kb-row { display: flex; justify-content: center; gap: 6px; }
.key {
  flex: 1 1 0;
  max-width: 64px;
  min-width: 0;
  height: clamp(48px, 7vh, 60px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: var(--stroke-thin) solid var(--ink-soft);
  border-radius: var(--r-sm);
  background: var(--paper-light);
  box-shadow: 0 3px 0 var(--paper-shade);
  font-family: var(--font-word);
  font-weight: 700;
  font-size: clamp(1.3rem, 4.6vw, 1.7rem);
  color: var(--ink);
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  transition: transform 90ms var(--out), box-shadow 90ms var(--out), background-color 90ms;
}
.key:active, .key.is-pressed {
  transform: translateY(2px);
  box-shadow: 0 1px 0 var(--paper-shade);
  background: var(--yolk-light);
}
.key-back { flex-grow: 1.5; max-width: 96px; }
.kb-actions { margin-top: 2px; }
.key-check {
  flex: 0 1 260px;
  max-width: 260px;
  background: var(--yolk);
  border: var(--stroke) solid var(--ink);
  box-shadow: 0 4px 0 var(--yolk-deep);
  font-family: var(--font-ui);
  font-size: var(--text-md);
  letter-spacing: 0.02em;
}
.key-check:active { background: var(--yolk); box-shadow: 0 1px 0 var(--yolk-deep); }
.keyboard.is-abc .key:not(.key-check):not(.key-back) { max-width: 74px; }
@media (min-width: 900px) and (pointer: fine) {
  .keyboard { max-width: 640px; }
  .key { height: 50px; }
  /* A to Z has an extra row: keep the whole board (and Check) on a laptop screen */
  .keyboard.is-abc { gap: 6px; padding-block: 8px; }
  .keyboard.is-abc .key { height: 48px; }
}

/* ---------- tiles (letters, chunks, stones) ---------- */
.tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 56px;
  min-height: 60px;
  padding: 4px 14px;
  border: var(--stroke) solid var(--ink);
  border-radius: 12px;
  background: var(--paper-light);
  box-shadow: 0 3px 0 var(--paper-shade);
  font-family: var(--font-word);
  font-weight: 700;
  font-size: clamp(1.5rem, 5vw, 2rem);
  letter-spacing: var(--letter-space);
  color: var(--ink);
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  transition: transform var(--t-micro) var(--out), box-shadow var(--t-micro) var(--out), opacity var(--t-micro);
}
.tile:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--paper-shade); }
.tile.is-used { opacity: 0.25; pointer-events: none; }
.tile.is-stamp { animation: stamp 160ms var(--out); }
@keyframes stamp { from { transform: scale(1.1); } to { transform: none; } }

/* ---------- progress: eggs on a twig ---------- */
.twig {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: clamp(4px, 1.5vw, 12px);
  padding: 0 10px 6px;
  min-height: 44px;
}
.twig::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 4px;
  height: 5px;
  border-radius: 4px;
  background: var(--twig);
  box-shadow: 0 2px 0 var(--twig-deep);
}
.twig-item {
  position: relative;
  z-index: 1;
  width: clamp(26px, 6vw, 36px);
  height: clamp(30px, 7vw, 40px);
}
.twig-item .wordling { width: 100%; height: 100%; overflow: visible; }
/* warmth you can see at twig size: a flat warm patch that grows behind the egg, and
   cracks and outline drawn heavier than the 200-unit egg would have them */
.twig-item.is-warming::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 55%;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--yolk-light);
  border: 1.5px solid var(--yolk-deep);
  /* the egg fills about 60% of its box: the patch starts just outside it and grows */
  transform: translate(-50%, -50%) scale(calc(0.78 + var(--warmth, 0) * 0.4));
  z-index: -1;
}
.twig-item.is-egg .wl-crack { stroke-width: 11px; }
.twig-item.is-egg .wl-egg > path[fill="none"]:not(.wl-crack) { stroke-width: 7px; }
.twig-item.is-current { animation: twig-bob 1.6s ease-in-out infinite; }
@keyframes twig-bob { 50% { transform: translateY(-4px); } }

/* ---------- bottom sheet modal ---------- */
.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(42, 38, 51, 0.4); /* --ink at 40%, for browsers without color-mix */
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fade-in 200ms var(--out) both;
}
.modal {
  width: 100%;
  max-width: 560px;
  max-height: calc(100dvh - 40px - env(safe-area-inset-top, 0px));
  overflow: auto;
  background: var(--paper-light);
  border: var(--stroke) solid var(--ink);
  border-bottom: none;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 26px max(20px, env(safe-area-inset-right, 0px)) calc(24px + env(safe-area-inset-bottom, 0px)) max(20px, env(safe-area-inset-left, 0px));
  animation: sheet-up 320ms var(--out) both;
}
.modal-scrim.is-closing { animation: fade-out 200ms var(--in) both; }
.modal-scrim.is-closing .modal { animation: sheet-down 220ms var(--in) both; }
@media (min-width: 700px) {
  .modal-scrim { align-items: center; }
  .modal { border-radius: var(--r-xl); border-bottom: var(--stroke) solid var(--ink); }
}
.modal-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: flex-end; margin-top: 20px; }
.modal-grip { width: 48px; height: 5px; border-radius: 3px; background: var(--rule); margin: -12px auto 14px; }
@keyframes fade-in { from { opacity: 0; } }
@keyframes fade-out { to { opacity: 0; } }
@keyframes sheet-up { from { transform: translateY(60px); opacity: 0; } }
@keyframes sheet-down { to { transform: translateY(60px); opacity: 0; } }

/* ---------- toast ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 300;
  transform: translateX(-50%);
  max-width: min(92vw, 480px);
  padding: 12px 18px;
  background: var(--ink);
  color: var(--paper-light);
  border-radius: var(--r);
  font-weight: 700;
  animation: toast-in 260ms var(--out) both;
}
.toast.is-leaving { animation: fade-out 200ms var(--in) both; }
@keyframes toast-in { from { transform: translate(-50%, 16px); opacity: 0; } }

/* ---------- feedback (after a whole word is submitted) ---------- */
.feedback {
  display: grid;
  justify-items: center;
  gap: 10px;
  text-align: center;
}
.feedback-msg { font-size: var(--text-lg); font-weight: 700; }
.feedback-msg.is-right { color: var(--leaf); }
.feedback-msg.is-think { color: var(--think-ink); }
.compare-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  font-family: var(--font-word);
  font-weight: 700;
  font-size: clamp(1.8rem, 7vw, 3rem);
  letter-spacing: var(--letter-space);
  isolation: isolate;
}
.compare-row .c { position: relative; display: inline-block; min-width: 0.35em; text-align: center; }
.compare-row .c.is-off::before {
  content: "";
  position: absolute;
  inset: 0.2em -0.04em 0.05em;
  background: var(--think);
  border-radius: 0.14em;
  z-index: -1;
  mix-blend-mode: multiply;
}
.compare-row .c.is-missing { color: var(--think-ink); }
.compare-row .c.is-missing::after {
  content: "";
  position: absolute;
  left: 10%; right: 10%; bottom: 0.05em;
  height: 3px;
  background: var(--think-ink);
  border-radius: 2px;
}
.compare-row .c.is-extra { color: var(--ink-faint); text-decoration: line-through; text-decoration-thickness: 3px; text-decoration-color: var(--think-ink); }
.compare-label { font-size: var(--text-xs); font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft); }
/* with a why card the comparison steps down a size, so the card and Try again fit a phone */
.feedback.has-why { gap: 8px; }
.feedback.has-why .compare-row { font-size: clamp(1.6rem, 6.2vw, 2.6rem); line-height: 1.25; }
.feedback.has-why .compare-label { line-height: 1.3; }
/* still too tall for the screen (a short phone, easier-reading type): the comparison
   keeps its letters and drops its labels */
.feedback.has-why.is-tight { gap: 6px; }
.feedback.has-why.is-tight .compare-label { display: none; }

/* the green tick, drawn by stroke */
.tick { width: 72px; height: 72px; }
.tick path { stroke-dasharray: 80; stroke-dashoffset: 80; animation: draw 360ms var(--out) forwards 80ms; }
@keyframes draw { to { stroke-dashoffset: 0; } }

/* ---------- tip card ---------- */
.tip {
  position: relative;
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 16px;
  background: var(--yolk-light);
  border: var(--stroke-thin) solid var(--ink);
  border-radius: var(--r);
  font-size: var(--text-md);
  text-align: left;
}
.tip mark {
  background: linear-gradient(transparent 45%, var(--think) 45%, var(--think) 90%, transparent 90%);
  color: inherit;
  font-weight: 700;
  padding-inline: 0.06em;
}
.tip .icon { flex: none; margin-top: 2px; }
.tip-pond { background: var(--pond-light); }
.tip-leaf { background: var(--leaf-light); }

/* ---------- sound buttons (lib/phonics.js) ---------- */
/* The word as a teacher draws it on the whiteboard: a dot under each one-letter sound,
   a bar under two, three or four letters making one sound, an arc joining a split
   digraph. Everything is in em, so the buttons scale with the word and sit centred
   under their letters at either font setting. Measured against Andika: with a 1.15
   line box the baseline sits at 0.99em and descenders reach 1.23em, so the buttons
   ride just below the descenders. */
.phonics {
  --ph-size: clamp(2rem, 8vw, 3rem);
  --ph-fit: 0.68;               /* em of width per letter, spacing included */
  width: 100%;
  min-width: 0;
  display: flex;
  justify-content: center;
  color: var(--ink);
}
:root[data-font="readable"] .phonics { --ph-fit: 0.75; }
.phonics.is-small { --ph-size: clamp(1.75rem, 7vw, 2.3rem); }
.phonics.is-large { --ph-size: clamp(2.4rem, 10vw, 4rem); }
@supports (width: 1cqi) {
  .phonics { container-type: inline-size; }
}
.phonics-word {
  --ph-row: 1.37em;             /* centre line of the buttons */
  --ph-dot: 0.19em;
  --ph-stroke: 0.115em;
  --ph-pad: calc(var(--letter-space) / 2 + 0.035em);
  --ph-arc: 0.3em;              /* how far a split digraph's arc dips below the dots */
  --ph-gap: 70ms;               /* the buttons are drawn left to right */
  display: inline-flex;
  align-items: flex-start;
  flex-wrap: nowrap;
  white-space: nowrap;
  padding-bottom: 0.36em;
  font-family: var(--font-word);
  font-weight: 700;
  /* big enough to read, small enough to stay on one line in its column */
  font-size: min(var(--ph-size), calc((100vw - 48px) / (var(--len, 8) * var(--ph-fit))));
  line-height: 1.15;
  letter-spacing: 0;
  isolation: isolate;
}
@supports (width: 1cqi) {
  .phonics-word { font-size: min(var(--ph-size), calc((100cqi - 4px) / (var(--len, 8) * var(--ph-fit)))); }
}
.phonics.is-plain .phonics-word { padding-bottom: 0.08em; }
.phonics.has-split .phonics-word { padding-bottom: 0.62em; }
.phonics.has-labels .phonics-word { padding-bottom: calc(0.36em + max(0.36em, 13.2px)); }

.ph-g {
  position: relative;
  display: inline-flex;
  padding-inline: var(--ph-pad);
}
.ph-ls { position: relative; display: inline-flex; }
.ph-l + .ph-l { margin-left: var(--letter-space); }
.ph-space { flex: none; width: 0.36em; }
/* a silent letter is there, but quiet: no button */
.ph-g.is-silent { color: var(--ink-faint); }

.ph-btn {
  position: absolute;
  top: var(--ph-row);
  background: currentColor;
  pointer-events: none;
}
.ph-dot {
  left: 50%;
  width: var(--ph-dot);
  height: var(--ph-dot);
  margin: calc(var(--ph-dot) / -2) 0 0 calc(var(--ph-dot) / -2);
  border-radius: 50%;
}
.ph-bar {
  left: calc(var(--ph-pad) + 0.03em);
  right: calc(var(--ph-pad) + 0.03em);
  height: var(--ph-stroke);
  margin-top: calc(var(--ph-stroke) / -2);
  border-radius: var(--ph-stroke);
  transform-origin: left center;
}
/* a split digraph: a smooth arc from the middle of the vowel, dipping under the
   letters between, up to the middle of its e. Three pieces, so it follows the letters
   at any size: a curve down, a straight run, a curve up. */
.ph-arc {
  position: absolute;
  top: var(--ph-row);
  height: var(--ph-arc);
  border: 0 solid currentColor;
  border-bottom-width: var(--ph-stroke);
  pointer-events: none;
}
.ph-arc.is-start {
  left: calc(50% - var(--ph-stroke) / 2);
  right: -0.01em;
  border-left-width: var(--ph-stroke);
  border-bottom-left-radius: 100% 100%;
}
/* the pieces overlap by a hair, so no seam shows where they meet */
.ph-arc.is-mid { left: -0.02em; right: -0.02em; }
.ph-arc.is-end {
  left: -0.01em;
  right: calc(50% - var(--ph-stroke) / 2);
  border-right-width: var(--ph-stroke);
  border-bottom-right-radius: 100% 100%;
}
/* round ends where the arc starts and finishes */
.ph-arc.is-start::before, .ph-arc.is-end::before {
  content: "";
  position: absolute;
  top: calc(var(--ph-stroke) / -2);
  width: var(--ph-stroke);
  height: var(--ph-stroke);
  border-radius: 50%;
  background: currentColor;
}
.ph-arc.is-start::before { left: calc(var(--ph-stroke) * -1); }
.ph-arc.is-end::before { right: calc(var(--ph-stroke) * -1); }
/* "uh" under a schwa's dot, when labels are on */
/* (positioned in the word's em; the text inside is small) */
.ph-label {
  position: absolute;
  top: calc(var(--ph-row) + 0.12em);
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
}
.ph-label > span {
  font-size: max(0.3em, 11px);
  line-height: 1.2;
  font-weight: 700;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}

/* the part to look at: a pink highlighter swash behind the letters, and their button
   in the same ink, nudged once the buttons are drawn */
.ph-g.is-focus { color: var(--ink); }
.ph-g.is-focus .ph-ls::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: -0.05em;
  right: -0.05em;
  bottom: 0.12em;
  height: 0.44em;
  background: var(--think);
  border-radius: 0.12em 0.2em 0.1em 0.18em;
  mix-blend-mode: multiply;
  transform-origin: left center;
}
.ph-g.is-focus.is-silent .ph-ls { color: var(--ink-soft); }
.ph-g.is-focus .ph-btn, .ph-arc.is-focus { color: var(--think-ink); }

.phonics.is-play .ph-dot { animation: ph-dot-in 300ms var(--out) both; animation-delay: calc(var(--i, 0) * var(--ph-gap) + 120ms); }
.phonics.is-play .ph-bar { animation: ph-bar-in 320ms var(--out) both; animation-delay: calc(var(--i, 0) * var(--ph-gap) + 120ms); }
.phonics.is-play .ph-arc { animation: ph-arc-in 420ms var(--out) both; animation-delay: calc(var(--i, 0) * var(--ph-gap) + 120ms); }
.phonics.is-play .ph-g.is-focus .ph-ls::before { animation: swash 320ms var(--out) both; animation-delay: 380ms; }
.phonics.is-play .ph-g.is-focus .ph-dot {
  animation: ph-dot-in 300ms var(--out) both, ph-nudge 520ms var(--out) 900ms;
  animation-delay: calc(var(--i, 0) * var(--ph-gap) + 120ms), 900ms;
}
.phonics.is-play .ph-g.is-focus .ph-bar {
  animation: ph-bar-in 320ms var(--out) both, ph-nudge-bar 520ms var(--out) 900ms;
  animation-delay: calc(var(--i, 0) * var(--ph-gap) + 120ms), 900ms;
}
@keyframes ph-dot-in { from { transform: scale(0); opacity: 0; } }
@keyframes ph-bar-in { from { transform: scaleX(0); opacity: 0; } }
@keyframes ph-arc-in { from { transform: translateY(-0.12em); opacity: 0; } }
@keyframes ph-nudge { 45% { transform: scale(1.45); } }
@keyframes ph-nudge-bar { 45% { transform: scale(1.06, 1.6); } }
/* reduced motion: everything is simply there (durations are cut globally; cut the waits too) */
:root[data-motion="reduced"] .phonics *, :root[data-motion="reduced"] .phonics *::before { animation-delay: 0ms !important; }
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .phonics *, :root:not([data-motion="full"]) .phonics *::before { animation-delay: 0ms !important; }
}

/* ---------- why a spelling went wrong (lib/phonics.js whyCard) ---------- */
.why-card {
  position: relative;
  width: 100%;
  max-width: 520px;
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 14px 16px;
  background: var(--paper-light);
  border: var(--stroke-thin) solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: 0 3px 0 var(--paper-shade);
  text-align: center;
}
.why-head {
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.3;
  color: var(--think-ink);
  text-wrap: balance;
}
/* the concept's name sits on the card's top edge, like a label tab */
.why-card.has-chip { margin-top: 14px; padding-top: 20px; }
.why-chip {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  max-width: calc(100% - 32px);
  line-height: 1;
}
.why-chip .tag { font-size: var(--text-xs); box-shadow: 0 2px 0 var(--think); }
.why-line {
  max-width: 36ch;
  font-size: var(--text-md);
  line-height: 1.4;
  text-wrap: pretty;
}
.why-line mark {
  background: linear-gradient(transparent 52%, var(--think-light) 52%, var(--think-light) 92%, transparent 92%);
  color: inherit;
  font-weight: 700;
  padding-inline: 0.04em;
}
.why-more { width: 100%; margin-bottom: -10px; }
.why-more > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  border-radius: var(--r-sm);
  color: var(--pond);
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
  list-style: none;
  touch-action: manipulation;
}
.why-more > summary::-webkit-details-marker { display: none; }
.why-more > summary span {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  text-decoration-color: var(--pond-light);
}
/* a small ink chevron that turns when the answer is open */
.why-more > summary::before {
  content: "";
  flex: none;
  width: 0.5em;
  height: 0.5em;
  border: solid currentColor;
  border-width: 0 2.5px 2.5px 0;
  transform: translateY(-0.12em) rotate(45deg);
  transition: transform var(--t-micro) var(--out);
}
.why-more[open] > summary::before { transform: translateY(0.12em) rotate(-135deg); }
.why-more > summary:hover span { text-decoration-color: var(--pond); }
.why-more > p {
  margin: 0 auto 10px;
  max-width: 40ch;
  padding: 10px 12px;
  background: var(--paper);
  border-radius: var(--r-sm);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--ink);
  text-align: left;
}
/* compact: sits with the word comparison and Try again above a phone's keyboard */
.why-card.is-compact { gap: 6px; padding: 10px 12px; border-radius: var(--r); }
.why-card.is-compact.has-chip { padding-top: 18px; }
.why-card.is-compact .why-head { font-size: var(--text-md); }
.why-card.is-compact .why-line { font-size: var(--text-sm); line-height: 1.35; }
@media (min-width: 700px) and (min-height: 760px) {
  .why-card.is-compact { gap: 8px; padding: 14px 18px; }
  .why-card.is-compact .why-line { font-size: var(--text-md); }
}

/* ---------- clues: the word itself is a blank line (lib/clue.js) ---------- */
.clue-blank {
  display: inline-block;
  width: 3.2em;
  height: 0.9em;
  margin-inline: 0.12em;
  vertical-align: baseline;
  border-bottom: 3px solid var(--think-ink);
}
/* a blank never wraps away from the quotes and full stop that hug it */
.clue-blank-wrap { white-space: nowrap; }

/* ---------- forms (grown-ups area) ---------- */
.field { display: grid; gap: 6px; }
.field > label, .field-label { font-weight: 700; font-size: var(--text-sm); }
.field-hint { font-size: var(--text-xs); color: var(--ink-soft); }
.input, .textarea, .select {
  width: 100%;
  min-height: 52px;
  padding: 10px 14px;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--r-sm);
  background: var(--paper-light);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-md);
}
.textarea { min-height: 160px; resize: vertical; line-height: 1.5; }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* the chevron: two ink strokes drawn with gradients, so it takes the --ink token */
.select {
  --chev: transparent calc(50% - 1.3px), var(--ink) calc(50% - 1.3px), var(--ink) calc(50% + 1.3px), transparent calc(50% + 1.3px);
  appearance: none;
  background-image: linear-gradient(45deg, var(--chev)), linear-gradient(-45deg, var(--chev));
  background-size: 8px 8px;
  background-repeat: no-repeat;
  background-position: right 22px center, right 15px center;
  padding-right: 40px;
}

/* segmented choice */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--paper-light);
}
.segmented button {
  min-height: 48px;
  padding: 8px 16px;
  border: 0;
  border-right: var(--stroke-thin) solid var(--ink);
  background: transparent;
  font-weight: 700;
  cursor: pointer;
}
.segmented button:last-child { border-right: 0; }
/* chosen: yolk, plus a thick ink underline, so it shows without colour too */
.segmented button[aria-pressed="true"] { background: var(--yolk); box-shadow: inset 0 -5px 0 var(--ink); }

/* toggle switch */
.switch { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 52px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch .track {
  flex: none;
  position: relative;
  width: 58px;
  height: 34px;
  border: var(--stroke) solid var(--ink);
  border-radius: 10px;
  background: var(--paper-deep);
  transition: background var(--t-micro);
}
.switch .track::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 24px; height: 24px;
  border-radius: 7px;
  background: var(--paper-light);
  border: var(--stroke) solid var(--ink);
  transition: transform var(--t-micro) var(--out);
}
.switch input:checked + .track { background: var(--leaf); }
.switch input:checked + .track::after { transform: translateX(24px); }
.switch input:focus-visible + .track { outline: 3px solid var(--focus); outline-offset: 3px; }

/* ---------- lists & tables ---------- */
.list-plain { list-style: none; margin: 0; padding: 0; }
.divided > * + * { border-top: var(--stroke-thin) dashed var(--rule); }
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: var(--r-sm); }
.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.table th, .table td { padding: 10px 12px; text-align: left; border-bottom: var(--stroke-thin) solid var(--rule); white-space: nowrap; }
.table th { font-size: var(--text-xs); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Mon–Fri week dots ---------- */
.week-dots { display: inline-flex; gap: 8px; align-items: center; }
.week-dots .dot {
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border: var(--stroke) solid var(--ink);
  border-radius: 50%;
  font-size: 0.75rem;
  font-weight: 700;
  background: var(--paper-light);
}
.week-dots .dot.is-done { background: var(--yolk); }
.week-dots .dot.is-today { box-shadow: 0 0 0 3px var(--paper), 0 0 0 5px var(--pond); }

/* ---------- Spell Beast idle animation (.wordling: the class keeps its old name) ---------- */
.wordling { display: block; overflow: visible; }
.wordling-wrap { display: inline-block; line-height: 0; }
.wordling .wl-body { transform-box: fill-box; transform-origin: 50% 100%; animation: wl-breathe 2.6s ease-in-out infinite; }
.wordling .wl-eyes { transform-box: fill-box; transform-origin: 50% 50%; animation: wl-blink 5.2s infinite; }
.wordling .wl-egg { transform-box: fill-box; transform-origin: 50% 100%; }
.wordling .wl-egg.is-warm { animation: wl-wobble 4s ease-in-out infinite; }
.wordling .wl-sparkle { transform-box: fill-box; transform-origin: center; animation: wl-twinkle 2.4s ease-in-out infinite; }
.wordling .wl-wave { transform-box: fill-box; transform-origin: 30% 90%; animation: wl-wave 1.2s ease-in-out infinite; }
/* the sleepy "z z" are drawn in the text colour, so they show on dusk panels too */
.wordling .wl-zzz { fill: currentColor; }
.wordling .wl-zzz text { animation: wl-z 3s ease-in-out infinite; }
.wordling .wl-zzz text + text { animation-delay: 0.6s; }
.wordling.is-still .wl-body, .wordling.is-still .wl-eyes, .wordling.is-still .wl-egg, .wordling.is-still .wl-wave { animation: none; }
@keyframes wl-breathe { 50% { transform: scale(1.015, 1.035); } }
@keyframes wl-blink { 0%, 93%, 100% { transform: scaleY(1); } 95% { transform: scaleY(0.1); } }
@keyframes wl-wobble { 0%, 80%, 100% { transform: rotate(0); } 84% { transform: rotate(-5deg); } 88% { transform: rotate(4deg); } 92% { transform: rotate(-2deg); } }
@keyframes wl-twinkle { 50% { transform: scale(0.55) rotate(20deg); opacity: 0.6; } }
@keyframes wl-wave { 50% { transform: rotate(-22deg); } }
@keyframes wl-z { 0% { opacity: 0; transform: translate(0, 4px); } 30%, 70% { opacity: 1; } 100% { opacity: 0; transform: translate(6px, -8px); } }
.wordling.is-tapped .wl-body { animation: wl-squash 420ms var(--out); }
@keyframes wl-squash { 30% { transform: scale(1.15, 0.85); } 65% { transform: scale(0.92, 1.1); } }

/* ---------- empty states ---------- */
.empty {
  display: grid;
  justify-items: center;
  gap: 12px;
  padding: 32px 16px;
  text-align: center;
  color: var(--ink-soft);
}

/* ---------- choice cards (pick a game, pick a list) ---------- */
.choice {
  display: flex;
  gap: 14px;
  align-items: center;
  width: 100%;
  padding: 14px 16px;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--r-lg);
  background: var(--paper-light);
  box-shadow: 0 4px 0 var(--paper-shade);
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  cursor: pointer;
  transition: transform var(--t-micro) var(--out), box-shadow var(--t-micro) var(--out);
}
.choice:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--paper-shade); }
/* chosen: yolk, plus a doubled ink outline, so it shows without colour too */
.choice[aria-pressed="true"], .choice.is-selected { background: var(--yolk-light); box-shadow: inset 0 0 0 2px var(--ink), 0 4px 0 var(--yolk-deep); }
.choice[aria-pressed="true"]:active, .choice.is-selected:active { box-shadow: inset 0 0 0 2px var(--ink), 0 1px 0 var(--yolk-deep); }
.choice-title { font-weight: 700; font-size: var(--text-md); line-height: 1.25; }
.choice-sub { font-size: var(--text-sm); color: var(--ink-soft); line-height: 1.35; }
.choice-art { flex: none; width: 64px; height: 64px; display: grid; place-items: center; }
.choice-art .wordling { width: 64px; height: 64px; }

/* ---------- activity stage (the session and the games menu run activities in one) ---------- */
.activity { display: flex; flex-direction: column; gap: 16px; width: 100%; }

/* ---------- grown-ups gate ---------- */
.gate-modal > h2 { text-align: center; }
.gate-question { font-size: var(--text-xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.gate-answer {
  align-self: center;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 7ch;
  height: 64px;
  padding: 0 18px;
  background: var(--paper-deep);
  border-radius: var(--r);
  box-shadow: inset 0 2px 0 var(--paper-shade);
  font-family: var(--font-word);
  font-weight: 700;
  font-size: 2.2rem;
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
}
.gate-answer.is-empty::after { content: ""; width: 1.1em; height: 4px; border-radius: 2px; background: var(--ink-faint); }
.gate-pad {
  align-self: center;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  max-width: 300px;
  margin-top: 4px;
}
.gate-pad .key { max-width: none; height: 56px; }
/* a class device's gate: the question is words, and a quiet line points teachers to the PIN */
.gate-hint { max-width: 300px; margin: 2px auto 0; font-size: var(--text-xs); color: var(--ink-soft); }
