/* Kid wall layout. Every colour and size comes from tokens.css.
   No inline style attribute appears anywhere in this page: the CSP in
   wall.html grants style-src 'self' with no 'unsafe-inline', so a state
   that needs to look different gets a class, never a style attribute. */

#demobanner{background:var(--brand-fill);color:var(--brand-fill-ink);
  padding:var(--space-3) var(--space-5);text-align:center;font-size:var(--text-s)}
#demobanner p{margin:0;max-width:960px;margin-inline:auto}
#demobanner a{color:inherit;font-weight:600}

header{display:flex;align-items:center;gap:var(--space-4);
  padding:var(--space-3) var(--space-5);position:sticky;top:0;background:var(--bg);
  border-bottom:1px solid var(--line);z-index:5}
/* The related rail (2026-08-28) put real scrollable content below the stage
   for the first time, so the page CAN scroll while a video is on screen. A
   sticky header pinned at y:0 through that scroll would pass in front of
   the still-visible bottom of the stage mid-scroll: HB-7 forbids that. So
   the header drops out of sticky (and scrolls away like everything else)
   for exactly as long as the player is up, rather than trying to bound the
   rail's height to keep the old "the page never scrolls" invariant, which
   a related rail cannot honestly promise. tests/browser-check.mjs measures
   the result at full scroll rather than trusting this rule alone. */
body:has(#player.on) header{position:static}
header .mark{width:28px;height:28px;flex:0 0 auto;color:var(--brand-fill)}
header .kidicon{width:32px;height:32px;flex:0 0 auto;border-radius:var(--radius-s);
  object-fit:cover;background:var(--surface-sunken);display:block}
/* Author CSS always outranks the UA stylesheet's [hidden]{display:none},
   regardless of specificity, so the display:block above wins over the
   hidden attribute wall.js sets on a dead token or a load error and the
   empty bordered square shows anyway. This rule must stay in lockstep
   with any change to the selector above. */
header .kidicon[hidden]{display:none}

/* The drawn fallback for a missing or broken kidicon (Design 4). Colour and
   motif arrive as classes -- c0..c5, m0..m3 -- from LanternAvatar.svg
   (assets/wall/avatar.js), never as a hex baked into the markup, and the
   fill comes from the same --kid-* tokens assets/guardian/guardian.css
   defines, so the wall and the guardian pages can never draw two different
   colours for the same index. */
.avatar.c0 .base{fill:var(--kid-teal)}
.avatar.c1 .base{fill:var(--kid-coral)}
.avatar.c2 .base{fill:var(--kid-plum)}
.avatar.c3 .base{fill:var(--kid-sea)}
.avatar.c4 .base{fill:var(--kid-moss)}
.avatar.c5 .base{fill:var(--kid-rose)}
.avatar text{fill:#fff}
.avatar .deco{fill:#fff}
.avatar.m0 .deco{opacity:.18}
.avatar.m1 .deco{opacity:.14}
.avatar .ring1,.avatar .ring2{fill:none;stroke:#fff;stroke-width:6}
.avatar.m2 .ring1{stroke-opacity:.22}
.avatar.m2 .ring2{stroke-opacity:.16}
.avatar.m3 .ring1{stroke-opacity:.2}

header h1{font-family:var(--font-display);font-weight:700;font-size:var(--text-xl);
  letter-spacing:.04em;white-space:nowrap}
header h2{font-family:var(--font-display);font-weight:600;font-size:var(--text-l);
  color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- nav + channel view (Jeff's ruling, 2026-08-28) ----
   Plain flow, never positioned: same "nothing sits over the player" rule as
   everything else here, and #player.on hides both exactly like it hides
   #grid and #searchbar (see that rule below). */
#navbar{display:flex;gap:var(--space-3);padding:var(--space-5) var(--space-5) 0;
  max-width:1280px;margin:0 auto;overflow-x:auto}
/* Icon-first pills (Jeff's directive, 2026-09-01: "All pills need
   iconography," "this needs to work for a 2 year old and non reader to
   grok"). The glyph is the primary read; the label rides underneath for a
   parent glancing over a shoulder, never the other way around. Still a
   real >=44px touch target (var(--tap-kid) is 64px) at the toddler-poke
   sizes the directive calls for. */
.navbtn{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:2px;min-width:72px;min-height:var(--tap-kid);
  padding:var(--space-2) var(--space-4);border:1px solid var(--line-strong);
  border-radius:var(--radius-l);background:var(--surface-raised);color:var(--ink);
  cursor:pointer;font-family:var(--font-display)}
.navicon{width:30px;height:30px;flex:0 0 auto;display:block;color:var(--brand-fill)}
.navlabel{font-size:var(--text-s);font-weight:700;white-space:nowrap}
.navbtn.on{background:var(--brand-fill);color:var(--brand-fill-ink);border-color:var(--brand-fill)}
/* Solid-toy glyphs (Jeff's pick, 2026-09-01: icons C on Forest & Butter)
   are single-color fill="currentColor" silhouettes, which retires the
   #225-era [fill]/[stroke] override hack: the selected pill flips its
   glyph to the ink color through .on's own `color` plus the one rule
   below. The lone two-tone detail is Watched's check, a `.cut` stroked
   in the surface behind the glyph so it reads as a cutout -- on .on that
   surface IS --brand-fill, hence the paired rule. */
.navbtn.on .navicon{color:var(--brand-fill-ink)}
.navicon .cut{stroke:var(--surface-raised)}
.navbtn.on .navicon .cut{stroke:var(--brand-fill)}
.navbtn:active{transform:scale(.97)}

#channelbar{display:flex;align-items:center;gap:var(--space-4);
  padding:var(--space-4) var(--space-5) 0;max-width:1280px;margin:0 auto}
#channelbar[hidden]{display:none}
#navbar[hidden]{display:none}
#channelBack{flex:0 0 auto;min-height:var(--tap-kid);padding:0 var(--space-6);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--surface-raised);color:var(--ink);cursor:pointer;
  font-family:var(--font-display);font-size:var(--text-m);font-weight:700}
#channelbar h2{font-family:var(--font-display);font-size:var(--text-l);font-weight:700;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- search ----
   In normal flow, never positioned: the same "nothing sits over the player"
   rule as everything else on this page, and it costs nothing extra here
   because #player.on hides this row exactly like it hides #grid. */
#searchbar{display:flex;gap:var(--space-3);align-items:center;
  padding:var(--space-5) var(--space-5) 0;max-width:1280px;margin:0 auto}
#searchbar input{flex:1;min-width:0;min-height:var(--tap-kid);
  background:var(--surface-sunken);border:1px solid var(--line);
  border-radius:var(--radius-pill);padding:0 var(--space-5);
  font-family:var(--font-body);font-size:var(--text-l);color:var(--ink)}
#searchbar input::placeholder{color:var(--ink-faint)}
/* -webkit-appearance:none strips the iOS Safari factory chrome (the tiny grey
   clear-x and magnifier) so the one deliberate control this page ships,
   #qClear, is the only one visible. */
#searchbar input[type=search]{-webkit-appearance:none;appearance:none}
/* Chrome and Safari both grow their own cancel-x inside a type=search field
   independent of `appearance:none` on the input itself, and it does not run
   our clearSearch(): a tap on it empties the box but leaves searchQuery and
   the filtered grid exactly as they were, which is a broken control on a kid
   surface. #qClear is the ONE clear affordance this page ships. */
#searchbar input[type=search]::-webkit-search-cancel-button,
#searchbar input[type=search]::-webkit-search-decoration{-webkit-appearance:none;display:none}
#qClear{flex:0 0 auto;width:var(--tap-kid);height:var(--tap-kid);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--surface-raised);color:var(--ink);font-size:var(--text-l);
  font-family:var(--font-body);cursor:pointer}
#qClear:active{transform:scale(.94)}
#qClear[hidden]{display:none}

/* ---- the friendly result count (unit 2, Jeff's directive 2026-09-01) ----
   For a parent glancing over a shoulder, not a control a child has to
   read: the cards in #grid below are the real signal. Small, quiet type;
   the icon carries "these are videos" without asking anyone to read a
   word. */
#searchCount{display:flex;align-items:center;gap:var(--space-2);
  max-width:1280px;margin:0 auto;padding:var(--space-3) var(--space-5) 0;
  color:var(--ink-soft);font-family:var(--font-body);font-size:var(--text-s);
  font-weight:700}
#searchCount[hidden]{display:none}
.counticon{width:16px;height:16px;flex:0 0 auto}

/* ---- the wall ---- */
#grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-5);
  padding:var(--space-6) var(--space-5) var(--space-7);max-width:1280px;margin:0 auto}
@media (min-width:720px){#grid{grid-template-columns:repeat(3,1fr);
  gap:var(--space-5);padding:var(--space-6) var(--space-6) var(--space-8)}}
@media (min-width:1100px){#grid{grid-template-columns:repeat(4,1fr)}}

/* .card is a plain container holding two direct children: .cardplay (a real
   <button> around the thumbnail) and .info. Never a button inside a button:
   a nested control is invalid markup and is unreachable from a keyboard or
   a TV remote. */
.card{background:var(--surface-raised);border:1px solid var(--line);
  border-radius:var(--radius-tile);overflow:hidden;text-align:left;color:var(--ink);
  box-shadow:var(--shadow-1);display:block;width:100%;
  transition:box-shadow var(--dur-2) var(--ease),transform var(--dur-1) var(--ease)}
.card:hover{box-shadow:var(--shadow-2);border-color:var(--line-strong)}
.card:has(.cardplay:active){transform:scale(.97)}
.cardplay{all:unset;display:block;cursor:pointer;min-height:var(--tap-kid)}
.thumb{display:block;position:relative;aspect-ratio:16/9;background:var(--surface-sunken)}
.thumb img{width:100%;height:100%;object-fit:cover;display:block}
.thumb img.missing{display:none}
.dur{position:absolute;right:8px;bottom:8px;background:var(--scrim);color:#fff;
  border-radius:6px;padding:2px 8px;font-family:var(--font-display);font-weight:600;
  font-size:var(--text-xs);font-variant-numeric:tabular-nums}
/* The "short" text overlay is retired (Jeff's directive, 2026-09-01: "this
   then means we can replace the 'short' overlay with the lightning too"):
   the badge is now the same lightning glyph as the Shorts nav pill
   (wall.js's LIGHTNING_SVG), never the word "short". */
.badge{position:absolute;left:8px;top:8px;background:var(--brand-fill);
  color:var(--brand-fill-ink);border-radius:6px;padding:4px;display:flex}
.badge svg{width:14px;height:14px;display:block}
/* The clamped title carries NO padding of its own. -webkit-line-clamp clips at
   the CONTENT box, so bottom padding on the clamped element lets a sliver of
   line 3 bleed through under the clamp (seen on the first Chrome pass here, and
   the same D8 bug the lab viewer fixed). All the spacing lives on .info. */
.info{padding:var(--space-3) var(--space-4) var(--space-4)}
.card h3{font-size:var(--text-l);font-weight:800;line-height:var(--leading-tight);
  cursor:pointer;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden}
/* The channel name is a real second control (channel view, 2026-08-28's
   ruling), a sibling of .cardplay rather than nested inside it, same reason
   as the lab's own two-sibling-buttons fix: a <p> inside a <button> is
   invalid markup and unreachable from a keyboard or a TV remote. */
.card .ch{all:unset;display:block;cursor:pointer;font-size:var(--text-s);
  color:var(--ink-soft);padding-top:var(--space-2);min-height:var(--space-7);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* "+ Add to my list" is retired from the kid UI entirely (Jeff's directive,
   2026-09-01: "though let's just get rid of the list" -- the .mylist
   button, onToggleMyList/sendListToggle and their card wiring are gone
   from wall.js; control/src/lib/list.ts's own getList/postList door is
   untouched, just unused from here). This rule is deleted with it, not
   left as dead CSS. */
/* Liked (Jeff's ruling, 2026-08-31, board T-1813; icon-first per the
   2026-09-01 directive: "Like this video" is now a thumbs-up glyph, not a
   text pill). A plain toggle -- a heart/like is a keep a kid can give and
   take back, never a rating, and there is still no thumbs-down anywhere on
   this surface -- but the button itself is a compact icon target now
   rather than a full-width text pill: the accessible name (aria-label)
   carries "Like this video"/"Unlike this video" for anyone who needs it
   read aloud, since there is no visible text left to read. */
.card .heart{display:inline-flex;align-items:center;justify-content:center;
  margin-top:var(--space-2);width:var(--tap-min);height:var(--tap-min);padding:0;
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--surface-raised);color:var(--ink);cursor:pointer}
.card .heart svg{width:22px;height:22px;display:block;color:var(--brand-fill)}
/* "Did my like stick?" is the card's most important feedback: the lit
   state inverts butter-on-green to green-ink-on-butter (currentColor,
   same design as .navbtn.on above). */
.card .heart.on{background:var(--brand-fill);border-color:var(--brand-fill)}
.card .heart.on svg{color:var(--brand-fill-ink)}
/* Demo mode's own link-out state (makeCard, wall.js): "Sign up to like
   videos" is a real sentence for an adult visitor, not an icon, so it keeps
   the old full-width text-pill shape instead of the icon circle above. */
.card .heart.demolink{display:block;width:auto;height:auto;min-height:var(--space-7);
  padding:0 var(--space-3);font-family:var(--font-body);font-size:var(--text-s);font-weight:600}

/* ---- Look further, the deliberate second tap (unit 2, Jeff's directive
   2026-09-01) ----
   Sits right after #searchCount, ABOVE #grid (PR #226 review, P1: moved off
   its original after-#grid spot, which measured below the fold on a
   390x844 phone in every state tried -- see wall.html's own comment on
   #lookFurtherWrap for the measurement and the reasoning). A big, chunky,
   icon-first tap target -- the same tap-kid floor every other kid control
   on this page uses -- never a bare text link. */
#lookFurtherWrap{display:flex;justify-content:center;padding:0 var(--space-5) var(--space-6)}
#lookFurtherWrap[hidden]{display:none}
#lookFurtherBtn{display:flex;align-items:center;gap:var(--space-3);
  min-height:var(--tap-kid);padding:0 var(--space-6);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--surface-raised);color:var(--ink);cursor:pointer;
  font-family:var(--font-display);font-size:var(--text-l);font-weight:700}
#lookFurtherBtn:active{transform:scale(.97)}
#lookFurtherBtn svg{width:26px;height:26px;flex:0 0 auto;display:block}

/* ---- K1: the search-miss "look further" grid ----
   `.askcard` reuses .card's box but carries no .cardplay: there is nothing
   to press on the tile itself, only the ask button below it. */
#askwrap{max-width:1280px;margin:0 auto;padding:0 var(--space-5) var(--space-6)}
#askgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-4)}
@media (min-width:1100px){#askgrid{grid-template-columns:repeat(4,1fr)}}
.askcard{background:var(--surface-raised);border:1px solid var(--line);
  border-radius:var(--radius-tile);overflow:hidden;text-align:left;color:var(--ink);
  box-shadow:var(--shadow-1);display:block;width:100%}
.askbtn{display:block;width:100%;margin-top:var(--space-2);min-height:var(--tap-kid);
  padding:0 var(--space-3);border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  background:var(--surface-raised);color:var(--ink);cursor:pointer;
  font-family:var(--font-body);font-size:var(--text-s);font-weight:600}
.askbtn:disabled{cursor:default;opacity:.7}
/* Unit 2's own fail-closed state (wall.js's LOOK_FURTHER_CLOSED_MSG):
   a plain, calm sentence in place of the candidate grid, same voice as
   .boot p but sized to sit inside #askgrid's own column span rather than
   taking over the whole viewport like a boot failure does. */
.lfclosed{grid-column:1/-1;color:var(--ink-soft);font-family:var(--font-body);
  font-size:var(--text-m);font-weight:600;padding:var(--space-4) 0;text-align:center}

/* ---- more ---- */
#morewrap{display:flex;justify-content:center;padding:0 var(--space-5) var(--space-8)}
#morewrap[hidden]{display:none}
#more{background:var(--brand-fill);color:var(--brand-fill-ink);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  min-height:var(--tap-kid);padding:0 var(--space-7);cursor:pointer;
  font-family:var(--font-display);font-size:var(--text-l);font-weight:700}
#more:active{transform:scale(.97)}
#sentinel{height:1px}

/* ---- the YouTube Brand Feature (see wall.html) ----
   Sized and spaced against the brand site's own numbers for the real mark,
   so swapping 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, and 24px of padding all round clears the second with room to
   spare. Full-width and centred so it reads as the page's own footer rather
   than a caption belonging to the last card above it. */
#ytbrand{padding:var(--space-6) var(--space-5);text-align:center;
  font-family:var(--font-body);font-size:var(--text-m);line-height:20px;
  color:var(--ink-soft)}

/* ---- player ----
   The stage holds the iframe and nothing else. No element is ever positioned
   in front of the player: the back control sits ABOVE the stage, in normal
   flow, so the player, its controls and all YouTube branding stay visible
   and untouched. */
#player{display:none}
#player.on{display:block}
#player.on ~ #navbar,#player.on ~ #channelbar,
#player.on ~ #searchbar,#player.on ~ #searchCount,#player.on ~ #grid,
#player.on ~ #lookFurtherWrap,#player.on ~ #askwrap,
#player.on ~ #morewrap,#player.on ~ #sentinel{display:none}
.pbar{display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-3) var(--space-5) 0;max-width:1280px;margin:0 auto}
#back{background:var(--surface-raised);color:var(--ink);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);
  min-height:var(--tap-kid);padding:0 var(--space-6);cursor:pointer;
  font-family:var(--font-display);font-size:var(--text-l);font-weight:700}
#back:active{transform:scale(.97)}
/* "Tell a grown-up" (C3). Same big touch target as #back, styled as a
   secondary action so it never reads as the primary control on the stage.
   The button's own text swaps to the sent state (wall.js) rather than a
   second element appearing, so there is exactly one thing on screen a child
   has to make sense of. */
#flag{background:transparent;color:var(--ink-soft);
  border:1px solid var(--line);border-radius:var(--radius-pill);
  min-height:var(--tap-kid);padding:0 var(--space-5);cursor:pointer;
  font-family:var(--font-display);font-size:var(--text-m);font-weight:600;
  margin-left:auto}
#flag:active{transform:scale(.97)}
#flag:disabled{cursor:default;opacity:.7}
/* The stage is capped by the HEIGHT the viewport has left, not only by width,
   so the whole 16/9 player and its controls sit on screen without a scroll on
   an iPad in landscape. svh rather than vh: iOS Safari's toolbars change the
   large viewport under you mid-video. */
.stage{background:#000;border-radius:var(--radius-m);overflow:hidden;
  width:calc(100% - 2 * var(--space-5));
  max-width:min(1280px, calc((100vh - 200px) * 16 / 9));
  max-width:min(1280px, calc((100svh - 200px) * 16 / 9));
  margin:var(--space-4) auto 0}
.stage iframe{width:100%;aspect-ratio:16/9;border:0;display:block}
#ptitle{max-width:1280px;margin:0 auto;padding:var(--space-4) var(--space-5) var(--space-8);
  font-family:var(--font-display);font-size:var(--text-xl);font-weight:700}

/* ---- related rail: normal flow, BELOW the stage, never in front of it ---- */
#relatedwrap{max-width:1280px;margin:0 auto;padding:0 var(--space-5) var(--space-8)}
.railhead{font-family:var(--font-display);font-size:var(--text-m);font-weight:700;
  color:var(--ink-soft);padding-bottom:var(--space-3)}
#related{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-4)}
@media (min-width:720px){#related{grid-template-columns:repeat(4,1fr)}}

/* ---- the two failure states ---- */
.boot{grid-column:1/-1;min-height:60vh;display:flex;align-items:center;
  justify-content:center;text-align:center;padding:var(--space-8) var(--space-5)}
.boot p{font-size:var(--text-2xl);font-weight:700;color:var(--ink);
  max-width:460px;line-height:var(--leading-tight)}
/* The daily limit's own state: the same layout as the two failure states, a
   calmer message and a lighter second line rather than one flat block of
   bold text (docs/design/dial-model-2026-08-29.md §9). */
.boot.limit p.sub{font-size:var(--text-lg);font-weight:400;margin-top:var(--space-3)}
