/* =====================================================================
   type-scale.css — THE shared type / space / measure scale for
   marketing-site/. Owned by the `type-scale` surface (wave 2, 2026-07-26).

   WHY THIS FILE EXISTS
   --------------------
   Before this file, the same ramp was hand-copied into four separate inline
   <style> blocks and had already drifted. Measured on 2026-07-26:

     h1   index.html          clamp(2.5rem, 6.4vw, 4.6rem)
          myy-kaapelit.html   clamp(2.3rem, 5.6vw, 3.8rem)
          laskuri.html        clamp(2.3rem, 5.6vw, 3.8rem)
          reilu-punnitus.html clamp(2.1rem, 5.2vw, 3.6rem)
     h3   index.html          1.16rem      laskuri/myy   1.10rem
     .price-source  index.html 0.68rem (~10.9px)   myy-kaapelit 0.70rem
     seller-app/src/theme/tokens.css ships a PIXEL ladder: --fs-base: 15px

   Four hand-tuned ramps cannot be kept in sync by review, and the drift had
   already landed the site's price-provenance and deduction sentences at
   ~10.9px in the muted tier. So: one file owns the ramp, and
   `scripts/check_type_contrast.js` enforces it mechanically.

     "A recursive OKLCH optimiser that holds hue constant raised automatic
      WCAG-AA fix success from 66.45% to 93.68% overall and to 100% on pairs
      with an initial contrast ratio above 2.0, at 24 ms/pair"
      — RP-000225 (E1), p. 4-5

   THAT CITATION JUSTIFIES AUTOMATING THE *CHECK*, NOT AUTO-REWRITING COLOUR.
   RP-000225 is an E1 preprint benchmark over 10,000 SYNTHETIC colour pairs
   with NO user study at all: its central premise that the larger hue-preserving
   shifts it produces (P90 deltaE2000 = 15.55) stay perceptually acceptable is
   argued from theory and "validated by no user study" (p. 3, p. 6), and the
   paper carries an unreconciled internal discrepancy in its own worked example
   (deltaE 14.2 on p. 3 vs 36.56 on p. 5 for the same output colour). Its own
   recommendation #3 is "Do not rely on numeric WCAG-AA pass alone as
   'accessible enough'" and its stated limitation is that it is a "numeric-only
   target ... ignoring font weight, spacing, viewing distance, lighting, user
   vision" (p. 6). Verified in
   _EXTRACTED_RESEARCH/accessibility_inclusive_design/RP-000225_2025_Lalitha_Context_Adaptive_Color_Optimization.md
   So the checker FAILS a build; it never rewrites a brand colour.

     "font comparisons must be normalised by x-height, not point size, or the
      comparison is confounded" — RP-000219 (D2), p. 27

   That is a methodological constraint, and it is the reason this file does NOT
   claim that its ramp is "more readable" than the four it replaces. Space
   Grotesk and Inter have different x-heights, so the old per-page rem numbers
   were never comparable to each other in the first place. The ramp below is
   chosen for CONSISTENCY and for the small-print floor — both defensible
   without a readability claim — not for a reading-speed win this library
   cannot support.

   WHAT THIS FILE OWNS (frozen token API — 5 agents build against it)
   -----------------------------------------------------------------
     --step--2 … --step-6      fluid size ramp (clamp, rem min AND rem max)
     --lh-tight/snug/body/cyr  line heights
     --tracking-tight/normal/wide
     --sp-1 … --sp-12          spacing ramp (4px base, expressed in rem)
     --measure-prose/lead/ui   line-length caps (ch)
     --font-display / --font-body / --font-display-cyr
     --text-smallprint         the floor colour for cost/condition/term text

   WHAT THIS FILE DELIBERATELY DOES NOT OWN
   ----------------------------------------
   Colour (--bg, --text, --muted, --copper*), radius, shadow, --maxw, --ease.
   Those stay in each page's own <style> so the blast radius stays small and
   --muted is not contested. The single exception is --text-smallprint, which
   is a TYPE-LAYER decision (see the ethics note on it below), not a palette
   entry.

   CASCADE ORDER — READ THIS BEFORE CONSUMING
   ------------------------------------------
   <link rel="stylesheet" href="type-scale.css"> goes in <head> BEFORE the
   page's inline <style>, so the inline block WINS every conflict. Consumers
   must DELETE their duplicated font-size / line-height / letter-spacing /
   padding declarations, not layer over them. A page that links this file and
   keeps its old declarations has consumed nothing.

   NO EXTERNAL REQUESTS. This file contains no @import, no url(), no
   @font-face and no reference to any CDN. It adds zero network requests.
   (Pre-existing, flagged not fixed here: all four hand-maintained pages and
   the seller-app still load Space Grotesk + Inter from fonts.googleapis.com,
   which contradicts the project's no-CDN rule and puts a render-blocking
   third-party request on the trust-critical first paint. Out of scope for a
   layout wave — see .agent-logs/site-rework-type-scale.md.)
   ===================================================================== */

:root {
  /* -------------------------------------------------------------------
     1. FLUID SIZE RAMP
     -------------------------------------------------------------------
     Every step is clamp(REM-min, REM-part + VW-part, REM-max).

     Both bounds are rem ON PURPOSE. A vw-only maximum silently defeats the
     reader's own text-size setting: vw does not respond to "make text
     bigger", so a size pinned at its vw ceiling stops growing. With a rem
     ceiling, raising the root size raises the ceiling too, and the rem term
     inside the preferred value means the text grows even when the viewport
     does not.

       "Honour OS text scaling. ... Offer per-user typographic controls
        (font, size, letter/line spacing, contrast polarity)"
        — ethics_dark_patterns_wellbeing.md, Do-list, citing RP-000219 (D2),
          p. 53 (an individual's fastest font read ~32% faster than their
          slowest, comprehension held at 86-94%)

     HONEST LIMIT ON THAT CITATION: the ~32% figure is RP-000219 *citing
     Wallace et al. 2020*, on short simple passages, from one connected
     author group; RP-000219 is a D2 framework paper with no primary data.
     It supports "let people control type", not "this ramp is 32% faster".
     This site cannot ship a JS font-picker (it must work with JS disabled),
     so the honest subset of that pattern is the only one implemented here:
     DO NOT BREAK the controls the reader's OS and browser already give them.

     Consequences, enforced by scripts/check_type_contrast.js:
       - no px in any size token          (px ignores the reader's setting)
       - no bare vw as a min or max bound (vw ignores the reader's setting)
       - no maximum-scale / user-scalable=no in any viewport meta
       - no fixed `height` on a text box anywhere in this layer

     Fluid range: 320px -> 1280px viewport. Below 320 every step sits on its
     rem minimum; above 1280 on its rem maximum.
     ------------------------------------------------------------------- */

  /* --step--2 / --step--1 are RESERVED for non-essential chrome: eyebrows,
     breadcrumbs, uppercase micro-labels, decorative counters.
     THEY ARE NEVER LEGAL FOR TEXT THE SELLER MUST READ TO UNDERSTAND THE
     DEAL — see the small-print floor at --step-0. The checker escalates a
     sub-floor size to an ERROR when the selector carries cost/condition
     semantics, and reports it as a warning otherwise. */
  --step--2: clamp(0.8rem,   0.79rem + 0.05vw, 0.83rem);   /* 12.8 -> 13.3px */
  --step--1: clamp(0.875rem, 0.86rem + 0.075vw, 0.92rem);  /* 14.0 -> 14.7px */

  /* --step-0 IS THE BODY SIZE AND THE SMALL-PRINT FLOOR. Nothing that states
     a cost, a deduction, a condition, a term, a tare rule or a price
     provenance may render below this, in any locale. */
  --step-0:  clamp(1rem,     0.98rem + 0.1vw,  1.0625rem); /* 16.0 -> 17.0px */

  --step-1:  clamp(1.125rem, 1.08rem + 0.21vw, 1.25rem);   /* 18.0 -> 20.0px */
  --step-2:  clamp(1.25rem,  1.17rem + 0.42vw, 1.5rem);    /* 20.0 -> 24.0px */
  --step-3:  clamp(1.4rem,   1.25rem + 0.75vw, 1.85rem);   /* 22.4 -> 29.6px */
  --step-4:  clamp(1.65rem,  1.38rem + 1.33vw, 2.45rem);   /* 26.4 -> 39.2px */
  --step-5:  clamp(2.05rem,  1.57rem + 2.42vw, 3.5rem);    /* 32.8 -> 56.0px */
  --step-6:  clamp(2.4rem,   1.7rem  + 3.5vw,  4.5rem);    /* 38.4 -> 72.0px */

  /* -------------------------------------------------------------------
     2. LINE HEIGHT
     -------------------------------------------------------------------
     HYPOTHESIS (uncited) — every number in this block.
     This library contains NO line-height study. layout_attention_cognitive_load.md
     lists its own gaps explicitly, and RP-000219 (D2) merely CATALOGUES
     letter/word spacing and line/column width as manipulable variables
     (pp. 26-33) without giving a validated range for any of them. So these
     values are house style chosen to satisfy two hard constraints that ARE
     defensible without a citation:
       (a) descenders must not collide with the next line's ascenders, and
       (b) a value of exactly 1 clips diacritics and Cyrillic descenders.
     The one citable thing here is methodological and it is a warning, not a
     licence: "font comparisons must be normalised by x-height, not point
     size" — RP-000219 (D2), p. 27. Space Grotesk and Inter do not share an
     x-height, so a single line-height number is already a compromise between
     two faces and should not be presented as optimal for either.
     ------------------------------------------------------------------- */
  --lh-tight: 1.1;    /* display headings (h1/h2). Was 1.06-1.08 sitewide. */
  --lh-snug:  1.3;    /* h3, card titles, multi-line UI labels, buttons     */
  --lh-body:  1.65;   /* continuous prose. Was 1.6 on body.                 */

  /* --lh-cyr is the Cyrillic heading relaxation. It is applied by REBINDING
     --lh-tight under :lang(ru), :lang(uk) at the bottom of this file, so a
     consumer that writes `line-height: var(--lh-tight)` gets the relaxation
     for free and never has to know Cyrillic exists. */
  --lh-cyr:   1.25;

  /* -------------------------------------------------------------------
     3. TRACKING
     -------------------------------------------------------------------
     HYPOTHESIS (uncited) — same reasoning as line-height. No tracking study
     in this library.

     The one HARD rule here is not a magnitude and does not need one:
     NEVER a negative letter-spacing on an element that can hold ru/uk text.
     The site currently ships h1,h2,h3 { letter-spacing: -0.03em } on all
     four hand-maintained pages, which is inherited by the generated /ru/ and
     /uk/ pages. Cyrillic lowercase is built from more vertical strokes per
     word than Latin (и, ш, щ, ц, п, н), so negative tracking closes the
     counters and turns a heading into a picket fence. The :lang() block at
     the bottom rebinds --tracking-tight to 0 for exactly that reason.
     ------------------------------------------------------------------- */
  --tracking-tight:  -0.02em;  /* display headings (Latin only — see below) */
  --tracking-normal:  0;
  --tracking-wide:    0.16em;  /* uppercase eyebrows / micro-labels         */

  /* -------------------------------------------------------------------
     4. SPACING RAMP — 4px base, expressed in rem
     -------------------------------------------------------------------
     rem, not px, for the same reason the size ramp is rem: when the reader
     enlarges text, the space around it has to grow too or the layout
     collides. --sp-1..--sp-8 are strict 4px multiples; --sp-9..--sp-12 step
     up, because a strictly linear 12-stop ramp tops out at 48px, which is
     too small for section rhythm. That break is deliberate and documented
     rather than silent.
     ------------------------------------------------------------------- */
  --sp-1:  0.25rem;   /*  4px */
  --sp-2:  0.5rem;    /*  8px */
  --sp-3:  0.75rem;   /* 12px */
  --sp-4:  1rem;      /* 16px */
  --sp-5:  1.25rem;   /* 20px */
  --sp-6:  1.5rem;    /* 24px */
  --sp-7:  1.75rem;   /* 28px */
  --sp-8:  2rem;      /* 32px */
  --sp-9:  2.5rem;    /* 40px — ramp breaks from strict 4px linearity here */
  --sp-10: 3rem;      /* 48px */
  --sp-11: 4rem;      /* 64px */
  --sp-12: 5rem;      /* 80px */

  /* -------------------------------------------------------------------
     5. MEASURE (line-length caps)
     -------------------------------------------------------------------
     HYPOTHESIS (uncited) — the numbers. There is no line-length study in
     this library; RP-000219 (D2) lists "line/column width" among the
     variables a readability study COULD manipulate (pp. 26-33) and stops
     there. These are house caps wired to a test, not an optimum.

     LOCALISATION NOTE. `ch` is the advance width of the "0" glyph, so a ch
     cap is a Latin-calibrated measurement. Cyrillic running text is ~12%
     longer than the Finnish source (measured across 475 catalogue strings on
     2026-07-26: ru x1.119, uk x1.100), so at the same ch cap a ru paragraph
     holds fewer words and runs more lines. That direction is SAFE — more
     lines, never overflow — so the caps are not relaxed per locale. What is
     NOT safe is a cap expressed in px or a container with a fixed height;
     neither appears in this file.
     ------------------------------------------------------------------- */
  --measure-prose: 68ch;   /* continuous body prose                        */
  --measure-lead:  56ch;   /* hero lead / intro paragraph                  */
  --measure-ui:    42ch;   /* helper text, field hints, card body          */

  /* -------------------------------------------------------------------
     6. FONT STACKS
     ------------------------------------------------------------------- */
  --font-display: "Space Grotesk", "Inter", system-ui, -apple-system,
                  BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-body:    "Inter", system-ui, -apple-system, BlinkMacSystemFont,
                  "Segoe UI", Roboto, "Helvetica Neue", Arial,
                  "Noto Sans", sans-serif;

  /* --font-display-cyr — the ru/uk heading stack.

     THE DEFECT (verifiable, and it is a defect regardless of how the font
     question resolves): --font-display puts "Space Grotesk" first and
     "Inter" second, and the browser resolves a font stack PER GLYPH. If
     Space Grotesk lacks Cyrillic, a ru/uk heading that mixes scripts — and
     they do; "Kaapelipankki" and "Romuralli" are identical in all five
     locales by contract — renders its Latin glyphs in Space Grotesk and its
     Cyrillic glyphs in Inter, inside the same word-space. Two faces, two
     x-heights, two stroke weights, one heading.

     WHAT I COULD NOT VERIFY, AND WHY I AM SAYING SO INSTEAD OF ASSERTING IT:
     the fonts are loaded from fonts.googleapis.com and this machine has no
     network egress (curl exit 35, SSL connect error, 2026-07-26), so I could
     not confirm from here whether the served Space Grotesk carries a
     cyrillic unicode-range. I am not going to state a subset list from
     memory as if I had checked it. The deeper point is that this is not
     checkable AT BUILD TIME AT ALL while the font comes from a third-party
     CDN — a trust-critical typographic decision on two of five locales has
     been delegated to a resource nobody in this repo controls.

     THE REMEDY IS SAFE UNDER BOTH BRANCHES: name an explicit Cyrillic stack
     so ru/uk headings resolve to ONE deterministic face rather than a
     per-glyph mix. If Space Grotesk turns out to carry Cyrillic, the cost is
     that ru/uk lose the display face; the display/body distinction is then
     carried by weight and size, which still works. If it does not, this
     fixes a live rendering bug. Inter ships Cyrillic; Segoe UI and Roboto
     carry Cyrillic as OS fallbacks on Windows and Android respectively.

     If the owner self-hosts the fonts (which the no-CDN rule already wants),
     the coverage question becomes locally checkable and this is a one-line
     revisit. Recorded in .agent-logs/site-rework-type-scale.md. */
  --font-display-cyr: "Inter", "Noto Sans", system-ui, -apple-system,
                      "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* -------------------------------------------------------------------
     7. --text-smallprint — THE ETHICS TOKEN
     -------------------------------------------------------------------
     This is the colour for any text that carries a COST, a DEDUCTION, a
     CONDITION, a TERM, a TARE RULE or a PRICE PROVENANCE.

     It is deliberately NOT a muted tier. It sits in the same lightness band
     as --text. The name is not a licence to dim; it exists so the checker
     can find every such string mechanically and so a designer reaching for
     "the small-print colour" lands on a legible one instead of --muted.

     WHY THIS IS A TOKEN AND NOT A PER-PAGE DECISION. Setting a real
     condition two steps below body size in grey is false hierarchy, and
     false hierarchy is the most-tested harmful pattern in the library:

       "Across 148 controlled experiments in 27 papers, 86 of 101 head-to-head
        experiments (85.1%) found a statistically significant behavioural
        effect ... Interface Interference - dominated by Visual Prominence and
        False Hierarchy - was the most-tested category (157 instances)"
        — RP-000124 (A1), p. 7-9

     RP-000124 also records that warnings, education, cool-down interstitials
     and priming almost uniformly FAILED to mitigate (23 of 27 mitigation
     experiments, p. 9-10). So the remedy is not a disclaimer next to the
     grey 11px line; it is to not set it in grey 11px. Prominence is load-
     bearing, not decorative:

       "Trust cues need prominence, not just presence | RP-000203 | D1 |
        Privacy policies rarely noticed on any of 100 sites | p. 12"

     And the population that pays for it first is the one already telling us:

       "visual acuity (10/144, 7%) - described concretely as 'fonts or icons
        are too small, color contrast'" — RP-000419 (C1), pp. 17-18, Table 6 p. 18

     HONEST LIMIT: RP-000419 is a frequency count of barrier MENTIONS across
     57 telehealth studies of volunteers over 50. It is correlational, it is
     not population prevalence, and nothing in this library shows that fixing
     a named accessibility barrier increases use (the guide states this
     outright). It tells us what people report being blocked by. It does not
     promise a conversion effect, and none is claimed.

     MEASURED, not eyeballed. Against the site's declared surfaces
     (--bg #0b0d11, --bg-2 #0f1115, --surface #171b22) the worst case for
     #eef1f5 is 15.2:1 — AAA at every size. Recomputed on every run by
     scripts/check_type_contrast.js, which fails the build below AA.
     Deliberately NOT auto-corrected: RP-000225's optimiser has no user
     validation (p. 3, p. 6). */
  --text-smallprint: #eef1f5;
}

/* =====================================================================
   CYRILLIC REBINDING
   ---------------------------------------------------------------------
   ru is the reference locale for every layout decision on this site (ru
   totals x1.119 the Finnish character count, 65% of its 475 catalogue
   strings are longer than the Finnish key, and its worst short-label
   blowup is x2.30: "Lisätiedot" -> "Дополнительные сведения").

   This block REBINDS existing tokens rather than adding new ones, so every
   consumer inherits the Cyrillic treatment without writing a :lang() rule
   of its own. Consumers write var(--lh-tight); ru/uk get 1.25 instead of
   1.1 automatically.

   The two rules here are HARD, and they are the two the contract bans
   sitewide:
     1. never line-height: 1 on an element that can hold ru/uk text
     2. never negative letter-spacing on an element that can hold ru/uk text
   The specific replacement VALUES are HYPOTHESIS (uncited) — see the
   line-height block above. The BAN is not: it is a legibility failure you
   can see, not an effect size I am borrowing an RP-ID for.
   ===================================================================== */
:lang(ru),
:lang(uk) {
  --lh-tight: var(--lh-cyr);   /* 1.1  -> 1.25 : Cyrillic descenders (д, ц, щ)
                                  and the breve on й need the room */
  --lh-snug:  1.4;             /* 1.3  -> 1.4                                */
  --lh-body:  1.75;            /* 1.65 -> 1.75                               */
  --tracking-tight: 0;         /* HARD BAN: never negative on Cyrillic       */
  --font-display: var(--font-display-cyr);
}

/* =====================================================================
   MINIMAL RESET — the only declarations this file applies to elements.
   Everything above is tokens. These four are here because they are the
   ones that must not be re-declared per page, and because leaving them
   out would let a consumer reintroduce exactly the bans above.
   ===================================================================== */

/* Never let a page defeat pinch-zoom or OS text scaling. -adjust:100%
   stops iOS Safari inflating text in landscape, which silently overrides
   the reader's own setting. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Longest-locale insurance, applied at the layer everyone inherits.
   A long ru/uk word (Дополнительные, Домовтеся, вивезення) — or a long
   Finnish compound (markkinahinnan, punnitustodistuksen, myyntilomake) — in a
   narrow chrome element at 320px must WRAP or BREAK: it may never clip and it
   may never push the document wider than the viewport. Both keywords break a
   word only when there is no other option, so normal-width rendering is
   unaffected in every locale.

   WHY `anywhere` AND NOT `break-word` (fixed 2026-07-27, WCAG 1.4.4).
   The two keywords differ in EXACTLY ONE respect, and it is the respect that
   decides this: the soft wrap opportunities `anywhere` introduces ARE counted
   when the browser computes an element's MIN-CONTENT size; `break-word`'s are
   not. A `1fr` grid track, an `auto` track and a flex item's automatic minimum
   size are all floored at min-content. So under `break-word` a single long word
   still set the floor, the track grew to fit it, and the whole column rendered
   past a 320px viewport — measured at 320px with the root font-size forced to
   32px (= 200% text scaling): +160px on index.html's contact grid, +157px on
   its hero grid, +144px on reilu-punnitus.html's trust row, +133px on its
   3-card band, +82px on kuparipitoisuus.html's price bar. Nothing SCROLLED,
   because html/body carry overflow-x:hidden, so it was clipped in silence and
   a scrollWidth-based test reported it green. Gate: verify_i18n_pages.js
   section 4d. */
body {
  overflow-wrap: anywhere;
}

/* Headings: the size/weight is the page's business, the ban is not.
   text-wrap: balance is progressive enhancement — ignored by browsers that
   do not support it, and it never truncates.

   hyphens: auto is the companion to `overflow-wrap: anywhere` above, added
   2026-07-27 for the same defect. `anywhere` is what stops a 14-character
   Finnish compound clipping at 320px/200%, but on its own it breaks the word at
   an arbitrary character with NO hyphen, so "Romukaapelin" reads as "Romu" then
   "kaapelin" — two words. `hyphens: auto` gives the browser its own dictionary
   break points and prints the hyphen, so the same wrap reads as one word. It
   needs `lang` on an ancestor, which every page sets on <html> (fi/en/et/ru/uk),
   and a browser without a dictionary for that language simply ignores it: it can
   never truncate and it never widens anything.

   NOT VERIFIED IN THIS ENVIRONMENT, AND THEREFORE NOT COUNTED AS THE FIX.
   Chrome fetches its hyphenation dictionaries through the component updater, and
   the offline headless profile the gate measures in has none — measured: at
   320px/root 32px the Finnish h1 still breaks WITHOUT a hyphen, and the
   overflow count is 0 either way. So this line is progressive enhancement whose
   benefit is unconfirmed here; `overflow-wrap: anywhere` is what actually stops
   the clipping. Kept because it is the correct declaration for the case and
   costs nothing when it is ignored. */
h1, h2, h3, h4, h5, h6 {
  text-wrap: balance;
  hyphens: auto;
  -webkit-hyphens: auto;
}

/* THE SMALL-PRINT FLOOR, as an enforceable class.
   Any element carrying a cost, deduction, condition, term, tare rule or
   price provenance gets .smallprint and lands at body size in a legible
   colour. min-height, never height. See RP-000124 (A1), p. 7-9 above. */
.smallprint {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--text-smallprint);
  max-width: var(--measure-prose);
}
