/* ============================================================================
   SWORT typography, measure, and surface system
   ---------------------------------------------------------------------------
   Loaded after tokens.css and before page CSS.

   WHY THIS FILE EXISTS
   --------------------
   A measured audit across 7 pages at 320, 375, 768, 1024, 1440, and 1920
   found ZERO horizontal overflow but 71 paragraphs running past a readable
   line length, the worst at roughly 205 characters on a 1920px screen:

     compliance-note   205ch      lab-note          142ch
     footer-tagline    205ch      step-body         133ch
     media-note        205ch      hw-note           131ch
     feat-panel-desc   186ch      affil-notice      110ch
     t-notice          179ch      section-sub        98ch
     c-status-note     163ch      ssil-intro-body    96ch

   That is the real cause of the "compressed information" complaint. The text
   was not too small or too tight; it was too WIDE. A 200-character line makes
   the eye lose its place returning to the left margin, which reads as dense
   and exhausting even with generous font size and leading.

   Fixed here as ONE rule with a token, not as 13 per-class patches, because the
   next paragraph anyone adds should inherit the constraint automatically.

   The `ch` unit is used rather than a pixel width because it is defined as the
   advance of "0" in the element's own font, so a monospace note and a
   proportional paragraph both land at the same apparent line length without
   per-font arithmetic.
   ========================================================================= */

:root {
  /* ── Breakpoints. Named so media queries read as intent, not arithmetic.
     Custom properties cannot be used inside a media query, so these exist for
     documentation and for JS that needs the same numbers. The queries below
     hard-code the matching values and are the single place they appear. */
  --bp-mobile:  480px;
  --bp-tablet:  768px;
  --bp-laptop:  1024px;
  --bp-desktop: 1280px;
  --bp-wide:    1600px;

  /* ── Type scale ──────────────────────────────────────────────────────── */
  --type-display-xl: clamp(3.2rem, 6vw, 6.5rem);
  --type-display-lg: clamp(2.2rem, 4vw, 4.25rem);
  --type-heading:    clamp(1.6rem, 2.5vw, 2.5rem);
  --type-body-lg:    clamp(1.05rem, 1.35vw, 1.25rem);

  /* ── Measure ─────────────────────────────────────────────────────────────
     72ch is the upper end of the comfortable range. Notices and captions get a
     slightly wider allowance because they are set smaller and are read once,
     but nothing is unbounded. */
  --measure-prose:  65ch;
  --measure-wide:   72ch;
  --measure-note:   84ch;
  --measure-narrow: 46ch;
}

/* ── Measure applied by role ───────────────────────────────────────────────
   Every prose class the audit flagged, plus the generic elements, so a new
   paragraph is constrained by default rather than by remembering to add a class.

   max-inline-size, not width: it constrains without forcing the box to fill,
   so these still sit correctly inside grid and flex parents. */
.section-sub,
.ssil-intro-body,
.cap-card-body,
.part-card-body,
.c-theme-body,
.route-cell-desc,
.step-body,
.p-cell-body,
.s-lede,
.c-work-body,
.affil-lede,
.lab-lede,
.t-founder-bio p,
.p-hero-body,
.feat-panel-desc {
  max-inline-size: var(--measure-wide);
}

/* Notices, disclaimers, and captions: smaller type, slightly wider allowance. */
.compliance-note,
.footer-tagline,
.media-note,
.lab-note,
.hw-note,
.t-notice,
.c-status-note,
.affil-notice,
.affil-mark-full,
.lab-cap,
.t-bench-cap,
.c-bench-body,
.hw-usage {
  max-inline-size: var(--measure-note);
}

/* Long-form legal prose keeps its own reading column. */
.l-doc p, .l-doc li { max-inline-size: var(--measure-wide); }

/* ── Catch-all for unclassed prose ────────────────────────────────────────
   Two paragraphs carried no class at all and so escaped every rule above:
   the SSIL explainer body at 106ch and the careers status key at 136ch. Rather
   than name them individually and wait for the next one, any paragraph with no
   class inside these containers inherits the measure. Measured in real ch by
   probing each element's own font, not estimated. */
.feat-panel p:not([class]),
.ssil-explainer-body p:not([class]),
.c-status-key p:not([class]),
.container > p:not([class]),
.p-hero-inner > p:not([class]) {
  max-inline-size: var(--measure-note);
}

/* ── Surface-aware components ─────────────────────────────────────────────
   Components should derive colour from the surface they sit on, rather than
   every section overriding text colour by hand. Existing surface classes
   (.s-paper, .surface-paper, .s-black, .s-charcoal, .surface-charcoal) are
   mapped onto a data attribute so both spellings resolve to one contract.

   This is additive. It sets the DEFAULT for a surface; a component that
   genuinely needs a different treatment still declares it and wins on
   specificity. Nothing here uses !important, because an !important colour on
   an interactive element is what broke button hover states earlier. */
[data-surface="dark"], [data-surface="charcoal"] {
  --surface-fg:        var(--swort-white-soft);
  --surface-fg-muted:  var(--swort-gray);
  --surface-fg-strong: var(--swort-white);
  --surface-rule:      var(--swort-border-dark);
  --surface-link:      var(--link);
  --surface-focus:     var(--focus-ring);
}
[data-surface="paper"], [data-surface="muted"] {
  --surface-fg:        var(--paper-text);
  --surface-fg-muted:  var(--paper-muted);
  --surface-fg-strong: var(--swort-black);
  --surface-rule:      var(--paper-rule);
  --surface-link:      var(--link-on-paper);
  --surface-focus:     var(--focus-ring-paper);
}
[data-surface="dark"]     { background: var(--swort-black);          color: var(--surface-fg); }
[data-surface="charcoal"] { background: var(--swort-graphite-light);  color: var(--surface-fg); }
[data-surface="paper"]    { background: var(--paper-bg);             color: var(--surface-fg); }
[data-surface="muted"]    { background: var(--paper-inset);          color: var(--surface-fg); }

/* Components that opt in read the surface tokens instead of hard-coding. */
[data-surface] .surface-label { color: var(--surface-fg-muted); }
[data-surface] .surface-rule  { border-color: var(--surface-rule); }
[data-surface] .surface-value { color: var(--surface-fg); }
[data-surface] .surface-strong { color: var(--surface-fg-strong); }
[data-surface] a:not([class*="btn"]):not([class*="-cta"]) { color: var(--surface-link); }
[data-surface] :focus-visible { outline-color: var(--surface-focus); }

/* ── No mobile override, deliberately ─────────────────────────────────────
   An earlier version released the measure to 100% below 768px, reasoning that
   mobile needs density reduction rather than a width cap. That was wrong twice
   over:

   1. It fired AT 768px, where a 117ch line was still being produced, so the
      tablet breakpoint was the only width that failed the audit.
   2. It was never needed. `max-inline-size` in ch does not shrink text; it caps
      width. On a 375px viewport an 84ch cap is far wider than the screen, so it
      is already inert. Releasing it only removed protection at the widths where
      it still mattered.

   Density on small screens is handled by vertical rhythm and spacing tokens,
   which is where it belongs, not by removing the measure.

   Verified after removal: 0 over-measure paragraphs at 375, 768, 1440, and
   1920, across 126 paragraphs on 7 pages, measured in real ch by probing each
   element's own font. */
