/* ==========================================================================
   ACME CO — styles.css

   Design language: the document.
   Borrowed from the split sheet CatalogTracker itself generates —
   a mono numeral, a tracked-caps label, and a hairline running to the margin.

   - One ground. No cards, no fills, no shadows, no alternating bands.
   - The hairline is the only structural device. The section rubric IS the
     divider, so one line does the work of a rule, a number, and a heading.
   - Mono carries the document's apparatus — section numerals and their labels,
     figure numerals and captions, dates. Sans carries everything read as
     language: headings, copy, product names, actions.
   - The page is monochrome. The screenshots are the only colour in the
     content; accent blue means "you can act on this" and nothing else.

   Ordering convention: Tokens → Reset → Base → Layout → Components → Sections
   Every spacing value is a token; every token is a multiple of 8.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Spacing — 8-point grid */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-6: 48px;
  --space-8: 64px;
  --space-12: 96px;
  --space-16: 128px;

  /* Type. -apple-system resolves to SF Pro with optical sizing on Apple
     platforms; Inter is the cross-platform stand-in where it is installed.
     The mono is its sibling — SF Mono on Apple — and is used for numerals
     only, which is what earns a second face on a one-typeface site. */
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
    "Inter", "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --text-display: clamp(2.25rem, 1.05rem + 5.1vw, 4.5rem);
  --text-title: clamp(1.75rem, 1.16rem + 2.5vw, 3rem);
  --text-subtitle: clamp(1.5rem, 1.14rem + 1.55vw, 2.25rem);
  --text-lead: clamp(1.1875rem, 1.06rem + 0.55vw, 1.5rem);
  --text-body: 1.0625rem;
  --text-small: 0.9375rem;
  --text-micro: 0.8125rem;

  /* Motion */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-fast: 150ms;
  --duration: 250ms;

  /* Layout */
  --container: 61.25rem; /* 980px */
  --measure: 36rem;      /* ~55 characters at lead size */
  --gutter: clamp(24px, 5vw, 40px);
  --radius-pill: 980px;
  --header-h: 44px; /* HIG touch minimum; design.md exempts targets from the grid */

  /* Where the container's content edge falls, for full-bleed rows that still
     need to line up with it. Resolves against the element that uses it. */
  --edge: max(var(--gutter), calc((100% - var(--container)) / 2 + var(--gutter)));
}

/* Light is the default and the no-JS baseline. */
:root,
:root[data-scheme="light"] {
  color-scheme: light;

  --bg: #ffffff;
  --fg: #1d1d1f;
  --fg-secondary: #6e6e73;
  --accent: #0071e3;      /* selection tint */
  --accent-ink: #0066cc;  /* interaction: link text and focus */
  --separator: rgba(0, 0, 0, 0.12);
  --header-bg: rgba(255, 255, 255, 0.72);
}

:root[data-scheme="dark"] {
  color-scheme: dark;

  --bg: #000000;
  --fg: #f5f5f7;
  --fg-secondary: #a1a1a6;
  --accent: #0071e3;
  --accent-ink: #2997ff;
  --separator: rgba(255, 255, 255, 0.16);
  --header-bg: rgba(0, 0, 0, 0.72);
}

/* Without JavaScript there is no data-scheme, so fall back to the OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-scheme]) {
    color-scheme: dark;

    --bg: #000000;
    --fg: #f5f5f7;
    --fg-secondary: #a1a1a6;
    --accent: #0071e3;
    --accent-ink: #2997ff;
    --separator: rgba(255, 255, 255, 0.16);
    --header-bg: rgba(0, 0, 0, 0.72);
  }
}

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

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

* { margin: 0; }

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

html {
  /* Anchored jumps have to clear the sticky bar, not slide under it. */
  scroll-padding-top: calc(var(--header-h) + var(--space-4));
  -webkit-text-size-adjust: 100%;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: 1.6;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color var(--duration) var(--ease),
              color var(--duration) var(--ease);
}

h1, h3 {
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.08;
  text-wrap: balance;
}

p { text-wrap: pretty; }

a {
  color: var(--accent-ink);
  text-underline-offset: 3px;
  transition: color var(--duration-fast) var(--ease);
}

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

::selection {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

svg { display: block; }

/* --------------------------------------------------------------------------
   Layout
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Tighter than the banded version was. A ruled rubric is a strong divider, and
   a strong divider needs less air around it than a change of background did. */
.section { padding-block: var(--space-8); }

/* A run of short numbered clauses. The rubric already puts 48px between its
   rule and its text, so 48px a side keeps the gap between clauses exactly
   double the gap inside one — proximity doing the grouping. */
.section--clause { padding-block: var(--space-6); }

.lead {
  max-width: var(--measure);
  font-size: var(--text-lead);
  line-height: 1.45;
  color: var(--fg-secondary);
  letter-spacing: -0.01em;
}

.kicker {
  color: var(--fg-secondary);
  font-size: var(--text-small);
}

/* Body prose. Held to the measure so a clause stays inside 60–75 characters
   however wide the window gets. */
.prose {
  max-width: var(--measure);
  color: var(--fg);
}

.prose + .prose { margin-top: var(--space-2); }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   The rubric — number, label, rule. Taken from the generated split sheet.
   It is the section heading and the section divider at the same time.
   -------------------------------------------------------------------------- */

.rubric {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  /* Lighter than the 0.1em the sans wanted — mono is already wide, and caps
     at full tracking came apart into loose letters. */
  letter-spacing: 0.06em;
  line-height: 1;
  text-transform: uppercase;
}

.rubric::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--separator);
  transition: background-color var(--duration) var(--ease);
}

/* No scroll-triggered reveal here on purpose. Hiding structural text until a
   script reveals it means a failed observer costs you the content, and
   design.md is explicit that nothing should move without the visitor causing
   it. Motion on this page is input-driven or it reports state; never both
   absent. */

/* Figures read better untracked than the letters beside them. */
.rubric-index {
  color: var(--fg-secondary);
  letter-spacing: 0;
}

/* --------------------------------------------------------------------------
   Components
   -------------------------------------------------------------------------- */

/* No shadow at rest: its blur would bleed back onto the page from off-screen. */
.skip-link {
  position: absolute;
  top: var(--space-1);
  left: var(--space-1);
  z-index: 10;
  padding: var(--space-1) var(--space-2);
  background: var(--bg);
  border: 1px solid var(--separator);
  border-radius: 8px;
  transform: translateY(calc(-100% - var(--space-4)));
}

.skip-link:focus-visible { transform: none; }

/* The one interactive device on the page. Large text, hairline underline,
   no fill — a pill would be the last piece of borrowed web-UI chrome. */
.action {
  display: inline-block;
  padding-block: var(--space-1);
  color: var(--fg);
  font-size: var(--text-subtitle);
  font-weight: 600;
  /* Display line-height: at 1.6 the line box left ~25px of dead space under
     the text, so the gap to the next rubric measured larger than it was. */
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--separator);
  text-underline-offset: 8px;
  transition: text-decoration-color var(--duration) var(--ease);
}

.action:hover {
  color: var(--fg);
  text-decoration-color: currentColor;
}

.action .arrow {
  display: inline-block;
  transition: transform var(--duration) var(--ease-out);
}

.action:hover .arrow { transform: translateX(4px); }

/* Quiet link — the one sanctioned exception to "accent means interactive".
   It reads as the sentence it sits in until someone reaches for it, then
   commits. The hairline underline is present at rest, so the affordance is
   never carried by colour alone. :active covers touch, where hover cannot. */
.link-quiet {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  /* Mixed from the inherited colour so the resting underline stays legible
     whatever text it sits in — grey colophon or full-strength prose. */
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 4px;
  transition: color var(--duration-fast) var(--ease),
              text-decoration-color var(--duration-fast) var(--ease),
              text-decoration-thickness var(--duration-fast) var(--ease);
}

/* Reached for: the accent arrives and the rule thickens. Two cues, so the
   change never depends on colour alone. :active covers touch, where there is
   no hover to rely on. */
.link-quiet:hover,
.link-quiet:focus-visible,
.link-quiet:active {
  color: var(--accent-ink);
  text-decoration-color: currentColor;
  text-decoration-thickness: 2px;
}

/* --------------------------------------------------------------------------
   Nav — sticky, translucent, blurred. The bar and its hairline run the full
   viewport width rather than the container: it is chrome sitting above the
   document, not part of the document's own ruled structure.
   -------------------------------------------------------------------------- */

/* The bar does not span the viewport. It is two floating chips — a wordmark
   capsule and a detached round control — which is the shape CatalogTracker's own
   tab bar uses. Borrowed from the product for the same reason the rubric was.
   The strip itself is inert so it cannot swallow clicks on the content it
   floats over; only the chips take pointer events. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 5;
  padding-block: var(--space-2);
  pointer-events: none;
}

/* Three tracks, and the outer two are equal by construction — that is what
   puts the middle one on the true centre of the viewport. Flex cannot do
   this: space-between or justify-content: center would centre the wordmark
   in whatever the toggle leaves over, which is the centre of nothing. Equal
   tracks hold regardless of what flanks them, including the no-JS case where
   the toggle is display:none and not a grid item at all.

   minmax(0, 1fr), never a bare 1fr — this is the whole of it. A bare 1fr
   floors at min-content, so at 320px the trailing track was propped open by
   the toggle while the leading track collapsed, the tracks stopped being
   equal, and the mark sat 17.5px left of centre. Measured, not theorised,
   and invisible at every width above it. An explicit 0 minimum makes the
   tracks equal at every viewport and lets the toggle overflow its track,
   which its negative margin already had it doing.

   No gap. The mark is centred by construction and the toggle is pinned to
   the content edge, so a gap moves neither — it only shrinks the spacer
   tracks it sits between. Verified byte-identical geometry with and
   without, so it is not here. */
.header-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  min-height: var(--header-h);
}

/* Nothing until content is underneath, the way a UINavigationBar carries no
   chrome at rest. Same threshold that used to drive the separator, so this
   needed no new script — and with JavaScript off the chips simply stay plain. */
.wordmark,
.appearance {
  pointer-events: auto;
  border: 1px solid transparent;
  transition: background-color var(--duration) var(--ease),
              border-color var(--duration) var(--ease),
              color var(--duration-fast) var(--ease);
}

:root:not(.is-top) .wordmark,
:root:not(.is-top) .appearance {
  background: var(--header-bg);
  border-color: var(--separator);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
}

/* Without backdrop-filter the translucency reads as washed-out text. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root:not(.is-top) .wordmark,
  :root:not(.is-top) .appearance { background: var(--bg); }
}

@media (prefers-reduced-transparency: reduce) {
  :root:not(.is-top) .wordmark,
  :root:not(.is-top) .appearance {
    background: var(--bg);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* Centre track. The capsule's padding is symmetric, so centring the capsule
   centres the letters — no optical correction owed. The negative left margin
   this used to carry is gone with the left-edge alignment it existed to
   produce; reintroducing it would pull the mark off centre by exactly its
   own padding. */
.wordmark {
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  min-height: var(--header-h);
  padding-inline: var(--space-2);
  border-radius: var(--radius-pill);
  color: var(--fg);
  font-size: var(--text-body);
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
}

/* The wordmark is one unstyled text node on purpose. If part of it is ever
   given its own element — a mirrored letter, a coloured glyph, anything —
   note that .wordmark is inline-flex, so each surrounding text run becomes
   its own flex item and innerText comes back broken ("A\nC\nME CO") while
   textContent stays clean. A DOM parser never notices; rendered-text
   extraction, which is how much answer-engine scraping works, reads the
   brand name as separate words. Wrap the whole label in a single span if it
   comes to that, and check innerText, not just textContent. */

/* One toggle, two states. A three-state control could not avoid a dead tap:
   'auto' always resolves to the same scheme as light or dark depending on the
   hour, so one step of any cycle changed nothing on screen. Choosing a scheme
   now sets a temporary override that lapses at the next sunrise or sunset, so
   automatic resumes on its own and every tap flips what you see.

   Revealed only once the script has set data-scheme, since it does nothing
   without it. The icon shows the state you would move to, matching the label. */
.appearance { display: none; }

[data-scheme] .appearance {
  grid-column: 3;
  justify-self: end;
  display: grid;
  place-items: center;
  width: var(--header-h);
  height: var(--header-h);
  /* Kept from when the wordmark overhung the left: the circle overhangs the
     right gutter so the icon itself lands on the content edge. That is still
     right with a centred mark — it is the trailing control holding the grid,
     and it is what keeps a real gap between the two touch targets at 320px
     instead of the 5px true centring would otherwise leave. */
  margin-right: calc(var(--space-2) * -1);
  padding: 0;
  border-radius: 50%;
  background: none;
  color: var(--fg-secondary);
  cursor: pointer;
}

.appearance:hover { color: var(--fg); }

.appearance .icon {
  grid-area: 1 / 1;
  opacity: 0;
  transform: rotate(-45deg) scale(0.7);
  transition: opacity var(--duration-fast) var(--ease),
              transform var(--duration) var(--ease-out);
}

[data-scheme="light"] .icon-dark,
[data-scheme="dark"] .icon-light {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

/* The bottom value is --space-4, not --space-8, and the reason is arithmetic
   rather than taste. The hero's bottom padding and the following section's top
   padding compound: at 64px each they produced a 128px seam, which is the
   --space-16 page-level gap appearing where a --space-12 major-section break
   belongs — a token nobody chose, emergent from two adjacent paddings. 32px
   plus the section's 64px is exactly --space-12, and it lifts everything below
   the hero by 32px on every page. */
.hero { padding-block: var(--space-12) var(--space-4); }

@media (min-width: 48rem) {
  .hero { padding-block: var(--space-16) var(--space-4); }
}

.hero h1 {
  font-size: var(--text-display);
  letter-spacing: -0.03em;
  line-height: 1.04;
}

/* Held at --text-title, but no longer for the original reason. This h1 used to
   be the bare product name, and the argument was that 72px is a lot of weight
   carrying very little information. It now carries a claim as well, so that
   argument has expired — and the size still stands, because a two-part h1 at
   display size is a wall of type rather than a headline. Roughly three lines at
   320px, two on a phone in portrait, one on a laptop.

   Relies on source order rather than added specificity, per the file's stated
   ordering convention. */
.hero--product h1 { font-size: var(--text-title); }

.hero--product .kicker { margin-bottom: var(--space-1); }

.hero .lead { margin-top: var(--space-3); }

/* The hero's action, and the note under it. 48px sets the action apart from the
   lead it follows; 16px keeps the note attached to the action it qualifies.
   Proximity does the grouping, so neither needs a box drawn round it. */
.hero .action { margin-top: var(--space-6); }

.note {
  max-width: var(--measure);
  margin-top: var(--space-2);
  color: var(--fg-secondary);
  font-size: var(--text-small);
}

/* --------------------------------------------------------------------------
   CatalogTracker
   -------------------------------------------------------------------------- */

.product-name {
  margin-top: var(--space-1);
  font-size: var(--text-title);
}

.product .lead { margin-top: var(--space-3); }

/* Screenshots. The row runs full-bleed so a shot is always cut off at the
   right edge — that clipped edge is the only affordance the scroller needs.
   300px caps it: the sources are 600px, which is exactly 2x here and 3x at
   the 200px floor. Going wider would mean re-encoding for no real gain. */
.shots-row {
  display: flex;
  gap: var(--space-2);
  margin: var(--space-8) 0 0;
  padding-inline: var(--edge);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--edge);
}

/* The native bar is hidden only because the readout below replaces it — two
   indicators for one axis is one too many. Gated on `js` because the readout is
   built by a script: design.md says never hide a scrollbar without putting
   something in its place, and with JavaScript off nothing was in its place. The
   row was then a seven-frame scroller whose only affordance was a clipped edge,
   and the meter still rendered "01/07" with a full-width thumb — an instrument
   reporting that everything was visible while six frames sat off-screen.
   Now: script present, meter shown and bar hidden; script absent, the reverse.
   Exactly one indicator either way, and the class is set pre-paint so hiding
   the bar never costs a layout shift. */
:root.js .shots-row {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

:root.js .shots-row::-webkit-scrollbar { display: none; }

/* Drawn inside: at the viewport edges an outset ring would be clipped. */
.shots-row:focus-visible { outline-offset: -3px; }

.shots-row > li {
  flex: none;
  scroll-snap-align: start;
}

.shot {
  --shot-w: clamp(200px, 25vw, 300px);
  margin: 0;
}

.shot img {
  display: block;
  width: var(--shot-w);
  height: auto;
  /* The iPhone's own screen radius, 55 of 402 points, held at every size. */
  border-radius: calc(var(--shot-w) * 0.1368);
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--separator);
}

.shot figcaption {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: color var(--duration-fast) var(--ease);
}

/* The caption brightens under the pointer — the readout acknowledging which
   frame you are looking at. Deliberately not a lift: these are not links, and
   moving them would promise a click that does not exist. */
.shot:hover figcaption { color: var(--fg); }

.shot-index { letter-spacing: 0; }

/* One frame on the company index, where there is no walkthrough for it to be
   part of and so no numeral — design.md numbers these to declare a sequence,
   and a lone numbered figure would be declaring one that is not there. Sized
   exactly like a frame in the row so the two pages agree about how big the
   product is. 48px above groups it with the copy; the action's own 64px below
   separates it. */
.shot--single { margin-top: var(--space-6); }

/* Telemetry for the row: position in the sequence, then a proportional
   indicator. Same shape as a rubric — figure, then a rule to the margin —
   so the instrument is built from the vocabulary already on the page. */
.shots-meter {
  display: none;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding-inline: var(--edge);
}

:root.js .shots-meter { display: flex; }

/* After the rule above, and load-bearing for that reason: equal specificity, so
   source order is what lets the script's `hidden` win when every frame fits.
   The file's ordering convention is the mechanism here, not a coincidence. */
.shots-meter[hidden] { display: none; }

.shots-count {
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.06em;
}

.shots-track {
  position: relative;
  flex: 1;
  height: 1px;
  background: var(--separator);
  transition: background-color var(--duration) var(--ease);
}

/* Sized and placed by transform rather than width/left: it updates on every
   scroll frame, and no transition — an instrument tracks 1:1 or it lies. */
.shots-thumb {
  position: absolute;
  inset: 0;
  background: var(--fg);
  transform-origin: left;
  transform: translateX(calc(var(--thumb-start, 0) * 100%))
             scaleX(var(--thumb-size, 1));
}

.product .action { margin-top: var(--space-8); }

/* --------------------------------------------------------------------------
   Writing

   Long-form is the one place descendant selectors beat explicit classes: a
   piece has twenty-odd paragraphs and nobody should be writing class="prose"
   on each of them. Everything is scoped under .article so nothing leaks.

   Headings here are set in the sans, not the rubric's mono. The rubric carries
   the document's apparatus — numerals, labels, dates — and an h2 phrased as a
   question someone actually asked is language, which design.md gives to the
   sans. The dateline is apparatus, so it keeps the mono.
   -------------------------------------------------------------------------- */

.article { padding-block: var(--space-12) var(--space-8); }

.article h1 { font-size: var(--text-title); }

/* One rule owns vertical flow: every block in the article gets space above it,
   and the exceptions below say how much. This started as `p + p`, which looked
   right and was not — a paragraph following a blockquote, a table or a list is
   not a `p + p`, so it got no space at all and collided with what it followed.
   Specificity is deliberate rather than incidental: the owl is two classes, and
   every override carries a third simple selector so it wins on merit and not on
   source order. */
.article .container > * + * { margin-top: var(--space-2); }

.article .container > h2 { margin-top: var(--space-8); }

.article .container > .lead { margin-top: var(--space-4); }

.article .container > blockquote,
.article .container > table,
.article .container > ul,
.article .container > ol { margin-top: var(--space-3); }

/* The disclaimer is an aside, not the next paragraph, so it gets real air. */
.article .container > .note { margin-top: var(--space-6); }

.article .dateline {
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.06em;
}

.article h2 {
  max-width: var(--measure);
  margin-bottom: var(--space-3);
  font-size: var(--text-subtitle);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  text-wrap: balance;
}

.article p,
.article ul,
.article ol {
  max-width: var(--measure);
}

.article ul,
.article ol { padding-left: var(--space-3); }

.article li + li { margin-top: var(--space-1); }

/* Verbatim from the standard, so it is set apart as quotation rather than
   paraphrased into our own voice. The hairline is the only device it gets. */
.article blockquote {
  max-width: var(--measure);
  margin-top: var(--space-3);
  padding-left: var(--space-3);
  border-left: 1px solid var(--separator);
  color: var(--fg-secondary);
}

/* Identifiers are apparatus, so they are set in the mono. 0.92em because the
   mono's figures render optically larger than the sans at a matched size. */
.article code {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

/* Hairlines and nothing else — no header fill, no zebra striping. The first
   column carries the worked example, split into its four parts, so the table
   is the diagram as well as the reference. */
.article table {
  width: 100%;
  max-width: var(--measure);
  margin-top: var(--space-3);
  border-collapse: collapse;
  font-size: var(--text-small);
}

.article th,
.article td {
  padding: var(--space-1) var(--space-2) var(--space-1) 0;
  border-bottom: 1px solid var(--separator);
  text-align: left;
  vertical-align: baseline;
}

.article th {
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.article tbody th {
  color: var(--fg);
  font-size: var(--text-small);
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}

/* The index. One entry today; the rule between entries is what the second one
   will land against. The titles reuse .action rather than cloning it — a post
   title is navigation set in the page's own type, which is what .action is. */
.posts {
  max-width: var(--measure);
  padding: 0;
  list-style: none;
}

/* On /writing the list follows a rubric, which already carries 48px beneath
   it. On the home page it follows a lead, which carries nothing — so the first
   date collided with the last line of the lead. Same 48px the product entry
   puts between its lead and its frame. */
.lead + .posts { margin-top: var(--space-6); }

.posts > li + li {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--separator);
}

.post-date {
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.06em;
}

/* The title's underline is a strong horizontal, and a summary sitting 8px under
   it reads as attached to the rule rather than to the title. 16px lets the
   summary belong to the entry instead. */
.posts h3 + p { margin-top: var(--space-2); }

/* "All writing", where the home page lists a subset. 64px and not the 48px
   between entries: the action closes the list rather than joining it, and
   matching the interval would read as a fourth headline with no date. Same
   value, for the same reason, as the action closing the product section. */
.posts + .action { margin-top: var(--space-8); }

/* --------------------------------------------------------------------------
   Split sheet

   The design language came from the split sheet CatalogTracker generates, so a
   split sheet on the site is the vocabulary returning to its source. Nothing
   new is invented here: a party block is a rubric with a numeral and a rule,
   and a field is a hairline you write on. design.md already said the hairline
   is the only structural device — on a form it is also the correct affordance,
   which is why this needed no boxes, no fills and no new tokens.

   There is no <form>, no action and no submit. CLAUDE.md rejected a form that
   silently discards an address; this collects nothing and sends nothing, which
   is a different thing and is stated on the page rather than merely true.
   -------------------------------------------------------------------------- */

.sheet { max-width: 44rem; }

/* Same shape as .rubric — mono numeral, tracked-caps label, hairline to the
   margin — one level down, because a party is a subsection of the document
   rather than a section of the page. */
.sheet-block { margin-top: var(--space-6); }

.sheet-block > h3 {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-secondary);
}

.sheet-block > h3::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--separator);
}

/* Two columns where there is room, one where there is not. minmax(0, 1fr) and
   not 1fr: a grid track floors at min-content, and an input's default size
   attribute is wide enough to push a bare 1fr past its share and overflow the
   row on a narrow screen. Same failure the header's centre track had. */
.sheet-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-3);
}

.sheet-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* The label is apparatus, so it is mono and tracked like every other label on
   the site. It sits above the rule rather than inside it — a placeholder that
   vanishes on focus is exactly the pattern that makes printed forms unfillable
   and screen-reader labels disappear. */
.sheet-field label {
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* A rule you write on. No box, no fill, no radius: the hairline already is the
   field, and drawing a container around it would be the first card on the site.
   44px is the HIG touch minimum, which design.md exempts from the 8-grid. */
.sheet-field input {
  min-height: 44px;
  padding: 0 0 var(--space-1);
  border: 0;
  border-bottom: 1px solid var(--separator);
  border-radius: 0;
  background: none;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--text-body);
  transition: border-color var(--duration-fast) var(--ease);
}

/* Accent means "you can act on this", and a field being edited is the clearest
   case of it. The rule thickens as well as changes colour — two cues, the same
   rule .link-quiet follows, so it never rests on colour alone. */
.sheet-field input:focus {
  outline: none;
  border-bottom: 2px solid var(--accent-ink);
  padding-bottom: calc(var(--space-1) - 1px);
}

/* Keyboard users still get the ring; the thickened rule alone is not a focus
   indicator anyone should have to infer. */
.sheet-field input:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 4px;
}

.sheet-field--share { max-width: 12rem; }

/* The totals readout. Same instrument grammar as the screenshot meter: mono
   figures, a hairline above, no easing. It states the shortfall in words as
   well as figures because design.md forbids carrying meaning in colour — and
   having done so, it needs no colour at all, which suits a monochrome page.

   Not a live region: it updates on every keystroke and would chatter. It is
   plain text a screen reader can reach, which is the honest middle. */
.sheet-total {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-3);
  border-top: 1px solid var(--separator);
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.sheet-total output { color: var(--fg); }

/* Signature lines. Deliberately not inputs — a signature is wet ink or it is
   nothing, and offering a text field for one would suggest this page can do
   something it cannot. */
.sheet-sign {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-6) var(--space-4);
  margin-top: var(--space-6);
}

.sheet-sign > div {
  padding-top: var(--space-8);
  border-bottom: 1px solid var(--fg);
}

.sheet-sign p {
  margin-top: var(--space-1);
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.site-footer { padding-block: var(--space-8); }

.colophon {
  margin-top: var(--space-8);
  color: var(--fg-secondary);
  font-size: var(--text-micro);
}

/* The stamp sits last, the way a generated-on line does on a printed document.
   Mono, because it is numerals. Monospaced figures also mean the digits change
   without the line reflowing — a proportional face would jitter every second.
   Hidden until the script fills it, so no empty element without JavaScript. */
.stamp {
  display: block;
  margin-top: var(--space-1);
  color: var(--fg-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: 0.04em;
}

/* --------------------------------------------------------------------------
   Reduced motion — reduce, never remove
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }

  .action:hover .arrow { transform: none; }

  .appearance .icon { transform: none; }
}

/* --------------------------------------------------------------------------
   Print

   Only the split sheet page has anything worth printing, and on that page only
   the sheet itself is. Everything explaining how to use it is scaffolding for
   the screen — printed, it would be four pages of preamble in front of the one
   page somebody actually needs to sign.

   Scoped to .sheet-page so no other page acquires print behaviour by accident.
   -------------------------------------------------------------------------- */

@media print {
  /* The sheet is a legal-ish document that gets photographed, emailed and
     filed. It is black on white wherever it is printed from, regardless of the
     scheme the reader had on screen — a dark-mode print is unreadable on paper
     and wastes the toner making itself so. */
  .sheet-page {
    background: #fff;
    color: #000;
    --fg: #000;
    --fg-secondary: #000;
    --separator: rgb(0 0 0 / 45%);
  }

  .sheet-page .site-header,
  .sheet-page .skip-link,
  .sheet-page .hero,
  .sheet-page .section--guide,
  .sheet-page .site-footer { display: none; }

  .sheet-page .section--sheet { padding-block: 0; }

  /* The rubric survives as the document's title — it is the only heading left
     once the page furniture is gone. Its numeral does not: "01" numbers a
     section of a web page, and printed there is only one. */
  .sheet-page .rubric-index { display: none; }

  .sheet-page .container {
    max-width: none;
    padding-inline: 0;
  }

  .sheet-page .sheet { max-width: none; }

  /* A party block that breaks across a page boundary is the one failure that
     makes a printed form unusable — half the fields on one sheet, the signature
     on another, and no way to tell they belong together. */
  .sheet-block,
  .sheet-sign > div { break-inside: avoid; }

  /* The totals readout reports live state, and on paper there is no live state
     — it prints whatever happened to be on screen and then claims it forever.
     The blank total on the form is the one that gets filled in by hand. */
  .sheet-total { display: none; }

  .sheet-field input {
    /* Borders are dropped by some engines under print colour adjustment, and a
       field with no rule is an invisible one. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    border-bottom: 1px solid rgb(0 0 0 / 45%);

    /* 44px is the HIG touch minimum and paper has no touch. Screen spacing
       printed straight ran the sheet to 2.6 pages, and break-inside on the
       party blocks would have rounded that up to four — a four-page split
       sheet is one nobody prints twice. At 32px it lands on two, which is
       what a four-writer sheet should be, and there is still more room to
       write in than a ruled notebook gives. */
    min-height: 32px;
  }

  /* The same trade, applied to the gaps. Every value here is still a token —
     the grid is tightened, not abandoned, so the printed rhythm is the screen
     rhythm one step down rather than a second layout with its own arithmetic. */
  .sheet-grid { gap: var(--space-2) var(--space-4); }

  .sheet-block { margin-top: var(--space-3); }

  .sheet-page .rubric { margin-bottom: var(--space-3); }

  .sheet-sign { margin-top: var(--space-4); }

  .sheet-sign > div { padding-top: var(--space-6); }

  @page { margin: 16mm; }
}
