/* ============================================================================
   fde-grid.css — THE FDE EDITORIAL GRID
   A named-role breakout grid for for-employers.html. Additive, opt-in, inert
   until a class is used. Nothing here selects a bare element at the start of a
   rule, so linking this file changes zero pixels on any page.

   ── THE COMPLAINT THIS ANSWERS ────────────────────────────────────────────
   "Every chapter is the same centred single column — a stack of identical
   boxes." That uniformity is the machine-generated tell. The cure is NOT
   random per-section layout (that is the same disease with more entropy). The
   cure is a SYSTEM with a small number of named placements, where variance is
   a choice from a menu and the menu is visible in the source.

   ── THE SYSTEM, IN FOUR SENTENCES ─────────────────────────────────────────
   1. Twelve equal columns, zero column-gap, capped at 66rem, with a bleed
      track on each side.  (playbook L7: "if you use a horizontal grid, use 12")
   2. The trench between two side-by-side blocks is AN EMPTY COLUMN, not a gap.
      That is why there is no gap arithmetic anywhere in this file, and why the
      only overflow vector left is min-content — which is pinned at 0 below.
   3. A block picks ONE role. The role is its column span and its offset.
      Nothing is centred by default, so two chapters cannot accidentally match.
   4. Roles come in three families, and the family decides what may go inside:
        MARK      1–3 cols   labels, folios, numbers.  NEVER a sentence.
        MEASURE   4–7 cols   prose. Holds 45–75 characters by construction.
        ARTIFACT  8–12 + bleed   media, walls, benches, tables. Prose inside
                                 an ARTIFACT is capped by --fde-measure.

   ── THE COMPOSITION RULE (the one thing to remember) ──────────────────────
   Children are placed in DOM order, left to right. A block whose START column
   is left of the previous block's END column begins a new row. So:

       <p  class="fde-mark">02</p>          cols 1–3   ┐ same row: c4 <= c7
       <div class="fde-recto">…</div>       cols 7–12  ┘
       <div class="fde-span">…</div>        cols 1–12  → new row (1 < 13)

   This is CSS Grid's own sparse auto-placement. It means DOM order is always
   reading order — no `order`, no explicit grid-row, no tab-order damage.
   (playbook L12: DOM order = reading order.)

   ── THE MEASURE CONTRACT ──────────────────────────────────────────────────
   Measured in-browser against this page's real, loaded fonts — not estimated:

       face                            1ch (advance of "0")   avg char (English)
       Hanken Grotesk 17px (--t3 body)       9.517px               7.392px
       Cormorant Gar. it 24px (--t4 lede)   10.531px               8.340px
       JetBrains Mono 16px (--t2 mono)       9.600px              10.080px

   `ch` is the advance of "0", which in these proportional faces is ~29% WIDER
   than an average English character. So `max-width: 56ch` is NOT 56 characters:

       characters = ch_value x (advance_of_zero / avg_char_width)
       56ch = 533px -> 72.1 characters   (Hanken 17px)      <- browser-verified
       56ch = 590px -> 70.7 characters   (Cormorant it 24px)
       56ch = 538px -> 53.3 characters   (JetBrains Mono 16px)

   All three land inside Bringhurst's 45–75. That is why --fde-measure is 56ch
   and not 66ch — 66ch would be 85 characters of Hanken and would fail.

   TRAP, and the reason the numbers above are measured rather than derived:
   the `ch` UNIT excludes letter-spacing, but RENDERED text includes it. Body
   here carries -0.006em, so a "0" occupies 9.42px on screen while 1ch computes
   as 9.517px. Predicting the cap from a rendered measurement is off by ~5px.

   The role span holds the FLOOR (never below 45 characters); --fde-measure
   holds the CEILING (never above 75). Both are needed: a 12-column ARTIFACT
   role is 1056px = 143 characters of body text without the cap.

   ── THE ONE RULE THAT IS DOCUMENTATION, NOT CODE ──────────────────────────
   Monospace at --t2 averages 10.08px/char, so 45 characters needs 454px. A
   MARK role (264px) and the narrow end of MEASURE cannot supply it. Therefore:
   MONO-SET PROSE ONLY IN ROLES OF 6 COLUMNS OR MORE. CSS cannot enforce this —
   a min-width would cause horizontal overflow, which is worse. `.fde-debug`
   (bottom of file) flags the common violation visually.

   ── BREAKPOINTS ───────────────────────────────────────────────────────────
   1068 and 734 only. Both are already the page's (they are Apple's, and the
   --t6/--t5/--t4 ladder steps on exactly these). No new breakpoints invented.
     >1068   full 12-column composition, side-by-side pairs live
     <=1068  pairs unstack; offsets and left/right anchoring survive, so the
             page still does not read as six identical centred columns
     <=734   one column. Every role collapses. No half-broken middle state.

   ── WHAT THIS FILE DOES NOT DO ────────────────────────────────────────────
   Declares zero font-size. The type ladder (--t2/--t3/--t4/--t5/--t6 and
   --t-label) is closed and owned by the page; nothing here adds a rung.
   Declares zero color. Declares nothing on :root.
   ========================================================================== */


/* ── 1 · THE CONTAINER ─────────────────────────────────────────────────────
   Tokens live on .fde-grid, not :root, so this file cannot leak into a page
   that merely links it. They inherit to every descendant, including subgrids. */
.fde-grid{
  /* geometry */
  --fde-col:    5.5rem;                        /* 88px — one of twelve        */
  --fde-gutter: clamp(1.25rem, 5vw, 4rem);     /* 20px -> 64px                */
  --fde-content: calc(var(--fde-col) * 12);    /* 66rem / 1056px, informative */

  /* typography contract */
  --fde-measure: 56ch;                         /* = 71.4 chars of --t3 body   */
  --fde-bearing: 0.07em;                       /* measured left side bearing,
                                                  Hanken "n" @17px = 1.224px  */
  /* vertical rhythm — the page's own --sp scale, extended, each step >=1.5x
     (playbook L2: never a linear scale, no two steps closer than ~25%)       */
  --fde-row: clamp(1.5rem, 2.8vw, 2.75rem);

  display: grid;
  grid-template-columns:
    [bleed-start] minmax(var(--fde-gutter), 1fr)
    repeat(12, [c] minmax(0, var(--fde-col)))
    [c] minmax(var(--fde-gutter), 1fr) [bleed-end];
  column-gap: 0;                 /* the trench is a COLUMN, never a gap       */
  row-gap: var(--fde-row);
  align-content: start;
  align-items: start;            /* a companion block tops out with its pair  */
  grid-auto-columns: minmax(0, 1fr);   /* overflow guard: any accidental
                                          implicit column resolves to 0       */
}

/* min-inline-size:0 is the single most important line in this file. A grid
   track's default minimum is min-content, so one long token (a URL, a hash, a
   command) inside a 3-column role pushes the whole grid wider than the
   viewport. Pinning it to 0 makes horizontal overflow structurally impossible.

   The grid-column here is the DEFAULT ROLE: an unclassed child sits on all
   twelve and is held to measure by §3. It must be declared, not left to auto —
   auto-placement would drop an unclassed child into the gutter track. */
.fde-grid > *{
  grid-column: c 1 / c 13;
  min-inline-size: 0;
  max-inline-size: 100%;
  margin-block: 0;               /* the grid owns vertical rhythm; per-block
                                    exceptions come from .fde-lead-* below    */
}

/* Chapters ship with the framework's inline padding
   (chaptered-scroll.css: padding-inline: clamp(1.4rem,7vw,7rem)). That padding
   double-counts against --fde-gutter and stops .fde-bleed reaching the
   viewport edge. Put this on the SECTION, not the grid. Opt-in; no negative
   margins, no 100vw, so it cannot introduce a scrollbar. */
.fde-flush{ padding-inline: 0 }


/* ── 2 · THE ROLES ─────────────────────────────────────────────────────────
   Widths quoted at 1440px, where the 12 columns pin at 88px and the two 1fr
   gutters absorb the remaining 384px (192px each) — browser-verified as
   [192px | 12 x 88px | 192px].
   "chars" = real English characters of --t3 body (17px Hanken, 7.392px/char)
   after --fde-measure (56ch = 533px) has been applied. Every figure below was
   read off the live layout, not calculated.                                  */

/* MARK — folios, eyebrows, numbers, a 2–4 word stamp. 264px / 35.7 chars,
   which is BELOW the 45-character floor by design: this family is excluded
   from the measure contract because it carries marks, not sentences. */
.fde-grid > .fde-mark      { grid-column: c 1  / c 4  }   /* cols 1–3   264px */
.fde-grid > .fde-mark-end  { grid-column: c 10 / c 13 }   /* cols 10–12 264px */

/* MEASURE — prose. Six placements, no two alike, none centred. */
.fde-grid > .fde-lede      { grid-column: c 1  / c 8  }   /* 7 cols 616 -> 70.7 chars @--t4 */
.fde-grid > .fde-text      { grid-column: c 1  / c 7  }   /* 6 cols 528 -> 71.4 chars */
.fde-grid > .fde-inset     { grid-column: c 3  / c 9  }   /* 6 cols 528 -> 71.4, stepped in 2 */
.fde-grid > .fde-recto     { grid-column: c 7  / c 13 }   /* 6 cols 528 -> 71.4, flush right */
.fde-grid > .fde-column    { grid-column: c 1  / c 6  }   /* 5 cols 440 -> 59.5 chars */
.fde-grid > .fde-aside     { grid-column: c 8  / c 13 }   /* 5 cols 440 -> 59.5, companion */

/* ARTIFACT — media, walls, benches, tables, terminals. Never a prose spine.
   Any <p> inside is still held to 533px = 72.1 chars by the contract in §3 —
   this is the line that stops a full-bleed band producing a 195-char line. */
.fde-grid > .fde-back      { grid-column: c 1  / c 11 }   /* 10 cols  880 -> prose 533 / 72.1 */
.fde-grid > .fde-lean      { grid-column: c 3  / c 13 }   /* 10 cols  880 -> prose 533 / 72.1 */
.fde-grid > .fde-frame     { grid-column: c 2  / c 12 }   /* 10 cols  880 -> prose 533 / 72.1 */
.fde-grid > .fde-span      { grid-column: c 1  / c 13 }   /* 12 cols 1056 -> prose 533 / 72.1 */
.fde-grid > .fde-bleed     { grid-column: bleed-start / bleed-end }
                                                          /* 1440px     -> prose 533 / 72.1 */

/* The pairs that share a row at >1068px (start >= previous end):
     .fde-mark    + .fde-recto     3 | trench 3 | 6
     .fde-text    + .fde-aside     6 | trench 1 | 5
     .fde-column  + .fde-aside     5 | trench 2 | 5
     .fde-column  + .fde-mark-end  5 | trench 4 | 3
     .fde-lede    + .fde-mark-end  7 | trench 2 | 3
   Everything else stacks, which is the correct default. */


/* ── 3 · THE MEASURE CONTRACT ──────────────────────────────────────────────
   Belt AND braces. The role span sets the floor; this sets the ceiling, and it
   applies at ANY depth so a paragraph dropped into a full-bleed band cannot
   produce a 195-character line. Selector starts with .fde-grid, so it is inert
   outside the grid. overflow-wrap is the long-token guard that pairs with
   min-inline-size:0 above. */
.fde-grid :is(p, li, blockquote, dd, dt, figcaption){
  max-inline-size: var(--fde-measure);
  overflow-wrap: break-word;
}
/* Opt out where the element is a layout cell rather than a reading block —
   a table cell, a caption inside a tile, a chip in a row. */
.fde-grid .fde-nomeasure,
.fde-grid .fde-nomeasure :is(p, li, figcaption){ max-inline-size: none }

/* A wide artifact that genuinely cannot shrink (a wide table, a long command)
   scrolls inside itself rather than widening the document. */
.fde-scroll{
  overflow-x: auto;
  overscroll-behavior-inline: contain;   /* playbook SC10 */
}


/* ── 4 · VERTICAL RHYTHM ───────────────────────────────────────────────────
   row-gap is the baseline. These add to it, and only where a block needs to
   breathe differently. Values are the page's own --sp scale (0.5/0.9/1.4/2.2/
   3.4rem) extended by the same ~1.55 ratio, so nothing new is invented. */
.fde-grid > .fde-lead-0 { margin-block-start: calc(var(--fde-row) * -1) }  /* tuck up */
.fde-grid > .fde-lead-1 { margin-block-start: var(--sp-2, .9rem)  }
.fde-grid > .fde-lead-2 { margin-block-start: var(--sp-4, 2.2rem) }
.fde-grid > .fde-lead-3 { margin-block-start: var(--sp-5, 3.4rem) }
.fde-grid > .fde-lead-4 { margin-block-start: clamp(3.4rem, 6vw, 5.2rem) }


/* ── 5 · ALIGNMENT & OPTICS ────────────────────────────────────────────────
   Grid gives real optical tools; use them instead of nudging by eye.
   (playbook O1: optical beats mathematical.) */

/* Align a hanging folio to the FIRST BASELINE of the headline beside it, not
   to the top of its em-box. This is the correct fix for "the label sits too
   high next to the display line" — a display face at 1.06 leading carries
   ~0.03em of phantom half-leading that box alignment can never remove. */
.fde-grid > .fde-align-base { align-self: first baseline }
.fde-grid > .fde-align-last { align-self: last baseline }
.fde-grid > .fde-align-end  { align-self: end }
.fde-grid > .fde-align-fill { align-self: stretch }

/* Optical flush-left. A rule, image or border sits exactly on the grid line;
   a glyph starts one side-bearing to the right of it, so flush text reads
   inset. Measured on this page: Hanken "n" @17px = 1.224px = 0.072em.
   Apply to the TEXT block that must align to a graphic edge above or below. */
.fde-optic-flush{ margin-inline-start: calc(var(--fde-bearing) * -1) }

/* Hanging punctuation. hanging-punctuation is Safari-only, so the negative
   text-indent is the shipped implementation and the property is the upgrade.
   (playbook O5: production value for a serif opening quote is -0.4em.) */
.fde-hang{ text-indent: -0.4em }
@supports (hanging-punctuation: first){
  .fde-hang{ text-indent: 0; hanging-punctuation: first last }
}


/* ── 6 · RECURSION VIA SUBGRID ─────────────────────────────────────────────
   A full-bleed band that needs its caption back on the text grid does not need
   magic numbers: it inherits the same twelve columns and the same role names.

     <figure class="fde-bleed fde-subgrid">
       <img …>                                    <- no role: spans the bleed
       <figcaption class="fde-text">…</figcaption> <- lands on cols 1–6 exactly
     </figure>

   Subgrid is Baseline (Firefox 71 / Safari 16 / Chrome 117). The @supports
   fallback keeps the caption readable rather than aligned, which is the right
   trade for a browser that old. */
@supports (grid-template-columns: subgrid){
  .fde-grid > .fde-subgrid{
    display: grid;
    grid-template-columns: subgrid;   /* inherits [c] 1–13 and the gutters */
    row-gap: var(--fde-row);
    align-content: start;
  }
  .fde-grid > .fde-subgrid > *{ grid-column: c 1 / c 13; min-inline-size: 0 }
  .fde-grid > .fde-subgrid > .fde-mark     { grid-column: c 1  / c 4  }
  .fde-grid > .fde-subgrid > .fde-mark-end { grid-column: c 10 / c 13 }
  .fde-grid > .fde-subgrid > .fde-lede     { grid-column: c 1  / c 8  }
  .fde-grid > .fde-subgrid > .fde-text     { grid-column: c 1  / c 7  }
  .fde-grid > .fde-subgrid > .fde-inset    { grid-column: c 3  / c 9  }
  .fde-grid > .fde-subgrid > .fde-recto    { grid-column: c 7  / c 13 }
  .fde-grid > .fde-subgrid > .fde-column   { grid-column: c 1  / c 6  }
  .fde-grid > .fde-subgrid > .fde-aside    { grid-column: c 8  / c 13 }
  .fde-grid > .fde-subgrid > .fde-bleed    { grid-column: bleed-start / bleed-end }
}
@supports not (grid-template-columns: subgrid){
  .fde-grid > .fde-subgrid > :is(.fde-text, .fde-inset, .fde-recto, .fde-column,
                                 .fde-aside, .fde-lede){
    margin-inline: var(--fde-gutter);
  }
}


/* ── 7 · <=1068px — PAIRS UNSTACK, ASYMMETRY SURVIVES ──────────────────────
   The naive move here is "everything becomes one centred column", which
   re-creates the exact defect this file exists to fix — at 1024px a reviewer
   would see six identical stacked boxes. Instead the side-by-side pairs
   unstack while left/right anchoring and the stepped offsets stay, so the
   chapters still differ from each other.

   Browser-verified at both ends of the band:
     768px  viewport -> .fde-text role 576px, prose 533px = 72.1 chars  OK
                        .fde-column   role 518px, prose 518px = 70.1 chars  OK
    1068px  viewport -> .fde-text role 801px, prose 533px = 72.1 chars  OK
                        .fde-column   role 721px, prose 533px = 72.1 chars  OK
   The role widens 576 -> 801px across the band while the line stays put. That
   is what makes ONE span legal across a 1.4x width swing: the cap, not the
   column count, is holding the measure here. */
@media (max-width: 1068px){
  .fde-grid > .fde-mark      { grid-column: c 1  / c 5  }   /* 4 cols, still hangs */
  .fde-grid > .fde-mark-end  { grid-column: c 9  / c 13 }   /* 4 cols, still hangs */
  .fde-grid > .fde-lede      { grid-column: c 1  / c 12 }   /* 11 cols */
  .fde-grid > .fde-text      { grid-column: c 1  / c 11 }   /* 10 cols, left-anchored */
  .fde-grid > .fde-inset     { grid-column: c 2  / c 12 }   /* 10 cols, stepped */
  .fde-grid > .fde-recto     { grid-column: c 3  / c 13 }   /* 10 cols, right-anchored */
  .fde-grid > .fde-column    { grid-column: c 1  / c 10 }   /*  9 cols */
  .fde-grid > .fde-aside     { grid-column: c 3  / c 13 }   /* 10 cols, drops below and indents */
  .fde-grid > .fde-back,
  .fde-grid > .fde-lean,
  .fde-grid > .fde-frame,
  .fde-grid > .fde-span      { grid-column: c 1  / c 13 }
  /* .fde-bleed unchanged — a bleed is a bleed at every width. */

  @supports (grid-template-columns: subgrid){
    .fde-grid > .fde-subgrid > .fde-mark     { grid-column: c 1 / c 5  }
    .fde-grid > .fde-subgrid > .fde-mark-end { grid-column: c 9 / c 13 }
    .fde-grid > .fde-subgrid > .fde-lede     { grid-column: c 1 / c 12 }
    .fde-grid > .fde-subgrid > .fde-text     { grid-column: c 1 / c 11 }
    .fde-grid > .fde-subgrid > .fde-inset    { grid-column: c 2 / c 12 }
    .fde-grid > .fde-subgrid > .fde-recto    { grid-column: c 3 / c 13 }
    .fde-grid > .fde-subgrid > .fde-column   { grid-column: c 1 / c 10 }
    .fde-grid > .fde-subgrid > .fde-aside    { grid-column: c 3 / c 13 }
  }
}


/* ── 8 · <=734px — ONE COLUMN, NO EXCEPTIONS ───────────────────────────────
   The template drops to gutter | content | gutter and every role collapses.

   THE CATCH-ALL IS LOAD-BEARING, AND ITS SPECIFICITY IS THE WHOLE TRICK.
   Measured failure before this was written the way it is now: at 734px
   .fde-bleed rendered 128px wide instead of 734px, and .fde-text rendered
   587px on a three-track template. Cause: `.fde-grid > *` is (0,1,0) and
   LOSES to the §7 rules `.fde-grid > .fde-text` at (0,2,0). Both media queries
   match at 734px, so the tablet spans survived, `c 11` referenced a named line
   that no longer exists, and CSS resolved it against IMPLICIT lines — silently
   growing columns off the side of the grid.
   The fix is the `[class]` twin: an attribute selector carries class-level
   specificity, so `.fde-grid > [class]` is (0,2,0), ties every role rule, and
   wins on source order. `.fde-grid > *` stays for children with no class
   attribute at all. .fde-bleed is then restated last to win the same tie.
   ANY NEW ROLE ADDED TO §2 IS COVERED AUTOMATICALLY — nothing to maintain here.

   Honest note on the floor: 45 characters of --t3 body needs 45 x 7.392 =
   333px of text column. A 320px viewport cannot supply it at any gutter. This
   file minimises the gutter to 1rem below 375px so the column is as wide as
   the device allows (288px = 39 chars) and states the shortfall rather than
   pretending. At 375px it is 335px = 45.3 chars, and the contract holds. */
@media (max-width: 734px){
  .fde-grid{
    grid-template-columns:
      [bleed-start] var(--fde-gutter)
      [c] minmax(0, 1fr)
      [c] var(--fde-gutter) [bleed-end];
  }
  .fde-grid > *          { grid-column: c 1 / c 2 }   /* (0,1,0) no-class children */
  .fde-grid > [class]    { grid-column: c 1 / c 2 }   /* (0,2,0) ties every role   */
  .fde-grid > .fde-bleed { grid-column: bleed-start / bleed-end }  /* (0,2,0), last */

  /* offsets and hangs are meaningless in one column — reset them so nothing
     inherits a stale indent */
  .fde-grid > .fde-lead-0{ margin-block-start: calc(var(--fde-row) * -.5) }
  .fde-optic-flush{ margin-inline-start: 0 }

  @supports (grid-template-columns: subgrid){
    .fde-grid > .fde-subgrid > *          { grid-column: c 1 / c 2 }
    .fde-grid > .fde-subgrid > [class]    { grid-column: c 1 / c 2 }
    .fde-grid > .fde-subgrid > .fde-bleed { grid-column: bleed-start / bleed-end }
  }
}
@media (max-width: 374px){
  .fde-grid{ --fde-gutter: 1rem }
}


/* ── 9 · PRINT ─────────────────────────────────────────────────────────────
   A hiring manager will Cmd-P this page to PDF and forward it. A 12-column
   grid on paper is a defect, and the page's own print block cannot know about
   .fde-grid. Flatten it. */
@media print{
  .fde-grid{ display: block }
  .fde-grid > *{ margin-block: 0 0 1rem; max-inline-size: 100% }
  .fde-grid :is(p, li, blockquote, figcaption){ max-inline-size: none }
  .fde-scroll{ overflow-x: visible }
}


/* ── 10 · DEBUG (opt-in, ships inert) ──────────────────────────────────────
   Add .fde-debug alongside .fde-grid to see the twelve columns and to catch
   the two violations this file cannot enforce in CSS. */
.fde-debug{
  background-image: repeating-linear-gradient(
    to right,
    color-mix(in oklab, currentColor 6%, transparent) 0 var(--fde-col),
    transparent var(--fde-col) calc(var(--fde-col) * 2));
  background-position: center top;
  background-size: var(--fde-content) 100%;
  background-repeat: repeat-y;
}
/* violation 1 — a sentence inside a MARK role (35.7 chars, below the floor) */
.fde-debug :is(.fde-mark, .fde-mark-end) :is(p, li){ outline: 2px dashed #8E2419 }
/* violation 2 — mono prose in a role narrower than 6 columns (45 chars of
   JetBrains Mono at --t2 needs 454px; .fde-column and .fde-aside are 440px) */
.fde-debug :is(.fde-column, .fde-aside) .fde-mono{ outline: 2px dashed #8E2419 }
@media (max-width: 734px){ .fde-debug{ background-image: none } }
