/*
 * Sikai — student theme.
 *
 * Every rule is nested under .sikai-student. stylesheet_link_tag :app serves
 * every stylesheet in this directory on every page, so an unscoped rule here
 * would restyle the admin, and an unscoped admin rule would restyle this.
 */

.sikai-student {
  --s-sky-top:     #BFE6FF;
  --s-sky-bottom:  #DFF3FF;
  --s-grass:       #DCEFD4;
  --s-hill:        #C6E6B4;

  --s-accent:      #7C3AED;
  --s-accent-dark: #4C1D95;
  --s-ink:         #1F2937;
  --s-card:        #FFFFFF;

  --s-ok:          #16A34A;
  --s-ok-dark:     #14532D;
  --s-try:         #FEF3C7;
  --s-try-border:  #D9A441;
  --s-try-ink:     #8A6216;

  /* A locked lesson is a "not yet", not a failure -- a neutral grey, never the
     amber "try again" pair above and never anything red. */
  --s-locked:      #9CA3AF;
  --s-locked-ink:  #4B5563;

  --s-radius:      16px;
  --s-radius-pill: 999px;
  --s-tap:         48px;
  --s-gap:         12px;
  --s-pad:         16px;

  --s-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --s-text:    17px;
  --s-text-lg: 21px;
  --s-text-xl: 27px;

  margin: 0;
  /* 100vh first: WebViews that do not parse dvh drop that declaration and keep
     this one, so the background still fills the screen on older Android. */
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--s-font);
  font-size: var(--s-text);
  color: var(--s-ink);
  background: linear-gradient(var(--s-sky-top) 0%, var(--s-sky-bottom) 44%, var(--s-grass) 100%);
  background-attachment: fixed;
  -webkit-text-size-adjust: 100%;
}

.sikai-student * { box-sizing: border-box; }

/* The hidden attribute must beat component display rules. Several components
   below set `display: block`, and an author declaration overrides the user-agent
   [hidden] rule — which silently left the no-JS PIN fallback on screen next to
   the keypad. */
.sikai-student [hidden] { display: none !important; }

.sikai-student .s-screen {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--s-pad);
  padding-bottom: calc(var(--s-pad) + env(safe-area-inset-bottom));
}

.sikai-student h1 {
  font-size: var(--s-text-xl);
  line-height: 1.2;
  margin: 0 0 var(--s-gap);
}

.sikai-student .s-card {
  background: var(--s-card);
  border-radius: var(--s-radius);
  padding: var(--s-pad);
  box-shadow: 0 4px 14px rgb(31 41 55 / 15%);
}

.sikai-student .s-tile {
  display: flex;
  align-items: center;
  min-height: var(--s-tap);
  padding: 14px 16px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius);
  background: var(--s-card);
  box-shadow: 0 3px 0 rgb(124 58 237 / 26%);
  color: inherit;
  font-weight: 700;
  text-decoration: none;
}

@media (prefers-reduced-motion: no-preference) {
  /* transform only — box-shadow and background-color transitions repaint, which is
     exactly what the low-end Android constraint forbids. */
  .sikai-student .s-tile { transition: transform 120ms ease; }
}

.sikai-student .s-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--s-gap);
  margin-bottom: var(--s-pad);
}

/* Vertical counterpart to .s-grid: lesson and topic lists read better as a
   single stacked column than as a multi-column grid -- titles run long and
   the list length varies (2 to 15 rows) in a way a fixed-width grid does not.
   list-style/padding reset here rather than on a caller: its three existing
   users (learn/subjects/show, learn/lessons/show x2) are <div>s, for which
   both declarations are a no-op, but /learn/progress is a <ul> and needs
   them -- fixing it on the shared class means any future <ul> caller gets
   the reset for free instead of re-discovering the same 40px indent bug. */
.sikai-student .s-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
  margin-bottom: var(--s-pad);
  list-style: none;
  padding: 0;
}

/* Only real user of .s-list__row so far: /learn/progress's own lesson rows.
   Title left, mastery chip right, both vertically centred; the title
   truncates instead of pushing the chip off-screen or wrapping onto a
   second line at 360px. Same 48px minimum as .s-board__row for tap parity,
   even though nothing here is currently tappable. */
.sikai-student .s-list__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-gap);
  min-block-size: var(--s-tap);
}
.sikai-student .s-list__title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-inline-size: 0;
}

/* .s-tile is a single-line flex row by default; wrapping a tile's content in
   .s-tile__body stacks a title and a meta line inside that one flex item. */
.sikai-student .s-tile__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sikai-student .s-tile__title {
  font-size: var(--s-text-lg);
}

.sikai-student .s-tile__meta {
  font-weight: 400;
  opacity: 0.75;
}

/* Marks a completed topic on the lesson list. Always paired with the word
   "Done" in the markup, never a bare checkmark -- a lone tick next to
   unmarked rows would read as pass/fail. Reuses --s-ok-dark, the darker end
   of the same "got it right" green used elsewhere, rather than introducing a
   second success color. Incomplete topics render no counterpart element at all: unmarked,
   not flagged. */
.sikai-student .s-tile__done {
  color: var(--s-ok-dark);
  font-weight: 700;
  opacity: 1;
}

/* A lesson that has not opened yet. Listed, never hidden -- the child sees the
   whole road ahead -- and rendered as a non-link, so the styling here is the
   only thing telling them it is not tappable yet. Deliberately neutral: dashed
   outline and a grey border, never red and never a cross. A locked lesson is
   not a failure, it is a "not yet", so nothing here may read as a reprimand.
   Must come after .s-tile above -- same specificity, so it only overrides that
   block's border and shadow by sitting later in the file. */
.sikai-student .s-tile--locked {
  border-style: dashed;
  border-color: var(--s-locked);
  background: rgb(255 255 255 / 70%);
  box-shadow: none;
}

.sikai-student .s-tile--locked .s-tile__title { opacity: 0.75; }

/* The "Finish X to open this one." line. Full opacity, unlike .s-tile__meta
   above (which it must follow, for the same reason .s-tile--locked must follow
   .s-tile): it is the one instruction on the tile, so it has to be the most
   readable thing on it, not the faintest. */
.sikai-student .s-tile__locked {
  color: var(--s-locked-ink);
  font-weight: 700;
  opacity: 1;
}

.sikai-student .s-muted {
  margin: 0 0 var(--s-pad);
  opacity: 0.7;
}

.sikai-student .s-link {
  display: inline-block;
  min-height: var(--s-tap);
  padding: 14px 4px;
  color: var(--s-accent-dark);
  font-weight: 700;
}

.sikai-student .s-dots {
  display: flex;
  justify-content: center;
  gap: var(--s-gap);
  margin: var(--s-pad) 0 24px;
}

.sikai-student .s-dot {
  width: 18px;
  height: 18px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius-pill);
  background: transparent;
}

.sikai-student .s-dot.is-filled { background: var(--s-accent); }

/* .s-keypad, not .s-pad — --s-pad is the padding token and the two read badly together. */
.sikai-student .s-keypad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-gap);
  max-width: 320px;
  margin: 0 auto var(--s-pad);
}

.sikai-student .s-key {
  min-height: 64px;
  border: 0;
  border-radius: var(--s-radius);
  background: var(--s-card);
  box-shadow: 0 3px 0 rgb(124 58 237 / 26%);
  color: var(--s-ink);
  font: 800 var(--s-text-xl) / 1 var(--s-font);
  cursor: pointer;
}

.sikai-student .s-key:active { transform: translateY(2px); box-shadow: 0 1px 0 rgb(124 58 237 / 26%); }
.sikai-student .s-key-quiet { background: transparent; box-shadow: none; }

@media (prefers-reduced-motion: no-preference) {
  /* transform only. The dot fill is deliberately instant: a PIN digit landing
     immediately is better feedback for a child than a fading one. */
  .sikai-student .s-key { transition: transform 90ms ease; }
}

/* No-JS fallback for the PIN pad: a real input a child can type into. */
.sikai-student .s-pin-field {
  display: block;
  width: 100%;
  min-height: var(--s-tap);
  margin: 0 0 var(--s-gap);
  padding: 10px 16px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius);
  background: var(--s-card);
  color: var(--s-ink);
  font: 800 var(--s-text-xl) / 1 var(--s-font);
  letter-spacing: 0.3em;
  text-align: center;
}

/* flex, not block, and it has to be. <button> and <input type="submit">
   centre their own label; an <a> does not -- so every link wearing this class
   rendered its text jammed into the top-left corner of the pill. That was
   already true of the "Back to ..." links on the done screens before any
   nav link was converted. line-height 1.2 rather than 1 so a long label
   ("Back to Lines and Angles") wraps to two readable lines instead of
   colliding with itself. */
.sikai-student .s-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 48px;
  padding: 10px 16px;
  border: 0;
  border-radius: var(--s-radius-pill);
  background: var(--s-accent);
  color: #fff;
  font: 800 var(--s-text-lg) / 1.2 var(--s-font);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.sikai-student .s-button:active { transform: translateY(2px); }

@media (prefers-reduced-motion: no-preference) {
  /* transform/opacity only — box-shadow and background-color transitions
     repaint, which is exactly what the low-end Android constraint forbids. */
  .sikai-student .s-button { transition: transform 90ms ease; }
}

.sikai-student .s-label {
  display: block;
  margin: 0 0 6px;
  font-weight: 700;
}

/* ---- Habre, the companion (D-03) ------------------------------------
   Shared hb-body/hb-head geometry is defined once per page in
   learn/shared/_habre_defs (rendered from layouts/student.html.erb); each
   pose below only adds its own eyes/mouth/arms. :curious gets a static tilt
   -- interested, never sad -- so it is not gated behind reduced motion.
   Everything that actually moves is transform/opacity only and lives inside
   the reduced-motion query below, per the low-end-Android constraint that
   already cost a fix once on this branch. */
.sikai-student .s-habre {
  display: inline-block;
}

.sikai-student .s-habre svg {
  display: block;
}

.sikai-student .s-habre--curious {
  transform: rotate(-13deg);
  transform-origin: 50% 88%;
}

@media (prefers-reduced-motion: no-preference) {
  .sikai-student .s-habre--idle {
    animation: s-habre-bob 2.4s ease-in-out infinite;
    transform-origin: 50% 100%;
  }

  .sikai-student .s-habre__blink {
    animation: s-habre-blink 3.8s infinite;
    transform-origin: center;
  }

  .sikai-student .s-habre--happy {
    animation: s-habre-jump 0.85s cubic-bezier(.3, 1.4, .5, 1) infinite;
    transform-origin: 50% 100%;
  }

  @keyframes s-habre-bob {
    0%, 100% { transform: translateY(0) rotate(0); }
    50%      { transform: translateY(-5px) rotate(-1.5deg); }
  }

  @keyframes s-habre-blink {
    0%, 93%, 100% { transform: scaleY(1); }
    96%           { transform: scaleY(.08); }
  }

  @keyframes s-habre-jump {
    0%, 60%, 100% { transform: translateY(0); }
    30%           { transform: translateY(-15px) rotate(5deg); }
  }
}

/* ---- Topic screen (reading and answering) -----------------------------
   Habre's line lives in a speech bubble; the topic's own explanation renders
   verbatim in a separate card below it (D-09) -- they are deliberately two
   different pieces of markup, not one. */
.sikai-student .s-speech {
  display: flex;
  align-items: flex-end;
  gap: var(--s-gap);
  margin-bottom: var(--s-gap);
}

.sikai-student .s-speech__bubble {
  position: relative;
  margin: 0 0 8px;
  padding: 12px 16px;
  background: var(--s-card);
  border-radius: var(--s-radius);
  box-shadow: 0 4px 14px rgb(31 41 55 / 15%);
  font-weight: 700;
}

.sikai-student .s-explanation {
  margin-bottom: var(--s-pad);
  line-height: 1.5;
}

.sikai-student .s-explanation p { margin: 0; }

.sikai-student .s-figures {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
  margin-bottom: var(--s-pad);
}

.sikai-student .s-figure {
  margin: 0;
  text-align: center;
}

.sikai-student .s-figure__img {
  max-width: 100%;
  height: auto;
  border-radius: var(--s-radius);
}

.sikai-student .s-figure__caption {
  margin-top: 6px;
  font-size: 0.9em;
  opacity: 0.75;
}

/* The gentle counterpart to admin's "figure missing: <path>" -- a child sees
   this instead of a raw file path when an asset does not resolve. */
.sikai-student .s-figure__missing {
  display: block;
  padding: var(--s-pad);
  border-radius: var(--s-radius);
  background: var(--s-try);
  border: 2px solid var(--s-try-border);
  color: var(--s-try-ink);
  font-weight: 700;
}

.sikai-student .s-worked { margin-bottom: var(--s-pad); }

.sikai-student .s-worked__title {
  font-size: var(--s-text-lg);
  margin: 0 0 8px;
}

.sikai-student .s-worked__prompt {
  margin: 0 0 var(--s-gap);
  font-weight: 700;
}

.sikai-student .s-worked__steps {
  margin: var(--s-gap) 0;
  padding-left: 1.4em;
}

.sikai-student .s-worked__steps li { margin-bottom: 8px; }
.sikai-student .s-worked__steps li:last-child { margin-bottom: 0; }

.sikai-student .s-worked__answer {
  margin: var(--s-gap) 0 0;
  padding: 12px 16px;
  border-radius: var(--s-radius);
  background: rgb(22 163 74 / 10%);
  border: 2px solid var(--s-ok);
  color: var(--s-ok-dark);
  font-weight: 700;
}

/* ---- Practice screen (answering) --------------------------------------
   D-02: a wrong choice gets the amber "try" treatment below, never red --
   .is-try is the only state a wrong answer ever receives, and it stays next
   to .is-correct rather than any "is-wrong"/"is-incorrect" class existing at
   all. The chosen choice stays in the DOM and every other choice stays
   exactly as selectable as it was before the attempt. */
.sikai-student .s-question { margin-bottom: var(--s-pad); }

.sikai-student .s-question__prompt {
  margin: 0 0 var(--s-pad);
  font-size: var(--s-text-lg);
  font-weight: 700;
}

.sikai-student .s-choices {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
}

.sikai-student .s-choice {
  display: block;
  width: 100%;
  min-height: var(--s-tap);
  padding: 14px 16px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius);
  background: var(--s-card);
  color: var(--s-ink);
  font: 700 var(--s-text-lg) / 1.3 var(--s-font);
  text-align: left;
  cursor: pointer;
}

.sikai-student .s-choice:active { transform: translateY(2px); }

@media (prefers-reduced-motion: no-preference) {
  /* transform only, matching .s-tile and .s-button above. */
  .sikai-student .s-choice { transition: transform 90ms ease; }
}

.sikai-student .s-choice.is-correct {
  background: rgb(22 163 74 / 10%);
  border-color: var(--s-ok);
  color: var(--s-ok-dark);
}

.sikai-student .s-choice.is-try {
  background: var(--s-try);
  border-color: var(--s-try-border);
  color: var(--s-try-ink);
}

.sikai-student .s-numeric-form {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
}

.sikai-student .s-numeric-input {
  display: block;
  width: 100%;
  min-height: var(--s-tap);
  padding: 10px 16px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius);
  background: var(--s-card);
  color: var(--s-ink);
  font: 800 var(--s-text-xl) / 1 var(--s-font);
  text-align: center;
}

.sikai-student .s-numeric-input.is-correct {
  border-color: var(--s-ok);
  color: var(--s-ok-dark);
}

.sikai-student .s-numeric-input.is-try {
  background: var(--s-try);
  border-color: var(--s-try-border);
  color: var(--s-try-ink);
}

/* ---- Self-check and task items (D-02) ---------------------------------
   fill_blank/short_answer are never auto-graded: the child reveals the real
   answer and solution, then judges themselves. Neither self-mark button may
   read as the recommended choice -- elsewhere in this stylesheet .s-button
   (filled, accent background) IS the go-forward signal ("Keep going →",
   "Check", "I've done this" all use it), so leaving "I got it" filled while
   "Not quite" stays outlined would tell the child which answer wins before
   they've judged themselves. The rule below overrides both buttons inside
   .s-self-check__buttons -- regardless of which of .s-button/.s-button--
   secondary they carry -- to the same outlined treatment, so the two are
   truly identical siblings here. practice_habre_pose keeping Habre at :idle
   for both is the other half of that same guarantee. */
.sikai-student .s-self-check {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
}

.sikai-student .s-self-check__commit {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
}

.sikai-student .s-button--ghost {
  background: transparent;
  border: 2.5px solid var(--s-accent);
  color: var(--s-accent-dark);
}

.sikai-student .s-reveal {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: var(--s-pad);
  border-radius: var(--s-radius);
  background: rgb(124 58 237 / 6%);
  border: 2px solid var(--s-accent);
}

.sikai-student .s-reveal__row {
  margin: 0;
  line-height: 1.5;
}

.sikai-student .s-reveal__label {
  display: block;
  font-size: 0.85em;
  font-weight: 700;
  opacity: 0.7;
}

.sikai-student .s-reveal__solution {
  margin: 0;
  line-height: 1.5;
}

/* A second, inline Habre next to the reveal -- smaller than the main
   feedback speech row above the question, so it reads as a side note rather
   than repeating that row's layout. */
.sikai-student .s-speech--inline {
  align-items: center;
  margin-bottom: 0;
}

.sikai-student .s-speech--inline .s-habre svg {
  width: 72px;
  height: 76px;
}

.sikai-student .s-self-check__buttons {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
}

.sikai-student .s-self-check__buttons .s-button,
.sikai-student .s-self-check__buttons .s-button--secondary {
  background: var(--s-card);
  border: 2.5px solid var(--s-accent);
  color: var(--s-accent-dark);
}

.sikai-student .s-task-form {
  display: flex;
}

/* ---- Completion screen (finishing a topic) ----------------------------
   D-04: no score, no percentage, no grade -- three plain counts only. Habre
   is :happy here (student.css already animates that pose above); the
   confetti burst is lifted from docs/design/student-view/02-companion-depth.html
   verbatim. */
.sikai-student .s-done {
  text-align: center;
}

.sikai-student .s-done__stage {
  position: relative;
  display: flex;
  justify-content: center;
  margin-bottom: var(--s-gap);
}

.sikai-student .s-confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.sikai-student .s-confetti i {
  position: absolute;
  top: 30%;
  left: 40%;
  width: 7px;
  height: 11px;
  border-radius: 2px;
  opacity: 0;
}

/* Horizontal start offsets from the source markup's inline `left:NN%` on
   each <i> -- moved here (D-04: no "%" in this screen's HTML) rather than
   left inline. */
.sikai-student .s-confetti i:nth-child(2) { left: 46%; }
.sikai-student .s-confetti i:nth-child(3) { left: 54%; }
.sikai-student .s-confetti i:nth-child(4) { left: 38%; }
.sikai-student .s-confetti i:nth-child(5) { left: 50%; }

@media (prefers-reduced-motion: no-preference) {
  /* transform/opacity only, matching the low-end-Android constraint every
     other animation on this page follows. */
  .sikai-student .s-confetti i {
    animation: s-confetti-burst 1.3s 0.35s ease-out infinite;
  }

  @keyframes s-confetti-burst {
    0%   { opacity: 1; transform: translate(0, 0) rotate(0); }
    100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--r)); }
  }
}

.sikai-student .s-done__heading {
  font-size: var(--s-text-xl);
  color: var(--s-accent-dark);
  margin: 0 0 4px;
}

.sikai-student .s-done__subtitle {
  margin: 0 0 var(--s-pad);
  opacity: 0.75;
}

.sikai-student .s-tally {
  display: flex;
  gap: var(--s-gap);
  margin-bottom: var(--s-pad);
}

.sikai-student .s-tally__item {
  flex: 1;
  padding: 12px 6px;
  border-radius: var(--s-radius);
  background: var(--s-card);
  box-shadow: 0 4px 14px rgb(31 41 55 / 15%);
  text-align: center;
}

.sikai-student .s-tally__item b {
  display: block;
  font-size: var(--s-text-xl);
  color: var(--s-accent-dark);
}

.sikai-student .s-tally__item span {
  display: block;
  margin-top: 2px;
  font-size: 0.8em;
  opacity: 0.7;
}

.sikai-student .s-done__next {
  margin-bottom: var(--s-gap);
  padding: 12px 16px;
  border-radius: var(--s-radius);
  background: var(--s-card);
  box-shadow: 0 4px 14px rgb(31 41 55 / 15%);
  text-align: left;
}

.sikai-student .s-done__next-label {
  display: block;
  margin-bottom: 2px;
  font-size: 0.8em;
  opacity: 0.7;
}

/* ---- "Pick up where you left off" (/learn) --------------------------
   The one card above the subject chooser. Its container is a plain wrapper so
   both states -- an offer and the all-caught-up note -- sit apart from the
   grid below by the same margin. */
.sikai-student .s-continue {
  margin-bottom: var(--s-pad);
}

/* The offer itself is a .s-tile, so the 48px tap target, the radius and the
   focus ring all come from there and cannot drift from the tiles under it.
   Only the fill is different, and only so the thing a child came back for
   reads as the first thing to press rather than a fourth subject. Must come
   after .s-tile -- same specificity, so it only overrides that block's
   background and border by sitting later in the file. White on --s-accent
   clears 4.5:1. */
.sikai-student .s-tile--continue {
  border-color: var(--s-accent-dark);
  background: var(--s-accent);
  color: #fff;
}

.sikai-student .s-tile--continue .s-tile__meta { opacity: 0.9; }

.sikai-student .s-continue__label {
  font-size: 0.8em;
  font-weight: 700;
  opacity: 0.9;
}

/* Nothing open right now is not a failure and may not look like one: the plain
   card surface every other calm message uses, never the locked tile's dashed
   grey and never anything red. */
.sikai-student .s-continue__clear {
  margin: 0;
}

/* ---------------------------------------------------------------- go deeper

   The "want more?" offer and the generated lesson it leads to. Everything
   here reuses .s-card, .s-worked and .s-explanation for the body -- a deep
   lesson must read as the same app, not as a bolted-on AI feature -- so these
   rules only cover what those do not already give.
*/
/* A published deep lesson's explanation is several sections joined with
   blank lines, and its section headings sit on their own line. HTML collapses
   both, which ran the whole thing together as one paragraph -- heading and
   body in the same sentence. pre-line restores the line structure the text
   already carries. Pack-authored explanations contain no newlines, so they
   render exactly as before. */
.sikai-student .s-explanation p { white-space: pre-line; }

.sikai-student .s-deep-offer {
  margin-top: var(--s-pad);
  text-align: center;
}

.sikai-student .s-deep-offer p {
  margin: 0 0 var(--s-gap);
}

.sikai-student .s-deep__heading {
  font-size: var(--s-text-lg);
  line-height: 1.25;
  margin: 0 0 var(--s-gap);
}

.sikai-student .s-deep__words {
  margin: 0;
}

.sikai-student .s-deep__words dt {
  font-weight: 700;
  margin-top: var(--s-gap);
}

.sikai-student .s-deep__words dd {
  margin: 0;
}

.sikai-student .s-deep__questions {
  margin: 0;
  padding-left: 1.2em;
}

.sikai-student .s-deep__questions li {
  margin-bottom: var(--s-gap);
}

/* A drawn figure is inline SVG with its own intrinsic width, so unlike
   .s-figure__img (which constrains an <img>) the constraint has to land on
   the svg element itself or a wide bar chart pushes the page sideways on a
   360px phone. */
.sikai-student .s-figure--drawn {
  margin: var(--s-gap) 0;
  overflow-x: auto;
}

.sikai-student .s-figure--drawn svg {
  max-width: 100%;
  height: auto;
}

/* ---- A question that is finished -------------------------------------
   Once a child has it right the controls close, so a stray tap cannot
   replace "You got it!" with a hint and take the Next button away. Only ever
   the response the child just gave stays at full strength -- the others step
   back rather than vanish, because a child rereading the question should
   still see what the options were. Never greyed to the point of unreadable:
   this is a finished question, not a disabled form. */
.sikai-student .s-choice:disabled { cursor: default; }

.sikai-student .s-choice:disabled:not(.is-correct):not(.is-try) {
  opacity: 0.6;
  border-color: var(--grid, #c9ccd1);
}

.sikai-student .s-button:disabled { opacity: 0.55; cursor: default; }

.sikai-student .s-numeric-input[readonly] { cursor: default; }

/* ---- Where to go next ------------------------------------------------
   Every screen ends with somewhere to go, and on a child's screen those have
   to look pressable. They were .s-link: a correct 48px tap target, and
   invisible as a control -- a seven-year-old reads a bold coloured line as
   more text, not as something to press. Watching a child hesitate on "Try
   the questions" is what this fixes.

   Two weights, never one. The forward action is filled and the way back is
   outlined, so a screen offering both does not ask a child to choose between
   two identical buttons -- which is what the topic-done screen did, with the
   two of them flush against each other and no gap at all. */
.sikai-student .s-nav {
  display: flex;
  flex-direction: column;
  gap: var(--s-gap);
  /* Both sides. On the feedback screen this row sits between the "You got
     it!" bubble and the question card below it, and with only a top margin
     the button butted straight into the card and read as part of it. */
  margin: var(--s-pad) 0;
}

/* Outlined, and a step down in size from the filled one above it. Still full
   width and still 48px tall: "secondary" here means visually quieter, never
   harder to hit -- back is the control a lost child needs most. */
.sikai-student .s-button--back {
  background: var(--s-card);
  border: 2.5px solid var(--s-accent);
  color: var(--s-accent-dark);
  font-size: var(--s-text);
}

/* button_to renders a <form>, so inside .s-nav the flex child is the form
   and the button inside it needs the full width itself. */
.sikai-student .s-nav .button_to { margin: 0; }

/* Progress page ------------------------------------------------------- */

.sikai-student .s-panel { margin-block: calc(var(--s-gap) * 2); }

/* Every section below the hero is a plain .s-card -- the page was five bare
   <section> elements floating on the gradient with nothing holding them,
   so the fix borrows the container this file already uses everywhere else
   rather than inventing a second one. */
.sikai-student .s-section-title {
  margin: 0 0 var(--s-gap);
  font-size: var(--s-text-lg);
  font-weight: 800;
}

/* ---- Hero: points and streak -----------------------------------------
   These are the headline numbers of the whole gamification feature, so they
   get the one bold surface on the page -- the same accent gradient
   .s-tile--continue uses on /learn for "the thing you came back for" --
   instead of reading as a body-weight paragraph like the rest of the page.
   var(--s-card) doubles as "white text" here: it is already #fff in the
   token block, so reusing it keeps this rule inside the tokens-only rule
   instead of writing a new raw color for the same value. White (var(--s-card))
   on var(--s-accent), the lighter end of the gradient, measures 5.7:1 --
   verified the same way the .s-tile--continue comment above already does. */
.sikai-student .s-hero {
  padding: var(--s-pad);
  border-radius: var(--s-radius);
  background: linear-gradient(135deg, var(--s-accent), var(--s-accent-dark));
  color: var(--s-card);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--s-accent-dark) 35%, transparent);
}
.sikai-student .s-hero__eyebrow {
  margin: 0 0 var(--s-gap);
  font-size: var(--s-text);
  font-weight: 700;
}
.sikai-student .s-hero__stats {
  display: flex;
  gap: var(--s-pad);
}
.sikai-student .s-hero__stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* A divider between points and streak, not a second card: both numbers are
   the same headline, so splitting them into two boxes would rank one over
   the other. color-mix over --s-card is this file's way of writing
   "translucent white" without a raw rgba(). */
.sikai-student .s-hero__stat + .s-hero__stat {
  padding-inline-start: var(--s-pad);
  border-inline-start: 2px solid color-mix(in srgb, var(--s-card) 35%, transparent);
}
.sikai-student .s-hero__value {
  font-size: calc(var(--s-text-xl) * 1.35);
  font-weight: 800;
  line-height: 1;
}
.sikai-student .s-hero__label { font-weight: 600; }
.sikai-student .s-hero__empty { margin: var(--s-gap) 0 0; font-weight: 600; }

.sikai-student .s-bars { list-style: none; padding: 0; display: grid; gap: var(--s-pad); }
.sikai-student .s-bar {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px var(--s-gap);
  align-items: center;
}
.sikai-student .s-bar__label { grid-column: 1; font-weight: 700; }
.sikai-student .s-bar__value {
  grid-column: 2;
  font-weight: 700;
  color: var(--s-accent-dark);
  font-variant-numeric: tabular-nums;
}
.sikai-student .s-bar__track {
  grid-column: 1 / -1;
  display: block;
  block-size: 14px;
  border-radius: var(--s-radius-pill);
  background: color-mix(in srgb, var(--s-ink) 10%, transparent);
  overflow: hidden;
}
.sikai-student .s-bar__fill {
  display: block;
  block-size: 100%;
  border-radius: var(--s-radius-pill);
  background: linear-gradient(90deg, var(--s-accent-dark), var(--s-accent));
}

/* Tight auto-fill grid, not the flex-wrap row the badge shelf used before --
   at minmax(88px,1fr) a 328px card interior (360px screen minus two 16px
   card paddings) fits three per row, so seven badges run three short rows
   instead of one tall column. */
.sikai-student .s-badges {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: var(--s-gap);
}
/* An earned badge is a miniature .s-tile: solid accent border, the same
   pressable offset shadow (recreated with color-mix over --s-accent rather
   than .s-tile's raw rgb(), since that literal predates the tokens-only
   rule and new rules here follow it), full-colour icon. */
.sikai-student .s-badge {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 4px;
  padding: 12px 6px;
  border: 2.5px solid var(--s-accent);
  border-radius: var(--s-radius);
  background: var(--s-card);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--s-accent) 26%, transparent);
  text-align: center;
}
.sikai-student .s-badge__icon { font-size: var(--s-text-xl); }
.sikai-student .s-badge__name { font-weight: 700; font-size: 0.85em; line-height: 1.2; }
/* Only rendered for earned badges now -- see the view. A locked badge shows
   its name only; the earn condition reappears once it is won rather than
   repeating a full sentence on all seven shelf slots at once. */
.sikai-student .s-badge__hint { font-size: 0.75em; opacity: 0.75; margin-top: 1px; }

/* Dimmed, never removed: the silhouette is what makes a badge worth wanting
   before it exists (D-18). Same dashed-border, no-shadow grammar as
   .s-tile--locked above, so a locked badge and a locked lesson read as the
   same kind of "not yet" rather than two different visual languages. Only
   the icon dims -- .s-badge__icon below -- because the review round that
   first measured this found the name and hint text failing WCAG AA
   (2.04:1 and 1.74:1 against --s-sky-bottom) once opacity 0.45 here
   compounded with .s-badge__hint's own opacity. --s-locked-ink is already
   the muted "not yet" token; dimming it a second time is what broke it.
   The badge is now an opaque white card rather than sitting directly on the
   gradient, which only raises that number: --s-locked-ink on --s-card
   measures 7.55:1. */
.sikai-student .s-badge--locked {
  border-style: dashed;
  border-color: var(--s-locked);
  box-shadow: none;
  color: var(--s-locked-ink);
}
.sikai-student .s-badge--locked .s-badge__icon { opacity: 0.45; filter: grayscale(1); }

.sikai-student .s-board { list-style: none; padding: 0; display: grid; gap: 2px; }
.sikai-student .s-board__row {
  display: flex;
  align-items: center;
  gap: var(--s-gap);
  padding-inline: 10px;
  padding-block: 8px;
  min-block-size: var(--s-tap);
  border-radius: var(--s-radius);
}
/* A faint zebra stripe -- color-mix over --s-ink rather than a raw grey --
   so five undecorated flex rows read as a list with rank order instead of a
   run-on paragraph of names and numbers. */
.sikai-student .s-board__row:nth-child(odd) {
  background: color-mix(in srgb, var(--s-ink) 5%, transparent);
}
.sikai-student .s-board__rank {
  inline-size: 1.75em;
  flex-shrink: 0;
  font-weight: 800;
  color: var(--s-accent-dark);
  text-align: center;
}
.sikai-student .s-board__name {
  flex: 1;
  min-inline-size: 0;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sikai-student .s-board__points {
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--s-accent-dark);
}
.sikai-student .s-board__mine {
  margin-block-start: var(--s-gap);
  padding: 10px var(--s-pad);
  border-radius: var(--s-radius);
  background: color-mix(in srgb, var(--s-accent) 10%, transparent);
  color: var(--s-accent-dark);
  font-weight: 700;
  text-align: center;
}

/* My lessons, nested inside .s-card here only (the only current caller that
   puts .s-list inside a card) -- a hairline divider between rows gives the
   list some structure now that it sits on a card surface instead of loose
   on the gradient. */
.sikai-student .s-card .s-list .s-list__row + .s-list__row {
  padding-block-start: var(--s-gap);
  border-block-start: 1px solid color-mix(in srgb, var(--s-ink) 8%, transparent);
}

.sikai-student .s-band {
  border-radius: var(--s-radius-pill);
  padding-inline: var(--s-gap);
  font-size: var(--s-text);
}
.sikai-student .s-band--got_it { background: color-mix(in srgb, var(--s-ok) 18%, transparent); color: var(--s-ok-dark); }
.sikai-student .s-band--getting_there { background: var(--s-try); color: var(--s-try-ink); }
.sikai-student .s-band--keep_going { background: color-mix(in srgb, var(--s-locked) 25%, transparent); color: var(--s-locked-ink); }

.sikai-student .s-empty { color: var(--s-locked-ink); }

/* Badge toasts -------------------------------------------------------- */

.sikai-student .s-badge-toasts {
  position: fixed;
  inset-block-start: var(--s-gap);
  inset-inline: var(--s-gap);
  z-index: 20;
  display: grid;
  gap: var(--s-gap);
}
.sikai-student .s-badge-toast {
  display: flex;
  gap: var(--s-gap);
  align-items: center;
  padding: var(--s-pad);
  border-radius: var(--s-radius);
  background: var(--s-card);
  color: var(--s-ink);
  box-shadow: 0 2px 10px color-mix(in srgb, var(--s-ink) 20%, transparent);
}
/* Task 9's partial referenced this class before it existed; matches the
   badge shelf's own icon size (.s-badge__icon) rather than inheriting body
   text size. */
.sikai-student .s-badge-toast__icon { font-size: var(--s-text-xl); flex-shrink: 0; }
.sikai-student .s-badge-toast__text { display: grid; }

/* Status strip -------------------------------------------------------- */

.sikai-student .s-strip {
  display: flex;
  align-items: center;
  gap: calc(var(--s-gap) * 2);
  padding: var(--s-pad);
  margin-block-end: var(--s-pad);
  border-radius: var(--s-radius);
  background: var(--s-card);
  text-decoration: none;
  color: inherit;
  /* --s-tap is 48px in this file, already above the 44px PLAT-01 floor. */
  min-block-size: var(--s-tap);
}
.sikai-student .s-strip__item { display: grid; }
.sikai-student .s-strip__value { font-size: var(--s-text-lg); font-weight: 700; line-height: 1.1; }
.sikai-student .s-strip__label { font-size: var(--s-text); color: var(--s-locked-ink); }
.sikai-student .s-strip__more { margin-inline-start: auto; font-size: var(--s-text-lg); color: var(--s-locked-ink); }

.sikai-student .s-xp-tick { font-weight: 600; color: var(--s-accent-dark); }

/* MUST stay last in this stylesheet. Media queries carry no specificity bonus,
   so an override placed above the rule it overrides is silently dead. This block
   overrides base rules for .s-keypad (line 154), so it must appear after them. */
/* Web is a first-class target (D-15). One layout that works at every width,
   not a phone column stretched onto a laptop. */
@media (min-width: 700px) {
  .sikai-student {
    --s-text:    18px;
    --s-text-lg: 23px;
    --s-text-xl: 32px;
    --s-pad:     24px;
  }

  .sikai-student .s-screen {
    max-width: 720px;
    padding: 32px;
  }

  .sikai-student .s-keypad { max-width: 380px; }
}
