@charset "UTF-8";
/* ============================================================================
   ИнфраГео / InfraGeo — site.css  ·  the ONE shared stylesheet
   Direction : tokenized-multipage  ·  tenant expression: "instrument"
   Built     : 2026-07-25 (rebuild-2026-07, phase 1)

   READ BEFORE EDITING
   1. TIER 2 below is transcribed VERBATIM from clients/infrageo/brand/tokens.css.
      tokens.css is the contract. If this block and tokens.css ever disagree,
      tokens.css wins and this block is corrected — never the other way round.
      Tier 2 carries LITERAL HEX on purpose: design_token_audit.py only measures
      tokens whose value contains a hex. Do not rewrite tier 2 into var().
   2. No `transition: all`, ever. Named properties only, and only transform /
      opacity / colour.
   3. No `hyphens: auto`, no `overflow-wrap: anywhere`, no `word-break: break-all`
      on Cyrillic. Bulgarian has no reliable browser hyphenation dictionary.
   4. Text wrapping is set ONCE, globally, in § 2. Do not repeat it per component.
   5. THE CONTAINER IS THE ONE MEASURE. `.wrap` is the frame and is always
      centred. `--measure` lives on PARAGRAPHS. A heading fills its column
      (max-width: none). A constrained block is either centred or fills —
      never constrained AND left-hugging. qa_layout_measure.py fails on that.
   6. Every element with width/height attributes relies on the global
      `height: auto` in § 2. Removing it renders every photograph squeezed.
   ========================================================================== */

/* ==========================================================================
   § 0  TYPEFACES — what is actually served, and what is not
   --------------------------------------------------------------------------
   STATE, 2026-07-25: NO FONT FILE SHIPS WITH THIS BUILD.
   There is no `website-v2/fonts/` directory and no woff2 anywhere in the repo.
   The `@font-face` block below is therefore COMMENTED OUT ON PURPOSE.
   Uncommenting it before the files exist fires six requests that 404, and a
   404 font is strictly worse than an honest system fallback: the reader still
   ends up on the fallback face, only after a wasted swap period and six failed
   requests. The block is written out in full so that shipping the real type
   system is one uncomment plus two preload tags, with no other CSS edit.

   WHAT RENDERS TODAY. Every --font-* token in § 1 names its Plex family first,
   so IBM Plex is used on any machine that has it installed and everywhere else
   the browser walks the tail of the stack:
     sans   Windows Segoe UI · macOS SF Pro · Android Roboto · Linux fontconfig
     mono   Windows Consolas · macOS SF Mono · Android Roboto Mono
   All of those carry full Cyrillic at every weight this build asks for, so the
   page reads correctly and the weight hierarchy holds. What is lost is the
   metric-sibling relationship between Plex Sans and Plex Mono that tokens.css
   argues for — mono values and sans labels stop reading as one document.
   § 1b lengthens the mono tail so that loss is as small as it can be; see the
   note there for why the sans tail is left alone.

   TO SHIP THE REAL TYPE SYSTEM — three steps, no other edit anywhere:

   1. Create `website-v2/fonts/` and put subset woff2 files in it, named
      exactly as the block below expects:
        ibm-plex-sans-400.woff2   body copy, every paragraph
        ibm-plex-sans-500.woff2   --font-weight-medium: .btn .nav__link, dd
        ibm-plex-sans-600.woff2   --font-weight-display: every h1-h6
        ibm-plex-sans-700.woff2   --font-weight-strong: strong, b
        ibm-plex-mono-400.woff2   every ledger row, all 12 dates, every micro
                                  label, the spec sheet, every plate caption
        ibm-plex-mono-500.woff2   .plate__cap b only — OPTIONAL, see the last
                                  paragraph of this comment
      Source: github.com/IBM/plex — SIL Open Font License 1.1, so self-hosting
      is licensed. A Google Fonts <link> is not an alternative here: it leaks
      the visitor's IP to a third party and adds a blocking third-party request
      above the fold, both of which this build rules out.

      Subset with pyftsubset, Latin and Cyrillic together in ONE file per
      weight, e.g. for sans 400:

        pyftsubset IBMPlexSans-Regular.ttf
          --unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,
            U+02DA,U+02DC,U+0300-0301,U+0400-045F,U+0490-0491,U+2013-2014,
            U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2039-203A,
            U+2044,U+2116,U+2212"
          --layout-features="kern,liga,locl,tnum"
          --flavor=woff2 --output-file=ibm-plex-sans-400.woff2

      KEEP `tnum` IN THE FEATURE LIST. `font-variant-numeric: tabular-nums` is
      set on .mono, .spec-sheet dd, .card__index, .step__num and .plate__cap. A
      subset that drops tnum silently un-aligns every number on the dossier,
      which is the one page that cannot afford it.
      KEEP `locl`. All five documents are lang="bg" and Plex carries the
      Bulgarian Cyrillic letterforms behind that feature.
      U+2116 is «№» and U+00B7 (inside the U+0000-00FF run) is the «·» that
      .meta draws between every metadata item — both are load-bearing here.

   2. Uncomment the block below as one piece. Nothing else in this stylesheet
      changes: the token stacks in § 1 already name the families first.

   3. Add exactly TWO preloads to the <head> of all five pages, above the
      stylesheet link. Two, not six — these are the only faces on the critical
      path (sans 400 is the hero standfirst and all body copy, mono 400 is the
      hero plate caption and the spec sheet):

        <link rel="preload" href="/fonts/ibm-plex-sans-400.woff2" as="font"
              type="font/woff2" crossorigin>
        <link rel="preload" href="/fonts/ibm-plex-mono-400.woff2" as="font"
              type="font/woff2" crossorigin>

      `crossorigin` is REQUIRED even though the files are same-origin. Without
      it the font is fetched twice and the preload is worse than nothing.

   BUDGET: six subset faces land at roughly 55-65 KB in total, of which only
   the two preloaded ones (~20 KB) sit above the fold, against the 180 KB
   above-the-fold budget that currently spends 39.7 KB.

   NEVER SELF-HOSTED: --font-brand names "BankGothic Md BT". The OTF in
   brand/identity/ has NO confirmed web licence — BUILD-NOTES § 6 Branch B. It
   must not get an @font-face under any circumstances. The wordmark ships as
   SVG paths and that covers the whole job.

   IF YOU SHIP FEWER FILES: `font-synthesis: none` is set on body, so a missing
   weight is never faked — CSS falls back to the nearest declared weight of the
   same family. Dropping mono 500 renders .plate__cap b at mono 400, where its
   brighter --color-surface-invert-ink still carries the emphasis, so that one
   file is genuinely optional. Do NOT drop sans 600: every heading asks for it.
   ========================================================================== */

/* IBM Plex, self-hosted (OFL). Per-subset woff2 (Cyrillic + Latin);
   unicode-range fetches only the subset a glyph needs. font-synthesis:none
   on body means a missing weight is never faked. (M8 resolved 2026-08-23) */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-cyrillic-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-cyrillic-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-cyrillic-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-cyrillic-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/ibm-plex-sans-latin-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-cyrillic-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-cyrillic-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-latin-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   § 1  TIER 2 / TIER 3 — the token contract (verbatim from brand/tokens.css)
   ========================================================================== */

:root {

  /* -- action: the marking red -------------------------------------------- */
  --color-primary:        #c1121f;  /* CTA fill, axis mark, active tick. 5.75:1 on --color-bg */
  --color-primary-ink:    #ffffff;  /* on primary — 6.22:1 */
  --color-primary-hover:  #9e0e19;  /* hover / pressed fill — white ink 8.30:1 */
  --color-primary-invert: #ff4d57;  /* the mark ON the dark panel — 5.55:1 on --color-surface-invert */
  --color-link:           #a50f1b;  /* red as running text — 6.59:1 worst light ground */

  /* -- annotation: the marking amber --------------------------------------- */
  --color-accent:         #bf6708;
  --color-accent-ink:     #160f05;  /* on accent — 4.67:1 */

  /* -- ground plus structure: cool slate ----------------------------------- */
  --color-bg:             #f4f6f8;  /* drafting paper, faintly cool */
  --color-surface:        #ffffff;  /* cards, ledger rows, form fields */
  --color-surface-sunken: #e8ecf0;  /* the density-strip bed, inset blocks */
  --color-ink:            #0f1a24;  /* body — 16.24:1 on bg */
  --color-ink-muted:      #59656f;  /* labels, captions — 5.03:1 worst light ground */
  --color-line:           #c2cdd6;  /* decorative hairline */
  --color-line-strong:    #75828e;  /* MEANINGFUL rules: ledger rows, gap dashes — 3.31:1 */

  /* -- the inverted instrument panel (photo plates, spec-sheet header) ------ */
  --color-surface-invert:     #0e1720;
  --color-surface-invert-ink: #e6edf3;  /* on the panel — 15.29:1 */
  --color-invert-label:       #9dacb8;  /* mono labels on the panel — 7.77:1 (measured) */
  --color-line-invert:        #2c3946;  /* hairline on the panel */

  /* -- focus: a two-tone ring, legible on ANY ground incl. the red button --- */
  --color-focus-ring:       #0f1a24;
  --color-focus-ring-inner: #ffffff;

  /* -- status: exactly two. ------------------------------------------------- */
  --color-success:     #1b6b3a;
  --color-success-ink: #ffffff;  /* 6.54:1 */
  --color-success-bg:  #e7f3ec;
  --color-danger:      #7a1416;  /* deep oxide red, clearly darker than the CTA */
  --color-danger-ink:  #ffffff;  /* 9.04:1 */
  --color-danger-bg:   #f8e9e9;

  /* -- type ---------------------------------------------------------------- */
  --font-brand:   "BankGothic Md BT", "IBM Plex Sans", system-ui, sans-serif;
  --font-display: "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --font-body:    "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;

  --font-weight-body:    400;
  --font-weight-medium:  500;
  --font-weight-display: 600;
  --font-weight-strong:  700;

  --fs-hero:  clamp(2rem, 5.4vw, 3.5rem);
  --fs-h1:    clamp(1.75rem, 4vw, 2.75rem);
  --fs-h2:    clamp(1.375rem, 2.6vw, 1.875rem);
  --fs-h3:    clamp(1.125rem, 1.6vw, 1.375rem);
  --fs-lead:  clamp(1.1875rem, 1.4vw, 1.375rem);
  --fs-body:  clamp(1.0625rem, 0.35vw + 1rem, 1.125rem);  /* 17->18px, outdoor floor */
  --fs-small: 0.9375rem;  /* 15px — captions, form help */
  --fs-micro: 0.75rem;    /* 12px — UPPERCASE MONO LABELS ONLY, never a sentence */

  --leading-tight:   1.08;
  --leading-snug:    1.28;
  --leading-normal:  1.6;   /* body — Cyrillic wants the extra */
  --leading-relaxed: 1.75;
  --tracking-tight:  -0.015em;
  --tracking-caps:   0.09em;
  --tracking-brand:  0.06em;    /* BankGothic: POSITIVE ONLY, no Cyrillic kerning */
  --tracking-mono:   0.01em;

  /* -- space / shape / depth ------------------------------------------------ */
  --space-1: 0.25rem; --space-2: 0.5rem;  --space-3: 0.75rem; --space-4: 1rem;
  --space-5: 1.25rem; --space-6: 1.5rem;  --space-8: 2rem;    --space-10: 2.5rem;
  --space-12: 3rem;   --space-16: 4rem;   --space-20: 5rem;   --space-24: 6rem;
  --space-32: 8rem;

  --radius-sm: 2px; --radius-md: 3px; --radius-lg: 4px; --radius-full: 999px;

  --shadow-1: 0 1px 2px rgba(15, 26, 36, 0.07);
  --shadow-2: 0 10px 28px rgba(15, 26, 36, 0.12);
  --border-hairline: 1px;
  --border-rule:     2px;

  /* -- motion --------------------------------------------------------------- */
  --ease:        cubic-bezier(0.2, 0, 0, 1);   /* the instrument settle */
  --ease-out:    cubic-bezier(0, 0, 0.2, 1);
  --ease-linear: linear;                       /* strip fills and progress only */
  --duration-fast: 90ms;
  --duration-base: 160ms;
  --duration-slow: 240ms;
  --reveal-rise:   8px;

  /* -- layering ------------------------------------------------------------- */
  --z-base: 0; --z-raised: 10; --z-dropdown: 1000; --z-sticky: 1100;
  --z-overlay: 1300; --z-modal: 1400; --z-toast: 1600;

  /* -- layout (the two width axes are DIFFERENT things) --------------------- */
  --container-max:  1180px;  /* outer FRAME — ALWAYS margin-inline: auto */
  --container-wide: 1360px;  /* dossier: density strip and ledger only */
  --container-pad:  var(--space-5);
  --measure:        64ch;    /* body PROSE only — never a heading, never the frame */
  --measure-lead:   46ch;
  --measure-narrow: 32ch;

  --wrap-heading: balance;
  --wrap-body:    pretty;

  /* -- TIER 3 — COMPONENT (var() references only; never a literal hex) ------ */

  --control-h:      48px;
  --control-pad-x:  var(--space-5);
  --control-radius: var(--radius-sm);
  --control-border: var(--border-hairline) solid var(--color-line-strong);
  --btn-bg:         var(--color-primary);
  --btn-ink:        var(--color-primary-ink);
  --btn-bg-hover:   var(--color-primary-hover);

  --spec-bg:        var(--color-surface-invert);
  --spec-ink:       var(--color-surface-invert-ink);
  --spec-label-ink: var(--color-invert-label);
  --spec-label-fs:  var(--fs-micro);
  --spec-value-fs:  var(--fs-h3);
  --spec-rule:      var(--border-hairline) solid var(--color-line-invert);
  --spec-col-min:   168px;
  --spec-row-gap:   var(--space-6);

  --strip-bed:        var(--color-surface-sunken);
  --strip-h:          96px;
  --strip-axis:       var(--border-hairline) solid var(--color-line-strong);
  --strip-tick-w:     3px;
  --strip-tick-min-h: 12px;
  --strip-tick-max-h: 56px;
  --strip-tick-gap:   3px;
  --strip-tick:       var(--color-primary);
  --strip-tick-idle:  var(--color-line-strong);
  --strip-tick-hit:   44px;
  --strip-year-rule:  var(--color-accent);
  --strip-label-ink:  var(--color-ink-muted);
  --strip-label-fs:   var(--fs-micro);

  --ledger-bg:          var(--color-surface);
  --ledger-bg-hover:    var(--color-bg);
  --ledger-rule:        var(--border-hairline) solid var(--color-line-strong);
  --ledger-mark:        var(--border-rule) solid var(--color-primary);
  --ledger-row-min-h:   56px;
  --ledger-row-pad-y:   var(--space-4);
  --ledger-index-w:     2.5rem;
  --ledger-date-w:      11ch;
  --ledger-count-w:     7ch;
  --ledger-phase-ink:   var(--color-ink);
  --ledger-meta-ink:    var(--color-ink-muted);
  --ledger-thumb:       112px;
  --ledger-thumb-ratio: 1 / 1;   /* SQUARE. tokens.css is the source of truth here;
     the package §5.2 and style-guide §3 still print `4 / 3`, which is the pre-correction
     value. The 2026-07-25 correction note in tokens.css is the live one. */

  --gap-rule:  var(--color-line-strong);
  --gap-dash:  6px 5px;
  --gap-ink:   var(--color-ink-muted);
  --gap-fs:    var(--fs-micro);
  --gap-pad-y: var(--space-5);

  --plate-bg:      var(--color-surface-invert);
  --plate-border:  var(--border-hairline) solid var(--color-line-invert);
  --plate-radius:  var(--radius-sm);
  --plate-cap-ink: var(--color-invert-label);
  --plate-cap-fs:  var(--fs-micro);

  /* -- TIER 3 additions owned by THIS stylesheet (build-level, not brand) --- */
  --section-y:   clamp(var(--space-16), 6.5vw, var(--space-24));
  --section-y-s: clamp(var(--space-12), 5vw, var(--space-16));
  --header-h:    72px;
  --logo-h:      34px;   /* SM cut on every breakpoint — see § 5 */
  --rule-hair:   var(--border-hairline) solid var(--color-line);
  --rule-strong: var(--border-hairline) solid var(--color-line-strong);
}

/* ==========================================================================
   § 1b  FALLBACK TAIL — a build-level extension, NOT a token change

   § 1 above stays byte-identical to brand/tokens.css and remains the contract
   for WHICH families this brand wants. This block does one thing: it lengthens
   the tail of --font-mono for the case § 0 describes, where no Plex file is
   served. The Plex family still comes first and the order of everything
   tokens.css names is unchanged, so nothing here can be read as overriding the
   brand's choice — it only names what the browser should reach for after the
   brand's choice is unavailable.

   Why mono and not sans. `system-ui` resolves on every browser that ships
   today (Segoe UI / SF Pro / Roboto), all with full Cyrillic, so the sans tail
   is already correct and adding Segoe UI or Arial after it would be noise.
   The mono tail is genuinely broken: Chrome on Windows does not implement
   `ui-monospace`, and "Cascadia Mono" is not on a stock Windows 10, so the
   stack falls through to the `monospace` keyword — whatever the user's default
   fixed font happens to be, in practice often Courier New, whose Cyrillic is
   thin, narrow and visibly foreign next to Segoe UI at the 12px micro size
   that carries every label on this site. Consolas ships with every Windows
   since Vista and covers Cyrillic; DejaVu Sans Mono and Liberation Mono cover
   the Linux side. All three are far closer to Plex Mono in colour and width
   than the generic keyword, and all three carry tabular figures, which the
   ledger and the spec sheet depend on.
   ========================================================================== */

:root {
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas,
               "DejaVu Sans Mono", "Liberation Mono", monospace;
}

/* ==========================================================================
   § 2  RESET + GLOBAL DEFAULTS
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--space-4));
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--font-weight-body);
  line-height: var(--leading-normal);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* --- THE GLOBAL WRAP CONTRACT (rule 4 — set once, never repeated) --------- */
p, li, dd, blockquote, figcaption { text-wrap: pretty; }
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }

/* Cyrillic never hyphenates. Kept explicit so no component can re-enable it. */
body, p, li, h1, h2, h3, h4, h5, h6, dt, dd, figcaption, label, button, a {
  hyphens: manual;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  max-width: none;                     /* headings FILL their column — rule 5 */
  font-family: var(--font-display);
  font-weight: var(--font-weight-display);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--color-ink);
}
h1 { font-size: var(--fs-h1); line-height: var(--leading-tight); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

p, ul, ol, dl, figure, blockquote, table { margin: 0; }

/* --- THE GLOBAL IMG RULE (rule 6 — height:auto or every photo squeezes) --- */
img, svg, picture, video, canvas {
  display: block;
  max-width: 100%;
}
img, video, canvas { height: auto; }

a {
  color: var(--color-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
  margin: 0;
}

/* The list reset is for the DECORATIVE lists — .card-grid, .steps, the nav,
   the footer columns — where a bullet would be noise. It is NOT for lists in
   running prose, and on its own it strips the markers, the indent and the
   item spacing off the 22 legal items on /poveritelnost/, which then read as
   one run-on paragraph. § 4 restores all three inside .prose. Keep the two
   halves together: narrowing this selector instead would put markers back on
   every future decorative list by default. */
ul, ol { padding: 0; list-style: none; }

strong, b { font-weight: var(--font-weight-strong); }

/* Every flex/grid child can shrink — the phantom-column guard. */
.wrap > *, .hero__grid > *, .card-grid > *, .steps > *, .rail > * { min-width: 0; }

/* --- FOCUS: the two-tone ring, legible on ANY ground incl. the red button -- */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid transparent;      /* forced-colors fallback */
  outline-offset: 2px;
  box-shadow:
    0 0 0 2px var(--color-focus-ring-inner),
    0 0 0 4px var(--color-focus-ring);
  border-radius: var(--radius-sm);
}

::selection { background: var(--color-primary); color: var(--color-primary-ink); }

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

/* ==========================================================================
   § 3  HELPERS
   ========================================================================== */

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.skip-link {
  position: absolute; left: var(--space-4); top: var(--space-2);
  z-index: var(--z-toast);
  transform: translateY(-200%);
  background: var(--color-surface);
  color: var(--color-ink);
  padding: var(--space-3) var(--space-5);
  border: var(--rule-strong);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: transform var(--duration-base) var(--ease);
}
.skip-link:focus-visible { transform: translateY(0); }

.nowrap { white-space: nowrap; }

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-mono);
}

/* micro label — UPPERCASE MONO ONLY, never a sentence */
.micro {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
}

/* metadata row — the "·" is drawn by CSS so a separator can never start a line */
.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0;
}
.meta > * + *::before {
  content: "·";
  margin-inline: var(--space-2);
  color: var(--color-ink-muted);
}

/* The unmissable placeholder. BLOCKS DEPLOY — see index.html footer comment. */
.unconfirmed {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--color-danger);
  background: var(--color-danger-bg);
  border: var(--border-hairline) dashed var(--color-danger);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
}

/* ==========================================================================
   § 4  LAYOUT — the frame, the bands, the sections
   ========================================================================== */

.wrap {
  max-width: var(--container-max);
  margin-inline: auto;                 /* constrained blocks are ALWAYS centred */
  padding-inline: var(--container-pad);
}
.wrap--wide { max-width: var(--container-wide); }   /* dossier strip + ledger only */

.band { background: var(--color-bg); }
.band--surface { background: var(--color-surface); }
.band--sunken  { background: var(--color-surface-sunken); }
/* at most ONE inverted band per page */
.band--invert {
  background: var(--color-surface-invert);
  color: var(--color-surface-invert-ink);
}
.band--invert h1, .band--invert h2, .band--invert h3 { color: var(--color-surface-invert-ink); }
.band--invert .micro, .band--invert .sec-head__eyebrow { color: var(--color-invert-label); }
.band--invert a { color: var(--color-primary-invert); }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: var(--section-y-s); }
.section + .section { border-top: var(--rule-hair); }

/* prose: the measure lives on the TEXT, never on the wrapper */
.prose > p,
.prose > ul > li,
.prose > ol > li { max-width: var(--measure); }
.prose > * + * { margin-top: var(--space-5); }
.prose a { color: var(--color-link); }

/* A list in running prose is prose, so it gets its markers, its indent and its
   item spacing back — the § 2 reset is for decorative lists only. This is the
   whole of chapter 8 on /poveritelnost/: seven GDPR rights, the collected-data
   list and the processors list, 22 items that otherwise render as unindented
   unmarked lines with no gaps between them. `.prose > * + *` sets the rhythm
   BETWEEN blocks and targets the <ul> element, never its <li> children, so the
   item spacing has to be stated here.
   The indent is padding-inline-start on the list, which does not touch the
   measure: it already sits on the <li> two rules above and the list itself has
   no max-width, so the container stays the one measure. */
.prose ul { list-style: disc; padding-inline-start: 1.25em; }
.prose ol { list-style: decimal; padding-inline-start: 1.5em; }
.prose ul ul { list-style: circle; }
.prose li::marker { color: var(--color-ink-muted); }
.prose li + li { margin-top: var(--space-2); }

.lead { font-size: var(--fs-lead); max-width: var(--measure-lead); }

/* rail-and-spine: the shared metadata rail. The dossier extends this. */
.rail {
  display: grid;
  gap: var(--space-6) var(--space-8);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .rail { grid-template-columns: var(--spec-col-min) minmax(0, 1fr); }
}
@media (min-width: 64rem) { .rail { grid-template-columns: 200px minmax(0, 1fr); } }
@media (min-width: 80rem) { .rail { grid-template-columns: 216px minmax(0, 1fr); } }

/* ==========================================================================
   § 5  HEADER + NAV
   Three links. NO hamburger at any breakpoint. Below 1024px a persistent
   contact link rides in the header; below 768px "Контакти" yields to it.
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--color-bg);
  border-bottom: var(--rule-hair);
}
.site-header__inner {
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding-block: var(--space-3);
}

.brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  color: var(--color-ink);            /* the mark is NEVER red, NEVER on a photo */
  text-decoration: none;
}
/* HOVER IS GUARDED THROUGHOUT THIS FILE.
   Every `:hover` in site.css sits inside `@media (hover: hover) and
   (pointer: fine)`. A touch device fires `:hover` on tap and then keeps it
   until the user taps somewhere else, so an unguarded hover state paints a
   stale, unasked-for mark on the last thing that was touched. `:focus-visible`
   and `:focus-within` stay OUTSIDE the guard — those are keyboard and
   assistive states and they must reach every device. `:active` also stays
   outside: on touch it is the only channel that says "I heard you". */
@media (hover: hover) and (pointer: fine) {
  .brand:hover { color: var(--color-ink); }
}

/* SM cut of the mark, at every breakpoint. currentColor, transparent, no square.
   Two lockups ship: the horizontal one, and the symbol alone below 36rem, where
   the 240px lockup plus three nav items no longer fit a 390px header. Both are
   the SAME SM cut — this is a lockup swap, not a second mark. */
.ig-logo, .ig-mark {
  height: var(--logo-h);
  width: auto;
  max-width: 100%;
}
.ig-mark { display: none; }
@media (max-width: 35.9375rem) {
  .brand .ig-logo { display: none; }
  .brand .ig-mark { display: block; }
}
@media (min-width: 48rem) { :root { --logo-h: 38px; } }

.nav {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: clamp(var(--space-4), 2.4vw, var(--space-8));
}
.nav__link {
  position: relative;
  color: var(--color-ink);
  text-decoration: none;
  font-weight: var(--font-weight-medium);
  font-size: var(--fs-small);
  padding-block: var(--space-2);
  background-image: linear-gradient(var(--color-primary), var(--color-primary));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 var(--border-rule);
  transition: background-size var(--duration-fast) var(--ease);
}
.nav__link:focus-visible { background-size: 100% var(--border-rule); }
.nav__link[aria-current="page"] { background-size: 100% var(--border-rule); }
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover { background-size: 100% var(--border-rule); }
}

/* Compound selectors on purpose: these elements also carry `.btn`, whose own
   `display: inline-flex` sits LATER in the file. A single-class rule here would
   lose on source order and the mobile swap would silently never happen. */
.nav > .nav__cta { flex: 0 0 auto; }
.nav > .header-contact { display: none; }

@media (max-width: 63.9375rem) {
  .nav > .header-contact { display: inline-flex; align-items: center; }
  .nav > .nav__cta { display: none; }
}
@media (max-width: 47.9375rem) {
  .nav > .nav__link--contact { display: none; }
  .site-header__inner { gap: var(--space-4); }
  .nav > .header-contact { padding-inline: var(--space-4); }
}
/* Tight phones (<= ~424px): shrink the nav gap + contact-button padding so the
   header row clears the page gutter instead of overflowing ~11px at 360px;
   below 320px drop the three text links to brand + the contact CTA (same
   pattern as .nav__link--contact at <=48rem). */
@media (max-width: 26.5rem) {
  .nav { gap: var(--space-3); }
  .nav > .header-contact { padding-inline: var(--space-3); }
}
@media (max-width: 20rem) {
  .nav > .nav__link { display: none; }
}

/* ==========================================================================
   § 6  BUTTONS
   48px minimum target: hands on site are in gloves.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  padding-inline: var(--control-pad-x);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--control-radius);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--ease),
    border-color var(--duration-fast) var(--ease),
    color var(--duration-fast) var(--ease),
    transform var(--duration-fast) var(--ease);
}

.btn--primary {
  background: var(--btn-bg);
  color: var(--btn-ink);
  border-color: var(--btn-bg);
}
.btn--primary:focus-visible { background: var(--btn-bg-hover); border-color: var(--btn-bg-hover); color: var(--btn-ink); }

.btn--secondary {
  background: transparent;
  color: var(--color-ink);
  border: var(--control-border);
}
.btn--secondary:focus-visible { background: var(--color-surface); }

.band--invert .btn--secondary {
  color: var(--color-surface-invert-ink);
  border-color: var(--color-line-invert);
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover { background: var(--btn-bg-hover); border-color: var(--btn-bg-hover); color: var(--btn-ink); }
  .btn--secondary:hover { background: var(--color-surface); }
  .band--invert .btn--secondary:hover { background: rgba(255, 255, 255, 0.06); }
}

/* PRESS. On a phone, in gloves, there is no hover at all, so the press state is
   the only channel that says the tap registered. A monotonic settle on the
   brand curve: the scale goes down and comes back and never overshoots,
   because a precision instrument does not bounce (BUILD-NOTES decision 5).
   Deliberately OUTSIDE the hover guard — touch is exactly who needs it — and
   deliberately AFTER it, so that on a mouse, where both states are live at
   once, the press reads over the hover instead of losing to it on source
   order. The reduced-motion rule in § 2 collapses the duration; the state
   change itself survives, instant and still legible. */
.btn:active { transform: scale(0.98); }
.btn--primary:active { background: var(--btn-bg-hover); border-color: var(--btn-bg-hover); color: var(--btn-ink); }
.btn--secondary:active { background: var(--color-surface-sunken); }
.band--invert .btn--secondary:active { background: rgba(255, 255, 255, 0.12); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
}

/* running text link — underline-grow, the only link motion in the system */
.link {
  color: var(--color-link);
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 var(--border-hairline);
  transition: background-size var(--duration-fast) var(--ease);
}
.link:focus-visible { background-size: 100% var(--border-hairline); }
@media (hover: hover) and (pointer: fine) {
  .link:hover { background-size: 100% var(--border-hairline); }
}

.link--strong { font-weight: var(--font-weight-medium); }

/* ==========================================================================
   § 7  SECTION HEADERS
   The eyebrow is mono caps. The title FILLS its column. The standfirst is
   prose and therefore carries the reading measure — that is the correct
   division and the one qa_layout_measure.py expects.
   ========================================================================== */

.sec-head {
  max-width: none;                     /* rule 5 — fills, never left-hugs */
  margin-bottom: var(--space-10);
}
.sec-head__eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: var(--rule-strong);
}
.sec-head__title { max-width: none; }
.sec-head__stand {
  margin-top: var(--space-4);
  font-size: var(--fs-lead);
  max-width: var(--measure-lead);
  color: var(--color-ink);
}

/* ==========================================================================
   § 8  HERO — split 7/5, text one side, ONE plate the other.
   Never a full-bleed photograph with text laid over it: a scrim over a real
   site photograph turns evidence into decoration.
   ========================================================================== */

.hero { padding-block: clamp(var(--space-12), 5.5vw, var(--space-20)); }

.hero__grid {
  display: grid;
  gap: clamp(var(--space-8), 4vw, var(--space-16));
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 64rem) {
  .hero__grid { grid-template-columns: 7fr 5fr; }
}

.hero__copy { max-width: none; }

.hero__eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
  margin-bottom: var(--space-5);
}

.hero__title {
  max-width: none;                     /* fills the 7-track — rule 5 */
  font-size: var(--fs-hero);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.hero__stand {
  margin-top: var(--space-6);
  font-size: var(--fs-lead);
  max-width: var(--measure-lead);
  color: var(--color-ink);
}

.hero__actions { margin-top: var(--space-8); }

.hero__note {
  margin-top: var(--space-5);
  font-size: var(--fs-small);
  color: var(--color-ink-muted);
  max-width: var(--measure-lead);
}

.hero__plate { justify-self: stretch; }
@media (max-width: 63.9375rem) {
  .hero__plate { max-width: 32rem; margin-inline: auto; width: 100%; }
}

/* ==========================================================================
   § 9  EVIDENCE PLATE — a photograph framed as an exhibit
   Bordered, seated on the inverted panel, captioned in mono. Never wallpaper,
   never text laid over the image.
   ========================================================================== */

.plate {
  background: var(--plate-bg);
  border: var(--plate-border);
  border-radius: var(--plate-radius);
  padding: var(--space-3);
  color: var(--color-invert-label);
}
.plate__frame {
  position: relative;
  border: var(--border-hairline) solid var(--color-line-invert);
  background: var(--color-surface-invert);
  overflow: hidden;
}
.plate__frame > img,
.plate__frame > picture > img { width: 100%; }

.plate__cap {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--plate-cap-fs);
  line-height: 1.5;
  letter-spacing: var(--tracking-mono);
  font-variant-numeric: tabular-nums;
  color: var(--plate-cap-ink);
}
/* No max-width on the caption. The plate is already the measure, and the
   caption it actually carries — „25.07.2024 · посещение 01 от 12 · 9 кадъра" —
   is 42 characters, so a 32ch cap wrapped and orphaned it inside a plate wide
   enough to hold the whole line. A second constraint inside a measured
   container is the shape decision 1 of BUILD-NOTES forbids. */
.plate__cap b { color: var(--color-surface-invert-ink); font-weight: var(--font-weight-medium); }

/* a plate sitting on the light ground rather than in a dark band */
.plate--flush { padding: var(--space-2); }

/* ==========================================================================
   § 10  SPEC SHEET — ONE <dl> that IS the grid.
   Per-row wrappers are FORBIDDEN: they make every label column measure itself
   and the rail goes ragged. All <dt>/<dd> are direct children.
   ========================================================================== */

.spec-sheet {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3) var(--space-8);
  margin: 0;
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  background: var(--spec-bg);
  color: var(--spec-ink);
  border-radius: var(--radius-md);
}
@media (min-width: 48rem) {
  .spec-sheet {
    grid-template-columns: minmax(var(--spec-col-min), max-content) minmax(0, 1fr);
    gap: var(--spec-row-gap) var(--space-10);
  }
}

.spec-sheet dt {
  font-family: var(--font-mono);
  font-size: var(--spec-label-fs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--spec-label-ink);
  align-self: baseline;
  padding-top: var(--space-1);
}
.spec-sheet dd {
  margin: 0;
  min-width: 0;
  font-size: var(--spec-value-fs);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-snug);
  font-variant-numeric: tabular-nums;
  color: var(--spec-ink);
}
.spec-sheet dd small {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  font-weight: var(--font-weight-body);
  color: var(--spec-label-ink);
}
/* hairline between pairs, drawn on the panel */
@media (min-width: 48rem) {
  .spec-sheet dt:not(:first-of-type),
  .spec-sheet dd:not(:first-of-type) {
    border-top: var(--spec-rule);
    padding-top: var(--space-5);
  }
}

/* Below 48rem the grid is ONE column, so a uniform row-gap put the same space
   between a label and its value as between one pair and the next: five
   label/value pairs dissolved into ten evenly spaced lines, on the evidence
   header of the dossier and the proof block of the home page, on the device
   this brand explicitly designs for. `.facts` already gets this right outside
   any media query — the fix is to give .spec-sheet the same shape. The label
   sits tight to its value, the hairline moves onto the <dt> and separates the
   pairs. Desktop is untouched: everything here is behind the max-width. */
@media (max-width: 47.9375rem) {
  .spec-sheet { gap: var(--space-2) var(--space-8); }
  .spec-sheet dt:not(:first-of-type) {
    border-top: var(--spec-rule);
    margin-top: var(--space-5);
    padding-top: var(--space-5);
  }
}

/* ==========================================================================
   § 11  CARDS + STEPS — hairline frames, no shadow, no hover-lift.
   Replacement for hover-lift is mark-on-hover: the ground changes and a 2px
   rule appears on the leading edge. A sign that appears, not a card that floats.
   ========================================================================== */

.card-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-6);
  background: var(--color-surface);
  border: var(--rule-hair);
  border-inline-start: var(--border-rule) solid transparent;
  border-radius: var(--radius-md);
}
/* NO HOVER ON .card, ON PURPOSE. The four service cards hold a span, an h3 and
   a p — no anchor, no button, no input, nothing focusable — so mark-on-hover
   here was a promise the card cannot keep: the reader hovers, the ground
   changes, a 2px primary rule appears on the leading edge, they click, and
   nothing happens, directly above the „Вижте четирите услуги" link that is the
   real destination. `.card:focus-within` was unreachable for the same reason.
   `.step` two rules below has never had a hover, and that was the tell.
   If the cards ever become links, mark-on-hover comes back — inside the hover
   guard, with the transition list restored:
     background-color and border-inline-start-color, --duration-fast, --ease. */
.card__index {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
  font-variant-numeric: tabular-nums;
}
.card__title { font-size: var(--fs-h3); max-width: none; }
.card__body { color: var(--color-ink-muted); font-size: var(--fs-small); max-width: var(--measure); }

.steps {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  counter-reset: step;
}
.step {
  min-width: 0;
  padding-top: var(--space-5);
  border-top: var(--border-rule) solid var(--color-line-strong);
}
.step__num {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tracking-caps);
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--space-3);
}
.step__title { font-size: var(--fs-h3); max-width: none; }
.step__body { margin-top: var(--space-3); color: var(--color-ink-muted); max-width: var(--measure); }

/* a bare list of verifiable facts — same discipline as .spec-sheet:
   ONE <dl> that IS the grid, all <dt>/<dd> direct children, no row wrappers. */
.facts {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2) var(--space-8);
  margin: 0;
}
@media (min-width: 48rem) {
  .facts { grid-template-columns: var(--spec-col-min) minmax(0, 1fr); gap: var(--space-4) var(--space-8); }
}
.facts dt {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
  font-weight: var(--font-weight-body);
  padding-top: var(--space-1);
}
.facts dd { margin: 0; min-width: 0; max-width: var(--measure); }
.facts dt:not(:first-of-type) {
  border-top: var(--rule-hair);
  margin-top: var(--space-3);
  padding-top: var(--space-5);
}
@media (min-width: 48rem) {
  .facts dd:not(:first-of-type) {
    border-top: var(--rule-hair);
    margin-top: var(--space-3);
    padding-top: var(--space-5);
  }
}

/* spacing utilities — the ONLY sanctioned way to add block rhythm inside a
   section. Never an inline style; never a new bespoke margin. */
.mt-6  { margin-top: var(--space-6); }
.mt-8  { margin-top: var(--space-8); }
.mt-10 { margin-top: var(--space-10); }
.mt-12 { margin-top: var(--space-12); }

/* ==========================================================================
   § 12  FORMS  (shared, so /kontakti/ does not invent a second set)
   ========================================================================== */

.form { display: grid; gap: var(--space-6); max-width: 34rem; }
.field { display: grid; gap: var(--space-2); min-width: 0; }
.field__label {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
}
.input, .textarea, .select {
  width: 100%;
  min-height: var(--control-h);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  color: var(--color-ink);
  border: var(--control-border);
  border-radius: var(--control-radius);
  font-size: var(--fs-body);
  transition: border-color var(--duration-fast) var(--ease);
}
.textarea { min-height: 8rem; resize: vertical; line-height: var(--leading-normal); }
@media (hover: hover) and (pointer: fine) {
  .input:hover, .textarea:hover, .select:hover { border-color: var(--color-ink-muted); }
}
.help { font-size: var(--fs-small); color: var(--color-ink-muted); }

.msg { padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); font-size: var(--fs-small); }
.msg--ok  { background: var(--color-success-bg); color: var(--color-ink); border-inline-start: var(--border-rule) solid var(--color-success); }
/* error is never a fill: the CTA is the only filled red on the page */
.msg--err { background: var(--color-danger-bg); color: var(--color-danger); border-inline-start: var(--border-rule) solid var(--color-danger); }

/* ==========================================================================
   § 13  FOOTER
   ========================================================================== */

.site-footer {
  background: var(--color-surface-sunken);
  border-top: var(--rule-strong);
  padding-block: var(--space-12) var(--space-8);
  font-size: var(--fs-small);
}
.site-footer__grid {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .site-footer__grid { grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)); gap: var(--space-10); }
}
.footer-col { min-width: 0; display: grid; gap: var(--space-3); align-content: start; }
.footer-col h2 {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  font-weight: var(--font-weight-body);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-ink-muted);
}
.footer-col ul { display: grid; gap: var(--space-2); }
.footer-col a { color: var(--color-ink); text-decoration: none; }
.footer-col a:focus-visible { text-decoration: underline; }
@media (hover: hover) and (pointer: fine) {
  .footer-col a:hover { text-decoration: underline; }
}
.footer-col .ig-logo { height: 30px; color: var(--color-ink); }

.footer-legal {
  margin-top: var(--space-10);
  padding-top: var(--space-6);
  border-top: var(--rule-hair);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  color: var(--color-ink-muted);
  font-size: var(--fs-micro);
  font-family: var(--font-mono);
}

/* ==========================================================================
   § 14  MOTION — reveal-on-scroll, section blocks ONLY.
   Zero JavaScript: scroll-driven animations where supported, fully visible
   everywhere else. A record that appears in pieces becomes a slideshow, so
   this never goes on an individual ledger row.
   ========================================================================== */

@keyframes ig-reveal {
  from { opacity: 0; transform: translateY(var(--reveal-rise)); }
  to   { opacity: 1; transform: none; }
}

/* `animation-duration` is IGNORED once `animation-timeline` resolves to a
   progress timeline — the real extent of the fade is the scroll distance in
   `animation-range`, and the declared 240ms never runs. `entry 8% cover 20%`
   spanned 250-450px of scroll depending on section height, which is not a
   reveal, it is a scrub: a reader who stops mid-range parks the copy at about
   opacity 0.5 and it stays there. `entry 0% entry 30%` closes it inside the
   element's own entry, so the section is fully opaque as soon as it is
   properly on screen and there is no partial state to sit in.
   --duration-slow is left in the shorthand on purpose: it is what the
   animation falls back to if the timeline ever fails to resolve. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: ig-reveal var(--duration-slow) var(--ease) both;
      animation-timeline: view();
      animation-range: entry 0% entry 30%;
    }
  }
}

/* ==========================================================================
   § 15  PRINT — the reader forwards this link upward and prints it in a report.
   ========================================================================== */

@media print {
  :root { --container-pad: 0; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .site-header, .skip-link, .nav, .hero__actions, .btn { display: none !important; }
  .band--invert, .spec-sheet, .plate, .plate__frame {
    background: #fff !important;
    color: #000 !important;
    border: 1px solid #000 !important;
    box-shadow: none !important;
  }
  .spec-sheet dt, .plate__cap, .micro { color: #333 !important; }
  details { display: block; }
  details > summary { list-style: none; }
  a[href^="http"]::after,
  a[href^="/"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #444; }
  .section { padding-block: 12pt; break-inside: avoid; }
  .reveal { animation: none !important; opacity: 1 !important; transform: none !important; }
}
