/* Lantern guardian, level-0 alpha.
   One stylesheet for six pages. Light theme only, on purpose: this is a
   parent surface read on a phone in a lit room, and a QR code that a dark
   theme inverts is a QR code that may not scan.
   Token vocabulary follows viewer/tokens.css (deco ivory, deep navy ink,
   lantern amber), restated here rather than imported: that file lives in
   the household lab, which this repo never reads at runtime.
   No external font, no CDN, nothing that leaves the page. */

:root {
  --font-display: -apple-system, BlinkMacSystemFont, 'Avenir Next', 'Segoe UI', sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Segoe UI', sans-serif;
  --text-xs: 12px; --text-s: 13.5px; --text-m: 15px; --text-l: 17px;
  --text-xl: 20px; --text-2xl: 25px; --text-num: 34px;
  --leading: 1.45; --leading-tight: 1.2;
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-7: 32px; --space-8: 48px;
  --radius-s: 8px; --radius-m: 12px; --radius-l: 16px; --radius-pill: 999px;
  --tap-min: 44px;

  --bg: #fdfcf9;
  --surface: #ffffff;
  --surface-sunken: #f4efe2;
  --line: #e2d8c1;
  --line-strong: #c7ba9b;
  --ink: #232a3f;
  --ink-soft: #565e74;
  --ink-faint: #878da0;
  --brand-fill: #ffb937;
  --brand-fill-ink: #392a06;
  --brand-text: #8a5f00;
  --include: #1e7a4a; --include-soft: #ddeee2;
  --review: #8f5e00; --review-soft: #f6e8c7;
  --urgent: #b3243c; --urgent-soft: #f8dadd;
  --info: #33619e; --info-soft: #e0e8f3;
  --kid-teal: #177e78; --kid-coral: #bb4f2e; --kid-plum: #7c4d8f;
  --kid-sea: #2f6aa0; --kid-moss: #5c751c; --kid-rose: #b04a6e;
  --focus: #2c5cc5;
  --shadow-1: 0 1px 3px rgba(35, 42, 63, .10);
  --shadow-2: 0 6px 20px rgba(35, 42, 63, .13);
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  padding: var(--space-5) var(--space-4) var(--space-8);
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--text-m)/var(--leading) var(--font-body);
  overflow-x: hidden;
}

main { max-width: 34rem; margin: 0 auto; }

h1, h2, h3 { font-family: var(--font-display); line-height: var(--leading-tight); margin: 0 0 var(--space-3); }
h1 { font-size: var(--text-2xl); font-weight: 700; }
h2 { font-size: var(--text-xl); font-weight: 700; }
h3 { font-size: var(--text-l); font-weight: 700; }
p { margin: 0 0 var(--space-4); }
p:last-child { margin-bottom: 0; }
a { color: var(--brand-text); }

.masthead { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: var(--space-6); }
.masthead .marksvg { width: 22px; height: 22px; align-self: center; flex: 0 0 auto; }
.masthead .mark { font-family: var(--font-display); font-weight: 700; font-size: var(--text-l); letter-spacing: .02em; }
.masthead .where { color: var(--ink-faint); font-size: var(--text-s); }

.lede { color: var(--ink-soft); font-size: var(--text-l); }
.small { font-size: var(--text-s); color: var(--ink-soft); }
.faint { color: var(--ink-faint); }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--text-s); }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
  box-shadow: var(--shadow-1);
}
.card > :last-child { margin-bottom: 0; }

section.q { margin-bottom: var(--space-6); }
section.q > h2 { margin-bottom: var(--space-2); }
section.q .why { color: var(--ink-soft); font-size: var(--text-s); margin-bottom: var(--space-4); }

label { display: block; font-weight: 700; margin-bottom: var(--space-2); }

input[type="text"], input[type="email"] {
  width: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  font: 400 var(--text-l)/var(--tap-min) var(--font-body);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
}
input[type="text"]:focus, input[type="email"]:focus,
button:focus-visible, input[type="range"]:focus-visible, a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* the honeypot: present for a bot, gone for a person and for a screen reader */
.trap {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.trap input { width: 1px; height: 1px; min-height: 0; padding: 0; border: 0; }

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap-min); padding: 0 var(--space-5);
  font: 700 var(--text-m)/1 var(--font-body);
  color: var(--brand-fill-ink); background: var(--brand-fill);
  border: 1px solid #e0a02a; border-radius: var(--radius-pill);
  cursor: pointer; text-decoration: none;
}
.btn.wide { width: 100%; }
.btn.ghost { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn.danger { background: var(--urgent); border-color: var(--urgent); color: #fff; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
/* 44 px, not 36. "Copy link" and, more to the point, "Retire this link and
   remove <name>" were 96x36 and 240x36: a destructive action under the
   comfortable minimum (SECURITY-VERIFY, parent journey, item 6). `small` now
   means narrower type and tighter padding, never a shorter target. */
.btn.small { min-height: var(--tap-min); padding: 0 var(--space-4); font-size: var(--text-s); }

.bands { display: grid; gap: var(--space-2); grid-template-columns: 1fr; }
@media (min-width: 30rem) { .bands { grid-template-columns: 1fr 1fr; } }

/* "What you get": the signup page's own sizzle reel (2026-08-29). Both images
   are real screenshots off the guardian and wall test fixtures, not mockups,
   so what a parent sees here is what the product actually renders. One column
   on a phone, two side by side once there is room. */
.whatyouget { margin-bottom: var(--space-6); }
.sizzle { display: grid; gap: var(--space-4); grid-template-columns: 1fr; }
@media (min-width: 30rem) { .sizzle { grid-template-columns: 1fr 1fr; } }
.sizzle figure { margin: 0; }
/* A landscape capture beside a portrait one shrinks to nothing. Wide spans the row. */
.sizzle figure.wide { grid-column: 1 / -1; }
.sizzle img {
  display: block; width: 100%; height: auto;
  /* portrait shots cap below (see figure:not(.wide)) so a phone gets a
     framed miniature, not a life-size clone of its own screen */
  border: 1px solid var(--line); border-radius: var(--radius-m);
  box-shadow: var(--shadow-1);
}
.sizzle figure:not(.wide) img { max-height: 540px; width: auto; margin: 0 auto; }
.sizzle figcaption { margin-top: var(--space-2); font-size: var(--text-s); color: var(--ink-soft); }
.band {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: var(--tap-min); padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong); border-radius: var(--radius-m);
  background: var(--surface); cursor: pointer; font-weight: 700;
}
.band input { margin: 0; width: 20px; height: 20px; accent-color: var(--brand-text); }
.band .count {
  margin-left: auto; font-weight: 400; font-size: var(--text-s); color: var(--ink-faint);
  display: inline-flex; align-items: center; gap: 4px;
}
.band .count .shelficon { flex: none; }
.band:has(input:checked) { border-color: var(--brand-text); box-shadow: inset 0 0 0 1px var(--brand-text); }

/* A dial is a <details>. It ships CLOSED (Jeff, 2026-08-28), it opens on a tap
   or a keypress with no script, and its slider is in the document either way,
   so the count and the reading work on a dial nobody has opened. */
.dial, .daylimit-panel { margin-bottom: var(--space-3); border: 1px solid var(--line);
        border-radius: var(--radius-m); background: var(--surface); }
.dial > summary, .daylimit-panel > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  min-height: var(--tap-min); padding: var(--space-3) var(--space-4);
  cursor: pointer; font-weight: 700; list-style: none;
}
.dial > summary::-webkit-details-marker, .daylimit-panel > summary::-webkit-details-marker { display: none; }
.dial > summary::after, .daylimit-panel > summary::after {
  content: ""; flex: 0 0 auto; width: 9px; height: 9px; margin-left: var(--space-1);
  border-right: 2px solid var(--ink-faint); border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg) translate(-2px, -2px); transition: transform .15s ease;
}
.dial[open] > summary::after, .daylimit-panel[open] > summary::after { transform: rotate(225deg) translate(-2px, -2px); }
.dial > summary .q, .daylimit-panel > summary .q { flex: 1 1 auto; }
.dial .dialbody, .daylimit-panel .dialbody { padding: 0 var(--space-4) var(--space-4); }
.dial .dialbody .why, .daylimit-panel .dialbody .why { margin-top: 0; }
.dial .val, .daylimit-panel .val {
  font-family: var(--font-display); font-weight: 700; font-size: var(--text-xl);
  min-width: 2.2ch; text-align: right;
}
/* The move a reading made, marked for as long as it takes to look at it. */
.dial.moved { border-color: var(--brand-text); }
.dial.moved .val { animation: dialpop .5s ease; }
@keyframes dialpop {
  0% { transform: scale(1); color: var(--ink); }
  40% { transform: scale(1.35); color: var(--brand-text); }
  100% { transform: scale(1); color: var(--ink); }
}
@media (prefers-reduced-motion: reduce) {
  .dial.moved .val { animation: none; }
  .dial > summary::after { transition: none; }
}

/* What this number lets through and what it blocks, in the lab's own words.
   The wording changes with the value; see LG.DIAL_BANDS in guardian.js. The
   two floors share this same container but render one line instead of two
   (LG.floorSays), so .floorline picks up the block layout below for free. */
.dialsays { margin-top: var(--space-3); font-size: var(--text-s); color: var(--ink-soft); }
.dialsays span { display: block; margin-top: var(--space-1); }
.dialsays .allows b { color: var(--include); }
.dialsays .blocks b { color: var(--urgent); }
.dialwhy {
  margin-top: var(--space-3); padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--brand-fill); background: var(--surface-sunken);
  font-size: var(--text-s); color: var(--ink);
}
.dialwhy[hidden] { display: none; }

/* The dial-change sheet (LG.reasonSheetHtml, 2026-08-29-provenance.sql).
   Pills are toggle buttons, not radios: a parent can pick more than one. */
.reasonsheet { margin-top: var(--space-4); }
.reasonsheet > .q { font-size: var(--text-s); font-weight: 600; margin-bottom: var(--space-2); }
.reasonsheet .opt { font-weight: 400; color: var(--ink-soft); }
.reasonsheet .pills { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-2); }
.reasonsheet .pill {
  font: inherit; font-size: var(--text-s); padding: var(--space-1) var(--space-3);
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink); cursor: pointer;
}
.reasonsheet .pill[aria-pressed="true"] { background: var(--brand-fill); border-color: var(--brand-fill); color: var(--on-brand, #fff); }
.reasonsheet textarea { width: 100%; font: inherit; }
.reasonsheet .disclosure { font-size: var(--text-xs, 0.75rem); color: var(--ink-soft); margin-top: var(--space-1); }

/* The two lists a reading hands back, as read-only tags: what the model
   understood from the sentence. They were tappable until the 2026-08-28
   review, which found nothing downstream ever read the surviving list back. */
.chiplabel { margin: var(--space-4) 0 var(--space-2); font-size: var(--text-xs);
             font-weight: 700; color: var(--ink-faint); text-transform: uppercase;
             letter-spacing: .04em; }
.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 34px; padding: 0 var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink); font: inherit; font-size: var(--text-s);
}

/* The button is working. A ring, not a sentence: the sentence is in the .msg
   beside it and this is the thing that says the page has not stalled. */
.working {
  display: inline-block; width: 18px; height: 18px; margin-left: var(--space-3);
  vertical-align: middle; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--brand-text);
  animation: workingspin .9s linear infinite;
}
.working[hidden] { display: none; }
@keyframes workingspin { to { transform: rotate(360deg); } }
/* No reduced-motion override here on purpose: the sheet's own global rule at
   the bottom already sets `animation: none !important` on everything, so the
   ring is a static one and the sentence in the .msg beside it is what says the
   page is working. An `animation-duration` here would be dead CSS. */
input[type="range"] {
  width: 100%; margin: var(--space-2) 0 var(--space-1);
  height: var(--tap-min); accent-color: var(--brand-text); background: transparent;
}
.dial .ends { display: flex; justify-content: space-between; font-size: var(--text-xs); color: var(--ink-faint); }

/* Read-only evening ceiling under a dial that has one (scariness,
   sensory_load, sleep_disruption). Never a control — it names the number
   already in force at night (docs/design/dial-model-2026-08-29.md §5, §9). */
.dialevening {
  margin-top: var(--space-3); padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--ink-faint); background: var(--surface-sunken);
  font-size: var(--text-s); color: var(--ink-soft);
}
.dialevening b { color: var(--ink); }

/* "Eight more limits": a group of dials collapsed together, so a parent who
   has not touched any of them sees one row, not eight. A reading that moves
   one opens both this and the dial itself (LG.setDial). */
.dialgroup-more {
  margin-bottom: var(--space-3); border: 1px solid var(--line);
  border-radius: var(--radius-m); background: var(--surface);
}
.dialgroup-more > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  min-height: var(--tap-min); padding: var(--space-3) var(--space-4);
  cursor: pointer; font-weight: 700; list-style: none;
}
.dialgroup-more > summary::-webkit-details-marker { display: none; }
.dialgroup-more > summary::after {
  content: ""; flex: 0 0 auto; width: 9px; height: 9px;
  border-right: 2px solid var(--ink-faint); border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg) translate(-2px, -2px); transition: transform .15s ease;
}
.dialgroup-more[open] > summary::after { transform: rotate(225deg) translate(-2px, -2px); }
.dialgroup-more-body { padding: 0 var(--space-4) var(--space-4); }
.dialgroup-more-body > .why { margin-top: 0; }
.dialgroup h3 { margin: var(--space-5) 0 var(--space-1); font-size: var(--text-m); }
@media (prefers-reduced-motion: reduce) {
  .dialgroup-more > summary::after { transition: none; }
}

/* The sticky count bar overlays whatever is at the bottom of the viewport, so
   every slider reserves a bar's height of clear space under itself. Combined
   with scroll-padding-bottom below, a dial scrolled into view is never the
   thing the bar is sitting on. Scoped to the page that has the bar. */
.has-bar .dial .dialbody { padding-bottom: calc(var(--counter-bar-h) + var(--space-3)); }
.has-bar { scroll-padding-bottom: calc(var(--counter-bar-h) + var(--space-4)); }
.has-bar input[type="range"] { scroll-margin-bottom: calc(var(--counter-bar-h) + var(--space-5)); }

/* The progress line. Four questions plus the age question is five, and a page
   2,360 px long on a 844 px viewport is three screens with nothing telling a
   parent where they are (SECURITY-VERIFY, parent journey, item 4). It is a
   line and a sentence, not a wizard: the page stays one scroll. */
.progress {
  position: sticky; top: 0; z-index: 4;
  margin: 0 calc(var(--space-4) * -1) var(--space-5);
  padding: var(--space-2) var(--space-4) var(--space-3);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-xs); color: var(--ink-soft);
}
.progress .track {
  display: block; height: 3px; margin-top: var(--space-2);
  background: var(--line); border-radius: var(--radius-pill); overflow: hidden;
}
.progress .track i {
  display: block; height: 100%; width: 0;
  background: var(--brand-text); border-radius: inherit;
  transition: width 180ms ease-out;
}
@media (prefers-reduced-motion: reduce) { .progress .track i { transition: none; } }

/* The resume line, shown only when sessionStorage handed something back. */
.resumed { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
.resumed[hidden] { display: none; }

.counter {
  background: var(--include-soft); border: 1px solid #bcd9c8;
  border-radius: var(--radius-l); padding: var(--space-4) var(--space-5);
  margin: var(--space-5) 0;
}
/* On the setup page the count rides the bottom of the screen while the dials
   are on screen, then settles into its own place under them: a parent
   dragging the first dial has to see the number the drag is changing. Only
   one of these may be sticky on a page, so settings uses the plain card.

   It is a THIN bar, and that is a correctness property rather than taste. At
   390x844 the old three-line pill was 104 px of opaque surface pinned to the
   bottom of the viewport and it sat on top of the first dial's slider, which is
   the one control the whole page exists to move (SECURITY-VERIFY, parent
   journey; shots/02-setup-390.png). One line is --counter-bar-h tall, the dials
   reserve that much room under each slider through .dial's padding, and
   scroll-padding-bottom below keeps any browser-driven scroll clear of it. */
:root { --counter-bar-h: 56px; }

.counter.bar {
  position: sticky; bottom: 0; z-index: 3;
  display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
  min-height: var(--counter-bar-h);
  border: 0; border-top: 1px solid #bcd9c8;
  border-radius: var(--radius-l) var(--radius-l) 0 0;
  padding: var(--space-3) var(--space-5);
  margin: var(--space-5) calc(var(--space-4) * -1) 0;
  box-shadow: 0 -6px 20px rgba(35, 42, 63, .10);
}
.counter.bar .n { font-size: var(--text-xl); display: inline; }
.counter.bar .mode { font-size: var(--text-s); }
.counter.bar .of { font-size: var(--text-xs); width: 100%; }
.counter .n {
  font-family: var(--font-display); font-weight: 700; font-size: var(--text-num);
  line-height: 1; color: var(--include); display: block;
}
.counter .mode { font-size: var(--text-m); color: var(--ink); }
.counter .of { font-size: var(--text-s); color: var(--ink-soft); }
.counter[data-state="waiting"] { background: var(--surface-sunken); border-color: var(--line); }
.counter[data-state="waiting"] .n { color: var(--ink-faint); }
.counter[data-state="error"] { background: var(--urgent-soft); border-color: #e9b7bf; }
.counter[data-state="error"] .n { color: var(--urgent); }
/* A count of zero. Not an error, and not a success either: the settings are
   answering and they admit nothing, so the wall would be empty. It reads as the
   amber of something to look at rather than the green of something done. */
.counter[data-state="zero"] { background: var(--review-soft); border-color: #e0c78a; }
.counter[data-state="zero"] .n { color: var(--review); }

.consent { display: flex; gap: var(--space-3); align-items: flex-start; margin: var(--space-4) 0; }
.consent input { width: 22px; height: 22px; margin: 2px 0 0; flex: none; accent-color: var(--brand-text); }
.consent label { font-weight: 400; margin: 0; }

/* The beta agreement's four plain-words lines, under the tickbox on /join and
   inside the interstitial. Same words in both, from LG.AGREEMENT_LINES. */
.agreement-lines { color: var(--ink-soft); }
.agreement-lines p:last-child { margin-bottom: 0; }

/* The interstitial for a parent whose family joined before the agreement.
   `main` is hidden by the class guardian.js sets before it asks /api/me, and
   revealed either when the tick is already on record or when this overlay's
   Continue button has saved one. The overlay itself sits outside `main`, so
   hiding the page never hides the thing asking. */
.lg-agreement-pending main { visibility: hidden; }
.agreement-overlay {
  position: fixed; inset: 0; z-index: 100; overflow-y: auto;
  background: var(--bg);
  padding: var(--space-5) var(--space-4) var(--space-8);
}
.agreement-card { max-width: 46rem; margin: 0 auto; }

.note { background: var(--info-soft); border-radius: var(--radius-m); padding: var(--space-3) var(--space-4); font-size: var(--text-s); }
.msg { border-radius: var(--radius-m); padding: var(--space-3) var(--space-4); margin: var(--space-4) 0; font-size: var(--text-m); }
.msg.ok { background: var(--include-soft); color: var(--include); }
.msg.bad { background: var(--urgent-soft); color: var(--urgent); }
.msg.info { background: var(--info-soft); color: var(--info); }
.msg[hidden] { display: none; }

/* "Work them out again", guardian parity P5 (2026-08-31): the stamp and the
   button sit on one line on a wide screen and wrap to their own lines on a
   phone, rather than the button ever crowding the sentence. */
.recompute { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-2); }

/* The "needs you" queue, guardian parity P1 (2026-08-31). The left border is
   the same signal as the chip's colour, restated so a parent scanning the
   card edge sorts it correctly even if the two colours read close together;
   the chip's own text ("urgent" / "warn" / "info") is what actually carries
   the meaning, not the colour alone. */
.alarm { border: 1px solid var(--line); border-left: 4px solid var(--line-strong);
  border-radius: var(--radius-m); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-3); }
.alarm:last-child { margin-bottom: 0; }
.alarm h3 { margin: 0 0 var(--space-2); font-size: var(--text-m); display: flex; align-items: center; gap: var(--space-2); }
.alarm[data-severity="urgent"] { border-left-color: var(--urgent); }
.alarm[data-severity="warn"] { border-left-color: var(--review); }
.alarm[data-severity="info"] { border-left-color: var(--info); }
.sevchip { font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 2px var(--space-2); border-radius: var(--radius-pill); flex: none; }
.sevchip[data-severity="urgent"] { background: var(--urgent-soft); color: var(--urgent); }
.sevchip[data-severity="warn"] { background: var(--review-soft); color: var(--review); }
.sevchip[data-severity="info"] { background: var(--info-soft); color: var(--info); }
.alarm .since { font-size: var(--text-xs); color: var(--ink-faint); margin: 0 0 var(--space-2); }
.alarm .actions { display: flex; align-items: center; gap: var(--space-3); }
.alarm .err { color: var(--urgent); font-size: var(--text-s); }

.kid { display: flex; flex-direction: column; gap: var(--space-4); }
.who { display: flex; align-items: center; gap: var(--space-3); }
.who h2, .who h3 { margin: 0; }
.avatar { width: 44px; height: 44px; border-radius: 50%; flex: none; display: block; }
.qrwrap { align-self: center; width: 190px; max-width: 60%; }
.qrwrap svg { display: block; width: 100%; height: auto; border-radius: var(--radius-s); }
.linkline {
  display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap;
  background: var(--surface-sunken); border-radius: var(--radius-m); padding: var(--space-3);
}
.linkline .url {
  flex: 1 1 12rem; min-width: 0; overflow-wrap: anywhere; word-break: break-all;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--text-s);
}
.nudges { margin: 0; padding-left: 1.1rem; font-size: var(--text-s); color: var(--ink-soft); }
.nudges li { margin-bottom: var(--space-2); }

/* is this child's shelf built yet: the handoff card's own state line */
.ready {
  border-radius: var(--radius-m); padding: var(--space-2) var(--space-3);
  font-size: var(--text-s); font-weight: 600;
}
.ready[data-state="waiting"] { background: var(--review-soft); color: var(--review); }
.ready[data-state="ok"] { background: var(--include-soft); color: var(--include); }

/* The unlock list is COLLAPSED on guardian home until level 1 arrives.
   It was 2,846 px of page explaining two API keys that "do nothing yet", and a
   parent on night one read the longest thing on the page and came away thinking
   the product was unfinished (SECURITY-VERIFY, parent journey, item 5). One
   line, and the list is one tap away for anyone who wants to read it.

   <details> rather than a script: it works with the page's JS blocked, it is
   keyboard and screen-reader native, and there is nothing here to get wrong. */
.folded { border: 1px solid var(--line); border-radius: var(--radius-m); background: var(--surface-sunken); }
.folded > summary {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-min); padding: 0 var(--space-4);
  cursor: pointer; font-size: var(--text-s); color: var(--ink-soft);
  list-style: none;
}
.folded > summary::-webkit-details-marker { display: none; }
.folded > summary::after {
  content: '\203A'; margin-left: auto; font-size: var(--text-l);
  transition: transform 150ms ease-out;
}
.folded[open] > summary::after { transform: rotate(90deg); }
.folded[open] > summary { border-bottom: 1px solid var(--line); }
.folded .inner { padding: var(--space-4); }
.folded .inner > :last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) { .folded > summary::after { transition: none; } }

.todo { border: 1px solid var(--line); border-radius: var(--radius-m); padding: var(--space-4); margin-bottom: var(--space-3); background: var(--surface-sunken); }
.todo h3 { margin-bottom: var(--space-1); color: var(--ink-soft); }
.todo p { margin-bottom: var(--space-2); color: var(--ink-soft); font-size: var(--text-s); }
.todo .payoff { color: var(--ink); font-size: var(--text-s); }
.todo .locked {
  display: inline-block; margin-top: var(--space-2);
  background: var(--review-soft); color: var(--review);
  border-radius: var(--radius-pill); padding: 2px var(--space-3);
  font-size: var(--text-xs); font-weight: 700;
}

.queue { list-style: none; margin: 0; padding: 0; }
.queue li { display: flex; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: 1px solid var(--line); font-size: var(--text-s); }
.queue li:last-child { border-bottom: 0; }
.queue .t { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.queue .d { flex: none; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

/* The Library card. One block per band, and the number the parent came for is
   the one thing set at display size. */
.lib { border-top: 1px solid var(--line); padding-top: var(--space-4); margin-top: var(--space-4); }
.lib:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.lib h3 { margin-bottom: var(--space-1); }
.lib-n { font-family: var(--font-display); font-size: var(--text-l); margin-bottom: var(--space-2); }
.lib-n span { font-size: var(--text-num); font-weight: 700; line-height: 1; margin-right: var(--space-2); }
.lib [data-state="running"] { color: var(--review); font-weight: 700; }
.lib .queue { margin-top: var(--space-2); }

/* "optional", on a heading. A word, not a badge with an opinion. */
.opt {
  font-family: var(--font-body); font-size: var(--text-xs); font-weight: 400;
  color: var(--ink-faint); text-transform: none; letter-spacing: 0;
}

.ok-line { color: var(--include); font-weight: 700; }

/* Free text about CONTENT, never about the child. The one textarea shape in the
   product, so the two places that carry it cannot drift apart. */
textarea {
  display: block; width: 100%; box-sizing: border-box;
  font-family: var(--font-body); font-size: var(--text-m); line-height: var(--leading);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius-m);
  padding: var(--space-3); resize: vertical; min-height: 84px;
}
textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

input[type="file"] {
  display: block; width: 100%; box-sizing: border-box;
  font-size: var(--text-s); color: var(--ink-soft);
  border: 1px dashed var(--line-strong); border-radius: var(--radius-m);
  padding: var(--space-3); background: var(--surface-sunken);
}

/* The one <select> in the product: the bug report's child picker. It takes the
   text input's shape so the report form reads as one thing rather than three. */
select {
  display: block; width: 100%; box-sizing: border-box;
  min-height: var(--tap-min); padding: 0 var(--space-3);
  font: 400 var(--text-m)/var(--tap-min) var(--font-body);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
}
select:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Each label read with its own field rather than with the note above it. */
#reportProblem label { margin-top: var(--space-4); }
#reportProblem label:first-of-type { margin-top: 0; }

.steps { padding-left: 1.3rem; }
.steps li { margin-bottom: var(--space-3); }
.plain { list-style: none; margin: 0 0 var(--space-4); padding: 0; }
.plain li { padding: var(--space-1) 0; border-bottom: 1px solid var(--line); }
.plain li:last-child { border-bottom: 0; }

.danger-zone { border-color: #e9b7bf; }
.danger-zone h2 { color: var(--urgent); }

/* The YouTube Brand Feature (see review.html and channels.html). Sized and
   spaced against the brand site's own numbers for the real mark, so dropping
   the official asset in later changes the picture and not the layout: 20dp
   is its minimum height and its clear space has to be at least that same
   height on every side. 15px type on a 20px line clears the first, 24px of
   padding clears the second. It sits above .foot and reads a step stronger
   than those links: an attribution is a statement, not secondary navigation,
   so it takes --ink-soft rather than --ink-faint. */
.ytbrand {
  margin-top: var(--space-7); padding: var(--space-6) 0;
  font-size: var(--text-m); line-height: 20px; color: var(--ink-soft);
  border-top: 1px solid var(--line);
}
.ytbrand + .foot { margin-top: 0; }

.foot { margin-top: var(--space-7); font-size: var(--text-s); color: var(--ink-faint); }
/* The footer links were 95x16 and 52x16. They are secondary navigation, not
   primary actions, so they stay visually small and grow their target with
   padding instead of type. */
.foot a {
  color: var(--ink-soft);
  display: inline-flex; align-items: center;
  min-height: var(--tap-min); padding: 0 var(--space-1);
}

/* ---- "Choose their icon" (icon.js) --------------------------------------

   The file input is inside its own <label class="btn">, which is what makes
   the whole button open the picker on iOS: a bare <input type="file"> renders
   as a system control that cannot be styled and reads "No file chosen" beside
   itself. The input is moved off-screen rather than display:none, because a
   display:none input is unreachable from a keyboard in some engines. */
.iconpick h3 { margin-bottom: var(--space-2); }
.iconrow { display: flex; align-items: center; gap: var(--space-4);
  margin: var(--space-4) 0; flex-wrap: wrap; }
.iconpreview { width: 88px; height: 88px; border-radius: var(--radius-m);
  border: 1px solid var(--line); background: var(--surface-sunken); flex: none; display: block; }
.iconacts { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.iconacts .btn { position: relative; overflow: hidden; }
.iconacts input[type="file"] { position: absolute; width: 1px; height: 1px;
  opacity: 0; left: 0; top: 0; }
.iconcands { display: flex; gap: var(--space-3); flex-wrap: wrap;
  margin-bottom: var(--space-4); }
.iconcandbtn { all: unset; cursor: pointer; border-radius: var(--radius-m);
  padding: 3px; border: 2px solid transparent; min-height: var(--tap-min);
  display: inline-flex; align-items: center; }
.iconcandbtn:hover, .iconcandbtn:focus-visible { border-color: var(--line-strong); }
.iconcand { width: 56px; height: 56px; border-radius: var(--radius-m); display: block; }

/* Spacing utilities. GUARDIAN_CSP is `style-src 'self'`, which blocks an inline
   style attribute as surely as it blocks an inline <script>, so the handful of
   one-off margins the pages needed are classes rather than style="…". */
.m-0  { margin: 0; }
.mt-2 { margin-top: var(--space-2); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-5 { margin-bottom: var(--space-5); }
.ml-2 { margin-left: var(--space-2); }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* Visually hidden, still read aloud. The dial sliders carry a real <label>
   that the summary already says in words, and the chips carry the word
   "remove" that the x only draws. */
.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
