/* 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).
 */

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

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

/* Live account and encounter forms. Kept deliberately plain: the child-facing
 * desk remains the visual centre, while grown-up controls read as controls. */
.auth-card,
.policy {
  max-width: 42rem;
  margin-inline: auto;
}

.auth-card input,
.policy input,
.card input,
.card select,
.row input {
  display: block;
  width: 100%;
  margin-top: var(--space-1);
  padding: .7rem .8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
}

.avatar-login {
  display: grid;
  gap: var(--space-2);
  justify-items: center;
}

.desk--single {
  grid-template-columns: minmax(0, 1fr) minmax(12rem, 18rem);
}

.row--between {
  justify-content: space-between;
}

.print-story {
  max-width: 42rem;
  margin: 2rem auto;
}

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

@media print {
  .masthead,
  .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;
  }
}

@media (max-width: 48rem) {
  .desk--single {
    grid-template-columns: 1fr;
  }
}

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 {
  margin: 0 0 var(--space-3);
  line-height: 1.25;
  font-weight: 700;
  /* Never all-caps: capitals remove the word-shape a developing reader uses. */
  text-transform: none;
}

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

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;
}

/* 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 --------------------------------------------------------------- */

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

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

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

.masthead__title {
  font-size: var(--size-md);
  font-weight: 700;
  margin: 0;
  /* Holds the mark and the screen name together on the left and pushes Back to
   * the right, which space-between alone would not do with three children. */
  margin-right: auto;
}

/* --- 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;
  flex: none;
  color: var(--logo);
  border-radius: var(--radius-sm);
}

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

/* The sign-in screen is the one place the wordmark earns its space. */
.brand--lockup svg {
  height: auto;
  width: min(18rem, 70vw);
}

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

.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);
}

/* --- 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;
}

/* --- 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 transparent;
  border-radius: var(--radius-xl);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}

.button:hover { background: var(--surface-sunk); }
.button:active { transform: translateY(1px); }

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

.button--primary:hover { background: var(--accent-strong); }

/* "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: var(--border);
  color: var(--text-soft);
  font-weight: 400;
}

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

.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);
}

.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;
}

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

.desk {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}

@media (min-width: 60rem) {
  .desk {
    grid-template-columns: 14rem minmax(0, 1fr) 16rem;
    align-items: start;
  }
}

/* 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-2) 0 var(--space-2) var(--space-4);
  margin-bottom: var(--space-4);
}

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

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

.writing-box {
  width: 100%;
  min-height: 22rem;
  padding: var(--space-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  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;
}

/* 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-2);
}

/* --- 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);
}

/* --- Nudges and 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-sm) var(--radius-sm) 0;
  padding: var(--space-3) var(--space-4);
  color: var(--text);
  max-width: var(--measure);
}

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

/* 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;
  font-size: var(--size-sm);
}

/* 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 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-soft);
  font-size: var(--size-sm);
}

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

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

.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);
  box-shadow: var(--shadow-low);
  max-width: 34rem;
}

/* 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); }
}

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

.shelf {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 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;
}

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

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

/* --- 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 -------------------------------------------------------------- */

.avatars {
  display: flex;
  gap: var(--space-8);
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

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

.avatar__disc {
  width: 7rem;
  height: 7rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-wash);
  border: 3px solid var(--accent);
  font-size: var(--size-2xl);
  font-weight: 700;
  color: var(--accent-strong);
}

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

/* --- 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;
}

/* --- 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);
}
