/* bylineAL — the home / landing skin.
   Shared by index.html (via front-door.css), editorial.html and corporate/.

   Refined private-press editorial typography on a cool grey-green ground, deep
   ink, brand teal used sparingly. Rule lines and typographic hierarchy do the
   structural work: no cards, no shadows, no glassmorphism.

   The three lines above used to describe cream paper, deep ink and an oxblood
   accent. None of those had existed since the 2026-07-14 de-tell. Corrected
   2026-07-27 with the rest of the stale-comment class in the design review. */

/* Axis tags sorted, Schibsted as a range, Fraunces removed.
   See the note at the head of editorial-frame.css. 2026-07-27. */
@import url('https://fonts.googleapis.com/css2?family=Schibsted+Grotesk:wght@400..800&family=Newsreader:ital,opsz,wght@0,6..72,200..800;1,6..72,200..800&family=JetBrains+Mono:wght@400;500;700&display=swap');

:root {
  /* NINE TEXT SIZES for the whole product, replacing fifteen. Eight of them
     applied 2026-08-10 from the styling preview; the ninth added 2026-08-12 on
     Ritu's ruling. This is the LAST step to touch, because it is the only one
     that can change where a word breaks.

     The fifteen fractional sizes had one cause and it was not a second scale:
     the root font is 18px and sizes were written in rem, so 0.7rem is 12.6px.

     THE NINTH SIZE CLOSES THE ONE BROKEN STEP. The scale used to run 40px
     straight to 68px, a jump of 1.7 where every other step is 1.2 to 1.45.
     40 to 48 is 1.20 and 48 to 68 is 1.42. It cost no renaming: --t-8 held 68
     and not one declaration in the product used it, so 48 took that name. 48 is
     not an invented number either. It is where the front door's headline stops
     growing, 2.6rem or 46.8px, which was the one headline end that landed on
     nothing.

     A HEADLINE IS NOT A SIZE HERE. It is a ramp that grows with the window, and
     it stays one: h1 keeps its clamp, and only the two ENDS of that clamp read
     tokens. Freezing a headline onto a fixed step is what turned eight of them
     from one line into two when this was measured on 2026-08-10. See
     --track-hero below, and the hero step in preview/build-precision.mjs. */
  --t-1:         12px;
  --t-2:         14px;
  --t-3:         16px;
  --t-4:         19px;
  --t-5:         23px;
  --t-6:         28px;
  --t-7:         40px;
  --t-8:         48px;
  --t-9:         68px;
  /* SEVEN SPACING STEPS for the whole product, replacing thirteen. Applied
     2026-08-10 from the styling preview.

     Values under 4px are left alone: below the base step there is no step to
     round to, and a 1px nudge is usually doing optical work a scale should not
     overrule.

     INLINE padding and margin are only ever REDUCED, never raised. Raising them
     narrows the column the text is set in, and a narrower column is how a
     paragraph gains a line. Measured: rounding to nearest added a line to five
     paragraphs on corporate and social at 390. See inlineAt() in
     preview/build-precision.mjs for which positions of a shorthand count as
     inline. */
  --s-1:         4px;
  --s-2:         8px;
  --s-3:         16px;
  --s-4:         24px;
  --s-5:         40px;
  --s-6:         64px;
  --s-7:         104px;
  /* The x-height match for the mono-to-sans move. MEASURED, not guessed:
     JetBrains Mono renders an x-height of 0.55 em and Schibsted Grotesk 0.527,
     so giving the sans the mono's ratio scales it up about 4.3% and a 12px
     label renders at 12.52px with the two faces sitting on one x-height. See
     preview/x-height.json.

     A number and not the from-font keyword: the number is written down, it can
     be checked against that file, and it does not change silently when a font
     file is updated. */
  --x-adjust:    0.55;
  /* TWO TRACKING VALUES for the whole product, replacing ten. Applied
     2026-08-10 from the styling preview.

     --track-caps is for text the sheet also sets in uppercase. Capitals need
     the air; a sentence does not, which is why positive tracking on mixed-case
     text goes to plain normal here rather than to 0.16em. That would be grotesque
     on a sentence, and 26 selectors were carrying it.

     --track-tight is the only negative value. h1 keeps its own: loosening
     DeskTriage's and FieldReportage's headline from -0.03em to -0.01em wrapped
     both onto two lines, and the line count wins.

     NOT DONE HERE: the trailing letter-space. CSS puts the tracking after the
     LAST character too, so a centred or right-aligned capped label sits half a
     tracking left of where it should. Cancelling it means adding a negative
     margin to 160 rules, which is a second decision and gets measured on its
     own. */
  --track-tight: -0.01em;
  --track-caps:  0.16em;
  /* ONE TRACKING VALUE FOR EVERY HEADLINE. Applied 2026-08-12.

     The product carried four: -0.03em on the tool pages, -0.03em on editorial
     and corporate, -0.025em on the front door, -0.02em on social. This is the
     TIGHTEST of them, and tightest is the safe direction: loosening
     DeskTriage's and FieldReportage's headline from -0.03em to -0.01em wrapped
     both onto two lines when the tracking step was measured on 2026-08-10.
     Tightening cannot add a line.

     It is separate from --track-tight because that is the body's value,
     -0.01em, and a 68px headline and a 16px label do not want the same one. */
  --track-hero:  -0.03em;
  /* ONE RULE WEIGHT for the whole product: every divider in the five sheets
     reads this token instead of carrying its own number.

     THE VALUE IS 1px, AND 0.5px WAS TRIED FIRST AND MEASURED. In Chrome a
     0.5px border occupies exactly the same space as a 1px one and reports as
     1px, at BOTH display densities: a 40px-tall box with 0.5px borders and one
     with 1px borders both measure 42px. Ritu's ruling of 2026-08-10 is that
     Chrome is the only engine in scope, so a half-pixel here is a number that
     looks like a decision and paints like nothing. It would also halve every
     divider on the day some future Chrome starts honouring it, with nobody
     deciding that.

     The point of the token is that the next person changes ONE number, not 141.
     Guard: tests/test_css_architecture.py::HairlineTokenTest. */
  --hairline:    1px;
  /* Day — WARM PAPER, 2026-08-10. Ritu's pick from the styling preview, and DAY
     ONLY: "warm is messing up Night and Press". Night keeps its near-black
     ground and Press keeps #F4EFE5, which is already warmer than this, so
     overriding Press would have cooled the one theme that was already warm.
     This moves Day towards Press rather than away from it.
     The three move together. Changing only --paper leaves every panel and
     inset box cool grey on a warm page. Prior values, teal-biased grey from the
     de-tell of 2026-07-14: #E9EDED / #E4E9E9 / #F1F4F4. */
  --paper:       #F2F0EA;
  --paper-deep:  #EBE8E0;
  --paper-warm:  #F7F5F0;
  --ink:         #1A1717;
  --ink-soft:    #2F2A26;
  --quiet:       #6B6259;
  /* Finding 25. These were warm beige inherited from the retired cream palette,
     sitting on a cool grey-green ground — the only warm elements left on the
     page — and near-invisible with it: --rule measured 1.49:1 and --rule-soft
     1.16:1. This sheet's own thesis is that rule lines do the structural work,
     so they have to be visible enough to do it. Re-derived on the page's own
     cool axis and lifted: --rule 2.96:1, --rule-soft 1.52:1, which keeps the
     hairline subordinate to the structural line. Closes the divergence logged
     at DESIGN-SYSTEM.md:16. */
  --rule:        #868A8A;
  --rule-soft:   #BEC2C2;
  --accent:      #156169;   /* brand teal — from the wordmark */
  --accent-deep: #082A4C;   /* brand navy — from the wordmark */
  /* Pencil amber, editor's annotations. Converged 2026-07-27 onto the canonical
     value in editorial-frame.css:35. This sheet carried #B8801C, so --mark named
     two different colours across the system (design review, finding 23). The
     canonical value is also the legible one: it read 4.82:1 on --paper against
     2.90:1, which was below AA for the annotation text it colours.

     DEEPENED 2026-08-03 to 6.03:1, with --accent, --good and --warn. Ritu's
     catch: "even the colour looks pale on that", on a 14in office laptop. All
     four sat ON the 4.5 AA floor (warn was 4.51), and a floor means legible,
     not intentional: a P3 MacBook flatters them where a dim sRGB panel does
     not. Lightness only, hue and saturation held, so nothing changed colour. */
  --mark:        #76510F;
  --good:        #465F36;
  --warn:        #8B4517;

  --font-display: "Schibsted Grotesk", system-ui, -apple-system, sans-serif;
  --font-body:    "Newsreader", "Iowan Old Style", Georgia, serif;
  --font-mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Side margin, proportional. See the long note in editorial-frame.css: the old
     clamp capped at 45px, so below 1386px the margin stopped growing and a 14in
     office laptop at 1280 got 44px and looked cramped where a MacBook at 1512
     got 100px. Ritu, 2026-08-03: "it should be ideally aesthetic on all kinds of
     laptops." Above the frame's cap nothing moves. */
  --gutter: clamp(1.5rem, 5vw, 5rem);
  /* One frame for the whole product (spec 2026-08-03 §5A). Ritu's ruling: the
     front door is the standard, so this is the front door's column, and every
     masthead inset and every primary content column derives from it. Declared
     identically in editorial-frame.css and social-dark.css because no stylesheet
     is loaded by every page; tests/frame-token.test.js keeps the three in step.
     The root font is 18px, so 72rem is 1296px, not 1152px. */
  --frame:  min(72rem, 100% - var(--gutter) * 2);
  /* Prose cap inside the wide frame. Deliberately NOT --measure, which is 62ch
     and caps the rewrite output column in editorial-frame.css. See the note
     there. */
  --prose:  min(38rem, 100%);
  --col:    min(58rem, 100% - var(--gutter) * 2);
  --rhythm: 1.65;

  --pill:   999px;          /* capsule radius — boxed buttons (2026-07-12) */
  --box:    16px;           /* input + output boxes (Ritu, 2026-08-07) */

  /* The press, 2026-08-17. Corporate's Compose, door tabs and fact bar read
     these from here, because every Corporate page loads this sheet before
     corporate.css. The values and the reasoning behind them live in
     editorial-frame.css; this copy exists because a sheet that uses a token
     must be able to see it, and the two are held identical by
     tests/test_css_architecture.py::SharedPressTest. */
  --press-deep:  0.955;
  --press-light: 0.98;
  --press-in:    85ms;
  --press-out:   200ms;
  --press-ease:  cubic-bezier(0.23, 1, 0.32, 1);
  /* Finding 21. This token exists in editorial-frame.css, social-dark.css and
     byline-kit.css but was missing here, so nothing on the home pages had a
     touch floor: .mast-nav-link measured 26px, .band-link 19px, and 31 of 39
     targets on the front door were under 44. */
  --touch-min: 44px;
  /* Ritu's ruling, 2026-07-28. Finding 21 applied --touch-min to every target
     including inline and footer text links, and on a phone that is what made the
     front door's footer 1,444px tall — 17 text links each holding open a 44px
     box. 44px is the AAA target size (2.5.5) and it is right for CONTROLS:
     buttons, pills, dropdowns, toggles. Inline links in prose and in footer
     lists take the AA floor (2.5.8), which is 24px and which exempts inline
     links in a sentence outright. Two floors, named, so neither is a guess. */
  --touch-link: 24px;
}
/* The press stays under reduced motion and stops travelling; it does not go
   away. Same ruling as editorial-frame.css, said here because Corporate never
   loads that sheet. */
@media (prefers-reduced-motion: reduce) {
  :root { --press-in: 0s; --press-out: 0s; }
}

* { box-sizing: border-box; }

/* ------------------------------------------------------------------
   FOCUS — finding 4.

   Six rules in this sheet paired :hover with :focus-visible on one selector and
   set outline:0, so the keyboard focus style WAS the hover style: a background
   change measuring 1.04:1 against a 3:1 requirement. A keyboard user could not
   see where they were.

   The pairing stays, because focus should show the same affordance as hover.
   What it must not do is suppress the ring. outline-offset is negative so the
   ring sits inside the element and cannot be clipped by the overflow-x:hidden
   on body. --accent-deep on --paper measures 11.4:1.
   ------------------------------------------------------------------ */
:focus-visible {
  outline: 2px solid var(--accent-deep);
  outline-offset: -2px;
}

/* Every page's first tab stop. Off-screen until focused. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent-deep);
  color: var(--paper);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-2);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  text-decoration: none;
  padding: var(--s-3) var(--s-3);
  border-radius: var(--pill);
}
.skip-link:focus {
  left: var(--gutter);
  top: 0.6rem;
}

/* The scrollbar is part of the frame — see the note in editorial-frame.css.
   A short page has no scrollbar, so its viewport is ~15px wider, so a
   viewport-derived masthead inset puts the wordmark 7.5px off every page that
   does scroll. Ritu ruled 2026-08-03 that it holds the space everywhere. */
html { scrollbar-gutter: stable; }

html, body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;   /* never scroll sideways — a space-between masthead on
                           width:100% would otherwise push its right cluster
                           off-screen if any child overflows (parity w/ tools) */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: clamp(16px, 0.6vw + 14px, 18px);
  line-height: var(--rhythm);
  font-feature-settings: "ss01", "onum";
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Subtle paper texture — barely there, signals craft */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background-image:
    radial-gradient(rgba(26,23,23,0.018) 1px, transparent 1px),
    radial-gradient(rgba(26,23,23,0.012) 1px, transparent 1px);
  background-size: 7px 7px, 13px 13px;
  background-position: 0 0, 3px 4px;
  pointer-events: none;
  z-index: -1;
}

/* ============================================================
   Masthead
   ============================================================ */

/* align-items was `baseline` until 2026-07-28. A baseline row only works when
   every item HAS a comparable baseline. The wordmark is an image (its baseline
   is its bottom edge) and the Teach pill is a flex box whose baseline is the
   text sitting inside 44px of padding, so the two never lined up. Measured on
   editorial.html: at 390px the wordmark's centre sat at 42.5px and the pill's at
   57.4px, 14.9px out and plainly visible once the suite descriptor hides at
   640px; at 1440px the whole right-hand group hung 20.5px low. Centring is the
   correct axis for a row mixing an image, a pill and a line of text. */
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-3);
  /* Full-bleed chrome, contents on the product frame. The 2026-07-31 ruling was
     that every landing places the wordmark the front door's way; this rule used
     --col (58rem) while the front door used --fd-col (72rem), so the code
     contradicted its own comment and this was the worst-drifting surface.
     Fixed 2026-08-03 by deriving from the shared --frame.
     The calc collapses to exactly --gutter below the cap. */
  padding: 1.5rem calc((100% - var(--frame)) / 2) 1rem;
  border-bottom: 1px solid var(--rule);
  width: 100%;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  line-height: 1;
}

.wordmark img {
  /* Optical alignment, 2026-07-28. The nib artwork carries ~1px of internal
     padding at phone scale, so although the image BOX starts on the gutter
     like everything else, the mark's first ink landed 1px inside it. Measured
     off the rendered pixels on DeskCopyPro at 360px: nib 21.0px, the theme
     capsule / eyebrow / h1 all 20.0px, and the delta held at every sampling
     threshold from 40 down to 2, so it is the artwork and not anti-aliasing.
     Ritu: "the wordmark logo sticks out a tad". Corrected in CSS; the SVG is
     a fixed asset and is never edited. */
  margin-left: -1px;
  display: block;
  height: clamp(36px, 4vw, 48px);
  width: auto;
}

.suite-mark {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--quiet);
  white-space: nowrap;
}

/* The painted pill tracks the wordmark instead of the touch floor.

   Yesterday's review lifted this from 26px to a 44px min-height for finding 21,
   which was right for the finger and wrong for the eye: a solid navy 44px pill
   next to a 36px wordmark made the nav link the heaviest object in the masthead
   on a phone. Ritu, 2026-07-28: "the button looks too large on a small device".

   So the two jobs are separated. `height` paints a pill slightly shorter than
   the wordmark at every width (32px against 36px on a phone, 44px against 48px
   on a desktop), which keeps the brand mark dominant. The ::after below holds
   the tap target at the full 44px floor regardless. Ritu's ruling of the same
   day stands: 44px for real controls, and this is one. */
.mast-nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: clamp(32px, 3.2vw, 44px);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--paper-warm);
  background: var(--accent-deep);
  /* Filled navy, so the Teach mortarboard follows this button's own label. In
     the accent it measured 2.04:1 on this ground (2026-08-05). */
  --ico-color: currentColor;
  text-decoration: none;
  padding: 0 var(--s-3);
  border-radius: var(--pill);
  line-height: calc(var(--s-1) * 3);
  transition: background .15s ease;
}
/* Tap target only — never painted. Keeps the 44px floor while the pill above
   is allowed to be smaller. Nothing sits above or below it in the masthead row,
   so the overhang cannot swallow a neighbouring control. */
.mast-nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: var(--touch-min);
}
.mast-nav-link:hover { background: var(--accent); color: var(--paper-warm); --ico-color:currentColor; }
.mast-nav-link.here {
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}

/* ============================================================
   Page container
   ============================================================ */

.page {
  /* On the product frame, not this sheet's --col (spec §5A). The masthead moved
     to --frame on 2026-08-03; leaving the body on --col left the wordmark 127px
     left of the page's own headline at 1920. Prose inside is capped by --prose,
     not by narrowing this container. */
  width: var(--frame);
  margin: 0 auto;
  padding: clamp(2rem, 6vw, 4rem) 0 var(--s-7);
}

/* ============================================================
   Display hierarchy
   ============================================================ */

.display-eyebrow {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-4);
  font-weight: 500;
}

/* ------------------------------------------------------------------
   THE HEADING RAMP — one continuous curve per element, no breakpoints.

   This used to be three clamp tiers per element cut at 900px and 600px.
   Each tier carried its own floor and ceiling, so the tiers never met and
   the type jumped at both seams. Measured on editorial.html 2026-07-27:

     901 -> 900 px   h1.display  44.20 -> 32.40   (-27%)
     601 -> 600 px   h1.display  28.17 -> 35.20   (+25%, GREW as the page narrowed)
                     .tool-name  23.77 -> 27.28
                     h2          21.13 -> 21.60

   Design review finding 15 caught the 600px seam; the 900px one is the same
   defect in the other direction. A single clamp cannot have a seam, so the
   whole class is gone rather than patched at two points.

   Each ramp is solved to hit its intended size at 320px and at 1440px, taking
   into account that --root-size is itself fluid (16px -> 18px), so a rem here
   is not a constant. Verify by measuring across widths, not by reading.

     element            320px    1440px
     h1.display         24.96    70.20
     h2.section-title   20.00    45.00
     .tool-card .tool-name 19.20  37.44

   Hierarchy h1 > h2 > tool-name now holds at EVERY width, which the tiers were
   introduced to achieve and did not (finding 16: h2 was smaller than the
   .tool-name beneath it at 983 of 1121 widths).
   ------------------------------------------------------------------ */

h1.display {
  font-family: var(--font-display);
  font-variation-settings: "wght" 780;
  font-size: clamp(var(--t-5), 3.9vw + 0.78rem, var(--t-9));
  line-height: 1.05;
  letter-spacing: var(--track-hero);
  margin: 0 0 var(--s-4);
  max-width: 26em;
  color: var(--ink);
  text-wrap: balance;                               /* even 2-line breaks (suite-wide hero law) */
}
/* Teal accent words in a hero never split across a line break. */
h1.display .accent,
h1.display .tw { white-space: nowrap; }

h2.section-title {
  font-family: var(--font-display);
  /* Heavier than .tool-name (520), which it outranks. Until the variable font
     was actually loading, 560 and 600 both painted at body weight and an h2
     and its child h3 were indistinguishable. */
  font-variation-settings: "wght" 620;
  font-size: var(--t-7);
  line-height: calc(var(--s-1) * 12);
  letter-spacing: var(--track-tight);
  margin: var(--s-6) 0 var(--s-2);
  color: var(--ink);
}

.section-rule {
  border: 0;
  border-top: 1px solid var(--ink);
  width: 3.5rem;
  margin: 0 0 var(--s-4);
  opacity: 1;
}

.lede {
  font-family: var(--font-body);
  font-variation-settings: "opsz" 18, "wght" 400;
  font-size: var(--t-5);
  line-height: calc(var(--s-1) * 9);
  color: var(--ink-soft);
  max-width: 36em;
  margin: 0 0 var(--s-5);
}

p { margin: 0 0 1em; }

/* ============================================================
   Mono label
   ============================================================ */

.mono-label {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--quiet);
  display: block;
  margin: 0 0 var(--s-2);
  font-weight: 500;
}

/* ============================================================
   Tool cards — landing
   ============================================================ */

/* Finding 21. The column count used to be IMPLICIT: `auto-fit` with a 22rem
   track floor worked it out from whatever width the container happened to be,
   and the border and padding rules below hard-coded the answer as "never more
   than two". On 2026-08-03 `.page` moved from --col (58rem) to --frame (72rem)
   so the wordmark would line up with the page headline; a third track started
   fitting; and every "every second card" rule landed on an arbitrary card. On
   live prod that read as: no rule between DeskCopy Pro and DeskPop, no rule
   between IntroDoctor and DeskTriage, a right-hand edge on row 1 that row 2
   did not have, and row 2's left card indented while row 1's sat on the spine.
   Ritu saw it and asked whether it was a style norm. It was not.

   The defect is not the number 2. It is that the number was implicit, so a
   change to an unrelated container could move it without anything saying so.
   The count is DECLARED now, one band at a time, and the column maths lives
   inside the same band as the number it depends on, so the two cannot drift
   apart again. See the three bands below. */
.tool-grid {
  --cols: 3;
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: 0;
  margin-top: var(--s-5);
  border-top: var(--hairline) solid var(--rule-soft);
  position: relative;
}

/* The closing rule, drawn by the grid rather than by whatever cards happen to
   sit in the last row. Every card carries a rule beneath it, which closed the
   ledger full-width for as long as the last row was full — 8 tools over two
   columns. At three it is 8 over 3, so the last row holds two cards and the
   closing rule stopped two thirds of the way across; Corporate's "On the
   bench" holds two of three and did the same. This sits at the grid's bottom
   edge, which is exactly where the last row's own borders sit, so on a full
   last row the two coincide on the same pixel and nothing doubles. */
.tool-grid::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  border-bottom: var(--hairline) solid var(--rule-soft);
  pointer-events: none;
}

.tool-card {
  display: block;
  text-decoration: none;
  color: var(--ink);
  /* Finding 20. The left padding was 0.5rem, reserving room for the hover
     slide, which put every card's content 9px right of the h1, lede, rules and
     footer at every width from 320 to 1440. The card broke the page's spine to
     make room for an animation. The indent is a transform now.

     But "flush to the page spine" and "clear of the vertical rule" are two
     different requirements, and zeroing this for every card collapsed them:
     the right-hand column's text ended up touching the rule between the
     columns, visible only at rest because the hover transform pushed it clear.
     The left column is flush (rule below); every other column keeps a real
     gutter, symmetric with the 2rem on the right. */
  padding: var(--s-5) var(--s-4) var(--s-5) var(--s-4);
  border-bottom: var(--hairline) solid var(--rule-soft);
  /* Findings 14 and 21. The verticals are drawn on the LEFT of each card and
     suppressed on the first column. A right-hand border cannot express this
     grid, and no amount of correcting the nth-child maths would have fixed it:
     the last row is almost never full (5 tools on Corporate, 8 on Editorial,
     7 on live prod, all against 3 columns), and a right border makes the final
     card of a short row draw a rule into empty space — plus it puts an outer
     edge down the right of every full row, which this design does not have.
     Drawn on the left, a rule appears ONLY between two cards, at any column
     count, full row or short. */
  border-left: var(--hairline) solid var(--rule-soft);
  position: relative;
  transition: background 180ms ease;
  min-height: 16rem;
}

/* The three bands. Each one declares its column count AND the first-column
   maths that depends on it, and they are mutually exclusive, so no band has to
   undo another's work.

   The first column carries no left rule (nothing to separate it from) and no
   left padding, so its first line sits flush against the page spine, aligned
   with the h1, the lede, the section rules and the footer. Every other column
   keeps a real 2rem gutter, symmetric with the 2rem on its right.

   895 and 1335 are the widths at which `auto-fit` itself broke to two and to
   three columns, BISECTED on the rendered page, so the column count does not
   move at any width between 320 and 1920 — only the rules and the alignment
   change. They are not the derived numbers, and the difference is the whole
   lesson: `html, body` carries `font-size: clamp(16px, 0.6vw + 14px, 18px)`,
   so a rem here is 18px, not 16px, and the 22rem track floor is 396px. Derived
   off 16px the answers come out 782 and 1174, which are wrong by 113px and
   161px and would have silently given three columns where prod shows two.
   The measurement confirms itself: the grid is exactly 792px (2 x 396) at 895
   and exactly 1188px (3 x 396) at 1335.

   If --frame, --gutter, the root font-size or the 22rem intent changes,
   RE-BISECT these two numbers. Do not recompute them. Computing instead of
   measuring is how the old 640px got here, and then the 867px that replaced
   it, and then this. */
@media (max-width: 894px) {
  .tool-grid { --cols: 1; }
  .tool-card { border-left: 0; padding-left: 0; }
}

@media (min-width: 895px) and (max-width: 1334px) {
  .tool-grid { --cols: 2; }
  .tool-card:nth-child(2n + 1) { border-left: 0; padding-left: 0; }
}

@media (min-width: 1335px) {
  .tool-grid { --cols: 3; }
  .tool-card:nth-child(3n + 1) { border-left: 0; padding-left: 0; }
}

/* Finding 19. The hover slide animated padding-left, forcing a reflow on every
   frame; because the blurb is capped at 24em it re-wrapped under the cursor
   mid-gesture. Two unsynchronised clocks ran in one gesture: 16px over 320ms
   for the text and 8px over 280ms for the arrow. Now one transform on the
   card's children, one duration, one curve. Transform does not reflow, so the
   text cannot re-wrap while it moves. */
.tool-card > * {
  transition: transform 180ms cubic-bezier(0.22, 0.8, 0.2, 1);
}

.tool-card:hover,
.tool-card:focus-visible {
  background: var(--paper-deep);
}

.tool-card:hover > *,
.tool-card:focus-visible > * {
  transform: translateX(1.5rem);
}

.tool-card .tool-eyebrow {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  /* 0.68rem = 11.10px at 390, under the 0.70rem floor the landing pages were
     lifted to. Missed by the sweep that set the floor. 2026-07-28. */
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-3);
  font-weight: 500;
}

.tool-card .tool-name {
  font-family: var(--font-display);
  /* Lighter than h2.section-title (620), which outranks it. This was 600
     against the section heading's 560 — the card title was both larger AND
     bolder than the heading it sat under. */
  font-variation-settings: "wght" 520;
  font-size: var(--t-7);
  line-height: calc(var(--s-1) * 11);
  letter-spacing: var(--track-tight);
  margin: 0 0 var(--s-2);
  color: var(--ink);
}

.tool-card .tool-blurb {
  font-family: var(--font-body);
  font-size: var(--t-4);
  color: var(--ink-soft);
  margin: 0 0 var(--s-4);
  max-width: 24em;
  line-height: calc(var(--s-1) * 7);
  /* Ritu, 2026-08-05: "each line juts out longer and suddenly the last line
     looks amputated". Measured at 1512 before this: DeskCopy Pro ran
     271/322/339/353 and then a 49px last line, one word alone. DeskPop 43,
     FieldReportage 40, HeadlineBench 57: the same widow on four of eight cards.
     Greedy wrapping fills each line to the edge and leaves whatever is left.
     text-wrap: balance evens the lines across the block instead, which is what
     it exists for on short passages like these. Browsers that do not support
     it ignore it and wrap as before, so it costs nothing. */
  text-wrap: balance;
}

.tool-card .tool-meta {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  color: var(--quiet);
  letter-spacing: normal;
  margin: 0;
}

.tool-card .tool-arrow {
  position: absolute;
  right: 1.5rem;
  top: 2.5rem;
  font-family: var(--font-display);
  font-variation-settings: "wght" 400;
  font-size: var(--t-6);
  color: var(--ink);
  transition: transform 280ms cubic-bezier(0.22, 0.8, 0.2, 1), color 220ms ease;
  line-height: calc(var(--s-1) * 7);
}

.tool-card:hover .tool-arrow,
.tool-card:focus-visible .tool-arrow {
  transform: translateX(8px);
  color: var(--accent);
}
/* The arrow is a child of .tool-card, so it already carries the 180ms transform
   above; this keeps its colour on the same clock rather than a second one. */
.tool-card .tool-arrow { transition: transform 180ms cubic-bezier(0.22, 0.8, 0.2, 1), color 180ms ease; }

/* Finding 10. A blanket opacity:.5 dragged the eyebrow to 2.05:1, the blurb to
   2.87:1 and the status line to 1.64:1 — so the least legible text on the page
   was the text carrying the state. Unavailability is now stated explicitly, at
   full opacity, in the colour the system already has for subordinate text. The
   card reads as unavailable because it says so, not because it is faded.
   cursor:not-allowed also implied a control that would work if you were
   permitted; these are not controls. */
.tool-card.is-coming {
  cursor: default;
}

.tool-card.is-coming .tool-name,
.tool-card.is-coming .tool-blurb {
  color: var(--quiet);
}

/* The state line is the one thing on an unavailable card that must stay fully
   legible — it is what tells you why. Under the blanket opacity it measured
   1.64:1, the least legible text on the page. */
.tool-card.is-coming .tool-meta {
  color: var(--ink-soft);
}

.tool-card.is-coming:hover {
  padding-left: var(--s-2);
  background: transparent;
}

.tool-card.is-coming .tool-meta {
  color: var(--mark);
}

/* ============================================================
   Selectors row
   ============================================================ */

.selectors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: 0;
  border-top: var(--hairline) solid var(--rule-soft);
  border-bottom: var(--hairline) solid var(--rule-soft);
  margin: var(--s-5) 0 var(--s-5);
}

.selector {
  padding: var(--s-3) var(--s-3);
  border-right: var(--hairline) solid var(--rule-soft);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.selector:last-child { border-right: 0; }

.selector label {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--quiet);
  font-weight: 500;
}

.selector select {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--quiet) 50%),
    linear-gradient(135deg, var(--quiet) 50%, transparent 50%);
  background-position:
    calc(100% - 12px) calc(50% + 1px),
    calc(100% - 7px) calc(50% + 1px);
  background-size: 5px 5px;
  background-repeat: no-repeat;
  border: 0;
  border-bottom: 1px solid var(--rule-soft);
  font-family: var(--font-body);
  font-variation-settings: "opsz" 16, "wght" 500;
  font-size: var(--t-4);
  color: var(--ink);
  padding: var(--s-1) var(--s-4) var(--s-2) 0;
  cursor: pointer;
  transition: border-color 220ms ease;
}

.selector select:hover,
.selector select:focus {
  border-bottom-color: var(--accent);
}

/* ============================================================
   Source paste area
   ============================================================ */

.source-block { margin: var(--s-5) 0 var(--s-4); }

.source-block textarea {
  width: 100%;
  min-height: 14rem;
  background: var(--paper-deep);
  border: 1px solid var(--rule);
  border-radius: var(--box);
  font-family: var(--font-body);
  font-variation-settings: "opsz" 16, "wght" 400;
  font-size: var(--t-4);
  line-height: calc(var(--s-1) * 7);
  color: var(--ink);
  padding: var(--s-3) var(--s-3);
  resize: vertical;
  transition: border-color 240ms ease, background 240ms ease;
}

.source-block textarea::placeholder {
  color: var(--quiet);
  font-style: italic;
  font-variation-settings: "opsz" 16, "wght" 400, "ital" 1;
}

/* The resize grip's two hairlines, removed on Ritu's 2026-08-07 ruling. Paint
   only: the drag stays. Mirrors editorial-frame.css, where the reasoning is. */
textarea::-webkit-resizer { display: none; }

.source-block textarea:focus {
  border-color: var(--ink);
  background: var(--paper-warm);
}

/* ============================================================
   Compose action — typeset, not a button
   ============================================================ */

.action-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: var(--s-4) 0 var(--s-5);
  gap: var(--s-4);
  flex-wrap: wrap;
}

.compose {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ink);
  font-family: var(--font-display);
  font-variation-settings: "wght" 500;
  font-size: var(--t-5);
  line-height: calc(var(--s-1) * 6);
  color: var(--ink);
  padding: 0 0 var(--s-2);
  cursor: pointer;
  letter-spacing: var(--track-tight);
  transition: color 240ms ease, border-color 240ms ease;
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
}

.compose:hover:not(:disabled),
.compose:focus-visible:not(:disabled) {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.compose:disabled {
  color: var(--quiet);
  border-bottom-color: var(--rule);
  cursor: not-allowed;
}

/* The compose button's trailing arrow span and its 280ms hover slide are
   CULLED (2026-08-05). Ritu's ruling: every primary action carries a mark,
   and one
   mark per button. `.compose` now leads with <span class="ico" data-icon="run">,
   the same mark the editorial `.run-btn` took when its own ::after arrow went,
   so the two suites' primary actions finally read as one control. The slide
   went with the arrow rather than transferring to the mark: it existed because
   a TRAILING arrow points out of the button, and a LEADING mark sliding toward
   its own label reads as a wobble, not as forward motion.
   Guard: tests/test_css_architecture.py::ComposeGlyphStaysCulled. */
.compose .ico { flex: none; }

.action-meta {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--quiet);
}

/* ============================================================
   Status bar
   ============================================================ */

.status-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 0.8rem var(--gutter);
  font-family: var(--font-mono);
  font-size: var(--t-2);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  transform: translateY(100%);
  transition: transform 320ms cubic-bezier(0.22, 0.8, 0.2, 1);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

.status-bar.visible { transform: translateY(0); }

.status-bar::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--mark);
  border-radius: 50%;
  animation: pulse 1.6s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 0.35; transform: scale(0.8); }
  50%      { opacity: 1;    transform: scale(1.1); }
}

/* ============================================================
   Output — the rendered brief / statement
   ============================================================ */

.output {
  margin-top: var(--s-5);
  border-top: var(--hairline) solid var(--rule-soft);
  padding-top: var(--s-5);
  position: relative;
  min-height: 0;
}

.output:empty { display: none; }

.output[data-draft] { opacity: 0.72; }

.output .out-eyebrow {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--quiet);
  margin: 0 0 var(--s-5);
  font-weight: 500;
}

.output .out-section {
  margin: 0 0 var(--s-5);
  position: relative;
}

.output .out-section-label {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-2);
  display: block;
  font-weight: 500;
}

.output .out-section-body {
  font-family: var(--font-body);
  font-variation-settings: "opsz" 18, "wght" 400;
  font-size: var(--t-4);
  line-height: calc(var(--s-1) * 8);
  color: var(--ink);
  font-feature-settings: "onum", "tnum";
}

.output .out-section-body p { margin: 0 0 0.85em; }
.output .out-section-body p:last-child { margin-bottom: 0; }

.output .out-section-body strong { font-weight: 600; }
.output .out-section-body em {
  font-style: italic;
  font-variation-settings: "opsz" 18, "wght" 400, "ital" 1;
}

/* Bottom line section — display treatment with drop cap */
.output .out-section.kind-bottomline .out-section-body {
  font-family: var(--font-display);
  font-variation-settings: "wght" 460;
  font-size: var(--t-6);
  line-height: calc(var(--s-1) * 9);
  letter-spacing: var(--track-tight);
  color: var(--ink);
}

.output .out-section.kind-bottomline .out-section-body p:first-child::first-letter {
  font-family: var(--font-display);
  font-variation-settings: "wght" 600;
  float: left;
  font-size: 3.6em;
  line-height: 0.86;
  margin: 0.06em 0.14em -0.08em 0;
  color: var(--accent);
}

/* Statement output — headline + dateline + lead get display treatment */
.output .out-section.kind-headline .out-section-body {
  font-family: var(--font-display);
  font-variation-settings: "wght" 560;
  font-size: var(--t-7);
  line-height: calc(var(--s-1) * 11);
  letter-spacing: var(--track-tight);
}

.output .out-section.kind-dateline .out-section-body {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-3);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-soft);
}

.output .out-section.kind-lead .out-section-body p:first-child::first-letter {
  font-family: var(--font-display);
  font-variation-settings: "wght" 600;
  float: left;
  font-size: 3.4em;
  line-height: 0.86;
  margin: 0.06em 0.14em -0.08em 0;
  color: var(--accent);
}

.output .out-section.kind-quote .out-section-body {
  font-family: var(--font-body);
  font-variation-settings: "opsz" 20, "wght" 400, "ital" 0;
  font-size: var(--t-4);
  line-height: calc(var(--s-1) * 7);
  padding-left: var(--s-3);
  border-left: var(--hairline) solid var(--rule-soft);
  color: var(--ink-soft);
}

.output .out-section.kind-wire-line .out-section-body {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--mark);
  font-weight: 500;
}

.output .out-section.kind-boilerplate .out-section-body,
.output .out-section.kind-disclosure .out-section-body,
.output .out-section.kind-contact .out-section-body {
  font-size: var(--t-3);
  color: var(--quiet);
}

/* ============================================================
   Output actions
   ============================================================ */

/* Since 2026-08-05 this bar sits ABOVE the output, not below it (Ritu: "COPY
   AND EXPORT.TXT BUTTONS SHOULD BE RIGHT ABOVE THE OUTPUT BOX"). The rule
   stays on top, which is the same shape the editorial .action-bar has in the
   same position: the line closes off the composer above, and the buttons then
   sit tight against the output they act on, so they read as belonging to it. */
.output-actions {
  display: flex;
  gap: var(--s-4);
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: var(--hairline) solid var(--rule-soft);
}

/* Copy and Export are CONTROLS, so they take the 44px floor, not the 24px one.
   This rule was a hairline-underline text action with no min-height, measured
   41x25 and 111x25 on Client Letters (2026-08-02) — under the floor Ritu set on
   2026-07-28, and quiet enough that he could not find them on his own tool:
   "if I, as the builder, can't spot them easily, then how can a user do so?"
   Now the house outline pill, the same treatment .fact-bar button and the
   reportage tools' copy buttons already wear. */
.output-actions button {
  appearance: none;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--pill);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-2);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink);
  padding: var(--s-2) var(--s-3);
  min-height: var(--touch-min);
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  transition: color 220ms ease, border-color 220ms ease;
  font-weight: 500;
}

.output-actions button:hover:not(:disabled),
.output-actions button:focus-visible:not(:disabled) {
  color: var(--accent);
  border-color: var(--accent);
}

.output-actions button:disabled {
  color: var(--quiet);
  cursor: not-allowed;
  opacity: 0.4;
}

/* ============================================================
   Footer note
   ============================================================ */

.foot {
  margin-top: var(--s-7);
  padding-top: var(--s-4);
  border-top: var(--hairline) solid var(--rule-soft);
  font-family: var(--font-body);
  font-size: var(--t-3);
  color: var(--quiet);
  display: flex;
  flex-direction: row;              /* copy left, powered-by right (matches the front door) */
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}

/* Ritu, 2026-09-12, on his phone: "on the editorial landing page, the footer
   is all over the place", and "Footers should be on the right. The copyright
   mark ... should always be on the right."

   MEASURED at 390 before this: this footer is a direct child of <body>, not of
   the content wrapper, so it had NO side gutter at all. Its two items ran
   0..216 and 242..390, one starting on the screen's left edge and the other
   ending on its right edge, while every line of copy above them sits between
   25 and 365. That is the "all over the place".

   Below --phone-max the footer takes the page's own gutter and both lines end
   where the page's text ends, stacked right. The desktop row is untouched: it
   already reads copy left, powered-by right, which is the front door's own
   arrangement and is not what he was looking at. 699.98px rather than this
   sheet's own 640px, because this is the app's breakpoint and the tools he was
   comparing it against are cut there. */
@media (max-width: 699.98px) {
  /* Ritu, 2026-09-12: "the wordmark on the landing page and tools are too
     tiny", and "Do apps have such tiny identities?" The tools went to 30px in a
     54px bar; these bars are 76 to 92px and were still drawing the same 36px
     the tools used to, so the mark read smaller here than anywhere. MEASURED at
     360 from 36 to 50px: nothing overflows at any of them and the bar grows by
     at most 10px.

     40px, not 46: the app's tool bars cannot carry 46 without truncating the
     story name beside the mark, so the whole product uses the one number and
     these pages cannot drift above the tools they front. See the measurement
     table in editorial-frame.css. */
  .masthead .wordmark img, .masthead-brand .wordmark img { height: 40px; width: auto; }

  .foot {
    flex-direction: column;
    align-items: flex-end;
    text-align: right;
    padding-inline: calc((100% - var(--frame)) / 2);
    gap: var(--s-1);
  }
  .foot .copy { justify-content: flex-end; }
}

.foot .copy {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  flex-wrap: wrap;
  color: var(--quiet);
  font-size: var(--t-2);
}

.foot .copy img {
  height: 22px;
  width: auto;
  vertical-align: middle;
  position: relative;
  top: -1px;
}

.foot .powered {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  /* was 0.66rem = 10.78px at 390, under the 0.70rem floor. Same miss. */
  font-size: var(--t-1);
  font-style: italic;
  letter-spacing: normal;
  color: var(--quiet);
  opacity: 0.78;
  margin-left: auto;               /* push to the right, regardless of siblings */
}

.foot .back {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  margin-top: var(--s-2);
}

.foot a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: var(--hairline) dotted var(--quiet);
  transition: color 200ms ease, border-color 200ms ease;
}

.foot a:hover,
.foot a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ============================================================
   Responsive
   ============================================================ */

/* Finding 13. This reset used to fire at 640px while the grid actually collapses
   to one column at about 853px, so across that 211px band every non-last card
   drew a rule into empty space — a band that contains iPad portrait (768) and
   iPad Air (820).

   The collapse point falls out of the grid itself, and it has to be MEASURED,
   not derived. Deriving it from 2 x 22rem plus the gutter clamp gives 853px;
   the real point is 868px, and shipping the derived figure left a 15px band
   still drawing orphan rules. Computing this number instead of measuring it is
   how the original 640px got here in the first place. Bisected on
   editorial.html, 2026-07-27:

     867px -> one column        868px -> two columns

   Media queries cannot read a custom property, so this cannot literally share a
   token with the grid. If --col, --gutter or the 22rem track floor changes,
   re-bisect and move this number. Do not recompute it.

   2026-08-07: that warning came true and nobody acted on it. --col stopped
   governing the grid when `.page` moved to --frame on 2026-08-03, and 867 was
   never re-bisected; the collapse had moved out to 895, so across the 868-894
   band the cards ran ONE column while still being told to drop their rules and
   their left padding — harmless there by luck, but the number was stale by 28px
   and pointed at nothing. The tool cards no longer live here at all. Their
   collapse is declared with their column count, in the three bands beside
   `.tool-card` above, which is the only arrangement that keeps the two
   numbers honest. `.selector` and `.action-row` keep this breakpoint: they are
   flex/auto-fit rows with no card maths riding on them. */
@media (max-width: 867px) {
  .selector { border-right: 0; border-bottom: var(--hairline) solid var(--rule-soft); }
  .selector:last-child { border-bottom: 0; }
  .action-row { flex-direction: column; align-items: flex-start; gap: var(--s-3); }
}

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

.tool-name .accent { color: var(--accent); }
.display .accent { color: var(--accent); }

/* ============================================================
   Masthead right cluster — wordmark stands alone left; the Teach
   nav link + suite descriptor group on the right (not beside the mark).
   ============================================================ */
.mast-right {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}

/* Hide the suite descriptor on mobile — mirrors the tool masthead (descriptor
   hidden ≤760px). The nowrap "Editorial Intelligence" / "Social" string was
   shoving the Teach button off-screen on narrow phones. */
@media (max-width: 640px) {
  .masthead .suite-mark { display: none; }
}

/* ============================================================
   Suite band — a brief, non-card explanation + link for a destination
   that isn't a tool (the Teaching Room). Distinct from the tool cards:
   no box, no arrow grid; a quiet rule-topped strip in editorial voice.
   ============================================================ */
.suite-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-6);
  padding-top: var(--s-4);
  border-top: var(--hairline) solid var(--rule-soft);
}
.band-text { max-width: 46ch; }
.band-eyebrow {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 500;
  margin: 0 0 var(--s-2);
}
.band-line {
  font-family: var(--font-body);
  font-variation-settings: "opsz" 18, "wght" 400;
  font-size: var(--t-4);
  line-height: calc(var(--s-1) * 7);
  color: var(--ink-soft);
  margin: 0;
}
.band-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  font-weight: 500;
}
.band-link:hover { color: var(--accent-deep); }

@media (max-width: 640px) {
  .suite-band { flex-direction: column; align-items: flex-start; gap: var(--s-3); }
}

/* Mobile/tablet type-scale correction (placed after the base rules so it wins
   on source order). The hero is stepped down for phones in the ramp near the
   top; here .tool-name and .section-title step down WITH it so the hero stays
   the largest element and the hierarchy matches desktop. Desktop untouched. */
/* The 900px and 600px heading overrides that used to live here are gone. They
   were the other half of the seam documented at the heading ramp near the top
   of this file: each tier had its own floor and ceiling, so the type jumped at
   both breakpoints. The single continuous clamp on each element replaces all
   six declarations and cannot produce a seam. 2026-07-27, findings 15 and 16. */

/* ============================================================
   HOUSE-STYLED DROPDOWN MENUS — de-tell, 2026-07-31
   Same move as editorial-frame.css (see its block for the full note): the
   open menu of a native <select> is the browser's — system font, blue bar —
   and reads as the AI-page tell. `appearance: base-select` (Chromium 135+)
   hands it to this sheet; Safari/Firefox keep the system menu, unharmed.
   ============================================================ */
@supports (appearance: base-select) {
  /* .selector select pins `appearance: none` for the fallback path;
     the opt-in must win by weight. */
  select { appearance: base-select !important; }
  ::picker(select) { appearance: base-select; }
  select::picker-icon { display: none; } /* keep the sheet's own chevron */

  ::picker(select) {
    background: var(--paper);
    color: var(--ink);
    border: 1px solid var(--rule);
    border-radius: 0;
    /* No overlay token in this sheet; shadow derived from --ink #1A1717.
       Offset + soft blur — a floating panel, not a halo. */
    box-shadow: 0 10px 28px -12px rgba(26, 23, 23, 0.35);
    padding: var(--s-1) 0;
    scrollbar-width: thin;
    scrollbar-color: var(--rule) transparent;
    transform-origin: top;
    transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1),
                transform 150ms cubic-bezier(0.23, 1, 0.32, 1),
                display 150ms allow-discrete, overlay 150ms allow-discrete;
  }
  @starting-style {
    ::picker(select) { opacity: 0; transform: translateY(-3px) scale(0.98); }
  }

  select option {
    padding: var(--s-2) var(--s-3);
    cursor: pointer;
  }
  select option::checkmark { display: none; }
  select option:checked {
    text-decoration: underline;
    text-underline-offset: 0.22em;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--accent);
  }
  select option:focus-visible { outline: none; }
  select option:focus-visible,
  select option:active {
    background: var(--accent);
    color: var(--paper);
  }
  @media (hover: hover) and (pointer: fine) {
    select option:hover { background: var(--accent); color: var(--paper); }
  }
  @media (max-width: 520px) {
    select option { min-height: var(--touch-min); display: flex; align-items: center; }
  }
  @media (prefers-reduced-motion: reduce) {
    ::picker(select) { transition: opacity 120ms ease; }
    @starting-style {
      ::picker(select) { opacity: 0; transform: none; }
    }
  }
}


/* ============================================================
   HOUSE-STYLED CHECKS — same de-tell sweep as the dropdown block
   above (2026-07-31); full note in editorial-frame.css. Hairline
   box, accent fill, glyph in the page's own paper; --check-accent
   / --check-glyph let a page recolour a lane without falling back
   to the native control.
   ============================================================ */
input[type="checkbox"], input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px; height: 16px; flex: none;
  display: inline-grid; place-content: center;
  background: transparent;
  border: 1px solid var(--quiet);
  border-radius: 0;
  vertical-align: -3px;
  margin: 0;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 120ms ease-out;
}
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background-color: var(--check-accent, var(--accent));
  border-color: var(--check-accent, var(--accent));
}
input[type="checkbox"]::after {
  content: ""; width: 4px; height: 8px; margin-top: -2px;
  border-right: 2px solid var(--check-glyph, var(--paper));
  border-bottom: 2px solid var(--check-glyph, var(--paper));
  transform: rotate(45deg) scale(0.6);
  opacity: 0;
  transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), opacity 120ms ease-out;
}
input[type="checkbox"]:checked::after { transform: rotate(45deg) scale(1); opacity: 1; }
input[type="radio"]::after {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--check-glyph, var(--paper));
  transform: scale(0.6);
  opacity: 0;
  transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), opacity 120ms ease-out;
}
input[type="radio"]:checked::after { transform: scale(1); opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  input[type="checkbox"]:hover, input[type="radio"]:hover { border-color: var(--accent); }
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  input[type="checkbox"]::after, input[type="radio"]::after { transition: opacity 120ms ease; }
}

/* The `hidden` ATTRIBUTE outranks any display we set on a control.
   Ritu caught this live on 2026-08-02: CLEAR was sitting on screen at
   "DRAFT · 0 WORDS", and SPEAK was overlapping the draft box. Cause: the
   control pass gave these buttons `display: inline-flex`, and a class selector
   beats the UA stylesheet's `[hidden] { display: none }`. Every control that
   reveals itself by dropping the attribute — Clear, Speak, the output actions,
   the fact bar — was therefore always visible. Any rule that sets `display` on
   something toggled by `hidden` needs this backstop, so it lives at the sheet
   level rather than being remembered per rule. */
[hidden] { display: none !important; }


/* Per-band text sizes, appended by preview/build-precision.mjs
   --apply-type. The product's root font is a clamp, so one rem is a
   different number of pixels at each width and a single mapped value
   would be wrong everywhere except the width it was mapped at. These
   carry identical specificity to the rule above and come later, which is
   how the narrower band wins. */
@media (max-width: 620px) {
h2.section-title {
  font-size: var(--t-6);
}
}
@media (max-width: 620px) {
.lede {
  font-size: var(--t-4);
  /* The leading follows the type. Nine steps of the baseline grid was drawn for
     23px; against this 19px it reads 1.89, and a page of body copy set at 1.89
     is what he saw as a gap between sentences. Seven steps is 28px, 1.47.
     Ritu, 2026-09-13: "symmetry is the essence of this project." */
  line-height: calc(var(--s-1) * 7);
}
}
@media (max-width: 620px) {
.tool-card .tool-name {
  font-size: var(--t-5);
}
}
@media (max-width: 620px) {
.tool-card .tool-blurb {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.selector select {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.source-block textarea {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.compose {
  font-size: var(--t-4);
}
}
@media (max-width: 620px) {
.output .out-section.kind-bottomline .out-section-body {
  font-size: var(--t-5);
}
}
@media (max-width: 620px) {
.output .out-section.kind-headline .out-section-body {
  font-size: var(--t-6);
}
}
@media (max-width: 620px) {
.output .out-section.kind-dateline .out-section-body {
  font-size: var(--t-2);
}
}
@media (max-width: 620px) {
.output-actions button {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
.skip-link {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
h2.section-title {
  font-size: var(--t-5);
}
}
@media (max-width: 430px) {
.lede {
  font-size: var(--t-3);
  /* Six steps, 24px, 1.5 against this 16px. Same reason as the 620 band above:
     a font-size step in a media query carries its leading with it. */
  line-height: calc(var(--s-1) * 6);
}
}
@media (max-width: 430px) {
.tool-card .tool-name {
  font-size: var(--t-4);
}
}
@media (max-width: 430px) {
.status-bar {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
.output .out-section-body {
  font-size: var(--t-3);
}
}
@media (max-width: 430px) {
.output .out-section.kind-headline .out-section-body {
  font-size: var(--t-5);
}
}
@media (max-width: 430px) {
.foot .copy {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
.band-line {
  font-size: var(--t-3);
}
}

/* THE NARROWEST SCREEN, 2026-09-13. The same step editorial-frame.css carries,
   and it must be the same in all three root sheets or the wordmark's own inset
   drifts between surfaces: declared in that sheet alone, wordmark_frame_check
   reported the left edge 8px apart across surfaces at 320. The reasoning is
   written out once, at the foot of assets/css/editorial-frame.css.
   tests/frame-token.test.js keeps these three in step. */
@media (max-width: 349.98px) {
  :root { --gutter: 1rem; }
}

/* The account control's styles moved to assets/css/account.css on 2026-09-20,
   so that the two Social pages which mount the control but do not load this
   sheet get them too. See that file's header for the measurement. */

/* THE NAME IS NOT TRANSFORMED, ANYWHERE. Ritu, 2026-09-19, on the name itself:
   "Written bylineAL for a reason." MEASURED the same day: .suite-mark is
   uppercase with caps tracking, so all six help pages rendered the brand as
   BYLINEAL, which is the word this product tells readers it is NOT spelled as.
   help/about.html carried it in the masthead directly above the paragraph
   explaining the spelling. The tracking goes with the transform: caps tracking
   on mixed-case text opens gaps the letterforms were not drawn for. */
.brand { text-transform: none; letter-spacing: 0; }
