/* =============================================================================
   Fernando Kylas Brand System: core.css
   -----------------------------------------------------------------------------
   the universal design language: faces, tokens, reset and type roles

   GENERATED. Do not edit this file.

   Source:    lab/publication-engine/platform/design-system/css/{fonts,tokens,base,typography}.css
   Producer:  assets/css/build-css.py
   Authority: docs/foundations/*.md, which wins over both.

   Load order is part of the contract: core.css first and always; print.css only
   where a document will be printed; report.css only where it is a Fernando Kylas
   report. See docs/architecture.md §7.
   ============================================================================= */

/* =============================================================================
   Fernando Kylas Report Engine: Fonts
   -----------------------------------------------------------------------------
   Licensed brand faces: Strawford and Geomanist. Both families are fully
   licensed for desktop and web (see assets/fonts/README.md).

   SINGLE SOURCE OF TRUTH. These @font-face rules are the ONLY place the engine
   touches font binaries. The url() paths resolve RELATIVE TO THIS FILE and point
   at the repository's governed font store at assets/fonts/, the one location the
   .gitignore font-binary block protects. The engine deliberately does NOT carry
   its own copy of the binaries, so there is exactly one place to purge before any
   public release, and no uncovered exposure path.

   INTEGRATION POINT. A product that consumes this engine from a different path
   repoints the fonts by overriding these four levels of `./` (or by serving the
   same faces from its own governed store). This file is the only thing to change.

   Only the weights the report roles actually use are loaded, for weight:
     Strawford: Light 300, Regular 400, Medium 500
     Geomanist: Book 350, Regular 400, Medium 500, Bold 700, Book Italic 350

   ONE ITALIC IS ADOPTED: Geomanist Book Italic, at 350 so it pairs with Book.
   Registering it at the body weight is the whole fix: `<em>` in prose resolves to
   a drawn italic through the browser's own default, so no selector is added and no
   type value changes. Emphasis at any other weight: inside a heading, a caption,
   a display line or <strong>, still synthesises, because no italic is adopted
   there. That is deliberate: forcing <em> to 350 would make emphasis inside a
   heading lighter than the heading, which is worse than the artefact.

   THIS IS THE FACE foundations/typography.md §4 ADOPTS: Geomanist Book Italic at
   350, the body weight, and the only italic the system takes. Both the webfont
   and the desktop package carry an italic for all nine weights, so nothing about
   this choice is a packaging compromise.
   ============================================================================= */

/* ── Strawford, the memorable voice (display roles only) ─────────────────── */

@font-face {
  font-family: "Strawford";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/strawford-light.woff2") format("woff2");
}

@font-face {
  font-family: "Strawford";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/strawford-regular.woff2") format("woff2");
}

@font-face {
  font-family: "Strawford";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/strawford-medium.woff2") format("woff2");
}

/* ── Geomanist, the clear voice (reading, interface, metadata, statistics) ── */

@font-face {
  font-family: "Geomanist";
  font-style: normal;
  font-weight: 350;             /* Book, the body weight (typography.md §3.4) */
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/geomanist-book.woff2") format("woff2");
}

@font-face {
  font-family: "Geomanist";
  font-style: normal;
  font-weight: 400;             /* Regular: captions and metadata */
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/geomanist-regular.woff2") format("woff2");
}

@font-face {
  font-family: "Geomanist";
  font-style: normal;
  font-weight: 500;             /* Medium: labels, nav, buttons, card titles */
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/geomanist-medium.woff2") format("woff2");
}

@font-face {
  font-family: "Geomanist";
  font-style: normal;
  font-weight: 700;             /* Bold: statements, statistics, metric values */
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/geomanist-bold.woff2") format("woff2");
}

/* The canonical italic. Declared at 350 to pair with Book, so emphasis in body
   prose is a drawn face rather than a synthesised oblique. The 350 convention
   applies to the italic for the same reason it applies to the roman: it is the
   reading weight, and <em> inherits the weight of the text it sits in. */
@font-face {
  font-family: "Geomanist";
  font-style: italic;
  font-weight: 350;             /* Book Italic: emphasis in body prose */
  font-display: swap;
  src: url("https://assets.fernandokylas.com/fkpp/v1.0.0/fonts/geomanist-book-italic.woff2") format("woff2");
}


/* =============================================================================
   Fernando Kylas Report Engine: Design Tokens
   -----------------------------------------------------------------------------
   Every value here is derived from the canonical Brand System foundations, not
   invented for this engine:
     • colour   → foundations/colour.md            (warm-paper v3.1 system)
     • type     → foundations/typography.md         (measured Strawford/Geomanist)
     • spacing  → base-8 rhythm (typography.md §13 / spacing pending migration)
     • radii    → docs/typography/design.md          (architectural radius system)

   Where a value departs from a foundation because report media requires it, the
   departure is noted inline AND in IMPLEMENTATION.md. Nothing is casual.

   Naming: all tokens are prefixed `--fk-` so they never collide with a consuming
   product's own custom properties. Type tokens carry size/line-height/tracking/
   weight per role; typography.css binds them to role classes.
   ============================================================================= */

:root {
  /* LIGHT, NOT `light dark`: corrected 2026-08-25.
     Dark is reached through [data-theme] and never through prefers-color-scheme
     (see THEME INVERSION below), so declaring both here told the UA the document
     renders itself dark when it does not. A reader with a dark system preference
     and JavaScript disabled got the warm paper ground with dark scrollbars and
     dark form controls, because the anti-flash snippet that sets [data-theme] is
     the only thing that reads the preference, and it is a script.
     The dark block re-declares this as `dark`, so a themed document still gets
     dark UA chrome. Nothing else about either ground changes. */
  color-scheme: light;

  /* ── Font families ──────────────────────────────────────────────────────
     Two voices split by PURPOSE, not by heading-vs-body (typography.md §1).
     Strawford = memorable/display; Geomanist = clear/reading+interface. */
  --fk-font-display: "Strawford", Georgia, "Times New Roman", serif;
  --fk-font-text: "Geomanist", "Helvetica Neue", Arial, sans-serif;

  --fk-weight-display-light: 300;   /* Strawford Light: display ≥ 96px only   */
  --fk-weight-display: 400;         /* Strawford Regular: display 36–84px      */
  --fk-weight-display-medium: 500;  /* Strawford Medium: headings 22–32px      */
  --fk-weight-book: 350;            /* Geomanist Book: body copy               */
  --fk-weight-regular: 400;         /* Geomanist Regular: captions, metadata   */
  --fk-weight-medium: 500;          /* Geomanist Medium: labels, nav, buttons  */
  --fk-weight-bold: 700;            /* Geomanist Bold: statements, statistics  */

  /* ═══════════════════════════════════════════════════════════════════════
     COLOUR: primitives (foundations/colour.md §2). NEVER reference a
     primitive directly in a component; always go through a semantic token.
     ═══════════════════════════════════════════════════════════════════════ */
  --fk-c-paper:        #F6F4F1;   /* warm paper: page ground (decided)         */
  --fk-c-paper-card:   #FCFCFB;   /* raised card surface on light               */
  --fk-c-rev-1:        #FAFAF7;   /* warm off-white: reversed text             */
  --fk-c-ink:          #090909;   /* ink ground (dark surface)                  */
  --fk-c-ink-1:        #0A0A0A;   /* primary ink: text on light                */
  --fk-c-ink-2:        #4A4A4A;   /* mid grey: secondary text on light         */
  --fk-c-ink-3:        #616161;   /* metadata grey on light                     */
  --fk-c-rev-2:        #ADADAD;   /* secondary text on dark (8.87:1)            */
  --fk-c-rev-3:        #8E8E8E;   /* metadata grey on dark                      */
  --fk-c-green:        #869F75;   /* the one accent hue                         */
  --fk-c-green-wash:   #EBF0E9;   /* green wash surface (light only)            */
  --fk-c-line:         #E4E3E8;   /* rule / divider on light                    */
  --fk-c-line-rev:     #2A2A2A;   /* rule / divider on dark                     */

  /* ── Semantic colour tokens. LIGHT (default) ───────────────────────────
     A ground change must propagate through these, never through a fixed hex.
     Theme inversion is a SINGLE contract: [data-theme="dark"] below. Setting a
     dark ground any other way (inline background) is a lint error (colour.md §7). */
  --fk-surface:          var(--fk-c-paper);
  --fk-surface-raised:   var(--fk-c-paper-card);
  --fk-surface-accent:   var(--fk-c-green-wash);   /* light only               */
  --fk-text-primary:     var(--fk-c-ink-1);        /* 18.03:1 on paper         */
  --fk-text-secondary:   var(--fk-c-ink-2);        /*  8.07:1 on paper         */
  --fk-text-tertiary:    var(--fk-c-ink-3);        /*  5.64:1 on paper         */
  /* TERTIARY TEXT ON A RAISED SURFACE. `COLOUR-01`, decided 2026-08-26.
     On light this is the same value: #616161 measures 6.03:1 on the #FCFCFB card,
     so nothing changes here and the token exists so the pair is declared in both
     themes rather than only in the one that needed it. */
  --fk-text-tertiary-raised: var(--fk-c-ink-3);    /*  6.03:1 on the raised card */
  --fk-border:           var(--fk-c-line);
  --fk-border-strong:    var(--fk-c-ink-2);        /* deliberate editorial rule */
  --fk-accent:           var(--fk-c-green);        /* MARKS ONLY on light (2.65:1) */
  --fk-accent-ink:       var(--fk-c-ink-1);        /* accent's text stand-in on light */

  /* Severity is carried by INK WEIGHT + LABEL, never by hue (colour.md §6:
     "No colour carries meaning alone"; audits must survive greyscale print).
     These map to text/rule emphasis, not to red/amber/green. */
  --fk-sev-critical:  var(--fk-text-primary);   /* + filled ink chip in component */
  --fk-sev-high:      var(--fk-text-primary);
  --fk-sev-medium:    var(--fk-text-secondary);
  --fk-sev-low:       var(--fk-text-tertiary);
  --fk-positive:      var(--fk-accent-ink);     /* on light: ink; green as mark only */
  --fk-focus-ring:    var(--fk-text-primary);   /* 2px ink ring (typography.md §14) */

  /* ── Role tokens (colour.md §4.2) ────────────────────────────────────────
     Named so intent is recorded at the call site and so a future decision can
     diverge a role from its current tier in ONE place. Every one resolves to an
     existing reading tier: this system has no success-green, warning-amber or
     critical-red, because meaning is carried by SHAPE + LABEL (colour.md §8).
     `information` and `observation` share a value deliberately, an interface
     notice and an audit finding class are different vocabularies. */
  --fk-critical:      var(--fk-text-primary);   /* + filled square + label     */
  --fk-warning:       var(--fk-text-primary);   /* + half-filled square + label */
  --fk-success:       var(--fk-accent-ink);     /* + filled circle + label     */
  --fk-information:   var(--fk-text-secondary); /* + outlined circle + label   */
  --fk-observation:   var(--fk-text-secondary); /* + outlined circle + label   */
  --fk-evidence:      var(--fk-surface-raised); /* panel ground + source line  */
  --fk-confidence:    var(--fk-text-primary);   /* 1–3 filled dots of 3        */
  --fk-disabled:      var(--fk-text-tertiary);  /* + aria-disabled             */
  --fk-unmeasured:    var(--fk-text-tertiary);  /* + dashed outline + label    */

  /* ── Interaction (colour.md §4.2) ────────────────────────────────────────
     Hover NEVER reduces contrast: the colour holds and the underline thickens.
     Dimming a link makes it less legible exactly when attention arrives. */
  --fk-interactive:        var(--fk-text-primary);
  --fk-interactive-hover:  var(--fk-text-primary);
  --fk-surface-selected:   var(--fk-surface-accent);
  --fk-rule-accent:        var(--fk-accent);    /* signature rule, a mark only  */

  /* ── Data series (colour.md §8.3) ────────────────────────────────────────
     TWO tone-only series is the mathematical maximum on either ground: fitting
     three marks each 3:1 apart needs a luminance the ground's own 3:1 ceiling
     forbids. Mutual separation 3.20:1 light / 3.13:1 dark. Beyond two, use a
     pattern fill, direct labelling or small multiples, never another hue. */
  --fk-data-1:        var(--fk-text-primary);
  --fk-data-2:        var(--fk-text-tertiary);
  --fk-data-accent:   var(--fk-accent);         /* third series ONLY with a pattern or label */

  /* ═══════════════════════════════════════════════════════════════════════
     TYPE ROLES: size / line-height (unitless) / tracking (em) / weight.
     Sizes in rem (1rem = 16px root) so readers can scale on screen; print.css
     re-declares the reading roles in pt against the accessible-print floor.
     Line-heights are unitless ratios computed from the measured px line boxes
     in typography.md §3 (e.g. body 17/28 = 1.647).
     ═══════════════════════════════════════════════════════════════════════ */

  /* Display / editorial: Strawford (typography.md §3.1) */
  --fk-display-size:     4rem;      /* 64px  display-m                          */
  --fk-display-lh:       1.031;     /* 66/64                                    */
  --fk-display-track:    -0.026em;
  --fk-display-weight:   var(--fk-weight-display);

  --fk-report-title-size:   3rem;   /* 48px  display-s                          */
  --fk-report-title-lh:     1.104;  /* 53/48                                    */
  --fk-report-title-track:  -0.022em;
  --fk-report-title-weight: var(--fk-weight-display);

  --fk-section-title-size:   2.25rem;  /* 36px  heading-l                       */
  --fk-section-title-lh:     1.167;    /* 42/36                                 */
  --fk-section-title-track:  -0.016em;
  --fk-section-title-weight: var(--fk-weight-display);

  --fk-quote-size:    1.875rem;     /* 30px  editorial quote (Strawford)        */
  --fk-quote-lh:      1.333;        /* 40/30                                    */
  --fk-quote-track:   -0.012em;
  --fk-quote-weight:  var(--fk-weight-display);

  /* Structural headings & interface: Geomanist (typography.md §3.2)
     Below ~28px Strawford and Geomanist are hard to tell apart, so the two-voice
     split spends Geomanist Medium here, not Strawford (typography.md §3.2). */
  --fk-subsection-size:   1.5rem;   /* 24px  subsection title                   */
  --fk-subsection-lh:     1.25;     /* 30/24                                    */
  --fk-subsection-track:  -0.006em;
  --fk-subsection-weight: var(--fk-weight-medium);

  --fk-card-title-size:   1.375rem; /* 22px  card-title                         */
  --fk-card-title-lh:     1.273;    /* 28/22                                    */
  --fk-card-title-track:  -0.004em;
  --fk-card-title-weight: var(--fk-weight-medium);

  /* Reading: Geomanist text scale (typography.md §3.4) */
  --fk-lead-size:   1.375rem;       /* 22px  lead                               */
  --fk-lead-lh:     1.545;          /* 34/22                                    */
  --fk-lead-track:  -0.004em;
  --fk-lead-weight: var(--fk-weight-book);

  --fk-body-l-size:  1.1875rem;     /* 19px  body-l                             */
  --fk-body-l-lh:    1.632;         /* 31/19                                    */
  --fk-body-l-track: 0em;
  --fk-body-l-weight: var(--fk-weight-book);

  --fk-body-size:   1.0625rem;      /* 17px  body, never below 16px in content */
  --fk-body-lh:     1.647;          /* 28/17                                    */
  --fk-body-track:  0em;
  --fk-body-weight: var(--fk-weight-book);

  --fk-body-s-size:  0.9375rem;     /* 15px  body-s / table body                */
  --fk-body-s-lh:    1.6;           /* 24/15                                    */
  --fk-body-s-track: 0.004em;
  --fk-body-s-weight: var(--fk-weight-book);

  --fk-caption-size:  0.8125rem;    /* 13px  caption / metadata                 */
  --fk-caption-lh:    1.462;        /* 19/13                                    */
  --fk-caption-track: 0.010em;
  --fk-caption-weight: var(--fk-weight-regular);

  --fk-footnote-size:  0.75rem;     /* 12px  footnote                           */
  --fk-footnote-lh:    1.417;       /* 17/12                                    */
  --fk-footnote-track: 0.010em;
  --fk-footnote-weight: var(--fk-weight-regular);

  --fk-label-size:   0.75rem;       /* 12px  label: uppercase eyebrow          */
  --fk-label-lh:     1.333;         /* 16/12                                    */
  --fk-label-track:  0.14em;        /* never track uppercase below +0.08em      */
  --fk-label-weight: var(--fk-weight-medium);

  /* Statistics / metrics: Geomanist Bold (typography.md §3.3) */
  --fk-metric-size:     3rem;       /* 48px  metric value                       */
  --fk-metric-lh:       1.0;
  --fk-metric-track:    -0.016em;
  --fk-metric-weight:   var(--fk-weight-bold);

  --fk-metric-lg-size:  4rem;       /* 64px  stat-l: hero KPI                  */
  --fk-metric-lg-lh:    0.969;      /* 62/64                                    */
  --fk-metric-lg-track: -0.018em;

  /* ═══════════════════════════════════════════════════════════════════════
     SPACING: base-8 vertical rhythm (typography.md §13). rem so it tracks
     the type scale. Named by step, never by pixel value.
     ═══════════════════════════════════════════════════════════════════════ */
  --fk-space-0:   0;
  --fk-space-1:   0.25rem;   /*  4 */
  --fk-space-2:   0.5rem;    /*  8: base unit */
  --fk-space-3:   0.75rem;   /* 12 */
  --fk-space-4:   1rem;      /* 16 */
  --fk-space-5:   1.5rem;    /* 24 */
  --fk-space-6:   2rem;      /* 32 */
  --fk-space-7:   2.5rem;    /* 40 */
  --fk-space-8:   3.5rem;    /* 56 */
  --fk-space-9:   4.5rem;    /* 72 */
  --fk-space-10:  6rem;      /* 96 */
  --fk-space-11:  8rem;      /* 128 */
  --fk-space-12:  10rem;     /* 160 */

  /* Semantic spacing, the rhythm reports actually use */
  --fk-flow-tight:     var(--fk-space-3);   /* between tightly-bound lines      */
  --fk-flow:           var(--fk-space-5);    /* default paragraph flow           */
  --fk-flow-loose:     var(--fk-space-6);    /* between grouped blocks           */
  --fk-section-gap:    var(--fk-space-10);   /* between major sections (screen)  */
  --fk-heading-gap:    var(--fk-space-6);    /* heading → its content            */

  /* THE EDITORIAL PAUSES. grid-layout-rhythm.md §4.7, canon since 2026-08-25
     (`GRID-03`). These name the three RELATIONSHIPS of §4.1, which is the layer
     §6 measured as missing: three chapters reached past the semantic tokens to
     the raw scale and then duplicated the result three times, because there was
     a vocabulary for space inside a block and none for space between blocks.

     ADDITIVE, AND NOTHING RENDERS DIFFERENTLY. Two of the four already had a
     token and are chained rather than redefined, so each distance still has one
     definition. Related is the only relationship carrying two distances, and
     §4.3 is explicit that this is one type: a block ending in a visible edge
     already supplies a boundary and its own padding. */
  --fk-pause-bound:               var(--fk-heading-gap);  /* 32 · no perceived boundary */
  --fk-pause-related:             var(--fk-space-8);      /* 56 · after plain content   */
  --fk-pause-related-compensated: var(--fk-space-9);      /* 72 · after a visible edge  */
  --fk-pause-section:             var(--fk-section-gap);  /* 96 · a new thought begins  */

  /* ═══════════════════════════════════════════════════════════════════════
     PAGE / A4: print geometry (physical units) + screen shell (px).
     ═══════════════════════════════════════════════════════════════════════ */
  --fk-page-width:      210mm;
  --fk-page-height:     297mm;
  --fk-page-margin-top:    22mm;
  --fk-page-margin-bottom: 20mm;
  --fk-page-margin-inner:  20mm;   /* side margins                             */
  --fk-page-gutter-print:  10mm;   /* two-column gap in print                  */

  /* THE PAGE EDGE ON SCREEN. grid-layout-rhythm.md §2, canon since 2026-08-26
     (`GRID-01`). 5% of viewport width between a 20px floor and a 64px ceiling,
     the ceiling reached at 1280. Ratified from the behaviour the three rendered
     chapters already realised, measured at eleven viewports.

     IT INTERPOLATES, AND THAT IS THE SYSTEM'S ONE EXEMPTION FROM STEPPING.
     §10 requires spatial resolution to step at named breakpoints so that space
     cannot drift out of register with type, which steps because tracking is a
     property of rendered size. §10.3 exempts a value derived from the CANVAS
     rather than from type, because such a value has no type metric to drift
     against, and the page edge is the only one. `PLAT-08` resolved.

     Print does not use this: a physical canvas takes the mm margins above. */
  --fk-page-edge:       clamp(1.25rem, 5vw, 4rem);

  /* Screen shell, the on-screen "document" is a centred column, not a browser
     app. Wide enough for dashboards, constrained for reading.

     WIDENED 2026-07-27. The shell was 72rem / 1152px, of which 8rem is gutter, so
     the content column measured 1024px: 71% of a 1440px viewport. Measured across
     all fourteen engine consumers before the change: every one of them, at 1024px.
     A publication that spends 29% of a desktop viewport on empty page reads as a
     PDF pretending to be a website, and it is the reason contents lists, tables,
     definition lists and comparison bars were all fighting for room at once.

     The screen shell is now 80rem / 1280px → a 1152px content column, 80% of 1440.
     Deliberately NOT edge-to-edge: 144px of page remains on each side at 1440, and
     the gutter is unchanged, so the document still reads as a document. Print
     geometry is untouched: A4 is a fixed canvas and has nothing to gain from this
     (see the medium rule in DESIGN.md §Medium). */
  --fk-shell-max:     80rem;   /* 1280px: outer report canvas                 */
  --fk-content-max:   72rem;   /* 1152px: section content, = shell − gutter   */
  --fk-measure:       39rem;   /* ~624px: long-form body measure (62–70 ch)   */
  --fk-measure-lead:  41.25rem;/* ~660px: lead paragraph measure              */
  --fk-measure-wide:  47.5rem; /* ~760px: editorial intro measure             */

  /* STRUCTURED-DATA MEASURE: the width for blocks that are read by scanning
     rather than by reading: contents lists, reference registers, definition lists,
     score lines, comparison bars.

     It exists because those blocks had been sharing `--fk-measure-wide` (760px)
     with editorial prose, and the two have opposite needs: prose wants a short line
     so the eye can return, a data block wants room so a label and its value are not
     forced into two cramped columns. One token was doing two jobs, which is the same
     coupling `grid-layout-rhythm.md` §12 prohibits and `.fk-prose` was split to end.

     960px, between the wide reading measure and the content column: enough for a
     label column plus a full value, never so wide that a contents entry's title and
     its number lose their relationship. */
  --fk-measure-structural: 60rem;  /* 960px: scanned, not read                */

  --fk-gutter:        clamp(1.25rem, 5vw, 4rem);  /* screen page side padding  */
  --fk-col-gap:       var(--fk-space-6);          /* two-column screen gap      */

  /* ═══════════════════════════════════════════════════════════════════════
     BORDERS · RADII · ELEVATION: architectural, restrained (design.md).
     ═══════════════════════════════════════════════════════════════════════ */
  --fk-hairline:      1px solid var(--fk-border);
  --fk-rule:          2px solid var(--fk-text-primary);   /* deliberate divider */
  --fk-rule-accent:   2px solid var(--fk-accent);         /* signature rule     */

  --fk-radius-none:   0;
  --fk-radius-sm:     2px;    /* tags, chips                                    */
  --fk-radius-md:     4px;    /* inputs, small panels                          */
  --fk-radius-lg:     6px;    /* cards, large panels (maximum)                 */
  --fk-radius-pill:   999px;  /* compact metadata tags only                    */

  /* Elevation is a last resort: reports lean on space, rule and surface, not
     shadow (design.md). Stripped entirely in print.css. */
  --fk-shadow-soft:   0 12px 40px rgba(9, 9, 11, 0.05);

  /* ── Scroll areas (screen only) ──────────────────────────────────────────
     IN-PAGE horizontal overflow, a wide table, a specimen strip, a code block.
     NOT the viewport scrollbar, which is the reader's own furniture and is left
     to the operating system.

     FOUR TOKENS, AND NO DARK COUNTERPART IS NEEDED. Each resolves to a semantic
     token that the theme block already inverts, and a custom property is
     substituted where it is USED rather than where it is declared, so these
     follow the theme with one definition. A second set under [data-theme] would
     be two places to change one decision.

     The track is deliberately the divider token: it should read as a fine line
     of the same family as every other rule in the system. The thumb is tertiary
     ink: present, restrained, and never competing with the content it sits
     under. */
  --fk-scrollbar-size:        0.5rem;                  /* the band, not the line */
  --fk-scrollbar-track:       var(--fk-border);
  --fk-scrollbar-thumb:       var(--fk-text-tertiary); /* 5.64:1 light · 6.08:1 dark */
  --fk-scrollbar-thumb-hover: var(--fk-text-secondary);/* 8.07:1 light · 8.87:1 dark */

  /* ── Motion (screen only; respected under prefers-reduced-motion) ────────── */
  --fk-ease:          cubic-bezier(0.22, 1, 0.36, 1);
  --fk-dur:           200ms;

  /* ── Breakpoints (reference only: media queries cannot read custom props;
        these values are the single source the media queries below mirror) ──
        mobile ≤ 480 · tablet ≤ 768 · laptop ≤ 1024 · desktop ≥ 1280 */
}

/* ═════════════════════════════════════════════════════════════════════════
   THEME INVERSION: the ONE sanctioned route to a dark ground (colour.md §7).
   Only the semantic tokens flip. On dark, green becomes text-capable (6.86:1),
   so severity/positive tokens gain the accent where the foundation permits it.
   ═════════════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"],
[data-theme="dark"] {
  /* The other half of the `color-scheme` correction above: a themed document
     gets dark scrollbars, form controls and canvas from the UA. */
  color-scheme: dark;

  --fk-surface:          var(--fk-c-ink);
  --fk-surface-raised:   var(--fk-c-line-rev);         /* nearest dark raise    */
  --fk-surface-accent:   rgba(134, 159, 117, 0.14);    /* wash, never solid pale */
  --fk-text-primary:     var(--fk-c-rev-1);            /* 19.04:1 on ink        */
  --fk-text-secondary:   var(--fk-c-rev-2);            /*  8.87:1 on ink        */
  --fk-text-tertiary:    var(--fk-c-rev-3);            /*  6.08:1 on ink        */
  /* THE FIX FOR `COLOUR-01`, decided by Fernando 2026-08-26, Route A.
     #8E8E8E on the #2A2A2A card measures 4.38:1 against a 4.5:1 body requirement,
     so a caption inside a dark card was non-conforming. The card is NOT darkened:
     measured, the darkest ground that lets #8E8E8E pass is #282828, which clears
     the threshold by 0.00 and drops the card's separation from the page from
     1.39:1 to 1.35:1. No lighter ground exists at all, because #8E8E8E would need
     a ground at luminance 1.392 and white is 1.000.
     So the TEXT moves, to a value already in the system: rev-2 is the dark theme's
     own text-secondary. 6.40:1, and no new primitive. */
  --fk-text-tertiary-raised: var(--fk-c-rev-2);        /*  6.40:1 on the card   */
  --fk-border:           var(--fk-c-line-rev);
  --fk-border-strong:    var(--fk-c-rev-2);
  --fk-accent:           var(--fk-c-green);            /* text-capable on dark  */
  --fk-accent-ink:       var(--fk-c-green);            /* green may be text here */
  --fk-sev-critical:     var(--fk-text-primary);
  --fk-sev-high:         var(--fk-text-primary);
  --fk-sev-medium:       var(--fk-text-secondary);
  --fk-sev-low:          var(--fk-text-tertiary);
  --fk-positive:         var(--fk-c-green);
  --fk-focus-ring:       var(--fk-text-primary);

  /* Role, interaction and data tokens re-alias so they follow the dark tiers.
     `success` gains the green here because green is text-capable at 6.86:1 on
     ink, the asymmetry in colour.md §5, resolved in one place. */
  --fk-critical:         var(--fk-text-primary);
  --fk-warning:          var(--fk-text-primary);
  --fk-success:          var(--fk-c-green);
  --fk-information:      var(--fk-text-secondary);
  --fk-observation:      var(--fk-text-secondary);
  --fk-evidence:         var(--fk-surface-raised);
  --fk-confidence:       var(--fk-text-primary);
  --fk-disabled:         var(--fk-text-tertiary);
  --fk-unmeasured:       var(--fk-text-tertiary);
  --fk-interactive:        var(--fk-text-primary);
  --fk-interactive-hover:  var(--fk-text-primary);
  --fk-surface-selected:   var(--fk-surface-accent);
  --fk-rule-accent:        var(--fk-accent);
  --fk-data-1:           var(--fk-text-primary);   /* 19.04:1 on ink            */
  --fk-data-2:           var(--fk-text-tertiary);  /*  6.08:1 · 3.13:1 from data-1 */
  --fk-data-accent:      var(--fk-accent);
}


/* =============================================================================
   Fernando Kylas Report Engine: Base
   -----------------------------------------------------------------------------
   Reset, document defaults, semantic-element styling, and accessibility helpers.
   Semantic HTML must look right WITHOUT role classes: h1–h6, p, ul/ol, table,
   blockquote all inherit sensible brand defaults here. Role classes in
   typography.css are for precise control; the elements degrade gracefully.
   ============================================================================= */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--fk-space-8);
  /* Geomanist and Strawford use cap-height lining figures; default to tabular
     figures nowhere globally, but expose them where numerics matter (§base). */
  font-feature-settings: "kern" 1;
}

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

body {
  background: var(--fk-surface);
  color: var(--fk-text-primary);
  font-family: var(--fk-font-text);
  font-weight: var(--fk-body-weight);
  font-size: var(--fk-body-size);
  line-height: var(--fk-body-lh);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis: none;               /* never fake a weight we did not load */
}

/* ── Media resets ─────────────────────────────────────────────────────────── */
img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
svg { fill: currentColor; }

/* ── Form controls: typography inheritance belongs to the ENGINE ────────────
   Interactive controls do NOT inherit the document font: every browser gives
   them a UA font, and nothing above this line changed that. The consequence was
   that a bare <button> rendered in the system font while every other node on the
   page rendered in Geomanist or Strawford.

   Until now the gap was covered by `.fk-btn` in components.css, which declares a
   family explicitly. That made a COMPONENT load-bearing for a RESET, with three
   costs: a control without the class fell back silently, `input`, `select` and
   `textarea` had no component at all and therefore no repair, and every consumer
   had to know to patch it locally. A derived publication should inherit correct
   typography automatically.

   `inherit` rather than a token, deliberately: the value still resolves from
   `body` and from tokens.css, so this reset ORIGINATES NOTHING and cannot drift
   from the type system. `.fk-btn` and every other component keep their explicit
   declarations, which win on specificity, so nothing that already looked right
   changes.

   `optgroup` is included because it is the one control descendant browsers style
   separately. `PLAT-12`, resolved 2026-07-25. */
button, input, select, textarea, optgroup {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
}

/* ── Semantic headings: default to report roles (typography.md) ───────────── */
h1, h2, h3, h4, h5, h6 { color: var(--fk-text-primary); text-wrap: balance; }

h1 {   /* report title: Strawford display-s */
  font-family: var(--fk-font-display);
  font-weight: var(--fk-report-title-weight);
  font-size: var(--fk-report-title-size);
  line-height: var(--fk-report-title-lh);
  letter-spacing: var(--fk-report-title-track);
}
h2 {   /* section title: Strawford heading-l */
  font-family: var(--fk-font-display);
  font-weight: var(--fk-section-title-weight);
  font-size: var(--fk-section-title-size);
  line-height: var(--fk-section-title-lh);
  letter-spacing: var(--fk-section-title-track);
}
h3 {   /* subsection: Geomanist Medium (silhouette-invisible zone → clear voice) */
  font-family: var(--fk-font-text);
  font-weight: var(--fk-subsection-weight);
  font-size: var(--fk-subsection-size);
  line-height: var(--fk-subsection-lh);
  letter-spacing: var(--fk-subsection-track);
}
h4 {   /* card / minor heading: Geomanist Medium */
  font-family: var(--fk-font-text);
  font-weight: var(--fk-card-title-weight);
  font-size: var(--fk-card-title-size);
  line-height: var(--fk-card-title-lh);
  letter-spacing: var(--fk-card-title-track);
}
h5 {   /* label-scale heading */
  font-family: var(--fk-font-text);
  font-weight: var(--fk-body-weight);
  font-size: var(--fk-body-size);
  line-height: var(--fk-body-lh);
}
h6 {   /* uppercase label */
  font-family: var(--fk-font-text);
  font-weight: var(--fk-label-weight);
  font-size: var(--fk-label-size);
  line-height: var(--fk-label-lh);
  letter-spacing: var(--fk-label-track);
  text-transform: uppercase;
  color: var(--fk-text-tertiary);
}

/* ═══════════════════════════════════════════════════════════════════════════
   READING MEASURE: A PROPERTY OF CONTEXT, NEVER OF AN ELEMENT SELECTOR ALONE
   ---------------------------------------------------------------------------
   `--fk-measure-context` is an INHERITED custom property. A container declares
   the measure for its whole subtree; prose-bearing elements consume it. Two
   different questions, now answered in two different places:

     WHICH elements carry continuous prose   → a question about KIND, answered by
                                               the selector list below
     HOW WIDE that prose sets                → a question about CONTEXT, answered
                                               by whichever ancestor declares it

   WHY INHERITANCE, AND NOT A DESCENDANT SELECTOR. Inheritance crosses any number
   of intervening elements, including an attribute-less <div>. A descendant or
   child selector does not, and `GRID-05` recorded what that costs: an
   attribute-less <div> was deciding the most deliberate spatial rule in the
   system, and the fix was that recognition must never be positional. The same
   reasoning applies here. **This model survives HTML restructuring because it
   does not depend on structure at all.**

   WHAT IT REPLACES. Measure was bound per element type: `p`, `ul`, `ol` and
   `blockquote` each carried their own cap. A `dd` is not a `p`, so
   definition-list content escaped the reading width entirely, and the Identity
   chapter had to cap it locally. Measured 2026-07-25, the same defect silently
   broke a demonstration on the static site: a rail could not scroll because the
   paragraph inside it was capped at the measure and never exceeded its container.
   `FOUNDATION-ARCHITECTURE-REVIEW.md` Part 9 item 3 names the correction;
   `grid-layout-rhythm.md` §3.6 requires it: a band must never be coupled to
   anything other than width.

   HOW A CONSUMER USES IT
     • declare a context   .fk-reading / --wide / --lead   (typography.css)
     • release a subtree   --fk-measure-context: none
     • add a band          set the property; no new selector, no new element list

   WHY THERE IS NO :root DEFAULT. Each element keeps its own default as the var()
   fallback, so output is unchanged wherever no context speaks. The fallback is
   not a compromise: it states what that element does when nothing has an opinion.
   A paragraph reads at the reading measure unless its context says otherwise.

   FUTURE FOUNDATIONS. A new band is a new value for one property. Neither the
   selector list nor the mechanism changes, which is the point.

   `PLAT-07`, resolved 2026-07-25.

   ---------------------------------------------------------------------------
   `PLAT-14`. TESTED AND REFUTED, 2026-07-26. Do not re-apply the "fix".

   THE CLAIM. These caps are unsafe because the measure tokens are in `rem`: at 200%
   text `--fk-measure` resolves to 1,248 px, so a paragraph in a 320 px viewport is
   capped at four times the screen. `accessibility.md` requires text resizable to 200%
   and nothing in this system had ever been measured at it (`EXAMPLE-01`), so the
   concern was reasonable, and it was recorded as an engine defect every consumer
   inherits.

   IT IS NOT A DEFECT. `max-width` cannot make a block wider than its containing
   block, so a cap larger than the container is inert. Isolated at 320 px with the
   root font size doubled, the whole test is six lines of HTML:

       block in normal flow             bare cap 320 px · min(…,100%) 320 px
       block in a max-content grid      bare cap 320 px · min(…,100%) 320 px
       block in a minmax(0,1fr) grid    bare cap 320 px · min(…,100%) 320 px

   The `min(…, 100%)` form was applied to all 33 rem- and ch-based caps across
   base.css, typography.css, layout.css and components.css, and measured across 30
   consumers at four viewports: **0 measurements improved, 0 worsened.** Reverted.

   AND IT BROKE THIS MECHANISM, which is the more useful half of the finding.
   `min(none, 100%)` is invalid CSS, so the caps in typography.css had to name the
   TOKEN rather than `--fk-measure-context` in order to survive `min()`. Measured
   consequence: a `.fk-prose` element with a consumer-set `--fk-measure-context: 10rem`
   computed `min(624px, 100%)` and **ignored the context entirely**: removing the
   exact flexibility `PLAT-07` exists to provide, in order to fix nothing.

   WHAT ACTUALLY OVERFLOWS AT 200% TEXT is max-content sizing in an ANCESTOR, and
   inline content that cannot break. Measured causes, worst first: an unwrapped
   `<table>` (+812 px), a long inline `<code>` run (+960 px), a flex row whose children
   lack `min-width: 0` (+373 px), an implicit grid column (+34 px). Each is its own
   record. The reading measure sits downstream of all of them and is innocent of every
   one, and note that layout.css §grid already uses `minmax(min(…, 100%), 1fr)` for
   precisely the ancestor case, which is where the pattern genuinely belongs.

   THE LESSON WORTH KEEPING: a cap is not a width. Before changing a shared engine
   rule, isolate the mechanism in a throwaway test page and measure it. This
   hypothesis survived a plausible write-up and a 30-consumer baseline, and died to
   six lines of HTML.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Body copy ────────────────────────────────────────────────────────────── */
p { max-width: var(--fk-measure-context, var(--fk-measure)); }
p + p { margin-top: var(--fk-flow); }

strong, b { font-weight: var(--fk-weight-medium); }  /* Book has no bold; step to Medium */
em, i { font-style: italic; }                        /* Geomanist webfont ships italics */
small { font-size: var(--fk-caption-size); }
sup, sub { line-height: 0; }

/* ── Links ────────────────────────────────────────────────────────────────
   Ink text with an underline. On hover the underline picks up the green accent: permitted because an underline is a MARK, not text (colour.md §5). */
a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: var(--fk-border-strong);
  transition: text-decoration-color var(--fk-dur) var(--fk-ease);
}
a:hover { text-decoration-color: var(--fk-accent); }

/* ── Lists ────────────────────────────────────────────────────────────────── */
ul, ol { max-width: var(--fk-measure-context, var(--fk-measure)); padding-left: var(--fk-space-5); }
li + li { margin-top: var(--fk-flow-tight); }
li::marker { color: var(--fk-text-tertiary); }

/* A definition list is prose-bearing and was the element the per-element binding
   missed. `dl` is the container and `dd` the prose; `dt` is a label and is
   deliberately not capped, because a term is not continuous reading. */
dd { max-width: var(--fk-measure-context, var(--fk-measure)); }

/* A caption is continuous prose about a figure, and it reads at the same measure
   as any other prose. It was uncapped for the same reason `dd` was. */
figcaption { max-width: var(--fk-measure-context, var(--fk-measure)); }

/* ── Quotes ───────────────────────────────────────────────────────────────── */
/* A quotation sets wider than body prose (typography.md §6). It consumes the
   context like everything else, and its own default is the wide measure. Its
   children are NOT re-scoped: a paragraph inside a blockquote keeps whatever
   context it inherits, which preserves existing rendering exactly. */
blockquote { max-width: var(--fk-measure-context, var(--fk-measure-wide)); }

/* ── Tables: clean editorial default (design.md: no boxed spreadsheet) ─────── */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fk-body-s-size);
  line-height: var(--fk-body-s-lh);
}
caption { text-align: left; }
th { text-align: left; font-weight: var(--fk-weight-medium); }
th, td {
  padding: var(--fk-space-3) var(--fk-space-4);
  border-bottom: var(--fk-hairline);
  vertical-align: top;
}
thead th {
  font-family: var(--fk-font-text);
  font-size: var(--fk-label-size);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fk-text-tertiary);
  border-bottom: var(--fk-rule);
}
/* Numeric cells: tabular lining figures, right-aligned (typography.md §7). */
td.fk-num, th.fk-num, .fk-num { font-variant-numeric: tabular-nums; }
td.fk-num, th.fk-num { text-align: right; }

hr {
  border: 0;
  border-top: var(--fk-hairline);
  margin-block: var(--fk-space-8);
}

/* There is no brand monospace, so code takes the interface voice with tabular
   figures. `pre` is included so a code BLOCK matches inline `code`, without it
   the block falls back to the system monospace and the page mixes two voices. */
pre {
  font-family: var(--fk-font-text);
  font-variant-numeric: tabular-nums;
}

code, kbd, samp {
  font-family: var(--fk-font-text);
  font-variant-numeric: tabular-nums;
  font-size: 0.9em;
  background: var(--fk-surface-raised);
  border: var(--fk-hairline);
  border-radius: var(--fk-radius-sm);
  padding: 0.1em 0.35em;
}

/* ─────────────────────────────────────────────────────────────────────────────
   A CODE CHIP INSIDE TERTIARY PROSE. `COLOUR-01`, and `PLAT-16` for the count.

   `code` is the one raised surface that does not colour its own text: it paints
   `--fk-surface-raised` under whatever the surrounding prose set, and inherits it.
   THAT MAKES THE USUAL REBIND USELESS HERE. `color` inherits as a COMPUTED value,
   so re-pointing `--fk-text-tertiary` on the chip cannot change what the chip has
   already inherited — the parent resolved the variable before the chip existed.
   Measured on the deploy host 2026-08-27: 54 chips inside tertiary prose at
   **4.38:1** against a 4.5:1 body requirement, dark theme only. The declaration
   must therefore come from ABOVE, naming the chip.

   ONLY PROSE-BEARING TERTIARY ROLES ARE LISTED. Roughly forty engine rules resolve
   to tertiary; the great majority are labels, values and badges that never contain
   a code span, and enumerating them would be the repetition the raised-surface note
   in components.css exists to avoid. A NEW TERTIARY ROLE THAT CARRIES PROSE JOINS
   THIS LIST: that is the whole maintenance obligation.

   On the light theme `--fk-text-tertiary-raised` and `--fk-text-tertiary` are one
   value, so nothing here moves. The change is dark-only, and it is 4.38 → 6.40.
   ───────────────────────────────────────────────────────────────────────────── */
:is(
  .fk-caption,
  .fk-metadata,
  .fk-footnote,
  .fk-figure__caption, figcaption,
  .fk-label,
  .fk-text-tertiary,
  th
) :is(code, kbd, samp) {
  color: var(--fk-text-tertiary-raised);
}

/* ── Focus: 2px ink ring at 2px offset (typography.md §14; green is illegal
      as a focus colour at 2.65:1) ────────────────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--fk-focus-ring);
  outline-offset: 2px;
  border-radius: var(--fk-radius-sm);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--fk-accent); color: var(--fk-c-ink-1); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE SCROLL AREA: in-page horizontal overflow
   ---------------------------------------------------------------------------
   A wide table, a specimen strip, a code block. The default scrollbar is a piece
   of operating-system furniture: heavy, rounded, and loud beside hairline rules
   and a restrained type scale. This is the system's own.

   IT DOES NOT TOUCH THE VIEWPORT SCROLLBAR, and that is the whole reason the
   selector is `body *` rather than `*` or `:root`. Both `scrollbar-width` and
   `scrollbar-color` INHERIT, so declaring them on `html` or `body` would style
   the page's own scrollbar, the reader's furniture, not ours. Excluding those
   two elements is the difference between styling our components and restyling
   somebody's browser.

   WHY A UNIVERSAL SELECTOR RATHER THAN A LIST OF CLASSES. Measured across the
   deployed tree, seven distinct containers scroll horizontally, and three of
   them are chapter-local classes (`.ty-rung__scroll`, `.ty-duplex`, `.ty-measure`)
   declared inside a chapter's own embedded stylesheet. Naming them here would put
   publication class names into platform CSS, and editing them there would copy
   this treatment into three files. One rule that says "any in-page scroller"
   reaches all of them and duplicates nothing.

   `:where()` keeps specificity at zero, so any component can still opt out.

   NO ANIMATION, no shadow, no gradient, no pill. */
:where(body *) {
  scrollbar-width: thin;                                   /* Firefox */
  scrollbar-color: var(--fk-scrollbar-thumb) transparent;  /* thumb, then track */
}

/* WebKit and Blink: Safari and the Chromium family. A separate mechanism from
   the two properties above, not a fallback for them: an engine takes one or the
   other, so both are declared and neither is conditional. */
body *::-webkit-scrollbar {
  block-size: var(--fk-scrollbar-size);
  inline-size: var(--fk-scrollbar-size);
}

/* THE TRACK IS A LINE, NOT A TROUGH. Transparent ground with a single hairline
   along the top edge, so it reads as one more rule in a system built from them
   rather than as a sunken channel. */
body *::-webkit-scrollbar-track {
  background: transparent;
  border-block-start: 1px solid var(--fk-scrollbar-track);
}

/* THE THUMB IS A RECTANGULAR BLOCK. Flat ends: `border-radius: 0` is stated
   rather than assumed, because the UA default is a capsule.

   The transparent border inset is what makes the thumb thinner than the band it
   travels in: the visible block is 4px while the DRAG TARGET stays the full 8px.
   Shrinking the target instead would have been the obvious way to get a thin
   thumb and the wrong one. */
body *::-webkit-scrollbar-thumb {
  background: var(--fk-scrollbar-thumb);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: 0;
}
body *::-webkit-scrollbar-thumb:hover  { background: var(--fk-scrollbar-thumb-hover);
                                         background-clip: padding-box; }
body *::-webkit-scrollbar-thumb:active { background: var(--fk-scrollbar-thumb-hover);
                                         background-clip: padding-box; }
body *::-webkit-scrollbar-corner { background: transparent; }

/* FORCED COLOURS: hand it back. In a forced-colours mode the reader has told the
   operating system what contrast they need, and a restrained grey thumb is
   exactly the kind of decision that stops being restrained and starts being
   unusable. The browser's own treatment wins. */
@media (forced-colors: active) {
  :where(body *) { scrollbar-width: auto; scrollbar-color: auto; }
  body *::-webkit-scrollbar-track { border-block-start: 0; }
  body *::-webkit-scrollbar-thumb { background: revert; border: 0; }
}

/* THE CANONICAL SCROLL AREA, for anything new that needs one.
   `.fk-table-wrap` is its table-specific sibling and predates it (components.css).
   The padding keeps the content off the scrollbar so the two do not read as one
   object, the typography chapter arrived at the same conclusion independently
   and set it locally on `.ty-rung__scroll`.

   GIVE IT `tabindex="0"`. A scrolling region that cannot take focus cannot be
   scrolled by a keyboard-only reader at all: the pointer and the trackpad are
   the only ways in. The attribute is markup, so this stylesheet cannot supply it,
   and it is named here because this is where someone reaches for the class.
   Measured 2026-07-29: `.ty-rung__scroll` in the typography chapter has this
   defect today. It predates this treatment and is recorded rather than repaired,
   because that chapter is protected. */
.fk-scroll-x {
  overflow-x: auto;
  overflow-y: hidden;
  max-width: 100%;
  padding-block-end: var(--fk-space-2);
}

/* ── Accessibility helpers ────────────────────────────────────────────────── */
.fk-sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
.fk-skip-link {
  position: absolute;
  left: var(--fk-space-4); top: -4rem;
  z-index: 100;
  background: var(--fk-text-primary);
  color: var(--fk-surface);
  padding: var(--fk-space-3) var(--fk-space-4);
  border-radius: var(--fk-radius-md);
  text-decoration: none;
  transition: top var(--fk-dur) var(--fk-ease);
}
.fk-skip-link:focus { top: var(--fk-space-4); }

/* ═══════════════════════════════════════════════════════════════════════════
   TABLE — the scroll container and the compact variant. `component-architecture.md`
   §10.2, moved here from components.css on 2026-08-31.
   ═══════════════════════════════════════════════════════════════════════════
   MOST OF TABLE IS ALREADY HERE and always was: the bare `table`, `th`, `td` and
   `caption` rules above give every consumer the same collapsed borders, the same
   hairline row rule and the same cell padding. `.fk-table` is an empty marker for
   exactly that reason — it inherits, it does not restate.

   WHAT WAS STRANDED was the part that matters at a narrow viewport. `.fk-table-wrap`
   lived in `components.css`, a consumer layer the brand-system site does not load, so
   the site had no scroll container. **On the morning of 2026-08-31 that cost a real
   defect:** the imagery chapter's ground-library table measured 472 px wide in a
   320 px viewport at 200% text and pushed the page 192 px sideways — the worst
   overflow on the site. The fix was `.si-table-wrap`, a duplicate written because
   `.fk-table-wrap` could not be reached. This is that duplicate resolved.

   A SCROLL CONTAINER IS THE CORRECT ANSWER for a data table, not a smaller font or a
   dropped column. WCAG 1.4.10 exempts content requiring two-dimensional layout from
   reflow, and the relationship between a row and its column headers IS the content.
   What is never permitted is the PAGE scrolling sideways instead.

   `overscroll-behavior-x: contain` came from the site's version and is kept: without
   it, reaching the end of the table's horizontal scroll chains to the page, and on a
   trackpad that reads as a back-navigation gesture. */
.fk-table-wrap {
  overflow-x: auto;
  max-width: 100%;
  overscroll-behavior-x: contain;
}
/* The wrapper owns the outer rhythm so the table does not double it. */
.fk-table-wrap > table { margin-block: 0; }

/* An empty marker, and deliberately so: a table styled by the element rules above
   needs no class, and this exists to be named in markup that wants to be explicit. */
.fk-table { /* inherits the base table styling above */ }

.fk-table--compact :is(th, td) {
  padding: var(--fk-space-2) var(--fk-space-3);
  font-size: var(--fk-footnote-size);
}

/* ═══════════════════════════════════════════════════════════════════════════
   METRIC — a labelled figure. `component-architecture.md` §10.6, built 2026-08-31.
   ═══════════════════════════════════════════════════════════════════════════
   *The single number a page wants remembered.* Its responsibilities are the label
   and value pairing, the rule above it, tabular alignment, and the relationship to a
   supporting qualifier. Its NON-responsibilities are comparison, trend, sparklines,
   and anything a chart does: **a metric states one number.**

   WHY IT IS BUILT NOW. §10.6 said it moves "when this component exists, and not
   before", and §14 scheduled it after Quotation. What made the case is evidence
   rather than the schedule: the brand-system site had built `.si-figure`, a local
   Metric, because none existed. That is the same shape as Button — a specified
   universal component that no consumer could reach, so a consumer built its own.

   WHY IT IS IN base.css, not components.css. Button's move on the same day proved
   the point: `components.css` is a CONSUMER layer, and a universal component placed
   there is unreachable by anything that does not load report anatomy. Metric's type
   roles are already universal — `.fk-metric-value` and `.fk-metric-label` are in
   typography.css, which every consumer loads — so only the structure was missing.

   WHAT THE LOCAL COPY HAD LOST, and this is the reason the duplication mattered
   rather than merely being untidy: `.si-figure__value` reproduced five of the type
   role's declarations and **dropped `font-variant-numeric: tabular-nums lining-nums`.**
   The site renders `33,844` beside `7` in a four-column grid, which is exactly where
   proportional figures misalign. §10.6 names tabular alignment a responsibility of
   this component; the copy silently was not meeting it. Using the role rather than
   restating it is what makes that impossible to lose again.

   THE VALUE TAKES THE TYPE ROLE `.fk-metric-value` in the markup. This component
   supplies structure, rhythm and the qualifier; it does not restate type. */
.fk-metrics {
  display: grid;
  /* THE SINGLE-COLUMN CASE IS DECLARED, not implicit. An implicit column is sized
     `auto`, which resolves to min-content and cannot shrink below its widest child:
     at 200% text that grew to 346 px inside a 320 px viewport and pushed the page
     sideways. `lint-resize.py` R1 exists for this exact construct. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--fk-space-6);
  margin: 0;
  --fk-measure-context: none;
}
@media (min-width: 40rem) { .fk-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) { .fk-metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* THE RULE ABOVE IT is §10.6's, and it is a hairline rather than a box: a metric is
   an entry in a series, not a card. `component-architecture.md` §3 is explicit that
   a card is a container and says nothing a heading and space do not. */
.fk-metric {
  border-block-start: var(--fk-hairline);
  padding-block-start: var(--fk-space-4);
}

.fk-metric__label {
  margin: 0;
  margin-block-start: var(--fk-space-3);
  display: block;
  font-size: var(--fk-body-s-size);
  line-height: var(--fk-body-s-lh);
  font-weight: var(--fk-weight-medium);
}

/* THE SUPPORTING QUALIFIER. §10.6 calls it "the relationship to a supporting
   qualifier" and it is deliberately inside the `<dd>`: it qualifies the label, and a
   screen reader should read the two as one description rather than as two. */
.fk-metric__note {
  display: block;
  margin-block-start: var(--fk-space-2);
  font-size: var(--fk-caption-size);
  line-height: var(--fk-caption-lh);
  letter-spacing: var(--fk-caption-track);
  font-weight: var(--fk-caption-weight);
  color: var(--fk-text-secondary);
}

/* ═══════════════════════════════════════════════════════════════════════════
   BUTTON — THE UNIVERSAL CONTROL. Moved here from the engine's components.css on
   2026-08-31, and merged with the site's `.si-btn` in the same move.
   ═══════════════════════════════════════════════════════════════════════════
   WHY IT IS IN base.css. `component-architecture.md` §10.3 calls Button a universal
   concept, and that reasoning stands. **Its §4 does NOT justify this placement and was
   wrongly cited here at first:** §4 says a universal component goes in `core.css` §6,
   and that `core.css` was removed on 2026-08-17 — today's is GENERATED from these
   files, so nothing is placed in it directly. That document's own scope note says it
   describes the removed lineage and is not a description of what is published now.

   THE REASON THAT ACTUALLY HOLDS comes from the tree rather than a document: **a
   component both consumers need must live in a layer both consumers load.** Button was
   in `components.css`, a CONSUMER layer of 84 report-anatomy classes that the
   brand-system site does not load. So the site could not reach Button and
   built its own, and the system rendered two different buttons: `inline-flex`
   against `inline-block`, outlined-by-default against filled-by-default, on 2
   routes and 19 routes respectively.

   That is §2's fifth test failing inside one repository: *if two products would
   independently invent it and get it slightly different, the brand has already
   lost.* base.css is where the engine keeps what every consumer needs — the skip
   link, the focus ring, `.fk-themetoggle` — and the toggle moved here on 2026-07-27
   after the identical problem. This follows that precedent rather than inventing one.

   WHICH IMPLEMENTATION WON, AND WHY IT WAS NOT THE ENGINE'S. Three behaviours come
   from the site's version because they were MEASURED and the engine's were not:

     `max-width: 100%` + `overflow-wrap`   at 200% text "Read the foundations" is
                                           261 px inside a 216 px panel column. A
                                           two-line label beats a sideways page
     `line-height: var(--fk-body-s-lh)`    the engine set `1`, which collides the
                                           two lines the rule above now permits
     hover fills with `--fk-text-secondary` the engine filled with
                                           `--fk-surface-raised`, which is #FCFCFB
                                           on #F6F4F1 paper: **1.07:1**, a no-op
                                           with a token name

   POLARITY IS THE ENGINE'S: outlined at rest, `--primary` for the filled variant.
   The site had it inverted. The engine's is the more conventional reading and it
   already carries `--ghost` and `--sm`, which the Colour chapter's greyscale
   control uses. Inverting the engine to suit the site would have broken that.

   Both hovers land on the same ground, and that is deliberate: the palette has two
   neutral grounds on light and no third, so the honest options were a ground a
   reader can actually see or a border change alone. They stay distinguishable at
   rest, where the filled button sits two steps darker (2.23:1 between the grounds),
   and no two buttons are ever hovered at once.

   Measured, label against its hovered ground:
     light   paper #F6F4F1 on ink-2 #4A4A4A    8.07:1
     dark    ink   #090909 on rev-2 #ADADAD    8.87:1 */
.fk-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--fk-space-2);
  /* A LABEL MAY WRAP AND A BUTTON MAY NOT EXCEED ITS CONTAINER. Both matter only at
     200% text and neither changes anything at normal size. `lint-resize.py` R2 holds
     the `overflow-wrap` as a guard: losing it brings the overflow back silently. */
  max-width: 100%;
  overflow-wrap: break-word;
  font-family: var(--fk-font-text);
  font-weight: var(--fk-weight-medium);
  font-size: var(--fk-body-s-size);
  line-height: var(--fk-body-s-lh);
  letter-spacing: var(--fk-body-s-track);
  padding: var(--fk-space-3) var(--fk-space-5);
  border: 1px solid var(--fk-border-strong);
  border-radius: var(--fk-radius-md);      /* architectural, not pill */
  background: none;
  color: var(--fk-text-primary);
  cursor: pointer;
  text-decoration: none;
  /* Under `prefers-reduced-motion: reduce` this file neutralises every transition
     globally, so the end state still arrives and only the travel is removed. */
  transition: background var(--fk-dur) var(--fk-ease),
              border-color var(--fk-dur) var(--fk-ease),
              color var(--fk-dur) var(--fk-ease);
}

/* An anchor carries `:link` and `:visited`, and this file gives every `a` an
   underline the component must override in all of them. Enumerated rather than left
   to the shorthand, and no `!important`: a class selector already outranks `a`. */
.fk-btn,
.fk-btn:link,
.fk-btn:visited,
.fk-btn:hover,
.fk-btn:focus,
.fk-btn:focus-visible,
.fk-btn:active { text-decoration: none; }

/* The outlined button fills, and its label flips from ink to paper: a background AND
   a foreground change, which survives greyscale by construction. */
.fk-btn:hover {
  background: var(--fk-text-secondary);
  border-color: var(--fk-text-secondary);
  color: var(--fk-surface);
}

.fk-btn--primary {
  background: var(--fk-text-primary);
  color: var(--fk-surface);
  border-color: var(--fk-text-primary);
}
/* The filled button lightens from primary ink to secondary. NOT A HUE SIGNAL: the
   palette is monochrome here, so the change is a shift in ink weight. */
.fk-btn--primary:hover {
  background: var(--fk-text-secondary);
  border-color: var(--fk-text-secondary);
}

.fk-btn--ghost { border-color: transparent; }
.fk-btn--sm {
  padding: var(--fk-space-2) var(--fk-space-3);
  font-size: var(--fk-footnote-size);
}

/* ═══════════════════════════════════════════════════════════════════════════
   FORM CONTROLS — completing the element layer, 2026-09-01
   ---------------------------------------------------------------------------
   A HOLE RATHER THAN A NEW COMPONENT SYSTEM, and the difference decides how much
   belongs here. This file already styles roughly twenty-eight native elements
   substantively: `a`, `table`, `th`, `td`, `ul`, `ol`, `p`, `pre`, `code`, `hr`,
   `img`, `blockquote`, `figcaption`. Form controls had the font reset at the top
   of this file and nothing else, so they rendered as operating-system defaults
   wearing brand type. `--fk-radius-md` has carried the comment "inputs, small
   panels" since the scale was written: the token was declared for a control
   nobody had built.

   SO IT IS ELEMENT-LEVEL AND STOPS THERE. No `.fk-field`, no `.fk-form`, no
   `.fk-input`, no variants. `component-architecture.md`: "a variant is a new
   decision, not a small one … variants are how a seven-component system becomes
   a forty-component system without anyone deciding to." A product composes
   labels, help text and layout; the brand says what a control looks like.

   FOCUS IS ALREADY CORRECT AND IS NOT REDECLARED. The global `:focus-visible`
   above gives every control the 2px ink ring at 2px offset. A second focus
   treatment here would be a second source of truth for one behaviour.

   NO COLOUR CARRIES AN ERROR. `colour.md` §8.1: status survives greyscale,
   colour-vision deficiency and a photocopier because it is carried by geometry
   and a text label, never by hue, and §11 rejects a red/amber/green status
   palette outright. An invalid field is therefore its message plus a heavier
   border. No red enters this system.

   CHECKBOXES AND RADIOS ARE LEFT NATIVE, DELIBERATELY. `accent-color` would tint
   them with the green, and the tick the browser draws inside that fill is a
   contrast this system cannot measure — `CLAUDE.md` does not permit an
   approximated ratio. The native control is accessible by construction, so it
   keeps the platform's own. They are excluded by type below rather than reset
   afterwards.

   44px MINIMUM, Fernando's decision 2026-09-01. WCAG 2.2 AAA target size and the
   convention on both mobile platforms. `min-block-size` rather than `height`, so
   the control grows with its text at 200% instead of clipping it, which is the
   failure `EXAMPLE-01` exists to catch.

   `.fk-label` IS NOT A FORM LABEL. It is the eyebrow type role, and putting it on
   a `<label>` would give a field the eyebrow's size and tracking. A form label is
   ordinary text; `.fk-body-s` if it needs a role at all.
   ═══════════════════════════════════════════════════════════════════════════ */
input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"],
           [type="image"], [type="submit"], [type="reset"], [type="button"]),
textarea,
select {
  padding: var(--fk-space-3) var(--fk-space-4);
  min-block-size: 44px;
  max-width: 100%;
  /* `min-width: 0` because an <input> carries an INTRINSIC width from its `size`
     attribute — about twenty characters — and a grid or flex item defaults to
     `min-width: auto`, which refuses to shrink below that. `max-width: 100%`
     cannot save it: the control's min-content contribution widens the container
     itself, so the whole column grows instead. Measured 2026-09-01 at 200% text
     in a 320px viewport: 410px controls in a 320px column, 154px of overflow.
     Same defect and same fix as `.si-band` and `fieldset` below.

     CONSUMER RESPONSIBILITY, STATED SO IT IS NOT A TRAP. This shrinks the control
     itself. A WRAPPER the product puts around it — a grid or flex item holding a
     label and a field — keeps `min-width: auto` and will still refuse to shrink,
     because a control's min-content contribution is its `size`, not its
     `min-width`. Measured: with a wrapping div, 154px of overflow at 320px and
     200% text; the product adding `min-width: 0` to that wrapper takes it to 0,
     and so does `width: 100%` on the field. This file declines to force
     `width: 100%` because how wide a field sits is layout, and a postcode is not
     a full-width field. `.si-band` in this repository's own site layer carries
     exactly this declaration for exactly this reason. */
  min-width: 0;
  border: 1px solid var(--fk-border-strong);
  border-radius: var(--fk-radius-md);
  background: var(--fk-surface);
  color: var(--fk-text-primary);
}

/* Vertical only. A horizontally resizable textarea can be dragged wider than its
   column, and horizontal overflow is the assertion that stays at zero. */
textarea { resize: vertical; }

/* Firefox ships a placeholder below full opacity, which would drop it under the
   ratio measured for this token: `text-tertiary` is 5.64:1 on paper and 6.08:1 on
   ink. A placeholder is legible here, not exempt. */
::placeholder { color: var(--fk-text-tertiary); opacity: 1; }

/* `colour.md` §14 records a disabled control as exempt from the gate. This clears
   it anyway on both grounds, so no reader is punished for the exemption. */
:disabled { color: var(--fk-text-tertiary); cursor: not-allowed; }

/* AFTER the reader has typed, never before. `:invalid` matches an empty required
   field on first paint, which scolds someone for not having started.

   TYPED OUT PER ELEMENT rather than left bare, and that is not style. A bare
   `:user-invalid` is specificity (0,1,0); the control rule above is (0,1,1)
   because `:not()` takes its most specific argument. The bare form lost, its
   `border` shorthand reset the width, and an invalid field looked identical to a
   valid one. Caught by measuring the rendered border, not by reading the file. */
input:user-invalid,
textarea:user-invalid,
select:user-invalid { border-width: 2px; }

/* `min-width: 0` because a fieldset resolves to min-content by default and will
   push a wide child past the page edge instead of letting it shrink. */
fieldset {
  border: var(--fk-hairline);
  border-radius: var(--fk-radius-md);
  padding: var(--fk-space-4);
  min-width: 0;
}
legend { padding-inline: var(--fk-space-2); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE THEME CONTROL: the one control every FK digital publication shares
   ---------------------------------------------------------------------------
   IT LIVES IN base.css, NOT components.css, AND THE REASON IS THE SITE LAYER.
   `components.css` is report anatomy: 84 classes for findings, score lines and
   roadmaps, and the Brand System website deliberately does NOT link it
   (site/CLAUDE.md §1: "NOT inherited: layout, components, print"). A control defined
   there could not be shared with the site, so the site would keep its own copy: which
   is the exact state this change exists to end. Two implementations of one control is
   how the engine came to have a text button and the site a mark-plus-word, neither
   matching the other.

   base.css already holds this class of thing: the skip link and the focus ring are
   cross-media affordances every consumer needs, not document anatomy. A theme control
   is the same kind of object.

   ICON-ONLY, and that is the decision rather than a saving. The word "Theme" told a
   reader nothing the mark does not, "Dark" and "Light" are ambiguous about whether
   they name the current state or the destination, and the word was the single item
   that pushed the publication bar past 320px on five reports (`PLAT-15`, layout.css).

   THE MARK is a circle split down the middle, half filled with ink and half open
   the system's own "partial" vocabulary, and an obvious light/dark switch. It was
   already implemented twice: once in the site layer as `.si-theme__mark` and once
   nowhere at all in the engine, where the control was a text button. This is now the
   single definition and the site layer consumes it.

   IT FLIPS ON CHANGE. The filled half moves from left to right, so the two halves
   exchange sides and the control shows that something happened at the exact moment the
   ground changes underneath it. `currentColor` means the mark inverts with the theme
   for free. Under `prefers-reduced-motion` the flip is instant, not absent: the end
   state carries meaning, only the travel is decoration.

   THE ACCESSIBLE NAME NAMES THE ACTION, and it changes with the theme
   "Switch to dark mode" while light is active, "Switch to light mode" while dark is.

   THIS SUPERSEDES THE EARLIER ARRANGEMENT, which was a fixed name plus `aria-pressed`,
   and the reason is that the two patterns must not be combined. A control announcing
   "Switch to light mode, pressed" is worse than either alone. So the state moved into
   the name and `aria-pressed` was removed, which is the second of the two arrangements
   the ARIA practices describe for a toggle. The hazard the old note recorded is still
   real and still avoided: **the visible tooltip is never the accessible name.** It is
   generated content, the name is `aria-label`, and the two are set from one string in
   the same place so they cannot drift.

   TARGET SIZE: `PLAT-06` is still open: no target-size number is specified anywhere in
   the foundations (`accessibility.md §Interaction` is outstanding). The control is sized
   from the space tokens; the measured box is recorded in the publication's validation
   record rather than claimed as compliant here, because there is no canonical number to
   compare it against and inventing one is what the open item exists to prevent. */
.fk-themetoggle {
  position: relative;                  /* the tooltip's containing block */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: calc(var(--fk-space-4) + var(--fk-space-5));   /* 16 + 24 = 40 */
  block-size: calc(var(--fk-space-4) + var(--fk-space-5));
  padding: 0;
  background: transparent;
  /* NO BORDER AT REST. It read as a secondary call to action sitting beside the
     navigation, when it is a utility that acts on the page you are already reading.
     The hit area is unchanged, the box is still 40 px; only its outline is gone. */
  border: 0;
  border-radius: var(--fk-radius-pill);
  color: var(--fk-text-primary);
  cursor: pointer;
  transition: background var(--fk-dur) var(--fk-ease), color var(--fk-dur) var(--fk-ease);
}

/* HOVER: a disc materialises under the mark.
   `--fk-border` rather than `--fk-surface-raised`, and the choice was measured: on the
   light ground `--fk-surface-raised` is #FCFCFB against a #F6F4F1 page, which is
   1.07:1 and invisible. `--fk-border` is #E4E3E8, 1.16:1, the strongest neutral the
   palette has on light, and on dark the two tokens are the same #2A2A2A (1.39:1)
   anyway. The mark stays at full strength on it: 15.51:1 light, 13.73:1 dark. */
.fk-themetoggle:hover { background: var(--fk-border); }

/* ACTIVE: the press is firm, and it borrows the button language rather than
   inventing a third: the same `--fk-text-secondary` fill the site's buttons take on
   hover, with the mark inverting to the surface colour. 8.07:1 light, 8.87:1 dark. */
.fk-themetoggle:active {
  background: var(--fk-text-secondary);
  color: var(--fk-surface);
}

/* ── THE TOOLTIP ────────────────────────────────────────────────────────────
   Generated content, so it is decoration and never the accessible name. That is
   `aria-label`, set on the same element from the same string.

   THE TEXT IS SELECTED BY THE THEME ATTRIBUTE, NOT WRITTEN BY SCRIPT. Both strings
   live in the DOM as data attributes and CSS picks the one that names what a click
   will do. So the tooltip is correct on the FIRST PAINT of a reload: including the
   reload where the anti-flash script has already applied a saved dark theme before
   any other script has run, which a JS-written tooltip could not guarantee.

   RIGHT-ANCHORED, NOT CENTRED. The control is the last thing in the bar, so a centred
   tooltip would overflow the viewport on a narrow screen. `inset-inline-end: 0` keeps
   its trailing edge on the control's, where there is always room to its left.

   It is absolutely positioned, so it cannot move anything around it. */
.fk-themetoggle::after {
  content: attr(data-tip-to-dark);
  position: absolute;
  inset-block-start: calc(100% + var(--fk-space-2));
  inset-inline-end: 0;
  z-index: 30;
  padding: var(--fk-space-2) var(--fk-space-3);
  font-family: var(--fk-font-text);
  font-size: var(--fk-footnote-size);
  font-weight: var(--fk-weight-regular);
  line-height: 1.3;
  letter-spacing: 0.006em;
  white-space: nowrap;
  background: var(--fk-text-primary);
  color: var(--fk-surface);
  border-radius: var(--fk-radius-sm);
  opacity: 0;
  transform: translateY(-2px);
  pointer-events: none;             /* it can never intercept the click it describes */
  transition: opacity var(--fk-dur) var(--fk-ease),
              transform var(--fk-dur) var(--fk-ease);
}
[data-theme="dark"] .fk-themetoggle::after { content: attr(data-tip-to-light); }

/* Shown on pointer AND on keyboard focus. `:focus-visible` only, so a mouse press
   does not leave a tooltip hanging after the click. */
.fk-themetoggle:hover::after,
.fk-themetoggle:focus-visible::after {
  opacity: 1;
  transform: none;
}
.fk-themetoggle__mark {
  inline-size: 1rem;
  block-size: 1rem;
  border: 1px solid currentColor;
  border-radius: var(--fk-radius-pill);
  /* Half ink, half paper. A gradient rather than two boxes so one element carries
     the whole mark and there is nothing to keep in sync. */
  background: linear-gradient(to right, currentColor 0 50%, transparent 50% 100%);
  flex: none;
  transition: transform var(--fk-dur) var(--fk-ease);
}
/* THE FLIP. Rotating the mark exchanges the halves; the circle is radially symmetric
   so the rotation is invisible except as the swap. Driven by the theme attribute, so
   it is correct on first paint after a reload too, not only after a click. */
[data-theme="dark"] .fk-themetoggle__mark { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .fk-themetoggle__mark { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BLEED — the fifth band, published 2026-09-01 when its contract was approved
   ---------------------------------------------------------------------------
   `grid-layout-rhythm.md` §3.5, approved as Version 7 on 2026-09-01. Four of the
   five bands have shipped here all along; this is the last, and it was withheld
   for a stated reason rather than by oversight. `GRID-02` left the contract
   undefined, so the escape lived in the site layer with nothing composed against
   it, and the masthead and footer were its only users.

   TWO ELEMENTS, AND THAT IS THE WHOLE CONTRACT. The outer crosses the page edge
   and carries the surface. The inner re-establishes the page edge and holds the
   content. Neither does the other's job, and alignment is the reason: the inner
   is an ordinary band that has never heard of bleed, so a bleeding section lines
   up with its neighbours by construction rather than by two rules kept in
   agreement by hand.

   MEASURED FROM THE CONTAINER, NEVER FROM THE VIEWPORT. `100vw` is wider than the
   container whenever a classic scrollbar is present, and that difference is
   horizontal overflow on assertion 1, which has to stay at zero from 320 to
   1600 px. Negating the page-edge token is exact at every viewport and at 200%
   text, because the escape and the restoration resolve from one value.

   NO `min(…, 100%)` ON THE INNER CAP, DELIBERATELY. `PLAT-14` above is tested and
   refuted: `max-width` cannot make a block wider than its containing block, so a
   cap larger than the container is inert. The site file keeps that pattern for a
   measured local reason; it does not belong here.

   SCOPED TO PUBLIC PAGES BY THE CHAPTER, NOT BY THIS FILE. §3.5: no documentation
   chapter uses bleed, and none should. CSS cannot enforce that. The print rule
   below is what makes a printed public page behave like the document it becomes.

   NO PAUSE OF ITS OWN. A bleed takes the pause its relationship calls for (§4).
   The padding here is the distance between the surface's edge and the content
   inside it, which is a different measurement, and conflating the two is how a
   rhythm acquires exceptions.
   ═══════════════════════════════════════════════════════════════════════════ */
.fk-bleed {
  margin-inline: calc(var(--fk-page-edge) * -1);
  padding-inline: var(--fk-page-edge);
}

/* The content column, centred. A narrower band may be nested inside when the
   content calls for one, which is why this caps a width and sets no measure
   context: §3.6 chooses a band by content role, not by what contains it. */
.fk-bleed__inner {
  margin-inline: auto;
  max-width: var(--fk-content-max);
}

/* A document has no reason to reach the paper's edge (§3.5), and a printed public
   page is a document. The inner needs no rule: it was never the thing bleeding. */
@media print {
  .fk-bleed { margin-inline: 0; padding-inline: 0; }
}

/* ── Print / screen visibility switches ───────────────────────────────────── */
.fk-print-only { display: none; }
@media print {
  .fk-no-print { display: none !important; }
  .fk-print-only { display: revert; }
}


/* =============================================================================
   Fernando Kylas Report Engine: Typographic Roles
   -----------------------------------------------------------------------------
   Every role below is documented in DESIGN.md §Typography with its family,
   weight, size, line-height, tracking, use, print and responsive behaviour.
   Values come from foundations/typography.md; report-specific exceptions are
   noted there and in IMPLEMENTATION.md.

   Two hard rules from the foundation are enforced here:
     • ONE display voice per layout, never Strawford display AND Geomanist Bold
       display in the same section (typography.md §1). Roles keep them separate.
     • Below ~28px the two families are near-indistinguishable, so structural
       headings at that size use Geomanist Medium, not Strawford (§3.2).
   ============================================================================= */

/* ── Display / editorial: Strawford ──────────────────────────────────────── */
.fk-display {
  font-family: var(--fk-font-display);
  font-weight: var(--fk-display-weight);
  font-size: var(--fk-display-size);
  line-height: var(--fk-display-lh);
  letter-spacing: var(--fk-display-track);
  color: var(--fk-text-primary);
  text-wrap: balance;
}

.fk-report-title {
  font-family: var(--fk-font-display);
  font-weight: var(--fk-report-title-weight);
  font-size: var(--fk-report-title-size);
  line-height: var(--fk-report-title-lh);
  letter-spacing: var(--fk-report-title-track);
  color: var(--fk-text-primary);
  text-wrap: balance;
}

.fk-report-subtitle {
  font-family: var(--fk-font-text);
  font-weight: var(--fk-lead-weight);
  font-size: var(--fk-lead-size);
  line-height: var(--fk-lead-lh);
  letter-spacing: var(--fk-lead-track);
  color: var(--fk-text-secondary);
  max-width: var(--fk-measure-lead);
  text-wrap: pretty;
}

.fk-section-title {
  font-family: var(--fk-font-display);
  font-weight: var(--fk-section-title-weight);
  font-size: var(--fk-section-title-size);
  line-height: var(--fk-section-title-lh);
  letter-spacing: var(--fk-section-title-track);
  color: var(--fk-text-primary);
  text-wrap: balance;
}

.fk-subsection-title {
  font-family: var(--fk-font-text);
  font-weight: var(--fk-subsection-weight);
  font-size: var(--fk-subsection-size);
  line-height: var(--fk-subsection-lh);
  letter-spacing: var(--fk-subsection-track);
  color: var(--fk-text-primary);
}

.fk-card-title {
  font-family: var(--fk-font-text);
  font-weight: var(--fk-card-title-weight);
  font-size: var(--fk-card-title-size);
  line-height: var(--fk-card-title-lh);
  letter-spacing: var(--fk-card-title-track);
  color: var(--fk-text-primary);
}

/* Editorial pull quote: Strawford. Opening quote hangs outside the measure
   (typography.md §7). Provided via .fk-quote > p and a hanging-punctuation hint. */
.fk-quote {
  font-family: var(--fk-font-display);
  font-weight: var(--fk-quote-weight);
  font-size: var(--fk-quote-size);
  line-height: var(--fk-quote-lh);
  letter-spacing: var(--fk-quote-track);
  color: var(--fk-text-primary);
  max-width: var(--fk-measure-wide);
  hanging-punctuation: first;
  text-wrap: pretty;
}

/* ── Reading: Geomanist text scale ───────────────────────────────────────── */
.fk-lead {
  font-family: var(--fk-font-text);
  font-weight: var(--fk-lead-weight);
  font-size: var(--fk-lead-size);
  line-height: var(--fk-lead-lh);
  letter-spacing: var(--fk-lead-track);
  color: var(--fk-text-secondary);
  max-width: var(--fk-measure-lead);
  text-wrap: pretty;
}
.fk-body-l {
  font-size: var(--fk-body-l-size);
  line-height: var(--fk-body-l-lh);
  letter-spacing: var(--fk-body-l-track);
  font-weight: var(--fk-body-l-weight);
  max-width: var(--fk-measure);
}
.fk-body {
  font-size: var(--fk-body-size);
  line-height: var(--fk-body-lh);
  letter-spacing: var(--fk-body-track);
  font-weight: var(--fk-body-weight);
  max-width: var(--fk-measure);
}
.fk-body-s {
  font-size: var(--fk-body-s-size);
  line-height: var(--fk-body-s-lh);
  letter-spacing: var(--fk-body-s-track);
  font-weight: var(--fk-body-s-weight);
}

/* ── Metadata, captions, footnotes, labels ────────────────────────────────── */
.fk-caption {
  font-size: var(--fk-caption-size);
  line-height: var(--fk-caption-lh);
  letter-spacing: var(--fk-caption-track);
  font-weight: var(--fk-caption-weight);
  color: var(--fk-text-tertiary);
}
.fk-metadata {
  font-size: var(--fk-caption-size);
  line-height: var(--fk-caption-lh);
  letter-spacing: 0.006em;
  font-weight: var(--fk-caption-weight);
  color: var(--fk-text-tertiary);
}
.fk-footnote {
  font-size: var(--fk-footnote-size);
  line-height: var(--fk-footnote-lh);
  letter-spacing: var(--fk-footnote-track);
  font-weight: var(--fk-footnote-weight);
  color: var(--fk-text-tertiary);
}
.fk-label {
  display: inline-block;
  font-family: var(--fk-font-text);
  font-size: var(--fk-label-size);
  line-height: var(--fk-label-lh);
  letter-spacing: var(--fk-label-track);
  font-weight: var(--fk-label-weight);
  text-transform: uppercase;
  color: var(--fk-text-tertiary);
}

/* ── Statistics / metric values: Geomanist Bold, tabular lining figures ───── */
.fk-metric-value {
  font-family: var(--fk-font-text);
  font-weight: var(--fk-metric-weight);
  font-size: var(--fk-metric-size);
  line-height: var(--fk-metric-lh);
  letter-spacing: var(--fk-metric-track);
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--fk-text-primary);
}
.fk-metric-value--lg {
  font-size: var(--fk-metric-lg-size);
  line-height: var(--fk-metric-lg-lh);
  letter-spacing: var(--fk-metric-lg-track);
}
.fk-metric-label {
  font-family: var(--fk-font-text);
  font-size: var(--fk-label-size);
  line-height: var(--fk-label-lh);
  letter-spacing: var(--fk-label-track);
  font-weight: var(--fk-label-weight);
  text-transform: uppercase;
  color: var(--fk-text-tertiary);
}

/* ── Colour modifiers (semantic only, never a raw hex on a node) ──────────── */
.fk-text-secondary { color: var(--fk-text-secondary); }
.fk-text-tertiary  { color: var(--fk-text-tertiary); }
.fk-text-primary   { color: var(--fk-text-primary); }

/* ── Optical alignment: large Strawford shifts left ~0.022em so its stem
      aligns with the column beneath (typography.md §7). Scoped to display roles;
      NEVER applied fluidly (tracking/optics are properties of rendered size). ── */
.fk-optical { margin-left: -0.022em; }

/* ═══════════════════════════════════════════════════════════════════════════
   READING CONTEXTS: the declarative half of the measure model
   ---------------------------------------------------------------------------
   A container declares the measure for its subtree. base.css explains the
   mechanism; these are the named contexts the engine ships. Adding a band means
   adding one of these, never touching an element selector.

   Each sets the property AND caps itself, so the container and its contents agree
   and a context works whether its prose is a direct child or six levels down.

   THE CAP NAMES THE VARIABLE, NOT THE TOKEN, and that is load-bearing. Because
   `max-width` follows `--fk-measure-context`, the `--full` modifier below releases the
   width by setting one property, and a consumer can retune a context to any value and
   have the container follow. Rewriting these three to name their tokens directly was
   tried on 2026-07-26 and measured to break exactly that: `.fk-prose` with a
   consumer-set context of `10rem` ignored it. Reverted. See `PLAT-14` in base.css.
   ═══════════════════════════════════════════════════════════════════════════ */
.fk-reading        { --fk-measure-context: var(--fk-measure);      max-width: var(--fk-measure-context); }
.fk-reading--lead  { --fk-measure-context: var(--fk-measure-lead); max-width: var(--fk-measure-context); }
.fk-reading--wide  { --fk-measure-context: var(--fk-measure-wide); max-width: var(--fk-measure-context); }

/* Release a subtree entirely: a rail that must hold its true size, a bleed
   section, a table cell, a grid child. `none` is a real value rather than the
   absence of one, so it overrides an inherited context instead of falling back
   to the element default. One property releases the whole subtree, which works only
   because the caps above follow the variable rather than naming their token. */
.fk-reading--full  { --fk-measure-context: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   FLOW AND MEASURE ARE TWO SYSTEMS, AND THEY ARE NO LONGER COUPLED
   ---------------------------------------------------------------------------
   `.fk-prose` supplied vertical rhythm AND capped its container at the reading
   measure. One class, two unrelated jobs, and the cost is measured: of 61
   structural headings across the three rendered chapters, **2 use it.** The
   chapters needed the rhythm at the wide band, could not have it without the
   width, and each reimplemented the rhythm locally, three times over.

   `grid-layout-rhythm.md` §12 prohibits exactly this: *"Coupling a binding to a
   width or measure: authors who need a different width lose the binding with
   it."* §13.4 puts it more sharply: the container designed to protect the
   relationship is the reason the relationship is unprotected.

   So the rhythm is now available at ANY width as `.fk-flow`, and `.fk-prose` is
   retained as the compound (reading context plus flow) with byte-identical
   behaviour for every existing consumer.
   ═══════════════════════════════════════════════════════════════════════════ */
.fk-prose { --fk-measure-context: var(--fk-measure); max-width: var(--fk-measure-context); }

:is(.fk-prose, .fk-flow) > * + * { margin-top: var(--fk-flow); }
:is(.fk-prose, .fk-flow) > * + :is(h2, h3, h4, .fk-section-title, .fk-subsection-title, .fk-card-title) {
  margin-top: var(--fk-space-8);
}
/* The protected heading gap: a heading and its opening sentence are one thought,
   and no rhythm rule may come between them (grid-layout-rhythm.md §1, §5).
   Available at any width now, which is what the three chapters needed. */
:is(.fk-prose, .fk-flow) :is(h2, h3, h4) + * { margin-top: var(--fk-heading-gap); }
:is(.fk-prose, .fk-flow) :is(ul, ol) { margin-top: var(--fk-flow); }

/* =============================================================================
   Responsive type steps. STEPPED, not clamp(). Tracking is a property of the
   RENDERED size, so a fluid clamp() would hold its em tracking all the way down
   and clot the setting (typography.md §5, §14). Each step re-declares size; the
   em tracking token stays valid because sizes only change at named breakpoints.
   Optical alignment stays scoped to ≥64px and is dropped below.
   ============================================================================= */

/* Laptop / tablet: ≤ 1024px */
@media (max-width: 1024px) {
  :root {
    --fk-display-size:       3.25rem;   /* 52 */
    --fk-report-title-size:  2.5rem;    /* 40 */
    --fk-section-title-size: 2rem;      /* 32 */
    --fk-metric-lg-size:     3.5rem;    /* 56 */
  }
}

/* Tablet: ≤ 768px */
@media (max-width: 768px) {
  :root {
    --fk-display-size:       2.75rem;   /* 44 */
    --fk-display-track:      -0.022em;
    --fk-report-title-size:  2.25rem;   /* 36 */
    --fk-report-title-track: -0.016em;
    --fk-section-title-size: 1.75rem;   /* 28 */
    --fk-section-title-track:-0.012em;
    --fk-quote-size:         1.625rem;  /* 26 */
    --fk-metric-size:        2.5rem;    /* 40 */
    --fk-metric-lg-size:     3rem;      /* 48 */
  }
  /* Below display sizes the optical nudge is imperceptible and can misalign. */
  .fk-optical { margin-left: 0; }
}

/* Mobile: ≤ 480px */
@media (max-width: 480px) {
  :root {
    --fk-display-size:       2.25rem;   /* 36 */
    --fk-report-title-size:  1.875rem;  /* 30 */
    --fk-report-title-track: -0.012em;
    --fk-section-title-size: 1.5rem;    /* 24 */
    --fk-section-title-track:-0.006em;
    --fk-lead-size:          1.1875rem; /* 19: lead steps down one notch */
    --fk-quote-size:         1.5rem;    /* 24 */
  }
}
