/* GENERATED by scripts/sync-design.py. Order matters: derived tokens, then identity, then the brand layer. */

/* GENERATED by gtm-design-profile/derive.py for deployedbyai.com at 2026-09-07T19:42:10+00:00. Layered over the house TOKENS. Do not edit. */
:root{
  --fs--2:11px; --fs--1:12px; --fs-0:16px; --fs-1:21px; --fs-2:28px; --fs-3:38px; --fs-4:51px;
  --lh--2:1.45; --lh--1:1.5; --lh-0:1.55; --lh-1:1.35; --lh-2:1.25; --lh-3:1.2; --lh-4:1.16;
  --measure:55ch; /* 66 average characters */
  --mono:ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-7:32px; --sp-8:40px; --sp-9:48px; --sp-section:60px; --sp-page:96px;
  --header-h:64px; --bp-stack:640px; --bp-split:760px; --bp-wide:1000px;
}
body{font-size:var(--fs-0);line-height:var(--lh-0)}
h1{font-size:var(--fs-4);line-height:var(--lh-4);font-weight:600;letter-spacing:0}
h2{font-size:var(--fs-3);line-height:var(--lh-3);font-weight:600}
h3{font-size:var(--fs-2);line-height:var(--lh-2);font-weight:600}
@media (max-width:639px){h1{font-size:var(--fs-2);line-height:var(--lh-2)}}
.label,.k,h2.sec{font-size:var(--fs--2);line-height:var(--lh--2);letter-spacing:1.3px;text-transform:uppercase;font-weight:600}
.meta,.muted-sm,small{font-size:var(--fs--1);line-height:var(--lh--1)}
p,.prose li,.lede,.description{max-width:var(--measure)}
.list>*,.frames>*{min-width:0}
code,pre,kbd,samp,.hash{font-family:var(--mono);font-size:var(--fs--1);line-height:var(--lh--1);font-variant-numeric:tabular-nums}
.num,.hash,td.num{font-variant-numeric:tabular-nums}
.grid>*,.two>*,.stats>*{min-width:0}
pre{max-width:100%;overflow:auto}
.nav{position:sticky;top:0;z-index:10;min-height:var(--header-h);background:var(--paper);border-bottom:2px solid var(--ice)}
@media (pointer:coarse){input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),:is(select,textarea):not([hidden]){font-size:max(16px,1em)}}
@media (max-width:640px){.wrap{padding-left:16px;padding-right:16px}}
@media (max-height: 500px) and (orientation: landscape){
  .nav{position:static}
  h2.sec{margin-top:calc(var(--sp-section) * 0.6)}
  .hero{margin:var(--sp-2) 0 var(--sp-5)}
  .wrap{padding-top:var(--sp-4)}
}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{transition:none !important;animation:none !important}}


/* identity: GENERATED by scripts/sync-design.py from design/identity.json. Do not edit. */
/* chosen on the board: candidate B, Brand accent | Two registers: EB Garamond argues, Inter reports */
:root {
  --id-accent: #397E5B;
  --id-accent-rgb: 57,126,91;
  --id-underline: #397E5B;
  --id-label: #397E5B;
  --id-body: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --id-display: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --gh-font-body: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --gh-font-heading: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --id-machine: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* candidate A: House bronze, brand secondary | One voice: Inter throughout | contrast floor 4.77:1 */
html[data-theme="a"] {
  --id-accent: #8C6A2F;
  --id-accent-rgb: 140,106,47;
  --id-underline: #8C6A2F;
  --id-label: #8C6A2F;
  --id-body: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --id-display: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --gh-font-body: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --gh-font-heading: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --id-machine: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* candidate B: Brand accent | Two registers: EB Garamond argues, Inter reports | contrast floor 4.67:1 */
html[data-theme="b"] {
  --id-accent: #397E5B;
  --id-accent-rgb: 57,126,91;
  --id-underline: #397E5B;
  --id-label: #397E5B;
  --id-body: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --id-display: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --gh-font-body: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --gh-font-heading: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --id-machine: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* candidate C: Monochrome ink, bronze badge only | Machine first: JetBrains Mono display over Inter body | contrast floor 4.98:1 */
html[data-theme="c"] {
  --id-accent: #0F0F0F;
  --id-accent-rgb: 15,15,15;
  --id-underline: #0F0F0F;
  --id-label: #5b544a;
  --id-body: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --id-display: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --gh-font-body: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --gh-font-heading: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --id-machine: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}


/* THE CONSOLE. The brand layer, dark ground. Jack chose it 2026-09-10 in a rendered
 * preflight (design/preflight/), over The Ledger and The Broadsheet.
 *
 * WHAT MAKES IT THE CONSOLE, and it is not the darkness:
 *   1. The machine layer is a FIRST CLASS VOICE, not a footnote. Mono, uppercase,
 *      letterspaced, at a size a person can actually read.
 *   2. The serif speaks only where a HUMAN is speaking: headlines, item bodies, the wall.
 *      Everything the system says (dates, labels, calls, sources) is mono.
 *   3. The three calls are the loudest thing on the page. A reader deciding whether to put
 *      something in production wants the verdict before the prose.
 *   4. Colour is STRUCTURAL. On a near black ground the accent is the only saturated thing,
 *      so it means "act here" everywhere it appears and nowhere else.
 *
 * CONSTRAINTS THIS FILE IS HELD TO, by scripts/sync-design.py --check:
 *   every font-size is on the ramp [11 12 16 21 28 38 51]; every weight is 400, 500 or 600;
 *   --page agrees with the profile's content width. A number changes only by re-deriving.
 *
 * CONTRAST: every text token here clears WCAG AA against its own ground, asserted from the
 * GENERATED file by scripts/verify-contrast.py, which is in scripts/verify-all.sh.
 */

:root {
  /* ground and ink */
  --ground:      #0D100E;   /* the page */
  --panel:       #141915;   /* a raised surface: rows, the field */
  --ink:         #EDEBE4;   /* 16.04:1 on ground */
  --ink-soft:    #9A9C93;   /*  6.88:1 */
  --ink-faint:   #80837B;   /* 4.62:1 on the PANEL, which is the worst ground it lands on */
  --hair:        rgba(237, 235, 228, 0.12);
  --hair-strong: rgba(237, 235, 228, 0.20);

  /* the calls. One hue each, and nothing else on the page competes with them. */
  --deploy:      #3FD98A;   /* 10.48:1. Also the accent: DEPLOY is the brand's own verb. */
  --wait:        #E88A5C;   /*  7.49:1 */
  --watch:       #7E8C86;   /*  4.61:1 */
  --accent:      var(--deploy);
  --on-accent:   #08110C;   /* 10.5:1 on --deploy */

  --serif: "EB Garamond", "EB Garamond Fallback", Georgia, "Times New Roman", serif;   /* the fallback face is metric matched to EB Garamond (partials/typography/serif.hbs), so a late swap shifts nothing */
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono:  "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;


  /* ------------------------------------------------------------------ THE INSET LADDER
   * WHERE A BLOCK STARTS, which is the only thing a left aligned page has to get right.
   *
   * Measured on production 2026-09-13: the home page rendered TEN distinct content left
   * edges at 1280 (80, 108, 109, 146, 167, 175 ...) and six at 390, and /about/, /standard/
   * and /welcome/ each rendered two at 390 where they render one at 1280. Nothing in the
   * suite could see it: verify-alignment.mjs rule D compares three chrome containers to
   * each other and never reads where TEXT starts.
   *
   * TWO MECHANISMS, and the second is the one nobody would guess.
   *
   * 1. The space scale is DERIVED and emitted into the served bundle as --sp-1..--sp-9
   *    (design/tokens.css, from profile.json space.scale). This file used it ZERO times and
   *    typed 26 distinct spacing values instead, 17 of them off the scale entirely. The
   *    ramp is enforced for type and was never enforced for space.
   *
   * 2. THE BORDER IS PART OF THE INSET AND NOTHING ACCOUNTED FOR IT. A reader sees the
   *    CONTENT edge, which is padding PLUS border. Three surfaces with padding 24, 28 and 28
   *    and borders 1, 0 and 1 produced content edges at 25, 28 and 29: three rungs where the
   *    design intends one or two. That is where the 1px and 4px near misses came from, and
   *    a 1px offset is never a design decision, it is always a mistake.
   *
   * So a bordered surface subtracts its own border and the CONTENT edge lands on the rung. */
  --inset-page:    var(--sp-8);   /* 40: the page gutter, desktop */
  --inset-surface: var(--sp-7);   /* 32: the paper reading well inside the dark chrome */
  --inset-block:   var(--sp-6);   /* 24: a card on the ground: a call, the gate, the offer */
  --inset-slot:    var(--sp-5);   /* 20: a slot inside the well */
  /* The phone ladder. One rung shallower at every level, because 40px of gutter on a 390px
     screen is 20 percent of the viewport. Same border compensation. */
  --inset-page-phone:    var(--sp-4);   /* 16 */
  --inset-surface-phone: var(--sp-4);   /* 16 */
  --inset-block-phone:   var(--sp-4);   /* 16 */
  --hair-w: 1px;                  /* every surface border on this site, named so calc() can
                                     subtract it rather than each rule guessing */

  --page: 1200px;
  /* NO --measure HERE. It is DERIVED: design/profile.json type.measure_css_ch is 55, from a
     measured field median of 70.5ch taken to 94 percent and clamped, then divided by 1.2
     because a CSS ch is the zero glyph and not an average character. design/tokens.css emits
     it and applies it to every prose block. This file re-declared it as 66ch and, because the
     brand layer is concatenated AFTER tokens.css in the bundle, the hand written number won:
     the site rendered 74 characters per line where the derivation says 66. A number changes by
     re-deriving, never by being typed here, and sync-design.py --check now refuses this line.

     --content-width is DECLARED so the vendor's article grid, which computes
     min(var(--content-width, 720px), 100% - var(--container-gap) * 2), reads OUR token instead
     of its own fallback. Same 720px today; the point is that the reading canvas is now a value
     this repo owns rather than one the theme happens to default to. The canvas is the block
     column; --measure is the text column inside it, and they are different things. */
  --content-width: 720px;
}

html { background: var(--ground); }
body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* The human voice. */
h1, h2, h3, .dcj-h1, .dcj-sample-title, .dcj-row-title, .gh-article-title { font-family: var(--serif); }
/* EMPHASIS IS 600, NEVER "BOLDER". The UA stylesheet gives <strong> and <b> `font-weight:
 * bolder`, which resolves RELATIVE to the parent: 700 on a 400 paragraph, 900 on a 600 label.
 * No loaded face carries 900 (the mono stops at 800, the serif at 800), so the browser
 * synthesised it. Measured on production 2026-09-13: the proof line's "1 edition" at 700, the
 * founding count's "99" at 900 on the home page, /pricing/ and the wall, "$12 a year" at 900
 * on /pricing/, and every ledger subject at 700 for a paid reader. The profile allows 400, 500
 * and 600 (design/RULES.md rule 7) and only two scoped rules below said so. One rule, once. */
strong, b { font-weight: 600; }

/* The machine voice. Promoted from 11px whisper to a legible 12px with real spacing: this
 * is the thing the brand has that a summary newsletter does not. */
.dcj-meta, .dcj-label, .dcj-row-date, .dcj-sample-date, .dcj-verdict, .dcj-plan-tag {
  font-family: var(--mono);
  font-size: var(--fs--1);
  line-height: 1.5;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 500;
}

.dcj-wrap { max-width: var(--page); margin: 0 auto; padding: 0 var(--inset-page); }
.dcj-measure { max-width: var(--measure); }
.dcj-stack { margin-top: 48px; }
/* THE SECTION LABEL IS A MACHINE LABEL, EVERYWHERE. The Console commit (2026-09-10) deleted
 * this class's voice and re-added only its margin, and the well got its own restatement
 * (.gh-content .dcj-section, further down) while the eleven emitters outside the well did not.
 * Measured on production 2026-09-13: "The current edition", "How it works", "What is free",
 * "Founding memberships" and "Read one and check it" all rendered as the UI sans at 16px, in
 * the same face, size and colour as the paragraph under each of them. The label takes the
 * machine voice above (12px, not the pre Console 11px: the Console promoted the whisper), at
 * the label weight the well already uses, and never the accent (rule 8: the accent is "act
 * here" and a heading is not that). */
.dcj-section {
  font-family: var(--mono); font-size: var(--fs--1); line-height: 1.5; font-weight: 600;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-soft);
  margin-top: 48px; margin-bottom: var(--sp-2);
}

/* ---------------------------------------------------------------- chrome: the header
 * The stock header is replaced, not overridden: partials/components/navigation.hbs is
 * ours now. It used rounded pills, a centred logo and a grey field, which is where half the
 * "two design languages on one page" came from. */
.dcj-nav {
  border-bottom: 1px solid var(--hair);
  background: var(--ground);
  position: sticky; top: 0; z-index: 40;
}
.dcj-nav-in {
  max-width: var(--page); margin: 0 auto; padding: var(--sp-4) var(--inset-page);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-6);
}
.dcj-brand {
  font-family: var(--mono); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink);
  text-decoration: none; white-space: nowrap;
}
.dcj-brand i { color: var(--deploy); font-style: normal; }
.dcj-nav-menu { display: flex; align-items: center; gap: 26px; }
.dcj-nav-menu a {
  font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-soft); text-decoration: none;
  transition: color 140ms ease;
}
.dcj-nav-menu a:hover { color: var(--ink); }
/* ---------------------------------------------------------------- one control height
 * Measured on production 2026-09-11: FOUR button heights on one brand. The header pill 32px,
 * the page CTAs 38px, the outlined button 44.6px, the field row 50.4px, and the referral copy
 * button 36.6px, from five paddings typed independently and one icon's incidental 6px. Two of
 * them, including the primary signup call on a phone, were under the touch target this
 * project's own design profile LOCKS at 44px (design/profile.json, controls.touch_target,
 * source "lock"). So the scale is not a taste: 44 is the locked minimum and 52 is one 8px step
 * up it, on the 4px space scale the rest of this file uses (design/profile.json, space.scale).
 *
 * Height is SET and the content centred, rather than derived from a padding. A padding derives
 * a height only if you also control the line box, which is how five of them drifted apart.
 * scripts/verify-alignment.mjs rule E fails the build if two controls in one row disagree. */
:root { --ctl: 44px; --ctl-lg: 52px; }

/* THE RIGHT HAND SIDE OF THE HEADER IS A ROW, and it has to say so.
 * Measured on production 2026-09-11, signed out at 390px: the Subscribe pill sat 7.6px below
 * the wordmark's centre line and crossed the header's own bottom hairline. At 1280px it sat
 * 1.6px low and painted 11.2px outside its parent. One missing rule caused all of it.
 * `.dcj-nav-actions` had no declaration at all, so it was a block box holding inline children,
 * and the pill was an inline <a> carrying 10px of vertical padding. Vertical padding does not
 * grow an inline box's LINE box, it paints outside it, so nothing could centre the pill and its
 * height never reached its parent. The burger, being inline-flex, sat on the shared baseline at
 * a third height again. Declaring the row fixes all three at once.
 * scripts/verify-alignment.mjs reads this off the rendered page and fails on a regression. */
/* ONE CHROME CONTROL BOX. The pill stood 32px and the burger 36px in the same row, four
 * pixels apart, because one is 12px of text in 20px of padding and the other is a 24px icon
 * in 12px of padding, and the two numbers had never been asked to agree. They take one value
 * now. It is the PILL's 32px rather than the burger's 36px on purpose: the pill's size is a
 * decision that shipped and Jack has been looking at it, the burger's was an accident of
 * `padding: 6px` around an icon. That also holds the header at the 64px it has always been,
 * at both widths, which it was not: 390px ran 68.8px before this. */
.dcj-nav-actions { --ctl-chrome: 32px; display: flex; align-items: center; gap: 12px; }
.dcj-nav-cta {
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600; letter-spacing: 0.10em;
  text-transform: uppercase; background: var(--deploy); color: var(--on-accent);
  padding: 0 18px; border-radius: 3px; text-decoration: none; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; line-height: 1;
  height: var(--ctl-chrome);
}
.dcj-burger {
  display: none; background: none; border: 0; color: var(--ink); padding: 0;
  align-items: center; justify-content: center; line-height: 0;
  height: var(--ctl-chrome); width: var(--ctl-chrome);
}
/* THE HEADER CARRIES NO VENDOR ID. It kept the stock theme's `gh-navigation` id on the belief
 * that the stock main.js bound to it; measured 2026-09-13, that script binds to the CLASS
 * `.gh-navigation`, which this header does not carry, so the id did no work except match the
 * vendor's rules. Two of those cost readers: `#gh-navigation{height:64px}` typed the header's
 * height over the one our padding derives (7.2px of dead space at 1280, 5.8px short at 390),
 * and the phone rule `.is-open#gh-navigation{position:fixed;inset:0;height:100%}` turned the
 * open header into an 844px overlay whose menu, placed at top:100% below, sat at y 843 in an
 * 844px viewport: every phone reader who tapped the burger saw an empty screen, on every
 * route. The id is gone from partials/components/navigation.hbs, so no override is needed and
 * the height is derived again. */

/* ---------------------------------------------------------------- the hero
 * A GRID, which is the other half of the diagnosis: at 1280 the old page put a 35 percent
 * column against 55 percent of nothing. The right rail carries the offer so the fold does
 * real work at desktop, and it stacks under the hero on a phone. */
.dcj-hero { padding: 56px 0 48px; }
.dcj-hero-grid { display: grid; grid-template-columns: 1.5fr 1fr; gap: 56px; align-items: start; }
.dcj-h1 {
  font-size: var(--fs-4); line-height: 1.05; font-weight: 600; letter-spacing: -0.022em;
  margin-top: 16px; color: var(--ink);
}
.dcj-lede {
  font-family: var(--serif); font-size: var(--fs-1); line-height: 1.5;
  color: var(--ink-soft); margin-top: 20px;
}
.dcj-split { font-size: var(--fs-0); line-height: 1.65; color: var(--ink-soft); margin-top: 22px; }
.dcj-split strong, .dcj-split b { color: var(--ink); font-weight: 600; }

/* the offer rail */
.dcj-offer {
  border: var(--hair-w) solid var(--hair); border-radius: 5px;
  padding: calc(var(--inset-block) - var(--hair-w));
  background: var(--panel);
}
/* The rail's FULL ACCESS label took the accent, and so did the paid chip on /pricing/: the paid
 * tier's NAME was carrying the "act here" colour on two surfaces, by an unwritten convention.
 * Rule 8 (design/RULES.md) is explicit that only a call takes it. Measured 2026-09-13: the
 * accent census on / found this label as the one non actionable element in the hue. A label
 * heading a card reads as the card's title, one step above the machine voice's faint. */
.dcj-offer .dcj-meta { color: var(--ink-soft); }
.dcj-offer-price {
  font-family: var(--serif); font-size: var(--fs-2); line-height: 1.2; font-weight: 600;
  margin-top: 8px; letter-spacing: -0.02em;
}
.dcj-offer-note { font-size: var(--fs--1); line-height: 1.6; color: var(--ink-faint); margin-top: 8px; }

/* ---------------------------------------------------------------- the field
 * ONE field shape everywhere: the hero, the footer, Portal. The old page had a white square
 * field in the hero and a grey pill in the footer. */
.dcj-field-row { display: flex; margin-top: 18px; border: 1px solid var(--hair-strong);
  border-radius: 4px; overflow: hidden; background: var(--ground); }
.dcj-field-row:focus-within { border-color: var(--deploy); }
.dcj-field {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--ink);
  font-family: var(--sans); font-size: var(--fs-0); padding: 14px 14px;
}
.dcj-field::placeholder { color: var(--ink-faint); }
.dcj-field:focus { outline: none; }
.dcj-submit {
  border: 0; background: var(--deploy); color: var(--on-accent); cursor: pointer;
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600; letter-spacing: 0.10em;
  text-transform: uppercase; padding: 14px 20px; white-space: nowrap;
}
.dcj-cta-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 14px; }
/* TWO BUTTONS SIDE BY SIDE ARE ONE SIZE. Measured on production 2026-09-11 at 1280px: the
 * field row stood 50.4px and `Read every edition` beside it stood 44.6px, because three
 * paddings were typed independently (13px on .gh-button, 12px on .dcj-button-outline, 14px
 * inside the join row) and nothing ever compared them. The outlined button now takes its
 * height FROM THE ROW rather than from a padding of its own, so it is right at whatever the
 * field computes to and stays right when the field changes. Stretching the whole row instead
 * was the first attempt and is wrong: it pulls the field off its own centre line, which
 * scripts/verify-alignment.mjs caught in its own selftest before this ever reached the site.
 * The padding STAYS. Zeroing it was the second attempt and it read fine at 1280 and collapsed
 * the button to 20.6px at 390, where the two wrap onto separate lines and a stretched item
 * stretches only to its own line: with no padding left there was nothing to stretch to. Kept,
 * the wrapped button is its natural 44.6px and the side by side one takes the field's 52.4px,
 * both from this one declaration and neither from a number anybody typed. */
.dcj-calls-row > .dcj-button-outline, .dcj-cta-row > .dcj-button-outline {
  align-self: stretch; display: inline-flex; align-items: center;
}
.dcj-button, .gh-button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--ctl); background: var(--deploy); color: var(--on-accent);
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600; letter-spacing: 0.10em;
  text-transform: uppercase; padding: 0 20px; border-radius: 3px; text-decoration: none;
  border: 0; cursor: pointer;
}
.dcj-button-outline {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--ctl); background: transparent; color: var(--ink);
  border: 1px solid var(--hair-strong); border-radius: 3px;
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 500; letter-spacing: 0.10em;
  text-transform: uppercase; padding: 0 20px; text-decoration: none;
}
.dcj-button-outline:hover { border-color: var(--deploy); color: var(--deploy); }
/* A TEXT STYLED ACTION IS STILL A TOUCH TARGET. `or pay monthly` on /pricing/ starts a
 * checkout, the wall's `Already a member? Sign in` is the only sign in entry on the paid
 * conversion surface, and the paid home's `Every edition` is the only action under its list.
 * Measured on production 2026-09-13 at 390 (real mobile context, coarse pointer): 21.6px,
 * 22.2px and 21.6px tall beside 44px buttons in the same row. The profile locks the touch
 * target at 44 under coarse pointers (design/DESIGN.md Controls), so under a coarse pointer
 * the link's BOX grows to --ctl and its type does not change. The underline moved from a
 * border-bottom to text-decoration for exactly that reason: a border sits on the box's
 * bottom edge, 13px under the words once the box is 44px tall, while a decoration follows
 * the text. A fine pointer keeps the text height, which is what the lock's own wording says. */
.dcj-link {
  font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ink-soft); display: inline-flex; align-items: center;
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
  text-decoration-color: var(--hair-strong);
}
.dcj-link:hover { color: var(--deploy); text-decoration-color: var(--deploy); }
@media (pointer: coarse) { .dcj-link { min-height: var(--ctl); } }

/* ---------------------------------------------------------------- the calls band
 * Three cells, hairline separated, each headed by its own colour and a dot. This is the
 * loudest structure on the page on purpose. */
.dcj-calls { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--hair); border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); }
.dcj-call { background: var(--ground); padding: var(--inset-block) var(--inset-block) calc(var(--inset-block) + var(--sp-2)); }
.dcj-call-k {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase;
}
.dcj-call-k::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.dcj-call-deploy { color: var(--deploy); }
.dcj-call-wait   { color: var(--wait); }
.dcj-call-watch  { color: var(--watch); }
.dcj-call p { font-size: var(--fs-0); line-height: 1.6; color: var(--ink-soft); margin-top: 12px; }

/* ---------------------------------------------------------------- the record */
.dcj-row {
  /* The date track fits the longest date the slot prints, SATURDAY 30 SEP 2026: twenty machine
   * characters, each the mono advance (0.6em) plus the machine tracking (0.14em) at --fs--1. It
   * was 160px, typed for DD MMM YYYY, and when the three surfaces took one format on 2026-09-13
   * the date ran 17.6px into the gap and ended 6px from the title (third verifier pass).
   * verify-rendered-type.mjs T12 holds a grid child's ink inside its own track. */
  display: grid; grid-template-columns: calc(20 * 0.74 * var(--fs--1)) 1fr auto; gap: 24px; align-items: center;
  background: var(--panel); border: var(--hair-w) solid var(--hair); border-radius: 5px;
  /* On the block rung, border subtracted (rule 3). It typed `20px 24px`, so its content edge
   * sat at 105 at 1280 and 41 at 390 where every other card lands on 104 and 32 (measured
   * 2026-09-13); sync-design.py now lists .dcj-row among the containers that create an edge. */
  padding: calc(var(--sp-5) - var(--hair-w)) calc(var(--inset-block) - var(--hair-w));
  margin-top: 14px; text-decoration: none; color: inherit;
}
.dcj-row:hover { border-color: var(--hair-strong); }
.dcj-row-title { font-size: var(--fs-1); line-height: 1.3; font-weight: 600; letter-spacing: -0.015em; color: var(--ink); }
.dcj-row-note { font-size: var(--fs-0); color: var(--ink-soft); margin-top: 6px; }
.dcj-verdict {
  font-size: var(--fs--2); font-weight: 600; letter-spacing: 0.16em; padding: 6px 11px;
  border-radius: 3px; border: 1px solid currentColor; white-space: nowrap;
}
.dcj-verdict-deploy { color: var(--deploy); background: rgba(63, 217, 138, 0.10); }
.dcj-verdict-wait   { color: var(--wait);   background: rgba(232, 138, 92, 0.10); }
.dcj-verdict-watch  { color: var(--watch);  background: rgba(126, 140, 134, 0.10); }

/* ---------------------------------------------------------------- the wall
 * ONE upgrade block per medium (docs/copy-spec.md). It is the only place on a page where the
 * accent fills a surface, so it reads as the one thing to act on. */
.dcj-gate {
  border: var(--hair-w) solid rgba(63, 217, 138, 0.30); border-radius: 5px;
  padding: calc(var(--inset-block) - var(--hair-w));
  margin-top: var(--sp-5);
  background: linear-gradient(180deg, rgba(63, 217, 138, 0.07), rgba(63, 217, 138, 0.01));
}
.dcj-gate .dcj-section, .dcj-gate .dcj-meta { color: var(--deploy); margin-top: 0; }

/* ONE RUNG INSIDE THE WELL. The gate is a card on the dark ground on the home hero, where it
 * takes --inset-block, and it is a block inside the paper well on an edition, where it has to
 * share a left edge with the slots the renderer emits around it. It did not: the gate sat at
 * 24 and IF YOU RUN at 21, three pixels apart inside one surface, at both widths. */
.gh-content .dcj-gate, .dcj-sample-issue .dcj-gate {
  padding: calc(var(--inset-slot) - var(--hair-w));
}

/* A WELL INSIDE A WELL. The home page samples the current edition as
 * article.dcj-sample-issue wrapping div.gh-content, and BOTH carried the well's inset and
 * border, so the sample's text sat 64px inside the paper where the same markup on an edition
 * page sits 32px in. Measured 2026-09-13: the sample's body began at x=141 against the page
 * rung of 80. The outer surface owns the inset; the inner one is only the colour. */
.dcj-sample-issue > .gh-content {
  padding: 0; border: 0; border-radius: 0; background: none;
}
.dcj-promo-what { font-family: var(--serif); font-size: var(--fs-1); line-height: 1.45; margin-top: 10px; color: var(--ink); }
.dcj-promo-what strong { font-weight: 600; }

/* ---------------------------------------------------------------- THE READING WELL
 * The edition renders on PAPER even on the dark site, and this is the load bearing decision
 * of the whole direction rather than a colour preference.
 *
 * ONE renderer feeds the page and the inbox: scripts/issue_render.py emits the item shape with
 * INLINE styles, because email has no stylesheet, and those styles are built for paper
 * (#0F0F0F ink on #fbfaf8). Jack chose light for email on 2026-09-10, and Gmail and Outlook
 * would force that hand anyway. Inline styles beat any stylesheet, so on a dark ground the
 * shared content rendered near black on near black: measured on staging, the whole first lead
 * was invisible.
 *
 * There were two ways out. Teach the renderer a medium and emit two palettes, which puts two
 * sources behind one look and is exactly how the page and the email drifted apart before
 * 2026-09-08. Or give that content the ground it was designed for. This is the second, so the
 * renderer stays single source, and the reading passage gets the surface long prose wants.
 *
 * The Console is therefore dark CHROME around a light WELL, which is the shape the email
 * preflight already argued for, applied to the web.
 */
:root { --well: #FBFAF8; --well-ink: #0F0F0F; --well-soft: #4A4A4A; --well-hair: rgba(15,15,15,.14); }

.dcj-well, .gh-content, .dcj-sample-issue {
  background: var(--well);
  color: var(--well-ink);
  border-radius: 5px;
}
.gh-content, .dcj-sample-issue {
  padding: calc(var(--inset-surface) - var(--hair-w));
  border: var(--hair-w) solid var(--well-hair);
}
.gh-content > *, .dcj-sample-issue > * { color: var(--well-ink); }
.gh-content p, .gh-content li, .dcj-sample-issue p { color: var(--well-ink); }
.gh-content a { color: #1A6B4F; }
.gh-content blockquote { border-left: 3px solid #1A6B4F; color: var(--well-soft); }
.gh-content hr { border: 0; border-top: 1px solid var(--well-hair); }
.dcj-sample-date { color: #74746F; }
.dcj-sample-title { color: var(--well-ink); }
.dcj-sample-body p { color: var(--well-soft); }
.dcj-sample-empty, .dcj-archive-empty { color: var(--well-soft); }

/* The article HEADER stays on the dark ground: it is chrome, not content. */
.gh-article-header { background: transparent; }
.gh-article-title { color: var(--ink); font-size: var(--fs-3); line-height: 1.1; font-weight: 600; }
/* The standfirst. It carries --measure like every other paragraph, and it did not until
 * 2026-09-10: design/tokens.css applies the measure to `p, .prose li, .lede, .description`
 * and this element matches none of those, so the vendor's 720px article grid governed it and
 * it rendered 79 characters per line at 1280, the widest text on the site, as the first
 * sentence on the page where a reader decides to pay. This is a SELECTOR, not a token: the
 * number still comes from the derivation. */
.gh-article-excerpt {
  color: var(--ink-soft); font-family: var(--serif); font-size: var(--fs-1);
  max-width: var(--measure);
}
/* FOUR VENDOR VALUES OFF THE RAMP, on surfaces every reader state shares. The stock stylesheet
 * loads BEFORE the brand bundle but wins on specificity: `.gh-content h2` (0,1,1) beats the
 * derived `h2` (0,0,1), and the other three had no brand rule at all. Measured on production
 * 2026-09-13, identical in every state and at both widths: every h2 in the well 27.2px
 * (1.6em of the well's 17px base) on /about/, /standard/, the edition and the home sample; the
 * edition's date 13.5px; the author name at weight 650; the hero field at weight 450.
 * sync-design.py reads brand.css and could not see any of them. The h2 takes the step under
 * the page title at each width (38 and 28 for the title, so 28 and 21 here), which is also the
 * nearest step to what shipped, so nothing jumps. */
.gh-content h2 { font-size: var(--fs-2); }
.gh-article-meta-content { font-size: var(--fs--1); }
.gh-article-author-name { font-weight: 600; }
.gh-form { font-weight: 400; }

/* The wall renders INSIDE the well, so it is a paper component here. Its own inline styles
 * come from the same renderer; these rules only carry the parts the theme owns. */
.dcj-gate {
  background: rgba(26, 107, 79, 0.05);
  border: 1px solid rgba(26, 107, 79, 0.30);
  color: var(--well-ink);
}
.dcj-gate .dcj-section, .dcj-gate .dcj-meta { color: #1A6B4F; }
.dcj-gate .dcj-promo-what { color: var(--well-ink); }
.dcj-gate .dcj-button, .dcj-gate .gh-button {
  background: #1A6B4F; color: #FFFFFF;
}
.dcj-gate .dcj-link { color: var(--well-soft); text-decoration-color: var(--well-hair); }
.dcj-gate .dcj-promo-count, .dcj-gate .dcj-promo-stamp { color: var(--well-ink); }
.dcj-gate .dcj-promo-stamp { color: #1A6B4F; }

/* ---------------------------------------------------------------- chrome: the footer
 * ONE footer. The stock gh-footer-signup block is gone from the template, which is what made
 * the page end twice in two different design languages. */
.dcj-foot { border-top: 1px solid var(--hair); margin-top: 64px; background: var(--ground); }
.dcj-foot-in {
  max-width: var(--page); margin: 0 auto; padding: var(--sp-7) var(--inset-page) var(--sp-8);
  display: flex; flex-wrap: wrap; gap: var(--sp-5) var(--sp-7); align-items: baseline; justify-content: space-between;
}
.dcj-foot a { color: var(--ink-soft); text-decoration: none; }
.dcj-foot a:hover { color: var(--deploy); }
.dcj-foot-legal { font-family: var(--mono); font-size: var(--fs--2); letter-spacing: 0.10em;
  color: var(--ink-faint); line-height: 1.9; }

/* ---------------------------------------------------------------- Portal
 * Portal renders in an iframe we cannot style, but the trigger and the account link are ours.
 * The floating launcher is off: this publication asks in the page, once. */
.gh-portal-triggerbtn-iframe, iframe[title="portal-trigger"] { display: none !important; }

/* ---------------------------------------------------------------- motion */
.dcj-rise { animation: dcj-rise 420ms cubic-bezier(.2,.7,.3,1) both; }
@keyframes dcj-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .dcj-rise { animation: none; } }

/* ---------------------------------------------------------------- phone
 * Every grid above is a desktop grid until told otherwise, and most readers arrive here. */
@media (max-width: 860px) {
  .dcj-hero-grid { grid-template-columns: 1fr; gap: 32px; }
  .dcj-calls { grid-template-columns: 1fr; }
  /* On a phone the calls stack into rows rather than sitting in a grid, so they take the
     page rung, not a card rung: a row that indented would break the page's left line. */
  .dcj-call { padding: var(--sp-5) 0 var(--sp-6); border-bottom: var(--hair-w) solid var(--hair); }
  .dcj-call:last-child { border-bottom: 0; }
}
@media (max-width: 640px) {
  /* ONE phone edge for the chrome and the page. This rule set 20px and a later one set
   * .dcj-wrap to 16px, so the wordmark sat 4px further in than the copy under it.
   * 16px is the derived value (profile.json space.page_side_phone), so all three take it. */
  /* The phone rung. One value for all three so the wordmark, the copy and the footer
     share a line; they did not until 2026-09-11, when one rule said 20 and a later one 16. */
  .dcj-wrap, .dcj-nav-in, .dcj-foot-in {
    padding-left: var(--inset-page-phone); padding-right: var(--inset-page-phone); }
  .dcj-nav-menu { display: none; }
  .dcj-burger { display: inline-flex; }
  /* THE CHROME CONTROLS TAKE THE LOCKED TOUCH TARGET ON A PHONE, and only on a phone. 32px is
   * a defensible size for a pill a mouse points at and is not one for the primary signup call
   * under a thumb: design/profile.json locks controls.touch_target at 44 and the header pill
   * was the surface breaking it. The profile already splits on pointer this way for inputs
   * ("coarse pointers get 16px and fine pointers keep the lock"), so this is that rule again.
   * The header's own padding drops one step on the 4px scale to pay for most of it: 44 plus 24
   * is 68px against the 64px it has always been, so a 12px larger tap target costs 4px of a
   * sticky header. */
  .dcj-nav-actions { --ctl-chrome: var(--ctl); }
  .dcj-nav-in { padding-top: 12px; padding-bottom: 12px; }
  /* THE OPEN MENU IS A LIST OF TOUCH TARGETS, on the page rung. Measured on production
   * 2026-09-13 at 390: each route link stood 18.6px tall and Sign in 27.1px, under the 44px
   * the profile locks (controls.touch_target), and the list sat 20px in where the wordmark
   * above it sits 16. Every link is a full width row at --ctl, so the rows are the spacing
   * and the gap is zero. */
  .dcj-nav.is-open .dcj-nav-menu {
    display: flex; flex-direction: column; align-items: stretch; gap: 0;
    position: absolute; left: 0; right: 0; top: 100%; background: var(--ground);
    border-bottom: 1px solid var(--hair); padding: var(--sp-1) var(--inset-page-phone) var(--sp-3);
  }
  .dcj-nav.is-open .dcj-nav-menu a { display: flex; align-items: center; min-height: var(--ctl); }
  .dcj-hero { padding: 32px 0 36px; }
  .dcj-h1 { font-size: var(--fs-2); }
  .dcj-lede { font-size: var(--fs-0); }
  .dcj-row { grid-template-columns: 1fr; gap: 10px; align-items: start;
    padding: calc(var(--sp-5) - var(--hair-w)) calc(var(--inset-block-phone) - var(--hair-w)); }
  .dcj-verdict { justify-self: start; }
  .gh-content p, .gh-content li { font-size: var(--fs-0); }
  .gh-article-title { font-size: var(--fs-2); }
  .gh-content h2 { font-size: var(--fs-1); }
  .dcj-foot-in { flex-direction: column; gap: 16px; }
}

/* ---------------------------------------------------------------- the rest of the surface
 * Every selector the light layer styled, carried over, so the switch cannot silently drop a
 * component. Enumerated from the old file rather than remembered.
 */

/* the hero form, as the templates actually mark it up */
.dcj-signup { margin-top: 20px; }
.dcj-calls-block { max-width: 560px; }
.dcj-calls-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.dcj-joinform { flex: 1; min-width: 280px; }
.dcj-joinrow {
  display: flex; border: 1px solid var(--hair-strong); border-radius: 4px;
  overflow: hidden; background: var(--ground);
}
.dcj-joinrow:focus-within { border-color: var(--deploy); }
/* The field row stands one step above the rest, because a 16px input needs the room and
 * because it is the single most important control on the site. Both halves take the SAME
 * declared height, so the row cannot be set by whichever of them happens to be taller.
 * THE ROW IS BORDERED, SO THE HALVES SUBTRACT THE BORDER (rule 3, the same subtraction every
 * inset makes): a 52px half inside a 1px border made the box a reader sees 54px, and the
 * outlined button beside it, which stretches to the row, 54px too. Measured on production
 * 2026-09-13 at 1280: input 52, button 52, row 54, `Read every edition` 54, on neither rung of
 * the control scale. The halves take 52 minus the two hairlines, so the ROW is 52. */
.dcj-joinrow .gh-form-input {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--ink);
  font-family: var(--sans); font-size: var(--fs-0); padding: 0 14px;
  height: calc(var(--ctl-lg) - 2 * var(--hair-w));
}
.dcj-joinrow .gh-form-input::placeholder { color: var(--ink-faint); }
.dcj-joinrow .gh-form-input:focus { outline: none; }
.dcj-joinrow .gh-button {
  border-radius: 0; padding: 0 20px; display: inline-flex; align-items: center; gap: 8px;
  height: calc(var(--ctl-lg) - 2 * var(--hair-w)); min-height: calc(var(--ctl-lg) - 2 * var(--hair-w));
}
/* NO ARROW, AT ANY WIDTH. The vendor hides the button's own label under 576px
 * (`.gh-form .gh-button span span{display:none}`) and shows an arrow icon in its place, and
 * the brand layer's override of that rule was dropped by the Console commit. Measured on
 * production 2026-09-13, signed out at 390: innerText "" and a 60px green square, on the one
 * control the home page exists for, while docs/copy-spec.md 0.5 names the label as the answer
 * to the price objection. The label stays at every width. The arrow goes at every width: it
 * only ever rendered on phones, so the desktop button never had one, and one control has
 * one shape. The loader and the checkmark are the button's OWN svgs, outside the span, and
 * the vendor's loading and success states still show those. */
.dcj-joinrow .gh-button svg { width: 15px; height: 15px; }
.dcj-joinrow .gh-button span svg { display: none; }
@media (max-width: 576px) {
  .gh-form .gh-button span span { display: inline; }
}
.dcj-cta-row-hero { margin-top: 16px; }
.dcj-confirm {
  display: none; font-family: var(--mono); font-size: var(--fs--1); line-height: 1.6;
  color: var(--deploy); margin-top: 10px;
}
.gh-form.success .dcj-confirm { display: block; }
.gh-form.success .dcj-joinrow { display: none; }
.dcj-prelaunch {
  font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.08em;
  color: var(--ink-faint); margin-top: 18px;
}
.dcj-signup-note { font-size: var(--fs--1); color: var(--ink-faint); margin-top: 10px; line-height: 1.6; }

/* proof, promo and the founding counter */
.dcj-proof { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--hair); }
/* Inside the rail the proof sits one space step under the note, on the scale (it was 18px,
 * on neither the 4px grid nor the scale). Declared HERE, before the phone block further down
 * zeroes it: that block's rule is at the same specificity and wins only if it is later. */
.dcj-offer .dcj-proof { margin-top: var(--sp-4); padding-top: var(--sp-4); }
.dcj-proof-line { font-size: var(--fs--1); line-height: 1.7; color: var(--ink-faint); }
.dcj-promo { margin-top: 18px; }
.dcj-promo-stamp {
  font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--deploy); font-weight: 600;
  /* One scale step under the sentence it footnotes. It sat at 0, its capitals resting on the
   * descenders above (fourth verifier pass, 2026-09-13); T13 holds it. */
  margin-top: var(--sp-2);
}
/* In the gate the stamp is also a .dcj-meta, and `.dcj-gate .dcj-meta { margin-top: 0 }` outranks the
 * rule above, so the edition's stamp stayed at 0 while the rail and /pricing/ moved to 8. */
.dcj-gate .dcj-meta.dcj-promo-stamp { margin-top: var(--sp-2); }
.dcj-promo-count {
  font-family: var(--mono); font-size: var(--fs-2); font-weight: 600; color: var(--ink);
  letter-spacing: -0.01em; line-height: 1.1; margin-top: 6px;
}
.dcj-promo-count small { font-size: var(--fs-0); color: var(--ink-soft); font-weight: 400; }

/* the sample: the current edition as a free reader sees it */
.dcj-sample { margin-top: 48px; }
/* the sample card IS the well: it holds the same rendered edition the post page holds */
/* This re-declared the well's inset 28px lower down the file and won, so the sampled
 * edition's surface sat one rung off every other paper surface on the site: its content
 * edge landed at 109 where the rung is 112. Measured 2026-09-13. The inset is declared
 * once, above, and this line is what a second declaration looks like. */
.dcj-sample-issue { padding: calc(var(--inset-surface) - var(--hair-w)); }
.dcj-sample-date {
  font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-faint);
}
.dcj-sample-title {
  font-size: var(--fs-2); line-height: 1.2; font-weight: 600; margin-top: 8px;
  letter-spacing: -0.02em; color: var(--ink);
}
.dcj-sample-body { margin-top: 16px; }
/* one rule for both surfaces that render {{content}}: the item body is the human voice */
.gh-content p, .gh-content li, .dcj-sample-body p {
  font-family: var(--serif); font-size: var(--fs-1); line-height: 1.6;
}
/* THE LEDGER ROW IS A TABLE CELL, and the rule above named only p and li. The renderer
 * (scripts/issue_render.py) emits the ledger's argument with no font-family on purpose, "so
 * the page keeps its serif", and the page kept the BODY's sans instead: measured on production
 * 2026-09-13 as a paid reader, every row rendered Inter between a serif lead and a serif
 * pattern, a third face that exists on the web only for the reader who paid for it. The size
 * is inline and belongs to the renderer; the family is the well's. */
.gh-content td { font-family: var(--serif); }
.dcj-sample-empty { font-family: var(--serif); font-size: var(--fs-1); line-height: 1.6; color: var(--ink-soft); }

/* the record list, the archive and pagination */
.dcj-feeds { margin-top: 20px; }
.dcj-feed { display: grid; gap: 14px; }
.dcj-row-date { white-space: nowrap; }
.dcj-row-lockcell { display: flex; align-items: center; gap: 10px; }
/* A LOCKED ROW STATES THE REASON IT IS LOCKED, in the machine voice, sized to its words. The
 * Console commit sized this class as a 14px icon box while partials/issue-row.hbs has rendered
 * the words "Full access" in it since 2026-09-09. Measured on production 2026-09-13: the two
 * words wrapped onto two lines of the UI sans and painted 11.8px below the card's border at
 * 390 and 13.9px past its right border at 1280, on /archive/ and the 404 page. This is the
 * pre Console recipe (its siblings are .dcj-verdict and .dcj-plan-tag): no width, no height,
 * the box is the label. */
.dcj-lock {
  font-family: var(--mono); font-size: var(--fs--2); letter-spacing: 0.07em; line-height: 1.45;
  text-transform: uppercase; color: var(--ink-faint);
  border: 1px solid var(--hair-strong); border-radius: 3px; padding: 3px 7px; white-space: nowrap;
}
.dcj-pagination {
  display: flex; justify-content: space-between; gap: 16px; margin-top: 32px;
  padding-top: 20px; border-top: 1px solid var(--hair);
}
.dcj-rule { border: 0; border-top: 1px solid var(--hair); margin: 40px 0; }
.dcj-source { font-family: var(--mono); font-size: var(--fs--1); color: var(--ink-faint); line-height: 1.7; }
.dcj-sourcing { color: var(--ink-faint); font-size: var(--fs--1); line-height: 1.7; }

/* pricing */
.dcj-plan-rows { margin-top: 20px; border-top: 1px solid var(--hair); }
/* CENTRE, NOT BASELINE. `.dcj-plan-tag` is a bordered chip with vertical padding, and a chip's
 * baseline is its TEXT baseline, so baseline alignment hangs its border and padding below the
 * line the row sits on: measured 1.85px low at both widths. The footer three doors down keeps
 * baseline on purpose, because every item there is plain text and a shared baseline is exactly
 * what it wants. The rule is the boxed item, not the property. */
.dcj-plan-row {
  display: flex; justify-content: space-between; gap: 20px; align-items: center;
  padding: 16px 0; border-bottom: 1px solid var(--hair); font-size: var(--fs-0);
  color: var(--ink-soft);
}
/* THE PLAN CHIP IS A LABEL, NOT A CALL. The paid chip took the accent with a 10 percent fill,
 * the same dress as a DEPLOY verdict, and it is a span in a row that nothing happens to when
 * tapped. Rule 8: the accent means "act here" and nowhere else, and the rail's own provenance
 * line was demoted from the accent for the same reason. Measured 2026-09-13: the census of the
 * hue on /pricing/ found five elements inside an <a> and this one span. Paid reads one step
 * louder than free, in ink. */
.dcj-plan-tag {
  font-size: var(--fs--2); font-weight: 600; letter-spacing: 0.14em;
  padding: 4px 9px; border-radius: 3px; border: 1px solid currentColor; color: var(--ink-soft);
  background: transparent; white-space: nowrap;
}
.dcj-plan-tag[data-v="free"] { color: var(--ink-faint); }
.dcj-price-lead {
  font-family: var(--serif); font-size: var(--fs-3); font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.15; color: var(--ink);
}
.dcj-price-alt { font-size: var(--fs-0); color: var(--ink-soft); margin-top: 8px; }
.dcj-cta { margin-top: 22px; }

/* the issue foot and the referral */
.dcj-issue-foot { margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--hair); }
.dcj-referral { margin-top: 12px; }
/* The referral url is a <code> beside a Copy button, not a link, so it does not take the
 * accent (rule 8): in the hue it read as a link that does not click. Found 2026-09-13 by the
 * rendered accent census, paid home and edition. */
.dcj-referral-link {
  font-family: var(--mono); font-size: var(--fs--1); color: var(--ink);
  word-break: break-all; text-decoration: none;
  /* The link and its one button on a row, wrapping on a phone rather than squeezing the
     url. Two declarations, because the button reuses .dcj-button-outline and needs nothing
     else: a copy control that arrives with its own visual language is the overcomplication. */
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.dcj-referral-copy { padding: 0 14px; flex: 0 0 auto; cursor: pointer; }
.dcj-callline { font-family: var(--mono); font-size: var(--fs--1); letter-spacing: 0.10em; }
.dcj-calls-aside { margin-top: 16px; }
/* The error pages, 404 and everything else. They were CENTRED, in a publication whose whole
 * identity is left alignment, and `padding: 80px 0` also cancelled .dcj-wrap's side padding
 * on the same element, so the copy sat at 40px while the wordmark above it sat at 80 and the
 * outline button below it sat left. Three alignments on one page. Measured 2026-09-10 from
 * the capture, not from the template: the template's own comment says it inherits the
 * Console, and reading that comment instead of the screenshot is how it was signed off as
 * reviewed. Only the vertical padding belongs here; .dcj-wrap owns the sides at every
 * breakpoint. */
.dcj-error { padding-top: 80px; padding-bottom: 80px; }

/* THE READING CANVAS STARTS ON THE SAME VERTICAL LINE AS THE WORDMARK.
 * The vendor's article grid centres its main column between two 1fr gutters, so `/pricing/`,
 * `/about/` and `/welcome/` began 200px to the right of the header above them at 1280 and
 * 200px at 1920, while `/` and `/archive/`, which use .dcj-wrap, began on the wordmark's
 * line. Four of six routes did not line up with their own chrome. Measured 2026-09-10 at
 * 1280, 1920 and 390 before and after.
 *
 * The LEFT gutter is computed to land exactly where .dcj-wrap's content edge lands, which
 * is (viewport - --page) / 2 + 40, floored at 40 so a viewport narrower than --page keeps
 * the page margin. The right gutter absorbs the slack. The full bleed paper band is
 * untouched: .gh-content still spans the viewport, only its column moves.
 *
 * The reading column itself does not change: 554px at 1280 and 1920, 307 to 358 on a phone,
 * measured on all five routes with zero overflow at any of the three widths. */
.gh-canvas {
  grid-template-columns:
    [full-start] max(40px, calc((100% - var(--page)) / 2 + 40px))
    [wide-start main-start] var(--main)
    [main-end] minmax(0, 1fr) [wide-end full-end];
}
@media (max-width: 640px) {
  .gh-canvas {
    grid-template-columns:
      [full-start] 16px [wide-start main-start] var(--main) [main-end] minmax(0, 1fr) [wide-end full-end];
  }
}

/* the calls, as the templates mark them: a data attribute, not a class */
.dcj-verdict[data-v="deploy"] { color: var(--deploy); background: rgba(63, 217, 138, 0.10); }
.dcj-verdict[data-v="wait"]   { color: var(--wait);   background: rgba(232, 138, 92, 0.10); }
.dcj-verdict[data-v="watch"]  { color: var(--watch);  background: rgba(126, 140, 134, 0.10); }
.dcj-call .dcj-verdict { display: inline-block; }
/* THE CHIP INSIDE A RENDERED EDITION. scripts/issue_render.py styles the issue slots INLINE so
 * the page and the inbox read one source, and its chip carried padding and a border with no
 * display, which is the same inline-box defect as the header pill: measured 2px outside its own
 * paragraph at 390px. The renderer now emits the display, so every edition from here is right at
 * the source. This rule exists for the one edition that was rendered BEFORE the fix and is
 * already scheduled, whose stored html cannot be rebuilt without touching Saturday's send. An
 * inline style is only beatable with !important, which is why this is the single one in the
 * file. It becomes redundant, never wrong, once no pre-fix edition is on the site. */
.gh-content .dcj-verdict, .dcj-sample-issue .dcj-verdict {
  display: inline-block !important; vertical-align: middle;
}

/* The last six, read off the light layer rather than remembered. .dcj-runin and
 * .dcj-ifyourun are deliberately absent here and were in that file too: the issue slots are
 * styled INLINE by scripts/issue_render.py so the page and the inbox read one source. Two
 * sources for one look is how they drift. */
.dcj-archive-empty { margin: 0; padding: 4px 0 14px; font-size: var(--fs-0); line-height: 1.6; color: var(--ink-soft); }
.dcj-ledger { border-top: 1px solid var(--hair-strong); padding-top: 12px; }
.dcj-promise p { font-size: var(--fs-0); line-height: 1.6; color: var(--ink-soft); }
/* THE LABEL INSIDE THE PROMISE IS A LABEL. `.dcj-promise p` (0,1,1) outranked `.dcj-section`
 * (0,1,0), so /pricing/'s THE STANDARD rendered at 16px beside four 12px labels (measured on
 * production 2026-09-13, after the section voice was restored). verify-rendered-type.mjs T3
 * holds every section label on a page to one size. */
.dcj-promise .dcj-section { font-size: var(--fs--1); line-height: 1.5; }
/* THE GATE OPENS ON ITS INSET. The well's section label carries margin 40px 0 10px, which is
 * right between two blocks and wrong as a first child: the home page hides the founding block in
 * its sample, so READ EVERY EDITION became the gate's first visible child and opened 60px under
 * the border against 20 at the side; on the edition the founding block's own 18px did the same
 * (second verifier pass, 2026-09-13). verify-rendered-type.mjs T9 now reads the gate too. */
.dcj-gate .dcj-gate-content > :first-child,
.dcj-sample .dcj-gate .dcj-promo + .dcj-cta-row + .dcj-section { margin-top: 0; }
/* THE WELL ENDS ON ITS INSET. The vendor gives a page's last block margin-bottom 6vw, so /about/,
 * /standard/ and /welcome/ ended on 112px of paper at 1280 against 35 at the top (same pass).
 * verify-rendered-type.mjs T10 holds the well's top and bottom within one scale step. */
.page-template .gh-content > :last-child:not(.kg-width-full) { margin-bottom: 0; }
/* ONE LABEL, ONE DISTANCE TO WHAT IT HEADS. The plan rows carried margin-top 20px of their own, so
 * WHAT IS FREE floated 42px above them while every other label on the page sits 8 above its
 * block (same pass). T11 holds labels on one ground to one gap. */
/* The first feed is a label over its rows, so it flows as a block: the feed's grid gap (14) stacked on
 * the label's own 8 and read 22. The second feed keeps the grid for the card and its call. */
.dcj-feed:has(> .dcj-section) { display: block; }
.dcj-section + .dcj-plan-rows { margin-top: 0; }
.dcj-wrap > section:last-child { padding-bottom: 88px; }
/* the monthly price rides inside the annual sentence, so it is prose, not a machine label */
.dcj-gate .dcj-plan-monthly, .dcj-plan-monthly {
  font-family: inherit; color: var(--ink-soft); letter-spacing: 0; text-transform: none;
}
.dcj-pricing .dcj-feeds { margin-top: 8px; }
.dcj-pricing .dcj-promo { max-width: none; }
.dcj-gate .dcj-promo-what { max-width: 52ch; }
/* "CLAIM IT WITH THE SIGNUP ABOVE" IS ONLY TRUE WHERE THERE IS A SIGNUP ABOVE.
 * promo-state.hbs is generated once and included in three places: the hero rail, the pricing
 * page and the wall on an edition. Only the first has the email field on the same page. The
 * light layer hid this pointer in the issue foot, which was the same problem seen once; the
 * state walk found it on /pricing/ and on the edition page too, where it sends a reader
 * looking for a form that is not there. The wall carries a "Claim one" button that links to
 * the field, so nothing is lost by hiding the sentence.
 * Default hidden, shown only in the hero rail, so a fourth include site cannot reintroduce it. */
.dcj-promo-cta { display: none; }
.dcj-offer .dcj-proof .dcj-promo-cta { display: inline; }


/* ---------------------------------------------------------------- form reset
 * the stock theme ships the signup as a 40px PILL CAPSULE with the input at
 * `position:absolute; inset:0` and the button absolutely placed inside it. Layering on top
 * of that does not work: measured on staging, .dcj-joinrow computed to 2x2px because its
 * only child was taken out of flow, so the field rendered as a floating grey pill with the
 * button escaping to its right. The whole structure is replaced, not decorated.
 * `position: relative` stays because the platform anchors the error message at top:100%.
 */
.dcj-signup .gh-form, .dcj-joinform {
  position: relative; display: block; background: transparent; border-radius: 0;
  height: auto; max-width: none; font-size: var(--fs-0); letter-spacing: normal;
}
.dcj-signup .gh-form > svg { display: none; }
/* The reset restates the field's height: it said `height: auto` here, which is what undoes the
 * vendor's inset:0, and the row then took the taller of a 50.4px padded line box and the
 * button. One declared height, the same subtraction as above, so the row is 52 exactly. */
.dcj-joinrow .gh-form-input {
  position: static; inset: auto; flex: 1 1 auto; width: auto;
  height: calc(var(--ctl-lg) - 2 * var(--hair-w));
  border: 0; border-radius: 0; background: transparent; color: var(--ink);
  padding: 0 14px; font-size: var(--fs-0); line-height: 1.4;
}
.dcj-joinrow .gh-form-input::placeholder { color: var(--ink-faint); }
.dcj-joinrow .gh-form-input:focus { outline: none; box-shadow: none; }
.dcj-joinrow .gh-button {
  position: static; right: auto; top: auto; flex: 0 0 auto;
  height: calc(var(--ctl-lg) - 2 * var(--hair-w)); border-radius: 0; padding: 0 20px;
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
  letter-spacing: 0.10em; text-transform: uppercase;
  background: var(--deploy); color: var(--on-accent);
}
.dcj-signup .gh-form [data-members-error] {
  font-size: var(--fs--1); margin-top: 8px; color: var(--wait);
}
.dcj-h1 { max-width: 20ch; }
@media (min-width: 640px) { .dcj-h1 { max-width: 16ch; } }

/* the stock theme ships buttons at a 999px pill. The house retired the pill: 3px in the Console. */
.gh-button, .gh-head-button { border-radius: 3px; }

/* THE BYLINE SITS ON THE PAGE GUTTER, like everything above and below it. the stock theme nudges the
 * meta row 6px right (`.gh-article-meta{margin-left:6px}`) to compensate for the negative
 * margin on its stacked avatar circles, a device this publication does not use: one author,
 * and now no stock silhouette. Measured on the scheduled edition 2026-09-11: the title sat at
 * 80 and the byline at 86 at 1280, 16 and 22 at 390. The gutter is derived from the page, so
 * the byline takes it rather than a correction for a thing that is not there. */
.gh-article-meta { margin-left: 0; }

/* (4) ONE founding offer per page. The sampled edition carries the EDITION's wall, which
 * offers the founding membership; the hero rail already offers it beside the field that
 * wall's own button points at. Inside the sample it is the second on the page, so it goes.
 * Same rule the light layer applied, kept because the reason did not change. */
.dcj-sample .dcj-gate .dcj-promo,
.dcj-sample .dcj-gate .dcj-promo + .dcj-cta-row { display: none; }


/* ---------------------------------------------------------------- the well wins, last
 * CASCADE ORDER, not specificity. The reading well is declared where it belongs
 * structurally, near the article, but the component rules further down the file were setting
 * the same properties for the DARK ground and, being later, they won. Measured on staging:
 * the sampled edition's own title rendered #EDEBE4 on #FBFAF8 and was invisible, on the one
 * card the home page exists to show.
 *
 * Everything inside a well takes the well's ink, restated here at the end so a component
 * added later cannot silently repaint itself dark on paper.
 */
.dcj-sample-issue .dcj-sample-title,
.dcj-sample-issue .dcj-sample-date,
.gh-content .dcj-sample-title { color: var(--well-ink); }
.dcj-sample-issue .dcj-sample-date { color: #74746F; }
.dcj-sample-issue .dcj-meta,
.gh-content .dcj-meta { color: #74746F; }
.dcj-sample-issue .dcj-row-title,
.gh-content .dcj-row-title { color: var(--well-ink); }
.dcj-sample-issue .dcj-row-note,
.gh-content .dcj-row-note { color: var(--well-soft); }
.dcj-sample-issue .dcj-promo-count,
.gh-content .dcj-promo-count { color: var(--well-ink); }
/* THE WALL'S COUNT AND PROVENANCE LINE KEEP THE MACHINE VOICE INSIDE THE WELL. Both are <p>
 * (partials/promo-state.hbs), so `.gh-content p` above, at 0,1,1, outranked their 0,1,0
 * classes and handed them the serif at body size: measured on production 2026-09-13, the
 * stamp "COUNTED FROM THE MEMBER LIST." rendered EB Garamond 21px with 3.36px of tracking,
 * taller than the paragraph above it, and the count "99 of 100 left" EB Garamond 21px, where
 * the same partial on the home rail renders mono 12 and mono 28. The colour block above
 * restated colour only, which is why it was not noticed. Two classes beat class plus type. */
.gh-content .dcj-promo-stamp { font-family: var(--mono); font-size: var(--fs--1); line-height: 1.5; letter-spacing: 0.14em; }
.gh-content .dcj-promo-count { font-family: var(--mono); font-size: var(--fs-2); line-height: 1.1; }
/* THE GATE'S GROUND IS TINTED, AND THE PROVENANCE INK WAS TUNED FOR BARE PAPER. #74746F is
 * exactly 4.50:1 on #FBFAF8 with no margin, and the gate lays a 5 percent green over the paper
 * (composite 239,242,239), which takes the stamp and the monthly price to 4.16:1, under AA.
 * Measured 2026-09-13 with a pixel sample, anonymous, both viewports. The line stays NEUTRAL
 * (the hierarchy note below took it out of the accent on purpose: a footnote about where a
 * number came from is not "act here"), one step darker: 4.97:1 on the sampled ground. */
.gh-content .dcj-gate .dcj-meta { color: #686863; }

/* THE MACHINE VOICE AND THE READING MEASURE INSIDE THE PAPER WELL.
 * .gh-content p (line 262) wins over the ground's own classes, so a theme partial rendered
 * in the well inherits 21px EB Garamond at margin-bottom 0 and a label becomes
 * indistinguishable from a paragraph. Measured 2026-09-10 on /welcome/ as a free member:
 * all four paragraphs of the inbox block came back "EB Garamond 21px w400 none mb=0px",
 * so the label read as body and the three asks ran together as one wall of text.
 * The EDITION never hits this, which is why it went unnoticed: issue_render.py inlines
 * label_style() on every label it emits, and an inline style beats any of this. A partial
 * cannot call the renderer, so the well needs the classes stated. */
.gh-content .dcj-section {
  font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--well-soft);
  margin: 40px 0 10px;
}
.gh-content .dcj-measure { font-size: var(--fs-0); margin-bottom: 0.9em; }
.gh-content .dcj-inbox > p:last-child { margin-bottom: 0; }


/* The sampled edition's title is an ANCHOR inside the paper well, and a link colour was
 * reaching it as pure white: measured 1.04:1 on paper, invisible, on the one card the home
 * page exists to show. The token fix before this one targeted .dcj-sample-title and missed,
 * because the text node lives on the <a> inside it. */
.dcj-sample-issue a, .gh-content .dcj-sample-title a, a.dcj-sample-title { color: var(--well-ink); }
.dcj-sample-issue .dcj-sample-title a:hover { color: #1A6B4F; }


/* ---------------------------------------------------------------- the phone reading column
 * NESTED PADDING COMPOUNDS, and on a phone it eats the thing the page is for. Measured on
 * staging at 390px: .dcj-wrap 20 + .dcj-sample-issue 28 + .gh-content 36 + .dcj-gate 28 left
 * the item paragraph 218px wide, THIRTY characters per line, against a comfortable range of
 * 45 to 75. The page looked fine in a thumbnail and was unreadable in the hand.
 *
 * The well keeps its inset on a desktop, where there is room for it to read as a surface,
 * and gives almost all of it back on a phone, where the column matters more than the frame.
 */
@media (max-width: 640px) {
  .dcj-sample-issue { padding: 0; border-radius: 4px; }
  .dcj-sample-issue > .dcj-sample-date,
  /* The well's title used to carry the inset itself while the well carried none, so the
     title sat on a different rung from the body three lines under it (17 against 34,
     measured at 390 on 2026-09-13). The WELL owns the inset at every width now. */
  .dcj-sample-issue > .dcj-sample-title { padding: 0; }
  .gh-content, .dcj-sample-issue {
    padding: calc(var(--inset-surface-phone) - var(--hair-w)); }
  .dcj-gate { padding: calc(var(--inset-block-phone) - var(--hair-w)); }
  .gh-article-header { padding-left: 0; padding-right: 0; }
  /* A MEDIA QUERY ADDS NO SPECIFICITY. The earlier phone block already stepped the item body
   * down to 16px, and the later rule that unified the type across the sample and the edition
   * page overrode it at the same specificity simply by being further down the file. Measured:
   * /about/ held 33 characters per line because its body was still 21px. Restated here, last,
   * where it wins. */
  .gh-content p, .gh-content li, .dcj-sample-body p { font-size: var(--fs-0); }
}

/* The platform's own accent variable is still #397E5B, the LIGHT theme's green, and the stock theme
 * paints the article tag with it. On the dark ground that computes 3.93:1 and fails AA. The
 * setting is left alone because Portal and the members UI read it; the theme overrides where
 * it lands on our ground. */
/* AN ACCENT BELONGS TO A GROUND, NOT TO A SITE. The first pass at this set one link colour
 * for everything inside the article, which painted the DARK ground's accent onto the paper
 * well and onto buttons that carry their own background. Eight elements failed AA at once:
 * green on green at 1:1 on /pricing/, and 1.75:1 on paper on /about/. One accent, two
 * grounds, and the rule has to say which.
 *
 * Dark chrome takes --deploy. The paper well takes the deep green the email uses, which is
 * the same colour the shared renderer already emits inline. Buttons are excluded from both:
 * a button's foreground is a property of its own background and never of the text around it.
 */
.gh-article-tag { color: var(--deploy); }
.gh-content a:not(.gh-button):not(.dcj-button):not(.dcj-button-outline),
.dcj-sample-issue a:not(.gh-button):not(.dcj-button):not(.dcj-button-outline) { color: #1A6B4F; }
.dcj-sample-issue .dcj-sample-title a { color: var(--well-ink); }
.gh-content .dcj-gate .dcj-link { color: var(--well-soft); }

/* Buttons on paper: the deep green with white on it, 5.31:1.
 * text-decoration is here because the stock theme's own `.gh-content a{text-decoration:underline}` is
 * specificity 0,1,1 and `.gh-button{text-decoration:none}` is 0,1,0, so the vendor's article
 * link rule reaches INTO every button in the edition body and wins. Nobody saw it until
 * 2026-09-12, because until the first edition published there was no button inside .gh-content
 * on any public surface: the home page sampled one true sentence and the issue page was a
 * draft preview. From 7:00 AM that morning the wall's `Read every edition` and `Claim one`,
 * the two controls the whole paid conversion runs through, carried underlined labels on the
 * home page and on the edition. An underlined label on a filled button reads as a broken link.
 * scripts/verify-journey.py has checked for exactly this since 2026-09-09 and its full walk is
 * not in the suite, which is why it went out. Two classes beat one class plus a type, so no
 * !important is needed. */
.gh-content .gh-button, .gh-content .dcj-button, .gh-content .dcj-button-outline,
.dcj-gate .gh-button, .dcj-gate .dcj-button, .dcj-gate .dcj-button-outline,
.dcj-sample-issue .gh-button, .dcj-sample-issue .dcj-button, .dcj-sample-issue .dcj-button-outline {
  background: #1A6B4F; color: #FFFFFF; text-decoration: none;
}
/* .dcj-link LOST THE SAME FIGHT AND WAS MISSED BY THE SWEEP ABOVE. It used to draw its own
 * underline with border-bottom, so the vendor's text-decoration landed ON TOP: measured on
 * production 2026-09-13, the wall's "Already a member? Sign in" reported textDecorationLine
 * underline AND borderBottomWidth 1px, a green underline hugging the text with a grey rule 2px
 * under it. The link's underline IS a text-decoration now (see .dcj-link: a border cannot
 * follow the text inside a 44px touch target), and the vendor's `.gh-content a{text-decoration:
 * underline}` is a SHORTHAND, which resets the colour to currentColor and the thickness to auto.
 * Two classes beat one class plus a type, so the paper values are restated here. */
.gh-content .dcj-link, .dcj-gate .dcj-link, .dcj-sample-issue .dcj-link {
  text-decoration-color: var(--well-hair); text-decoration-thickness: 1px; text-underline-offset: 4px;
}
.gh-content .dcj-button-outline, .dcj-gate .dcj-button-outline,
.dcj-sample-issue .dcj-button-outline { background: transparent; color: #1A6B4F; }
/* Buttons on the dark chrome keep the signal green with near black on it, 10.5:1. */
.dcj-nav-cta, .dcj-hero .gh-button, .dcj-hero .dcj-button, .dcj-joinrow .gh-button {
  background: var(--deploy); color: var(--on-accent);
}


/* ---------------------------------------------------------------- the phone, polished
 * Measured on production at 390px before this block: the home page ran 2,273px, 2.7 screens,
 * and stated the price TWICE inside 160px. The offer rail is a DESKTOP device: it exists
 * because at 1280 the right 55 percent of the fold was empty, and it earns its place there.
 * Stacked on a phone it lands directly under the split line, which has just said the same
 * thing in the same words, so the reader is told the price, then told it again.
 *
 * So on a phone the rail keeps only what is NOT said above it, the founding count and what is
 * on the record, and drops the heading, the price and the note. Nothing is lost: the split
 * line above carries the offer, and /pricing/ carries the detail.
 *
 * The rest is compression, not deletion. A phone reader is scrolling, so the vertical rhythm
 * that reads as calm at 1280 reads as padding here.
 */
@media (max-width: 860px) {
  /* the rail's offer is the split line's offer, said twice */
  .dcj-offer > .dcj-meta,
  .dcj-offer > .dcj-offer-price,
  .dcj-offer > .dcj-offer-note { display: none; }
  .dcj-offer { padding: calc(var(--inset-block-phone) - var(--hair-w)); }
  .dcj-offer .dcj-proof { margin-top: 0; padding-top: 0; border-top: 0; }
}
@media (max-width: 640px) {
  .dcj-hero { padding: 26px 0 30px; }
  .dcj-hero-grid { gap: 24px; }
  .dcj-lede { margin-top: 14px; }
  .dcj-split { margin-top: 16px; }
  .dcj-stack, .dcj-section { margin-top: 34px; }
  .dcj-call { padding: 16px 0 18px; }
  .dcj-call p { margin-top: 8px; }
  .dcj-promo-count { margin-top: 4px; }
  .dcj-promo-what { margin-top: 6px; }
  .dcj-wrap > section:last-child { padding-bottom: 48px; }
  .dcj-foot { margin-top: 40px; }
  .dcj-foot-in { padding-top: 24px; padding-bottom: 28px; }
}

/* A CONTENT HASH IS ALSO A CACHE BUST, AND ON 2026-09-22 IT HAD TO BE USED AS ONE.
 * The first build of the block below shipped as design.3938ced0674b.css and the edge answered
 * 301 to a trailing slash for it, then 404, with cf-cache-status HIT and an age of 33829s under
 * a one year max-age: a redirect cached before the file existed. The origin was serving the file
 * correctly the whole time, proven by the same URL with ?cb=1 returning 200 and 80,679 bytes.
 * The account token this engine deploys with cannot purge cache, so the fix is the one the
 * naming scheme already gives you: change the content, get a new name, get a URL the edge has
 * never seen. This comment is that change, and it is worth keeping because the failure mode is
 * silent: the site rendered, the brand layer simply was not there.
 * ---------------------------------------------------------------- the phone fold
 * THE HERO OWNS THE FIRST SCREEN, and until 2026-09-21 it owned 67 percent of it.
 *
 * Measured on production at a real 390x844, signed in as a free member: the header ran to
 * 69px, the hero ran 69px to 638px and then stopped, leaving 206px of ground with nothing in
 * it above the next section. The first tappable thing sat at y=467, so more than half the
 * screen was words before a reader could act. A hero that neither fills the fold nor clears
 * it reads as a page that was cut off mid thought.
 *
 * svh, NOT vh. On a phone 100vh is the LARGE viewport, the height with the browser chrome
 * retracted, so a vh sized hero is taller than the screen a reader actually arrives on and
 * its last line sits behind the URL bar. svh is the small viewport, chrome showing, which is
 * the fold on arrival. min-height and never height, so a longer word, a larger text size or
 * a translated string grows the box instead of clipping it.
 *
 * THE HEADER HEIGHT IS DERIVED, NOT TYPED. The nav measures var(--sp-3) + var(--ctl) +
 * var(--sp-3) + the hair rule = 69px, and it does NOT use --header-h: that token says 64px
 * and brand.css references it zero times, so subtracting it would have left a 5px seam under
 * the fold. Subtracting the primitives the nav is actually built from means the fill follows
 * the control height if the control height ever moves.
 *
 * PORTRAIT ONLY, and that is design/DESIGN.md's rule rather than a preference: a short
 * viewport takes "no full-height heroes" because a 390px tall landscape fold cannot hold one.
 * The landscape query is untouched, and so is every width above 640px.
 */
@media (max-width: 640px) and (orientation: portrait) {
  .dcj-hero {
    --dcj-fold: calc(100svh - (var(--ctl) + 2 * var(--sp-3) + var(--hair-w)));
    min-height: var(--dcj-fold);
    display: flex;
  }
  /* THE SLACK IS SPLIT, NOT POOLED, AND THAT IS AN AUTH STATE DECISION.
   * Measured on production at 390x844: a SIGNED OUT fold carries 649px of content, because the
   * signup form is 226px of it, so only 126px is spare. A SIGNED IN FREE member gets a 44px
   * button where that form was, so the same fold carries about 450px and roughly 325px is spare.
   * space-between was tried first and pooled all of it into ONE gap: tolerable signed out,
   * a 300px hole above the proof line signed in. Centring splits the spare evenly above and
   * below, so both states read as a composed screen with air rather than a page with a hole in
   * it, and neither depends on how much content the member branch happens to render. */
  .dcj-hero-grid {
    flex: 1 1 auto;
    align-content: center;
    row-gap: clamp(var(--sp-4), 3svh, var(--sp-7));
  }
}



/* ---------------------------------------------------------------- hierarchy, corrected
 * Two things read backwards on the phone once the page was short enough to see whole.
 *
 * THE LEDE WAS QUIETER THAN THE MECHANICS. Both it and the split line carried --ink-soft,
 * but the lede is serif and the split is sans, and sans reads heavier at the same size. So
 * the promise ("every item sourced, with a call on each") sat behind the billing detail
 * underneath it. The lede is the argument; it takes --ink.
 *
 * THE PROVENANCE LINE WAS SHOUTING. "Claim it with the signup above. Counted from the member
 * list." rendered in the accent, uppercase, at the loudest weight the machine layer has, for
 * a sentence that is a footnote about where a number came from. The accent means "act here",
 * and this is not that. The COUNT is the loud thing in that block and it already is.
 */
.dcj-lede { color: var(--ink); }
.dcj-promo-stamp { color: var(--ink-faint); }
.dcj-offer .dcj-promo-stamp { color: var(--ink-faint); }


/* ---------------------------------------------------------------- dead space in the rail
 * Measured on production: 89px of nothing between the rail's note and the founding count.
 * The proof block stacks three children and TWO of them render empty by design right now:
 * partials/subscriber-count.hbs is deliberately blank below the floor in growth/promo.json,
 * and the "editions on the record" line is absent until something is published. Their
 * margins are not blank, so an honest omission left a hole.
 *
 * Empty children collapse, and the first child that does render carries no top margin, so
 * the block is tight today and stays tight when the count and the record line appear.
 */
.dcj-proof > *:empty { display: none; }
.dcj-proof > *:first-child { margin-top: 0; }
/* The rail's proof spacing is declared with the proof block, ABOVE the phone block that zeroes
 * it. It sat here, 48 lines after that phone rule at the same specificity, so the phone rule
 * never won: measured on production 2026-09-13 at 390, the card opened on a 52px dead band
 * before "1 edition" where the inset is 16. sync-design.py --check now refuses a later
 * unscoped declaration of a property a phone block already set on the same selector. */
/* The real cause, found by measuring rather than guessing: .dcj-section is the page's
 * SECTION label and carries a 48px top margin, and partials/promo-state.hbs reuses that
 * class for its own heading. So the founding block opened with a page-level gap inside a
 * card. A label inside a component is a label, not a section. */
.dcj-promo > .dcj-section:first-child,
.dcj-offer .dcj-section { margin-top: 0; }


/* The pricing page stacks two blocks, "what is free" and the price, and .dcj-feeds sets no
 * gap between them, so the FULL ACCESS label and the price sat flush against the last plan
 * row and read as part of it. They are two answers to two questions. */
.dcj-pricing .dcj-feed + .dcj-feed { margin-top: 36px; }

/* THE FULL BLEED BAND IS A BAND, NOT A CARD. On a page and a post the paper well spans the
 * viewport (the reading column moves inside it), yet it kept the card's 5px corners and its side
 * borders at the screen's edge (fourth verifier pass, 2026-09-13). The band drops both, and adds
 * the border's width back to its side padding so its CONTENT edge stays on the surface rung
 * (rule 3). The home sample's card keeps its corners; it is not a band. T14 holds it. */
.gh-content.gh-canvas {
  border-radius: 0; border-left-width: 0; border-right-width: 0;
  padding-left: var(--inset-surface); padding-right: var(--inset-surface);
}
@media (max-width: 640px) {
  .gh-content.gh-canvas { padding-left: var(--inset-surface-phone); padding-right: var(--inset-surface-phone); }
}

/* THE WORDMARK IS A LINK, SO IT IS A TOUCH TARGET. At 390 under a coarse pointer the header's
 * wordmark was 24.8px tall and the footer's 21px, on every route (fifth verifier pass, 2026-09-13);
 * the lock is 44px under coarse pointers (DESIGN.md Controls). T7 counts it now. */
@media (pointer: coarse) {
  .dcj-brand { display: inline-flex; align-items: center; min-height: var(--ctl); }
}

/* THE SAMPLE CARD OPENS ON ITS PADDING. Its title is the card's first child and kept the title's
 * own 8px margin, so the card opened 40 under its border against a 32 side inset at 1280 and 24
 * against 16 at 390 (same pass). The outer surface owns the inset (rule 3). T10 reads it now. */
.dcj-sample-issue > :first-child { margin-top: 0; }

