/* components.css — the whole component set.
 *
 * No component names a colour, a size or a duration directly. Everything comes
 * from tokens.css, which is what makes "no red in the child's view" checkable
 * by reading one file instead of grepping the whole tree.
 *
 * There is no JavaScript in here and nothing needs any. Every state a child can
 * reach is a class the server rendered or a CSS state the browser already knows
 * (0011 — server-rendered HTML, minimal JS).
 *
 * Order: base, layout shell, type, then components grouped by the screen they
 * belong to. A new component goes next to the screen it serves, not at the end.
 */

/* --- Base ----------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-size: 18px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--size-base);
  line-height: var(--leading-ui);
  letter-spacing: 0;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--space-3);
  line-height: var(--leading-tight);
  font-weight: 700;
  /* Never all-caps: capitals remove the word-shape a developing reader uses. */
  text-transform: none;
  text-wrap: balance;
}

h1 { font-size: var(--size-2xl); }
h2 { font-size: var(--size-xl); }
h3 { font-size: var(--size-lg); }
h4 { font-size: var(--size-md); }

p {
  margin: 0 0 var(--space-4);
  max-width: var(--measure);
  /* Never justified: rivers of white space are hard for a weak reader. */
  text-align: left;
}

a {
  color: var(--accent);
  text-underline-offset: 3px;
}

a:hover { color: var(--accent-strong); }

/* One focus style, everywhere, and it is an ink outline. See tokens.css. */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Layout shell --------------------------------------------------------- */

.page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.page__body {
  flex: 1;
  width: 100%;
  max-width: var(--width-wide);
  margin: 0 auto;
  padding: var(--space-8) var(--space-6) var(--space-16);
}

/* A single form, a single decision, a single code to write down. */
.page__body--narrow { max-width: calc(var(--width-narrow) + var(--space-12)); }

/* Mostly reading: the privacy notice, the printed story. */
.page__body--read { max-width: calc(var(--width-read) + var(--space-12)); }

@media (max-width: 40rem) {
  .page__body { padding: var(--space-6) var(--space-4) var(--space-12); }
}

/* The landing page manages its own rhythm section by section, so the shell
 * gives it nothing at all.
 *
 * This has to sit *after* the narrow-screen rule above: a modifier class and
 * the class it modifies have the same specificity, so on a phone the media
 * query would otherwise win and inset every full-bleed band by 1rem — which
 * looks exactly like a broken layout rather than a deliberate one. */
.page__body--full {
  max-width: none;
  padding: 0 0 var(--space-16);
}

/* The page's own title lives in the page, not in the chrome. A screen name
 * squeezed into a 44px bar reads as a browser tab; the same words as an h1 with
 * air around them tell a child what they are looking at. */
.page__head {
  margin-bottom: var(--space-8);
  max-width: var(--measure);
}

/* Centred where the content under it is centred — the child's sign-in, where a
 * left-ranged title over a centred row of animals reads as two screens. */
.page__head--center {
  margin-inline: auto;
  text-align: center;
}

.page__head--center .page__lead { margin-inline: auto; }

/* Utility, used with it. Deliberately the only alignment utility that exists. */
.center {
  text-align: center;
  margin-inline: auto;
}

.page__title { margin-bottom: var(--space-2); }

.page__lead {
  color: var(--text-soft);
  font-size: var(--size-md);
  margin: 0;
}

.page__eyebrow {
  display: block;
  color: var(--text-faint);
  font-size: var(--size-sm);
  font-weight: 400;
  margin-bottom: var(--space-1);
}

/* --- Masthead ------------------------------------------------------------- */

.masthead {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-6);
  border-bottom: 1px solid var(--border);
  background: var(--surface-raised);
}

.masthead__inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  max-width: var(--width-wide);
  margin: 0 auto;
}

/* Holds the brand on the left and pushes everything else right, which
 * space-between alone would not do with three or more children. */
.masthead__spacer { margin-left: auto; }

.masthead__nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.masthead__link {
  color: var(--text-soft);
  text-decoration: none;
  font-size: var(--size-sm);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-xl);
  white-space: nowrap;
}

.masthead__link:hover {
  background: var(--surface-sunk);
  color: var(--text);
}

@media (max-width: 40rem) {
  .masthead { padding: var(--space-3) var(--space-4); }
  .masthead__link { padding: var(--space-2); }
}

/* --- Footer --------------------------------------------------------------- */

/* Quiet, and the only place the operator's name appears on a working screen.
 * A parent looking for "who is running this and how do I contact them" should
 * never have to hunt, and a child should never have it in their eyeline. */
.footer {
  border-top: 1px solid var(--border);
  background: var(--surface-raised);
  padding: var(--space-6);
  color: var(--text-faint);
  font-size: var(--size-sm);
}

.footer__inner {
  max-width: var(--width-wide);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
}

.footer a { color: var(--text-soft); }
.footer p { margin: 0; max-width: none; }

/* --- The mark ------------------------------------------------------------- */

/* The logo is inlined, not referenced, so that `color` reaches currentColor
 * inside it and --logo-highlight reaches the nib. One file serves the
 * terracotta mark on paper and the apricot one on a dark ground; there is no
 * second asset to keep in sync (0014). */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
  color: var(--logo);
  border-radius: var(--radius-sm);
  text-decoration: none;
}

.brand svg {
  display: block;
  height: 1.9rem;
  width: auto;
}

/* The wordmark set as type beside the mark. The lockup SVG carries its own
 * traced wording and is the brand moment; this is the everyday pairing, and it
 * stays legible at 1.9rem where a traced wordmark would not. */
.brand__word {
  font-size: var(--size-md);
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.01em;
}

/* The sign-in and landing screens are where the wordmark earns its space. */
.brand--lockup svg {
  height: auto;
  width: min(20rem, 72vw);
}

.brand--hero {
  display: flex;
  justify-content: center;
  margin: 0 auto var(--space-8);
}

/* --- Stacks and rows ------------------------------------------------------ */

.stack > * + * { margin-top: var(--space-4); }
.stack-tight > * + * { margin-top: var(--space-2); }
.stack-loose > * + * { margin-top: var(--space-8); }

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }
.row--baseline { align-items: baseline; }

/* A row of actions under a form or a piece of writing. The primary sits first
 * and the quiet ones follow; on a narrow screen they stack rather than
 * shrinking to unreadable widths. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.actions__spacer { margin-left: auto; }

@media (max-width: 34rem) {
  .actions { flex-direction: column; align-items: stretch; }
  .actions__spacer { margin-left: 0; }
  .actions .button { width: 100%; }
}

/* --- Sections ------------------------------------------------------------- */

/* The unit the grown-up area is built from: a heading, an optional line of
 * explanation, then content. Sections are separated by air, not by rules —
 * a page of boxed panels reads as a settings screen. */
.section { margin-top: var(--space-16); }

/* The first thing under the page title is already spaced by the title block. */
.section:first-child,
.page__head + .section { margin-top: 0; }

.section__head { margin-bottom: var(--space-6); }

.section__head h2 { margin-bottom: var(--space-2); }

.section__note {
  color: var(--text-soft);
  font-size: var(--size-sm);
  margin: 0;
}

/* --- Prose: the child's own words ----------------------------------------- */

/* Anywhere a child's writing appears — the textarea, the feedback view, the
 * shared page, the printed book — it is this. One typeface, one size, one
 * measure, so the desk already looks like the object being made (0014). */
.prose {
  font-family: var(--font-prose);
  font-size: var(--size-prose);
  line-height: var(--leading-prose);
  max-width: var(--measure);
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

/* The span the coach noticed. A wash and an underline, never a strike, never a
 * correction mark, and — the point — never red. */
.prose mark,
.prose .noticed {
  background: var(--accent-wash);
  color: inherit;
  border-bottom: 2px solid var(--accent);
  border-radius: 2px;
  padding: 0 1px;
}

/* Long-form grown-up reading: the privacy notice. Serif, because it is a
 * document and should look like one, at a measure that can actually be read. */
.reading {
  font-family: var(--font-prose);
  font-size: var(--size-base);
  line-height: var(--leading-prose);
}

.reading h2 {
  font-family: var(--font-ui);
  font-size: var(--size-lg);
  margin-top: var(--space-12);
}

/* Each section opens with its own h2, so the rule above would double up with
 * whatever spaces the sections themselves. */
.reading h2:first-child { margin-top: 0; }

/* The summary card before the notice and the consent card after it are separate
 * blocks, and a card's edge is not spacing. */
.card + .reading,
.reading + .card { margin-top: var(--space-12); }

.reading ul,
.reading ol {
  max-width: var(--measure);
  padding-left: var(--space-6);
}

.reading li { margin-bottom: var(--space-2); }

/* --- Buttons -------------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap);
  padding: var(--space-3) var(--space-6);
  border: 2px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease),
    border-color var(--fast) var(--ease);
}

.button:hover {
  background: var(--surface-sunk);
  color: var(--text);
}

.button:active { transform: translateY(1px); }

.button--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
  box-shadow: var(--shadow-low);
}

.button--primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--text-on-accent);
}

/* "I'm stuck" is always available and never shouts. A child who needs it is
 * already unhappy; a loud button makes asking feel like an admission. */
.button--quiet {
  background: transparent;
  border-color: transparent;
  color: var(--text-soft);
  font-weight: 400;
  padding-inline: var(--space-4);
}

.button--quiet:hover {
  background: var(--surface-sunk);
  color: var(--text);
}

.button--big {
  font-size: var(--size-md);
  padding: var(--space-4) var(--space-8);
  min-height: 3.4rem;
}

.button--block { width: 100%; }

.button[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* --- Cards and panels ----------------------------------------------------- */

.card {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-low);
}

.card--tint {
  background: var(--accent-faint);
  border-color: var(--accent-wash);
}

/* Stacked cards — one per child in the grown-up area — need air between them,
 * or two rounded rectangles touching read as one panel with a line in it. */
.card + .card { margin-top: var(--space-6); }

.card__title {
  font-size: var(--size-lg);
  margin-bottom: var(--space-2);
}

.panel {
  background: var(--surface-sunk);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.rule {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--space-8) 0;
}

/* A responsive grid of cards. One column on a phone, as many as fit above it,
 * and never a card narrower than its content wants. */
.grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
}

.grid--wide {
  grid-template-columns: repeat(auto-fill, minmax(min(24rem, 100%), 1fr));
}

/* --- Forms ---------------------------------------------------------------- */

/* A field is a label, its control, and an optional line of help — as one block
 * with its own spacing, so a form never needs a <br> or a wrapper div. */
.field {
  display: block;
  margin-bottom: var(--space-4);
}

/* A fieldset grouping radios is a field like any other. The browser default
 * draws a 2px inset border with a notch cut for the legend, which is the one
 * piece of native chrome that cannot be made to match anything. */
fieldset.field {
  border: 0;
  padding: 0;
  margin-inline: 0;
  min-width: 0;
}

fieldset.field > legend {
  padding: 0;
}

.field__label {
  display: block;
  font-weight: 700;
  font-size: var(--size-sm);
  margin-bottom: var(--space-1);
}

.field__help {
  display: block;
  color: var(--text-faint);
  font-size: var(--size-xs);
  margin-top: var(--space-1);
  max-width: var(--measure);
}

.field input,
.field select,
.field textarea,
.auth-card input,
.auth-card select {
  display: block;
  width: 100%;
  min-height: var(--tap);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
}

.field input:hover,
.field select:hover { border-color: var(--text-faint); }

.field input::placeholder { color: var(--text-faint); }

/* Two fields side by side where they belong together, one under the other when
 * there is no room for that to be legible. */
.field-row {
  display: grid;
  gap: 0 var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

/* A checkbox and its sentence. The whole line is the target, which matters on a
 * consent form more than anywhere else in the product. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  cursor: pointer;
  max-width: var(--measure);
}

.check:hover { border-color: var(--accent); }

.check input {
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  margin: 0;
  accent-color: var(--accent);
}

.check span { font-size: var(--size-sm); }

/* The one screen where a form is the whole page. */
.auth-card {
  max-width: var(--width-narrow);
  margin-inline: auto;
}

/* --- Notes ---------------------------------------------------------------- */

/* Pre-check messages, "I'm stuck" replies, autosave notices. An apricot wash
 * with the accent as a rule, and deliberately no colour of its own: a nudge is
 * instant, unarguable and not a judgement, so nothing here should read as
 * urgent. There is no --error token to reach for. */
.note {
  border-left: 4px solid var(--attention);
  background: var(--attention-wash);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  padding: var(--space-4) var(--space-6);
  color: var(--text);
  max-width: var(--measure);
}

.note p:last-child,
.note ul:last-child { margin-bottom: 0; }

.note--calm {
  border-left-color: var(--border);
  background: var(--surface-sunk);
  color: var(--text-soft);
}

/* Grown-up screens only. Same hue, deeper value, heavier rule: this did not
 * work, or this cannot be undone. See --alert in tokens.css. */
.note--warning {
  border-left-width: 6px;
  border-left-color: var(--alert);
  background: var(--alert-wash);
  color: var(--text);
  font-weight: 700;
}

.note--warning a { color: var(--alert); }

/* Full-bleed strip under the masthead. There are no inline styles anywhere in
 * this product: style-src is 'self' and stays that way. */
.banner {
  margin: 0;
  max-width: none;
  border-radius: 0;
  border-left: 0;
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) var(--space-6);
  font-size: var(--size-sm);
  text-align: center;
}

/* A row of links that wraps as words rather than as flex items, so no link is
 * broken across lines mid-phrase. */
.linkrow {
  display: block;
  color: var(--text-soft);
  font-size: var(--size-sm);
}

.linkrow a { white-space: nowrap; }

/* The nudge that suggests a next step. Ignorable by construction: it is a line
 * of text with a link, not a modal, and nothing waits on it. */
.nudge {
  color: var(--text-soft);
  font-size: var(--size-sm);
  max-width: var(--measure);
}

/* Nothing here yet, and that is fine. An empty state that looks like an error
 * makes a child think they broke something. */
.empty {
  border: 2px dashed var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-12) var(--space-6);
  text-align: center;
  color: var(--text-soft);
}

.empty p {
  margin-inline: auto;
  text-align: center;
}

.empty p:last-child { margin-bottom: 0; }

/* --- The landing page ----------------------------------------------------- */

/* The only screen in the product that is selling something, and it is selling
 * it to a parent. It gets more air and larger type than anywhere else, and it
 * still uses the same tokens: a landing page that does not look like the
 * product is a promise the product then breaks. */
.hero {
  background: var(--accent-faint);
  border-bottom: 1px solid var(--border);
  padding: var(--space-16) var(--space-6);
  text-align: center;
}

.hero__inner {
  max-width: var(--width-read);
  margin: 0 auto;
}

.hero__title {
  font-size: var(--size-4xl);
  line-height: 1.05;
  margin-bottom: var(--space-6);
}

.hero__lead {
  font-size: var(--size-md);
  color: var(--text-soft);
  margin: 0 auto var(--space-8);
  max-width: 34rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: center;
}

@media (max-width: 40rem) {
  .hero { padding: var(--space-12) var(--space-4); }
  .hero__title { font-size: var(--size-2xl); }
  .hero__actions { flex-direction: column; }
}

/* A band of the landing page. Alternating grounds give the page a rhythm
 * without any rules or boxes. */
.band {
  padding: var(--space-16) var(--space-6);
}

.band--sunk { background: var(--surface-sunk); }

.band__inner {
  max-width: var(--width-wide);
  margin: 0 auto;
}

.band__title {
  text-align: center;
  margin-bottom: var(--space-12);
}

@media (max-width: 40rem) {
  .band { padding: var(--space-12) var(--space-4); }
}

/* Three steps, numbered. The number is the ornament, so nothing else has to be.
 * Not a process diagram: a parent reads three sentences and stops. */
.steps {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
}

.step { counter-increment: step; }

.step::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin-bottom: var(--space-4);
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-strong);
  font-size: var(--size-lg);
  font-weight: 700;
}

.step h3 { margin-bottom: var(--space-2); }

.step p {
  color: var(--text-soft);
  margin: 0;
}

/* The trust list. This is the product's actual promise, so it is set as a list
 * of plain statements rather than as feature marketing. */
.promises {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.promise {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-4);
}

.promise strong {
  display: block;
  margin-bottom: var(--space-1);
}

.promise span {
  color: var(--text-soft);
  font-size: var(--size-sm);
}

/* --- The family code ------------------------------------------------------ */

/* Shown once, on purpose: only a one-way digest is stored. So on the one screen
 * where it appears it is the largest thing on the page, spaced so it can be
 * copied off a screen by hand without a transcription error. */
.code {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--size-3xl);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-align: center;
  color: var(--accent-strong);
  background: var(--surface-raised);
  border: 2px dashed var(--accent);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-4);
  margin: var(--space-6) 0;
  overflow-wrap: break-word;
  user-select: all;
}

@media (max-width: 34rem) {
  .code { font-size: var(--size-xl); letter-spacing: 0.08em; }
}

/* The child's entry of the same code. Same spacing as the display above so the
 * two read as the same object. */
.code-input {
  font-size: var(--size-xl);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-align: center;
  text-transform: uppercase;
}

.code-input::placeholder {
  font-weight: 400;
  letter-spacing: 0.08em;
}

/* --- The writing surface -------------------------------------------------- */

/* One column on a phone or a small tablet; the writing column and the coach
 * side by side above that. The writing column takes the space it is given and
 * the sidebar is fixed, because a writing box that grows with the window is
 * better than one that stays at a magazine measure while the window doesn't. */
.desk {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
  align-items: start;
}

@media (min-width: 62rem) {
  .desk { grid-template-columns: minmax(0, 1fr) 20rem; }
}

/* The prototype desk carries a third column — the parts strip — because a
 * multi-part story needs somewhere to move between parts. The live desk has one
 * part and does not, which is why this is a modifier rather than the default:
 * an empty 16rem column is what made the live desk look broken. */
@media (min-width: 74rem) {
  .desk--three { grid-template-columns: 13rem minmax(0, 1fr) 18rem; }
}

/* The sidebar sticks, so the coach and the round counter stay in view while a
 * child scrolls a long beginning. It never covers anything: it is its own
 * column. */
@media (min-width: 62rem) {
  .desk__aside {
    position: sticky;
    top: var(--space-6);
    display: grid;
    gap: var(--space-6);
  }
}

.desk__aside { display: grid; gap: var(--space-6); }

/* The bar is never hidden: the part's job and the criteria the child is being
 * asked for sit above the box they write in, not behind a button. */
.brief {
  border-left: 4px solid var(--accent);
  padding: var(--space-1) 0 var(--space-1) var(--space-6);
  margin-bottom: var(--space-6);
  max-width: var(--measure);
}

.brief__job {
  font-size: var(--size-md);
  font-weight: 700;
  margin: 0 0 var(--space-3);
}

.brief__criteria {
  margin: 0;
  padding-left: var(--space-6);
  color: var(--text-soft);
  font-size: var(--size-sm);
}

.brief__criteria li + li { margin-top: var(--space-1); }

.writing-box {
  width: 100%;
  min-height: 26rem;
  padding: var(--space-8);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: var(--shadow-low);

  /* Identical to .prose, deliberately. What the child types and what gets
   * printed are the same object seen at different moments. */
  font-family: var(--font-prose);
  font-size: var(--size-prose);
  line-height: var(--leading-prose);
  color: var(--text);
  resize: vertical;
}

@media (max-width: 40rem) {
  .writing-box { padding: var(--space-4); min-height: 18rem; }
}

/* A one-sentence answer to the coach's question, not a draft. A full-height
 * box here would read as "write more", which is the opposite of the ask. */
.writing-box--short { min-height: 6rem; }

.writing-box:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Word count, save state. Quiet on purpose — a counter that draws the eye turns
 * writing into a target-hitting exercise. */
.deskbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  color: var(--text-faint);
  font-size: var(--size-xs);
  margin-top: var(--space-3);
  padding-inline: var(--space-2);
  max-width: var(--measure);
}

/* --- Parts strip ---------------------------------------------------------- */

/* Soft containers, freely ordered (0006). Nothing here implies a sequence the
 * child has to follow. */
.parts {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.part {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  text-decoration: none;
}

.part[aria-current="page"] {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.part__dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--border);
  flex: none;
}

.part--started .part__dot { background: var(--text-faint); }
.part--done .part__dot { background: var(--done); }

.part__words {
  margin-left: auto;
  color: var(--text-faint);
  font-size: var(--size-xs);
}

/* --- The completion contract ---------------------------------------------- */

/* Every item is satisfiable by effort alone (0008). Moss appears here and
 * nowhere else in the product. */
.contract {
  list-style: none;
  margin: 0;
  padding: 0;
}

.contract__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  color: var(--text-soft);
}

.contract__item--done { color: var(--text); }

.contract__mark {
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  border: 2px solid var(--border);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--size-xs);
  line-height: 1;
}

.contract__item--done .contract__mark {
  border-color: var(--done);
  background: var(--done-wash);
  color: var(--done);
}

/* An outstanding item takes the accent, not red: it is not done *yet*, which is
 * a different statement from it being wrong. */
.contract__item--attention .contract__mark {
  border-color: var(--attention);
  color: var(--attention);
}

/* --- The coach ------------------------------------------------------------ */

.coach {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}

/* In the desk sidebar the coach stacks instead, so the speech bubble gets the
 * full column width rather than 60% of an already narrow one. */
.coach--stacked {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
}

.coach--stacked .coach__figure { align-self: flex-start; }
.coach--stacked .coach__speech { max-width: none; }

.coach__figure {
  flex: none;
  width: 5.5rem;
  height: 5.5rem;
}

.coach__speech {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-6);
  box-shadow: var(--shadow-low);
  max-width: 34rem;
}

.coach__speech p:last-child { margin-bottom: 0; }

.coach__name {
  display: block;
  font-size: var(--size-xs);
  color: var(--text-faint);
  margin-bottom: var(--space-1);
}

/* The coach's own words are interface, not prose: they are not the child's
 * writing and must not look like it. */
.coach__question {
  font-size: var(--size-md);
  font-weight: 700;
  margin: var(--space-2) 0 0;
}

/* Four states, four classes, no script. See coach.svg for what each part is. */
.coach__figure .blinker {
  animation: coach-blink 6s var(--ease) infinite;
  transform-origin: center;
}

.coach--reading .coach__figure .body { animation: coach-breathe 2.4s var(--ease) infinite; }
.coach--reading .coach__figure .pupil { transform: translateY(1.5px); }
.coach--thinking .coach__figure .ear-left { animation: coach-twitch 3.2s var(--ease) infinite; transform-origin: 34px 26px; }
.coach--asking .coach__figure .head { transform: rotate(-5deg); transform-origin: 50px 52px; }
.coach--asking .coach__figure .ear-left,
.coach--asking .coach__figure .ear-right { transform: translateY(-2px); }

@keyframes coach-blink {
  0%, 92%, 100% { transform: scaleY(1); }
  95% { transform: scaleY(0.08); }
}

@keyframes coach-breathe {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(1.2px); }
}

@keyframes coach-twitch {
  0%, 88%, 100% { transform: rotate(0deg); }
  92% { transform: rotate(-9deg); }
  96% { transform: rotate(4deg); }
}

/* --- The looks counter ---------------------------------------------------- */

/* Two looks per beginning. Shown as two shapes rather than "2 of 2 complete",
 * because a child counts dots faster than they read a fraction, and a fraction
 * looks like a score. */
.looks {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.look {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: var(--surface-sunk);
}

.look--used {
  border-color: var(--accent);
  background: var(--accent);
}

/* --- Shelf and covers ----------------------------------------------------- */

.shelf {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr));
  gap: var(--space-8) var(--space-6);
  list-style: none;
  margin: 0;
  padding: 0;
}

.cover-link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.cover {
  display: block;
  width: 100%;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-high);
  overflow: hidden;
  transition: transform var(--base) var(--ease), box-shadow var(--base) var(--ease);
}

.cover-link:hover .cover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-high);
}

.cover-caption {
  margin-top: var(--space-3);
  font-size: var(--size-sm);
}

.cover-caption__state {
  color: var(--text-faint);
  font-size: var(--size-xs);
}

/* A story on the shelf that has no cover yet. A spine and a title, drawn from
 * the same tokens, so an unfinished story still looks like a book rather than
 * like a table row. */
.spine {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: 100%;
  aspect-ratio: 2 / 3;
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  border-left: 6px solid var(--accent);
  background: var(--surface-raised);
  box-shadow: var(--shadow-low);
  transition: transform var(--base) var(--ease);
}

.cover-link:hover .spine { transform: translateY(-4px); }

.spine__title {
  font-family: var(--font-prose);
  font-size: var(--size-md);
  font-weight: 700;
  line-height: var(--leading-tight);
  overflow-wrap: break-word;
}

.spine__meta {
  color: var(--text-faint);
  font-size: var(--size-xs);
}

/* The card that starts a new story. Same shape as a book, so it sits in the
 * shelf grid rather than beside it. */
.shelf__new {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  aspect-ratio: 2 / 3;
  padding: var(--space-4);
  border: 2px dashed var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-soft);
  font: inherit;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.shelf__new:hover {
  border-color: var(--accent);
  color: var(--accent-strong);
  background: var(--accent-faint);
}

.shelf__plus {
  font-size: var(--size-2xl);
  line-height: 1;
}

/* --- Chips, dials, choosers ----------------------------------------------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

/* The child's own sentences, offered back for selection (the story map's
 * worst-moment question). Their words, so their typeface. */
.chip--sentence {
  font-family: var(--font-prose);
  text-align: left;
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  max-width: var(--measure);
}

.chip[aria-pressed="true"],
.chip--selected {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.dial {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  min-width: 9rem;
}

.dial__label {
  font-size: var(--size-xs);
  text-transform: none;
  color: var(--text-faint);
}

.dial__value {
  font-size: var(--size-lg);
  font-weight: 700;
  text-align: center;
}

/* --- Avatars -------------------------------------------------------------- */

/* Six drawn animals, one per child. Drawn rather than lettered because this is
 * the sign-in screen for an eight-year-old: picking your own animal out of a
 * row is a different act from reading your name off a list. */
/* Capped and centred rather than stretched across the page. Two siblings on a
 * 1400px monitor should be two cards you choose between, not two halves of the
 * screen — the choice is the whole screen, so it has to look like one. */
.avatars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 16rem));
  justify-content: center;
  gap: var(--space-6);
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: var(--width-read);
}

.avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: inherit;
}

/* Holds either a drawn animal or a single letter — the prototype screens have
 * no avatar to draw, only an initial — so it carries a display type size as
 * well as the geometry an inline SVG fills. */
.avatar__disc {
  display: grid;
  place-items: center;
  width: 6.5rem;
  height: 6.5rem;
  padding: var(--space-2);
  border-radius: 50%;
  background: var(--accent-faint);
  border: 3px solid var(--accent);
  color: var(--accent-strong);
  font-size: var(--size-2xl);
  font-weight: 700;
  line-height: 1;
}

.avatar__disc svg {
  width: 100%;
  height: 100%;
  display: block;
}

.avatar__disc--sm {
  width: 3.5rem;
  height: 3.5rem;
  border-width: 2px;
  padding: var(--space-1);
  font-size: var(--size-md);
}

.avatar__name {
  font-size: var(--size-md);
  font-weight: 700;
}

/* Choosing an animal for a new child. A radio group that looks like the thing
 * it picks: the input is hidden from sight but not from the keyboard, and the
 * disc carries both the checked state and the focus ring. */
.avatar-picks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-2);
}

.avatar-pick {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  font-size: var(--size-xs);
  color: var(--text-soft);
}

.avatar-pick .avatar__disc {
  background: transparent;
  border-color: var(--border);
  color: var(--text-faint);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
    color var(--fast) var(--ease);
}

.avatar-pick:hover .avatar__disc { border-color: var(--text-faint); }

.avatar-pick input:checked + .avatar__disc {
  background: var(--accent-faint);
  border-color: var(--accent);
  color: var(--accent-strong);
}

.avatar-pick input:focus-visible + .avatar__disc {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

/* One child's sign-in: their animal, their name, their PIN, their button, as a
 * single card so two siblings on one device never mistype into each other's. */
.avatar-login {
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  padding: var(--space-6) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: var(--shadow-low);
  height: 100%;
}

.avatar-login__hint {
  font-size: var(--size-xs);
  color: var(--text-faint);
}

.avatar-login input {
  width: 100%;
  max-width: 9rem;
  min-height: var(--tap);
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--size-lg);
  font-weight: 700;
  letter-spacing: 0.3em;
  text-align: center;
}

/* --- The guardian area ---------------------------------------------------- */

/* A child, as the grown-up area shows them: their animal, their name, what
 * they have written. Never a progress figure and never a comparison. */
.child-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.child-head h3 { margin: 0; }

.child-head__meta {
  color: var(--text-faint);
  font-size: var(--size-sm);
}

/* One story in the grown-up list. The title leads, the actions are quiet and
 * to the right, and there is no destructive control anywhere in the row. */
.story-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

.story-row:last-child { border-bottom: 0; }

.story-row__title {
  font-family: var(--font-prose);
  font-weight: 700;
  font-size: var(--size-md);
}

.story-row__meta {
  color: var(--text-faint);
  font-size: var(--size-sm);
}

.story-row__actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  font-size: var(--size-sm);
}

.stories-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Everything that cannot be undone, in one place, below everything else, behind
 * a disclosure. Not hidden — a parent who wants to delete their child's data
 * must be able to find it — but never adjacent to a control they use daily. */
.danger {
  margin-top: var(--space-16);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-sunk);
  padding: var(--space-6);
}

.danger > summary {
  cursor: pointer;
  font-weight: 700;
  color: var(--text-soft);
  list-style-position: inside;
}

.danger > summary:hover { color: var(--text); }

.danger[open] > summary { margin-bottom: var(--space-6); }

.danger__item + .danger__item {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--border);
}

.danger__item h3 {
  font-size: var(--size-md);
  margin-bottom: var(--space-2);
}

.danger__item p {
  color: var(--text-soft);
  font-size: var(--size-sm);
}

/* --- The guardian growth strip -------------------------------------------- */

/* Not a chart. Three segments, the current band filled, a small arrow for
 * direction of travel, muted palette, ordered by most recently observed and
 * never by weakness. A view sorted by weakness *is* a report card however it is
 * styled (0014, and the Milestone 4 kill criterion). */
.growth {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-6);
}

.growth__skill {
  display: grid;
  gap: var(--space-2);
  grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr);
  align-items: start;
}

@media (max-width: 44rem) {
  .growth__skill { grid-template-columns: 1fr; }
}

.growth__name { font-weight: 700; }

.growth__strip {
  display: flex;
  gap: 3px;
  align-items: center;
}

.growth__segment {
  height: 0.6rem;
  width: 2.6rem;
  border-radius: 3px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
}

.growth__segment--filled {
  background: var(--accent);
  border-color: var(--accent);
}

.growth__arrow {
  margin-left: var(--space-2);
  color: var(--text-faint);
  font-size: var(--size-xs);
}

/* The evidence is more use to a parent than the label is. */
.growth__excerpt {
  font-family: var(--font-prose);
  font-size: var(--size-sm);
  color: var(--text-soft);
  border-left: 2px solid var(--border);
  padding-left: var(--space-3);
  margin: 0;
}

.growth__seen {
  color: var(--text-faint);
  font-size: var(--size-xs);
}

/* --- Book preview --------------------------------------------------------- */

.book {
  display: grid;
  gap: var(--space-6);
  justify-items: center;
}

.book__page {
  width: min(100%, 34rem);
  aspect-ratio: 2 / 3;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-high);
  padding: var(--space-12) var(--space-8);
  overflow: auto;
}

/* --- The printed story ---------------------------------------------------- */

.print-story {
  max-width: var(--width-read);
  margin: 0 auto;
}

.print-story h1,
.print-story .byline {
  text-align: center;
}

.print-story .byline {
  color: var(--text-soft);
  font-family: var(--font-prose);
  font-style: italic;
  margin-bottom: var(--space-12);
}

.print-story .prose {
  max-width: none;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-12) var(--space-8);
  box-shadow: var(--shadow-low);
}

@media print {
  .masthead,
  .footer,
  .banner,
  .no-print {
    display: none !important;
  }
  .page,
  .page__body,
  .print-story {
    max-width: none;
    margin: 0;
    padding: 0;
  }
  .print-story .prose {
    border: 0;
    box-shadow: none;
    padding: 0;
  }
}

/* --- Styleguide only ------------------------------------------------------ */

.sg-section { margin-bottom: var(--space-16); }

.sg-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
}

.sg-swatch__chip {
  height: 4rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}

.sg-swatch__name {
  font-size: var(--size-xs);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.sg-frames {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.sg-frame {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}
