/*
  Base application styles. Layout primitives and the SJ design-system component
  styles live here, and every value comes from tokens.css.

  The child-facing controls are built as "stickers": a rounded shape with a
  solid lower edge that compresses under the finger. A four-year-old reads that
  as a physical button in a way a flat rectangle with a hover state does not,
  and it costs one box-shadow rather than an animation library.

  Nothing uses scoped CSS (.razor.css) yet. If a component ever needs it, also
  restore the ScriptureJourney.App.styles.css link in index.html: the SDK emits
  that bundle only once at least one scoped stylesheet exists.
*/

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

html {
  -webkit-text-size-adjust: 100%;
  font-size: calc(100% * var(--sj-text-scale));
}

body {
  margin: 0;
  min-height: 100dvh;
  background-color: var(--sj-color-canvas);
  background-image: var(--sj-gradient-canvas);
  background-attachment: fixed;
  color: var(--sj-color-ink);
  font-family: var(--sj-font-body);
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-normal);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  font-family: var(--sj-font-display);
  line-height: var(--sj-leading-tight);
  margin: 0;
}

p {
  margin: 0;
}

img,
svg,
video {
  max-width: 100%;
}

/* --------------------------------------------------------------- backdrop */

/*
  Soft fields of colour behind the whole app. Built from radial gradients rather than blurred
  elements: a blur filter over a full-screen layer is expensive on the cheap tablets a lot of
  these families are using, and at this softness the result is indistinguishable.

  Decorative and inert — it sits under everything and never receives a pointer.
*/
.sj-aurora {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.sj-aurora__field {
  position: absolute;
  border-radius: 50%;
}

.sj-aurora__field--one {
  top: -18vh;
  inset-inline-start: -12vw;
  width: 70vw;
  height: 70vw;
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--sj-color-accent) 30%, transparent) 0%,
    transparent 68%
  );
}

.sj-aurora__field--two {
  top: 22vh;
  inset-inline-end: -22vw;
  width: 78vw;
  height: 78vw;
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--sj-color-sky) 24%, transparent) 0%,
    transparent 68%
  );
}

.sj-aurora__field--three {
  bottom: -28vh;
  inset-inline-start: 12vw;
  width: 66vw;
  height: 66vw;
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--sj-color-primary) 18%, transparent) 0%,
    transparent 68%
  );
}

.sj-skip-link {
  position: absolute;
  top: calc(var(--sj-safe-top) + var(--sj-space-2));
  left: var(--sj-space-2);
  z-index: var(--sj-z-dialog);
  padding: var(--sj-space-2) var(--sj-space-4);
  background: var(--sj-color-surface);
  border-radius: var(--sj-radius-md);
  box-shadow: var(--sj-shadow-md);
  color: var(--sj-color-primary);
  font-weight: var(--sj-weight-medium);
  transform: translateY(-200%);
  transition: transform var(--sj-duration-fast) var(--sj-ease-standard);
}

.sj-skip-link:focus {
  transform: translateY(0);
}

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

/* Sits above the decorative backdrop. */
.sj-shell {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  padding-left: var(--sj-safe-left);
  padding-right: var(--sj-safe-right);
}

.sj-shell__header {
  position: sticky;
  top: 0;
  z-index: var(--sj-z-header);
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
  min-height: var(--sj-header-height);
  padding: var(--sj-safe-top) var(--sj-space-4) 0;
  background: var(--sj-glass-surface);
  backdrop-filter: var(--sj-glass-blur);
  -webkit-backdrop-filter: var(--sj-glass-blur);
  border-bottom: 1px solid var(--sj-glass-border);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--sj-color-border) 70%, transparent);
}

/*
  Where the frosted effect is unavailable the header would be a translucent strip with content
  scrolling through it, so it falls back to an opaque one.
*/
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sj-shell__header {
    background: var(--sj-color-canvas);
  }
}

/*
  A modal opened from inside the header — the support gate — must cover the viewport, and
  backdrop-filter makes the header the containing block for every fixed descendant, so the
  modal's inset: 0 resolved to a 55px strip (the same mechanism as the sj-rise theater trap,
  one element over). The glass is invisible under an open modal anyway, so it stands down
  while one is open; measured live, the modal went from 55px tall to the whole viewport.
*/
.sj-shell__header:has(.sj-modal) {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.sj-shell__brand {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--sj-space-2);
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
  color: var(--sj-color-ink);
  text-decoration: none;
  /* The product name never wraps or truncates; the language control yields space instead. */
  white-space: nowrap;
}

.sj-shell__brand-mark {
  flex: none;
  width: 2rem;
  height: 2rem;
}

.sj-shell__header-actions {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  margin-inline-start: auto;
}

/*
  Support-the-project pill (the owner's decision: on every page). A warm berry-tinted link in the
  header — the heart carries the meaning, and at phone width the word is clipped, never
  display: none, so the accessible name stays whole exactly as the player bar's labels do.
*/
.sj-support__button {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-1);
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-1) var(--sj-space-3);
  border: 1px solid color-mix(in srgb, var(--sj-color-berry) 35%, transparent);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-berry-soft);
  color: var(--sj-color-ink);
  font-family: var(--sj-font-body);
  font-size: var(--sj-text-sm);
  font-weight: 700;
  text-decoration: none;
}

.sj-support__button:hover {
  border-color: var(--sj-color-berry);
}

.sj-support__mark {
  color: var(--sj-color-berry);
  font-size: var(--sj-text-md);
  line-height: 1;
}

/* Small phones: the heart stays, the word is clipped so the wordmark keeps its room. */
@media (max-width: 26rem) {
  .sj-support__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* The support page: the pitch, the two ways to give, and the invitation to keep giving. */

/*
  The giving content stays under the hero at a width where two option cards can sit shoulder to
  shoulder without either becoming a ribbon of prose.
*/
.sj-support-content {
  max-width: 52rem;
  margin-inline: auto;
}

/* The faces in the painting sit in the upper half; a tall crop must keep them, not the robes. */
.sj-support-hero .sj-hero__image {
  object-position: 50% 30%;
}

/* Three quiet tiles saying what a gift makes, each with its own soft-coloured disc. */
.sj-support-impact {
  display: grid;
  gap: var(--sj-space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 40rem) {
  .sj-support-impact {
    grid-template-columns: repeat(3, 1fr);
  }
}

.sj-support-impact__tile {
  display: grid;
  justify-items: start;
  align-content: start;
  gap: var(--sj-space-2);
  padding: var(--sj-space-3) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-sm);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

.sj-support-impact__mark {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  font-size: var(--sj-text-lg);
}

.sj-support-impact__mark--art {
  background: var(--sj-color-berry-soft);
}

.sj-support-impact__mark--voice {
  background: var(--sj-color-sky-soft);
}

.sj-support-impact__mark--free {
  background: var(--sj-color-leaf-soft);
}

/*
  The compound selector is load-bearing: `.sj-card { display: block }` sits later in this file,
  and at equal specificity the later rule wins — the same shape as the studio's `.creator` vs
  `.rz-layout` defect. Found by computing the style, not by reading the markup.
*/
.sj-card.sj-support-locked {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--sj-space-3);
  padding-block: var(--sj-space-6);
}

.sj-support-locked__mark {
  display: grid;
  place-items: center;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  background: var(--sj-color-accent-soft);
  font-size: var(--sj-text-xl);
}

/* The two ways to give, shoulder to shoulder once there is room for both. */
.sj-support-options {
  display: grid;
  gap: var(--sj-space-4);
}

@media (min-width: 44rem) {
  .sj-support-options {
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
  }
}

/* Each option is a column whose actions sink to the bottom, so the two cards line up. The
   compound selector is load-bearing: `.sj-card`'s own display sits later in this file. */
.sj-card.sj-support-option {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-3);
}

.sj-support-option__actions {
  display: grid;
  gap: var(--sj-space-2);
  margin-top: auto;
}

/* The card option is the primary path and wears a quiet teal crown to say so. */
.sj-support-option--card {
  border-top: 4px solid var(--sj-color-primary);
}

.sj-support-zelle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sj-space-4);
  margin-top: auto;
}

/*
  The QR sits on its own white card whatever the page colour: a scanner wants quiet contrast, and
  the SVG's built-in quiet zone plus this border is what a camera actually needs.
*/
.sj-support-zelle__qr {
  width: min(11rem, 100%);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: #ffffff;
  box-shadow: var(--sj-shadow-sm);
}

.sj-support-zelle__number {
  display: grid;
  gap: var(--sj-space-1);
  margin: 0;
}

.sj-support-zelle__number strong {
  font-size: var(--sj-text-lg);
}

/* The share row: the free way to give, laid sideways so it reads as an aside, not a third bill.
   Compound for the same reason as the other two card rules. */
.sj-card.sj-support-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sj-space-4);
}

.sj-support-share__copy {
  flex: 1 1 20rem;
  display: grid;
  gap: var(--sj-space-2);
}

.sj-support-share > .sj-button {
  flex: none;
}

.sj-support-often {
  display: flex;
  align-items: baseline;
  gap: var(--sj-space-3);
  padding: var(--sj-space-4);
  border: 1px solid color-mix(in srgb, var(--sj-color-accent) 40%, transparent);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-accent-soft);
  color: var(--sj-color-ink);
}

.sj-support-often__mark {
  flex: none;
  color: var(--sj-color-berry);
  font-size: var(--sj-text-lg);
  line-height: 1;
}

.sj-support-smallprint {
  margin-block-end: 0;
  color: var(--sj-color-ink-subtle);
  font-size: var(--sj-text-xs);
}

.sj-support-error {
  color: var(--sj-color-danger);
}

.sj-support-thanks-mark {
  font-size: var(--sj-text-3xl);
  color: var(--sj-color-berry);
  line-height: 1;
}

.sj-support-share-outcome {
  margin-block-end: 0;
  color: var(--sj-color-ink-muted);
}

/*
  The support snackbar. Rendered by the layout, outside .sj-page, so fixed means the viewport.
  It sits above where the bottom nav lives — the stories list has one, and on screens that do
  not, floating a step higher is the cheaper wrong than covering a nav.

  The look is the product's, not a cookie banner's: a heart medallion, a display-font headline
  with one supporting line under it, and a warm gold-washed card. Its arrival lives in motion.css
  with every other animation.
*/
.sj-snackbar {
  position: fixed;
  inset-inline: var(--sj-space-3);
  bottom: calc(var(--sj-bottom-nav-height) + var(--sj-space-3) + var(--sj-safe-bottom));
  z-index: var(--sj-z-overlay);
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: var(--sj-space-3);
  row-gap: var(--sj-space-3);
  max-width: 36rem;
  margin-inline: auto;
  padding: var(--sj-space-4);
  border: 1px solid color-mix(in srgb, var(--sj-color-accent) 45%, transparent);
  border-radius: var(--sj-radius-xl);
  background: linear-gradient(
    180deg,
    var(--sj-color-surface),
    color-mix(in srgb, var(--sj-color-accent-soft) 55%, var(--sj-color-surface))
  );
  box-shadow:
    var(--sj-shadow-lg),
    0 0 0 6px color-mix(in srgb, var(--sj-color-accent) 12%, transparent);
}

.sj-snackbar__badge {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--sj-color-berry-soft);
  color: var(--sj-color-berry);
  font-size: var(--sj-text-lg);
  line-height: 1;
}

.sj-snackbar__copy {
  display: grid;
  gap: 2px;
}

.sj-snackbar__title {
  margin: 0;
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: 700;
  line-height: 1.2;
  color: var(--sj-color-ink);
}

.sj-snackbar__line {
  margin: 0;
  font-size: var(--sj-text-sm);
  color: var(--sj-color-ink-muted);
}

/* The actions span the card's full width: the CTA gets room instead of squeezing beside prose. */
.sj-snackbar__actions {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
}

.sj-snackbar__cta {
  flex: 1;
  justify-content: center;
  text-decoration: none;
}

.sj-snackbar__dismiss {
  flex: none;
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-1) var(--sj-space-3);
  border: 0;
  border-radius: var(--sj-radius-pill);
  background: transparent;
  color: var(--sj-color-ink-muted);
  font-family: var(--sj-font-body);
  font-size: var(--sj-text-sm);
  font-weight: 700;
  cursor: pointer;
}

.sj-snackbar__dismiss:hover {
  color: var(--sj-color-ink);
  background: color-mix(in srgb, var(--sj-color-ink) 6%, transparent);
}

/*
  Phones: a sheet across the foot of the screen, rather than a card floating in the middle of it.

  It is fixed over the screen it opens on, and one of those two screens is the child's story shelf.
  At 375x812 it measured 232px tall sitting at y=480, while the first story card began at 570 — so
  on the library it covered the shelf heading and every pixel of shelf that was left, and a child
  arriving at the stories list saw a donation card and no story.

  It is two rows and nothing wasted:

      ♥  Help us tell the next story      Not now
      [        Give what you like        ]

  The ask gets the whole of its row, and "Not now" goes up beside the headline to give it that.
  Sharing the row leaves the button 222px at the "Big" word size against the 245px its label needs,
  so it wrapped to two lines inside itself — and a wrapped button reads as broken where a wrapped
  sentence reads as a sentence. Widths that depend on a label fitting are also the first thing a
  translation breaks: "Give what you like" is three words in English and rarely three anywhere else.

  The card spans the screen and sits down on the navigation, square along the bottom and rounded
  only at the top, so it reads as a sheet the phone raised rather than a desktop card that shrank.
  Its border survives on the top edge alone, which leaves a warm hairline where it meets the shelf,
  and the ring of accent in the shadow goes — a glow all the way round a shape with no sides is
  just a band. What replaces it is a shadow thrown upwards, which is the only direction there is
  anything to lift away from.

  The medallion stays and gets smaller. It is the one warm thing on the bar and dropping it left a
  plain box of type; at 2rem it costs 32px of a row the headline was going to occupy anyway.

  The headline and the ask both step down one size. On the ask that is not cosmetic: at the "Big"
  word size, where every rem in this product is multiplied by 1.2, "Give what you like" at the
  desktop size needs 245px of a 223px column and wraps to two lines inside its own button, which is
  what made the old bar 183px tall.

  The supporting line is the one thing not shown here, and it is a real loss stated as one: it says
  why the ask is being made. The headline and the button still make a complete and honest request,
  the sentence itself is the first thing on /support where the button goes, and 40px of a phone is
  what it costs on the screen a child came to for stories. Restore it by deleting the one rule.
*/
@media (max-width: 30rem) {
  .sj-snackbar {
    inset-inline: 0;
    bottom: calc(var(--sj-bottom-nav-height) + var(--sj-safe-bottom));
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "badge copy dismiss"
      "cta cta cta";
    align-items: center;
    /* Every pixel of this row is width the headline is not getting: at the default word size the
       tighter gap is the difference between one line and two in English. It will still wrap in
       Portuguese, which is the point of the ask having a row to itself. */
    column-gap: var(--sj-space-2);
    row-gap: var(--sj-space-2);
    padding: var(--sj-space-3) var(--sj-space-4);
    border-inline: 0;
    border-block-end: 0;
    border-start-start-radius: var(--sj-radius-xl);
    border-start-end-radius: var(--sj-radius-xl);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    box-shadow: 0 -6px 24px color-mix(in srgb, var(--sj-color-ink) 14%, transparent);
  }

  .sj-snackbar__badge {
    grid-area: badge;
    width: 1.75rem;
    height: 1.75rem;
    font-size: var(--sj-text-sm);
  }

  .sj-snackbar__copy {
    grid-area: copy;
  }

  .sj-snackbar__title {
    font-size: var(--sj-text-md);
  }

  .sj-snackbar__line {
    display: none;
  }

  /*
    The actions stop being a row of their own and become two grid items, so the ask can have the
    width of the sheet while "Not now" keeps its place at the end of the same line.
  */
  .sj-snackbar__actions {
    display: contents;
  }

  .sj-snackbar__cta {
    grid-area: cta;
    font-size: var(--sj-text-md);
  }

  .sj-snackbar__dismiss {
    grid-area: dismiss;
    justify-self: end;
    padding-inline: var(--sj-space-2);
  }
}

/* Wide screens: one line — medallion, words, then the actions at the end at their own size. */
@media (min-width: 40rem) {
  .sj-snackbar {
    grid-template-columns: auto 1fr auto;
    max-width: 42rem;
  }

  .sj-snackbar__actions {
    grid-column: auto;
  }

  .sj-snackbar__cta {
    flex: none;
  }
}

/*
  Small phones (down to 320px). The wordmark and the language control together exceed the width
  at the default type scale, and the wordmark must not wrap or truncate, so both step down here
  rather than the brand giving way.
*/
@media (max-width: 30rem) {
  .sj-shell__header {
    padding-inline: var(--sj-space-3);
  }

  .sj-shell__brand {
    font-size: var(--sj-text-md);
  }

  .sj-shell__brand-mark {
    width: 1.75rem;
    height: 1.75rem;
  }
}

.sj-shell__body {
  flex: 1 1 auto;
  width: 100%;
}

.sj-shell__footer {
  padding: var(--sj-space-6) var(--sj-space-4)
    calc(var(--sj-space-6) + var(--sj-safe-bottom));
  color: var(--sj-color-ink-subtle);
  font-size: var(--sj-text-xs);
  text-align: center;
}

/* ---------------------------------------------------------------- stacks */

.sj-stack {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-4);
}

/* ------------------------------------------------------------- avatars */

.sj-avatar {
  display: inline-block;
  flex: none;
  line-height: 0;
  border-radius: var(--sj-radius-pill);
  box-shadow: inset 0 0 0 3px var(--sj-color-surface), var(--sj-shadow-sm);
}

.sj-avatar__art {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--sj-radius-pill);
}

.sj-avatar--sm {
  width: 2rem;
  height: 2rem;
}

.sj-avatar--md {
  width: 3rem;
  height: 3rem;
}

.sj-avatar--lg {
  width: 4.5rem;
  height: 4.5rem;
}

.sj-avatar--xl {
  width: 7rem;
  height: 7rem;
}

/* ------------------------------------------------------------ highlights */

/*
  The three-up row under the welcome hero. Each one leads with a piece of the same artwork the
  child will meet inside, so the promise on the welcome screen and the product agree.
*/
.sj-highlights {
  display: grid;
  gap: var(--sj-space-4);
  margin-top: var(--sj-space-8);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}

.sj-highlight {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sj-space-2);
  padding: var(--sj-space-5);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-xl);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-sm);
}

.sj-highlight__title {
  margin-top: var(--sj-space-2);
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

.sj-highlight__body {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

/* ----------------------------------------------------------------- cards */

.sj-card {
  display: block;
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-sm);
  text-align: start;
  overflow: hidden;
}

.sj-story-card-wrapper {
  position: relative;
  border-radius: var(--sj-radius-lg);
  transition:
    box-shadow var(--sj-duration-fast) var(--sj-ease-standard),
    transform var(--sj-duration-fast) var(--sj-ease-bounce);
}

.sj-story-card-wrapper:hover {
  border-color: var(--sj-color-accent);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-lg);
  transform: translateY(-0.25rem);
}

.sj-story-card-wrapper:active {
  transform: translateY(0);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-sm);
}

.sj-story-card {
  display: block;
  width: 100%;
  color: inherit;
  font: inherit;
  text-decoration: none;
  border-radius: var(--sj-radius-lg);
  cursor: pointer;
}

/*
  3:2, because that is the shape of the covers: a provider draws 1536x1024 and the hand-drawn
  plates are 16:10. A 4:3 card cropped a band off the top and the bottom of every one of them, and
  the top band is where the faces are. What crop is left comes off the ground.
*/
.sj-story-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--sj-color-surface-sunken);
}

.sj-story-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 38%;
  display: block;
}

.sj-story-card__favorite {
  position: absolute;
  inset-block-start: var(--sj-space-2);
  inset-inline-end: var(--sj-space-2);
  background: color-mix(in srgb, var(--sj-color-surface) 88%, transparent);
  border-radius: var(--sj-radius-pill);
  box-shadow: var(--sj-shadow-sm);
}

.sj-story-card__body {
  padding: var(--sj-space-4);
  border-top: 3px solid var(--sj-color-accent);
}

.sj-story-card__title {
  font-size: var(--sj-text-md);
  font-weight: var(--sj-weight-bold);
}

.sj-story-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sj-space-2);
  margin-top: var(--sj-space-2);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

/*
  Profile tile. Big, tinted, and slightly tilted under the finger — this is the first screen a
  child touches on their own, and it should not look like a list row.
*/
.sj-child-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sj-space-3);
  width: 100%;
  min-height: 13rem;
  padding: var(--sj-space-5) var(--sj-space-4);
  border: 2px solid var(--sj-color-border);
  border-radius: var(--sj-radius-xl);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-highlight-edge), 0 var(--sj-lift) 0 var(--sj-color-border),
    var(--sj-shadow-sm);
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: center;
  transition:
    box-shadow var(--sj-duration-fast) var(--sj-ease-standard),
    border-color var(--sj-duration-fast) var(--sj-ease-standard),
    transform var(--sj-duration-normal) var(--sj-ease-bounce);
}

.sj-child-card:hover {
  border-color: var(--sj-color-accent);
  box-shadow: var(--sj-highlight-edge), 0 var(--sj-lift) 0 var(--sj-color-accent),
    var(--sj-shadow-md);
  transform: translateY(-0.25rem) rotate(-0.6deg);
}

.sj-child-card:active {
  transform: translateY(var(--sj-lift));
  box-shadow: var(--sj-highlight-edge), 0 0 0 var(--sj-color-accent);
}

.sj-child-card__name {
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

.sj-child-card__caption {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

.sj-child-card__avatar {
  display: grid;
  place-items: center;
  font-size: var(--sj-text-2xl);
  line-height: 1;
}

/* The hero avatar as the way to another profile (UX-2). A plain button around the artwork, with
   the word kept under it for the children who read — the face is the control, the caption is the
   name of what it does. */
.sj-home-avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sj-space-1);
  padding: var(--sj-space-1);
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.sj-home-avatar__caption {
  padding: 0.125rem var(--sj-space-3);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-xs);
  font-weight: var(--sj-weight-bold);
}

/* ------------------------------------------------------------ the pre-reader pass (UX-5) */

/* A screen reading its own name aloud: a round speaker under the title, comfortable-sized. */
.sj-hear-this {
  display: grid;
  place-items: center;
  width: var(--sj-touch-comfortable);
  height: var(--sj-touch-comfortable);
  margin-block: var(--sj-space-2);
  padding: 0;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: 50%;
  background: var(--sj-color-surface);
  color: var(--sj-color-primary);
  font-size: var(--sj-text-lg);
  cursor: pointer;
}

/* The parent dashboard's three numbers per child (UX-8), stacked and steady. */
.sj-dashboard__figures {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* The saved-on-device row on story detail (UX-7): a sentence, a size, a quiet way back. */
.sj-save-story {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sj-space-3);
  margin-top: var(--sj-space-3);
}

.sj-save-story__note {
  margin: 0;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
}

.sj-save-story__remove {
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 0;
  background: transparent;
  color: var(--sj-color-primary);
  font: inherit;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

/* The grown-ups' fold on story detail: everything that used to stand between a pre-reader and
   the Play button, behind one named disclosure below the actions. */
.sj-grownups {
  margin-top: var(--sj-space-6);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
}

.sj-grownups__summary {
  min-height: var(--sj-touch-min);
  display: flex;
  align-items: center;
  color: var(--sj-color-ink-muted);
  font-weight: var(--sj-weight-bold);
  cursor: pointer;
}

.sj-grownups[open] .sj-grownups__summary {
  margin-bottom: var(--sj-space-2);
}

/* ------------------------------------------------------------- My Journey (UX-3) */

/* The newest badge on the home screen: a link into the journey, drawn as a soft banner. */
.sj-home-badge {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  margin-block: var(--sj-space-3);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-sm);
  color: inherit;
  font-weight: var(--sj-weight-bold);
  text-decoration: none;
  min-height: 2.75rem;
}

.sj-home-badge__glyph {
  font-size: var(--sj-text-lg);
}

.sj-journey-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  margin-block: var(--sj-space-3);
}

.sj-journey-stats__figure {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  margin: 0;
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  font-weight: var(--sj-weight-bold);
  font-variant-numeric: tabular-nums;
}

/* The trail: milestones down a dashed line, gently alternating, the way a path on a storybook
   map wanders. The line is the list's own left border, and the stones sit beside it. */
.sj-journey-trail {
  margin: var(--sj-space-3) 0;
  padding: var(--sj-space-2) 0 var(--sj-space-2) var(--sj-space-4);
  list-style: none;
  border-left: 3px dashed var(--sj-color-accent);
}

.sj-journey-trail__stone {
  margin-block: var(--sj-space-3);
}

/* The wander. Odd stones sit close to the line, even ones step out — enough to read as a path,
   little enough that a phone keeps both on screen. */
.sj-journey-trail__stone:nth-child(even) {
  margin-left: var(--sj-space-4);
}

.sj-journey-trail__link {
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
  max-width: 24rem;
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-sm);
  color: inherit;
  font-weight: var(--sj-weight-bold);
  text-decoration: none;
  min-height: 3.5rem;
}

.sj-journey-trail__cover {
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border: 2px solid var(--sj-color-accent);
  border-radius: 50%;
  object-fit: cover;
}

.sj-journey-trail__cover--blank {
  display: grid;
  place-items: center;
  background: var(--sj-color-surface-sunken);
  font-size: var(--sj-text-lg);
}

.sj-journey-trail__name {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
}

.sj-journey-trail__stars {
  font-size: var(--sj-text-sm);
}

/* A step not yet taken: a small empty circle on the line, saying only "there is more path". */
.sj-journey-trail__waypoint {
  width: 0.875rem;
  height: 0.875rem;
  margin-block: var(--sj-space-3);
  border: 3px solid var(--sj-color-border);
  border-radius: 50%;
}

.sj-journey-trail__ahead {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

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

.sj-journey-badges__badge {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  font-weight: var(--sj-weight-bold);
}

.sj-journey-badges__none {
  color: var(--sj-color-ink-muted);
}

.sj-journey-goal {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  color: var(--sj-color-ink-muted);
}

.sj-journey-goal__count {
  margin-left: var(--sj-space-2);
  font-weight: var(--sj-weight-bold);
  font-variant-numeric: tabular-nums;
}

/* The settings chips, wrapped so long pt-BR labels fall to a second row instead of the page
   scrolling sideways. */
.sj-settings-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  margin-block: var(--sj-space-2);
}

/* The avatar's frame when no progress figure exists: the same circle the ring draws, with no
   arc and no claim. Sized to match SJProgressRing at Size=104 / Thickness=7 so the card's
   layout is identical whichever one renders. */
.sj-child-card__frame {
  display: grid;
  place-items: center;
  width: 104px;
  height: 104px;
  border: 7px solid var(--sj-color-surface-sunken);
  border-radius: 50%;
}

/* --------------------------------------------------------- progress ring */

.sj-progress-ring {
  display: inline-grid;
  place-items: center;
}

.sj-progress-ring__svg,
.sj-progress-ring__label {
  grid-area: 1 / 1;
}

.sj-progress-ring__svg {
  transform: rotate(-90deg);
}

.sj-progress-ring__label {
  display: grid;
  place-items: center;
  font-size: var(--sj-text-xs);
  font-weight: var(--sj-weight-bold);
}

.sj-progress-ring__track {
  fill: none;
  stroke: var(--sj-color-surface-sunken);
}

.sj-progress-ring__indicator {
  fill: none;
  stroke: var(--sj-color-accent);
  stroke-linecap: round;
  transition: stroke-dashoffset var(--sj-duration-slow) var(--sj-ease-decelerate);
}

/* ------------------------------------------------------------ badge chip */

.sj-badge-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-2);
  padding: var(--sj-space-1) var(--sj-space-3);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface-sunken);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-xs);
  font-weight: var(--sj-weight-bold);
  white-space: nowrap;
}

.sj-badge-chip--accent {
  background: var(--sj-color-accent-soft);
  color: var(--sj-color-warning);
}

.sj-badge-chip--success {
  background: var(--sj-color-success-soft);
  color: var(--sj-color-success);
}

.sj-badge-chip--info {
  background: var(--sj-color-primary-soft);
  color: var(--sj-color-primary);
}

.sj-badge-chip--sky {
  background: var(--sj-color-sky-soft);
  color: var(--sj-color-sky);
}

.sj-badge-chip--berry {
  background: var(--sj-color-berry-soft);
  color: var(--sj-color-berry);
}

.sj-badge-chip--grape {
  background: var(--sj-color-grape-soft);
  color: var(--sj-color-grape);
}

.sj-badge-chip--leaf {
  background: var(--sj-color-leaf-soft);
  color: var(--sj-color-leaf);
}

/* ------------------------------------------------------------ bottom nav */

.sj-bottom-nav {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: var(--sj-z-bottom-nav);
  display: flex;
  justify-content: space-around;
  padding-bottom: var(--sj-safe-bottom);
  background: var(--sj-glass-surface);
  backdrop-filter: var(--sj-glass-blur);
  -webkit-backdrop-filter: var(--sj-glass-blur);
  border-top: 1px solid var(--sj-glass-border);
  box-shadow: 0 -4px 18px rgba(94, 58, 30, 0.08);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sj-bottom-nav {
    background: var(--sj-color-surface);
  }
}

.sj-bottom-nav__item {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sj-space-1);
  min-height: var(--sj-bottom-nav-height);
  padding: var(--sj-space-2);
  border: 0;
  background: transparent;
  color: var(--sj-color-ink-subtle);
  font: inherit;
  font-size: var(--sj-text-2xs);
  font-weight: var(--sj-weight-bold);
  text-decoration: none;
  cursor: pointer;
}

.sj-bottom-nav__item[aria-current="page"] {
  color: var(--sj-color-primary);
}

.sj-bottom-nav__icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2rem;
  border-radius: var(--sj-radius-pill);
  font-size: var(--sj-text-lg);
  line-height: 1;
}

/* The current destination is marked by a filled pill as well as by colour and aria-current. */
.sj-bottom-nav__item[aria-current="page"] .sj-bottom-nav__icon {
  background: var(--sj-color-primary-soft);
  transform: translateY(-0.125rem) scale(1.06);
}

@media (min-width: 64rem) {
  .sj-bottom-nav {
    position: static;
    justify-content: center;
    gap: var(--sj-space-4);
    border-top: 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }

  .sj-bottom-nav__item {
    flex: 0 0 auto;
    flex-direction: row;
    min-height: var(--sj-touch-min);
    padding-inline: var(--sj-space-4);
    border-radius: var(--sj-radius-pill);
    font-size: var(--sj-text-sm);
  }

  .sj-bottom-nav__item[aria-current="page"] {
    background: var(--sj-color-primary-soft);
  }

  .sj-bottom-nav__item[aria-current="page"] .sj-bottom-nav__icon {
    background: transparent;
    transform: none;
  }
}

/* --------------------------------------------------------- audio control */

.sj-audio-control {
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
}

.sj-audio-control__label {
  min-width: 8ch;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
}

.sj-audio-control__slider {
  flex: 1 1 auto;
  min-width: 6rem;
  accent-color: var(--sj-color-primary);
}

/* ---------------------------------------------------------- parent gate */

.sj-parent-gate {
  max-width: var(--sj-content-max-narrow);
  margin-inline: auto;
  padding: var(--sj-space-6);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-lg);
}

.sj-parent-gate__title {
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

.sj-parent-gate__description {
  margin-top: var(--sj-space-2);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

.sj-parent-gate__body {
  margin-top: var(--sj-space-5);
}

.sj-parent-gate__error {
  margin-top: var(--sj-space-3);
  color: var(--sj-color-danger);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
}

.sj-parent-gate__actions {
  display: flex;
  gap: var(--sj-space-3);
  margin-top: var(--sj-space-5);
}

/* --------------------------------------------------------------- library */

/*
 * The age filter row (ADR-0018). Wraps rather than scrolls: a horizontally scrolling strip hides
 * controls from exactly the children least likely to go looking for them.
 *
 * "Three or four chips fit across a phone" is what this comment used to claim, and it was never
 * measured. At 375px two fit: with the hearts offered the row is Kept, All ages, and one chip per
 * published band, and at 96/118/90/90/105 against a 337px column that is two rows at the default
 * word size and three at "Big".
 */
.sj-library__filter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  margin-block-end: var(--sj-space-4);
}

/*
 * On a phone the decoration goes and the word stays.
 *
 * The chip's own rule is that the label is never dropped, because a child who cannot read a symbol
 * is exactly the child who needs the control — and that is about the label, not about the balloon
 * beside it. The glyph is `aria-hidden` ornament costing about 21px a chip, which is the whole
 * difference between three rows and two at the "Big" word size: 72px of a screen whose shelf was
 * starting below the fold.
 *
 * Scoped to this filter rather than to the chip, because the other places chips appear — the
 * player's options panel, the child's settings — are not five across in one row.
 */
@media (max-width: 30rem) {
  .sj-library__filter .sj-toggle-chip__glyph {
    display: none;
  }
}

/* Said where the control would have been, so its absence has a visible reason. */
.sj-library__locked {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
  margin-block-end: var(--sj-space-4);
}

/* ------------------------------------------------------------- utilities */

/*
  A story is a picture with a name under it, and the picture is what a child picks from — so the
  card has to be big enough for the picture to be read as a scene rather than as a thumbnail.

  At 9.5rem a laptop drew eight covers to a row at 152px each, which is smaller than the artwork is
  ever shown anywhere else in the product and small enough that two woodland scenes are the same
  green square. 15rem gives four or five to a row on the same screen, each about 260px.

  The phone keeps the smaller figure, and that is deliberate rather than an oversight: 15rem there
  is one card per row and a library nobody can see the shape of. Two to a row is how a child scans
  a shelf.
*/
.sj-grid-cards {
  display: grid;
  gap: var(--sj-space-4);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

@media (max-width: 30rem) {
  .sj-grid-cards {
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  }
}

/* Profile tiles carry a 104px ring and need more room than a story cover does. */
.sj-grid-cards--profiles {
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
}

.sj-rail {
  display: grid;
  grid-auto-flow: column;
  /* Sized with the library grid above, and for the same reason: a cover is the thing being read. */
  grid-auto-columns: minmax(12rem, 15rem);
  gap: var(--sj-space-4);
  overflow-x: auto;
  padding-bottom: var(--sj-space-2);
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}

.sj-rail > * {
  scroll-snap-align: start;
}

/* ----------------------------------------------------------------- forms */

.sj-form {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-5);
}

.sj-field {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-2);
  margin: 0;
  padding: 0;
  border: 0;
}

.sj-field__label {
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
}

.sj-field__hint {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-xs);
}

.sj-field__error,
.sj-form__error {
  color: var(--sj-color-danger);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
}

/* Sits under the field it belongs to rather than in the button stack, so a third choice does not
   read as a third action of equal weight. */
.sj-field__aside {
  margin: 0;
}

/* The quiet way forward: drawn as words, not as a button -- but still a full touch target,
   because quiet is a look and not a smaller thing to press. Same idiom as the player's
   "skip this question". */
.sj-form__quiet {
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-2) 0;
  border: 0;
  background: transparent;
  color: var(--sj-color-primary);
  font: inherit;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

.sj-form__quiet:disabled {
  color: var(--sj-color-ink-subtle);
  cursor: default;
}

/* Something a form did, rather than something it refused. Deliberately not the danger colour and
   deliberately not beside the error: a parent who has just been sent a code is being told the
   thing worked. */
.sj-form__status {
  padding: var(--sj-space-3) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-primary-soft);
  color: var(--sj-color-ink);
  font-size: var(--sj-text-sm);
}

.sj-input {
  width: 100%;
  min-height: var(--sj-touch-comfortable);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-surface);
  color: inherit;
  font: inherit;
  font-size: var(--sj-text-md);
}

.sj-input:focus {
  border-color: var(--sj-color-primary);
}

/* Six digits copied off a screen, so they are set large and spaced far enough apart to be checked
   one at a time. Still Nunito rather than a monospace face: the product has two families and a
   third for one field is a font nobody chose. */
.sj-input--code {
  font-size: var(--sj-text-xl);
  font-weight: var(--sj-weight-bold);
  letter-spacing: 0.35em;
  text-align: center;
}

.sj-form__actions--spaced {
  margin-top: var(--sj-space-8);
}

.sj-form__switch {
  margin-top: var(--sj-space-6);
}

.sj-checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--sj-space-3);
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-2) 0;
  cursor: pointer;
  font-size: var(--sj-text-md);
}

.sj-checkbox input {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.1rem;
  accent-color: var(--sj-color-primary);
}

/* ------------------------------------------------------------- choices */

.sj-choice-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
}

.sj-choice {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-2);
  min-height: var(--sj-touch-comfortable);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  cursor: pointer;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  transition:
    border-color var(--sj-duration-fast) var(--sj-ease-standard),
    background-color var(--sj-duration-fast) var(--sj-ease-standard);
}

.sj-choice--selected {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary-soft);
  font-weight: var(--sj-weight-bold);
}

/* The radio itself is kept in the accessibility tree and focusable; the label is the target. */
.sj-choice input,
.sj-avatar-choice input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.sj-choice:focus-within,
.sj-avatar-choice:focus-within {
  outline: 3px solid var(--sj-color-focus);
  outline-offset: 2px;
}

/*
  Avatar picker. The tile is the artwork plus a ring; the ring thickens and colours on the
  chosen one, so selection is visible without reading and without relying on colour alone.
*/
.sj-avatar-choice {
  display: inline-grid;
  place-items: center;
  padding: var(--sj-space-1);
  border: 3px solid transparent;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  cursor: pointer;
  transition: transform var(--sj-duration-normal) var(--sj-ease-bounce);
}

.sj-avatar-choice:hover {
  transform: scale(1.06);
}

.sj-avatar-choice--selected {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary-soft);
}

/* ------------------------------------------------------------- steps */

.sj-steps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
  margin: 0 0 var(--sj-space-6);
  padding: 0;
  list-style: none;
}

.sj-steps__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-2);
  color: var(--sj-color-ink-subtle);
  font-size: var(--sj-text-xs);
}

.sj-steps__item--current {
  color: var(--sj-color-primary);
  font-weight: var(--sj-weight-bold);
}

.sj-steps__marker {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface-sunken);
  font-weight: var(--sj-weight-bold);
}

.sj-steps__item--current .sj-steps__marker {
  background: var(--sj-color-primary);
  color: var(--sj-color-ink-inverse);
}

/* -------------------------------------------------------------- panels */

.sj-panel {
  padding: var(--sj-space-4);
}

.sj-panel__label {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-xs);
}

/*
 * One parent decision per child, in a panel. Wraps at narrow widths so the switch drops under the
 * name rather than squeezing: the chip's touch target is the one thing here that must not give.
 */
.sj-settings-list {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-3);
  list-style: none;
  margin: var(--sj-space-4) 0 0;
  padding: 0;
}

.sj-settings-list__row {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  justify-content: space-between;
}

.sj-settings-list__name {
  display: flex;
  align-items: baseline;
  gap: var(--sj-space-2);
}

.sj-panel__value {
  margin-top: var(--sj-space-1);
  font-size: var(--sj-text-md);
  font-weight: var(--sj-weight-medium);
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------- parent feedback */

/*
 * Five stars on one story. Each star is its own full-height touch target; the glyph inside is
 * bigger than the text around it because the press is the point. The unfilled star keeps the
 * border colour rather than disappearing, so "three of five" is readable at a glance without
 * counting what is not there.
 */
.sj-rating {
  display: inline-flex;
}

.sj-rating__star {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  min-width: var(--sj-touch-min);
  min-height: var(--sj-touch-min);
  font-size: var(--sj-text-xl);
  line-height: 1;
  color: var(--sj-color-border-strong);
}

.sj-rating__star--on {
  color: var(--sj-color-accent);
}

.sj-rating__star:disabled {
  cursor: default;
  opacity: 0.6;
}

.sj-input--multiline {
  min-height: 7rem;
  resize: vertical;
}

/* What the family has already sent: kind and date as a quiet line, the words as the row. */
.sj-feedback-list {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-4);
  list-style: none;
  margin: var(--sj-space-4) 0 0;
  padding: 0;
}

.sj-feedback-list__message {
  margin: var(--sj-space-1) 0 0;
  overflow-wrap: anywhere;
}

/* -------------------------------------------------------------- modal */

.sj-modal {
  position: fixed;
  inset: 0;
  z-index: var(--sj-z-dialog);
  display: grid;
  place-items: center;
  padding: var(--sj-space-4);
  padding-bottom: calc(var(--sj-space-4) + var(--sj-safe-bottom));
  background: rgba(44, 31, 23, 0.5);
  overflow-y: auto;
}

.sj-modal__panel {
  width: 100%;
  max-width: var(--sj-content-max-narrow);
}

/* ----------------------------------------------- design-system gallery */

.sj-gallery {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-10);
}

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

.sj-gallery__panels {
  display: grid;
  gap: var(--sj-space-4);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.sj-gallery__swatches {
  display: grid;
  gap: var(--sj-space-3);
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
}

.sj-gallery__swatch {
  display: flex;
  align-items: flex-end;
  min-height: 4.5rem;
  padding: var(--sj-space-2);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-md);
  font-size: var(--sj-text-2xs);
  font-weight: var(--sj-weight-bold);
}

/* ------------------------------------------------------- Blazor plumbing */

.loading-progress {
  position: fixed;
  display: block;
  width: 8rem;
  height: 8rem;
  inset: 0;
  margin: auto;
}

.loading-progress circle {
  fill: none;
  stroke: var(--sj-color-surface-sunken);
  stroke-width: 0.6rem;
  transform-origin: 50% 50%;
  transform: rotate(-90deg);
}

.loading-progress circle:last-child {
  stroke: var(--sj-color-primary);
  stroke-dasharray: calc(
      3.141592653589793 * var(--blazor-load-percentage, 0%) * 0.8
    ),
    500%;
  transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
  position: absolute;
  inset: calc(50% - 0.8rem) 0 auto 0.2rem;
  text-align: center;
  font-weight: var(--sj-weight-bold);
  color: var(--sj-color-ink-muted);
}

.loading-progress-text::after {
  content: var(--blazor-load-percentage-text, "Loading");
}

#blazor-error-ui {
  display: none;
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: var(--sj-z-dialog);
  padding: var(--sj-space-4) calc(var(--sj-space-6) + 1rem) var(--sj-space-4)
    var(--sj-space-4);
  background: var(--sj-color-warning-soft);
  color: var(--sj-color-ink);
  box-shadow: var(--sj-shadow-lg);
}

#blazor-error-ui .dismiss {
  position: absolute;
  inset-block-start: var(--sj-space-2);
  inset-inline-end: var(--sj-space-3);
  cursor: pointer;
}

/* ------------------------------------------------- story card placeholder */

/*
  Drawn stand-in for a cover that has not been produced yet. Fills the media area exactly as a
  photograph would, so a catalog of mixed real and pending artwork still lines up.
*/
.sj-story-card__placeholder {
  width: 100%;
  height: 100%;
  max-width: none;
}

.sj-story-card__placeholder .sj-scene__art {
  width: 100%;
  height: 100%;
  border-radius: 0;
  object-fit: cover;
}

/* ------------------------------------------------------------ story detail */

.sj-story-detail__section {
  margin-top: var(--sj-space-8);
}

.sj-story-detail__list {
  margin: var(--sj-space-3) 0 0;
  padding-inline-start: var(--sj-space-6);
  color: var(--sj-color-ink-muted);
}

.sj-story-detail__list li {
  margin-bottom: var(--sj-space-2);
}

.sj-story-detail__prompt {
  margin-top: var(--sj-space-3);
  padding: var(--sj-space-5);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-accent-soft);
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
}

.sj-story-detail__sources {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  margin-top: var(--sj-space-3);
}

/* --------------------------------------------------------------- rail head */

.sj-rail-header {
  display: flex;
  align-items: baseline;
  gap: var(--sj-space-3);
  margin: var(--sj-space-8) 0 var(--sj-space-3);
}

.sj-rail-header__title {
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-xl);
  font-weight: var(--sj-weight-bold);
}

.sj-rail-header__action {
  margin-inline-start: auto;
}

/* ---------------------------------------------------- story activities (AC-1) */

/* The "Things to do" offer on story detail: reading first, making second. */
.sj-things-to-do {
  margin-top: var(--sj-space-6);
}

.sj-things-to-do__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
  margin-top: var(--sj-space-3);
}

/* One activity, as a page: prompt, playing surface, result line, pips. */
.sj-activity {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sj-space-4);
}

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

.sj-activity__prompt {
  margin: 0;
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

/* The retelling so far: placed scene cards, in story order. */
.sj-activity__done {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-2);
  align-self: stretch;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sj-activity__done-card {
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
  padding: var(--sj-space-2);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
}

.sj-activity__done-art {
  flex: none;
  width: 4.5rem;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--sj-radius-md);
}

.sj-activity__done-caption {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* The choices still in play, as generous picture cards. */
.sj-activity__options {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--sj-space-3);
  align-self: stretch;
}

/* The ways to go again, once a puzzle is whole: a row of words that wraps, not a picture grid. */
.sj-activity__again {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
  align-self: stretch;
}

.sj-activity-card {
  min-height: var(--sj-touch-min);
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-2);
  padding: var(--sj-space-2);
  border: 1px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.sj-activity-card__art {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--sj-radius-md);
}

.sj-activity-card__caption {
  color: var(--sj-color-ink);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

/*
  Phones: two choices across, and the retelling as a strip.

  Measured on "Put the story in order" at 375x812 at the "Big" word size. `minmax(10rem, 1fr)` is
  a column minimum in rem, and rem is exactly what the child's word-size preference multiplies —
  at 1.2 those ten rem are 192px against a 337px column, so `auto-fill` fitted one. Seven scenes
  became seven full-width cards and a 2952px page: three and a half screens to compare the first
  part of a story with the last, which is the one thing this activity asks a child to do.

  So the count is stated rather than derived. Two is what fits a phone with a picture big enough to
  recognise, and it must be two whether or not a family made the words bigger — a layout that
  changes shape with the type scale is how this screen came to have one column in the first place.

  The retelling so far becomes a horizontal strip, and that is the other half. It grows a row per
  correct tap, and at four placed the choices started at y=814 on an 812px screen: a child who had
  got four parts right could see none of the parts left. A strip costs a sideways scroll over
  scenes already placed — pictures being remembered, not controls being reached, which is the
  distinction that makes scrolling acceptable here and not in the library's filter.
*/
@media (max-width: 30rem) {
  /*
    Bringing the pips and the result up the page is information gained and height spent — about
    81px of it at the "Big" word size, which on the puzzle is the difference between the tray's
    second row of pieces being on the screen and being under it. The stack pays some of that back:
    four gaps between prompt, pips, result and playing surface at 19px each is 78px of nothing,
    and a phone cannot afford the same air a laptop can.
  */
  .sj-activity {
    gap: var(--sj-space-3);
  }

  /*
    The chrome above the game.

    Measured on all three activities at 375x812 at the "Big" word size: 446px — 55% of the phone —
    went on the app header, a two-line heading, the story's name, a full-size "Back to the story"
    button and a three-line instruction, before one thing a child can touch. It is why the puzzle's
    tray fell under the fold and why the coloring page's crayons did.

    Four trims, none of which takes a word off the screen or a target below the floor:

    The heading steps down one size, which is what takes "Put the picture together" from two lines
    to one — 51px, the largest saving here, for six pixels of type. It is a game's title standing
    over the game, not a page of prose that needs a display heading to open it. Scoped by `:has` to
    pages that hold an activity, because every other screen's `h1` is its subject rather than its
    label.

    The way out becomes the shape the player already uses for the same job: a 44px target rather
    than a 67px one. `--sj-touch-min` is the floor and this stays on it — a way out is a control a
    child uses once, not the one they aim at all game.

    The instruction steps down to the body size it reads as, and the content's opening gap gives up
    10px. Together: 133px back, and the pips move from y=446 to y=314.
  */
  .sj-page:has(.sj-activity) .sj-page__title {
    font-size: var(--sj-text-xl);
  }

  .sj-page:has(.sj-activity) .sj-page__content {
    margin-top: var(--sj-space-4);
  }

  .sj-activity__leave {
    margin-bottom: 0;
  }

  .sj-activity__leave .sj-button {
    min-height: var(--sj-touch-min);
    padding-block: 0;
    font-size: var(--sj-text-md);
  }

  .sj-activity__prompt {
    font-size: var(--sj-text-md);
  }

  .sj-activity__options {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Three lines of caption under a half-width picture is mostly caption. */
  .sj-activity-card__caption {
    -webkit-line-clamp: 2;
  }

  .sj-activity__done {
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    /* A scroll container reports its content as its minimum until it is told it may be narrower. */
    min-width: 0;
    max-width: 100%;
    padding-bottom: var(--sj-space-1);
  }

  .sj-activity__done-card {
    flex: none;
    flex-direction: column;
    align-items: stretch;
    width: 7.5rem;
    gap: var(--sj-space-1);
  }

  .sj-activity__done-art {
    width: 100%;
  }

  .sj-activity__done-caption {
    -webkit-line-clamp: 2;
  }
}

/* Said back after a tap, the interaction host's way: a sentence, never a score. */
.sj-activity__result {
  margin: 0;
  color: var(--sj-color-ink-muted);
  font-weight: var(--sj-weight-bold);
}

.sj-activity__result--good {
  color: var(--sj-color-success);
}

.sj-activity__tally {
  display: flex;
  align-items: center;
}

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

.sj-activity__pip {
  width: 0.65rem;
  height: 0.65rem;
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface-sunken);
}

.sj-activity__pip--done {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary);
}

.sj-activity__complete {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  margin: 0;
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

/* The puzzle: a board of spots and a tray of loose pieces, all buttons, nothing dragged. */
.sj-jigsaw__board {
  display: grid;
  gap: 2px;
  align-self: stretch;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface-sunken);
}

.sj-jigsaw__slot {
  border: 2px dashed var(--sj-color-border-strong);
  border-radius: var(--sj-radius-sm);
  background: var(--sj-color-surface);
  cursor: pointer;
}

.sj-jigsaw__slot:disabled {
  cursor: default;
}

/* A placed piece: the crop arrives from an inline style; the rule only shapes the box. */
.sj-jigsaw__piece {
  display: block;
  background-repeat: no-repeat;
}

.sj-jigsaw__tray {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
}

.sj-jigsaw__loose {
  width: clamp(4.5rem, 22vw, 7rem);
  padding: 0;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-md);
  background-repeat: no-repeat;
  cursor: pointer;
  /* A dragged piece must follow the finger, not scroll the page. Taps are unaffected. */
  touch-action: none;
}

.sj-jigsaw__loose--held {
  border-color: var(--sj-color-primary);
  outline: 3px solid var(--sj-color-primary);
  outline-offset: 2px;
}

/* The piece while it rides the pointer: a clone of the tray button, centred on the finger.
   pointer-events off, or the drop's hit-test would only ever find the ghost itself. */
.sj-jigsaw__ghost {
  position: fixed;
  z-index: var(--sj-z-dialog);
  transform: translate(-50%, -50%);
  opacity: 0.85;
  pointer-events: none;
  box-shadow: var(--sj-shadow-md);
}

/* The piece left behind in the tray while its ghost is carried. */
.sj-jigsaw__loose--lifted {
  opacity: 0.35;
}

/* The spot under a carried piece, saying "let go here is allowed". Applies to the disabled
   button too — hit-testing does not consult disabled, and neither should the invitation. */
.sj-jigsaw__slot--over {
  border-color: var(--sj-color-primary);
  border-style: solid;
  background: var(--sj-color-primary-soft);
}

/* ----------------------------------------------------------- coloring (AC-2) */

.sj-coloring {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-4);
  align-self: stretch;
}

/* The crayon box: twelve colours and the eraser, every one a real target. */
.sj-coloring__palette {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  align-items: center;
}

.sj-coloring__crayon {
  width: var(--sj-touch-min);
  height: var(--sj-touch-min);
  padding: 0;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  /* The colour arrives from an inline style; the rule only shapes the box. */
  background: var(--sj-crayon, var(--sj-color-surface));
  cursor: pointer;
}

/*
  The eraser: a swatch-shaped key carrying a drawing, sitting among the crayons.

  It used to be a pill reading "Eraser" — the one control in the box made of letters, on a screen
  for children who may not read yet, beside twenty-four signs that need no reading at all. The word
  is its accessible name now and the rubber is its face.
*/
.sj-coloring__eraser {
  width: var(--sj-touch-min);
  height: var(--sj-touch-min);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  color: var(--sj-color-ink);
  cursor: pointer;
}

/* Yes and Keep, which are words on purpose: this is the one moment the screen asks a question. */
.sj-coloring__confirm-key {
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  font: inherit;
  font-weight: var(--sj-weight-bold);
  color: var(--sj-color-ink);
  cursor: pointer;
}

.sj-coloring__crayon--held {
  border-color: var(--sj-color-primary);
  outline: 3px solid var(--sj-color-primary);
  outline-offset: 2px;
}

/* The page itself. The SVG scales to the column; regions show they are pressable. */
.sj-coloring__art {
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  overflow: hidden;
  background: var(--sj-color-surface);
}

.sj-coloring__art svg {
  display: block;
  width: 100%;
  height: auto;
}

.sj-coloring__art [data-region] {
  cursor: pointer;
}

/* The painting canvas (PS-1): paint below, line art above, stacked in one frame. */
.sj-coloring__art--paint {
  position: relative;
}

.sj-coloring__canvas {
  display: block;
  width: 100%;
  height: auto;
  cursor: pointer;
}

.sj-coloring__canvas--paint {
  /* A stroke is a drag; the page must not scroll or zoom under a drawing finger. */
  touch-action: none;
}

/* The line art, over the paint and out of the pointer's way. */
.sj-coloring__ink {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* The tool tray: the bucket, the three crayon widths, and undo. */
.sj-coloring__tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  align-items: center;
}

.sj-coloring__tool {
  min-width: var(--sj-touch-min);
  min-height: var(--sj-touch-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-surface);
  color: var(--sj-color-ink);
  cursor: pointer;
}

.sj-coloring__tool:disabled {
  opacity: 0.4;
  cursor: default;
}

/* The stamp picker, shown while the stamp tool is in hand. */
.sj-coloring__stamps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  align-items: center;
}

/*
  The fold stays open after "Start over" is pressed in it — `details` keeps its own state and
  nothing here closes it — so its card would sit over the question it just raised. While the
  question is up the card is hidden; it comes back, still open, once the question is answered.
*/
.sj-coloring:has(.sj-coloring__confirm) .sj-coloring__menu {
  display: none;
}

/* The start-over question, asked in place rather than over the picture. */
.sj-coloring__confirm {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-2);
  align-items: center;
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-surface);
}

/*
 * The keyboard cursor: where Enter will paint. A ring rather than a dot, so the pixel underneath
 * stays visible — and shown only under :focus-visible, so a child painting by pointer never sees
 * a ring they did not ask for.
 */
.sj-coloring__cursor {
  display: none;
  position: absolute;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 3px solid var(--sj-color-primary);
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgb(255 255 255 / 0.9);
  pointer-events: none;
}

.sj-coloring__canvas--paint:focus-visible ~ .sj-coloring__cursor {
  display: block;
}

/*
  The tool rail and the colour strip.

  On a phone they stack under the picture; from 64rem they stand either side of it, and the picture
  takes everything left over. `.sj-coloring__actions` and `.sj-coloring__print` used to live here —
  three full-width rows of words at the foot of the page — and are gone with the markup that wrote
  them.
*/
.sj-coloring__rail,
.sj-coloring__colours {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--sj-space-2);
}

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

/* The fold holding the rare things. Closed it is one key; open it is a small card beneath it. */
.sj-coloring__more {
  position: relative;
}

.sj-coloring__more-key {
  width: var(--sj-touch-comfortable);
  height: var(--sj-touch-comfortable);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  color: var(--sj-color-ink);
  cursor: pointer;
  /* The disclosure triangle is chrome a child cannot read; the three dots are the whole sign. */
  list-style: none;
}

.sj-coloring__more-key::-webkit-details-marker {
  display: none;
}

.sj-coloring__more[open] .sj-coloring__more-key {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary-soft);
}

.sj-coloring__menu {
  position: absolute;
  z-index: 20;
  inset-inline-start: 0;
  margin-block-start: var(--sj-space-2);
  min-width: 14rem;
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
  padding: var(--sj-space-2);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-lg);
}

/* A picture beside the words, so the row means something before it can be read. */
.sj-coloring__menu-item {
  min-height: var(--sj-touch-min);
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 0;
  border-radius: var(--sj-radius-md);
  background: none;
  font: inherit;
  font-weight: var(--sj-weight-bold);
  color: var(--sj-color-ink);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}

.sj-coloring__menu-item:hover {
  background: var(--sj-color-surface-sunken);
}

/*
  Phones: the picture goes edge to edge, and the boxes under it spend the gutters too.

  Everything here is width, and it buys height. Measured at 375x812 at the "Big" word size, which
  is what the child whose screen this was reported on is set to: the crayon box came to 302px and
  the tool rail to 187px, so the tools sat at y=883 — 71px below the fold, on a page 1199 tall. A
  child could see the picture or the tool that paints it, never both.

  The cause is one column of 337px and 53px targets. Five crayons fit across it, so twenty-four
  crayons are five rows; six tools fit in 366px, so six tools are two rows. Spending the page's
  own 19px gutters gives 356px, and at 356 six crayons fit and the tool row closes up — four rows
  instead of five, one row instead of two, and about 135px back.

  The targets do not give. `--sj-touch-min` is already the floor here and these are a child's
  primary controls; what changes is the space between them and the space around them.

  The picture is full-bleed for the same reason the player's scene frame is, and the negative
  margin must equal `.sj-page`'s phone padding or it will not sit flush. Its corners go with the
  gutters: a radius on a shape touching both edges reads as a rendering fault rather than a style.

  The order is untouched, and deliberately — picture, colours, tools is a measured decision the
  markup argues for at length, including why it is written in the document rather than in `order`.
*/
@media (max-width: 30rem) {
  .sj-coloring__art,
  .sj-coloring__colours,
  .sj-coloring__rail {
    margin-inline: calc(var(--sj-space-4) * -1);
  }

  .sj-coloring__art {
    border-inline: 0;
    border-radius: 0;
  }

  .sj-coloring__colours,
  .sj-coloring__rail {
    padding-inline: var(--sj-space-2);
  }

  .sj-coloring__palette,
  .sj-coloring__tools,
  .sj-coloring__rail {
    gap: var(--sj-space-1);
  }
}

/*
  The studio arrangement, from 64rem up.

  Before this the page asked `SJPage` for `Narrow` — the product's *reading* column — so a
  1536x1024 picture rendered at 576x385 inside a 653px band with 58% of the viewport empty either
  side, and the page still scrolled. A canvas was laid out as prose. It asks for `Wide` now, and
  here the column is spent on the picture: the rail and the strip take what they need and the art
  takes the rest.
*/
@media (min-width: 64rem) {
  .sj-coloring {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--sj-space-4);
  }

  .sj-coloring__art {
    grid-column: 2;
    grid-row: 1;
    justify-self: center;
  }

  .sj-coloring__rail {
    grid-column: 1;
    grid-row: 1;
    flex-direction: column;
    flex-wrap: nowrap;
  }

  /* The tool group inside the rail turns too, or the rail is a 365px-wide row standing on its end. */
  .sj-coloring__rail .sj-coloring__tools,
  .sj-coloring__widths {
    flex-direction: column;
    flex-wrap: nowrap;
  }

  /*
    The rail is the grid's `auto` column, so anything wide that lands in it is paid for by the
    picture. Two things did, and both were measured: the stamp picker is a row of six, and
    picking the stamp tool took the rail from 52px to 304px and the picture from 766px to 514px
    wide; the start-over question is a direct child of the grid with no placement, so it was
    auto-placed into the same column and took the picture to 224px while it was being asked.
    The stamps stand two abreast beside the tool that owns them; the question — and the
    "cannot be read back" sentence, placed the same way — go under the picture, in its column.
  */
  .sj-coloring__rail .sj-coloring__stamps {
    width: calc(var(--sj-touch-min) * 2 + var(--sj-space-2));
  }

  .sj-coloring__confirm,
  .sj-coloring > .sj-activity__result {
    grid-column: 2;
    justify-self: center;
  }

  /*
    Three crayons wide, and that is arithmetic rather than taste: two columns is twelve rows, which
    is 918px of swatches beside a picture with 890px to give, and the page scrolls again for the
    sake of the colours. Three columns is eight rows and 472px, and the strip is still narrow enough
    to sit at the picture's edge.
  */
  .sj-coloring__colours {
    grid-column: 3;
    grid-row: 1;
  }

  .sj-coloring__colours .sj-coloring__palette {
    width: calc(var(--sj-touch-comfortable) * 3 + var(--sj-space-2) * 2);
  }

  .sj-coloring__colours .sj-coloring__crayon {
    width: var(--sj-touch-comfortable);
    height: var(--sj-touch-comfortable);
  }

  /* The menu opens away from the edge it is pinned to. */
  .sj-coloring__menu {
    inset-inline-start: 100%;
    margin-block-start: 0;
    margin-inline-start: var(--sj-space-2);
  }

  /*
    Fit the box, keep the picture's own shape.

    Sized as the replaced element it is — `width`/`height` auto against a max on both axes — rather
    than by an `aspect-ratio`, because the two kinds of page are not the same shape: a generated
    page is 1536x1024 and a hand-derived one is 1200x750. Committing to either would letterbox or
    crop the other, and the player has already paid once for a frame that capped one axis and let
    the picture overflow the other.
  */
  .sj-coloring__canvas--paint {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: calc(100dvh - 22rem);
  }
}

/* The quiet "you started this one" mark on the picker's cards. */
.sj-coloring__started {
  display: block;
  color: var(--sj-color-primary);
  font-size: var(--sj-text-xs);
  font-weight: var(--sj-weight-bold);
}

/* ------------------------------------------- things the child made (AC-2) */

.sj-journey-creations {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--sj-space-3);
  margin: var(--sj-space-3) 0 0;
  padding: 0;
  list-style: none;
}

.sj-journey-creations__link {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-2);
  padding: var(--sj-space-2);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  text-decoration: none;
  color: var(--sj-color-ink);
}

/* The thumbnail replays the work on the same two stacked canvases the page uses (PS-1). */
.sj-coloring-thumb {
  position: relative;
  display: block;
}

.sj-coloring-thumb canvas {
  display: block;
  width: 100%;
  height: auto;
}

.sj-coloring-thumb__ink {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.sj-journey-creations__art svg {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--sj-radius-md);
}

.sj-journey-creations__name {
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
}

