/* ============================================================
   BOZA v7 — Base styles shared by every block prototype.
   Components here mirror the locked Direction D reference.
   ============================================================ */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
/* Every in-page jump has to stop below the sticky header, not behind it. The
   hero's scroll cue was the visible case -- it landed on the next section and
   the header covered the top third of it -- but the same is true of every
   `href="#id"` on the site, of `:target`, of find-in-page and of the browser
   restoring your position on a reload. One declaration on the scroll container
   covers all of them, which is why it belongs here rather than on the cue.

   `scroll-padding-top`, not `scroll-margin-top` on each target: the offset is a
   property of the viewport (something is parked at the top of it), not of the
   forty things you might scroll to, and a section added tomorrow gets it for
   free. --hdr-h is measured and published by chrome.js because the header
   changes height when it compacts; the 84px is only the value used in the
   moment before the script has run. --rv-bar is the review layer's own bar,
   which the header sits under. The last term is breathing room, so the section's
   top hairline is not welded to the underside of the header. */
/* and --hdr-top, where the header itself starts: 0 in the library, and in
   WordPress the admin bar's height when a person is signed in, which the
   header already steps down by and a jump did not (14.88) */
html:has(.hdr){scroll-padding-top:calc(var(--hdr-top,0px) + var(--hdr-h-min,var(--hdr-h,84px)) + var(--rv-bar,0px) + var(--scroll-gap,32px))}
/* the body's line is 1.6, the decision of 12 September (D37): the theme took it
   from theme.json's styles and the library drew 1.68, so the specimens read looser
   than the site they describe. One number now, in both. */
body{font-family:var(--sans);font-size:var(--fs-body);line-height:1.6;color:var(--ink);background:var(--paper);-webkit-font-smoothing:antialiased;text-wrap:var(--tw-body,pretty)}
/* the two text-wrap values are the site's to turn off: --tw-body and --tw-head set to auto */
h1,h2,h3,h4{text-wrap:var(--tw-head,balance)}
/* one gutter, and the container reads it, so anything that wants to bleed out
   to the screen edge can pull back exactly as far as the container pushed in.
   It was a literal in two places before, and 20px at a width where the real
   gutter was 32, which left a 12px strip of paper at both edges. */
:root{--gutter:32px}
@media(max-width:640px){:root{--gutter:20px}}
.container{max-width:var(--container);margin-inline:auto;padding-inline:var(--gutter)}
/* a section set to the narrow measure (14.28): the same number the reading
   measure has used since round 7, so a page of running words is one width
   whether the section says so or the block inside it does. */
.container.c-narrow{max-width:var(--container-narrow)}
/* 32px of gutter either side of a 390px screen leaves 326px of content, which is
   why several pages felt cramped. The system pages had already gone to 20 on
   their own; the whole library follows rather than one page being the outlier. */
h1,h2,h3,h4{font-family:var(--serif);font-weight:var(--fw-head);letter-spacing:-.008em;line-height:1.08}
/* a bare h1 is sized too, or a heading a person sets to H1 falls back to the
   browser's 2em and comes out smaller than the h2 under it (13.0) */
h1{font-size:var(--fs-h1)}
h2{font-size:var(--fs-h2)}
h3{font-size:var(--fs-h3)}
h4{font-size:var(--fs-h4)}
/* level and size are separate concerns: a heading can sit at the right depth in
   the outline and still be typeset small. In the theme this is the block's own
   heading-level control; here it is a class. */
.as-h4{font-size:var(--fs-h4)}
.as-h3{font-size:var(--fs-h3)}
/* and at the second level's size, its level kept: the 404's h1, which sits under
   the big number and cannot be as loud as it (14.7) */
.as-h2{font-size:var(--fs-h2)}
/* a heading at the small title's size, its level kept: the accordion's question,
   a post line's title and the footer's newsletter heading share it (13.2d) */
.as-title{font-size:calc(1.35rem * var(--scale-head));line-height:1.2}
p{color:var(--ink-soft)}
.lead{font-size:var(--fs-lead)}
/* The big number: a figure set as the display itself, the 404's four, nought
   and four, and whatever is set in italic inside it in the amber. It was the
   system family's `.num` inside the 404's own wrapper; a paragraph can take it
   now, so it is a type role like the lead (14.7), and it is called what an
   editor would call it, because the section head already has a Numeral and it
   is a different thing. On a dark ground it goes light, its italic keeping the
   amber the ground's own token gives it. */
.big-num{font-family:var(--serif);font-size:clamp(6.5rem,16vw,12rem);line-height:.85;color:var(--green);letter-spacing:-.03em;font-weight:var(--fw-head)}
.big-num em{font-style:italic;color:var(--amber-ink)}
/* doubled, because `.sec.on-ink p` names the ground twice and a paragraph, and the
   big number is a paragraph in the theme; and a dark type on a dark ground puts it
   back to the ink */
:is(.sec-green,.sec-ink,.on-ink) .big-num.big-num{color:var(--on-ink-fg)}
.sec.txt-dark .big-num.big-num{color:var(--ink)}
::selection{background:var(--green);color:#fff}
/* The focus ring is an accessibility floor, not a style. It was declared here
   as `a:focus-visible,button:focus-visible`, which any family rule of equal
   specificity beat on source order; `all:unset` on a button then wiped it
   outright. Nine of nine controls tested came back with outline-width 0. It is
   now unconditional, and it applies to anything focusable, including a div
   carrying tabindex. */
:focus-visible{outline:2.5px solid var(--amber) !important;outline-offset:3px}

/* a heading the page needs and the design does not. A listing page whose only
   heading is its h1 and whose rows are h3 has a hole in its outline; this fills
   it without putting a word on the screen. Not display:none, which would take it
   out of the accessibility tree along with everything else. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

/* There used to be `a:focus-visible,button:focus-visible{border-radius:4px}`
   here, to keep the ring off a hard corner on a bare text link. It did that,
   and it also flattened every shaped control it touched: a pill button (999px),
   a circular icon button (50%) and a card all snapped to the same 4px the
   moment they took focus, so tabbing through the page visibly restyled it.
   The outline already follows each element's own border-radius, so the fix is
   to stop setting one -- and to round only the case that motivated the rule,
   a link inside prose, which has no shape of its own. */
.rich a:focus-visible,p a:focus-visible,li a:focus-visible{border-radius:calc(var(--r-xs) / 2)}

/* One reduced-motion floor for the whole library, so no family has to remember.
   Durations collapse to a hair rather than to none on purpose: several blocks
   remove a node on transitionend, and `transition:none` never fires it, which
   would leave a dismissed alert on the page forever. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important}
}
/* A kicker is a one-line label sitting immediately above the thing it labels,
   and it was inheriting the body's leading, 1.6 -- roughly ten pixels of empty box,
   most of it underneath, which read as a gap the layout had not asked for.
   Every kicker in the system pairs with something directly below it, so the
   leading belongs to the label, not to the prose setting. */
.kicker{font-family:var(--serif);font-style:italic;font-size:1.05rem;line-height:1.25;color:var(--green)}
/* the column heading: the italic title over a list in a column, at the body's
   line height and 16px over what follows. Moved here from the lists family so
   it draws without a list on the page; the footer's ledger titles (13.2d) */
.col-h{font-family:var(--serif);font-style:italic;font-size:1.05rem;color:var(--green);margin-bottom:16px}
/* the kicker is heritage green, which on a green or ink ground is very nearly
   the ground. On a dark section it takes the on-dark amber, the same swap the
   more-link already makes. */
.sec-green .kicker,.sec-ink .kicker,.on-ink .kicker,.sec-green .col-h,.sec-ink .col-h,.on-ink .col-h{color:var(--amber-soft)}
.sec-num{font-family:var(--serif);font-style:italic;font-size:1.15rem;color:var(--amber-ink)}

/* ---------- buttons ---------- */
/* `background:transparent` on the base, because a <button> carrying .btn had the
   browser's own grey behind its words: the Posts block's load-more button was a
   grey box with an outline on every page that had one, and on a dark ground white
   words on that grey (14.13). An <a> never had a ground to lose. */
.btn{background:transparent;display:inline-flex;align-items:center;justify-content:center;gap:10px;border-radius:var(--r-btn);min-height:var(--h-ctl);padding:0 26px;font:inherit;font-weight:500;font-size:1rem;line-height:1.2;text-decoration:none;cursor:pointer;border:var(--line-w) solid transparent;transition:background var(--t-fast) var(--ease-out),color var(--t-fast),box-shadow var(--t-fast),border-color var(--t-fast)}
/* min-height, not height: a single line button is exactly the token, so a
   circle beside it matches by construction, and a label that has to wrap grows
   the button instead of clipping itself. */
/* the button used to fade its background on one clock and nudge its icon on
   another, so a single hover resolved twice */
.btn svg{transition:transform var(--t-fast) var(--ease-out),width var(--t-fast) var(--ease-out),opacity var(--t-fast),margin var(--t-fast) var(--ease-out)}
.btn:hover svg{transform:translateX(4px)}
.btn.icon-left:hover svg{transform:translateX(-4px)}
/* optical balance: pull the icon toward its button edge.
   Icons trail the text by default; .icon-left flips the side.
   (Position classes, not :first/:last-child — bare text nodes
   don't count as children, so those selectors both match.) */
.btn>svg{margin-right:-.25em}
.btn.icon-left>svg{margin-right:0;margin-left:-.25em}
.btn-green{background:var(--green);color:#fff}
/* Every solid button answers a hover by deepening away from its ground. Cream
   had no colour left to deepen into -- it sits on the dark grounds, where
   "deeper" means brighter -- so it had been given a drop shadow alone, which on
   a near-black ground is close to invisible. It goes to true white now and
   carries a cream halo, which reads instantly on green, ink and the
   photographic hero alike.
   House style: a button does not move on hover. Colour, shadow and the ring
   only, which is why the halo is doing the work a lift would otherwise do. */
.btn-green:hover{background:var(--green-deep);box-shadow:var(--shadow)}
.btn-line{border-color:var(--line-dark);color:var(--ink)}
.btn-line:hover{background:var(--ink);color:var(--paper)}
/* Like the slider's arrows (14.111): the round controls' look as a button,
   the default for a button in a slider's row of controls, asked for in review
   where the outline was near and not the same. Paper under a hairline here;
   the hover and the dark grounds are the arrows' own rules below, which name
   it beside .icon-chip, so the two cannot drift apart. */
.btn-chip{background:var(--paper);border-color:var(--line-dark);color:var(--ink)}
.btn-soft{background:var(--tint);color:var(--green-deep)}
.btn-soft:hover{background:#E2EBE3;box-shadow:var(--shadow)}
.btn-cream{background:var(--paper);color:var(--green-deep)}
.btn-cream:hover{background:#fff;
  box-shadow:0 0 0 var(--line-w) rgba(251,250,246,.55),var(--shadow-dark)}
.btn-ghost-dark{border-color:var(--on-ink-border);color:var(--on-ink-fg)}
.btn-ghost-dark:hover{border-color:#fff;background:var(--on-ink-fill-hi)}
/* the outline button on a dark ground takes the dark ground's outline, the ghost's,
   where it drew ink on ink (13.1's fixture, 13.5) */
:is(.sec-green,.sec-ink,.on-ink) .btn-line{border-color:var(--on-ink-border);color:var(--on-ink-fg)}
:is(.sec-green,.sec-ink,.on-ink) .btn-line:hover{border-color:#fff;background:var(--on-ink-fill-hi);color:var(--on-ink-fg)}
.btn.size-s{min-height:var(--h-ctl-s);height:auto;padding:0 19px;font-size:.92rem}
.btn.size-l{min-height:var(--h-ctl-l);height:auto;padding:0 34px;font-size:1.14rem;min-width:200px}
/* a button is never squeezed below its label. Two of them side by side on a
   360px screen used to shrink until the words broke over two and three lines,
   which reads as broken rather than tight. */
/* never shrink, but never break out of the box either: in a row a button now
   wraps onto its own line instead of being squeezed, and in a container too
   narrow for its label it caps at the container and wraps the words rather than
   sticking out of the card */
.btn{flex:0 0 auto;max-width:100%}
/* the row they sit in wraps instead, with the same vertical rhythm the buttons
   page uses */
.btn-row{display:flex;flex-wrap:wrap;gap:14px;align-items:center}
.btn-row.center{justify-content:center}
/* ---- the row's own choices (14.5) -------------------------------------------
   What the Buttons family's options table listed and a row never had: a gap
   from a short list, the regular 14px between them; a minimum width for every
   button in it, so two buttons whose words differ by a letter or two stand the
   same size, the label and its icon centred as they already are; one button's
   own minimum, for the one that matters most; and stacking, where a row that
   wraps two buttons onto two lines of different widths becomes a column of
   buttons one width, the row's, at the tier it is told; always stacked, the
   column is as wide as its widest button and every button that wide. The reviewer kept
   the minimum width on 13 September. */
.btn-row.btns-gap-s{gap:8px}
.btn-row.btns-gap-l{gap:22px}
.btn-row.btns-min-s>.btn{min-width:160px}
.btn-row.btns-min-m>.btn{min-width:200px}
.btn-row.btns-min-l>.btn{min-width:240px}
.btn.min-s{min-width:160px}
.btn.min-m{min-width:200px}
.btn.min-l{min-width:240px}
.btn-row.btns-stack-all{flex-direction:column;align-items:stretch;width:fit-content;max-width:100%}
.btn-row.btns-stack-all.center{margin-inline:auto}
@media (max-width:1000px){
  .btn-row.btns-stack-tab{flex-direction:column;align-items:stretch}
  .btn-row.btns-stack-tab>.btn{width:100%}
}
@media (max-width:640px){
  .btn-row.btns-stack-ph{flex-direction:column;align-items:stretch}
  .btn-row.btns-stack-ph>.btn{width:100%}
}
/* the text link as a button (14.5): the read-more, with its underline and
   arrow, where a button would be too loud beside another one */
.btn-row>.link-more{align-self:center}
.btn[disabled],.btn.is-disabled{opacity:.45;pointer-events:none}

/* loading state — shared by buttons and, later, forms.
   The spinner grows in and the icon folds away; nothing snaps. */
.btn .btn-spinner{position:relative;width:1.05em;height:1.05em;flex:none;transform:scale(0);opacity:0;margin-left:-.25em;margin-right:calc(-1.05em - 10px);transition:transform .3s var(--ease-out),opacity .25s,margin .3s var(--ease-out)}
.btn .btn-spinner::before{content:"";position:absolute;inset:0;border:var(--line-w-strong) solid currentColor;border-right-color:transparent;border-radius:50%;animation:btn-spin .7s linear infinite}
.btn.is-loading{pointer-events:none;opacity:.85}
.btn.is-loading .btn-spinner{transform:scale(1);opacity:1;margin-right:0}
.btn.is-loading>svg{width:0;opacity:0;margin-left:-10px;margin-right:0}
@keyframes btn-spin{to{transform:rotate(360deg)}}

/* icon-only chip */
/* flex:none for the same reason the button has it: a pair of arrows beside a
   heading used to be squeezed into ovals on a phone */
.icon-chip{display:grid;place-items:center;flex:none;width:var(--ic,var(--ic-md));height:var(--ic,var(--ic-md));border-radius:50%;border:var(--line-w) solid var(--line-dark);color:var(--ink);background:var(--paper);cursor:pointer;transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast)}
.icon-chip:hover,.btn-chip:hover{background:var(--green);border-color:var(--green);color:#fff}
.icon-chip.solid{background:var(--green);border-color:var(--green);color:#fff}
.icon-chip.solid:hover{background:var(--green-deep)}

/* ---------- the underline ----------
   One implementation, finally. The complete pattern existed only on the header's
   nav link: a resting hint at --u-hint under a sweep that scales from the left at
   --u-color. Everywhere else was a partial copy, and three families had fallen
   back to the browser's own underline. Put .u-line on the link, or on a span
   inside it when the link also holds an icon that should stay out of the line. */
.u-line{position:relative;padding-bottom:2px}
.u-line::before,.u-line::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--u-color)}
.u-line::before{opacity:var(--u-hint)}
.u-line::after{clip-path:inset(0 100% 0 0);transition:clip-path .3s var(--ease-out)}
a.u-line:hover::after,a:hover .u-line::after,button:hover .u-line::after,.u-line.on::after,a.on .u-line::after{clip-path:inset(0 0 0 0)}

/* ---------- the round icon container ----------
   Ten forks of this existed at 26, 30, 36, 38, 40, 42, 44, 50 and 54px. One
   component, one scale. Colour stays with whoever uses it. */
.ic-round{display:grid;place-items:center;border-radius:50%;flex:none;
  width:var(--ic,var(--ic-md));height:var(--ic,var(--ic-md))}
.ic-round.xs{--ic:var(--ic-xs)}
.ic-round.sm{--ic:var(--ic-sm)}
.ic-round.lg{--ic:var(--ic-lg)}
.ic-round.xl{--ic:var(--ic-xl)}
/* sitting beside a button: same token, so they cannot drift apart */
.ic-round.match-s{--ic:var(--h-ctl-s)}
.ic-round.match{--ic:var(--h-ctl)}
.ic-round.match-l{--ic:var(--h-ctl-l)}

/* ---------- links ---------- */
.link-more{display:inline-flex;align-items:center;gap:8px;font-weight:600;font-size:.97rem;color:var(--green);text-decoration:none}
/* The read-more is the house green, which is a paper colour: on the ink band it
   measured 2.46:1. Every other component in the library that can land on a dark
   ground carries a rule for it; this one had simply never been put on one until
   P6 wanted a link under a film. The amber is the accent that survives a dark
   ground and it is what the kickers and the quote italics already use there. */
.sec-green .link-more,.sec-ink .link-more,.on-ink .link-more{color:var(--amber-soft)}
.link-more .txt{position:relative;padding-bottom:3px}
.link-more .txt::before{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--u-color);opacity:var(--u-hint)}
.link-more .txt::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--u-color);clip-path:inset(0 100% 0 0);transition:clip-path .3s var(--ease-out)}
.link-more:hover .txt::after{clip-path:inset(0 0 0 0)}
.link-more svg{transition:transform .25s var(--ease-out)}
.link-more:hover svg{transform:translateX(4px)}
.link-more.on-dark{color:var(--amber-soft)}
.link-more.on-dark .txt::before,.link-more.on-dark .txt::after{background:var(--amber-soft)}

/* ---------- sections ---------- */
/* The padding scale, which decision d3 approved. `.sec` pads 104px top and
   bottom, which is right for a section standing on its own and too much for two
   bands in a row: two of them put 208px of nothing between two lines of text.
   Every specimen and every page had been working around it with an inline
   padding, 131 of them, which means the workaround rather than the token was
   the real design. Five steps, named the way the Section block's padding
   control is named. */
/* The five steps are named once per tier, and everything else refers to them.
   They used to be written straight onto `.pad-*`, which meant a step existed
   only as a symmetric pair -- and d3 closed with eight sections still carrying
   an inline `padding:44px 0 56px`, described as "deliberate asymmetry". They
   were not deliberate so much as inexpressible: a section that meets a header
   above and a wide band below genuinely wants two different steps, and the only
   way to say so was to leave the system.

   So the steps are values now, and there are three ways to spend them: `.pad-*`
   for both edges, `.pt-*` for the top and `.pb-*` for the bottom. An editor
   picking top and bottom independently is a normal thing to want, and in
   WordPress it is two select boxes rather than a custom CSS field. */
:root{--p-xs:36px; --p-s:56px; --p-m:76px; --p-d:104px; --p-l:136px}
@media(max-width:1000px){:root{--p-xs:30px; --p-s:48px; --p-m:60px; --p-d:76px;  --p-l:96px}}
@media(max-width:640px) {:root{--p-xs:24px; --p-s:38px; --p-m:48px; --p-d:58px;  --p-l:72px}}

/* A fallback chain rather than an assignment. Setting `--sec-pad` on `.sec`
   itself looks equivalent and is not: a section nested inside another stopped
   inheriting the outer one's step and jumped to the default, which grew the
   Section specimen's own padding demo by 96px. Only `.pad-*` writes the
   property, so the chain resolves the same way it always has. */
.sec{padding-top:var(--sec-pt,var(--sec-pad,var(--p-d)));
     padding-bottom:var(--sec-pb,var(--sec-pad,var(--p-d)))}
.sec.pad-xs{--sec-pad:var(--p-xs)}
.sec.pad-s {--sec-pad:var(--p-s)}
.sec.pad-m {--sec-pad:var(--p-m)}
.sec.pad-l {--sec-pad:var(--p-l)}
/* The steps as utilities, on the padding rather than on a custom property, so
   they work on anything -- a `.hero-page`, a bare div, a section. They were
   scoped to `.sec`, which meant `<section class="hero-page pt-s">` set nothing
   at all and the breadcrumbs on two new pages sat welded to the header. A class
   that silently does nothing is worse than one that does not exist. */
.pt-xs{padding-top:var(--p-xs)}  .pb-xs{padding-bottom:var(--p-xs)}
.pt-s {padding-top:var(--p-s)}   .pb-s {padding-bottom:var(--p-s)}
.pt-m {padding-top:var(--p-m)}   .pb-m {padding-bottom:var(--p-m)}
.pt-d {padding-top:var(--p-d)}   .pb-d {padding-bottom:var(--p-d)}
.pt-l {padding-top:var(--p-l)}   .pb-l {padding-bottom:var(--p-l)}
.pt-0 {padding-top:0}            .pb-0 {padding-bottom:0}
.sec-tint{background:var(--tint)}
.sec-warm{background:var(--tint-2)}
/* On the accent the dim words are the accent's own (14.16). A family writes --on-ink-dim
   once for every dark ground, and here that token is the accent's dim, which the
   paragraphs already were: the ink's dim cleared AA on the house accent by a hair,
   4.57 to 1, and on Terracotta's lighter accent it did not. */
.sec-green{background:var(--green-deep);color:#fff;--u-color:#fff;--on-ink-dim:var(--on-ink-dim-green)}
.sec-green h1,.sec-green h2,.sec-green h3,.sec-green h4{color:#fff}
.sec-green p{color:var(--on-ink-dim-green)}
.sec-ink{background:var(--ink);color:#fff;--u-color:#fff}
/* The amber has two jobs and needed two values: the flourish, which is a mark
   or a rule and can stay bright, and the amber that is text somebody reads,
   which had to darken to clear AA on the paper. On a dark ground the darkened
   one is worse than useless -- a 1.78:1 brown on the green -- so the token
   itself changes meaning here, the same way the on-ink ladder does, and every
   rule that asks for --amber-ink gets the right answer without knowing what
   ground it landed on. */
:is(.sec-green,.sec-ink,.on-ink){--amber-ink:var(--amber-soft)}
.sec-ink h1,.sec-ink h2,.sec-ink h3,.sec-ink h4{color:#fff}
.sec-ink p{color:var(--on-ink-dim)}
/* A link in running text or a list line on the ink: the muted white the footer's
   columns have always used, brightening to white (13.2c; the note 13.5 was to
   take, a link the accent green on the ink, taken early because the footer is
   ordinary blocks now). And a separator on the ink is the dark skin's line. */
.sec-ink p a,.on-ink p a,.sec-green .ilist a,.sec-ink .ilist a,.on-ink .ilist a{color:var(--on-ink-mute)}
.sec-ink p a:hover,.on-ink p a:hover,.sec-green .ilist a:hover,.sec-ink .ilist a:hover,.on-ink .ilist a:hover{color:#fff}
/* and the underline draws in on hover, as every footer link has since family 32 (13.2d) */
.sec-green .ilist a,.sec-ink .ilist a,.on-ink .ilist a{display:inline-block}
.sec-green .ilist a,.sec-ink .ilist a,.on-ink .ilist a,.sec-ink p a,.on-ink p a{position:relative;text-decoration:none;transition:color var(--t-fast)}
.sec-green .ilist a::after,.sec-ink .ilist a::after,.on-ink .ilist a::after,.sec-ink p a::after,.on-ink p a::after{content:"";position:absolute;left:0;right:0;bottom:-2px;height:1px;background:currentColor;clip-path:inset(0 100% 0 0);transition:clip-path .3s var(--ease-out)}
.sec-green .ilist a:hover::after,.sec-ink .ilist a:hover::after,.on-ink .ilist a:hover::after,.sec-ink p a:hover::after,.on-ink p a:hover::after{clip-path:inset(0 0 0 0)}
@media (prefers-reduced-motion: reduce){.sec-green .ilist a::after,.sec-ink .ilist a::after,.on-ink .ilist a::after,.sec-ink p a::after,.on-ink p a::after{transition:none}}
/* a one-line form on the ink takes the soft line round its field, as the footer's
   newsletter always has; the newsletter closer keeps its own, stronger one */
.sec-ink .form-line .nl-form,.on-ink .form-line .nl-form{--nl-line:var(--on-ink-line)}
/* a caption on the ink: the footer's "Follow the house" is one (13.2d) */
:is(.sec-green,.sec-ink,.on-ink) .cap{color:var(--on-ink-dim)}
.sec-ink .sep-line,.on-ink .sep-line{background:var(--on-ink-line)}
/* The section's own word for its type, and a colour of its own (13.1, D25).
   `.on-ink` was the components' name for a dark ground and the theme wrote it
   beside the accent and the ink; now the section says it itself, so its type
   goes light on any ground, and `.txt-dark` says the other thing on a dark
   one. A colour a person typed reaches the page as --sec-bg on the element and
   nowhere else, and `.sec-custom` is the one rule that reads it. The grounds
   are the design; these exist for the case a ground cannot draw. A section
   marked light on a typed colour, or under a photograph, also carries the ink
   ground's class with its paint replaced, so every component draws on it
   exactly as it draws on ink, by construction rather than by a new skin. */
.sec.on-ink{color:#fff;--u-color:#fff}
.sec.on-ink h1,.sec.on-ink h2,.sec.on-ink h3,.sec.on-ink h4{color:#fff}
.sec.on-ink p{color:var(--on-ink-dim)}
/* the kicker keeps its amber on a section marked light (13.2c): the rule above,
   written for the paragraph, outranked the kicker by one element */
.sec.on-ink .kicker,.sec.on-ink p.kicker,.sec.on-ink .col-h,.sec.on-ink p.col-h{color:var(--amber-soft)}
.sec-green.on-ink p{color:var(--on-ink-dim-green)}
/* A caption on a dark ground (14.62, D189). The grounds dress a heading and a
   paragraph by descent and said nothing about a caption, so an image's credit on
   a section turned to the accent kept the paper's faint ink and read at 1.55 to 1.
   The faint tier has no reading on a dark ground at all: on the ink and on the
   dark ground a caption takes the dim tier, which is what the gallery family has
   drawn its captions since it was written, and which .sec-green already remaps to
   its own dim. A caption on a light surface standing on a
   dark ground keeps the paper's, which the list of light surfaces below says.
   The captions are named one by one rather than as every figcaption, because a
   quote's attribution is a figcaption as well and on paper it is the ink itself.
   A core image's caption is the theme's, and assets/css/adapter.css says it there. */
:is(.sec-green,.sec-ink,.on-ink) :is(.cap,.art-lead figcaption,.art-fig figcaption,.pic-frame figcaption){color:var(--on-ink-dim)}
.sec.on-ink .sec-head{border-top-color:var(--on-ink-border)}
.sec.txt-dark{color:var(--ink);--u-color:var(--green)}
.sec.txt-dark h1,.sec.txt-dark h2,.sec.txt-dark h3,.sec.txt-dark h4{color:var(--ink)}
.sec.txt-dark p{color:var(--ink-soft)}
.sec.txt-dark .sec-head{border-top-color:var(--line-dark)}
/* ---- a light surface on a dark ground (14.13, D84) -------------------------
   Review, 15 September, building the first test pages: a row of tiles on a dark
   section had white words on their paper cards, and the map's card over the
   map a white name. The dark grounds dress words by descent, every heading
   under the ink white and every paragraph the dim white, which is right for
   words on the ground and wrong for words on a surface standing on it. So a
   surface that stays light on a dark ground takes the paper's words back, with
   the paper's underline colour and the paper's flourish text: a card, which is
   the tile on its paper, its tint, its sand, its flourish and a colour of its
   own with dark words, but not the quote family's card, which that family
   draws glass on a dark ground; an alert's soft and bold tiers; the popup; the
   map's card, its small card, the list beside the map and its tiles; and the
   framed and the split closing. A component the library dresses dark on a dark
   ground, the joined frame, the post card, the table, a person's card, is not
   a light surface. The first rule's list is the one list of light surfaces,
   and tools/on-dark.mjs reads it from here: a new one is added to this list,
   not beside it. The ground and the list together outrank the ground's own
   rule for the same words, so the order the sheets load in does not matter.
   --amber-ink-paper is the flourish text as the page has it, kept on the body
   before a dark ground changes what the token means. */
body{--amber-ink-paper:var(--amber-ink)}
/* The framed closing left this list in 14.61: it keeps no ground of its own now,
   so on a dark section it is white words in a white line rather than a sheet of
   paper, and over a photograph it follows the section's own text colour. The
   split closing stays, because it is a picture and a panel side by side and the
   panel is the paper the picture sits against. */
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split){color:var(--ink);--u-color:var(--green);--amber-ink:var(--amber-ink-paper)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) :is(h1,h2,h3,h4){color:inherit}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) p{color:var(--ink-soft)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) :is(.kicker,.col-h){color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .big-num.big-num{color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .cap{color:var(--ink-faint)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .btn-line{border-color:var(--line-dark);color:var(--ink)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .btn-line:hover{border-color:var(--line-dark);background:var(--ink);color:var(--paper)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .link-more{color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .tel-line a{color:var(--green);text-decoration:none}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .tel-line a:hover{color:var(--green-deep);text-decoration:underline}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .icon-chip{background:var(--paper);border-color:var(--line-dark);color:var(--ink)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .icon-chip:hover{background:var(--green);border-color:var(--green);color:#fff}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .cat{color:var(--green);background:var(--tint-deep)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .e-big a{color:var(--ink)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .e-big :is(.cv,a:hover){color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .rich.dropcap>p:first-child::first-letter{color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .rich blockquote em{color:var(--ink-soft)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) p a{color:var(--green);text-decoration:underline}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) p a:hover{color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .ilist a{display:inline;color:var(--ink);text-decoration:none}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .ilist a:hover{color:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) :is(p,.ilist) a::after{content:none}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .acc-body .rich li{color:var(--ink-soft)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .acc-body .rich li::before{background:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sl-count{color:var(--ink-soft)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sl-count b{color:var(--ink)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sl-prog{background:var(--line)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sl-dots{--sl-idle:#C9D2C9;--sl-hot:var(--ink-soft);--sl-on:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sl-prog i{background:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .e-big .t::after{background:var(--green)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sep-line{background:var(--line)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .sep-line.sep-dark{background:var(--line-dark)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) .joined{--j-line:var(--line-dark);background:var(--paper);border-color:var(--j-line)}
:is(.sec-green,.sec-ink,.on-ink) :is(.card:not(.c-ink,.c-green,.qcards>*),.alert:not(.al-ink),.pop,.ibox,.mini,.loc-card,.map-split,.cta-split) :is(a.joined>.cell,.joined>a.cell,.joined>.cell.linked):hover{background:var(--tint)}
.sec-custom{background:var(--sec-bg)}
/* A photograph under an ordinary section: drawn as the cover draws its own,
   behind the contents, under the image band's three flat overlays or a flat
   tint of a typed colour, with the ground's own padding and container. The
   custom tint doubles its class to beat the family's overlay at equal
   specificity, the way base rules always have. */
.sec-pic{position:relative;isolation:isolate;overflow:hidden}
/* --sec-focal is the one place the photograph is looked at, typed onto the section as two numbers (D30) */
.sec-pic>.bg,.sec-pic>.bg-pic>.bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:var(--sec-focal,50% 50%);z-index:0}
/* A still with a phone file of its own (14.55) is a <picture>, which the
   browser uses to choose the file and which must draw no box of its own: a
   grid or a flex section would make it an item and give it a cell. So it has
   no box, and the still inside is placed as if it were the section's child.
   The phone file is cut for a phone in a photo editor, so its middle is what
   is kept: the focal point was chosen for the other picture. */
.bg-pic{display:contents}
@media(max-width:640px){.bg-pic.has-ph>.bg{object-position:50% 50%}}
.sec-pic::after{content:"";position:absolute;inset:0;z-index:1;background:rgba(10,26,20,.5)}
.sec-pic[data-overlay="soft"]::after{background:rgba(10,26,20,.35)}
.sec-pic[data-overlay="strong"]::after{background:rgba(10,26,20,.65)}

/* ---- a picture beside the words, on any section (14.2) --------------------
   The v5 extended row, and the Section specimen's demos d and e made into what
   a section does. Review, 13 September: the picture at a chosen width, running to
   the section's top and bottom edges rather than inside its padding, out to
   the screen's edge on its side, on either side, and a shape of its own when
   it stacks.

   Full width: the picture is taken out of the flow and set against the section
   from edge to edge, so the section keeps its own padding step for the words
   and the picture ignores it. The words stay in the site's container and
   step clear of the picture by the picture's share of the section, less
   whatever the container is already inset by, plus the gutter the specimen
   drew. A share is a declared width: the regular 46 per cent the specimen
   drew, a third, two fifths, a half or three fifths.

   Framed: the picture stays inside the container, in the house frame, the
   specimen's demo d, and the words sit in the frame beside it.

   Stacked, at the tablet width or at the phone's, the picture comes back into
   the flow, above or below the words, flush with the section's edge on its
   side, at a ratio or a height of its own, because a picture with no height
   of its own collapses to a strip. The gap to the words is the section's own
   step. Rounded toward the words, the rounding follows it round, at the card's
   radius: it was one and three quarters of it until the radii check found
   28px on the Section's page, a corner no token owns, which a site set to
   square corners would still have drawn round. */
.sec.has-side{position:relative;--side-w:46%;--side-gap:64px}
.sec.has-side.side-third{--side-w:33.333%}
.sec.has-side.side-two-fifths{--side-w:40%}
.sec.has-side.side-half{--side-w:50%}
.sec.has-side.side-three-fifths{--side-w:60%}
.side-media{margin:0;overflow:hidden;background:var(--ph)}
/* The placeholder is for a frame with nothing in it yet. Under a photograph
   it showed through the anti-aliased edge of a rounded corner, a pale line
   round the curve on a dark ground, found in review (14.111). */
.side-media:has(>img,>picture,>video){background:none}
.side-media>img{display:block;width:100%;height:100%;object-fit:cover;object-position:var(--side-focal,50% 50%)}

/* full width, the picture against the section's edges */
/* the words sit in the middle of the section's height, as demo e draws them,
   when the picture makes the section taller than they are */
.sec.side-full{min-height:480px;display:flex;align-items:center}
.sec.side-full>.container{flex:1 1 auto;width:100%}
.sec.side-full>.side-media{position:absolute;top:0;bottom:0;width:var(--side-w);z-index:0}
.sec.side-full.side-start>.side-media{left:0}
.sec.side-full.side-end>.side-media{right:0}
.sec.side-full>.container{position:relative;z-index:1}
.sec.side-full.side-start>.container{padding-left:calc(var(--side-w) - max(0px,(100% - var(--container)) / 2) + var(--side-gap))}
.sec.side-full.side-end>.container{padding-right:calc(var(--side-w) - max(0px,(100% - var(--container)) / 2) + var(--side-gap))}
.sec.side-full.side-round.side-start>.side-media{border-radius:0 var(--r-card) var(--r-card) 0}
.sec.side-full.side-round.side-end>.side-media{border-radius:var(--r-card) 0 0 var(--r-card)}

/* A film beside the words (14.12), the v5 extended row's video: over the side
   picture and at its focal point, the frame holding it wherever the picture
   is, to the edge, framed or stacked. Its control, and the split frame's, sits
   in the frame's top end corner, since a caption takes the foot and the words
   are beside it. */
.side-frame>.side-media{position:relative}
.side-media>video.bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:var(--side-focal,50% 50%)}
:is(.hero-split .media,.side-media)>.icon-chip.film-pause.film-pause{top:14px;bottom:auto;inset-inline-end:14px}

/* inside the container and beside the words, with no frame round the pair
   (14.50, P9). The frame above is a card holding a picture and words; this is
   the page keeping its paper, with the picture as a figure on it. The reviewer chose it
   for a person: a portrait running off the screen's edge is a photograph of a
   place, and a face is not a place. */
.side-plain{display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);gap:clamp(24px,3.2vw,48px);align-items:start}
.sec.side-end .side-plain{grid-template-columns:minmax(0,1fr) var(--side-w)}
.sec.side-end .side-plain>.side-media{order:2}
.sec.side-round .side-plain>.side-media{border-radius:var(--r-card)}
.side-plain>.side-media{aspect-ratio:var(--side-ar,16/9);height:var(--side-h,auto)}
@media(max-width:900px){
  /* the picture on the right stacks too (14.66): a media query adds no
     specificity, so `.sec.side-end .side-plain` above, three classes, outranked
     this one-class rule and a phone kept two columns, the words in 140px with
     the heading out of them and the page 35px wider than the screen. Named here
     rather than made !important, since the desktop rule is not wrong. */
  .side-plain,
  .sec.side-end .side-plain{grid-template-columns:minmax(0,1fr);gap:24px}
  .side-plain>.side-media{max-width:360px}
  .sec.side-end .side-plain>.side-media{order:-1}
}
@media(max-width:640px){
  .side-plain>.side-media{max-width:none}
}

/* framed, inside the container */
.side-frame{display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);border:var(--line-w) solid var(--line-dark);border-radius:var(--r-card);overflow:hidden;min-height:340px;background:var(--paper)}
.sec.side-end .side-frame{grid-template-columns:minmax(0,1fr) var(--side-w)}
.sec.side-end .side-frame>.side-media{order:2}
.side-frame>.side-words{display:grid;align-content:center;padding:48px 44px}
.sec-green .side-frame,.sec-ink .side-frame,.on-ink .side-frame{border-color:var(--on-ink-border);background:transparent}
/* framed, rounded toward the words (14.103), from review, as a picture to the
   edge has been since 14.2: the two corners that face the words, the frame
   keeping its own outer ones; stacked, the pair that faces the words below or
   above, in the stacking rules */
.sec.side-framed.side-round.side-start .side-frame>.side-media{border-radius:0 var(--r-card) var(--r-card) 0}
.sec.side-framed.side-round.side-end .side-frame>.side-media{border-radius:var(--r-card) 0 0 var(--r-card)}

/* stacked: the tablet width, or the phone's */
@media(max-width:1000px){
  .sec.side-full.stack-tab{display:flex;flex-direction:column;align-items:stretch;min-height:0}
  .sec.side-full.stack-tab>.side-media{position:relative;inset:auto;width:auto;flex:none}
  .sec.side-full.stack-tab>.container{padding-inline:var(--gutter)}
  .sec.side-full.stack-tab.first-media{padding-top:0}
  .sec.side-full.stack-tab.first-media>.side-media{order:-1;margin-bottom:var(--sec-pad,var(--p-d))}
  .sec.side-full.stack-tab.first-words{padding-bottom:0}
  .sec.side-full.stack-tab.first-words>.side-media{order:2;margin-top:var(--sec-pad,var(--p-d))}
  .sec.side-full.stack-tab.side-round.first-media>.side-media{border-radius:0 0 var(--r-card) var(--r-card)}
  .sec.side-full.stack-tab.side-round.first-words>.side-media{border-radius:var(--r-card) var(--r-card) 0 0}
  .sec.stack-tab .side-frame{grid-template-columns:1fr;min-height:0}
  .sec.stack-tab.first-words .side-frame>.side-media{order:2}
  .sec.stack-tab.first-media .side-frame>.side-media{order:-1}
  .sec.side-framed.stack-tab.side-round.first-media .side-frame>.side-media{border-radius:0 0 var(--r-card) var(--r-card)}
  .sec.side-framed.stack-tab.side-round.first-words .side-frame>.side-media{border-radius:var(--r-card) var(--r-card) 0 0}
  .sec.stack-tab .side-media{aspect-ratio:var(--side-ar,16/9);height:var(--side-h,auto)}
}
@media(max-width:640px){
  .sec.side-full.stack-ph{display:flex;flex-direction:column;align-items:stretch;min-height:0}
  .sec.side-full.stack-ph>.side-media{position:relative;inset:auto;width:auto;flex:none}
  .sec.side-full.stack-ph>.container{padding-inline:var(--gutter)}
  .sec.side-full.stack-ph.first-media{padding-top:0}
  .sec.side-full.stack-ph.first-media>.side-media{order:-1;margin-bottom:var(--sec-pad,var(--p-d))}
  .sec.side-full.stack-ph.first-words{padding-bottom:0}
  .sec.side-full.stack-ph.first-words>.side-media{order:2;margin-top:var(--sec-pad,var(--p-d))}
  .sec.side-full.stack-ph.side-round.first-media>.side-media{border-radius:0 0 var(--r-card) var(--r-card)}
  .sec.side-full.stack-ph.side-round.first-words>.side-media{border-radius:var(--r-card) var(--r-card) 0 0}
  .sec.stack-ph .side-frame{grid-template-columns:1fr;min-height:0}
  .sec.stack-ph.first-words .side-frame>.side-media{order:2}
  .sec.stack-ph.first-media .side-frame>.side-media{order:-1}
  .sec.side-framed.stack-ph.side-round.first-media .side-frame>.side-media{border-radius:0 0 var(--r-card) var(--r-card)}
  .sec.side-framed.stack-ph.side-round.first-words .side-frame>.side-media{border-radius:var(--r-card) var(--r-card) 0 0}
  .sec.stack-ph .side-media{aspect-ratio:var(--side-ar,16/9);height:var(--side-h,auto)}
  .side-frame>.side-words{padding:clamp(26px,7vw,32px) clamp(16px,5.5vw,20px)}
}
/* the shape when stacked: a ratio, or a height, which wins over the ratio */
.side-shape-16-9{--side-ar:16/9}.side-shape-3-2{--side-ar:3/2}.side-shape-4-3{--side-ar:4/3}.side-shape-1-1{--side-ar:1/1}.side-shape-3-4{--side-ar:3/4}
.side-shape-short{--side-ar:auto;--side-h:240px}.side-shape-medium{--side-ar:auto;--side-h:360px}.side-shape-tall{--side-ar:auto;--side-h:480px}
.sec-pic>:not(.bg){position:relative;z-index:2}
.ov-custom.ov-custom::after{background:color-mix(in srgb,var(--sec-ov) var(--sec-ov-a),transparent)}
/* ---- a film behind the words (14.3) -----------------------------------------
   The loop the Video specimen promised, made a second source for the picture
   engine: a <video class="bg" data-film> beside the picture in a section or a
   cover, under the same overlay. The film is invisible until it has a frame to
   show (.is-on, set on the film the first time it plays, so the section's own
   classes stay what the block wrote), and the picture is what a slow
   connection, a visitor who asked for less motion and a page without scripts
   all see; paused, it keeps its frame rather than jumping back to the picture.
   The control is WCAG 2.2.2's, since the film moves for longer than five
   seconds: at the container's end edge, above the overlay, and hidden until
   the script has something for it to stop. The doubled class beats the dark
   grounds' chip at equal specificity, the way base rules always have. */
video.bg[data-film]{opacity:0;transition:opacity .6s var(--ease-out)}
video.bg[data-film].is-on{opacity:1}
.icon-chip.film-pause.film-pause{position:absolute;z-index:3;bottom:clamp(14px,2.2vw,22px);inset-inline-end:max(var(--gutter),calc((100% - var(--container)) / 2));--ic:var(--ic-sm);background:rgba(10,26,20,.4);border-color:rgba(255,255,255,.55);color:#fff}
.icon-chip.film-pause.film-pause:hover{background:rgba(10,26,20,.7);border-color:#fff}
/* hidden means hidden: the chip's own display:grid would otherwise beat the
   attribute, and a page without scripts, and the editor, would show a control
   that does nothing (found in the editor in 14.3's human check) */
.icon-chip.film-pause[hidden]{display:none}
/* on a cover the words sit at the bottom and run the width on a phone, so the
   control goes to the top corner, where a cover has nothing */
.hero-cover>.icon-chip.film-pause.film-pause{top:clamp(14px,2.2vw,22px);bottom:auto}
/* and the cover's words keep clear of it: they sit at the bottom, but a short
   cover's words fill the box, and on a phone the kicker reached up under the
   control (measured at 390 on the Hero page in 14.3) */
.hero-cover:has(>.film-pause)>.inner{padding-top:72px}
/* on a slide of the full slider (14.11) the slider's own controls take the
   bottom, so the film's goes to the top corner, as on a cover */
.hs>.icon-chip.film-pause.film-pause{top:clamp(14px,2.2vw,22px);bottom:auto}
/* and where the header lies over the page's opening, a cover or a slider at the
   top, the control drops below the header rather than sitting beside its
   buttons: measured on the house's front page shape in 14.11, where it landed
   next to Become a member */
:is(.place-over-dark,.place-over-light) :is(.hero-cover,.hs)>.icon-chip.film-pause.film-pause{top:calc(var(--hdr-h,84px) + clamp(14px,2.2vw,22px))}
@media (prefers-reduced-motion: reduce){video.bg[data-film]{transition:none}}
/* column-gap, not gap: the sub-line sits in row two of this grid, so a plain
   `gap:26px` was charged between the heading and its own sub-line as well as
   between the columns -- 26px of row gap plus the sub's 10px margin, 36px in
   all, which is why the description looked adrift from the heading it belongs
   to. The rows now space themselves. */
/* `baseline`, not `end`. The number in the margin is a label for the block and
   belongs on the first line of the heading it labels; with `end` it sat on the
   heading's *last* baseline, so a heading that wrapped to two lines left its
   own number stranded beside the second one. Both the aside and the action
   declare `align-self:end` for themselves, so they keep sitting on the bottom
   of the block and nothing else moves. A one-line heading renders identically,
   which is why this survived five pages. */
.sec-head{display:grid;grid-template-columns:auto 1fr auto;column-gap:26px;row-gap:0;align-items:baseline;border-top:var(--line-w) solid var(--line-dark);padding-top:24px;margin-bottom:58px}
.sec-green .sec-head,.sec-ink .sec-head{border-top-color:var(--on-ink-border)}
.sec-head h2{max-width:22ch;justify-self:start}
/* a head with no line above it (14.28). The hairline says "a new thing starts
   here"; where a section carries straight on from the one above, it reads as a
   break instead, and the reviewer asked to be able to take it off. The padding goes with
   it: it was the air under the line, and without the line it is a gap nobody
   asked for. */
.sec-head.no-rule{border-top:0;padding-top:0}
/* `.side` -- a heading on the left with its explanation set against the right
   edge -- is gone. It was the first arrangement this block had and the page
   templates stopped using it early: a heading and a paragraph at opposite ends
   of a rule read as two columns arguing rather than as one thing being
   introduced, and at the tablet tier the aside had to be unwound into a
   sub-heading anyway, so the layout only held at one width. It survived in the
   options block of all thirty-three specimens, which is where the reviewer found it again.
   The class is kept as an alias of `.sub` rather than deleted, so a page or a
   pattern that still says `side` gets the arrangement the house actually uses
   instead of an unstyled paragraph. */

/* The section head, second arrangement, and the one the page templates use.
   A heading on the left with a paragraph of explanation on the right reads as
   two columns arguing; the sub-text belongs under the heading it explains, and
   the space on the right belongs to an action.

   `.sub` lands in the heading's own column, so where there is a number in the
   margin the sub-text is inset to the heading rather than to the number.
   `.act` takes the right-hand slot and sits on the baseline of the block. */
.sec-head .sub,.sec-head .side{grid-column:2;grid-row:2;max-width:46rem;font-size:1.03rem;margin-top:7px;
  justify-self:start;text-align:left;align-self:start}
.sec-head .act{grid-column:3;grid-row:1/3;justify-self:end;align-self:end;white-space:nowrap}
.sec-head:has(.sub),.sec-head:has(.side){align-items:start}
.sec-head:has(.sub) .act,.sec-head:has(.side) .act{align-self:end}
/* Without a numeral there is no first column. The heading auto-placed into the
   numeral's `auto` track and the standfirst, pinned to column two, landed
   beside it as a second column, so a heading as short as four words wrapped
   while half the row sat empty (B4, the first real site). Two tracks then:
   the heading and the standfirst in the first, the action in the second
   across both rows. The house widths hold. Desktop only; the narrower tiers
   already put everything in one column. */
@media(min-width:1001px){
  .sec-head:not(:has(.sec-num)){grid-template-columns:minmax(0,1fr) auto}
  .sec-head:not(:has(.sec-num)) h2{grid-column:1;grid-row:1}
  .sec-head:not(:has(.sec-num)) .sub,.sec-head:not(:has(.sec-num)) .side{grid-column:1;grid-row:2}
  .sec-head:not(:has(.sec-num)) .act{grid-column:2;grid-row:1/3}
}
@media(max-width:1000px){
  /* flush with the content, not indented under the heading. The indent exists
     so the sub-text lines up with the words rather than with the number in the
     margin; below the desktop tier there is not enough width to give a column
     away to that. */
  .sec-head .sub,.sec-head .side{grid-column:1/-1;grid-row:auto;max-width:46rem}
  .sec-head .act{grid-column:1/-1;grid-row:auto;justify-self:start;margin-top:18px}
}
@media(max-width:640px){
  /* one column, so the sub-text has to flow after the heading rather than stay
     pinned to row two: the explicit row from the desktop rule left the heading
     auto-placing into row three, under its own explanation. */
  .sec-head .sub,.sec-head .side{grid-column:1;grid-row:auto;margin-top:7px}
}
/* three columns of heading, number and aside cannot share a phone. The aside
   drops under the heading and the whole block loosens. */
@media(max-width:1000px){
  .sec-head{grid-template-columns:auto 1fr;column-gap:20px;row-gap:0;margin-bottom:40px}
}
@media(max-width:640px){
  .sec-head{grid-template-columns:1fr;column-gap:10px;row-gap:0;padding-top:20px;margin-bottom:32px}
  .sec-head h2{max-width:none}
  /* one column, so the number now stacks above the heading and each part of the
     head carries its own space: the row gap that used to separate them is gone
     by design. The values are the phone rhythm the gap used to produce, so the
     only head that moved is the one that was paying twice. */
  .sec-head .sec-num{grid-column:1;margin-bottom:10px}
  .sec-head .act{margin-top:16px}
}

/* ---------- lead with a feature on the tablet tier ---------- */
/* Three across becomes two on a tablet, which leaves the third card alone on a
   row looking like a mistake. With `.feature-first` the first card takes the
   full width as a horizontal card and the other two sit at half each beneath,
   so the row is always full.
   
   The geometry lives here, once, because two families need the identical thing
   and had grown two versions of it -- the tiles at one breakpoint and the post
   grid at another, and the alignment that makes it look deliberate existed only
   on the home page, written against `.rooms`. That is why the same pattern on
   the event page did not match it.

   What makes it look deliberate: the feature's image is exactly one column
   wide, so its words begin on the same vertical as the edge of the card below
   it, rather than a few pixels off it. On that card's edge rather than on its
   words since 14.74, on review: the feature reads as one card beside the other
   two, and its words starting where the next card starts is the line the eye
   follows down. That is arithmetic on the grid's own gap, so the grid has to
   name its gap -- `--g`; the tiles' grid names it `--grid-gap`, and passes it on.

   Family-agnostic on purpose: a feature card is a media box followed by a body,
   whatever the family calls them, so this addresses first child and last child
   rather than `.media`/`.ph` and `.body`/`.bd`. The doubled class is there to
   out-specify each family's own padding, which is two classes deep.

   `:not(.hide)`, because a filtered-out card must never be dressed as the
   feature -- filtering to a category the first card is not in used to leave it
   laid out as the feature on top of the results. */
/* The hairline is in the arithmetic on purpose. `100%` of a card is its content
   box, already inside its own 1.5px border, while the column it has to line up
   with is measured outside one -- so without the correction the feature's
   picture overshot the picture below it by a border and its words started a
   border short. 1.5px, twice, and both edges land. */
.feature-first{--ff-media:calc((100% - var(--g,20px)) / 2 - var(--line-w));
  --ff-inset:calc(var(--g,20px) + var(--line-w))}
/* the tiles' grid keeps its gap in --grid-gap, 24px, where the post grid names
   --g; without this the tiles' feature was measured on a 20px gap and fell 4px
   short of the card below it */
.grid-3.feature-first{--g:var(--grid-gap,24px)}
@media(min-width:641px) and (max-width:1000px){
  /* a word tile has no picture to put beside its words (14.74): it takes the
     whole width and keeps its own column of words, where the grid below would
     put its heading in the picture's column */
  .feature-first.feature-first>.tile-basic:first-child:not(.hide){grid-column:1/-1}
  .feature-first.feature-first>*:first-child:not(.hide):not(.tile-basic){
    grid-column:1/-1;display:grid;grid-template-columns:var(--ff-media) 1fr;grid-template-rows:auto}
  .feature-first.feature-first>*:first-child:not(.hide):not(.tile-basic)>*:first-child{
    aspect-ratio:auto;height:100%;min-height:236px}
  .feature-first.feature-first>*:first-child:not(.hide):not(.tile-basic)>*:last-child{
    padding:30px var(--card-pad,26px) 32px var(--ff-inset);justify-content:center}
  .feature-first.feature-first>*:first-child:not(.hide):not(.tile-basic) h3{font-size:1.5rem}
}

/* ---------- the office panel ---------- */
/* A number, when it is answered, and a way to write instead, in the signature
   frame. It began on the event page beside the questions and the venue hire
   page wants the identical thing beside its enquiry form -- which is the moment
   a page-local rule stops being one. The page decides where it sits and how
   wide; this decides what it is.

   Sticky by default, because the panel answers the question you did not find
   after scrolling past everything, and static below the desktop tier, where it
   is no longer beside anything to be sticky against. */
/* ---------- a full-bleed media band ----------
   A picture that runs to both screen edges under a page header. Written on the
   hire page, and the journal article wants exactly the same thing for its lead
   image: inside the container it reads as an illustration, and to the edges it
   reads as the thing you are about to read about. */
.bleed-band{height:clamp(240px,32vw,420px);overflow:hidden;background:var(--ph)}
.bleed-band img{width:100%;height:100%;object-fit:cover;display:block}

/* ---------- a form and the panel beside it ----------
   The same shape as `.article-row` and deliberately a different measure: a form
   column has no reading measure to protect, so it takes what is left rather
   than being held to 46rem. Written on the hire page; the membership page wants
   the identical pair, which is the moment it stops being page-local. */
.ask-grid{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:56px;align-items:start}
@media(max-width:1000px){.ask-grid{grid-template-columns:1fr;gap:36px}}
/* Two fields to a row on a desktop, one on a phone. The pairs are the page's
   business; that they pair at all is not. */
.fld-grid{display:grid;gap:0}
.fld-grid .fld-row{display:grid;grid-template-columns:1fr 1fr;gap:0 20px}
@media(max-width:640px){.fld-grid .fld-row{grid-template-columns:1fr}}
.fld-grid textarea{resize:vertical;min-height:110px}
.fld-grid .note{font-size:.92rem}

/* ---------- an article and the panel beside it ----------
   Written on the event page as `.ev-body`, and the journal article wants the
   identical thing: a reading measure that does not stretch, and whatever the
   page needs kept beside it -- a booking panel, a contents list, an author.
   Two pages is the moment a page-local rule stops being one.

   The side column is sticky under the header rather than under the top of the
   window, and on a phone it stops being a side column at all: `order:-1` puts
   it first, because a reader who has already decided should not have to scroll
   past the article to act on it. */
.article-row{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:70px;align-items:start}
/* under the header wherever the header starts (--hdr-top: the admin bar in
   WordPress, which put a signed-in reader's rail behind the header), with
   the air every sticky thing shares (14.88) */
.article-side{position:sticky;top:calc(var(--hdr-top,0px) + var(--hdr-h-min,84px) + var(--rv-bar,0px) + var(--stick-air,40px));display:grid;gap:22px}
/* ---------- the article ----------
   Two shapes for a long piece, both drawn on the journal templates. The
   reading column: one measure, centred, and only a picture, a quotation and one
   notice allowed to break it. The dossier: the same prose beside a rail that
   stays with the reader, which is .article-row and .article-side above. What
   the two share is here under one name, art-, because a component named after
   the page it was first drawn on is a component nobody finds twice. */
/* the title block. Centred and held to a reading measure like everything
   under it: a headline set to the full container is a headline nobody
   finishes. No kicker above the h1: between the breadcrumb and the title it
   is a third label in a row of labels, and the breadcrumb has already said
   Journal. The interior header's rule goes when the head is an article's,
   because the lead picture under it wants no line between them. */
.art-head{max-width:44rem;margin-inline:auto;text-align:center}
.art-head .crumbs{justify-content:center;margin-bottom:20px}
.art-head .lead{margin-inline:auto}
.hero-page:has(>.container>.art-head){border-bottom:0}
/* who, when and how long, on one line above the article rather than in a rail
   beside it: a reader wants this before the first sentence and never after.
   Two groups rather than four items: four wrap on a phone and leave a
   separator stranded at the head of the second line. */
.art-meta{display:flex;flex-wrap:wrap;justify-content:center;gap:0 14px;margin:32px 0 0;padding-top:22px;border-top:var(--line-w-soft) solid var(--line);font-size:.94rem;color:var(--ink-soft)}
.art-meta b{color:var(--ink);font-weight:600}
.art-meta span+span::before{content:"\00b7";margin-right:14px;color:var(--line-dark)}
@media(max-width:640px){.art-meta{flex-direction:column;gap:5px}.art-meta span+span::before{content:none}}

/* ---------- a thing's own facts (P8a, P8b) ----------
   A journal's byline is four short atoms of one kind -- a name, a label, a
   date, a length -- and a line with dots between them is enough to tell them
   apart. A client's own kind of thing has facts instead, and a fact is a pair:
   When, Saturday 14 March. A pair whose label is not read as a label reads as
   two values, so "Saturday 14 March · The workshop · 220 kr." is a sentence
   nobody parses at a glance and a price that looks like a date's neighbour.

   One markup, `dl > div > dt + dd`, which is what a pair is, and three
   arrangements, because two facts and ten facts want different drawings and
   the count is not enough to choose between them:

     .facts-row    a row of small stacks on the middle line, for the two to
                   four a reading column's header carries where the byline was
     .facts-stack  the same pairs stacked, for the block that stands on a
                   cover's photograph beside the standfirst
     .facts-rail   label at one end, value at the other, a hairline between,
                   for the ten a dossier's rail holds in a card

   The pairs are the hero's own rail one step out: `.page-facts` has drawn the
   third of these beside a page header's title since the first venue page, and
   this is that drawing where a block can put it. */
/* one field where a paragraph would go (14.51): the label quiet in front of the
   value, or the value alone. The list of them is `.facts` below; this is one. */
.one-field{margin:0;display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px}
.one-field .t{font-size:.86rem;color:var(--ink-faint)}
.one-field .v{font-family:var(--serif);font-weight:540;color:var(--ink)}
.one-field .v a{color:var(--green);text-decoration:none}
.one-field .v a:hover{text-decoration:underline;text-underline-offset:3px}
.one-field-l .v{font-size:clamp(1.5rem,2.4vw,1.95rem);line-height:1.15}
.one-field-l .t{font-size:.9rem}
:is(.sec-green,.sec-ink,.on-ink) .one-field .t{color:var(--on-ink-dim)}
:is(.sec-green,.sec-ink,.on-ink) .one-field .v{color:#fff}
:is(.sec-green,.sec-ink,.on-ink) .one-field .v a{color:var(--amber-soft)}

.facts{margin:0}
.facts dd{margin:0}
.facts dd a{color:var(--green);text-decoration:none}
.facts dd a:hover{text-decoration:underline;text-underline-offset:3px}

/* the row, where the byline stood: the same place, the same hairline above,
   the same air under the lead */
.facts-row{display:flex;flex-wrap:wrap;justify-content:center;gap:16px 52px;margin-top:32px;padding-top:22px;border-top:var(--line-w-soft) solid var(--line)}
/* the same row under a page header's words rather than under a centred title:
   left where those words are, and no wider than the words it belongs to (14.48) */
.hero-facts-under{justify-content:flex-start;gap:16px 44px;max-width:44rem;margin-bottom:28px}
/* and on a cover, where P8b stacks them under the standfirst (14.54) */
.hero-facts-cover{margin-top:26px;max-width:48rem}

.hero-facts-under>div{text-align:left}

.facts-row>div{display:grid;gap:3px;min-width:6.5rem;text-align:center}
.facts-row dt{font-size:.82rem;color:var(--ink-faint)}
.facts-row dd{font-family:var(--serif);font-weight:540;font-size:1.1rem;line-height:1.25;color:var(--ink)}
@media(max-width:640px){.facts-row{gap:14px 30px}.facts-row>div{min-width:5.5rem}}

/* the stack, on a cover: no rules, because a rule drawn on a photograph is a
   fourth thing to look at, and across rather than down below the desktop tier,
   where the cover is already as tall as it is allowed to be */
.facts-stack{display:grid;gap:13px;align-content:end}
.facts-stack>div{display:grid;gap:2px}
.facts-stack dt{font-size:.84rem;line-height:1.3;color:var(--ink-faint)}
.facts-stack dd{font-family:var(--serif);font-weight:540;font-size:1.08rem;line-height:1.25;color:var(--ink)}
:is(.hero-cover,.sec-green,.sec-ink,.on-ink) .facts-stack dt{color:var(--on-ink-dim)}
:is(.hero-cover,.sec-green,.sec-ink,.on-ink) .facts-stack dd{color:#fff}
:is(.hero-cover,.sec-green,.sec-ink,.on-ink) .facts dd a{color:var(--amber-soft)}
@media(max-width:1000px){.facts-stack{grid-template-columns:repeat(3,minmax(0,1fr));gap:18px 34px}}
@media(max-width:640px){.facts-stack{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* the rail, in a card: ten pairs read down, the value in the serif against the
   far edge so a column of them is a column rather than ten ragged lines */
.facts-rail{display:grid;gap:0;border-top:var(--line-w) solid var(--line-dark)}
.facts-rail>div{display:flex;justify-content:space-between;align-items:baseline;gap:16px;padding:11px 0;border-bottom:var(--line-w-soft) solid var(--line)}
.facts-rail>div:last-child{border-bottom:0;padding-bottom:0}
.facts-rail dt{font-size:.88rem;color:var(--ink-soft);flex:none}
.facts-rail dd{font-family:var(--serif);font-weight:540;font-size:.98rem;line-height:1.3;text-align:right}
:is(.sec-green,.sec-ink,.on-ink) .facts-rail{border-top-color:var(--on-ink-border)}
:is(.sec-green,.sec-ink,.on-ink) .facts-rail>div{border-bottom-color:var(--on-ink-line)}
:is(.sec-green,.sec-ink,.on-ink) .facts-rail dt{color:var(--on-ink-dim)}

/* the one thing an item's page has that an article has not: the button, under
   the facts rather than after the words. A person deciding whether to come
   decides here; a call to action nine hundred words later is a call to action
   for the people who were coming anyway. */
.art-do{margin:26px 0 0;display:flex;justify-content:center}
/* the lead picture. A ratio, not a height: a band of a fixed number of pixels
   crops a different picture at every width. Four by three on a phone, where
   sixteen by nine is a letterbox. */
.art-shot{display:block;aspect-ratio:var(--art-ar,16/9);border-radius:var(--r-card);overflow:hidden;background:var(--ph)}
.art-lead{margin:0}
.art-lead figcaption,.art-fig figcaption{margin-top:13px;text-align:center;font-family:var(--serif);font-style:italic;font-size:.94rem;color:var(--ink-faint)}
@media(max-width:640px){.art-shot{aspect-ratio:var(--art-ar-ph,4/3)}}
/* the reading column, art-column beside art-body: a 940px block whose children
   are 41rem and centred, so a figure
   or a pull quote takes the whole block and every paragraph starts on the axis
   the one before it did. A child that may break the column says so by setting
   --col rather than by out-specifying the rule, because the rules it competes
   with are `.rich h2` and `.rich blockquote`, which set margin at the
   shorthand and so beat a plain margin-inline. Two classes left of the
   combinator settles it once for all of them. */
.art-column.art-column{max-width:940px;margin-inline:auto}
.rich.art-column>*{max-width:var(--col,41rem);margin-inline:auto}
.rich.art-column>.art-wide{--col:100%}
.rich.art-column>h2:first-of-type{margin-top:2.2rem}
.art-first{font-size:1.16rem;line-height:1.62}
/* the pull quote: centred and ruled across the top rather than rich text's bar
   down the left, because a bar down the side of a centred column points at
   nothing */
.rich.art-column>.art-pull{--col:760px;text-align:center;border-left:0;padding:34px 0 0;margin-block:2.6rem 2.8rem;border-top:var(--line-w) solid var(--line-dark);font-size:clamp(1.5rem,2.4vw,1.85rem)}
.art-pull em{margin-top:.7em}
.rich.art-column>.art-fig{margin-block:2.6rem}
.rich.art-column>.alert{margin-block:2.4rem}
.rich.art-column>.ilist{margin-bottom:1.6rem}
/* A picture beside the words (14.31). From writing a post: a picture in an
   article can be wide, and it should also be able to stand at the side with the
   words running past it, which is what every editor reaches for and what this
   library had no drawing of. The words WordPress uses for it are `alignleft`
   and `alignright`, and they are named here rather than mapped onto a class of
   the library's own, for the same reason the hand-drawn underline names the
   empty span WordPress writes: where the editor's word IS the vocabulary, a
   second name for it is the drift R6 exists to prevent.

   Under 700px it stops: half of a phone's column is 160px, which is a thumbnail
   beside a line of four words, so the picture goes back to standing on its own
   the full width of the column.

   The width is said twice: --col is how a child of the reading column says how
   wide it is, and max-width is for the article bodies that are not that column,
   where nothing reads --col and a float with no width floats nothing. */
.rich>:is(.alignleft,.alignright){--col:min(46%,360px);max-width:min(46%,360px);margin-block:.5rem 1.3rem}
.rich>.alignleft{float:left;margin-inline:0 1.9rem}
.rich>.alignright{float:right;margin-inline:1.9rem 0}
@media(max-width:700px){
  .rich>:is(.alignleft,.alignright){float:none;--col:100%;max-width:none;margin-inline:auto}
}
.art-frame{display:block;aspect-ratio:16/10;border-radius:var(--r-card);overflow:hidden;background:var(--ph);border:var(--line-w) solid var(--line-dark)}
.art-note{margin:2.2rem 0}
/* sub-heads in an article are subordinate to the headline, and --fs-h2 is the
   size of a section heading on a landing page: at 46px under a 57px h1 the
   two compete. The library keeps its scale; the article sets its own. */
.art-body h2{font-size:calc(clamp(1.6rem,2.5vw,2.05rem) * var(--scale-head))}
/* the foot of the article: what it was about, and the three things a reader
   might do with it. Drawn after a reading column it keeps the column's
   measure; in the dossier it is as wide as the prose. */
.art-end{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px;margin-top:44px;padding-top:24px;border-top:var(--line-w) solid var(--line-dark)}
.art-column+.art-end{max-width:41rem;margin-inline:auto}
.art-end .pillrow{margin:0}
.share{display:flex;align-items:center;gap:10px}
.share .lbl{font-family:var(--serif);font-style:italic;color:var(--ink-faint);font-size:.95rem;margin-right:4px}
:is(.sec-green,.sec-ink,.on-ink) .share .lbl{color:var(--on-ink-dim)}
.share .ic-btn{--act-h:40px}
/* the author, at the foot and at a size where a face is a face: the same
   portrait in a 46px byline circle is what made the first version of this
   page look broken */
.art-author{display:grid;grid-template-columns:auto 1fr;gap:34px;align-items:center;max-width:940px;margin-inline:auto}
.art-face{display:block;width:118px;height:118px;border-radius:50%;overflow:hidden;background:var(--ph);border:var(--line-w) solid var(--line-dark)}
.art-author .bd{max-width:44rem}
.art-author h2{margin:6px 0 10px}
.art-author p{margin-bottom:16px}
@media(max-width:640px){.art-author{grid-template-columns:1fr;gap:22px;justify-items:start}.art-face{width:92px;height:92px}}
/* the dossier's opening: the standfirst and the byline share one row on the
   photograph, in the article row's own columns, so the byline starts exactly
   where the rail starts and the standfirst runs to where the prose runs */
.art-open{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:22px 70px;align-items:end}
.art-open .lead{max-width:40rem;margin:0}
.art-by{display:flex;align-items:center;gap:16px}
.art-byface{display:block;width:60px;height:60px;border-radius:50%;overflow:hidden;flex:none;background:var(--ph);border:var(--line-w) solid var(--line-dark);position:relative}
.art-byface .plate{position:absolute;inset:0}
.art-by .who{display:grid;gap:5px;font-size:.92rem;line-height:1.3;color:var(--ink-soft)}
.art-by .who b{font-size:1.1rem;line-height:1.2;color:var(--ink);font-weight:600}
.hero-cover .art-byface{border-color:var(--on-ink-border)}
.hero-cover .art-by .who{color:var(--on-ink-dim)}
.hero-cover .art-by .who b{color:#fff}
@media(max-width:1000px){.art-open{grid-template-columns:1fr;gap:26px;align-items:start}.art-open .lead{max-width:42rem}}
/* the rail's own widgets: a table of contents that goes when the rail falls
   under the article, because an index under the thing it indexes is furniture,
   and a card with the padding a rail wants. A link is as wide as its text. */
.art-toc{display:grid;gap:10px;padding-bottom:24px;border-bottom:var(--line-w) solid var(--line-dark)}
.art-toc .kicker{margin-bottom:4px}
.art-toc a{justify-self:start;color:var(--ink-soft);text-decoration:none;font-size:.96rem;line-height:1.35}
.art-toc a:hover{color:var(--green)}
.art-toc .sub{padding-left:14px;font-size:.9rem}
/* on a dark ground (14.13): the dim white, brightening to white, where the
   paper's grey measured 2.6:1 on the ink */
:is(.sec-green,.sec-ink,.on-ink) .art-toc{border-bottom-color:var(--on-ink-border)}
:is(.sec-green,.sec-ink,.on-ink) .art-toc a{color:var(--on-ink-dim)}
:is(.sec-green,.sec-ink,.on-ink) .art-toc a:hover{color:#fff}
@media(max-width:1000px){.art-toc{display:none}}
.art-card{padding:26px 24px}
/* A rail card's title (14.31). It was a kicker, which is the green italic
   eyebrow this library uses ABOVE a heading, so the cards in the rail had an
   eyebrow and no heading at all: 15px, the size of their own body text, which
   is what the reviewer saw and called too small and unfitting. A card's title is a
   heading, at the size a 340px card can carry, which is the h4 of the scale
   rather than the h3 a card in a row of three gets. The kicker keeps its place
   for a card that wants both. */
.art-card :is(h2,h3,h4){font-size:var(--fs-h4);line-height:1.15;margin-bottom:10px}
.art-card .kicker{margin-bottom:8px}
.art-card .kicker+:is(h2,h3,h4){margin-top:-2px}
.art-card .bar{margin:4px 0 14px}
.art-card p{font-size:.94rem}
.art-card .btn{margin-top:16px;align-self:flex-start}
@media(max-width:1000px){
  .article-row{grid-template-columns:1fr;gap:44px}
  .article-row>.article-side{position:static}
  /* Opt in, and only where the panel is the point. On the event page the rail
     is a booking form and it goes first, because a visitor who has decided
     should not have to scroll past the programme notes to act. On an article it
     is a contents list and two cards, and putting those before the first
     sentence is the one thing a page of prose must not do -- which is what it
     did on a phone until the rule was made a choice rather than a default. */
  .article-row.act-first>.article-side{order:-1}
}

.ask{position:sticky;top:104px}
.ask .cell{display:grid;justify-items:start;gap:0}
.ask .as-h4{margin:6px 0 14px}
/* The same spacing for any heading in the rail, not only the one the prototype
   happened to draw. In WordPress the rail is filled with blocks, so the cell
   gets an ordinary h3 rather than the `.as-h4` this page typed by hand, and
   without this the heading sits hard on the list under it. */
.ask .cell>:is(h2,h3,h4){margin:6px 0 14px}
/* The big number, out of the rail. It was `.ask .ask-tel` and belonged to the
   office panel; a contact page wants to be rung from its prose as well, and a
   number set at reading size is a number nobody rings. So the role is declared
   once for both -- `.tel-line` is the paragraph, its link is the number -- and
   the rail keeps the margin that is its own. */
.ask .ask-tel,.tel-line a{font-family:var(--serif);font-weight:540;font-size:1.95rem;
  letter-spacing:-.015em;line-height:1.1;color:var(--green);text-decoration:none}
.ask .ask-tel{display:inline-block;margin-bottom:16px}
.ask .ask-tel:hover,.tel-line a:hover{color:var(--green-deep);text-decoration:underline;text-underline-offset:4px}
.sec-green .tel-line a,.sec-ink .tel-line a,.on-ink .tel-line a{color:var(--amber-soft)}
.sec-green .tel-line a:hover,.sec-ink .tel-line a:hover,.on-ink .tel-line a:hover{color:#fff}
/* the kicker labels the list under it and was sitting on top of it */
.ask .kicker{display:block;margin-bottom:12px}
.ask .ilist{margin-bottom:20px}
.ask .btn{width:100%}
@media(max-width:1000px){.ask{position:static}}

/* ---------- one line weight for every icon ---------- */
/* `stroke-width` is in the icon's own coordinates, so a 24-unit viewBox drawn
   at 13px renders a 2.2 stroke as 1.19 real pixels and the same 2.2 at 38px as
   3.48. Every block had been picking a number to compensate, seventeen numbers
   in all, and the compensation held to about 17px and then drifted.

   `vector-effect:non-scaling-stroke` moves the measurement into rendered space,
   so one number is one line weight at every size. The per-icon `stroke-width`
   attributes stay in the markup and are simply overridden -- a presentation
   attribute loses to any stylesheet -- so this is one rule rather than 800 edits,
   and removing the rule restores exactly what was there before.

   Scoped by viewBox, which turns out to be the exact boundary: 779 of the 800
   svgs in the library are drawn on `0 0 24 24` and every one of them is an icon.
   The rest are illustrations, logos, meter rings and underline squiggles, each
   with a drawing of its own, and none of them is touched. */
svg[viewBox="0 0 24 24"]{stroke-width:var(--icon-w)}
svg[viewBox="0 0 24 24"] :is(path,rect,circle,line,polyline,polygon,ellipse){
  vector-effect:non-scaling-stroke}

/* ---------- one image zoom, for every card that has one ---------- */
/* Three families had each grown their own: `.inner-zoom` at 1.06 in the tiles,
   `.zoomable` at 1.09 in the gallery, and in the post card an `<i>` that the
   art plates had since replaced -- so the post grid had quietly stopped zooming
   at all while the two beside it still did, at two different speeds. The box
   that clips is `.zoom`; whatever it contains is what moves; the amount is one
   token. A tile can be the hover target itself or sit inside a card that is. */
.zoom{overflow:hidden}
.zoom>*{transition:transform .62s var(--ease-out)}
/* An overlay -- a label or a type marker sitting on the corner of the picture --
   is inside the clipping box but is not part of the picture, so it stays put
   while the image behind it grows. Named rather than inferred: the two tiles
   that carry one had been left without a `.zoom` box altogether to avoid this,
   which meant two tiles in a row of six quietly did not zoom at all. */
/* A picture only answers the pointer when there is something to click. The
   bare `.zoom:hover` that used to be on the end of this list meant any image in
   a zoom box moved under the cursor whether or not it led anywhere, and P5's
   staff portraits -- three plain cards, no link, no dialog -- grew when you
   passed over them and did nothing when you clicked. A tile that is a link says
   so with the anchor; one that opens something else says so with `.zoom-host`.
   Nothing else in the library was relying on the bare form. */
a:hover .zoom>*:not(.ov-label):not(.ov-ic),
.zoom-host:hover .zoom>*:not(.ov-label):not(.ov-ic){transform:scale(var(--zoom,1.06))}
@media(prefers-reduced-motion:reduce){.zoom>*{transition:none}
  a:hover .zoom>*,.zoom-host:hover .zoom>*{transform:none}}


/* ---------- joined frame (signature device) ---------- */
/* The divider colour is a custom property, not a colour repeated in fourteen
   rules. It was repeated, and the dark-ground override then had to out-specify
   every one of them; at the tablet tier one rule carried a `:nth-last-child`
   and won, so on an ink section the horizontal rules were drawn in the ink
   colour and the frame lost half its dividers. A property inherits and does not
   compete, so a ground sets it once and every edge follows.

   The dividers are not borders any more, and that is the whole point. As borders
   they needed a rule per column count per tier -- `border-right` here,
   `nth-child(2n)` there, `nth-last-child(n+3)` for the last row -- and those
   rules escalated in specificity as the tiers narrowed, so the phone tier could
   not undo the tablet tier. On a phone a four-cell band kept a hairline down the
   right of a cell that had no neighbour and lost the one between the third and
   fourth. It had been fixed once per family and come back each time, because
   nothing about the shape was written down in one place.

   So: a one-hairline gap, and every cell casting a hard shadow into the gap to
   its right and the gap below it. The trailing column's and the last row's
   shadows fall in the frame's own border and are clipped by the overflow that
   is already there. No cell has to know how many columns there are, which row it
   is in, or how many siblings follow it, so there is nothing left to get wrong
   at a breakpoint -- the tiers now only say how many columns, and the dividers
   follow. It also survives a hover fill: the line lives outside the cell's box
   rather than under the next cell's background.

   `gap` is set explicitly rather than left to inherit. `.joined.stack` was
   picking up `gap:34px` from the Buttons family's `.stack`, which put 34px of
   nothing under every divider in the services rail and the office panel. */
.joined{--j-line:var(--line-dark);display:grid;gap:var(--frame-w);
  border:var(--frame-w) solid var(--j-line);border-radius:var(--r-card);
  overflow:hidden;background:var(--paper)}
.sec-green .joined,.sec-ink .joined,.on-ink .joined{--j-line:var(--on-ink-line);
  background:transparent;border-color:var(--on-ink-border)}
.joined>.cell{padding:38px 32px;transition:background .3s;
  box-shadow:var(--frame-w) 0 0 var(--j-line), 0 var(--frame-w) 0 var(--j-line)}
/* the tiers say how many columns and nothing else */
.joined.cols-1{grid-template-columns:1fr}
.joined.cols-2{grid-template-columns:1fr 1fr}
.joined.cols-3{grid-template-columns:repeat(3,1fr)}
.joined.cols-4{grid-template-columns:repeat(4,1fr)}
/* One column on purpose, for a rail beside something rather than a band across
   a page. */
.joined.stack{grid-template-columns:1fr}
/* hover states belong to interactive tiles only: anchors, or .linked stand-ins */
a.joined>.cell:hover,.joined>a.cell:hover,.joined>.cell.linked:hover{background:var(--tint)}
.sec-green a.joined>.cell:hover,.sec-green .joined>a.cell:hover,.sec-green .joined>.cell.linked:hover,
.sec-ink a.joined>.cell:hover,.sec-ink .joined>a.cell:hover,.sec-ink .joined>.cell.linked:hover{background:var(--on-ink-fill)}

/* The frame's own responsive behaviour, in one place, and now only ever a
   column count. Two families used to keep private copies of this at 900 and
   560px, which is why the dividers changed character at widths nothing else in
   the library reacts to. */
@media(max-width:1000px){
  .joined.cols-4{grid-template-columns:1fr 1fr}
  .joined.cols-3{grid-template-columns:1fr}
}
@media(max-width:640px){
  .joined.cols-2,.joined.cols-3,.joined.cols-4{grid-template-columns:1fr}
  .joined>.cell{padding:28px 24px}

  /* Switch: hold two columns on a phone. A cell carrying a sentence has to go
     to one column or the words run out of room, but a cell carrying a number
     and a label does not, and four numbers in a single stack read as a list
     rather than as a set. */
  .joined.keep-2.cols-4,.joined.keep-2.cols-2{grid-template-columns:1fr 1fr}
  .joined.keep-2>.cell{padding:24px 18px}
}

/* icon chip used inside cards */
/* --tint-deep, not --tint: the card's own hover is --tint, and a chip resting on
   its parent's hover colour dissolves at the moment it should stand out */
.card-ic{width:var(--ic-lg);height:var(--ic-lg);border-radius:50%;background:var(--tint-deep);color:var(--green);display:grid;place-items:center;margin-bottom:20px;transition:background var(--t-fast),color var(--t-fast),transform var(--t-fast) var(--ease-out)}
a.cell:hover .card-ic,.cell.linked:hover .card-ic,a.card:hover .card-ic,.card.linked:hover .card-ic{background:var(--green);color:#fff;transform:rotate(-6deg) scale(1.05)}
/* an icon placed in a card by hand is the chip by another name and takes the
   chip's rhythm above whatever follows it; without this a mark and a heading
   measured 0px apart in every icon tile of the first real site (B6) */
.card .ic-round,.cell .ic-round{margin-bottom:20px}

/* standalone card — hover only when the tile itself is a link (or .linked stand-in) */
/* A column, not a block, so the read-more can sit at the bottom. A row of cards
   whose paragraphs run to different lengths put its links at four different
   heights, which is the detail that makes a grid look assembled rather than
   designed -- and it had already been fixed once, for the tiles, and not carried
   across to the plain card. Block children stretch in a flex column exactly as
   they do in a block, so nothing else moves. */
/* ---------- the card ----------
   The most-used component in the library, and until the manifest asked every
   block what it leans on, the only one with no named home: it had been living
   inside the joined frame's section since round one. */
.card{display:flex;flex-direction:column;align-items:stretch;background:var(--paper);border:var(--frame-w) solid var(--line-dark);border-radius:var(--r-card);padding:34px 30px;transition:background .3s,box-shadow .3s}
.card>.link-more,.card>.btn-row,.card>.btn{margin-top:auto}
.card>.link-more,.card>.btn-row{padding-top:18px}
.card>.link-more,.card>.btn{align-self:flex-start}
/* Heading level is document structure; heading size is component design. A card
   that is genuinely a top-level topic of its page should be free to say <h2>
   without growing to 2.9rem, so the card fixes the size and leaves the level
   to the page. Only h2: h3 is already the card's size and an h4 inside a card
   is a deliberate sub-heading, which an earlier version of this rule promoted
   by five pixels in three families before the snapshot caught it. */
.card h2{font-size:var(--fs-h3)}
/* A heading and the paragraph under it are two things, not one. The card
   component never said so: the rhythm lived in `.tile-basic`, a modifier the
   tiles family happens to use, so the first page that wrote a plain `.card`
   with a heading and a paragraph in it got them touching. Nine pixels, on the
   card itself, at one class so any component that wants its own figure -- the
   tier, the horizontal tile, the team card -- still overrides it by writing
   later. */
.card h2,.card h3,.card h4{margin-bottom:9px}
/* and a cell of the joined frame, which never said so either: a joined tile's
   heading measured 0px from the line under it on the live site, in every cell of
   the practical section, until the 404's three cards were drawn from it (14.7).
   The rail panel's cell keeps its own figure, which outranks this. */
.cell h2,.cell h3,.cell h4{margin-bottom:9px}
/* A card is often the whole link. `.tile-img` and `.post` each remembered to say
   so and the plain card did not, so the first page to use `<a class="card">`
   got four cards of underlined green body copy -- the browser doing exactly
   what it should to an anchor nobody had dressed. The reset belongs with the
   component, not with the pages that happen to link one. */
a.card{text-decoration:none;color:inherit}
/* the joined frame's cell is a link as often, and the whole frame sometimes */
a.cell,a.joined{text-decoration:none;color:inherit}
a.card:hover,.card.linked:hover{background:var(--tint)}
/* --shadow-frame is what a shadow tile draws instead of its shadow where the
   shadows are switched off: the theme sets it to the hairline colour then, so
   a shadow tile in a row of framed ones is framed like them rather than bare
   (B11, the first real site). Unset, which is the library's own state, it is
   transparent and the shadow does the work. */
.card.shadow{border-color:var(--shadow-frame,transparent);box-shadow:var(--shadow)}
a.card.shadow:hover,.card.shadow.linked:hover{background:var(--paper);box-shadow:var(--shadow-lg)}

/* ---------- media frames ---------- */
.pic-frame{border-radius:var(--r-card);border:var(--line-w) solid var(--line-dark);background:var(--ph);display:grid;place-items:center;color:var(--ph-fg);position:relative;overflow:hidden;box-shadow:var(--shadow)}
/* The caption is one of the nine roles family 01 names, and the only place it
   had ever been drawn is inside a media frame -- where the look and the
   placement were written into one rule, so nothing else could use it. The role
   is declared once and the frame keeps its own placement. */
.cap,.pic-frame figcaption{font-family:var(--serif);font-style:italic;font-size:.9rem;color:var(--ink-faint)}
.pic-frame figcaption{position:absolute;bottom:12px;left:16px}
.pic-frame.plain{border:0;box-shadow:none}

/* ============================================================
   Shared layout utilities.
   ============================================================
   Every one of these was declared separately by two to six families, with
   different values, on names generic enough that whichever stylesheet loaded
   last decided what a page looked like. In the prototype each family is a page
   of its own so nothing showed; in WordPress every block ships its own
   stylesheet onto the same page, and the last one wins. `tools/collisions.py`
   is the check that keeps them here.

   Where the families disagreed the value is the one most of them used, and the
   two that wanted something else say so with a modifier rather than by
   redeclaring the name.
   ============================================================ */
/* no align-items here: a card grid wants equal heights, which is the default,
   and the one family that wanted its cells topped out says so with .top. */
.grid-1,.grid-2,.grid-3,.grid-4{display:grid;gap:var(--grid-gap,24px)}
.grid-1.top,.grid-2.top,.grid-3.top,.grid-4.top{align-items:start}
.grid-1{grid-template-columns:1fr}
.grid-2{grid-template-columns:1fr 1fr}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-4{grid-template-columns:repeat(4,1fr)}
.gap-s{--grid-gap:20px}
.gap-l{--grid-gap:44px}
/* the library's three tiers, not the four different ones the six copies used */
@media(max-width:1000px){.grid-3,.grid-4{grid-template-columns:1fr 1fr}}
@media(max-width:640px){.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}.gap-s{--grid-gap:14px}}

/* a reading measure for a block that should not run the full container width.
   Six families declared it at 820, 860 or 880px; the difference was never a
   decision, so it is one number, and since 14.28 it is a token, because a
   Section can be set to it and the two must not drift. */
.half{max-width:var(--container-narrow)}

/* a line of small print under something */
.note{font-size:.9rem;color:var(--ink-soft);max-width:54rem}
.note-gap{margin-top:18px}
/* small status pills, and a row of them */
.pill{font-size:.8rem;background:var(--tint);color:var(--green-deep);padding:5px 12px;border-radius:var(--r-btn)}
.pillrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}

/* a labelled form field */
.fld{display:grid;gap:6px;margin-bottom:16px}
/* `color` is set, and it was not. Every field in this library has only ever
   held a placeholder, which is the browser's grey, so nobody ever saw what
   typed text looked like: the browser's own black, which is the one black on a
   site whose every other word is --ink. Found on the first real form. */
.fld input,.fld textarea,.fld select{font:inherit;padding:12px 16px;border-radius:var(--r-inner);
  border:var(--line-w) solid var(--line);background:#fff;color:var(--ink);outline:none;width:100%;
  transition:border-color .2s,box-shadow .2s}
/* A text field is the one control where the shared amber ring is wrong, and for
   the same reason the newsletter field already opts out: the ring floats three
   pixels off the box while you are typing inside it, and it appears on a mouse
   click as well as a Tab, because a browser treats focus on a text field as
   keyboard focus by definition. The field states itself instead -- the border
   goes green and doubles -- which is a contiguous indicator on the control and
   clears the contrast floor. */
.fld input:focus,.fld textarea:focus,.fld select:focus{border-color:var(--green)}
.fld input:focus-visible,.fld textarea:focus-visible,.fld select:focus-visible{
  outline:none !important;box-shadow:0 0 0 var(--line-w) var(--green)}

/* a full-screen overlay. The header and the system pages had one each, three
   hundredths of a second apart. */
.ov{position:fixed;inset:0;z-index:120;visibility:hidden;pointer-events:none;transition:visibility .42s}
.ov.open{visibility:visible;pointer-events:auto}

/* the container for a run of post list rows, whose row is already here */
/* The list is a query container, so the row below answers the width it is
   actually given rather than the width of the window. Same reasoning as the
   file row in d6, and the same payoff: the row is correct in a sidebar at
   1440px, which a media query cannot express at all. */
.p-list{display:grid;max-width:920px;container-type:inline-size;container-name:postlist}
.p-list.full{max-width:none}
/* A row a pager has not reached yet. Both Posts and the search results hide one
   the same way, and the row itself is already here.
   The doubled class is deliberate, and it is the same trick `.f-item.f-item`
   already uses in this file: whatever the row is — a list row here, a card in a
   family stylesheet that loads later — it has a `display` of its own at one
   class, and one class cannot beat one class arriving afterwards. */
.more-row.more-row{display:none}

/* the inline newsletter form. The footer's and the CTA's differed in every
   value; this is the footer's, which is the later design, with the colours
   taken from custom properties so a green band and an ink band can each set
   their own without redeclaring the component. */
.nl-form{display:flex;gap:12px;flex-wrap:wrap;margin-top:16px;max-width:520px;width:100%}
.nl-form input{flex:1 1 220px;min-width:0;height:52px;font:inherit;font-size:.97rem;padding:0 20px;
  border-radius:var(--r-btn);border:var(--line-w) solid var(--nl-line,var(--on-ink-border));
  background:var(--nl-fill,var(--on-ink-fill));color:#fff;outline:none;
  transition:border-color .2s,background .2s}
.nl-form input::placeholder{color:var(--nl-hint,var(--on-ink-faint))}
.nl-form input:focus{border-color:#fff;background:var(--nl-fill-hi,var(--on-ink-fill-hi))}
/* A text field is the one control where the shared amber ring is wrong: it
   floats 3px off a 999px pill while you are typing inside it, and the field
   already has a focus state of its own. So the ring is replaced rather than
   removed -- the border goes white and doubles to a 2px perimeter, which is
   its own contiguous indicator against the dark fill and still clears the
   contrast floor. Removing it outright would not. */
.nl-form input:focus-visible{outline:none !important;
  box-shadow:0 0 0 var(--line-w) #fff}
/* the line form's button keeps the field's 52px, where the house's control height made it 56 (13.5) */
.nl-form .btn{flex:none;height:52px;min-height:52px;padding:0 28px;white-space:nowrap}
@media(max-width:640px){.nl-form .btn{width:100%;justify-content:center}}
/* A one-line form in a column too narrow for the field and its button side by
   side: the button wraps under the field and takes that line whole, as the
   footer's version c draws its sign-up in a column (14.7). Asked of the form's
   own width rather than the screen's, because a column is narrow on a desktop. */
.form-line{container-type:inline-size}
@container (max-width:420px){.nl-form .btn{width:100%;justify-content:center}}

/* ---------- lists ---------- */
.num-list{list-style:none;margin:0;counter-reset:nl}
.num-list li{display:flex;gap:18px;padding:17px 0;border-top:var(--line-w-soft) solid var(--line);align-items:baseline;counter-increment:nl}
.num-list li:last-child{border-bottom:var(--line-w-soft) solid var(--line)}
.num-list li::before{content:counter(nl,lower-roman) ".";font-family:var(--serif);font-style:italic;color:var(--green);flex:none;font-size:1.1rem;min-width:1.6em}
.num-list b{font-weight:600}
.num-list span{color:var(--ink-soft)}

/* ---------- accordion ---------- */
.acc-list{border:var(--line-w) solid var(--line-dark);border-radius:var(--r-card);overflow:hidden;background:var(--paper)}
.acc{border-bottom:var(--line-w) solid var(--line-dark)}
.acc:last-child{border-bottom:0}
.acc button{all:unset;display:flex;width:100%;justify-content:space-between;align-items:center;gap:16px;padding:24px 28px;cursor:pointer;box-sizing:border-box;transition:background .25s}
.acc:not(.open) button:hover{background:var(--tint)}
.acc .q{font-family:var(--serif);font-size:1.35rem;font-weight:520}
.acc .chev{flex:none;width:var(--ic-sm);height:var(--ic-sm);border-radius:50%;border:var(--line-w) solid var(--line-dark);display:grid;place-items:center;transition:transform .35s var(--ease-out),background .25s,color .25s;background:var(--paper)}
.acc.open .chev{transform:rotate(180deg);background:var(--green);border-color:var(--green);color:#fff}
.acc.open button:hover .chev{background:var(--green-deep);border-color:var(--green-deep);transform:rotate(180deg) scale(1.1)}
.acc-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .42s var(--ease-out)}
.acc.open .acc-body{grid-template-rows:1fr}
.acc-body>div{overflow:hidden;min-height:0}
.acc-body p{padding:0 28px 26px;font-size:.98rem;max-width:44rem}
/* Rich text inside an accordion body: the wrapper pads instead of each
   paragraph. It lived in family 09, which meant any page using the accordion
   outside that family got desktop padding on a phone and no list styling at
   all. `.acc-body` and `.rich` are both components of this file; a rule about
   one inside the other belongs here too. */
.acc-body .rich{padding:0 28px 28px;max-width:52rem;display:grid;gap:14px;overflow:visible}
.acc-body .rich p{padding:0;margin-bottom:0;font-size:.98rem}
.acc-body .rich ul{margin:0 0 0 2px;padding-left:0;list-style:none;display:grid;gap:9px}
.acc-body .rich li{display:flex;gap:11px;align-items:flex-start;font-size:.97rem;color:var(--ink-soft)}
.acc-body .rich li::before{content:"";flex:none;width:7px;height:7px;border-radius:50%;background:var(--green);margin-top:8px}
/* on a dark ground (14.13): the words the dim white and the marks the flourish,
   where they had kept the paper's grey on the ink */
:is(.sec-green,.sec-ink,.on-ink) .acc-body .rich li{color:var(--on-ink-dim)}
.sec-green .acc-body .rich li{color:var(--on-ink-dim-green)}
:is(.sec-green,.sec-ink,.on-ink) .acc-body .rich li::before{background:var(--amber-soft)}

/* The accordion had no phone tier at all: a 38px icon, a 1.35rem serif question
   and a 36px chevron inside 28px of padding leaves about 150px for the words on
   a 350px screen, which is why the questions were breaking into three cramped
   lines between two circles. Everything steps down together. */
@media(max-width:640px){
  .acc button{padding:18px 18px;gap:12px}
  .acc .q{font-size:1.12rem;line-height:1.3}
  .acc .chev{width:32px;height:32px}
  .acc .chev svg{width:15px;height:15px}
  .acc-body p{padding:0 18px 20px}
  /* the rich-text variant pads its own wrapper instead of each paragraph. It
     lived in family 09 with the rest of that variant, which meant any page
     using the accordion outside that family kept desktop padding on a phone. */
  .acc-body .rich{padding:0 18px 22px}
}

/* ---------- slider engine (shared: Sliders, Timeline strip, Logo Bar, Posts, Gallery) ---------- */
.slider{position:relative;--n:1;--sl-gap:24px;--peek:0px}
.sl-track{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - var(--peek) - (var(--n) - 1)*var(--sl-gap)) / var(--n));gap:var(--sl-gap);overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;cursor:grab}
.sl-track::-webkit-scrollbar{display:none}
.sl-track.dragging{cursor:grabbing;user-select:none}
.sl-track.dragging,.sl-track.settling{scroll-snap-type:none}
.sl-track>*{scroll-snap-align:start;min-width:0}
/* and a picture, which a browser lets a pointer pick up and carry off: a card
   with a real photograph in it dragged its image instead of the row (14.6) */
.sl-track a,.sl-track img{-webkit-user-drag:none;user-select:none}
.sl-nav{display:flex;align-items:center;gap:16px;margin-top:28px}
.icon-chip[disabled]{opacity:.32;pointer-events:none}
.sl-dots{display:flex;gap:8px;align-items:center}
/* The two dot colours are declared once, on the group, so a family or a section
   changes them by setting two custom properties instead of restating four
   rules. The dark grounds below do exactly that. */
.sl-dots{--sl-idle:#C9D2C9;--sl-hot:var(--ink-soft);--sl-on:var(--green)}
.sl-dot{all:unset;cursor:pointer;width:9px;height:9px;border-radius:var(--r-btn);background:var(--sl-idle);
  transition:background .25s,width .3s var(--ease-out);position:relative;overflow:hidden}
.sl-dot:hover{background:var(--sl-hot)}
.sl-dot.on{background:var(--sl-on);width:26px}
/* The autoplay indicator: the active dot IS the progress bar. The pill becomes
   an empty track in the idle colour and `i` is the elapsed part, filling it
   from the left in the accent. Painting the unelapsed part over the accent
   instead, which is what this did first, meant a translucent light over a light
   amber: two shades of the same colour and no readable edge between them. The
   pill is also longer while it is counting, because a two-tone bar needs room
   to be a bar. */
.sl-dot i{position:absolute;inset:0;border-radius:inherit;background:var(--sl-on);
  transform-origin:left;transform:scaleX(0);opacity:0}
.slider.has-auto .sl-dot.on{background:var(--sl-idle);width:42px}
.slider.has-auto .sl-dot.on i{opacity:1;animation:sl-fill var(--sl-delay,6s) linear forwards;animation-play-state:paused}
.slider.has-auto.is-playing .sl-dot.on i{animation-play-state:running}
@keyframes sl-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@media (prefers-reduced-motion: reduce){.sl-dot i{display:none}}
/* The pause control. WCAG 2.2.2 asks for a mechanism to stop anything that
   moves for more than five seconds, and hovering is not a mechanism for
   everyone. It was in family 16 where only that family could use it; it belongs
   with the engine that reads it. */
.sl-pause{position:relative}
/* put away by ui.js when the slider is not going to play (14.6) */
.icon-chip.sl-pause[hidden]{display:none}
/* and the film's, which is the same control with the same two faces (14.3) */
:is(.sl-pause,.film-pause) :is(.p-ic,.g-ic){position:absolute;inset:0;display:grid;place-items:center;transition:opacity .2s,transform .3s var(--ease-out)}
:is(.sl-pause,.film-pause) .g-ic{opacity:0;transform:scale(.6)}
:is(.sl-pause,.film-pause).paused .p-ic{opacity:0;transform:scale(.6)}
:is(.sl-pause,.film-pause).paused .g-ic{opacity:1;transform:scale(1)}
.sl-count{font-family:var(--serif);font-style:italic;font-size:1.05rem;color:var(--ink-soft);white-space:nowrap}
.sl-count b{color:var(--ink);font-weight:540;font-style:normal}
.sl-prog{position:relative;height:2px;background:var(--line);border-radius:var(--r-btn);flex:1;overflow:hidden;min-width:80px}
.sl-prog i{position:absolute;inset:0;background:var(--green);transform-origin:left;transform:scaleX(0);transition:transform .15s linear}
/* fade mode: slides stack and crossfade instead of scrolling */
.slider[data-fade] .sl-track{display:grid;grid-auto-flow:row;grid-auto-columns:auto;grid-template-columns:1fr;overflow:visible;cursor:auto}
.slider[data-fade] .sl-track>*{grid-area:1/1;opacity:0;transition:opacity .55s var(--ease-out);pointer-events:none}
.slider[data-fade] .sl-track>.on{opacity:1;pointer-events:auto;z-index:1}
/* A cross dissolve, not two simultaneous fades. Fading one slide out while the
   next fades in leaves both at half opacity halfway through, and whatever is
   behind them shows: on a dark hero that is a bright flash in the middle of
   every transition. The outgoing slide holds full opacity underneath while the
   incoming one fades in on top of it, and only lets go once it is covered. */
.slider[data-fade] .sl-track>.was{opacity:1;z-index:0;transition:none}
/* And if the engine never runs, the first slide is the one showing. `.on` is
   put on by ui.js, so a page whose script is blocked, still loading or thrown
   leaves every slide at opacity 0 -- which on the home page is a full screen of
   nothing where the opening should be. The question is asked directly: while no
   slide is marked, the first one shows. The moment the engine marks one, the
   rule stops matching and hands the slider back. */
.slider[data-fade] .sl-track:not(:has(>.on))>:first-child{opacity:1;pointer-events:auto}
/* ---------- a card slider's own choices (14.6) ----------
   How many cards are in view at each size. The card sliders in Sliders and
   Posts wrote three, two and one into their own family sheets; a block that
   offers the counts needs them as classes, on the Columns block's three tiers,
   so they are here. Whether the next card shows is `.edge`, below. */
.slider.per-1{--n:1}.slider.per-2{--n:2}.slider.per-3{--n:3}.slider.per-4{--n:4}
@media(max-width:1000px){.slider.per-t1{--n:1}.slider.per-t2{--n:2}.slider.per-t3{--n:3}}
@media(max-width:640px){.slider.per-p1{--n:1}.slider.per-p2{--n:2}}
/* the dots a little apart from the arrows they follow, which demo a wrote inline */
.sl-next+.sl-dots{margin-inline-start:6px}
/* ---------- a button in the row of controls (14.110) ----------
   Asked for in review: a Button beside the arrows, at the row's far end and as
   tall as their circles, Outline unless said, so at rest it reads as one more of
   the row's controls; any Button's style, icon and link. When the row has no
   room for it, it goes under the arrows on a line of its own, at the start,
   which ui.js marks with .act-under; a phone's row keeps arrows, dots and a
   short button together whenever they fit. With every card in view the arrows
   go and the button stays, at the row's end. */
.sl-nav:has(.sl-act){flex-wrap:wrap;row-gap:14px}
.sl-act{margin-inline-start:auto;display:flex;min-width:0;max-width:100%}
.sl-nav.act-under .sl-act{margin-inline-start:0;flex-basis:100%}
.sl-act .btn:not(.link-more){min-height:var(--ic,var(--ic-md));height:var(--ic,var(--ic-md));padding-block:0}
.slider.is-still .sl-nav:has(.sl-act){display:flex}
.slider.is-still .sl-nav:has(.sl-act)>:not(.sl-act){display:none}
/* ---------- a slider that goes round (14.110) ----------
   A slider that plays itself runs on from its last card to its first rather
   than stopping there or winding back across the row: ui.js lays a copy of the
   cards either side of them and, once a move has landed on a copy, puts the row
   back on the card it copies, where it looks the same. The copies are for the
   eye alone; with every card in view there is nothing to go round and they are
   put away. */
.slider.loop-idle .sl-clone{display:none}
/* Nothing to move. Every card is in view at this size, so the arrows could only
   ever be disabled and there would be one dot: a control for nothing. ui.js
   marks the slider, and marks it again when a resize changes the answer, so
   the same three cards have their arrows back on a tablet. */
.slider.is-still .sl-nav{display:none}

/* ---------- a band to the screen's edges (14.10) ----------
   The Sliders family's full-bleed option, its demo b, built on the Gallery's
   slider as "To the screen's edges". The row breaks out of the column to both
   edges of the screen, and the neighbours of the picture in view show past it
   on either side by the peek. The track's padding is the column's inset from the
   screen, and its scroll padding the same: snapping aligns a slide to the edge
   of the scroll port and not to the padding, so without it the first picture sat
   flush against the left of the screen with nothing beside it, which is how
   demo b drew itself until this round measured it. The controls stay in the
   column. The section clips what the band sends past the screen, so a scroll
   bar's width never becomes a sideways scroll. How many are in view is still
   the slider's own choice; the band is its own peek, so it carries no .edge.
   Its first picture and its arrows stand on the line the words start on, the
   container's edge plus its gutter, since 14.99, from review: they stood on
   the container's outer edge, 32 pixels left of the heading at a desk and 12
   right of it on a phone, where the gutter is 20 and the floor was 32. */
.slider.bleed{--sl-gap:20px;--peek:min(12vw,150px);--bleed:calc(max(0px,(100vw - var(--container)) / 2) + var(--gutter));margin-inline:calc(50% - 50vw)}
.slider.bleed .sl-track{padding-inline:var(--bleed);scroll-padding-inline:var(--bleed)}
.slider.bleed .sl-nav{max-width:var(--container);margin:26px auto 0;padding-inline:var(--gutter);gap:20px}
.sec:has(.slider.bleed){overflow-x:clip}
/* Its pictures: at the card's corners and in the shape the gallery chose.
   The width is the track's: a box with a ratio in a grid takes its width from
   its height rather than stretching, so a picture held to 300 pixels was 630
   wide on a phone and lay over its neighbour, in demo b as drawn. The 300
   pixels themselves went in 14.97, from review: three in view at 16:9 drew
   343 by 300, near square, because a floor on the height beats the ratio
   wherever the picture is narrow. The shape chosen is the shape drawn. */
.slider.bleed .g-tile{width:100%;border-radius:var(--r-card);overflow:hidden}
.slider.bleed .g-tile:hover{transform:none;box-shadow:none}
.g-tile.ar-21-10{aspect-ratio:21/10}
/* a caption over the picture's foot, on a shade of the ink, and the picture's
   number at its other end; doubled, because a caption on a dark ground is dimmed
   and this one is always on its shade */
.slider.bleed .g-fig{position:relative}
.slider.bleed.bleed .g-fig figcaption{position:absolute;left:0;right:0;bottom:0;margin:0;padding:44px 30px 20px;background:linear-gradient(transparent,color-mix(in srgb,var(--ink) 55%,transparent));color:#fff;font-family:var(--serif);font-style:italic;font-size:1rem;line-height:1.35;display:flex;justify-content:space-between;align-items:baseline;gap:20px;border-radius:0 0 var(--r-card) var(--r-card);pointer-events:none}
.slider.bleed .g-no{white-space:nowrap}
@media(max-width:640px){.slider.bleed.bleed .g-fig figcaption{padding:36px 20px 16px;font-size:.92rem}}

/* ---------- a picture that fills the box it is in ----------
   `img.plate` is in the markup of five families -- the hero slides, the event
   page's media frames, the gallery tiles, the quote portrait and the video
   poster -- and its rule lived in compose.css, which no longer exists. Four of
   the five happened to carry a more specific rule of their own and never
   noticed. The fifth did not: the video poster went into the figure's grid at
   its natural 1024px, took a row of its own, and pushed the play chip out of the
   bottom of the frame.

   `grid-area` rather than `position:absolute`, because it is ignored outside a
   grid and does not disturb the four that already position themselves. In a
   grid it puts the picture in the same cell as whatever is drawn over it, which
   is what every one of these frames wants. */
img.plate{display:block;width:100%;height:100%;object-fit:cover;grid-area:1/1}
/* ---------- a section on the page's middle line (14.7) ----------
   For a short statement that asks for one thing: a page not found, a thank you,
   a single ask. What the 404's own wrapper did, as a Section's choice: the words
   sit on the middle line, anything with a measure of its own keeps it there, a
   row of buttons, chips or large links gathers in the middle, and a card, a cell
   or the section head keeps its words at its own start, as the 404's three cards
   always have. */
.sec-center>.container{text-align:center}
.sec-center>.container>*{margin-inline:auto}
.sec-center .lead{max-width:38rem;text-wrap:balance}
.sec-center :is(.btn-row,.e-big,.chiprow-l){justify-content:center}
.sec-center :is(.card,.cell,.sec-head){text-align:start}
/* Its rhythm, which the 404's wrapper kept as margins of its own: the big number
   close over the heading, the lead close under it, and a row of actions, chips or
   large links given room above and more below. Written between neighbours rather
   than on the container's children, so it holds wherever a builder puts the words
   one wrapper deeper. */
.sec-center .big-num+*{margin-top:22px}
.sec-center :is(h1,h2,h3)+.lead{margin-top:12px}
.sec-center *+:is(.btn-row,.e-big,.chiprow-l){margin-top:34px}
.sec-center :is(.btn-row,.e-big,.chiprow-l)+*{margin-top:52px}
/* ---- a pattern over a section (14.95) ----
   Thin lines, a grid or dots over a section, on top of whatever is under it:
   its ground, its colour, its photograph and the shade over the photograph,
   its film. Only the words are above it. Five kinds, in any of the site's own
   colours at a strength from 2 to 40 per cent, so the same pattern reads on
   paper, on the accent and over a picture (Z, 27 September; the diagonals are
   14.86's lines of work in progress, which the placeholder page still wears).

   It is a layer of its own, `.tex`, placed after the section's picture and
   film and before its words, never first in the section, so the drift that
   moves a photograph moves the photograph alone. The photograph stands at 0,
   its shade at 1, the pattern at 2, and everything else in the section at 3,
   so the pattern lies over the shade rather than under it. The section never
   clips, so a column that stays in view still does.

   An outline button on a light ground keeps the ground inside it, so no line
   runs through its words; its hover is the house's own. */
.has-tex{position:relative;isolation:isolate;--tex-fill:var(--paper)}
.has-tex>.tex{position:absolute;inset:0;z-index:2;pointer-events:none;
  --tex-ink:color-mix(in srgb,var(--tex-c,var(--green)) var(--tex-o,8%),transparent)}
.has-tex>:not(.tex):not(.bg):not(.bg-pic):not(.bgph):not(.film-pause){position:relative;z-index:3}
.tex-c-accent{--tex-c:var(--green)}
.tex-c-ink{--tex-c:var(--ink)}
.tex-c-paper{--tex-c:var(--paper)}
.tex-c-amber{--tex-c:var(--amber)}
.tex-diagonals{background:repeating-linear-gradient(-45deg,var(--tex-ink) 0 var(--line-w-strong),transparent var(--line-w-strong) 22px)}
.tex-crosshatch{background:repeating-linear-gradient(45deg,var(--tex-ink) 0 var(--line-w-soft),transparent var(--line-w-soft) 18px),
  repeating-linear-gradient(-45deg,var(--tex-ink) 0 var(--line-w-soft),transparent var(--line-w-soft) 18px)}
.tex-grid{background:linear-gradient(var(--tex-ink) var(--line-w-soft),transparent var(--line-w-soft)) 0 0/24px 24px,
  linear-gradient(90deg,var(--tex-ink) var(--line-w-soft),transparent var(--line-w-soft)) 0 0/24px 24px}
.tex-dots{background:radial-gradient(circle,var(--tex-ink) 1.5px,transparent 2px) 0 0/16px 16px}
.tex-stripes{background:repeating-linear-gradient(0deg,var(--tex-ink) 0 var(--line-w-soft),transparent var(--line-w-soft) 20px)}
.sec-tint.has-tex{--tex-fill:var(--tint)}
.sec-warm.has-tex{--tex-fill:var(--tint-2)}
.has-tex:not(.sec-pic):not(.sec-green):not(.sec-ink):not(.sec-custom) .btn-line:not(:hover){background:var(--tex-fill)}
/* a page header wearing one is a page of its own: the pattern runs on to what follows, with no hairline across it */
.hero-page.has-tex{border-bottom:0}

/* on the middle line, a trail is centred too, and a list set two to a row
   keeps its lines at their start and at a reading width (14.86) */
.sec-center .crumbs{justify-content:center;margin-bottom:28px}
.sec-center :is(.ilist,.linklist).cols-2{text-align:start;max-width:34rem}

/* ---------- large links in a row (14.7) ----------
   The pages somebody was probably after, set large in the display serif, each
   behind its chevron, the underline drawing in under the pointer. The 404's,
   moved out of the system family so a Marked list can draw it anywhere, and so
   the 404 can be a list rather than a menu nobody finds. */
.e-big{display:flex;flex-wrap:wrap;gap:10px 30px;list-style:none}
.e-big a{display:inline-flex;align-items:center;gap:9px;font-family:var(--serif);font-size:clamp(1.25rem,2vw,1.7rem);line-height:1.2;text-decoration:none;color:var(--ink);transition:color .25s}
/* three sizes (14.101), from review, where it had one: regular is the 404's row as
   drawn, small a row for a footer or a card, large one that is the whole section */
.e-big.size-s{gap:8px 24px}
.e-big.size-s a{font-size:clamp(1.08rem,1.5vw,1.3rem)}
.e-big.size-s .cv{width:13px;height:13px}
.e-big.size-l{gap:12px 38px}
.e-big.size-l a{font-size:clamp(1.5rem,2.8vw,2.25rem)}
.e-big.size-l .cv{width:18px;height:18px}
.e-big .cv{flex:none;color:var(--green);transition:transform .3s var(--ease-out)}
.e-big a:hover .cv{transform:translateX(3px)}
.e-big .t{position:relative;padding-bottom:3px}
.e-big .t::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1.5px;background:var(--green);clip-path:inset(0 100% 0 0);transition:clip-path .32s var(--ease-out)}
.e-big a:hover{color:var(--green)}
.e-big a:hover .t::after{clip-path:inset(0 0 0 0)}
@media (prefers-reduced-motion: reduce){.e-big .t::after{transition:none}}
:is(.sec-green,.sec-ink,.on-ink) .e-big a{color:var(--on-ink-fg)}
:is(.sec-green,.sec-ink,.on-ink) .e-big :is(.cv,a:hover){color:var(--amber-soft)}
:is(.sec-green,.sec-ink,.on-ink) .e-big .t::after{background:var(--amber-soft)}

/* dark sections */
.sec-green .sl-dots,.sec-ink .sl-dots,.on-ink .sl-dots{
  --sl-idle:var(--on-ink-border);--sl-hot:var(--on-ink-hot);--sl-on:var(--amber-soft)}
.sec-green .sl-count,.sec-ink .sl-count{color:var(--on-ink-dim-green)}
.sec-green .sl-count b,.sec-ink .sl-count b{color:var(--on-ink-fg)}
.sec-green .sl-prog,.sec-ink .sl-prog{background:var(--on-ink-line)}
.sec-green .sl-prog i,.sec-ink .sl-prog i{background:var(--amber-soft)}
.sec-green .icon-chip,.sec-ink .icon-chip,.sec-green .btn-chip,.sec-ink .btn-chip{background:transparent;border-color:var(--on-ink-border);color:var(--on-ink-fg)}
.sec-green .icon-chip:hover,.sec-ink .icon-chip:hover,.sec-green .btn-chip:hover,.sec-ink .btn-chip:hover{background:var(--on-ink-fill-hi);border-color:#fff}

/* ============================================================
   ON-SCROLL MOTION
   Four gestures and nothing else. The restraint is the design: a theme that
   offers a dozen entrance effects produces sites that look cheap, because
   editors reach for variety and variety is what cheap looks like. One gesture
   repeating quietly is what reads as expensive.

     .m-rise      the workhorse. Fades up a little as it arrives.
     .m-fade      the same arrival with no travel, for anything already in place.
     .m-stagger   put it on a container: the children arrive in sequence.
     .m-parallax  media drifts slower than the page. Media only, never text.

   Intensity is two custom properties rather than four presets, so a site is
   tuned once and everything follows. --m-off on <html> stops all of it, which is
   what the WordPress switch writes.

   Two rules that matter more than the effects themselves. Nothing that is
   already on screen at first paint animates: an entrance you did not scroll to
   is a page that looks broken while it assembles itself. And every element ends
   at its resting state with no transform left behind, so nothing is left on a
   composited layer it does not need.
   ============================================================ */
.m-rise,.m-fade,.m-stagger>*{
  opacity:0;
  transition:opacity var(--m-time) var(--ease-out),transform var(--m-time) var(--ease-out)}
.m-rise,.m-stagger>*{transform:translateY(var(--m-travel))}
.m-rise.seen,.m-fade.seen,.m-stagger>.seen{opacity:1;transform:none}
/* the beat between siblings is set per child by ui.js as --i */
.m-stagger>*{transition-delay:calc(var(--i,0) * var(--m-stagger))}

/* while the page is booting nothing eases: whatever is already on screen simply
   is there, and only what you scroll to afterwards moves */
:root.m-boot .m-rise,:root.m-boot .m-fade,:root.m-boot .m-stagger>*{transition:none}

/* a group that arrives together rather than one by one */
.m-stagger.together>*{transition-delay:0s}

/* The opening arrives too, where the site says so (14.91): its kicker, its
   heading, its lead, its buttons, and a split's picture, a beat apart, as
   the page opens. The one entrance that plays on the first paint, which the
   rule above keeps everything else from doing, because an opening is what
   the reader came to and was asked for in review. A CSS animation rather
   than the observer: it starts with the first paint, so nothing shows and
   then hides, it needs no script, and it ends where the page would be
   without it. An opening that said none (m-still) is left alone. */
@keyframes m-arrive{from{opacity:0;transform:translateY(var(--m-travel))}}
:root[data-motion-ends] :is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>*,
:root[data-motion-ends] .sec:not(.m-still)>.hero-split>.media{
  animation:m-arrive var(--m-time) var(--ease-out) both;animation-delay:calc(var(--o,0) * var(--m-stagger))}
:is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>:nth-child(2){--o:1}
:is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>:nth-child(3){--o:2}
:is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>:nth-child(4){--o:3}
:is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>:nth-child(5){--o:4}
:is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>:nth-child(n+6){--o:5}
.sec>.hero-split>.media{--o:3}

/* media that drifts. The transform is written by ui.js; the element only needs
   to be told it may move and to keep its own overflow honest. */
.m-parallax{overflow:hidden}
.m-parallax>img,.m-parallax>.ph,.m-parallax>.pic-frame,.m-parallax>[data-drift]{
  will-change:transform}

/* How far a section's picture drifts (14.77), three steps on the one scale:
   regular is the house's own --m-drift, soft is half of it and strong half as
   much again. Soft is where a section starts, since a picture that moves a lot
   is noticed before the words over it are. */
.m-parallax.drift-soft{--m-drift:var(--m-drift-soft)}
.m-parallax.drift-strong{--m-drift:var(--m-drift-strong)}

/* A picture BEHIND a section (the cover's, the image closing's, an ordinary
   section's photograph) drifts both ways as the section goes by: down while it
   comes up from the bottom of the screen, up as it leaves at the top. So it is
   made taller by the drift at each end and centred on the section. Sized for
   rising alone, as the cover was, a section lower on the page showed a strip
   of bare ground along its top as it came in. Only while ui.js is drifting it
   (.drifting): with motion off the picture sits true, uncropped. */
.m-parallax.drifting>.bg,.m-parallax.drifting>.bg-pic>.bg{
  inset:auto 0;top:calc(var(--m-drift,.12) * -100%);height:calc(100% + var(--m-drift,.12) * 200%);
  will-change:transform}

/* the three ways motion stops: the visitor's own setting, the site's switch, and
   the editor turning it off for one block. All of them land in the same place. */
:root.m-off .m-rise,:root.m-off .m-fade,:root.m-off .m-stagger>*,
.m-none,.m-none *{opacity:1 !important;transform:none !important;transition:none !important}
/* and on paper (14.69): a section waits at opacity 0 until it is scrolled to, and
   since the house motion that is every section, so a page printed from the top
   would print the opening and nothing else */
@media print{
  .m-rise,.m-fade,.m-stagger>*{opacity:1 !important;transform:none !important;transition:none !important}
  :root[data-motion-ends] *{animation:none !important}
}
@media (prefers-reduced-motion: reduce){
  .m-rise,.m-fade,.m-stagger>*{opacity:1;transform:none;transition:none}
  .m-parallax>*,.m-parallax>.bg-pic>.bg{transform:none !important}
  :root[data-motion-ends] :is(.hero-page:not(.m-still)>.container,.sec:not(.m-still)>.hero-ed,.sec:not(.m-still)>.hero-split>div:first-child,.hero-cover:not(.m-still)>.inner)>*,:root[data-motion-ends] .sec>.hero-split>.media{animation:none}
}

/* ---------- horizontal scrollers ----------
   overflow-x:auto forces overflow-y to compute to auto as well, so the element
   becomes a clip box in both directions. A focus ring drawn outside a control's
   box is then cut off top and bottom, and what is left renders as two vertical
   bars either side of the label. Measured on the tab nav: the ring needed 2px
   of room and had 0 above and 1 below. Inside a scroller the ring is drawn
   inset, where nothing can clip it. */
.scroll-x{overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.scroll-x::-webkit-scrollbar{display:none}
.scroll-x :focus-visible,.scroll-hint :focus-visible,.sl-track :focus-visible,
.opts .container :focus-visible{outline-offset:-3px !important}

/* ---------- the media lightbox ----------
   Gallery and Video each built one. The mechanics were the same to the letter;
   what differed was arbitrary: one scrim at 88% and one at 72%, one growing in
   and one sliding up, radii 4px apart. The shell is shared, the scrim stays a
   variable because a photograph and a video want different darkness, and the
   entrance is one gesture. */
.lightbox{position:fixed;inset:0;z-index:100;display:grid;place-items:center;
  background:var(--lb-scrim,rgba(23,37,31,.88));opacity:0;pointer-events:none;transition:opacity .3s}
.lightbox.open{opacity:1;pointer-events:auto}
.lightbox .stage{position:relative;overflow:hidden;transform:scale(.96);transition:transform .35s var(--ease-out)}
.lightbox.open .stage{transform:scale(1)}
@media (prefers-reduced-motion: reduce){.lightbox,.lightbox .stage{transition:none}.lightbox .stage{transform:none}}
/* the stage answers a drag and a swipe (14.15): a finger's pan and a mouse's
   selection are the gesture's, a pinch is still the browser's zoom, and a
   picture is never dragged out of the page */
.lightbox .stage{touch-action:pinch-zoom;-webkit-user-select:none;user-select:none}
.lightbox .stage img{-webkit-user-drag:none}

/* ---------- the modal dialog ----------
   Popup and Team held copy-pasted twins that had already drifted: the close
   button 2px apart, the heading margin 10px apart, the body type a hair apart.
   One shell, and each family keeps only what goes inside it. --pw sets the
   width, .size-s and .size-l are the two presets. */
.pop-ov{position:fixed;inset:0;z-index:100;background:var(--scrim-modal);backdrop-filter:blur(3px);opacity:0;pointer-events:none;transition:opacity .35s;display:grid;place-items:center;padding:24px}
.pop-ov.open{opacity:1;pointer-events:auto}
.pop{position:relative;background:var(--paper);border:var(--line-w) solid var(--line-dark);border-radius:var(--r-card);box-shadow:var(--shadow-lg);width:min(var(--pw,560px),100%);max-height:min(82vh,760px);overflow-y:auto;padding:40px 42px;transform:translateY(22px) scale(.98);opacity:0;transition:transform .4s var(--ease-out),opacity .3s}
.pop-ov.open .pop{transform:none;opacity:1}
.pop .x{all:unset;box-sizing:border-box;cursor:pointer;position:absolute;top:14px;right:14px;width:var(--ic-sm);height:var(--ic-sm);display:grid;place-items:center;border-radius:50%;border:var(--line-w) solid var(--line-dark);background:var(--paper);color:var(--ink);transition:background .2s,color .2s,border-color .2s;z-index:1}
.pop .x:hover{background:var(--green);border-color:var(--green);color:var(--on-ink-fg)}
.pop.size-s{--pw:420px;text-align:center;padding:44px 38px}
.pop.size-l{--pw:860px}
/* a dialog on a phone spends 42px of a 350px screen on padding either side */
@media(max-width:640px){
  .pop-ov{padding:16px}
  .pop{padding:28px 22px}
  .pop .x{top:10px;right:10px}
}
@media (prefers-reduced-motion: reduce){.pop-ov,.pop{transition:none}.pop{transform:none}}

/* ---------- the post list row ----------
   Formalised in the system pages as one component with three switches, then
   forked into Posts as a subset that had already drifted (its arrow had lost
   flex:none). One definition, and the switches travel with it: .media adds a
   thumbnail, .nodate drops the date margin, and .on-ink is the dark skin. */
.post-line{display:grid;grid-template-columns:96px 1fr auto;gap:8px 30px;align-items:center;padding:24px 22px;margin:0 -22px;border-radius:var(--r-tile);text-decoration:none;color:inherit;transition:background .25s;position:relative}
/* The divider used to be drawn by `.post-line+.post-line::before`, which asks
   the wrong question. Adjacency is a DOM fact, so a filtered-out row still
   counted as the row above: filter a month down to one evening and that evening
   kept a hairline over it with nothing above it to divide from, while the gap
   the hidden rows left behind kept theirs too. The rule is now a property each
   row carries, switched off for the first row in a list and -- when a filter bar
   is driving the list -- for whichever row is currently first. */
/* A field of the post itself, under the title (14.36). The end of a row is one
   pill and an arrow and has no room for a price, and under the title is where a
   reader is already looking. The label is quiet and the value is in the words'
   own colour, as on a card. */
.post-line .flds{display:flex;flex-wrap:wrap;gap:4px 14px;margin-top:4px;font-size:.88rem;color:var(--ink-soft)}
.post-line .flds i{font-style:normal;color:var(--ink-faint);margin-right:5px}
.post-line .flds b{font-weight:500;color:var(--ink)}
.post-line::before{content:"";position:absolute;top:0;left:22px;right:22px;height:1px;
  background:var(--line);opacity:var(--pl-rule,1);transition:opacity .25s}
.post-line:first-child{--pl-rule:0}
.f-item.post-line.f-first{--pl-rule:0}
a.post-line:hover{background:var(--tint)}
/* the hovered row's tint wants clean edges, so it drops its own hairline and
   the one belonging to the row below -- looking past up to two filtered-out
   rows to find it, which is as deep as a real list ever needs. */
a.post-line:hover,
a.post-line:hover+.post-line,
a.post-line:hover+.post-line.hide+.post-line,
a.post-line:hover+.post-line.hide+.post-line.hide+.post-line{--pl-rule:0}
.post-line .dt{font-family:var(--serif);font-style:italic;color:var(--amber-ink);font-size:.98rem;line-height:1.3}
.post-line .dt b{display:block;font-style:normal;font-weight:540;font-size:1.5rem;color:var(--ink)}
.post-line .mid h3{font-size:1.35rem;line-height:1.25;margin-bottom:4px}
.post-line .mid p{font-size:.92rem;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
.post-line .mid mark{background:var(--warn-bg);color:inherit;border-radius:calc(var(--r-xs) / 2);padding:0 3px}
.post-line .end{display:flex;align-items:center;gap:16px}
/* the category pill, shared by the list row and the post card */
.cat{display:inline-flex;align-items:center;font-weight:600;font-size:.82rem;line-height:1;color:var(--green);background:var(--tint-deep);border-radius:var(--r-btn);padding:7px 12px;align-self:center;white-space:nowrap}
.post-line .go{width:var(--ic-sm);height:var(--ic-sm);border-radius:50%;border:var(--line-w) solid var(--line-dark);display:grid;place-items:center;color:var(--ink);background:var(--paper);flex:none;transition:background .25s,color .25s,border-color .25s,transform .3s var(--ease-out)}
a.post-line:hover .go{background:var(--green);border-color:var(--green);color:var(--on-ink-fg);transform:translateX(3px)}

/* switch: thumbnail */
.post-line.media{grid-template-columns:96px 128px 1fr auto}
.post-line .thumb{aspect-ratio:4/3;border-radius:var(--r-sm);background:var(--ph);display:grid;place-items:center;color:var(--ph-fg);font-family:var(--serif);font-style:italic;font-size:.7rem;overflow:hidden;text-align:center;padding:4px}
/* switch: no date margin, for result types that have no date */
.post-line.nodate{grid-template-columns:1fr auto}
.post-line.nodate.media{grid-template-columns:128px 1fr auto}

/* the dark skin for the same row */
.on-ink .post-line .dt b{color:var(--on-ink-fg)}
.on-ink .post-line .dt{color:var(--amber-soft)}
.on-ink .post-line .mid h3{color:var(--on-ink-fg)}
.on-ink .post-line .mid p{color:var(--on-ink-dim)}
.on-ink .post-line+.post-line::before{background:var(--on-ink-line-soft)}
.on-ink a.post-line:hover{background:var(--on-ink-fill)}
.on-ink .post-line .mid mark{background:transparent;color:var(--amber-soft);padding:0}
.on-ink .cat{color:var(--amber-soft);background:rgba(240,179,111,.15)}
/* on the accent the amber pill measured 3.9:1 on a post's glass card: the pill
   there is the dark card's label, white on the lighter fill, as a dark tile's
   label is. On the ground itself, a list row's, it keeps its amber (14.13). */
.sec-green .post .cat{color:#fff;background:var(--on-ink-fill-hi)}
.on-ink .post-line .go{border-color:var(--on-ink-border);color:var(--on-ink-fg);background:transparent}
.on-ink a.post-line:hover .go{background:#fff;border-color:var(--on-ink-fg);color:var(--ink)}
.on-ink .post-line .thumb{background:var(--ph-dark);color:var(--on-ink-faint)}

/* The list row on a phone. It lived in family 21 at a breakpoint of its own,
   which meant any page using the row outside that family kept the desktop track
   on a phone. HANDOVER records this exact failure once already: a component and
   the widths it answers to belong in the same file. */
@media(max-width:640px){
  .post-line,.post-line.media{grid-template-columns:1fr;padding:20px 16px;margin:0 -16px}
  .post-line .thumb{display:none}
  .post-line .end{display:none}
  .post-line .dt b{display:inline;font-size:1rem;margin-right:6px;color:var(--amber-ink);font-style:italic}
}

/* the row's narrow tier. It lived in the system family's own media query while
   the component itself had moved here, so any page that used the row outside
   those two families kept the desktop track list and pushed a phone sideways.
   A component and the widths it answers to belong in the same file.

   It asked the window, at 760px -- the one width in the shared layer that was
   not one of the three tiers, so between 641 and 760 every list row was in its
   phone layout while the rest of the page was still in its tablet one.

   640 is the row's own floor, measured rather than guessed: 96 for the date,
   128 for the thumbnail, 135 for the category and arrow, 90 of gaps and about
   180 left for the words. Asking the container instead of the window is what
   makes that number usable -- a 700px list inside a 1100px window keeps its
   thumbnail, which the old rule had no way to say, and a 274px sidebar
   collapses at any screen size, which it also had no way to say. */
@container postlist (max-width:640px){
  .post-line,.post-line.media,.post-line.nodate,.post-line.nodate.media{
    grid-template-columns:1fr;padding:20px 16px;margin:0 -16px;gap:6px}
  .post-line .end{display:none}
  .post-line .thumb{display:none}
  .post-line .dt b{display:inline;font-size:1rem;margin-right:6px;color:var(--amber-ink);font-style:italic}
  .on-ink .post-line .dt b{color:var(--amber-soft)}
}

/* ---------- rich text ----------
   Typography called it .rich and Section called it .prose, and they were the
   same three rules with a different measure. One name, and the measure is a
   modifier rather than a second component. */
.rich{max-width:46rem}
.rich.narrow{max-width:680px}
.rich.centred{margin-inline:auto}
.rich p{margin-bottom:1.1em}
/* a heading inside running text sets its own space: more above than below, so
   it belongs to what follows it rather than floating between two paragraphs */
/* In em, the space above a heading scales with the heading, so at --fs-h2 the
   1.7em opened a 79px hole above every sub-heading in an article -- a gap the
   size of a paragraph, which reads as a missing section rather than as a
   break. Running text has one rhythm and it is the body's, so the space is
   in rem: a shade under two body lines above an h2, a shade over one above an
   h3, and in both cases far less below, so the heading belongs to the
   paragraph it introduces. */
/* Direct children only. These are the rhythm of the rich text's own flow, and
   the moment they reached down into a component nested inside it they became a
   bug: the notice in an article got 32px of margin above its title, so a 44px
   chip lined up on the first line was suddenly 32px above the words it labels.
   That is what "the alert is misaligned" was, both times. A component inside
   prose brings its own spacing; prose spaces what it is actually setting. */
.rich>h2{margin:2.7rem 0 .55rem}
.rich>h3{margin:2rem 0 .45rem}
.rich>h4{margin:1.7rem 0 .35rem}
.rich>:first-child{margin-top:0}
/* The drop cap. An option, not a default: it belongs to a piece that opens on a
   paragraph and looks wrong on one that opens with a heading or under a
   standfirst, and plenty of editors will never want one at all. So it is a
   class the editor turns on -- `.rich.dropcap` -- and it is keyed to the first
   *child*, not the first paragraph, so a piece that starts with anything else
   quietly does not get one rather than dropping a capital into the middle of
   the article. */
.rich.dropcap>p:first-child::first-letter{float:left;font-family:var(--serif);
  font-weight:540;font-size:4.2rem;line-height:.82;margin:6px 14px 0 0;color:var(--green)}
.sec-green .rich.dropcap>p:first-child::first-letter,
.sec-ink .rich.dropcap>p:first-child::first-letter,
.on-ink .rich.dropcap>p:first-child::first-letter{color:var(--amber-soft)}
/* A list in running text (14.31).

   The reset at the top of this file takes every list's indent away, which is
   right for the lists this library draws itself -- the icon list, the numbered
   list, the row of large links, the menu -- and wrong for the one an editor
   writes, which arrives as a plain `ul` with its markers outside the box. On
   the site that put every bullet in the margin to the left of every other line
   on the page, which is what the reviewer saw reading a post.

   So a list in prose gets its indent back, and the indent is chosen so that the
   marker lines up with the column and the words sit a step inside it, rather
   than the other way round. Only in prose: `.rich` is the article body and the
   only place a bare list is running text. */
.rich :is(ul,ol):not([class]),.rich :is(ul,ol).wp-block-list{padding-inline-start:1.45em;margin-bottom:1.1em}
/* the items of that list and no other: a descendant selector here would reach
   the icon list, the large links and the menu inside an article as well */
.rich :is(ul,ol):not([class])>li,.rich :is(ul,ol).wp-block-list>li{margin-bottom:.35em}
.rich :is(ul,ol):not([class])>li:last-child,.rich :is(ul,ol).wp-block-list>li:last-child{margin-bottom:0}
/* And the list block wherever an editor puts one, a chapter, a tile, a column,
   a tab, a notice, a popup (14.89): the same indent, so the marker stands on
   the line the words start on and the words a step inside it, instead of the
   marker out in the margin. Asked for in review on a chapter's list, which
   had been the case everywhere but prose. `.wp-block-list` is the class
   WordPress gives the list block; nothing this library draws itself carries
   it, and a family that styles its own lists (the answers in a question)
   says so more strongly and keeps its look. */
:is(ul,ol).wp-block-list{padding-inline-start:1.45em}
:is(ul,ol).wp-block-list>li{margin-bottom:.35em}
:is(ul,ol).wp-block-list>li:last-child{margin-bottom:0}
.rich :is(ul,ol):not([class])>li>:is(ul,ol),.rich :is(ul,ol).wp-block-list>li>:is(ul,ol){margin-block:.35em 0}
/* prose links only. An unqualified .rich a out-specifies .link-more and .btn,
   which quietly stripped the weight off any component link inside rich text. */
.rich a:not([class]){color:var(--green);font-weight:500}
.rich>blockquote{font-family:var(--serif);font-size:1.5rem;line-height:1.4;border-left:calc(var(--line-w-strong) * 1.5) solid var(--green);padding-left:22px;margin:1.4em 0;color:var(--ink)}
/* The attribution is its own line. It was inline, so a pull quote that ended in
   one ran the speaker straight on from the last word of the sentence -- "did we
   want it back.Ole, who took the call" -- which reads as a typo rather than as
   a citation. Block, at reading size, in the accent. */
.rich blockquote em{display:block;margin-top:.55em;font-size:.95rem;font-style:normal;
  font-family:var(--sans);color:var(--ink-soft)}
.rich blockquote em::before{content:"— "}
.sec-green .rich blockquote em,.sec-ink .rich blockquote em{color:var(--on-ink-dim)}

/* ---------- the hand drawn underline ----------
   Lived in Hero and in Misc, and the two had already diverged: Hero's hard
   coded stroke-dasharray:1000, which only resolves for a path of roughly that
   length, and fired on page load rather than on sight. Misc's version
   normalises with pathLength="1" so any path draws correctly, and waits until
   the mark is actually in view so the gesture is seen rather than found already
   finished. Misc's is the one that survives. */
.u-draw{position:relative;white-space:nowrap}
.u-draw svg{position:absolute;left:-2%;bottom:-.28em;width:104%;height:.42em;color:var(--amber);overflow:visible}
.u-draw svg path,.u-draw svg ellipse{fill:none;stroke:currentColor;stroke-linecap:round;stroke-dasharray:1;stroke-dashoffset:1}
.u-draw.go svg path{animation:boza-draw .7s var(--ease-out) .55s forwards}
.u-draw.go svg ellipse{animation:boza-draw var(--t-reveal) var(--ease-out) .55s forwards}
.u-draw.green svg{color:var(--green)}
.u-draw.v-bold svg path{stroke-width:3.5}
.u-draw.v-fine svg path{stroke-width:1.8}
.u-draw.v-double svg path{stroke-width:1.6}
.u-draw.v-double svg path:nth-child(2){animation-delay:.95s;animation-duration:var(--t-mid)}
.u-draw.v-loop{padding:0 .14em}
.u-draw.v-loop svg{left:-15%;width:130%;top:-.44em;bottom:auto;height:calc(100% + .72em)}
.u-draw.v-loop svg ellipse{stroke-width:2.1}
/* The same mark, drawn by the stylesheet when nothing has drawn it in the
   markup.

   WHY BOTH

   In this library the mark is an inline <svg> written into the sentence, and
   that is the better drawing: the stroke itself is animated, so the line grows
   along its own length. It stays exactly as it is.

   In WordPress the mark is a rich-text format -- an editor selects a word and
   presses a button -- and a format wraps a range of text in a tag. It cannot
   put a child element inside that tag. So the span arrives with nothing in it,
   and without this it would be a span that does nothing at all.

   The mark is therefore a masked shape on a pseudo-element, using the same
   paths and the same weights, revealed left to right instead of stroked. At
   0.42em over 0.7s the two are the same gesture. `:not(:has(svg))` is what
   keeps them from ever both appearing: whoever drew it first wins, and in this
   library that is always the markup.

   `v-loop` is not here and cannot be. Its ellipse is hand-fitted to the word it
   goes round -- rx 57 for two words and 116 for four -- so it is a drawing
   somebody makes, not a style somebody picks. */
.u-draw{--u-mark:url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%2010'%20preserveAspectRatio='none'><path%20d=%22M2%207%20Q%2025%203%2050%206%20T%2098%204%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%223.5%22%20stroke-linecap=%22round%22/></svg>")}
.u-draw.v-fine{--u-mark:url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%2010'%20preserveAspectRatio='none'><path%20d=%22M2%207%20Q%2025%203%2050%206%20T%2098%204%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%221.8%22%20stroke-linecap=%22round%22/></svg>")}
.u-draw.v-bold{--u-mark:url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%2010'%20preserveAspectRatio='none'><path%20d=%22M2%207%20Q%2025%203%2050%206%20T%2098%204%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%223.5%22%20stroke-linecap=%22round%22/></svg>")}
.u-draw.v-double{--u-mark:url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20100%2010'%20preserveAspectRatio='none'><path%20d=%22M2%205%20Q%2030%201%2060%204%20T%2098%203%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%221.6%22%20stroke-linecap=%22round%22/><path%20d=%22M4%2010%20Q%2035%207%2065%209%20T%2096%208%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%221.6%22%20stroke-linecap=%22round%22/></svg>")}
.u-draw.green{--u-ink:var(--green)}
.u-draw:not(:has(svg))::after{content:"";position:absolute;left:-2%;bottom:-.28em;
  width:104%;height:.42em;background:var(--u-ink,var(--amber));
  -webkit-mask:var(--u-mark) 0 0/100% 100% no-repeat;mask:var(--u-mark) 0 0/100% 100% no-repeat;
  clip-path:inset(0 100% 0 0);transition:clip-path .7s var(--ease-out) .55s}
.u-draw.go:not(:has(svg))::after{clip-path:inset(0 0 0 0)}
@media (prefers-reduced-motion: reduce){.u-draw:not(:has(svg))::after{transition:none;clip-path:inset(0 0 0 0)}}
/* `isolation:isolate`, and it is load-bearing.

   The wash is a ::before at z-index -1 so the words sit on top of it. Without a
   stacking context of its own that -1 is measured against the page, and the
   negative layer is painted before every in-flow background above it -- so the
   wash lands behind the section's ground and vanishes. It is invisible on a
   tint, a green or an ink band and correct only on the body's own paper, which
   is the one ground this library ever drew it on. Found the first time a
   WordPress section put a ground behind it. One word fixes it: the span becomes
   its own stacking context, the -1 is measured inside that, and the words still
   sit on top. */
.u-hl{position:relative;white-space:nowrap;isolation:isolate}
.u-hl::before{content:"";position:absolute;left:-.12em;right:-.12em;top:.14em;bottom:-.02em;background:var(--amber);opacity:.26;border-radius:.18em .28em .16em .3em;clip-path:inset(0 100% 0 0);z-index:-1}
.u-hl.go::before{transition:transform .6s var(--ease-out) .55s;clip-path:inset(0 0 0 0)}
@media (prefers-reduced-motion: reduce){.u-draw svg path,.u-draw svg ellipse{animation:none !important;stroke-dashoffset:0}.u-hl::before{transition:none !important;clip-path:inset(0 0 0 0)}}

/* ---------- shared keyframes ----------
   These were declared twice each, in Logo Bar and Marquee Text, and in Hero and
   Misc. Keyframes are document-global, so on a page carrying two of those blocks
   one definition silently won and the other was dead. Named once, prefixed, so
   nothing on a WordPress page can collide with them either. */
@keyframes boza-marquee{to{transform:translateX(-50%)}}
@keyframes boza-draw{to{stroke-dashoffset:0}}

/* ---------- reveal ---------- */
/* A trap worth naming: .reveal sets `transition`, which is a shorthand, so any
   component that also carries .reveal loses its own transitions entirely. That
   is why a call to action inside a revealed section had no hover fade at all.
   The two components that get revealed directly declare the combined list. */
.reveal{opacity:0;transform:translateY(26px);transition:opacity var(--t-reveal) var(--ease-out),transform var(--t-reveal) var(--ease-out)}
.btn.reveal{transition:opacity var(--t-reveal) var(--ease-out),transform var(--t-reveal) var(--ease-out),background var(--t-fast) var(--ease-out),color var(--t-fast),box-shadow var(--t-fast),border-color var(--t-fast)}
.link-more.reveal{transition:opacity var(--t-reveal) var(--ease-out),transform var(--t-reveal) var(--ease-out),color var(--t-fast)}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion: reduce){.reveal{opacity:1;transform:none;transition:none}}

/* ============================================================
   Specimen chrome — the library's own UI around each block demo.
   Not part of the theme; deliberately plain so demos stand out.
   ============================================================ */
.demo-sep{background:repeating-linear-gradient(-45deg,transparent 0 10px,rgba(23,37,31,.028) 10px 11px),var(--paper);border-block:var(--line-w) dashed var(--line)}
.demo-sep .inner{max-width:var(--container);margin-inline:auto;padding:16px 32px;display:flex;align-items:baseline;gap:14px}
.demo-sep .no{font-family:var(--serif);font-style:italic;color:var(--amber)}
/* the specimen labels are section headings, so they are h2 in the markup and
   styled down here; they used to be h3 under an h1, a level skip on every page */
.demo-sep h2{font-size:1.1rem;font-weight:540;letter-spacing:-.008em}
.demo-sep p{font-size:.88rem}
.spec-bar{position:sticky;top:0;z-index:60;background:var(--ink);color:#fff;font-size:.9rem}
.spec-bar .inner{max-width:var(--container);margin-inline:auto;padding:12px 32px;display:flex;align-items:center;gap:18px}
.spec-bar a{color:var(--on-ink-dim);text-decoration:none}
.spec-bar a:hover{color:#fff}
.spec-bar b{font-weight:600}
.spec-bar .st{margin-left:auto;background:var(--on-ink-fill-hi);padding:4px 12px;border-radius:var(--r-btn);font-size:.78rem}
/* padding-block, not the shorthand: this element also carries .container, and
   `padding:64px 0 8px` wiped the container's 32px inline padding, which is why
   every one of the 33 specimen pages had its intro stuck to the screen edge on a
   phone. Same trap the system pages hit once already. */
.spec-intro{padding-block:64px 8px}
@media(max-width:640px){.spec-intro{padding-block:44px 4px}.spec-intro h1{font-size:clamp(2rem,8vw,2.6rem)}}
.spec-intro h1{font-size:clamp(2.4rem,4vw,3.4rem);margin:10px 0 14px}
.spec-intro p{max-width:44rem}
.demo-label{max-width:var(--container);margin:0 auto;padding:0 32px 18px;display:flex;align-items:baseline;gap:14px}
.demo-label .no{font-family:var(--serif);font-style:italic;color:var(--amber)}
.demo-label h2{font-size:1.2rem;font-weight:540;letter-spacing:-.008em}
.demo-label p{font-size:.9rem}
.demo-pad{padding:56px 0}
/* ---------- the filter bar ----------
   Posts and Resources each had one, with different pills, a different effect
   and different behaviour under rapid clicking. Posts' is the approved pattern:
   the grid re-lays instantly and the new set rises into place while fading in,
   and a fresh click cancels whatever the last one was still animating rather
   than locking the bar. This is that one, for both and for whatever comes next. */
.fbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:30px}
.fbar button{all:unset;box-sizing:border-box;cursor:pointer;font:inherit;font-size:.9rem;font-weight:600;
  padding:8px 16px;border-radius:var(--r-btn);border:var(--line-w) solid var(--line-dark);
  display:inline-flex;align-items:center;gap:8px;
  transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast)}
.fbar button:hover{background:var(--tint)}
.fbar button .c{font-size:.74rem;font-weight:600;background:var(--tint-deep);color:var(--green);
  border-radius:var(--r-btn);padding:1px 7px;transition:background var(--t-fast),color var(--t-fast)}
.fbar button.active{background:var(--green);border-color:var(--green);color:#fff}
.fbar button.active:hover{background:var(--green-deep);border-color:var(--green-deep)}
.fbar button.active .c{background:var(--green-deep);color:#fff}
.fbar .showing{margin-left:auto;font-family:var(--serif);font-style:italic;font-size:.95rem;color:var(--ink-soft)}
@media(max-width:640px){.fbar{gap:8px}.fbar .showing{margin-left:0;flex:1 0 100%}}
:is(.sec-green,.sec-ink,.on-ink) .fbar .showing{color:var(--on-ink-dim)}
/* the filtered item */
/* the doubled class is deliberate. .f-item is added by the filter driver, but a
   card already carries its own transition from its family's stylesheet, which
   loads after this one and wins at equal specificity. The rise-in was being
   declared and then silently dropped, so a filtered grid re-laid instantly with
   no motion at all. Two classes beat one without reaching for !important. */
.f-item.f-item,.f-group.f-group{transition:opacity .52s var(--ease-out),transform .42s var(--ease-out),background var(--t-fast)}
.f-item.enter.enter,.f-group.enter.enter{opacity:0;transform:translateY(20px);transition:none}
.f-item.hide{display:none}
.f-group.hide{display:none}

/* ---------- the scroll hint ----------
   A horizontal scroller that gives no sign it scrolls is a scroller nobody uses.
   The cue is a soft fade on whichever edge still has content behind it, done
   with a mask on the scroller itself rather than an overlay, so it needs no
   wrapper element and works over any background, light or dark. ui.js keeps the
   two custom properties in step with the scroll position. */
/* Only a tab nav carries the fade now. A card row has arrows, dots and a
   partially visible next card, which is three cues already; a gradient on top of
   them was decoration that had to be reasoned about at both ends and in both
   directions, and every hour spent on it was an hour spent on the least
   important signal on the page. */
.scroll-hint{
  /* a scroll hint is only ever put on a scroller, so it is one: the overflow
     used to be repeated by every family that carried this class, and removing
     one copy silently stopped a tab nav scrolling at all */
  overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;
  --hint-l:0px; --hint-r:0px; --hint-w:52px;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 var(--hint-l),#000 calc(100% - var(--hint-r)),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 var(--hint-l),#000 calc(100% - var(--hint-r)),transparent 100%);
  transition:-webkit-mask-image .2s linear, mask-image .2s linear}
.scroll-hint::-webkit-scrollbar{display:none}
.scroll-hint.hint-l{--hint-l:var(--hint-w)}
.scroll-hint.hint-r{--hint-r:var(--hint-w)}
/* run a scroller to the screen edge on a phone so the cut-off is obviously a
   cut-off and not the end of the row */
.bleed-phone{margin-inline:calc(-1 * var(--gutter));padding-inline:var(--gutter)}
@media(min-width:641px){.bleed-phone{margin-inline:0;padding-inline:0}}
/* ---------- a card row that runs to the screen edges ----------
   Above the tablet step there is no peek at all: the cards fill the text column
   exactly, because a cropped card beside a wide, quiet layout reads as a mistake
   rather than as an invitation, and a desktop already has arrows and dots.

   Below it the row runs out to both screen edges and the cards are sized so the
   next one shows past the gutter. That is where the peek earns its place: on a
   touch screen it is the only thing that says this row moves, and the cut at the
   very edge of the glass is what makes it read as more content rather than as a
   card someone trimmed. Scroll snap handles the other end on its own, so the
   last slide leaves the previous one showing on the left. */
@media(max-width:1000px){
  /* and the peek itself, which every card row that runs to the edges drew at
     the same two sizes in its own sheet (14.6) */
  .slider.edge{--gut:var(--gutter);margin-inline:calc(-1 * var(--gut));--peek:52px}
  /* scroll-padding as well as padding: snapping aligns a slide to the scroll
     port edge, so without it the first card lands past the gutter it is meant to
     line up with */
  .slider.edge .sl-track{padding-inline:var(--gut);scroll-padding-inline:var(--gut)}
  .slider.edge .sl-nav{padding-inline:var(--gut)}
}
@media(max-width:640px){.slider.edge{--peek:46px}}

/* ---------- how a card row shows there is more ----------
   The standard answer, and the one every carousel library settles on: size the
   cards so that a fraction of the next one is visible, and let scroll snap do
   the rest. Swiper calls it a fractional slidesPerView, Embla sizes the slides
   by percentage; it is the same idea. The track stays inside the text column, so
   the first card lines up with the words above it and nothing has to be pulled
   back out again.

   The peek sits on the trailing side, which is where the content actually is.
   Asking for it on both sides at once is asking for a slide before the first
   one; carousels do not do that, and the reason they do not is that indenting
   the first card breaks its alignment with everything above it. */

/* Responsive grid utilities for the specimen pages. An inline
   grid-template-columns cannot be answered by a media query, which is how three
   separate demos ended up holding their desktop shape on a phone. */
.g2{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.g3{--g:20px;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--g)}
.g-2-1{display:grid;grid-template-columns:2fr 1fr;gap:20px}
@media(max-width:1000px){.g3{grid-template-columns:1fr 1fr}}
@media(max-width:640px){.g2,.g3,.g-2-1{grid-template-columns:1fr}}

/* a row and a stack of examples on a specimen page. Buttons, Icons and Alerts
   each had one under a name a real page could easily want for itself. */
.demo-row{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.demo-stack{display:grid;gap:var(--stack-gap,14px)}
.demo-stack.roomy{--stack-gap:34px}

/* the breakpoint simulator's toolbar, byte-identical in Columns and Tiles */
.sim-bar{display:flex;align-items:center;gap:8px;padding:14px 20px;border-bottom:var(--line-w) solid var(--line-dark);background:var(--tint-2);flex-wrap:wrap}
.sim-bar span.lbl{font-size:.88rem;color:var(--ink-soft);margin-right:6px}
.sim-bar button{all:unset;cursor:pointer;font:inherit;font-size:.88rem;font-weight:600;padding:7px 16px;border-radius:var(--r-btn);border:var(--line-w) solid var(--line-dark);transition:background .2s,color .2s}
.sim-bar button.active{background:var(--green);border-color:var(--green);color:#fff}

.opts{padding:72px 0 96px}
.opts table{width:100%;border-collapse:collapse;font-size:.95rem}
.opts th{text-align:left;font-family:var(--serif);font-weight:540;font-size:1.05rem;padding:12px 14px;border-bottom:var(--line-w) solid var(--line-dark)}
.opts td{padding:11px 14px;border-bottom:var(--line-w-soft) solid var(--line);vertical-align:top}
.opts td:first-child{font-weight:600;white-space:nowrap}
.opts td code{background:var(--tint);border-radius:var(--r-xs);padding:2px 7px;font-size:.85rem}
/* the nowrap first column could not shrink, and it alone pushed seventeen of the
   thirty-three specimen pages wider than a phone */
@media (max-width:900px){
  /* fixed layout so a long prose cell wraps instead of setting the table's
     minimum width, which is what pushed the page sideways */
  .opts table{table-layout:fixed}
  .opts td:first-child,.opts th:first-child{width:22%}
  .opts td:first-child{white-space:normal}
  .opts th,.opts td{padding:10px 8px;overflow-wrap:anywhere}
}
@media (max-width:640px){
  .opts{padding:56px 0 72px}
  .opts table{font-size:.9rem}
}
.spec-bar .inner{flex-wrap:wrap;row-gap:6px}
@media (max-width:640px){.demo-sep .inner,.demo-label{flex-wrap:wrap;row-gap:4px}}

/* ---------- 23 · tooltips: one class on a button, an icon chip or a run of words ---------- */
.tt{position:relative}
.tt::after{content:attr(data-tip);position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(calc(-50% + var(--tt-x,0px))) translateY(4px);background:var(--ink);color:#fff;font-size:.82rem;font-weight:500;line-height:1.4;padding:8px 13px;border-radius:var(--r-sm);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .25s,transform .3s var(--ease-out);z-index:20}
.tt::before{content:"";position:absolute;left:50%;bottom:calc(100% + 4px);transform:translateX(-50%);border:6px solid transparent;border-top-color:var(--ink);border-bottom:0;opacity:0;transition:opacity .25s,transform .3s var(--ease-out);z-index:20}
/* shown where there is a pointer that can hover, on focus from the keyboard, and
   when a tap asks for it (14.8): a tap on a touch screen left :hover on, so the
   hover rule is for a pointer that hovers and nothing else */
@media (hover:hover){.tt:hover::after{opacity:1;transform:translateX(calc(-50% + var(--tt-x,0px)))}.tt:hover::before{opacity:1}}
.tt:focus-visible::after,.tt.tt-on::after{opacity:1;transform:translateX(calc(-50% + var(--tt-x,0px)))}
.tt:focus-visible::before,.tt.tt-on::before{opacity:1}
.tt.pos-b::after{bottom:auto;top:calc(100% + 10px);transform:translateX(calc(-50% + var(--tt-x,0px))) translateY(-4px)}
.tt.pos-b::before{bottom:auto;top:calc(100% + 4px);border:6px solid transparent;border-bottom-color:var(--ink);border-top:0}
@media (hover:hover){.tt.pos-b:hover::after{transform:translateX(calc(-50% + var(--tt-x,0px)))}}
.tt.pos-b:is(:focus-visible,.tt-on)::after{transform:translateX(calc(-50% + var(--tt-x,0px)))}
.tt.pos-r::after{left:calc(100% + 10px);bottom:auto;top:50%;transform:translateY(-50%) translateX(-4px)}
.tt.pos-r::before{left:calc(100% + 4px);bottom:auto;top:50%;transform:translateY(-50%);border:6px solid transparent;border-right-color:var(--ink);border-left:0}
@media (hover:hover){.tt.pos-r:hover::after{transform:translateY(-50%)}}
.tt.pos-r:is(:focus-visible,.tt-on)::after{transform:translateY(-50%)}
/* inline text tooltip trigger */
/* Below the tablet step every tooltip sits above its control, wraps at 260 pixels
   or at the screen less its gutters, and is not drawn at all until it shows. An
   absolutely placed bubble adds to the page's scrollable width even while it is
   invisible: the side-positioned one pushed a phone 95px sideways at rest. The
   first answer to that held the bubble to 68 per cent of the screen, and a bubble
   takes its width from the control it hangs off, so every one was squeezed to its
   longest word: "Print this page" in three lines 29 pixels wide, measured in 14.8.
   Now it is as wide as its words, and not there until it is wanted; one that
   shows near the edge of the screen is slid back inside by ui.js, through
   --tt-x, its arrow staying on its control. */
@media(max-width:1000px){
  .tt::after{width:max-content;max-width:min(260px,calc(100vw - 32px));white-space:normal}
  .tt:not(:focus-visible):not(.tt-on)::after,.tt:not(:focus-visible):not(.tt-on)::before{display:none}
  .tt.pos-r::after{left:50%;right:auto;bottom:calc(100% + 10px);top:auto;transform:translateX(calc(-50% + var(--tt-x,0px))) translateY(4px)}
  .tt.pos-r::before{left:50%;bottom:calc(100% + 4px);top:auto;transform:translateX(-50%);border:6px solid transparent;border-top-color:var(--ink);border-bottom:0;border-right:0}
  .tt.pos-r:is(:hover,:focus-visible,.tt-on)::after{transform:translateX(calc(-50% + var(--tt-x,0px)))}
}
/* and a pointer that hovers still has its hover there, a small window being a
   desktop too; doubled to outrank the rest rule above */
@media(max-width:1000px) and (hover:hover){
  .tt.tt:hover::after,.tt.tt:hover::before{display:block}
}
/* and opened below its control where there is no room above it, under a sticky
   bar or past the top of the screen, which ui.js measures as the bubble shows
   (14.9): one on a control just under the header opened behind the header.
   Doubled, to outrank a side tooltip stood upright below the tablet step. */
.tt.tt-below.tt-below::after{bottom:auto;top:calc(100% + 10px);transform:translateX(calc(-50% + var(--tt-x,0px))) translateY(-4px)}
.tt.tt-below.tt-below::before{bottom:auto;top:calc(100% + 4px);border:6px solid transparent;border-bottom-color:var(--ink);border-top:0}
@media (hover:hover){.tt.tt-below.tt-below:hover::after{transform:translateX(calc(-50% + var(--tt-x,0px)))}}
.tt.tt-below.tt-below:is(:focus-visible,.tt-on)::after{transform:translateX(calc(-50% + var(--tt-x,0px)))}
/* and put away by Escape until the pointer or the focus moves on (14.8) */
.tt.tt-off::after,.tt.tt-off::before{opacity:0}
.tt-word{cursor:help;border-bottom:var(--line-w) dotted var(--green);padding-bottom:1px}

/* ---------- 23 · back to top: the chip in the corner, shown after a screen of scrolling ---------- */
.btt{position:fixed;right:26px;bottom:26px;z-index:50;opacity:0;transform:translateY(12px);pointer-events:none;transition:opacity .3s,transform .35s var(--ease-out)}
.btt.show{opacity:1;transform:none;pointer-events:auto}

/* ---------- 33 · the search's chips: a row of terms that run a search, on any page ---------- */
.chiprow-l{display:flex;gap:9px;flex-wrap:wrap}
.chip-l{display:inline-flex;align-items:center;border:var(--line-w) solid var(--line-dark);border-radius:var(--r-btn);padding:8px 15px;font-size:.92rem;text-decoration:none;color:var(--ink);background:var(--paper);transition:background .25s,color .25s,border-color .25s}
.chip-l:hover{background:var(--green);border-color:var(--green);color:#fff}
