/* ============================================================
   SCREEN — the LAST sheet in the theme layer (buildless).
   Not an entry point any more: default.hbs links all eleven
   screen sheets flat, in cascade order — tokens (fonts, colours,
   typography, spacing, treatments), then base → layout →
   components → koenig → prose, then this file. It used to
   @import the five theme sheets above, which put them a round
   trip behind their own manifest; flat links let the preload
   scanner fetch the lot in parallel.
   This file must stay LAST: the rules below were written to sit
   after those five, and several win on order alone.
   Layout values ported from the design-system screen references
   (04-screens + demos).
   ============================================================ */

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

/* ============================================================
   THE FOLD / HERO
   Type on bare paper (left) layered over the one live field,
   which bleeds off the right edge. The legibility fade is the
   system's single permitted gradient: over field, under copy.

   Height is a token set per @custom.hero_height, and the plate
   is sized from it rather than from a viewport fraction, so the
   fold stays a band across the page instead of a full screen of
   paper the reader has to scroll past to reach the register.
   ============================================================ */
.hero {
  --hero-h: 32rem;
  position: relative;
  min-height: var(--hero-h);
  display: grid;
  align-items: center;
  overflow: hidden;
}
.hero--compact  { --hero-h: 24rem; }
.hero--standard { --hero-h: 32rem; }
.hero--tall     { --hero-h: 44rem; }
/* plate only: no type to clear, so the band can be shallower still */
.hero--bare.hero--compact  { --hero-h: 18rem; }
.hero--bare.hero--standard { --hero-h: 24rem; }
.hero--bare.hero--tall     { --hero-h: 34rem; }
.hero__field {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 58vw;
  z-index: 1;
}
/* with no type beside it the plate takes the whole band */
.hero--bare .hero__field { width: 100%; }
.hero__field .plate-field { height: 100%; }
.hero__field .plate-field--hero { aspect-ratio: auto; }
/* the one permitted gradient — solid paper (left) fading to clear (right) */
.hero__field::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(90deg,
    var(--paper) 0%,
    color-mix(in srgb, var(--paper) 55%, transparent) 32%,
    transparent 60%);
}
/* the bare plate fades into paper at the foot as well, so the band ends without a
   hard horizon — same single-gradient budget, one more stop on the same fade */
.hero--bare .hero__field::after {
  background: linear-gradient(180deg,
    transparent 0%,
    transparent 55%,
    color-mix(in srgb, var(--paper) 60%, transparent) 82%,
    var(--paper) 100%);
}
.hero__inner { position: relative; z-index: 2; width: 100%; }
.hero__copy { max-width: 34rem; padding: 3rem 0; }
/* tagline-only fold: the line carries itself, so it is set larger and the block
   loses the masthead's top padding */
.hero__copy--tagline { padding: 2rem 0; }
.hero__name {
  font-weight: var(--wt-semi);
  font-size: var(--step-5);
  line-height: 1;
  letter-spacing: var(--track-tight);
  color: var(--text-strong);
  margin: 0.6rem 0 0.7rem;
}
.hero__dek {
  font-style: italic;
  font-size: var(--step-1);
  color: var(--text-muted);
  max-width: 26rem;
}
/* standing alone, the tagline is the fold's only voice — set it up a step and in
   full ink, and give it the wider measure the masthead used to occupy */
.hero__copy--tagline .hero__dek {
  font-size: var(--step-2);
  line-height: 1.25;
  color: var(--text-body);
  max-width: 30rem;
}
/* BH-6's written introduction, set inside the copy column under a hairline. The
   measure is tighter than the column (30rem vs 34rem) so the paragraphs read as an
   inset note rather than a second dek. Values from demo-bh6-tall-frontispiece.
   The copy is a Ghost page, so `> *` catches whatever Koenig emits, not just <p>. */
.hero__intro {
  margin-top: 1.8rem;
  padding-top: 1.4rem;
  border-top: var(--hair) solid var(--rule);
}
.hero__intro > * {
  font-size: var(--step-0);
  max-width: 30rem;
  text-wrap: pretty;
}
.hero__intro > * + * { margin-top: 1.1rem; }
/* the fold is type on bare paper — an intro page that carries a figure or a card
   would be printing a second image into the one place the system reserves for the
   plate, so hold those back and let the prose stand */
.hero__intro figure,
.hero__intro img,
.hero__intro iframe,
.hero__intro [class*="kg-card"] { display: none; }

.hero__ledger {
  display: block;
  margin-top: 1.4rem;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-faint);
}
@media (max-width: 54rem) {
  .hero { --hero-h: 20rem; }
  .hero--bare { --hero-h: 15rem; }
  .hero__field { width: 100%; }
  .hero--bare .hero__field::after { background: none; }
  .hero__copy { padding: 2rem 0; }
  .hero__dek { max-width: none; }

  /* 04-screens §E: below ~760px the field belongs ABOVE the type block rather than
     behind it, and the frontispiece is where that stops being cosmetic. One tagline
     can ride the legibility fade; three paragraphs of introduction set over a
     mezzotint ground is exactly the meeting of type and field the system forbids —
     the fade only runs left-to-right, so a stacked column has nothing protecting it.
     Unstack: the plate takes its own band at the §E height, the copy follows on bare
     paper. Scoped to folds that actually carry an intro, so the tagline-only fold
     keeps the overlap it was designed with. */
  .hero:has(.hero__intro) { display: block; }
  .hero:has(.hero__intro) .hero__field {
    position: relative;
    inset: auto;
    height: 46vh;
  }
  /* nothing is set over it now, so the fade has nothing to protect */
  .hero:has(.hero__intro) .hero__field::after { background: none; }
}

/* FRAMING THE FOLD — the plate itself bleeds flush to the viewport's right edge
   (`.gh-main > .hero` above); the header's own rule directly above it, and the
   register's top rule directly below (see `.hero + .index .rows` further down),
   used to stop at the centred content measure instead, reading as unfinished next
   to the plate's flush edge. Each is redrawn as one pseudo-element — same left
   position, same border, just bled out to the viewport edge with the standard
   "centred box, one-sided bleed" calc — rather than tacking on a second segment
   that would risk a seam against the original. `.site-nav` is not a sibling of
   `.hero` — `.hero` is `.gh-main`'s own first child — so the nav side needs
   `:has(+ .gh-main > .hero)`, reaching past `<main>` into it, to fire only on
   the page that actually opens with a hero.
   Making the original border transparent still reserves its line-width of
   space — an absolutely positioned `inset:0` pseudo is offset to the padding
   edge, one line short of the box's true (border) edge, i.e. one line short
   of the plate. `bottom: calc(var(--line) * -1)` pushes the pseudo past the
   padding edge by exactly that reserved width so its own border lands flush
   with the hero, not a line above it. */
.site-nav:has(+ .gh-main > .hero) { position: relative; border-bottom-color: transparent; }
.site-nav:has(+ .gh-main > .hero)::after {
  content: "";
  position: absolute;
  top: 0;
  right: calc(50% - 50vw);
  bottom: calc(var(--line) * -1);
  left: 0;
  border-bottom: var(--line) solid var(--rule-strong);
  /* The pseudo blankets the whole bar to place its border on the box edge; a
     pseudo hit-tests as its originating element, so without this every link
     and control under it is dead. Same for the register rule below. */
  pointer-events: none;
}

/* ============================================================
   INDEX — hairline-ruled rows (bare_sheet / frontispiece base)

   Vertical rhythm: the register opens at the full section step,
   but closes on a short one so the pagination fixture sits with
   the list it belongs to instead of adrift between two voids.
   ============================================================ */
.index { padding-block: var(--section-y) var(--space-lg); }
/* The full section step is the opening move only when the register opens the page.
   Under the fold, the register's top rule is the plate's own baseline — it butts
   straight against the band so the impression and the rule read as one edge, and
   any padding here reopens the gap and the plate starts to float. Under an archive
   masthead the reason is plainer: that block has already set the distance, and a
   second section step on top of it just prints a void. */
/* `~`, not `+`: index.hbs prints the page's visually-hidden <h1> right after
   {{> hero}} whenever hero_copy doesn't carry the masthead itself (the tagline/
   none/bare variants, i.e. most configs) — that h1 is a real sibling element
   between .hero and .index, so the adjacent-sibling `+` never matches and both
   rules below silently fall back to their unbled defaults (the section-y gap
   reopens, the rule stops at the content measure). `~` reaches past it. */
.hero ~ .index,
.archive-head + .index,
.section-intro + .index { padding-top: 0; }
.index .rows { border-top: var(--line) solid var(--rule-strong); padding: 1rem 0 0; }
/* directly under the hero: bleed this rule right too, same technique and reasoning
   as the nav rule above the fold (see FRAMING THE FOLD, above the hero block) —
   including the `top: calc(var(--line) * -1)` compensation, mirrored because this
   is a border-TOP sitting under the plate rather than a border-bottom above it. */
.hero ~ .index .rows { position: relative; border-top-color: transparent; }
.hero ~ .index .rows::after {
  content: "";
  position: absolute;
  top: calc(var(--line) * -1);
  right: calc(50% - 50vw);
  bottom: 0;
  left: 0;
  border-top: var(--line) solid var(--rule-strong);
  pointer-events: none; /* covers every row — see the nav rule above */
}

.post-row { max-width: 52rem; border-top: var(--hair) solid var(--rule); }
.post-row:first-child { border-top: 0; }
.post-row__link { display: flex; align-items: center; gap: var(--space-md); padding: 1.2rem 0; text-decoration: none; }
.post-row__body { flex: 1 1 auto; min-width: 0; }
.post-row .post-meta { margin-bottom: 0.35rem; }
.post-row__title {
  font-weight: var(--wt-semi);
  font-size: var(--step-1);
  line-height: 1.15;
  color: var(--text-strong);
  margin: 0.35rem 0 0.2rem;
}
.post-row__link:hover .post-row__title { color: var(--accent-text); }
.post-row__dek { font-size: var(--step-0); color: var(--text-muted); }
/* the small per-row mark: feature-image thumbnail, or a slug-stable treatment field */
.post-row__mark {
  flex: none;
  width: 4.5rem;
  height: 4.5rem;
  border: var(--line) solid var(--rule-strong);
  background-color: var(--paper);
  box-shadow: var(--cast-sm);
}
.post-row__mark--img { object-fit: cover; border-radius: var(--radius); }
@media (max-width: 34rem) { .post-row__mark { width: 3.25rem; height: 3.25rem; } }

/* generated frontispiece (post with no photo): a real static motif in a plate */
.note-plate--generated .plate-field { width: 100%; }

/* ---- frontispiece: rows are the base; the intro block does the work ----
   That intro is not styled here because it does not live here: BH-6 sets it inside
   the fold's copy column so the plate runs its full height (`.hero__intro`, above,
   and partials/frontispiece-intro.hbs). Below the fold, frontispiece IS bare_sheet —
   which is the point of the candidate, not an omission. */

/* ---- triptych: centre note promoted by scale + hairline flanks ---- */
.index--triptych .tri {
  display: grid;
  grid-template-columns: 1fr 1.5fr 1fr;
  gap: var(--space-xl);
  align-items: start;
  padding: 2.4rem 0;
  border-top: var(--line) solid var(--rule-strong);
}
.index--triptych .tri__ctr {
  grid-column: 2;
  grid-row: 1 / span 99;
  text-align: center;
  padding: 0 var(--space-lg);
  border-left: var(--hair) solid var(--rule);
  border-right: var(--hair) solid var(--rule);
}
.index--triptych .tri__ctr .post-featured__title { font-size: var(--step-3); line-height: 1.05; letter-spacing: var(--track-tight); }
.index--triptych .tri__ctr .post-featured__dek { font-style: italic; font-size: var(--step-0); color: var(--text-muted); max-width: 24rem; margin-inline: auto; }
.index--triptych .tri__ctr .post-meta { justify-content: center; }
.tri__ent { text-decoration: none; display: block; }
.index--triptych .tri__ent:nth-of-type(odd) { grid-column: 1; }
.index--triptych .tri__ent:nth-of-type(even) { grid-column: 3; }
.tri__ent .post-row__title { font-size: var(--step-1); }
.tri__ent .post-row__dek { font-size: var(--step--1); }
@media (max-width: 54rem) {
  .index--triptych .tri { grid-template-columns: 1fr; }
  .index--triptych .tri__ctr { grid-column: 1; grid-row: auto; text-align: left; border: 0; padding: 0; }
  .index--triptych .tri__ctr .post-featured__dek { margin-inline: 0; }
  .index--triptych .tri__ent:nth-of-type(odd),
  .index--triptych .tri__ent:nth-of-type(even) { grid-column: 1; }
  .index--triptych .tri__ctr .post-meta { justify-content: flex-start; }
}

/* ---- the promoted note (triptych centre) ----
   These carry the `post-featured__` name because the 01 spec sketched a featured
   card partial for the home lead; 04-screens then chose BH-6, whose lead is a
   written introduction rather than a promoted post, and the card was never wired
   up. The card and its own rules are gone — what survives is the type treatment
   the triptych's centre column borrows, which is why these three are the only
   `post-featured__` selectors left. */
.post-featured__title { font-size: var(--step-4); line-height: 1.03; letter-spacing: var(--track-tight); color: var(--text-strong); margin: var(--space-sm) 0 0; }
.post-featured__dek { font-size: var(--step-1); color: var(--text-body); margin: var(--space-sm) 0 var(--space-md); max-width: 34rem; }
.post-featured__more { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--1); color: var(--accent-text); }
/* the promoted note is one whole link — no underline running through the
   centre column; hover carries the accent onto the title like a row hover */
.tri__ctr-link { display: block; text-decoration: none; }
.tri__ctr-link:hover .post-featured__title { color: var(--accent-text); }

/* ============================================================
   NOTE (post / page) — flush-left header, half-step-indented body
   ============================================================ */
.note { padding-block: 3.2rem 2.6rem; --note-indent: clamp(2rem, 6vw, 5.5rem); }
.note-head { max-width: 64rem; }
.note-head .kick { display: flex; flex-wrap: wrap; gap: 1.6rem; align-items: baseline; font-family: var(--font-mono); font-size: var(--step--2); text-transform: uppercase; letter-spacing: var(--track-label); }
.note-head .k1 { color: var(--text-strong); font-weight: var(--wt-med); }
.note-head .k2 { color: var(--text-faint); }
.note-head .k3 { color: var(--text-faint); }
.note-head .k4 { color: var(--accent-text); }
/* the way back to the note's section (tag.hbs) — quiet in the row's own faint
   ink, the accent only in hand, same idiom as the print link below */
.note-head .k-section { color: var(--text-faint); text-decoration: none; }
.note-head .k-section:hover { color: var(--accent-text); }
/* The reader-facing print affordance (theme.js builds it, so no-JS simply has
   none): off to the side, upper right — the last item on the kick line, set
   right. At wide viewports that is the top of what becomes the sidenote rail;
   the first margin note can only start beside the prose, below the head, so
   the two never collide. Mono chrome casing rides the kick's own register.
   print.css hides it on paper — the sheet must not advertise itself. */
.note-head .print-link { margin-left: auto; color: var(--text-faint); text-decoration: none; }
.note-head .print-link:hover { color: var(--accent-text); }
.note-title { font-weight: var(--wt-semi); font-size: var(--step-5); line-height: 1; letter-spacing: var(--track-tight); color: var(--text-strong); max-width: 64rem; margin: 1.1rem 0 0.8rem; }
.note-head .dek { font-style: italic; font-size: var(--step-1); color: var(--text-muted); max-width: 36rem; }
.note-plate { margin-top: var(--space-xl); max-width: var(--measure-wide); }
/* the feature image always fills the plate — never left-aligned inside the frame,
   regardless of the intrinsic size the `sizes` hint resolves to */
.note-plate img { display: block; width: 100%; height: auto; }
.note-head--page { text-align: center; }
/* centred text needs centred BOXES too — dek (36rem) and plate (--measure-wide)
   are narrower than the head and sat hard left while their text centred */
.note-head--page .note-title,
.note-head--page .dek,
.note-head--page .note-plate { margin-inline: auto; }

/* Settled (owner, 2026-08-11): the half-step indent stays — 04-screens' signature
   ("the offset is what makes the sheet read as a note"), re-adopted after a
   flush-left trial — with the measure widened one notch from the port's 40rem to
   43rem, then a further tenth on request (owner, 2026-08-12): 47.5rem holds ~82
   characters of Spectral at --step-0. --measure-wide grew in step (typography.css)
   so plates and wide cards keep standing a notch outside the text column. */
.note-body { margin: 2.8rem 0 0 var(--note-indent); max-width: 47.5rem; }
.note-body.kg-prose { max-width: 47.5rem; }

.note-foot { margin: var(--space-2xl) 0 0 var(--note-indent); max-width: 47.5rem; }
.note-foot .subscribe { margin-top: var(--space-xl); }

/* comments share the body's indent and measure; the iframe inside is Ghost's,
   headed by the theme's own eyebrow + right-set mono count */
.note-comments { margin: var(--space-2xl) 0 0 var(--note-indent); max-width: 47.5rem; }
/* ⚠ THIS `color` IS LOAD-BEARING AND IT IS NOT ABOUT THIS ELEMENT.
   Nothing in .note-comments inherits it — the head sets its own tones and the
   widget is an iframe. It is here because comments-ui decides light vs dark by
   READING IT. {{comments}} ships data-color-scheme="auto", and "auto" is not
   prefers-color-scheme: the widget takes the computed `color` of the element
   that contains #ghost-comments-root — this one — and calls the page dark when
   that colour contrasts under 5:1 with white. Its parser takes the substring
   between the first "(" and the last ")", splits it on commas and parseInts
   the three parts — so it reads rgb() and NOTHING ELSE. (Do not paste its
   regex in here: the literal ends `\s*` then a slash, which closes a CSS
   comment early. That is not a hypothetical — it silently deleted the rule
   below, and test/contrast.test.mjs is what noticed.)
   Inherited, this element's colour was
   --text-body, a color-mix(), and Chromium serialises that as
   `color(srgb 0.849 0.812 0.713)`: the parse yielded NaN, the `< 5` test was
   false, and the widget rendered its LIGHT palette under every scheme —
   near-black comments on aquatint night's dark plate, invisible.
   --text-strong is var(--ink), and every scheme declares --ink as a plain hex,
   so this computes to rgb(...) and Ghost's own detection works. Move this
   token onto a color-mix() and the widget silently goes light again; theme.js
   re-stamps the same value inline on a pigment swap (their re-check watches
   this element's style/class attributes, not <html>'s). */
.note-comments { color: var(--text-strong); }
/* the head keeps its own air above the widget; the matching trim of the widget's
   oversized internal margins happens in theme.js's injected sheet */
.note-comments__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md); margin-bottom: 1.2rem; }
.note-comments__count { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--2); color: var(--text-faint); }

.note-nav { display: flex; justify-content: space-between; gap: var(--space-md); flex-wrap: wrap; max-width: 72rem; margin: var(--space-xl) auto 0; padding-top: var(--space-md); border-top: var(--line) solid var(--rule-strong); }
/* a nav with no neighbours must not print its rule — an unlabelled line floating
   between comments and footer reads as a mistake, not a frame */
.note-nav:not(:has(.note-nav__link)) { display: none; }
.note-nav__link { text-decoration: none; max-width: 22rem; }
.note-nav__next { text-align: right; margin-left: auto; }
.note-nav__dir { display: block; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--2); color: var(--text-faint); }
.note-nav__link:hover { color: var(--accent-text); }

/* ---- page: reading column, no kicker/date, centered head ---- */
.note--page .note-body { margin-inline: auto; }

/* ============================================================
   SUBSCRIBE (members plate)
   ============================================================ */
.subscribe { display: grid; gap: var(--space-md); padding: var(--space-xl); }
.subscribe__eyebrow { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-eyebrow); font-size: var(--step--2); color: var(--text-faint); }
.subscribe__title { font-size: var(--step-2); margin-top: var(--space-2xs); color: var(--text-strong); }
.subscribe__dek { color: var(--text-muted); font-style: italic; }
/* The field is set in serif at --step-0 and the press in tracked mono at --step--1,
   so the two boxes are ~11px apart in height and can never share a top edge without
   looking dropped. Centre them: the field's rule and the press read as one line. */
.subscribe__form { display: flex; flex-wrap: wrap; gap: var(--space-2xs); align-items: center; }
.subscribe__label { position: absolute; left: -9999px; }
/* min-width:0 so a long address shrinks the field instead of pushing the row wide;
   the button keeps its intrinsic width so the plate can never be forced open */
.subscribe__input { flex: 1 1 16rem; min-width: 0; }
.subscribe__submit { flex: none; }

/* ---- form states (Portal writes `loading` / `success` / `error` onto the form) ----
   Both message lines are full-measure block items on their own flex line and wrap
   inside the plate. `overflow-wrap` matters: server messages are not length-bound. */
.subscribe__message,
.subscribe__ack {
  flex: 1 0 100%;
  min-width: 0;
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  line-height: var(--leading-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
/* An empty message collapses to zero height on its own. Deliberately NOT
   display:none — it is the form's aria-live region, and a region toggled out of
   the accessibility tree does not reliably announce when it comes back. */
.subscribe__message { color: var(--accent-text); }
.subscribe__ack { display: none; color: var(--text-muted); }

/* invalid: mark the field itself, so the message is not the only signal */
.subscribe__form.error .subscribe__input { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

/* In flight: the press is working — no spinner, the button just goes quiet.
   Quiet is the GROUND lifting off full ink, not the label fading: the label
   keeps the button's own paper-on-ink idiom (6.1–9.7:1 on ink-80), where a
   dimmed label would have been --text-faint on that same ink-80 and gone. */
.subscribe__form.loading .subscribe__submit { color: var(--text-invert); background: var(--ink-80); pointer-events: none; }

/* accepted: the form has done its job, so it steps aside for the acknowledgement */
.subscribe__form.success .subscribe__input,
.subscribe__form.success .subscribe__submit,
.subscribe__form.success .subscribe__message { display: none; }
.subscribe__form.success .subscribe__ack { display: block; }

/* ============================================================
   ARCHIVE (tag / author)
   ============================================================ */
.archive-head { padding-block: var(--section-y) var(--space-lg); }
.archive-head__kicker { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-eyebrow); font-size: var(--step--2); color: var(--text-faint); }
.archive-head__title { font-weight: var(--wt-semi); font-size: var(--step-5); line-height: 0.98; letter-spacing: var(--track-tight); color: var(--text-strong); margin-top: 0.4rem; }
.archive-head .dek { font-style: italic; font-size: var(--step-1); color: var(--text-muted); max-width: 40rem; margin-top: var(--space-sm); }
.archive-head__count { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--2); color: var(--text-muted); margin-top: var(--space-md); }

/* The section's own introduction (section-intro.hbs), between the masthead and
   the register — a Ghost page carrying the FULL prose layer (kg-prose sets the
   measure and every text rule), so only the seams are drawn here: the fold
   intro's hairline idiom above, and the distance the register reads as its
   padding-top below (`.section-intro + .index`, with the collapse rules
   further up). */
.section-intro {
  padding-block: var(--space-md) var(--space-xl);
  border-top: var(--hair) solid var(--rule);
}

/* ============================================================
   PAGINATION
   A fixture at the foot of the register, ruled to the same
   measure as the rows above it — not a free-floating island
   with a section step of air on either side.

   It is printed INSIDE `.index` (index / tag / author.hbs) for
   exactly that reason. As a direct child of `.gh-main` it took
   that layer's `margin-inline:auto` and `padding-inline:gutter`,
   which centred its 52rem rule in the 76rem page and inset the
   links a gutter further still — the same length as the row rules
   above it, but half a page out of phase with them. Inside the
   register it starts on the rows' left edge and its rule lands on
   their measure. Do not lift it back out to a `.gh-main` child.
   ============================================================ */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  max-width: 52rem;
  margin-top: var(--space-lg);
  padding-block: var(--space-sm);
  border-top: var(--hair) solid var(--rule);
}
/* the triptych rules to the full sheet, so its foot fixture does too */
.index--triptych .pagination { max-width: none; }
/* one page of notes = no fixture: a rule, "Nº 1 / 1" and two greyed arrows
   would be furniture with no function (same treatment as .note-nav above) */
.pagination:not(:has(a)) { display: none; }
.pagination__link { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--2); color: var(--text-muted); text-decoration: none; }
.pagination__link:hover { color: var(--accent-text); }
/* The one text tone in the theme deliberately left under the floor, and the
   markup is what earns it: the disabled arrow is a `<span aria-hidden="true">`
   holding a grid position, never a control and never announced (pagination.hbs
   prints the <a> only when there is a page to go to, and .pagination:not(:has(a))
   hides the whole fixture). WCAG 1.4.3 exempts exactly this — text that is
   incidental and inactive. Anything a reader is actually asked to read answers
   to the floor; see tokens/colors.css. */
.pagination__link.is-disabled { color: var(--ink-20); }
.pagination__count { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-label); font-size: var(--step--2); color: var(--text-faint); }

/* ============================================================
   PLATE STUDIO (custom-plate-studio.hbs)
   The bench: the proof plate on the left, the controls column
   on the right — a workbench, not a reading column.
   ============================================================ */
.studio {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20rem;
  gap: var(--space-lg) var(--space-xl);
  align-items: start;
  margin-top: var(--space-lg);
}
.studio__plate { margin: 0; }
.studio__plate .plate-field { width: 100%; }
.studio__caption {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-faint);
  margin: 0;
  padding: 0.55rem 0.75rem 0.65rem;
  border-top: var(--hair) solid var(--rule);
  text-align: center;
}
.studio__controls { display: grid; gap: var(--space-md); }
.studio__group {
  display: grid;
  gap: var(--space-xs);
  border-top: var(--hair) solid var(--rule);
  padding-top: var(--space-xs);
}
.studio__group:first-child { border-top: 0; padding-top: 0; }
.studio__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs); }
.studio__status {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--accent-text);
}
@media (max-width: 54rem) {
  .studio { grid-template-columns: 1fr; }
}

/* ============================================================
   ERROR PAGE
   Namespaced `.error-page`, NOT `.error`. Portal stamps a bare
   `error` class onto any [data-members-form] whose submission
   fails, so a `.error` block rule here reaches the subscribe
   form and re-flows it into a 60vh centred column. Keep every
   selector in this section prefixed.
   ============================================================ */
.error-page { min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-md); text-align: center; padding-block: var(--section-y); }
.error-page__code { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: var(--track-eyebrow); font-size: var(--step--1); color: var(--text-faint); }
.error-page__line { font-size: var(--step-3); color: var(--text-strong); }
.error-page__message { color: var(--text-muted); max-width: 32rem; }
