/* ---------------------------------------------------------------
   KidBD / blog.css
   One stylesheet for the blog hub and every article under /blog/,
   so two posts written four months apart cannot drift apart.

   It is the cream paper world, tuned for READING rather than for
   selling: the same ground, the same ink, the same flat zero-blur
   offset shadow, a longer measure, and one rank fewer of saturated
   ground than the landing page carries. `legal.css` is its nearest
   relative and the two are deliberately not the same file: a legal
   page is a short document nobody scrolls twice, an article is 2,000
   words with a contents rail, a progress bar and two offers inside
   it.

   THE FILE LIVES AT THE ROOT ON PURPOSE. Every `url("fonts/...")`
   below resolves against THIS file, not against the page that loads
   it, so a stylesheet parked in /blog/ would ask for /blog/fonts/
   and every face would 404 with nothing in the console to say so.
   The pages reach it as `../blog.css`.

   Nexus V22 / Kavalsia Inc.
   --------------------------------------------------------------- */

/* ---------------------------------------------------------------
   The two faces, served from this site, same four cuts and the same
   unicode-range split as legal.css. Variable instances, so one
   request covers 400 through 700 and a bold heading fetches nothing
   extra. No third-party font host: an article page has no more
   business telling Google who read it than a privacy policy does.
   --------------------------------------------------------------- */
@font-face{
  font-family:"Fredoka"; font-style:normal; font-weight:400 700; font-display:swap;
  src:url("fonts/fredoka-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Fredoka"; font-style:normal; font-weight:400 700; font-display:swap;
  src:url("fonts/fredoka-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:"Lexend"; font-style:normal; font-weight:400 700; font-display:swap;
  src:url("fonts/lexend-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Lexend"; font-style:normal; font-weight:400 700; font-display:swap;
  src:url("fonts/lexend-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ---------------------------------------------------------------
   Tokens. Every colour here is the house value as DESIGN.md records
   it, and `--mut` is `#4d4383`, the value index.html/app.html/
   funnel.css standardised on, NOT legal.css's older `#544a8c`. A new
   surface has no reason to inherit a known drift.

   The type ladder is solved by the house's two-endpoint method:
   slope = (max - min) / (1440 - 320) * 100, base = min - slope/100*320,
   so every rank hits exactly its minimum at 320 and exactly its
   maximum at 1440 and none of them parks flat for half the range.
   --------------------------------------------------------------- */
:root{
  --bg:#fff5d6; --su:#fffdf4; --ink:#2a2350; --mut:#4d4383; --ln:#f0e6bf;
  --grape:#6a49f2; --grape-d:#5535cf;
  --sun:#ffce3a; --red:#ff5a5f; --tang:#ff9f1c; --mint:#1fc08a; --sky:#3aa0ff; --pink:#ff77c2;
  --cta:#ffa41f; --cta-hi:#ffb03d;
  --well:#f6efd9;
  --fmut:#cdc6ef;
  /* The READING ink, added round 33. Body copy on this blog was set in
     `--mut`, the same muted purple the landing page uses for one-line
     captions under a heading. A caption in it is fine; 2,000 words of it
     is the reason an article here reads as work. This sits two thirds of
     the way from that caption ink back to full ink, so a paragraph is
     comfortable and a heading still out-ranks it. Derived from the two
     tokens either side of it, never typed as a hex. */
  --read:color-mix(in srgb, var(--ink) 72%, var(--mut));
  /* The one white in this file. It exists only on the ink footer, where
     it is the HIGH ink of that slab and not a surface colour: nothing in
     the cream world is ever painted with it. legal.css carries the same
     value as a bare literal in three rules; here it has a name so a later
     round can find every use of it in one grep. */
  --fhi:#ffffff;

  --disp:"Fredoka",ui-rounded,"Segoe UI",system-ui,sans-serif;
  --body:"Lexend",system-ui,-apple-system,"Segoe UI",sans-serif;

  --t-h1:clamp(34px,27.14px + 2.143vw,58px);
  --t-h1-hub:clamp(38px,30.57px + 2.321vw,64px);
  --t-h2:clamp(24px,20.57px + 1.071vw,36px);
  --t-h3:clamp(19px,17.57px + .446vw,24px);
  --t-lede:clamp(17.5px,16.5px + .313vw,21px);
  --t-body:clamp(16.5px,16.21px + .089vw,17.5px);
  --t-sm:clamp(14px,13.71px + .089vw,15px);

  --s1:6px; --s2:10px; --s3:16px; --s4:24px; --s5:36px; --s6:56px; --s7:88px;

  --r-sm:14px; --r:26px; --r-lg:38px; --r-pill:999px;
  --shadow:6px 8px 0 var(--ink);
  --shadow-sm:4px 5px 0 var(--ink);
  --shadow-hi:9px 12px 0 var(--ink);
  --pad:clamp(18px,5vw,60px);
  color-scheme:light;
}

*{margin:0;padding:0;box-sizing:border-box;border:0}
button,input,textarea,select{border:0;font:inherit;color:inherit;background:none}
html,body{max-width:100%}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:96px}
body{
  background:var(--bg); color:var(--ink);
  font-family:var(--body); font-size:var(--t-body); line-height:1.6;
  -webkit-font-smoothing:antialiased; position:relative;
  -webkit-tap-highlight-color:transparent;
  /* clip, not hidden: a `hidden` here kills position:sticky on the
     contents rail, and the rail is the whole point of the layout. */
  overflow-x:clip;
}
/* the paper's own grain, the same dot the legal pages carry */
body::before{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background-image:radial-gradient(var(--ln) 1.4px,transparent 1.4px);background-size:26px 26px;opacity:.5}
a{color:inherit}
img,svg{display:block;max-width:100%}
::selection{background:var(--sun);color:var(--ink)}

/* A halo, not a ring. Two spreads so the mark reads on cream and on
   the ink footer alike, the pattern index.html moved to in round 32. */
:focus-visible{outline:none;box-shadow:0 0 0 3px var(--bg),0 0 0 7px var(--grape);border-radius:var(--r-sm)}

.wrap{max-width:1180px;margin:0 auto;padding:0 var(--pad);position:relative;z-index:2}
.wrap.narrow{max-width:760px}

/* ---------------------------------------------------------------
   The reading progress bar. Fixed, 4px, and it is a TRANSFORM on a
   full-width element rather than an animated width, so a long article
   never lays out sixty times a second while the reader scrolls.

   Parked at its start it is scaleX(0), which is the correct rest
   state for a progress bar and not the Law 14 trap of a scale that
   erases an element that is meant to be visible: nothing has been
   read yet, so nothing is drawn.

   It is armed by blog.js. With no script the element is simply never
   given `data-prog`, and an unarmed bar paints nothing.
   --------------------------------------------------------------- */
.prog{position:fixed;inset:0 0 auto;height:4px;z-index:40;pointer-events:none;display:none}
.prog[data-prog]{display:block}
.prog i{display:block;height:100%;background:var(--cta);transform-origin:0 50%;transform:scaleX(0)}

/* ---------------------------------------------------------------
   THE MAST. Round 33 replaced `.topbar` with this.

   What was here was a lone "Back to KidBD" pill on the left and an
   orphan orange CTA on the right, with the breadcrumb on a third row
   under them: three stacked rows of chrome before the reader reached
   the title, and no way to get anywhere on this site except back to
   the landing page. A reader who arrived on an article from a search
   result could not reach the other two articles, the blog hub, or any
   section of the product without first leaving the article.

   This is the landing page's own nav, brought over rule for rule: the
   same cream pill on the same `--shadow-sm`, the same 999px radius,
   the same 18px lead padding, the same 48px control height, the same
   sun-fill hover on a link and the same rule that drops the link row
   under 860 where it would wrap. It is STICKY rather than fixed: a
   fixed bar wants a matching `padding-top` on body, and body here is
   also the ground for the progress bar and the sticky contents rail,
   so one fixed element fewer is one scroll-offset trap fewer.

   `scroll-padding-top` on html is 96px, which clears this bar at its
   tallest, so a jump to a section from the rail never lands under it.
   --------------------------------------------------------------- */
.mast{position:sticky;top:0;z-index:30;padding:12px calc(var(--pad) + 4px) 12px var(--pad)}
.mast .bar{max-width:1180px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;
  gap:10px;background:var(--su);border-radius:var(--r-pill);padding:9px 9px 9px 18px;
  box-shadow:var(--shadow-sm)}
.mast .brand{font-family:var(--disp);font-weight:700;font-size:clamp(18px,2.3vw,23px);display:flex;
  align-items:center;gap:9px;flex:none;min-height:48px;text-decoration:none}
.mast .brand .mk{width:30px;height:30px;flex:none;border-radius:10px;overflow:hidden}
.mast .nlinks{display:flex;gap:4px;align-items:center;min-width:0}
.mast .nlinks a{font-weight:700;font-size:15px;padding:9px 14px;border-radius:var(--r-pill);
  min-height:48px;display:inline-flex;align-items:center;white-space:nowrap;text-decoration:none;
  transition:background .15s}
.mast .nlinks a:hover{background:var(--sun)}
/* the page you are on is a FILL, never a rim and never an underline */
.mast .nlinks a[aria-current]{background:color-mix(in srgb, var(--sun) 52%, var(--su));font-weight:800}
.mast .go{display:inline-flex;align-items:center;justify-content:center;gap:8px;background:var(--cta);
  color:var(--ink);border-radius:var(--r-pill);padding:.6em 1.2em;font-weight:800;font-size:15px;
  box-shadow:var(--shadow-sm);min-height:48px;flex:none;white-space:nowrap;text-decoration:none;
  transition:transform .12s ease, box-shadow .12s ease, background .15s ease}
.mast .go:hover{transform:translate(-2px,-2px);box-shadow:6px 8px 0 var(--ink);background:var(--cta-hi)}
.mast .go:active{transform:translate(3px,4px);box-shadow:2px 2px 0 var(--ink)}
@media (max-width:1000px){.mast .nlinks a{font-size:14px;padding:9px 10px}}
@media (max-width:860px){.mast .nlinks a{display:none}}

/* breadcrumb. Small, muted, and it is the same list the BreadcrumbList
   in the head describes, so the page and its structured data cannot
   disagree about where the reader is. */
.crumbs{margin-top:var(--s3);font-size:var(--t-sm);color:var(--mut);display:flex;flex-wrap:wrap;
  align-items:center;gap:8px;line-height:1.35}
.crumbs a{font-weight:700;text-decoration:underline;text-underline-offset:3px}
.crumbs svg{width:13px;height:13px;flex:none;opacity:.6}
.crumbs [aria-current]{color:var(--ink);font-weight:700}

/* ---------------------------------------------------------------
   Chips. A faint fill of the project's own accent, never a rim. The
   age chip carries the one fact a parent needs before reading a
   word, which is the thing the reference surface does and this one
   did not: whose child this is for.
   --------------------------------------------------------------- */
.chip{display:inline-flex;align-items:center;gap:7px;border-radius:var(--r-pill);
  background:color-mix(in srgb, var(--sun) 30%, var(--su));
  color:var(--ink);font-weight:800;font-size:13.5px;padding:7px 15px;line-height:1.3}
/* Three fills across a chip row, not four. `.topic` keeps the base sun
   tint, `.age` is the one that has to be found at a glance because it is
   the fact a parent reads before deciding to read at all, and `.time` is
   deliberately NEUTRAL: a read time is a readout, not a category, and a
   fourth colour on this row turned it into a paint chart. */
.chip.age{background:color-mix(in srgb, var(--mint) 24%, var(--su))}
.chip.time{background:var(--well);color:var(--mut);font-weight:700}
.chip svg{width:14px;height:14px;flex:none}
.chiprow{display:flex;flex-wrap:wrap;gap:var(--s2)}

/* ---------------------------------------------------------------
   HUB
   --------------------------------------------------------------- */
header.hub{padding:clamp(26px,4vw,44px) 0 clamp(24px,3.4vw,40px)}
header.hub h1{font-family:var(--disp);font-weight:700;font-size:var(--t-h1-hub);line-height:1.02;
  letter-spacing:-.018em;text-wrap:balance;max-width:16ch}
header.hub .lede{margin-top:var(--s4);font-size:var(--t-lede);color:var(--mut);max-width:54ch;line-height:1.55}
header.hub .chiprow{margin-top:var(--s4)}
@media (min-width:1024px){
  /* Baseline, not centre: the deck's first line sits on the headline's LAST
     line, so the two columns read as one sentence continuing rather than as
     two blocks that happen to share a row. */
  header.hub .wrap{display:grid;grid-template-columns:minmax(0,1.18fr) minmax(0,.82fr);
    gap:clamp(30px,3.6vw,60px);align-items:end}
  /* 16ch, not 12: at 12 the headline broke to four lines with "written"
     stranded alone on the third, which `text-wrap:balance` cannot undo
     because the max-width is the harder constraint. 16ch is the measure
     the three-line break wants, and the column is sized to hold it. */
  header.hub h1{max-width:16ch}
  header.hub .deck{min-width:0;padding-bottom:6px}
  header.hub .lede{margin-top:0;max-width:46ch}
}

.hublist{display:grid;gap:var(--s5);padding-bottom:var(--s6)}

/* A post card is one object cut from card paper: the art sits in a
   tinted well at the left, the words at the right, and the whole card
   lifts on hover. No rim anywhere, and the hover is a lift, never a
   ring. */
.pcard{background:var(--su);border-radius:var(--r-lg);box-shadow:var(--shadow);overflow:hidden;
  display:grid;grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr);
  transition:transform .16s ease, box-shadow .16s ease}
.pcard:hover{transform:translate(-3px,-4px);box-shadow:var(--shadow-hi)}
.pcard:has(a:focus-visible){transform:translate(-3px,-4px);box-shadow:var(--shadow-hi)}
.pcard .art{background:color-mix(in srgb, var(--sun) 26%, var(--bg));display:grid;place-items:center;
  padding:clamp(16px,2.4vw,30px);min-height:210px}
.pcard .art img{width:100%;height:100%;max-height:260px;object-fit:contain}
/* `margin-top:auto` on the foot used to fight `justify-content:center` here:
   the words centred, the read line pinned to the bottom, and on a card as tall
   as its own artwork that left about 100px of hole between them. The stack
   centres as ONE block now and the foot sits where the copy leaves it. */
.pcard .body{padding:clamp(20px,2.8vw,36px);display:flex;flex-direction:column;gap:var(--s3);
  justify-content:center}
.pcard h2{font-family:var(--disp);font-weight:700;font-size:var(--t-h3);line-height:1.18;
  letter-spacing:-.01em;text-wrap:balance}
.pcard h2 a{text-decoration:none}
/* the whole card is the target, and the heading keeps the accessible
   name. `::after` is a hit area, not a border. */
.pcard h2 a::after{content:"";position:absolute;inset:0;z-index:3}
.pcard{position:relative}
.pcard p{color:var(--mut);font-size:var(--t-body);line-height:1.55}
.pcard .foot{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;margin-top:var(--s1)}
.pcard .read{display:inline-flex;align-items:center;gap:8px;font-weight:800;color:var(--grape-d);
  font-size:var(--t-sm);text-decoration:underline;text-underline-offset:3px}
.pcard .read svg{width:15px;height:15px;transition:transform .16s ease}
.pcard:hover .read svg{transform:translateX(4px)}
@media (max-width:860px){
  .pcard{grid-template-columns:1fr}
  .pcard .art{min-height:170px}
  .pcard .art img{max-height:190px}
}

/* ---------------------------------------------------------------
   ARTICLE
   --------------------------------------------------------------- */
/* ---------------------------------------------------------------
   The article masthead. Round 33 turned it into two columns.

   Stacked, it cost the reader about 900px before the first sentence:
   the title measured 20ch inside a 1180px wrap, so the right HALF of
   the band was empty paper, and the lead art then took a full screen
   of its own underneath. On a 12,000px article that is a screen and a
   half of entrance. Side by side the same two things occupy one
   screen, the empty column is gone, and the art is doing the job art
   does in a masthead rather than acting as a door the reader opens.

   It folds back to the stacked form under 1024, where a column of art
   beside a column of words would leave both too narrow to read.
   --------------------------------------------------------------- */
header.post{padding:clamp(22px,3.4vw,38px) 0 0}
header.post .words{min-width:0}
header.post h1{font-family:var(--disp);font-weight:700;font-size:var(--t-h1);line-height:1.04;
  letter-spacing:-.018em;text-wrap:balance;max-width:20ch}
header.post .lede{margin-top:var(--s4);font-size:var(--t-lede);color:var(--mut);max-width:48ch;line-height:1.55}
header.post .chiprow{margin-top:var(--s4)}
header.post .byline{margin-top:var(--s4);font-size:var(--t-sm);color:var(--mut)}
header.post .byline b{color:var(--ink);font-weight:800}

figure.lead{margin-top:var(--s5);background:color-mix(in srgb, var(--sun) 26%, var(--bg));
  border-radius:var(--r-lg);padding:clamp(14px,2.2vw,26px);box-shadow:var(--shadow-sm)}
figure.lead img{width:100%;height:auto;max-height:clamp(220px,32vw,420px);object-fit:contain;margin:0 auto}

@media (min-width:1024px){
  header.post .wrap{display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
    gap:clamp(30px,3.6vw,60px);align-items:center}
  header.post h1{max-width:14ch}
  header.post .lede{max-width:40ch}
  figure.lead{margin-top:0;min-width:0}
  /* The art well is now a column, not a band, so it is sized by the
     column it sits in rather than by the viewport: a `vw` cap here
     would read the WINDOW and blow the row open at 1440. */
  figure.lead img{max-height:min(360px,42vh)}
}

/* The two-column reading layout. The rail is sticky and the article
   is the wide column; under 1080 the rail folds into a plain
   contents card above the text, because a sticky rail on a phone is
   a strip that eats a third of the screen forever. */
.layout{display:grid;gap:clamp(24px,3.4vw,52px);padding:var(--s6) 0 var(--s7);align-items:start}
@media (min-width:1080px){
  .layout{grid-template-columns:minmax(0,1fr) 262px}
  /* The mast is sticky at top:0 and is 90px tall (12 + 9 + 48 + 9 + 12), so a
     rail parked at 26px spends the whole article with its heading underneath
     it. `scroll-padding-top` on html is the same clearance, which is why a
     jump from this rail lands correctly; the rail itself has to be told. */
  .rail{position:sticky;top:calc(90px + 16px);order:2}
  .article{order:1}
}
.article{max-width:66ch;min-width:0}

/* ---------------------------------------------------------------
   The contents rail. This is the one thing the reference surface for
   this pass does not have: a live index of a long article that says
   where the reader currently is. It marks the active section by
   swapping the link's FILL, never by drawing a rim on it.
   --------------------------------------------------------------- */
.rail .box{background:var(--su);border-radius:var(--r);padding:var(--s4);box-shadow:var(--shadow-sm)}
.rail h2{font-family:var(--disp);font-weight:700;font-size:15px;letter-spacing:.005em;margin-bottom:var(--s3)}
.rail ol{list-style:none;display:flex;flex-direction:column;gap:2px;counter-reset:toc}
.rail a{display:block;font-size:14px;line-height:1.38;color:var(--mut);font-weight:600;
  padding:9px 12px;border-radius:var(--r-sm);text-decoration:none;
  transition:background .16s ease, color .16s ease}
.rail a:hover{background:var(--well);color:var(--ink)}
.rail a[data-here]{background:color-mix(in srgb, var(--grape) 12%, var(--su));color:var(--ink);font-weight:800}
.rail .go{margin-top:var(--s3);display:flex;align-items:center;justify-content:center;gap:8px;
  background:var(--cta);color:var(--ink);font-weight:800;font-size:15px;min-height:48px;
  border-radius:var(--r-pill);box-shadow:var(--shadow-sm);text-decoration:none;
  transition:transform .12s ease, box-shadow .12s ease}
.rail .go:hover{transform:translate(-2px,-2px);box-shadow:6px 8px 0 var(--ink)}
.rail .go:active{transform:translate(2px,2px);box-shadow:2px 2px 0 var(--ink)}

/* the folded form, under the rail's breakpoint */
@media (max-width:1079px){
  .rail{order:1}
  .rail .box{padding:var(--s4)}
  .rail ol{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:2px}
}

/* ---------------------------------------------------------------
   Article body. One measure, one rhythm ladder, and the gap between
   a heading and the paragraph under it is deliberately a rung
   smaller than the gap above the heading, so a section reads as one
   block rather than as a line of text that happens to be bold.
   --------------------------------------------------------------- */
/* A chapter opens on air and closes on air. `--s7` above an h2 against
   `--s3` below it is the whole reason a section reads as one block: the
   biggest gap on the page is the one BETWEEN chapters, so a reader
   scrolling fast can see where one ends without reading a word of it.
   Round 33 moved the rung above from `--s6` to `--s7` and let the first
   paragraph of every chapter carry the lift that only `p.first` used to
   carry by hand. That lifted line is the chapter marker: it is made of
   type, so it costs no chrome, no rule and no number. */
.article h2{font-family:var(--disp);font-weight:700;font-size:var(--t-h2);line-height:1.12;
  letter-spacing:-.014em;margin:var(--s7) 0 var(--s3);text-wrap:balance;scroll-margin-top:96px}
.article h2:first-child{margin-top:0}
.article h3{font-family:var(--disp);font-weight:700;font-size:var(--t-h3);line-height:1.2;
  margin:var(--s5) 0 var(--s2);text-wrap:balance;scroll-margin-top:96px}
.article p{color:var(--read);margin-bottom:var(--s4);line-height:1.68}
/* `>` matters: without it this also caught the paragraph under the h2 inside
   the short-answer block, which is a 16px label over its own body copy, and
   set that body one rank ABOVE its own heading. */
.article > h2 + p,.article p.first{color:var(--ink);font-size:var(--t-lede);line-height:1.52}
.article b,.article strong{color:var(--ink);font-weight:800}
.article ul,.article ol{color:var(--read);padding-inline-start:1.4em;margin:0 0 var(--s4);line-height:1.62}
.article li{margin-bottom:var(--s2)}
.article li:last-child{margin-bottom:0}
.article li::marker{color:var(--grape-d);font-weight:700}
.article a{color:var(--grape-d);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.article a:hover{color:var(--ink)}
.article hr{height:0;margin:var(--s5) 0;background:none}

/* ---------------------------------------------------------------
   The short answer. It sits directly under the first heading of
   every article and answers the question in the title in two or
   three sentences. It is there for the reader who arrived from a
   search result with one question, and it is the block an answer
   engine can lift whole without inventing the middle of it.
   --------------------------------------------------------------- */
/* ROUND 33, on "less overwhelming". Every block in this article used to
   cast the same 4px/5px flat offset shadow: the short answer, every
   callout, every figure, the table, the step ladder, the FAQ and both
   KidBD bands. Down a 12,000px page that is twenty-odd objects all
   claiming to sit on top of the paper, and the page read as a stack of
   boxes rather than as something to read. The rule now: a block the
   reader ACTS on (the FAQ, a step in a ladder, a card, a band with a
   button in it) keeps the shadow and stays an object; a block the reader
   only READS separates by fill and spacing alone, which is what Law 1
   asks of this house anyway. Nothing was deleted, only un-lifted. */
.answer{background:color-mix(in srgb, var(--mint) 16%, var(--su));border-radius:var(--r);
  padding:clamp(18px,2.6vw,28px);margin:0 0 var(--s5)}
.answer h2{font-family:var(--disp);font-size:16px;margin:0 0 var(--s2);letter-spacing:.005em}
.answer p{color:var(--ink);margin:0}
.answer p + p{margin-top:var(--s2)}

/* a paragraph that has to be impossible to miss */
.callout{background:var(--well);border-radius:var(--r);padding:clamp(16px,2.4vw,24px);
  margin:var(--s5) 0}
.callout p{color:var(--ink);margin:0}
.callout p + p{margin-top:var(--s2)}
.callout.warn{background:color-mix(in srgb, var(--red) 13%, var(--su))}
.callout .k{display:block;font-family:var(--disp);font-weight:700;font-size:15px;margin-bottom:6px}

/* a pull line. One per article at most, and it is a size and a
   colour, never a quote mark glyph and never a rule beside it. */
.pull{font-family:var(--disp);font-weight:700;font-size:clamp(20px,17.71px + .714vw,28px);
  line-height:1.3;color:var(--ink);margin:var(--s5) 0;letter-spacing:-.012em;text-wrap:balance}

/* the numbered ladder used for a week-by-week or step-by-step run */
.steps{list-style:none;counter-reset:step;padding:0;display:grid;gap:var(--s3);margin:var(--s4) 0}
.steps li{counter-increment:step;background:var(--su);border-radius:var(--r);
  padding:clamp(16px,2.2vw,24px) clamp(16px,2.2vw,24px) clamp(16px,2.2vw,24px) clamp(62px,7vw,78px);
  box-shadow:var(--shadow-sm);position:relative;color:var(--mut);margin:0}
.steps li::before{content:counter(step);position:absolute;left:clamp(16px,2.2vw,22px);top:clamp(16px,2.2vw,22px);
  width:34px;height:34px;border-radius:var(--r-pill);background:var(--sun);color:var(--ink);
  font-family:var(--disp);font-weight:700;font-size:17px;display:grid;place-items:center}
.steps li b{display:block;color:var(--ink);font-family:var(--disp);font-weight:700;
  font-size:var(--t-h3);line-height:1.2;margin-bottom:var(--s1)}

/* the plain fact table. Same block-at-520 fold legal.css uses. */
.tbl{width:100%;border-collapse:collapse;margin:var(--s5) 0;font-size:var(--t-sm);
  background:var(--su);border-radius:var(--r);overflow:hidden}
/* The caption is INSIDE the table's border box, and the table is
   `overflow:hidden` with a 26px radius, so a caption at x=0 has its first
   glyph shaved off by the corner arc. It shipped reading "Vhat to change for
   each age band" at every width, and no layout probe could see it: scrollWidth,
   clientWidth and the bounding box all say 743 of 743, clean. Only the render
   shows it. The horizontal padding is the CELLS' padding, so the caption now
   also starts on the same vertical as the first column. */
.tbl caption{caption-side:top;text-align:start;font-family:var(--disp);font-weight:700;
  font-size:15px;padding:0 clamp(12px,1.6vw,18px) var(--s2);color:var(--ink)}
.tbl th{text-align:start;font-family:var(--disp);font-weight:700;color:var(--ink);
  background:color-mix(in srgb, var(--sun) 26%, var(--su));padding:12px clamp(12px,1.6vw,18px);line-height:1.35}
.tbl td{padding:12px clamp(12px,1.6vw,18px);vertical-align:top;color:var(--mut);line-height:1.45}
.tbl tbody tr:nth-child(even) td{background:color-mix(in srgb, var(--ln) 26%, var(--su))}
.tbl td:first-child{color:var(--ink);font-weight:700}
@media (max-width:560px){
  /* The CAPTION is in this list for a reason that is invisible until you look
     at a phone. `display:block` on the table leaves `caption` at its initial
     `table-caption`, which makes the browser build an anonymous table box
     around it; that box shrink-wraps to MIN-CONTENT, so a five word caption
     renders one word per line and the first letter is clipped by the table's
     own `overflow:hidden`. Every stacked element has to be told, including
     the one that is not a row. */
  .tbl,.tbl caption,.tbl tbody,.tbl tr,.tbl td{display:block;width:100%}
  .tbl thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .tbl tr{padding:var(--s3) clamp(12px,1.6vw,18px)}
  .tbl tbody tr:nth-child(even){background:color-mix(in srgb, var(--ln) 26%, var(--su))}
  .tbl tbody tr:nth-child(even) td{background:none}
  .tbl td{padding:3px 0}
  .tbl td::before{content:attr(data-h);display:block;font-family:var(--disp);font-weight:700;
    font-size:12.5px;color:var(--mut);margin-top:6px}
  .tbl td:first-child::before{margin-top:0}
}

/* an in-article illustration. The figure sits in its own tinted well
   so a drawing on transparency always has a known ground under it. */
figure.fig{margin:var(--s6) 0;background:color-mix(in srgb, var(--sun) 24%, var(--bg));
  border-radius:var(--r);padding:clamp(14px,2.2vw,26px)}
figure.fig img{width:100%;height:auto}
figure.fig figcaption{margin-top:var(--s3);font-size:var(--t-sm);color:var(--mut);line-height:1.45}

/* ---------------------------------------------------------------
   The KidBD band inside an article. Two sizes of one object: `.mid`
   is the compact one that sits at the halfway mark, `.end` is the
   closer. Both are the product's own cream paper and its own action
   orange, and NEITHER TYPES A PRICE: pay.js is the only home of
   every number in this product, and a figure typed into an article
   is a claim that cannot be kept in step with the checkout.
   --------------------------------------------------------------- */
.kbd{background:var(--su);border-radius:var(--r-lg);box-shadow:var(--shadow);
  padding:clamp(22px,3vw,38px);margin:var(--s6) 0;display:grid;gap:var(--s4);
  grid-template-columns:76px minmax(0,1fr);align-items:center}
.kbd .mark{width:76px;height:76px;flex:none}
.kbd .words{display:flex;flex-direction:column;gap:var(--s2)}
.kbd h2{font-family:var(--disp);font-weight:700;font-size:var(--t-h3);line-height:1.16;
  margin:0;letter-spacing:-.01em;text-wrap:balance}
.kbd p{color:var(--mut);margin:0;font-size:var(--t-body);line-height:1.52}
.kbd .act{display:inline-flex;align-items:center;justify-content:center;gap:9px;background:var(--cta);
  color:var(--ink);font-weight:800;font-size:var(--t-body);min-height:52px;padding:0 26px;
  border-radius:var(--r-pill);box-shadow:var(--shadow-sm);text-decoration:none;justify-self:start;
  transition:transform .12s ease, box-shadow .12s ease}
.kbd .act:hover{transform:translate(-2px,-2px);box-shadow:6px 8px 0 var(--ink)}
.kbd .act:active{transform:translate(3px,4px);box-shadow:2px 2px 0 var(--ink)}
.kbd .act svg{width:17px;height:17px}
.kbd .note{font-size:13.5px;color:var(--mut)}

/* the closer. Saturated ground, ink on it, never white, and the whole
   band is centred inside itself the way Law 8 asks of a banner. */
.kbd.end{grid-template-columns:1fr;text-align:center;justify-items:center;gap:var(--s3);
  background:color-mix(in srgb, var(--sun) 62%, var(--su));padding:clamp(28px,4vw,52px)}
.kbd.end .mark{width:96px;height:96px}
.kbd.end h2{font-size:var(--t-h2)}
.kbd.end p{max-width:46ch;color:color-mix(in srgb, var(--ink) 34%, var(--mut))}
.kbd.end .act{justify-self:center;margin-top:var(--s2)}
.kbd.end .words{align-items:center}
@media (max-width:620px){
  .kbd{grid-template-columns:1fr;justify-items:start;text-align:start}
  .kbd .mark{width:62px;height:62px}
}

/* ---------------------------------------------------------------
   FAQ. Native details/summary so it works with no script at all,
   and the marker is a drawn plus that rotates, never a glyph.
   --------------------------------------------------------------- */
.faq{display:grid;gap:var(--s2);margin:var(--s4) 0}
.faq details{background:var(--su);border-radius:var(--r);box-shadow:var(--shadow-sm);overflow:hidden}
.faq summary{list-style:none;cursor:pointer;display:flex;gap:var(--s3);align-items:center;
  padding:clamp(16px,2.2vw,22px);font-family:var(--disp);font-weight:700;font-size:var(--t-h3);
  line-height:1.3;color:var(--ink);min-height:60px}
.faq summary::-webkit-details-marker{display:none}
/* the halo goes INSET here: the card clips, and an outset halo on a
   summary inside overflow:hidden is trimmed on three sides. */
.faq summary:focus-visible{outline:none;box-shadow:inset 0 0 0 4px var(--grape)}
.faq .pm{margin-inline-start:auto;width:26px;height:26px;flex:none;position:relative}
.faq .pm::before,.faq .pm::after{content:"";position:absolute;inset:50% 2px auto 2px;height:3.4px;
  border-radius:var(--r-pill);background:var(--ink);transform:translateY(-50%);transition:transform .2s ease}
.faq .pm::after{transform:translateY(-50%) rotate(90deg)}
.faq details[open] .pm::after{transform:translateY(-50%) rotate(0deg)}
.faq .a{padding:0 clamp(16px,2.2vw,22px) clamp(18px,2.4vw,24px)}
.faq .a p{color:var(--mut);margin:0}
.faq .a p + p{margin-top:var(--s2)}
.faq .a ul{margin:var(--s2) 0 0}

/* ---------------------------------------------------------------
   Read next. Round 33 gave these cards their art.

   They were three text-only tiles at the bottom of a 12,000px article:
   the one place on the page where a reader who has finished has to be
   offered something, and it was the flattest set of objects on the
   page. Every article already owns a drawing, and the hub already
   shows those drawings on its own cards, so the art was there the
   whole time and only this row was not using it. The third card, the
   hub, carries the product mark instead, which is the honest picture
   of "all of them" and keeps the set of three even.

   Equal heights are structural, not a magic number: the grid stretches
   every item, the well is a fixed height so the three drawings sit on
   one horizon, and `.t` takes the remaining space so the read line at
   the bottom of each card lands on the same baseline whatever the
   title wraps to.
   --------------------------------------------------------------- */
.related{padding:0 0 var(--s7)}
.related h2{font-family:var(--disp);font-weight:700;font-size:var(--t-h2);line-height:1.12;
  letter-spacing:-.014em;margin-bottom:var(--s4);text-wrap:balance}
.rgrid{display:grid;gap:var(--s4);grid-template-columns:repeat(auto-fit,minmax(min(100%,264px),1fr))}
.rgrid a{background:var(--su);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;text-decoration:none;
  transition:transform .16s ease, box-shadow .16s ease}
.rgrid a:hover{transform:translate(-3px,-4px);box-shadow:var(--shadow)}
/* THE DRAWING IS SIZED IN ABSOLUTE UNITS, NOT IN PERCENT, and that is the
   whole of this rule. It shipped as `height:100%` of the well and painted
   203px of illustration into a 136px box, over the card title underneath.
   `place-items:center` leaves the implicit grid row AUTO-sized, an auto row
   is an indefinite height, and a percentage height against an indefinite box
   is discarded: the image fell back to `width:auto`, `max-width:100%` capped
   it at the column, and the height came back off the ratio.

   `grid-template-rows:1fr` makes that row definite and fixes it in Chrome.
   It did NOT fix it in the headless Chromium the slice renderer drives, which
   still dropped the percentage, so the same page was correct in one engine
   and clipped in the other and only the render showed it. So the height is
   now the well's own height minus its own padding, in px, with nothing left
   to resolve against anything.

   Two probes lied about this before the render caught it. Measured a frame
   early, the box read 136px with 16px of clearance, because the file had not
   decoded yet. Measured in the live browser it read 136px and correct, while
   the renderer was clipping it. `overflow:hidden` is the belt behind the
   braces: whatever a future drawing's intrinsics are, it cannot paint outside
   its own well again. */
.rgrid{--artH:clamp(132px,15vw,168px)}
.rgrid .art{background:color-mix(in srgb, var(--sun) 26%, var(--bg));display:grid;place-items:center;
  overflow:hidden;padding:var(--s3);height:var(--artH)}
.rgrid .art img{width:auto;max-width:100%;height:calc(var(--artH) - var(--s3) * 2);
  object-fit:contain}
/* the hub card's mark is a 512px square icon, not a scene: it is sized
   rather than fitted, or it fills the well edge to edge and stops
   looking like the same kind of object as the two beside it. */
.rgrid .art img.mk{height:auto;width:clamp(62px,7vw,80px)}
.rgrid .t{padding:clamp(18px,2.2vw,24px);display:flex;flex-direction:column;gap:var(--s2);flex:1}
.rgrid b{font-family:var(--disp);font-weight:700;font-size:17px;line-height:1.22;color:var(--ink);
  text-wrap:balance}
.rgrid span{font-size:var(--t-sm);color:var(--mut);line-height:1.45}
.rgrid .read{margin-top:auto;padding-top:var(--s2);display:inline-flex;align-items:center;gap:8px;
  font-weight:800;color:var(--grape-d);font-size:var(--t-sm)}
.rgrid .read svg{width:15px;height:15px;flex:none;transition:transform .16s ease}
.rgrid a:hover .read svg{transform:translateX(4px)}

/* ---------------------------------------------------------------
   Footer, the ink slab the legal pages already use, so the bottom of
   an article and the bottom of the refund policy are the same place.
   --------------------------------------------------------------- */
footer.site{background:var(--ink);color:var(--fmut);padding:clamp(34px,5vw,60px) 0;
  position:relative;z-index:2;font-size:14.5px;line-height:1.66}
footer.site a{color:var(--fhi);text-decoration:underline;text-underline-offset:3px;font-weight:700}
footer.site .flinks{display:flex;flex-wrap:wrap;gap:4px 16px;margin-bottom:var(--s3)}
footer.site .flinks a{min-height:44px;display:inline-flex;align-items:center;padding:0 4px}
footer.site b{color:var(--fhi)}
footer.site .fbrand{font-family:var(--disp);font-weight:700;font-size:20px;color:var(--fhi);
  display:flex;align-items:center;gap:10px;margin-bottom:var(--s3)}
/* SVG *AND* IMG. The rule shipped naming the element the footer does not
   use: every page here puts the mark in an <img src="kidbd-mark.svg">, not
   an inline <svg>, so the mark kept its intrinsic 512px, `max-width:100%`
   capped it at the full column and the footer row measured 414px inside a
   351px box. At 390 that is 43px of document wider than the viewport, on
   every blog page at once, and `overflow-x:clip` on body meant the eye
   never saw it and a probe that only asks "does the page scroll" read
   zero. Size the element that is actually there. */
footer.site .fbrand svg,footer.site .fbrand img{width:30px;height:30px;flex:none}
footer.site .fine{margin-top:var(--s3);font-size:13px;opacity:.86;max-width:76ch}
footer.site :focus-visible{box-shadow:0 0 0 3px var(--ink),0 0 0 7px var(--sun)}

/* ---------------------------------------------------------------
   Reveal. Armed by blog.js and ONLY by blog.js: with no script no
   element is ever given `data-rv`, so the page renders whole. The
   script arms only what is below the fold at load, so nothing that
   is already on screen flashes out and back in.
   --------------------------------------------------------------- */
[data-rv="armed"]{opacity:0;transform:translateY(16px)}
[data-rv="shown"]{opacity:1;transform:none;transition:opacity .5s ease, transform .5s cubic-bezier(.22,.7,.3,1)}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms !important;transition-duration:.001ms !important}
  [data-rv="armed"]{opacity:1;transform:none}
}

@media print{
  .prog,.topbar,.rail,.kbd,.related,footer.site{display:none}
  body::before{display:none}
  .article{max-width:none}
}
