/* The Short Read. One stylesheet for the whole site.

   White ground, Archivo and Public Sans, two accents with two jobs that never
   swap. Terracotta carries type: eyebrows, links, numbers, attributions, the
   quiet button. Royal green carries fills and rules: the badge, a callout's
   left edge and tint, the line above a position. Given equal weight the two
   stop reading as a palette and start reading as Christmas, so the separation
   of roles is what makes it work.

   The header used to say "light only, deliberately: the brand is paper". That
   is no longer true and was the only thing standing where a dark palette should
   have been: there was one :root block and no dark mode on the site at all.
   There are three blocks now, the structure the dossier already used.

   The accents get LIGHTER in dark rather than darker, which is why the
   terracotta becomes a salmon and the green a mint. A fill in either accent
   therefore cannot keep white text in both themes, which is what --on-accent
   and --on-mark are for: white on the dark terracotta, near-black on the light
   salmon and mint. Those two tokens exist because five rules in this file wrote
   #fff and #000 as literals, and a palette change is exactly when a literal
   stops being true. */

:root {
  --paper: #FFFFFF;
  --ink: #0B0A09;
  --body: #3D3B38;
  --muted: #6D6A65;
  --rule: #E6E4E0;
  --edge: #C7C4BE;
  --card: #FAF9F7;
  --tile: #F0EEEA;

  /* Type. */
  --accent: #8A2B1E;
  --accent-dark: #6B1F15;
  --accent-pale: #FCF1EF;
  /* Fills and rules. */
  --mark: #0E5A3C;
  --mark-pale: #ECF4F0;

  /* Text that sits on a filled accent or mark. */
  --on-accent: #FFFFFF;
  --on-mark: #FFFFFF;

  /* Body text inside the dark band, which is dark in both themes. */
  --dark-body: #CCC9C4;

  /* Layout, type and spacing. None of these is a colour.

     One content width for every page, and a side padding that grows to centre
     it: --px resolves where it is used, so 100% is that element's own
     containing block and the header, the sections and the footer all share
     the same left edge. */
  --content: 1200px;
  --gutter: 48px;
  --px: max(var(--gutter), calc((100% - var(--content)) / 2));
  --measure: 66ch;
  --masthead-h: 76px;
  --logo-h: 46px;
  --radius: 3px;
  --display: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --sans: 'Public Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  /* The type scale. Archivo is loaded at 700 and 800 only, Public Sans at
     400, 500 and 600, so nothing below asks for a weight that is not there. */
  --t-hero: clamp(36px, 4.6vw, 58px);
  --t-1: clamp(30px, 3.4vw, 44px);
  --t-2: clamp(22px, 2.2vw, 28px);
  --t-3: 19px;
  --t-lede: 18px;
  --t-body: 16px;
  --t-small: 14px;
  --t-label: 12px;

  /* The spacing scale. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 72px;
  --s-9: 96px;
  --section-y: clamp(48px, 6vw, 88px);
}

/* Any light/dark pair of marks: the header's, the home page's large one and
   the footer's. One of each pair is displayed, so one name is announced. */
.mark-dark { display: none; }

/* The reader's own setting, unless the page has asked for light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mark-light { display: none; }
  :root:not([data-theme="light"]) .mark-dark { display: block; }

  :root:not([data-theme="light"]) {
    --paper: #0C0C0B;
    --ink: #F8F7F5;
    --body: #CCC9C4;
    --muted: #95918B;
    --rule: #2A2927;
    --edge: #434039;
    --card: #161513;
    --tile: #1E1C1A;

    --accent: #E58A72;
    --accent-dark: #F2AC97;
    --accent-pale: #231714;
    --mark: #5FC395;
    --mark-pale: #0F1F18;

    /* Both accents are light enough in dark that white on them fails. */
    --on-accent: #0C0C0B;
    --on-mark: #0C0C0B;
  }
}

/* Chosen explicitly, whatever the system says. */
:root[data-theme="dark"] .mark-light { display: none; }
:root[data-theme="dark"] .mark-dark { display: block; }

:root[data-theme="dark"] {
  --paper: #0C0C0B;
  --ink: #F8F7F5;
  --body: #CCC9C4;
  --muted: #95918B;
  --rule: #2A2927;
  --edge: #434039;
  --card: #161513;
  --tile: #1E1C1A;

  --accent: #E58A72;
  --accent-dark: #F2AC97;
  --accent-pale: #231714;
  --mark: #5FC395;
  --mark-pale: #0F1F18;

  --on-accent: #0C0C0B;
  --on-mark: #0C0C0B;
}

/* ---------------------------------------------------------- base */

/* Every :hover rule in this file sits inside @media (hover: hover). A touch
   screen applies :hover to whatever was last tapped and keeps it there, so a
   tapped card stayed underlined and a tapped button stayed grey. Pointers
   that can hover get the hover; touch gets :active and focus as before. */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* Anchor targets land clear of the sticky header. */
  scroll-padding-top: calc(var(--masthead-h) + var(--s-4));
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; }
b, strong { font-weight: 600; }

a { color: var(--accent); }
@media (hover: hover) { a:hover { color: var(--accent-dark); } }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--s-4);
  top: var(--s-4);
  z-index: 40;
  padding: 10px var(--s-4);
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius);
}

/* For a label that a screen reader needs and the page does not show. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------- chrome */

/* The masthead is a sticky bar, paper-backed with the mark showing, on every
   page. The home page is the one exception, and only for the mark: there the
   large mark in the hero stands in for it until it has scrolled away (see
   the end of this file). */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--masthead-h);
  padding-inline: var(--px);
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
}

/* Single-line mark in the header; the nested mark is for large sizes only,
   which is the home page's hero. 46px is the smallest size at which the
   waveform above the words reads as a waveform rather than a hairline. */
.wordmark {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  text-decoration: none;
  color: var(--ink);
}
.wordmark img {
  height: var(--logo-h);
  width: auto;
}

.masthead nav {
  display: flex;
  align-items: center;
  gap: var(--s-6);
}
.masthead nav a {
  font-size: var(--t-body);
  font-weight: 500;
  text-decoration: none;
  color: var(--body);
  white-space: nowrap;
}
@media (hover: hover) { .masthead nav a:hover { color: var(--ink); } }
.masthead nav a[aria-current="page"] {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 8px;
  text-decoration-color: var(--accent);
}

.foot {
  padding: var(--s-6) var(--px);
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4) var(--s-5);
  flex-wrap: wrap;
  font-size: var(--t-small);
  color: var(--muted);
}
.foot-brand {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}
.foot nav { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); }
.foot a { color: var(--muted); text-decoration: none; }
@media (hover: hover) { .foot a:hover { color: var(--ink); } }

/* ---------------------------------------------------------- type */

h1, h2, h3 { margin: 0; font-weight: 700; }

.display {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.hero-title {
  font-size: var(--t-hero);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.032em;
}
.page-title {
  font-size: var(--t-1);
  line-height: 1.06;
  letter-spacing: -0.026em;
}
.section-title,
.band-title,
.sample-title,
.prose h2 {
  font-family: var(--display);
  font-size: var(--t-2);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
}
.minor-title,
.step h3,
.band-item h3 {
  font-family: var(--display);
  font-size: var(--t-3);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.005em;
}

.lede {
  margin: 0;
  font-size: var(--t-lede);
  line-height: 1.6;
  color: var(--body);
  max-width: var(--measure);
}

/* The one label style: eyebrows, form labels, the card's section labels,
   the account rows and the genre. */
.eyebrow,
.label,
label.tag,
.said span:not(.badge),
.row-item dt,
.genre {
  font-family: var(--sans);
  font-size: var(--t-label);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.small, .note {
  margin: 0;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--muted);
}

/* ---------------------------------------------------------- forms */

.field { display: flex; flex-direction: column; gap: var(--s-2); }

.row { display: flex; gap: var(--s-2); }
.row > input { flex: 1 1 auto; min-width: 0; }

input[type="email"],
input[type="text"],
input[type="search"] {
  height: 54px;
  width: 100%;
  padding: 0 var(--s-4);
  font-family: var(--sans);
  font-size: var(--t-body);
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
}
input::placeholder { color: var(--muted); }
input:focus-visible { outline-offset: 2px; }

/* The search field's own clear button is drawn in system blue, the only
   colour on the site outside the palette. Greyed into it. */
input[type="search"]::-webkit-search-cancel-button { filter: grayscale(1); opacity: 0.6; }

.btn {
  height: 54px;
  padding: 0 var(--s-6);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font-family: var(--sans);
  font-size: var(--t-body);
  font-weight: 600;
  line-height: 1;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.btn-solid { background: var(--ink); border-color: var(--ink); color: var(--paper); }
/* A step down the ink ramp, not a fill in either accent: terracotta is for
   type and green for marks, so neither belongs on a hovered button. */
@media (hover: hover) { .btn-solid:hover { background: var(--body); border-color: var(--body); color: var(--paper); } }
.btn-quiet { background: transparent; color: var(--ink); border-color: var(--ink); }
@media (hover: hover) { .btn-quiet:hover { background: var(--tile); color: var(--ink); } }
.btn-small { height: 44px; padding: 0 var(--s-5); font-size: var(--t-small); }
.btn-block { width: 100%; }
.btn:disabled,
.btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }
@media (hover: hover) { .btn-solid:disabled:hover { background: var(--ink); border-color: var(--ink); } }

/* "or", as a rule with a word in it, between the two ways in. */
.or {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.or::before, .or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--rule);
}

.error {
  margin: 0;
  font-size: var(--t-small);
  color: var(--accent-dark);
  font-weight: 500;
}
[hidden] { display: none !important; }

/* The sign-up, and the account page's sign-in, are the same component:
   one solid button, the rule, then the address with a quiet button. */
.signup {
  max-width: 520px;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.signup form { display: flex; flex-direction: column; gap: var(--s-2); }

@media (max-width: 520px) {
  .row { flex-direction: column; }
  .row > input { flex: 0 0 auto; }
  .row .btn { width: 100%; }
}

/* ---------------------------------------------------------- cards */

.card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s-6);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.badge {
  align-self: flex-start;
  font-size: var(--t-label);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-mark);
  background: var(--mark);
  padding: 6px 9px;
  border-radius: var(--radius);
}

hr.rule { width: 100%; border: none; border-top: 1px solid var(--rule); margin: 0; }

.said { display: flex; flex-direction: column; gap: var(--s-2); }
.said p { margin: 0; font-size: var(--t-body); line-height: 1.6; color: var(--body); max-width: var(--measure); }
.said p.small { font-size: var(--t-small); color: var(--muted); }

/* ---------------------------------------------------------- home */

.hero {
  padding: var(--s-7) var(--px) var(--section-y);
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  column-gap: var(--s-8);
  row-gap: var(--s-7);
  align-items: start;
}
.hero-brand { grid-column: 1 / -1; }
/* The nested mark at a size where its seven contours separate. Never
   cropped: the width is the only thing set and the height follows. */
.hero-mark {
  width: min(100%, 580px);
  height: auto;
}
.hero-mark.mark-light { display: block; }

.hero-copy {
  max-width: 600px;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.hero-copy .signup { margin-top: var(--s-2); }

/* The three steps: numeral beside the heading, so a step is one compact
   unit rather than a tall column of grey. */
.steps-side {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  padding-top: var(--s-2);
}
.step {
  display: grid;
  grid-template-columns: 2.4em minmax(0, 1fr);
  column-gap: var(--s-3);
  row-gap: var(--s-1);
  align-items: baseline;
}
.step .num {
  font-family: var(--display);
  font-size: var(--t-3);
  font-weight: 700;
  color: var(--accent);
}
.step p {
  grid-column: 2;
  margin: 0;
  font-size: var(--t-body);
  line-height: 1.55;
  color: var(--body);
}

/* The categories: three cards, the live one with its action, the others
   information only. Built from the card, badge, label and button. */
.categories {
  padding: 0 var(--px) var(--section-y);
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}
.cats-head { display: flex; flex-direction: column; gap: var(--s-3); }
.cats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5);
}
.cat { gap: var(--s-3); }
.cat p { margin: 0; }
.cat > p:not(.label), .cat-list { font-size: var(--t-body); line-height: 1.6; color: var(--body); }
.cat-list { margin: 0; padding-left: 1.2em; }
.cat-actions {
  margin-top: auto;
  padding-top: var(--s-2);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
}
.cat-actions .card-link { display: inline-flex; align-items: center; min-height: 44px; }
/* Coming soon: the badge's shape, in the label's colour. */
.badge-quiet {
  color: var(--muted);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--edge);
}

/* The sample is the product's proof, at a readable width. */
/* Two extracts: the disputed story, wide, and the generated card beside it.
   One column, each at a reading width, below 1080. */
.sample {
  padding: 0 var(--px) var(--section-y);
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--s-6);
  align-items: start;
}

/* The shorter card keeps its place beside the long one as the page scrolls. */
.sample > .card:not(.showcase) {
  position: sticky;
  top: calc(var(--masthead-h) + var(--s-5));
}

/* The disputed story. The disagreement is set off by a 2px --mark rule, as
   in the letter; each side gives its answer in Archivo, then who holds it
   in the accent, then why. The sides sit side by side when the card is
   wide enough for two readable columns, and stack when it is not. */
.split {
  /* A little more air than the card's own gap, above and below, so the
     disagreement reads as its own block between agree and added. */
  margin-block: var(--s-2);
  border-left: 2px solid var(--mark);
  padding-left: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.split-q {
  margin: 0;
  font-family: var(--display);
  font-size: var(--t-3);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.positions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s-5);
  padding-top: var(--s-1);
}
.position p { margin: 0; }
.position .stance {
  font-family: var(--display);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
}
.position .who {
  margin: var(--s-1) 0 var(--s-2);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--accent);
}
.position p:last-child {
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--body);
}

.sample-tail { padding-top: var(--s-2); }
.sample-head { grid-column: 1 / -1; margin: 0; }

/* The dark band. Dark in both themes, by inverting ink and paper. */
.band {
  padding: var(--section-y) var(--px);
  background: var(--ink);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}
.band .band-title { color: var(--paper); max-width: 22em; }
.band-head { display: flex; flex-direction: column; gap: var(--s-3); }
.band .band-label { margin: 0; color: var(--dark-body); }
.band-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-6) var(--s-8);
}
.band-item { display: flex; flex-direction: column; gap: var(--s-2); }
.band-item h3 { color: var(--paper); }
.band-item p { margin: 0; font-size: var(--t-body); line-height: 1.6; color: var(--dark-body); max-width: var(--measure); }

/* ---------------------------------------------------------- catalogue */

.picker-head {
  padding: var(--s-7) var(--px) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

.browse {
  padding: 0 var(--px) var(--s-5);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
}
.browse .search { flex: 1 1 320px; max-width: 480px; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
/* Pills are for the genre filter and nothing else. */
.chip {
  min-height: 40px;
  font-family: inherit;
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--body);
  background: var(--paper);
  border: 1px solid var(--edge);
  border-radius: 999px;
  padding: 0 var(--s-4);
  cursor: pointer;
}
@media (hover: hover) { .chip:hover { border-color: var(--ink); color: var(--ink); } }
.chip[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* Four columns on a desktop, three on a tablet, two on a phone. Fixed
   counts rather than auto-fill, because each description is shown whole and
   its length is capped against these widths (tests/test_catalogue_copy.py):
   at four columns a card is about 285px wide and no description runs past
   three lines. Cards in a row stretch to the tallest, so they line up. */
.show-grid {
  padding: 0 var(--px);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-5) var(--s-4);
}
@media (max-width: 1199px) {
  .show-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* A card is one toggle button: tapping anywhere on it selects the show. */
.show {
  position: relative;
  margin: 0;
  padding: 0;
  width: 100%;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  font-family: inherit;
  font-size: inherit;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  overflow: hidden;
}
@media (hover: hover) { .show:hover:not(:disabled) { border-color: var(--edge); } }
@media (hover: hover) { .show:hover:not(:disabled) .show-name { text-decoration: underline; text-underline-offset: 3px; } }
.show[aria-pressed="true"] {
  border-color: var(--mark);
  box-shadow: 0 0 0 1px var(--mark);
}
.show:disabled { cursor: not-allowed; }
.show:disabled .art, .show:disabled .tile, .show:disabled .show-body { opacity: 0.45; }

.art-wrap { position: relative; display: block; }
/* Square, the full width of the card, never distorted. */
.art {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--tile);
  border-bottom: 1px solid var(--rule);
}
.tile {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--tile);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* The selected mark sits on the artwork, top right. Empty ring until
   selected, then the green disc with a tick. */
.check {
  position: absolute;
  top: var(--s-2);
  right: var(--s-2);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  color: transparent;
  background: var(--paper);
  border: 2px solid var(--edge);
}
.show[aria-pressed="true"] .check {
  color: var(--on-mark);
  background: var(--mark);
  border-color: var(--mark);
}

.show-body {
  flex: 1;
  padding: var(--s-3) var(--s-4) var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.show-name {
  font-family: var(--display);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.25;
}
.meta { font-size: 13px; line-height: 1.4; color: var(--muted); }
/* Whole, never clamped: the cap is on the copy, not the box. */
.blurb {
  margin-top: var(--s-1);
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--body);
}
/* Pinned to the foot of the card, so the genres in a row line up however
   long each description is. */
.genre { margin-top: auto; padding-top: var(--s-2); font-size: 11px; }

.no-results {
  margin: 0;
  padding: var(--s-6) var(--px);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--body);
}
.grid-error { padding: var(--s-4) var(--px); }

/* Something missing: a plain line and a mail link, because there is nothing
   behind a form to receive a request. */
.request {
  margin: var(--s-7) 0 0;
  padding: 0 var(--px);
  font-size: var(--t-body);
  color: var(--body);
}

/* The address, asked for after the shows are chosen. Same block the home
   page used to hold. It sits above the sticky bar, with room so the bar
   does not cover the field when the page scrolls it into view. */
.join {
  padding: var(--s-6) var(--px) 0;
  scroll-margin-bottom: 96px;
}
.join .signup { width: 100%; }

/* The action bar. Sticky to the bottom of the window, so the count and
   Continue are on screen from the first card to the last. It sits at the
   end of main, so after the last card it simply comes to rest there. */
.action-bar {
  position: sticky;
  bottom: 0;
  z-index: 15;
  margin-top: var(--s-7);
  padding: var(--s-3) var(--px);
  background: var(--paper);
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}
.count { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.counter {
  font-size: var(--t-body);
  font-weight: 600;
  color: var(--ink);
}
.counter[data-full="true"] { color: var(--mark); }
.count-note { font-size: 13px; color: var(--muted); }
.actions { display: flex; align-items: center; gap: var(--s-5); flex-shrink: 0; }
.actions .back { font-size: var(--t-small); color: var(--muted); text-decoration: none; }
@media (hover: hover) { .actions .back:hover { color: var(--ink); } }
.action-error { padding: 0 var(--px); margin-top: var(--s-3); text-align: right; }

@media (max-width: 760px) {
  .show-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4) var(--s-3); }
  .show-body { padding: var(--s-2) var(--s-3) var(--s-3); }
  .show-name { font-size: 15px; }
  .blurb { font-size: 13px; }
  .tile { font-size: var(--t-2); }
  .browse .search { max-width: none; }
}
@media (max-width: 420px) {
  .actions .back { display: none; }
  .actions .btn { padding: 0 var(--s-5); }
}

/* ---------------------------------------------------------- account */

.account {
  padding: var(--s-7) var(--px) var(--section-y);
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}
.account .page-copy { max-width: 640px; }

.page-copy { display: flex; flex-direction: column; gap: var(--s-4); }
.page-copy > p:not(.small):not(.error):not(.note) {
  margin: 0;
  font-size: var(--t-lede);
  line-height: 1.6;
  color: var(--body);
  max-width: var(--measure);
}
.page-copy .signup { margin-top: var(--s-2); }
.page-copy > .btn { align-self: flex-start; margin-top: var(--s-2); }

.account-card { padding: 0; gap: 0; max-width: 820px; }
.rows { margin: 0; display: flex; flex-direction: column; }
.row-item {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: var(--s-5);
  padding: var(--s-5) var(--s-5);
  border-bottom: 1px solid var(--rule);
}
.row-item:last-child { border-bottom: 0; }
.row-item dt { padding-top: 3px; }
.row-item dd { margin: 0; font-size: var(--t-body); line-height: 1.55; color: var(--ink); }
.row-item dd .small { display: block; margin-top: var(--s-1); }
.row-item dd a { color: var(--accent); font-weight: 600; }

.show-strip {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
}
.show-strip li { width: 72px; }
.show-strip img, .show-strip .tile {
  width: 72px;
  height: 72px;
  aspect-ratio: auto;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  object-fit: cover;
  font-size: 20px;
}
.show-strip .name {
  display: block;
  margin-top: var(--s-2);
  font-size: var(--t-label);
  line-height: 1.3;
  color: var(--muted);
}
.show-strip .none { width: auto; font-size: var(--t-body); color: var(--muted); }

.quiet-foot { margin: 0; max-width: var(--measure); }

@media (max-width: 560px) {
  .row-item { grid-template-columns: minmax(0, 1fr); gap: var(--s-2); padding: var(--s-4) var(--s-4); }
}

/* ---------------------------------------------------------- confirm */

.confirm {
  padding: var(--s-7) var(--px) var(--section-y);
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--s-8);
  align-items: start;
}
.confirm .card { max-width: 440px; }
#where.is-address { font-weight: 600; color: var(--ink); }

.chosen { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--s-2); }
.chosen li { font-size: var(--t-body); display: flex; align-items: baseline; gap: var(--s-3); }
.chosen li::before { content: "\25CF"; color: var(--accent); font-size: var(--t-label); }

.when { display: flex; flex-direction: column; gap: var(--s-1); }
.when b { font-family: var(--display); font-size: var(--t-1); font-weight: 700; line-height: 1; }
.when b span { font-family: var(--sans); font-size: var(--t-lede); font-weight: 500; color: var(--muted); }
.card-link { font-size: var(--t-small); font-weight: 600; text-decoration: none; }

/* ---------------------------------------------------------- privacy */

/* Left-aligned on the same edge as every other page, at a reading width. */
.prose {
  padding: var(--s-7) var(--px) var(--section-y);
}
.prose > * { max-width: 720px; }
.prose .page-title { margin: 0 0 var(--s-4); }
.prose .lede { margin: 0 0 var(--s-2); }
.prose h2 { margin: var(--s-7) 0 var(--s-3); }
.prose p {
  margin: 0 0 var(--s-4);
  font-size: 17px;
  line-height: 1.65;
  color: var(--body);
}
.prose p.lede { font-size: var(--t-lede); }
.prose p.small { margin-top: var(--s-7); font-size: var(--t-small); color: var(--muted); }
.prose ul {
  margin: 0 0 var(--s-4);
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.prose li {
  position: relative;
  padding-left: 22px;
  font-size: 17px;
  line-height: 1.6;
  color: var(--body);
}
.prose li::before {
  content: "\25CF";
  position: absolute;
  left: 0;
  top: -0.05em;
  color: var(--accent);
  font-size: var(--t-label);
}
.prose li b { color: var(--ink); }

/* ---------------------------------------------------------- narrow */

@media (max-width: 1080px) {
  :root { --gutter: 32px; }
  .hero, .confirm { grid-template-columns: minmax(0, 1fr); gap: var(--s-6); }
  .sample { grid-template-columns: minmax(0, 1fr); }
  .cats { grid-template-columns: minmax(0, 1fr); max-width: 760px; }
  .sample > .card:not(.showcase) { position: static; }
  .sample .card { max-width: 760px; }
  .hero { row-gap: var(--s-6); }
  .steps-side { padding-top: 0; max-width: 600px; }
}

@media (max-width: 720px) {
  :root {
    --gutter: 20px;
    --masthead-h: 64px;
    --logo-h: 38px;
  }
  .masthead nav { gap: var(--s-4); }
  .masthead nav a { font-size: 15px; }
  .hero { padding-top: var(--s-5); row-gap: var(--s-5); }
  .hero-copy { gap: var(--s-4); }
  .hero-mark { width: 100%; }
  .lede { font-size: 17px; }
  .band-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
  .card { padding: var(--s-5); }
  .split { padding-left: var(--s-4); }
  .picker-head, .account, .confirm, .prose { padding-top: var(--s-6); }
  .steps-side { gap: var(--s-4); }
}

/* Three links do not fit beside the mark on a narrow phone: the row is
   about 258px and starts after the mark, so it runs past 430px. On a phone
   the mark takes the first line of the bar and the links the second, as one
   row that never wraps. If the row is still wider than the screen it scrolls
   sideways on its own (no scrollbar shown); the page does not get wider.
   This query does not apply above 460px, and it changes no selector but the
   header nav. */
@media (max-width: 460px) {
  /* The bar grows to hold the second line. A fixed 64px height clips it,
     and a 12px link is not a 40px target. */
  .masthead {
    height: auto;
    min-height: var(--masthead-h);
    padding-block: 2px;
    flex-wrap: wrap;
    row-gap: 0;
  }
  .wordmark { min-height: 40px; }
  /* The row runs to both edges of the screen, so a link scrolled under the
     edge is cut there rather than at the gutter. The padding inside puts the
     first link back on the gutter and leaves room for the focus ring, which
     an overflowing box would otherwise clip; the negative margins give that
     room back, so the bar is no taller for it. */
  .masthead nav {
    flex: 1 0 100%;
    min-width: 0;
    flex-wrap: nowrap;
    justify-content: flex-start;
    column-gap: var(--s-4);
    margin: -5px calc(-1 * var(--gutter));
    padding: 5px var(--gutter);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .masthead nav::-webkit-scrollbar { display: none; }
  .masthead nav a {
    flex: 0 0 auto;
    font-size: var(--t-label);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
    min-width: 40px;
    padding-inline: 2px;
  }
  .foot a {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }
}

/* ---------------------------------------------------------- home: the mark

   The home page shows its mark once. At the top the hero's large mark is it,
   and the header's small mark is held back; once the large mark has gone
   under the header the small one takes over. The swap is driven by an
   IntersectionObserver in index.html, and only runs when script has run
   (html.js), so without script both marks show rather than neither. */
.js .masthead[data-logo="hero"] .wordmark {
  visibility: hidden;
}

/* ---------------------------------------------------------- home: the opener

   The home page opens on the paper and the nested mark alone, one screen
   tall. The header is fixed over the page and parked above the top edge
   (html.js only, so without script it is simply there); index.html sets
   data-state="shown" as the mark finishes fading on scroll (or, with
   prefers-reduced-motion, once it has left the window).
   Focus inside the header brings it in too, so a keyboard user tabbing to
   the nav always sees where they are. */
.opener {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  padding: var(--s-8) var(--px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.opener-brand { width: min(100%, 640px); }
.opener-mark { width: 100%; height: auto; }
.opener-mark.mark-light { display: block; }

.opener-cue {
  position: absolute;
  left: 50%;
  bottom: max(var(--s-6), env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-2) var(--s-3);
  min-height: 44px;
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
@media (hover: hover) { .opener-cue:hover { color: var(--ink); } }
.opener-cue svg { display: block; }
/* The cue fades with the mark (an inline opacity from index.html); a
   keyboard user who lands on it still sees it. */
.opener-cue:focus-visible { opacity: 1 !important; }
@media (prefers-reduced-motion: no-preference) {
  .opener-cue svg { animation: cue 2.4s ease-in-out infinite; }
  @keyframes cue {
    0%, 60%, 100% { transform: translateY(0); }
    30% { transform: translateY(4px); }
  }
}

.js .masthead-reveal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}
.js .masthead-reveal[data-state="away"]:not(:focus-within) {
  transform: translateY(-100%);
}
@media (prefers-reduced-motion: no-preference) {
  .js .masthead-reveal { transition: transform 0.24s ease-out; }
}

/* The header no longer takes a row of its own on this page, so the hero
   keeps clear of it when it has arrived. */
.js .hero-after-opener { padding-top: calc(var(--masthead-h) + var(--s-6)); }

@media (max-width: 720px) {
  .opener { padding-block: var(--s-7); }
  .opener-brand { width: 100%; }
}

/* ---------------------------------------------------------- home: the products

   One section per product, in a column, after the opener. The category grid
   was three cards across; this page is a scroll, so the sections stack and
   the Deep Read's crop sits beside its text only while the row is wide
   enough. The frame uses the paper, the rule and the radius already on the
   site. The crop itself stays the white page it was rendered as. */

.home-intro {
  padding: var(--s-7) var(--px) var(--s-6);
}
.home-intro .lede { max-width: var(--measure); }
.js .home-intro.hero-after-opener {
  padding-top: calc(var(--header-h, var(--masthead-h)) + var(--s-6));
}

.product {
  padding: var(--s-7) var(--px);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-5);
}
.product-copy {
  max-width: var(--measure);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-4);
}
.product-copy > p:not(.label) {
  margin: 0;
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--body);
}
.product-more {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  font-weight: 600;
}
.product-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
}

/* The crop sits beside the text. The link stays in the text column, so it
   follows the paragraph rather than dropping to the foot of the image. */
.product.has-sample {
  display: grid;
  grid-template-columns: minmax(0, 36rem) minmax(0, 26rem);
  column-gap: var(--s-7);
  align-items: start;
  justify-content: start;
}
.product.has-sample .sample-frame { grid-column: 2; grid-row: 1; }

.sample-frame {
  margin: 0;
  min-width: 0;
  padding: var(--s-4);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.sample-frame img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--edge);
}
.sample-frame figcaption { margin: 0; }

/* The Deep Read sample is an SVG of a real page, shown no larger than the
   page itself: the crop is 453 pt of an A4 page, which is 604 px at real
   size. On a phone it takes the full width. */
.sample-frame.page-sample {
  width: 100%;
  max-width: calc(600px + 2 * var(--s-4) + 2px);
}
.sample-frame.page-sample img { max-width: 600px; }

@media (max-width: 800px) {
  /* Text, then the crop, then Learn more. display:contents lets the link,
     which lives in the text column on a wide screen, move below the crop. */
  .product.has-sample {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }
  .product.has-sample .product-copy { display: contents; }
  .product.has-sample .sample-frame { order: 2; max-width: 36rem; }
  .product.has-sample .product-more { order: 3; align-self: flex-start; }
  .product.has-sample .product-actions { order: 3; }
}

/* ---------------------------------------------------------- home: the phone row

   On a phone the four product sections are a row of cards that the reader
   swipes sideways, one card to a stop. Each card is 85% of the row, so the
   next one shows at the right edge: that is the sign the row moves. Only
   the row scrolls; the page does not get wider. Above 460px the wrapper has
   no style and nothing here applies. The dots under the row are made by
   index.html; without script there are none and the row works the same. */
.product-dots { display: none; }

@media (max-width: 460px) {
  .product-row {
    display: flex;
    align-items: stretch;
    gap: var(--s-3);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    padding: var(--s-5) var(--gutter) var(--s-2);
    border-top: 1px solid var(--rule);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .product-row::-webkit-scrollbar { display: none; }
  .product-row > .product {
    flex: 0 0 85%;
    min-width: 0;
    scroll-snap-align: start;
    padding: var(--s-5) var(--s-4);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    gap: var(--s-4);
  }
  .product-row .sample-frame { padding: var(--s-3); }
  /* Every card is as tall as the row, so the four read as one set, and the
     Sign up and Learn more links sit on the foot of their cards, level
     with each other, whatever the text above them runs to. */
  .product-row .product-actions,
  .product-row .product-copy > .product-more { margin-top: auto; }

  .product-dots {
    display: flex;
    justify-content: center;
    gap: var(--s-1);
    padding: 0 var(--gutter) var(--s-6);
  }
  .product-dots button {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    background: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
  .product-dots button::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--edge);
  }
  .product-dots button[aria-current="true"]::before { background: var(--ink); }
}
