/* ==========================================================================
   COMMERCE ONLINE â BROADSHEET
   Direction: Singapore Editorial (one direction, unmixed)

   Promoted from Concepts-v4/b-broadsheet/concept.css after Concept B was
   chosen. Two things were dropped on the way across: the concept bar, which
   was evaluation chrome, and the motion-plate rules, because the animated
   hero was abandoned and the still back-office photograph reinstated.

   What was added is everything a thirteen-page site needs that a single
   concept page did not: a breadcrumb, article typography, forms, the voice
   filter, and the several typographic panels that stand in for screenshots we
   are not allowed to publish.

   Reading order matches the numbered sections. Add to the section that owns
   the component rather than to the end of the file.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  --paper:    #F6F2E8;   /* newsprint, warm */
  --paper-2:  #FBF9F3;   /* an inset panel, a shade brighter */
  --paper-3:  #EBE5D6;   /* a tint band */

  --ink:      #15181D;   /* body text and headlines */
  --ink-2:    #4B5058;   /* secondary */
  --ink-3:    #7A7F87;   /* captions, folios, metadata */

  --rule:     #CFC7B5;   /* hairline */
  --rule-2:   #15181D;   /* the heavy rule under the masthead */

  --red:      #B23A22;   /* the second plate. Structural, never decorative. */
  --red-wash: #B23A2214;

  --f-head:  Spectral, Georgia, "Times New Roman", serif;
  --f-body:  "Source Sans 3", system-ui, sans-serif;
  --f-label: Lexend, system-ui, sans-serif;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  --gutter: 40px;
  --wrap: 1220px;
  --measure: 33rem;

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

@media (width <= 900px) { :root { --gutter: 20px; } }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: 1.0625rem;
  line-height: 1.66;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
p { margin: 0 0 var(--s5); }
p:last-child { margin-bottom: 0; }
b, strong { font-weight: 600; color: var(--ink); }
::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

.shell { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 30;
  background: var(--ink); color: var(--paper);
  padding: var(--s3) var(--s5); font-family: var(--f-label); text-decoration: none;
}
.skip-link:focus { left: var(--s4); top: var(--s4); }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* reveal.js sets the armed attribute on <html> only when it is going to run,
   so with JS blocked or reduced motion requested nothing is ever hidden. */
[data-reveal-armed] [data-reveal] {
  opacity: 0; transform: translate3d(0, 18px, 0);
  transition: opacity .8s var(--ease), transform .8s var(--ease);
}
[data-reveal-armed] [data-reveal][data-revealed] { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   2. Furniture â the small print labels that make a page read as edited
   -------------------------------------------------------------------------- */
.label {
  font-family: var(--f-label); font-size: .68rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase; color: var(--ink-3);
  margin: 0;
}
.label--red { color: var(--red); }
.rule-hair { border: 0; border-top: 1px solid var(--rule); margin: 0; }
.rule-heavy { border: 0; border-top: 2px solid var(--rule-2); margin: 0; }
.rule-red { border: 0; border-top: 2px solid var(--red); margin: 0; }

/* --------------------------------------------------------------------------
   3. Masthead
   -------------------------------------------------------------------------- */
.masthead { border-bottom: 1px solid var(--rule); }
.masthead__edition {
  max-width: var(--wrap); margin-inline: auto; padding: var(--s3) var(--gutter);
  display: flex; flex-wrap: wrap; gap: var(--s5); justify-content: space-between;
  font-family: var(--f-label); font-size: .66rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
  border-bottom: 1px solid var(--rule);
}
.masthead__edition b { color: var(--red); font-weight: 600; }
/* The middle line of the edition rule is the first thing to go when the bar
   gets tight â it is atmosphere, where the other two are orientation. */
@media (width <= 1000px) { .masthead__edition span:nth-child(2) { display: none; } }
@media (width <= 560px) { .masthead__edition { font-size: .6rem; gap: var(--s4); } }

.masthead__bar {
  max-width: var(--wrap); margin-inline: auto; padding: var(--s5) var(--gutter);
  display: flex; align-items: center; gap: var(--s6);
}
.wordmark { display: block; }
.wordmark img { width: 176px; height: auto; }

.nav { margin-left: auto; display: flex; align-items: center; gap: var(--s6); }
.nav a, .nav__toggle {
  font-family: var(--f-label); font-size: .82rem; font-weight: 500;
  letter-spacing: .04em; color: var(--ink); text-decoration: none;
  background: none; border: 0; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  border-bottom: 1px solid transparent; transition: border-color .3s var(--ease), color .3s var(--ease);
}
.nav a:hover, .nav__toggle:hover { color: var(--red); border-bottom-color: var(--red); }
/* The current page is marked in the second plate, the way a running head
   marks the section you are reading. */
.nav a[aria-current="page"] { color: var(--red); border-bottom-color: var(--red); }
.nav__group { position: relative; }
.nav__menu {
  position: absolute; top: calc(100% + var(--s4)); right: 0; min-width: 310px;
  background: var(--paper-2); border: 1px solid var(--rule); padding: var(--s3) 0; display: none; z-index: 20;
}
.nav__group[data-open="true"] .nav__menu { display: block; }
.nav__menu a { display: block; padding: var(--s3) var(--s5); font-size: .82rem; border: 0; }

.burger { display: none; margin-left: auto; width: 44px; height: 44px; background: none; border: 1px solid var(--rule); cursor: pointer; position: relative; }
.burger span:first-child, .burger::before, .burger::after {
  content: ""; position: absolute; left: 12px; right: 12px; height: 1.5px; background: var(--ink);
  transition: transform .35s var(--ease), opacity .2s;
}
.burger::before { top: 16px; } .burger span:first-child { top: 21px; } .burger::after { top: 26px; }
[data-nav-open="true"] .burger::before { transform: translateY(5px) rotate(45deg); }
[data-nav-open="true"] .burger span:first-child { opacity: 0; }
[data-nav-open="true"] .burger::after { transform: translateY(-5px) rotate(-45deg); }

@media (width <= 900px) {
  .masthead__bar { position: relative; }
  .burger { display: block; }
  .nav {
    display: none; position: absolute; left: 0; right: 0; top: 100%; z-index: 20;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--paper-2); border-block: 1px solid var(--rule);
    padding: var(--s4) var(--gutter) var(--s6);
  }
  [data-nav-open="true"] .nav { display: flex; }
  .nav > a, .nav__toggle { padding: var(--s4) 0; border-bottom: 1px solid var(--rule); }
  .nav__menu { position: static; display: block; border: 0; background: none; padding-left: var(--s5); }
}

/* Breadcrumb: the folio line. Present on every page below the top level. */
.crumbs {
  max-width: var(--wrap); margin-inline: auto; padding: var(--s4) var(--gutter) 0;
  font-family: var(--f-label); font-size: .68rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-3);
}
.crumbs a { text-decoration: none; }
.crumbs a:hover { color: var(--red); }
.crumbs span { color: var(--rule); margin-inline: var(--s2); }

/* --------------------------------------------------------------------------
   4. Type scale â Spectral does the shouting
   -------------------------------------------------------------------------- */
.head {
  font-family: var(--f-head); font-weight: 300; color: var(--ink);
  letter-spacing: -.018em; line-height: 1.02; margin: 0;
}
.head--banner { font-size: clamp(3.1rem, 8.4vw, 7rem); line-height: .96; }
/* Interior pages carry a longer headline than the home page, so the top of
   the clamp comes down â at 7rem a fourteen-word headline sets as a wall. */
.head--page { font-size: clamp(2.5rem, 5.6vw, 4.6rem); line-height: 1; }
.head--2 { font-size: clamp(2.1rem, 4.1vw, 3.4rem); line-height: 1.06; font-weight: 400; }
.head--3 { font-size: clamp(1.6rem, 2.7vw, 2.25rem); line-height: 1.1; font-weight: 400; }
.head--4 { font-size: clamp(1.2rem, 1.6vw, 1.4rem); line-height: 1.2; font-weight: 500; }
.head em { font-style: italic; color: var(--red); }

.stand {
  font-family: var(--f-head); font-weight: 300; font-style: italic;
  font-size: clamp(1.25rem, 2vw, 1.6rem); line-height: 1.42; color: var(--ink-2); margin: 0;
}
.copy { color: var(--ink-2); margin: 0; }
.measure { max-width: var(--measure); }
.measure--wide { max-width: 44rem; }
.measure--head { max-width: 17ch; }
.measure--head-2 { max-width: 24ch; }

.mb-3 { margin-bottom: var(--s3); } .mb-4 { margin-bottom: var(--s4); }
.mb-5 { margin-bottom: var(--s5); } .mb-6 { margin-bottom: var(--s6); }
.mb-7 { margin-bottom: var(--s7); } .mb-8 { margin-bottom: var(--s8); }
.mb-9 { margin-bottom: var(--s9); }

.section { padding-block: clamp(56px, 8vw, 116px); }
.section--rule { border-top: 1px solid var(--rule); }
.section--tint { background: var(--paper-3); border-block: 1px solid var(--rule); }
.section--tight { padding-block: clamp(48px, 6vw, 88px); }
/* An ink band. Used once per page at most, to break a long run of paper. */
.section--ink { background: var(--ink); color: var(--paper-3); border-block: 0; }
.section--ink .head, .section--ink b, .section--ink strong { color: var(--paper); }
.section--ink .copy, .section--ink .stand { color: #B9BCC2; }
.section--ink .label { color: #9EA2A9; }
.section--ink .label--red, .section--ink .head em { color: #E8A08C; }
.section--ink .rule-hair { border-top-color: #333840; }

/* Utilities, so nothing needs an inline style. An inline declaration outranks
   every media query, which means a layout value set in a style attribute
   survives into a breakpoint where it was never tested. */
.mt-3 { margin-top: var(--s3); }
.mt-5 { margin-top: var(--s5); }
.mt-7 { margin-top: var(--s7); }
.grow { flex: 1; }
.spacer { height: var(--s7); }

.slug { display: flex; align-items: baseline; gap: var(--s4); margin-bottom: var(--s5); }
.slug__n { font-family: var(--f-label); font-size: .68rem; font-weight: 700; letter-spacing: .2em; color: var(--red); }
.slug__t { font-family: var(--f-label); font-size: .68rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-3); }
.slug::after { content: ""; flex: 1; height: 1px; background: var(--rule); }
.section--ink .slug__t { color: #9EA2A9; }
.section--ink .slug::after { background: #333840; }

/* --------------------------------------------------------------------------
   5. Buttons and links â set as print, not as UI
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: var(--s3);
  font-family: var(--f-label); font-size: .86rem; font-weight: 600; letter-spacing: .02em;
  padding: 13px var(--s6); text-decoration: none; border: 1px solid var(--ink);
  transition: background-color .3s var(--ease), color .3s var(--ease), transform .2s var(--ease);
  cursor: pointer;
}
.btn:active { transform: translateY(1px); }
.btn--solid { background: var(--ink); color: var(--paper); }
.btn--solid:hover { background: var(--red); border-color: var(--red); }
.btn--outline { background: transparent; color: var(--ink); }
.btn--outline:hover { background: var(--ink); color: var(--paper); }
.section--ink .btn--solid { background: var(--paper); color: var(--ink); border-color: var(--paper); }
.section--ink .btn--solid:hover { background: var(--red); border-color: var(--red); color: var(--paper); }
.section--ink .btn--outline { color: var(--paper); border-color: #565C66; }
.section--ink .btn--outline:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s4); }

.more {
  display: inline-flex; align-items: baseline; gap: var(--s3);
  font-family: var(--f-label); font-size: .8rem; font-weight: 600; letter-spacing: .04em;
  color: var(--red); text-decoration: none; border-bottom: 1px solid var(--red); padding-bottom: 3px;
}
.more::after { content: "\2192"; transition: transform .3s var(--ease); }
.more:hover::after { transform: translateX(5px); }
@media (prefers-reduced-motion: reduce) { .more:hover::after { transform: none; } }
.more-row { display: flex; flex-wrap: wrap; gap: var(--s5) var(--s7); }

/* --------------------------------------------------------------------------
   6. Banner and plates
   -------------------------------------------------------------------------- */
/* The top padding used to be clamp(48px, 6vw, 88px), which on a wide screen
   put about 130px of nothing between the masthead rule and the kicker â the
   bar's own 24px, the folio line's 16px, and 88px of banner stacked on top of
   each other. A broadsheet does not leave that much white above the first
   line; it starts printing. Halved. */
.banner { padding-block: clamp(32px, 3.4vw, 52px) clamp(40px, 5vw, 64px); }

/* Where a folio line runs above the banner it has already supplied the offset,
   so the banner stops paying for it twice. Adjacent-sibling rather than a
   modifier class, because whether a page has crumbs is a fact about the page's
   structure and nothing should have to remember to declare it. */
.crumbs + .banner { padding-top: var(--s5); }
.banner__grid { display: grid; grid-template-columns: minmax(0, 2.05fr) minmax(0, 1fr); gap: var(--s8); align-items: end; }
.banner__kicker { margin-bottom: var(--s5); }
.banner__aside { border-left: 1px solid var(--rule); padding-left: var(--s6); }
.banner__aside p { font-size: .95rem; color: var(--ink-2); }
@media (width <= 900px) {
  .banner__grid { grid-template-columns: 1fr; gap: var(--s6); }
  .banner__aside { border-left: 0; border-top: 1px solid var(--rule); padding-left: 0; padding-top: var(--s5); }
}

/* The standing picture: a portrait plate in the outer column beside the
   headline. The four service pages and the test bed use it, which is what
   makes them read as a set even though the pictures differ. */
.banner--standing .banner__grid { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); align-items: start; gap: var(--s8); }
.banner--standing .banner__aside { border-left: 0; padding-left: 0; }
.standing { margin: 0; }
.standing .plate__img { height: clamp(320px, 40vw, 460px); }
.standing .plate__img img { height: 100%; object-fit: cover; }
@media (width <= 900px) {
  .banner--standing .banner__grid { grid-template-columns: 1fr; }
  .standing .plate__img { height: clamp(240px, 56vw, 340px); }
}

/* Photographs are printed, not placed. Duotone plus a fine screen so they
   sit on newsprint instead of floating above it. */
.plate { margin: 0; }
.plate__img { position: relative; overflow: hidden; background: var(--paper-3); }
.plate__img img { width: 100%; filter: grayscale(1) sepia(.22) contrast(1.08) brightness(1.02); }
.plate__img::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(#15181D 0.5px, transparent 0.6px);
  background-size: 3px 3px; opacity: .16; mix-blend-mode: multiply;
}
.plate__img--tall { height: clamp(340px, 42vw, 520px); }
.plate__img--tall img { height: 100%; object-fit: cover; object-position: 62% 50%; }
.plate__img--band { height: clamp(200px, 24vw, 300px); }
.plate__img--band img { height: 100%; object-fit: cover; object-position: 50% 45%; }

/* The product screenshot is the only hard evidence on the page, so it is
   exempt from the press treatment â duotone and the halftone screen both eat
   small UI text. It gets a plate of its own instead, which is also the right
   signal: this is a real screen, not a picture we chose. */
.plate--shot .plate__img {
  background: var(--paper-2); border: 2px solid var(--rule-2); padding: 10px;
}
.plate--shot .plate__img img { filter: none; }
.plate--shot .plate__img::after { display: none; }

/* A screenshot narrower than about 900px is unreadable whatever we do, so on
   a phone it scrolls at a legible width rather than shrinking to a smear. */
@media (width <= 760px) {
  .plate--shot .plate__img { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .plate--shot .plate__img img { min-width: 680px; }
}

.plate figcaption {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s4);
  margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--rule);
  font-size: .82rem; line-height: 1.45; color: var(--ink-3);
}
/* Child selector, not descendant: the figure number must never wrap, but a
   <b> lead-in inside the caption text is ordinary running bold and would take
   the page off the side of a phone if it inherited `nowrap`. */
.plate figcaption > b { font-family: var(--f-label); font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--red); font-weight: 600; white-space: nowrap; padding-top: 2px; }
.plate figcaption span b { color: var(--ink-2); font-weight: 600; }
@media (width <= 480px) { .plate figcaption { grid-template-columns: 1fr; gap: var(--s2); } }

/* --------------------------------------------------------------------------
   7. Brief box â artwork that has not been made yet
   -------------------------------------------------------------------------- */
/* Reserves the exact final geometry and prints the commission on the page, so
   the whole image plan can be read off the site and approved before anything
   is generated. Deliberately ugly enough that it can never be mistaken for a
   finished asset, and deliberately not grey: a neutral box looks like a
   loading state, which is how placeholders end up shipping by accident. */
.brief {
  border: 1px dashed var(--red); background: var(--red-wash);
  padding: var(--s5); display: flex; flex-direction: column; gap: var(--s3);
  justify-content: center; min-height: 260px;
}
.brief__head { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); align-items: baseline; }
.brief__tag { font-family: var(--f-label); font-size: .64rem; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--red); margin: 0; }
.brief__spec { font-family: var(--f-label); font-size: .62rem; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); margin: 0; }
.brief__line { font-size: .88rem; line-height: 1.5; color: var(--ink-2); margin: 0; }
.brief__line b { color: var(--ink); }
/* Sits inside .plate__img on a wide slot, so it has to fill the fixed height
   rather than being cropped by object-fit like a photograph would be. */
.plate__img--tall .brief, .standing .plate__img .brief { position: absolute; inset: 0; overflow: auto; }
.plate__img:has(.brief) { background: transparent; }
.plate__img:has(.brief)::after { display: none; }
/* A frame holding a brief reserves its geometry but is allowed to grow. The
   fixed heights above exist so the page does not jump when a real photograph
   decodes; a brief has no such risk, and on a narrow screen a band height of
   200px silently cuts the direction and the reason off the bottom â which is
   the only content on the box anybody is meant to read. `min-height`, not
   `height`, and deliberately no `aspect-ratio` alongside it: the two together
   make the browser resolve width from height and run off the side of a phone. */
.plate__img--band:has(.brief) { height: auto; min-height: clamp(200px, 24vw, 300px); }
.plate__img--tall:has(.brief) { height: auto; min-height: clamp(340px, 42vw, 520px); }
.standing .plate__img:has(.brief) { height: auto; min-height: clamp(320px, 40vw, 460px); }
.plate__img--tall:has(.brief) .brief,
.standing .plate__img:has(.brief) .brief { position: static; overflow: visible; }
@media (width <= 900px) {
  .standing .plate__img:has(.brief) { min-height: clamp(240px, 56vw, 340px); }
}

/* --------------------------------------------------------------------------
   8. Fact box â "the short version" as newspaper furniture
   -------------------------------------------------------------------------- */
.factbox { border-block: 2px solid var(--rule-2); padding-block: var(--s6); }
.factbox__head { display: flex; align-items: baseline; gap: var(--s4); margin-bottom: var(--s5); }
.factbox__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s6); }
.factbox__grid--3 { grid-template-columns: repeat(3, 1fr); }
.factbox__k { font-family: var(--f-label); font-size: .66rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--red); margin-bottom: var(--s3); padding-bottom: var(--s3); border-bottom: 1px solid var(--rule); }
.factbox__v { font-family: var(--f-head); font-size: 1.05rem; line-height: 1.45; color: var(--ink); }
@media (width <= 860px) { .factbox__grid, .factbox__grid--3 { grid-template-columns: 1fr 1fr; gap: var(--s5); } }
@media (width <= 520px) { .factbox__grid, .factbox__grid--3 { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   9. Index â a list of pieces, with numbers in the margin
   -------------------------------------------------------------------------- */
.index { border-top: 1px solid var(--rule); }
.index__row {
  display: grid; grid-template-columns: 64px minmax(0, 1fr) minmax(0, 22rem);
  gap: var(--s6); align-items: baseline;
  padding: var(--s6) 0; border-bottom: 1px solid var(--rule);
  text-decoration: none;
}
.index__row:hover .index__title { color: var(--red); }
/* These are spans, not headings, because the whole row is one <a> and a
   heading inside a link is legal but reads badly to a screen reader here.
   They therefore need display:block spelled out. */
.index__n { display: block; font-family: var(--f-head); font-size: 2.1rem; font-weight: 300; color: var(--red); line-height: 1; }
.index__title { display: block; font-family: var(--f-head); font-size: clamp(1.35rem, 2.1vw, 1.75rem); font-weight: 400; line-height: 1.18; margin: 0 0 var(--s3); transition: color .3s var(--ease); }
.index__body { display: block; font-size: .98rem; color: var(--ink-2); margin: 0; }
.index__cta { display: block; font-family: var(--f-label); font-size: .78rem; font-weight: 600; color: var(--red); }
.index__cta::after { content: " \2192"; }
@media (width <= 860px) {
  .index__row { grid-template-columns: 48px 1fr; gap: var(--s4); }
  .index__row > *:last-child { grid-column: 2; }
}

/* --------------------------------------------------------------------------
   10. Two-column body with a drop cap and marginalia
   -------------------------------------------------------------------------- */
.feature { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 15rem); gap: var(--s8); align-items: start; }
.feature--wide-aside { grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); }
.feature__body { columns: 2; column-gap: var(--s7); column-rule: 1px solid var(--rule); font-size: 1.02rem; }
.feature__body p { margin: 0 0 var(--s4); break-inside: avoid; }
.feature__body > p:first-child::first-letter {
  font-family: var(--f-head); font-weight: 400; float: left;
  font-size: 3.9rem; line-height: .82; padding: 4px var(--s3) 0 0; color: var(--red);
}
.margin-note { border-top: 2px solid var(--rule-2); padding-top: var(--s4); }
.margin-note p { font-size: .88rem; line-height: 1.5; color: var(--ink-2); }
.margin-note .label { margin-bottom: var(--s3); }
@media (width <= 1000px) { .feature, .feature--wide-aside { grid-template-columns: 1fr; gap: var(--s6); } }
@media (width <= 720px) { .feature__body { columns: 1; } }

/* --------------------------------------------------------------------------
   11. Stages â a rule with ticks, numbered above
   -------------------------------------------------------------------------- */
.stages { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(7, 1fr); border-top: 2px solid var(--rule-2); }
.stages--5 { grid-template-columns: repeat(5, 1fr); }
.stages--4 { grid-template-columns: repeat(4, 1fr); }
.stages__step { padding: var(--s5) var(--s4) 0 0; position: relative; }
.stages__step::before { content: ""; position: absolute; top: -2px; left: 0; width: 1px; height: 13px; background: var(--rule-2); }
.stages__step--after::before { background: var(--red); height: 20px; width: 2px; }
.stages__n { display: block; font-family: var(--f-head); font-size: 1.5rem; font-weight: 300; color: var(--ink-3); line-height: 1; margin-bottom: var(--s3); }
.stages__step--after .stages__n { color: var(--red); }
.stages__name { display: block; font-family: var(--f-label); font-size: .92rem; font-weight: 600; margin-bottom: 5px; }
.stages__desc { display: block; font-size: .84rem; line-height: 1.4; color: var(--ink-3); }
.stages-note { font-family: var(--f-label); font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--red); margin: var(--s5) 0 0; padding-left: calc(200% / 7); }
/* The registers one question is read across. Set as ruled small caps rather
   than pills: they are nouns being listed, not controls to press. */
.stages__regs { list-style: none; margin: var(--s3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
.stages__regs li { font-family: var(--f-label); font-size: .64rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); border: 1px solid var(--rule-2); padding: 2px var(--s2); }
@media (width <= 1000px) { .stages { grid-template-columns: repeat(4, 1fr); } .stages--5 { grid-template-columns: repeat(3, 1fr); } .stages-note { padding-left: 0; } }
@media (width <= 600px) { .stages, .stages--5, .stages--4 { grid-template-columns: repeat(2, 1fr); } }

.detail { border-top: 1px solid var(--rule); padding-top: var(--s5); max-width: 44rem; }
.detail summary { font-family: var(--f-label); font-size: .92rem; font-weight: 600; cursor: pointer; list-style: none; display: flex; gap: var(--s3); align-items: center; }
.detail summary::-webkit-details-marker { display: none; }
.detail summary::before { content: "+"; color: var(--red); font-size: 1.15rem; line-height: 1; }
.detail[open] summary::before { content: "\2013"; }
.detail__body { padding-top: var(--s5); color: var(--ink-2); }
/* The pre-launch notes are editorial admin, not content. Marked so they read
   as a note to ourselves and can be found and removed before go-live. */
.detail--note { border-top: 2px solid var(--red); }
.detail--note summary { color: var(--red); }

/* --------------------------------------------------------------------------
   12. Offer â a boxed proposition, set apart like a classified panel
   -------------------------------------------------------------------------- */
.offer { border: 2px solid var(--rule-2); background: var(--paper-2); display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
.offer__body { padding: clamp(32px, 4vw, 56px); }
.offer__photo { border-left: 2px solid var(--rule-2); position: relative; overflow: hidden; min-height: 380px; }
.offer__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) sepia(.22) contrast(1.08); }
.offer__photo::after { content: ""; position: absolute; inset: 0; background-image: radial-gradient(#15181D 0.5px, transparent 0.6px); background-size: 3px 3px; opacity: .16; mix-blend-mode: multiply; }
/* Track record â four checkable figures, set at headline size.
   These are the only large numbers on the site and every one of them can be
   verified by somebody other than us, which is the reason they are allowed to
   be this big. Anything unsourced stays in running text. */
.record { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border-top: 2px solid var(--rule-2); }
.record__cell { padding: var(--s5) var(--s5) var(--s5) 0; border-right: 1px solid var(--rule); }
.record__cell:last-child { border-right: 0; }
.record__n { display: block; font-family: var(--f-head); font-weight: 300; font-size: clamp(2rem, 4vw, 3.1rem); line-height: 1; color: var(--red); margin-bottom: var(--s3); }
.record__n--word { font-size: clamp(1.4rem, 2.4vw, 2rem); letter-spacing: -.01em; }
.record__d { display: block; font-size: .88rem; line-height: 1.45; color: var(--ink-2); padding-right: var(--s5); }
@media (width <= 860px) { .record { grid-template-columns: repeat(2, minmax(0, 1fr)); } .record__cell:nth-child(2n) { border-right: 0; } }
@media (width <= 460px) { .record { grid-template-columns: 1fr; } .record__cell { border-right: 0; border-bottom: 1px solid var(--rule); } }

.terms { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5) var(--s6); }
.terms__k { font-family: var(--f-label); font-size: .64rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--red); margin-bottom: var(--s2); }
.terms__v { font-size: .93rem; line-height: 1.45; color: var(--ink-2); }
@media (width <= 900px) {
  .offer { grid-template-columns: 1fr; }
  /* A fixed height, not an aspect-ratio: with aspect-ratio set the browser
     resolves the width from the height and overflows a phone screen. */
  .offer__photo { border-left: 0; border-top: 2px solid var(--rule-2); min-height: 0; height: clamp(220px, 52vw, 300px); }
  .terms { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   13. Pull quote, points, cards, stories, footer
   -------------------------------------------------------------------------- */
.pull { border-block: 2px solid var(--rule-2); padding-block: var(--s7); text-align: left; }
.pull p { font-family: var(--f-head); font-weight: 300; font-size: clamp(1.5rem, 3.1vw, 2.5rem); line-height: 1.24; margin: 0; max-width: 30ch; }
.pull cite { display: block; margin-top: var(--s5); font-family: var(--f-label); font-size: .68rem; font-style: normal; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3); }

.points { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s7) var(--s8); }
.points--3 { grid-template-columns: repeat(3, 1fr); gap: var(--s6) var(--s7); }
.points li { border-top: 2px solid var(--rule-2); padding-top: var(--s4); color: var(--ink-2); font-size: .98rem; }
.points li b { display: block; font-family: var(--f-head); font-size: 1.2rem; font-weight: 500; color: var(--ink); margin-bottom: var(--s3); line-height: 1.25; }
.points--3 li b { font-size: 1.1rem; }
.section--ink .points li { border-top-color: #565C66; color: #B9BCC2; }
@media (width <= 900px) { .points--3 { grid-template-columns: 1fr 1fr; } }
@media (width <= 760px) { .points, .points--3 { grid-template-columns: 1fr; gap: var(--s6); } }

/* A plain list where a card grid would be filler. Used for the six-item
   "what you get" runs on the service pages. */
.card-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s6) var(--s7); }
.card-list li { border-top: 1px solid var(--rule); padding-top: var(--s4); font-size: .95rem; color: var(--ink-2); }
.card-list li b { display: block; font-family: var(--f-head); font-size: 1.12rem; font-weight: 500; color: var(--ink); margin-bottom: var(--s2); line-height: 1.25; }
@media (width <= 900px) { .card-list { grid-template-columns: 1fr 1fr; } }
@media (width <= 600px) { .card-list { grid-template-columns: 1fr; gap: var(--s5); } }

/* A ticked list where the item is a sentence, not a heading plus a sentence. */
.ticks { list-style: none; margin: 0; padding: 0; }
.ticks li { position: relative; padding: var(--s4) 0 var(--s4) var(--s6); border-bottom: 1px solid var(--rule); color: var(--ink-2); font-size: .98rem; }
.ticks li:first-child { border-top: 1px solid var(--rule); }
.ticks li::before { content: "\2014"; position: absolute; left: 0; top: var(--s4); color: var(--red); }
.ticks li b { color: var(--ink); }
.ticks--cut li::before { content: "\00D7"; }
/* Two lists set side by side â kept / cut, before / after. Equal columns on
   purpose: an uneven split would read as one column being the real answer. */
.duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s8); align-items: start; }
.duo__head { font-family: var(--f-label); font-size: .68rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--red); margin: 0 0 var(--s4); }
@media (width <= 820px) { .duo { grid-template-columns: 1fr; gap: var(--s7); } }

.stories { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s8); }
.story { text-decoration: none; border-top: 2px solid var(--rule-2); padding-top: var(--s4); display: block; }
.story__voice { display: block; font-family: var(--f-label); font-size: .64rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--red); }
.story__voice--eng { color: var(--ink-3); }
.story__title { font-family: var(--f-head); font-size: clamp(1.3rem, 2vw, 1.7rem); font-weight: 400; line-height: 1.18; margin: var(--s3) 0; transition: color .3s var(--ease); }
.story:hover .story__title { color: var(--red); }
.story__excerpt { font-size: .95rem; color: var(--ink-2); margin: 0 0 var(--s4); }
.story__meta { display: block; font-family: var(--f-label); font-size: .68rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.story__plate { margin-bottom: var(--s4); }
.story__plate .plate__img { height: clamp(180px, 22vw, 240px); }
.story__plate .plate__img img { height: 100%; object-fit: cover; }
@media (width <= 760px) { .stories { grid-template-columns: 1fr; gap: var(--s6); } }

.get { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: var(--s8); align-items: start; }
@media (width <= 860px) { .get { grid-template-columns: 1fr; gap: var(--s7); } }
.engage { display: grid; grid-template-columns: 30px minmax(0, 190px) minmax(0, 1fr); gap: var(--s4); padding: var(--s4) 0; border-top: 1px solid var(--rule); align-items: baseline; }
.engage:last-child { border-bottom: 1px solid var(--rule); }
.engage__n { font-family: var(--f-head); font-size: 1.1rem; color: var(--red); }
.engage__name { font-family: var(--f-label); font-weight: 600; font-size: .93rem; }
.engage__desc { font-size: .9rem; color: var(--ink-3); }
@media (width <= 600px) { .engage { grid-template-columns: 26px 1fr; } .engage__desc { grid-column: 2; } }

.site-footer { border-top: 2px solid var(--rule-2); padding-block: var(--s8) var(--s6); }
.footer-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)); gap: var(--s7); }
.footer-blurb { font-size: .93rem; color: var(--ink-2); margin-top: var(--s4); max-width: 25rem; }
.footer-col { display: flex; flex-direction: column; gap: var(--s3); font-size: .9rem; }
.footer-col strong { font-family: var(--f-label); font-size: .66rem; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3); margin-bottom: var(--s2); font-weight: 600; }
.footer-col a { color: var(--ink-2); text-decoration: none; }
.footer-col a:hover { color: var(--red); }
.footer-col span { color: var(--ink-3); }
.footer-bottom { margin-top: var(--s8); padding-top: var(--s5); border-top: 1px solid var(--rule); display: flex; flex-wrap: wrap; gap: var(--s5); justify-content: space-between; font-size: .8rem; color: var(--ink-3); }
@media (width <= 800px) { .footer-grid { grid-template-columns: 1fr 1fr; gap: var(--s6); } }
@media (width <= 500px) { .footer-grid { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   14. Long-form article â the inside page
   -------------------------------------------------------------------------- */
/* Two insight pieces run to 1,300 and 1,900 words. At the page's normal
   measure they would set as a wall, so the article body drops to a single
   narrow column and the furniture â pictures, pull quotes, sources â is what
   breaks it up. Everything here is scoped under .article so the wider marketing
   sections keep their own rhythm. */
.article { max-width: 40rem; margin-inline: auto; }
.article > p { font-size: 1.12rem; line-height: 1.72; margin: 0 0 var(--s5); }
/* The drop cap is set on the first paragraph only, and only where a
   :first-letter can be styled â a paragraph opening on a link or an <em> is
   left alone rather than half-capped. */
.article > p:first-of-type::first-letter {
  float: left; font-family: var(--f-head); font-weight: 300;
  font-size: 3.6em; line-height: .78; padding: .06em .1em 0 0; color: var(--red);
}
.article h2 {
  font-family: var(--f-head); font-weight: 400; font-size: clamp(1.5rem, 3vw, 2rem);
  line-height: 1.18; margin: var(--s8) 0 var(--s5); max-width: 22ch;
}
.article h3 {
  font-family: var(--f-head); font-weight: 500; font-size: 1.24rem;
  line-height: 1.3; margin: var(--s7) 0 var(--s4);
}
.article ul, .article ol { margin: 0 0 var(--s5); padding-left: var(--s5); }
.article li { margin-bottom: var(--s3); line-height: 1.66; }
.article a { color: var(--ink); text-decoration-color: var(--red); }
.article a:hover { color: var(--red); }
.article hr { border: 0; border-top: 1px solid var(--rule); margin: var(--s8) 0; }

/* Pictures and quotes break the measure. That is the point of them â a figure
   that sits inside the column reads as an inset, not as a beat in the piece. */
.article .plate, .article .pull, .article .factbox, .article .detail {
  margin-block: var(--s8);
}
@media (width >= 1000px) {
  .article .plate, .article .pull { width: min(52rem, 92vw); margin-inline: calc((40rem - min(52rem, 92vw)) / 2); }
}
.article .pull p { font-size: clamp(1.4rem, 2.6vw, 2rem); }

.byline {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); align-items: baseline;
  padding-block: var(--s4); border-block: 1px solid var(--rule);
  font-family: var(--f-label); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3);
}
.byline b { color: var(--red); font-weight: 600; }

/* Sources are set as a printed reference list rather than as links buried in
   the prose, because the whole argument of these pieces is that a claim with a
   source beats a claim without one. Burying them contradicts that. */
.sources { border-top: 2px solid var(--rule-2); padding-top: var(--s5); margin-top: var(--s9); }
.sources ol { list-style: none; margin: 0; padding: 0; counter-reset: src; }
.sources li {
  counter-increment: src; position: relative; padding: var(--s3) 0 var(--s3) var(--s7);
  border-bottom: 1px solid var(--rule); font-size: .9rem; color: var(--ink-2);
}
.sources li::before {
  content: counter(src, decimal-leading-zero); position: absolute; left: 0; top: var(--s3);
  font-family: var(--f-label); font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; color: var(--red);
}
.sources a { color: var(--ink); }

/* --------------------------------------------------------------------------
   15. Transcript â a conversation set as printed dialogue
   -------------------------------------------------------------------------- */
/* The communication-channels page carries an invented WhatsApp exchange. A
   mocked-up chat bubble would be a picture of a screen we do not have, which is
   the thing this site keeps promising not to do. Set as dialogue with the
   speaker in the margin, it is unmistakably a transcript and unmistakably not a
   screenshot. */
.transcript { border-top: 2px solid var(--rule-2); border-bottom: 2px solid var(--rule-2); }
.transcript__turn {
  display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: var(--s5);
  padding: var(--s5) 0; border-bottom: 1px solid var(--rule);
}
.transcript__turn:last-child { border-bottom: 0; }
.transcript__who {
  font-family: var(--f-label); font-size: .64rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.transcript__turn--them .transcript__who { color: var(--red); }
.transcript__what { margin: 0; font-size: 1rem; line-height: 1.6; }
.transcript__what + .transcript__what { margin-top: var(--s3); }
.transcript__what b { font-family: var(--f-label); font-size: .9em; letter-spacing: .02em; }
/* The blank weekly report. Set as a ruled form rather than a screenshot of
   one, because the page's whole argument is that we will not show a client's
   actual week. A blank form is the honest artefact: it is what they get. */
.report { border: 2px solid var(--rule-2); background: var(--paper-2); }
.report__head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s3) var(--s5); align-items: baseline; padding: var(--s4) var(--s6); background: var(--rule-2); color: var(--paper); }
.report__head p { margin: 0; }
.report__title { font-family: var(--f-label); font-size: .74rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; }
.report__week { font-size: .86rem; color: var(--rule); }
.report__row { display: grid; grid-template-columns: minmax(0, 190px) minmax(0, 1fr); gap: var(--s5); padding: var(--s5) var(--s6); border-top: 1px solid var(--rule); }
.report__row:first-of-type { border-top: 0; }
.report__k { font-family: var(--f-label); font-size: .88rem; font-weight: 600; }
.report__v { font-size: .92rem; line-height: 1.5; color: var(--ink-3); }
@media (width <= 620px) {
  .report__head { padding: var(--s4) var(--s5); }
  .report__row { grid-template-columns: 1fr; gap: var(--s2); padding: var(--s4) var(--s5); }
  .transcript__turn { grid-template-columns: 1fr; gap: var(--s2); padding: var(--s4) 0; }
}

/* --------------------------------------------------------------------------
   16. Forms â set as a printed coupon, not as a web app
   -------------------------------------------------------------------------- */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5) var(--s6); }
.field { display: flex; flex-direction: column; gap: var(--s2); }
.field--full { grid-column: 1 / -1; }
.field > label {
  font-family: var(--f-label); font-size: .66rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3);
}
.field > label .req { color: var(--red); }
.field input, .field select, .field textarea {
  font-family: var(--f-body); font-size: 1rem; color: var(--ink);
  background: var(--paper-2); border: 0; border-bottom: 2px solid var(--rule-2);
  padding: var(--s3) var(--s2); border-radius: 0; width: 100%;
}
.field textarea { min-height: 150px; resize: vertical; line-height: 1.6; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: 2px solid var(--red); outline-offset: 2px; border-bottom-color: var(--red);
}
.field__hint { font-size: .82rem; color: var(--ink-3); }
.field--check { flex-direction: row; align-items: flex-start; gap: var(--s3); }
.field--check input { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 3px; accent-color: var(--red); border-bottom: 0; }
.field--check label { font-family: var(--f-body); font-size: .9rem; letter-spacing: 0; text-transform: none; color: var(--ink-2); }
@media (width <= 700px) { .form-grid { grid-template-columns: 1fr; } }

/* The form column and the "what happens next" column, side by side. */
.form-layout { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, .65fr); gap: var(--s9); align-items: start; }
@media (width <= 900px) { .form-layout { grid-template-columns: 1fr; gap: var(--s7); } }

/* Validation, sending, and the confirmation.

   THE HONEYPOT. Off-canvas rather than display:none or type="hidden", because
   a bot that fills forms reads the DOM and skips what is switched off, while a
   text input with a label beside it looks like something to fill in. A real
   visitor never reaches it: aria-hidden keeps it out of the accessibility tree
   and tabindex="-1" keeps it out of the tab order. Anything that arrives with
   it filled in is discarded by Formspree, server side, with a 200 back â so a
   bot cannot tell it was caught. If this rule is ever deleted the field
   becomes visible on both forms; it is not decoration. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* A field in error is marked three ways, because colour on its own is not a
   signal every reader receives: the rule under the control turns red AND
   thickens, the label turns red, and a sentence appears saying what is wrong. */
.field--bad > label { color: var(--red); }
.field--bad input, .field--bad select, .field--bad textarea {
  border-bottom-color: var(--red); border-bottom-width: 3px;
  background: var(--red-wash);
}
.field--bad.field--check input {
  outline: 2px solid var(--red); outline-offset: 2px; background: var(--paper-2);
}
/* The consent row is a flex ROW â box then wording â so an error appended to
   it becomes a third column sitting out to the right of the sentence, level
   with it and reading as part of it. Wrapping puts it on its own line under
   the wording, where every other error on the form is. */
.field--check { flex-wrap: wrap; }
.field--check .field__err { flex: 1 0 100%; margin-left: calc(18px + var(--s3)); }
.field__err {
  font-family: var(--f-body); font-size: .85rem; color: var(--red);
  margin: 0; padding-left: var(--s5); position: relative;
}
/* The arm of a proofreader's mark, set in the margin the way this page sets
   its slugs â not a warning triangle borrowed from somebody's alert box. */
.field__err::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: var(--s4); height: 2px; background: var(--red);
}

/* The submit row. The status line sits above the button on its own row rather
   than beside it, so the button does not move when a message appears. */
.form-foot { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s4); }
.form-status {
  font-family: var(--f-label); font-size: .72rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
  margin: 0; padding-left: var(--s5); position: relative;
}
.form-status::before {
  content: ""; position: absolute; left: 0; top: .5em;
  width: var(--s4); height: 2px; background: currentColor;
}
.form-status--bad { color: var(--red); }
.form-status--good { color: var(--ink); }
/* A failure carries a sentence written by Formspree or by the browser, so it
   can be any length. The label treatment above â uppercase, letterspaced â
   is set for four or five words and becomes a wall at twenty, so this variant
   drops back to body type at a readable measure. The count-of-bad-fields line
   keeps the label treatment; it is always short and always ours. */
.form-status--fail {
  font-family: var(--f-body); font-size: .92rem; font-weight: 400;
  letter-spacing: 0; text-transform: none; color: var(--red);
  max-width: 58ch; line-height: 1.5;
}
.form-foot .btn[disabled] { opacity: .55; cursor: progress; }

/* The confirmation that replaces the form once Formspree has taken it. Set as
   a panel under the red rule this site uses for anything it wants read, rather
   than as a green tick â the house signal for consequence is the second plate,
   and a stock success colour would be the one element on the page that came
   from somewhere else. */
.form-sent {
  border-top: 2px solid var(--red); background: var(--paper-2);
  padding: var(--s7) var(--s6) var(--s8);
}
/* Focus is moved here on success so a screen reader lands on the confirmation
   instead of staying on a button that no longer exists. The ring is shown only
   for keyboard focus; a programmatic move should not draw a box. */
.form-sent:focus { outline: none; }
.form-sent:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

@media print { .form-foot .btn, .form-status { display: none; } }

/* --------------------------------------------------------------------------
   17. Insights filter â the voice selector
   -------------------------------------------------------------------------- */
/* Rendered as tabs on a rule rather than as pills. Written into the markup
   already pressed, so if filter.js is blocked the buttons still describe the
   two voices and every post stays visible â a filter that hides content when
   the script fails is worse than no filter. */
.voice-filter {
  display: flex; flex-wrap: wrap; gap: var(--s6);
  border-bottom: 2px solid var(--rule-2); padding-bottom: var(--s3);
}
.voice-filter button {
  font-family: var(--f-label); font-size: .7rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3);
  background: none; border: 0; padding: var(--s2) 0; cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: calc(var(--s3) * -1 - 2px);
}
.voice-filter button:hover { color: var(--ink); }
.voice-filter button[aria-pressed="true"] { color: var(--red); border-bottom-color: var(--red); }
.voice-filter button:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
.stories[data-filtering] .story[hidden] { display: none; }

/* --------------------------------------------------------------------------
   18. Sitemap â the 404 page's contents list
   -------------------------------------------------------------------------- */
.sitemap { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s7); }
.sitemap__col { display: flex; flex-direction: column; gap: var(--s3); }
.sitemap__col strong {
  font-family: var(--f-label); font-size: .66rem; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: var(--red);
  border-bottom: 1px solid var(--rule); padding-bottom: var(--s2); margin-bottom: var(--s2);
}
.sitemap__col a { color: var(--ink-2); text-decoration: none; font-size: .95rem; }
.sitemap__col a:hover { color: var(--red); }
@media (width <= 760px) { .sitemap { grid-template-columns: 1fr; gap: var(--s5); } }

/* --------------------------------------------------------------------------
   19. Print
   -------------------------------------------------------------------------- */
/* A broadsheet that prints badly is an argument against itself. Navigation and
   the halftone screen come off, the duotone filter comes off so pictures do not
   go muddy on paper, and every link's destination is printed after it. */
@media print {
  .masthead__bar, .burger, .nav, .skip-link, .voice-filter, .btn-row, .site-footer { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .plate__img::after { display: none; }
  .plate__img img { filter: none; }
  .section, .shell { padding-block: 0; }
  .article a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .8em; color: #555; word-break: break-all; }
  .plate, .factbox, .pull, .brief, .transcript { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
}
