@charset "utf-8";
/* ===========================================================================
   RED EPOS — main.css
   One stylesheet. No imports, no framework, no build step, no !important.

   The design system is INHERITED from the approved prototype at
   /var/www/redepos.co.uk/mockup/redepos/ (brand-spec.md + styles.css).
   Token names match that codebase deliberately so the two stay comparable.

   Order
     1  Design tokens
     2  Ink / paper grounds
     3  Reset and base
     4  Typography
     5  Layout primitives
     6  Components
     7  Utilities
     8  Responsive refinements
     9  Reduced motion and print

   Contrast: this palette has two grounds, so every pair was measured twice.
   Values are noted beside anything close to the line. Three tokens were
   adjusted to reach WCAG 2.2 AA — see §1 and docs/DESIGN-NOTES.md.
   =========================================================================== */


/* ===========================================================================
   1  DESIGN TOKENS
   =========================================================================== */

:root {
  /* --- ground (inherited, unchanged) ------------------------------------ */
  --ink:    #0b0d10;   /* deepest ground   */
  --ink-2:  #11151a;   /* raised surface   */
  --ink-3:  #181d24;   /* card / panel     */
  --line:   #2b313a;   /* hairline border  */

  /* --- paper (inherited, unchanged) ------------------------------------- */
  --paper:   #f1efe9;
  --paper-2: #dfdcd4;
  --white:   #fffdf8;

  /* --- text (inherited) -------------------------------------------------- */
  --muted: #a6a8ab;    /* secondary text ON INK ONLY. 8.2:1 on --ink,
                          7.1:1 on --ink-3. Only 2.1:1 on paper — never
                          use it on a paper ground. */

  /* ADJUSTED FOR CONTRAST.
     The inherited --muted-dark was #6f7680, which is 3.99:1 on --paper and
     3.35:1 on --paper-2 — both below the 4.5:1 needed for body text. Darkened
     to #565d67, same cool-grey hue: 5.8:1 on --paper, 4.9:1 on --paper-2,
     6.5:1 on --white. Paper grounds only; it is 2.9:1 on --ink. */
  --muted-dark: #565d67;

  /* --- signal (inherited, unchanged) ------------------------------------ */
  --signal:      #ff413a;  /* identity and urgency. 5.6:1 on --ink.
                              Only 3.0:1 on --paper: large text and non-text
                              use there, never body copy. */
  --signal-deep: #c82723;  /* pressed / deep state, and the red that is safe
                              as body text on paper: 4.8:1 on --paper. */

  /* Deadline semantics ONLY, per the inherited brand spec. Not for success
     messages, not for "included" ticks, not for the 0% commission line.
     Both also fail on paper (1.6:1 and 1.8:1), so they are ink-ground only. */
  --amber: #f2b63d;
  --green: #37cc83;

  /* ADDED FOR CONTRAST.
     --line is 1.5:1 on --ink. That is correct for a decorative hairline but
     fails WCAG 2.2 1.4.11 (3:1) where a border is the only thing identifying
     a control. Form fields and outlined buttons use these instead:
       --edge-ink   4.1:1 / 3.9:1 / 3.6:1 on --ink / --ink-2 / --ink-3
       --edge-paper 4.0:1 / 3.3:1 / 4.5:1 on --paper / --paper-2 / --white */
  --edge-ink:   #6b7482;
  --edge-paper: #76766e;
  --rule-paper: #c9c5bc;   /* decorative hairline on paper, mirrors --line */

  /* --- families (inherited) ---------------------------------------------- */
  --display: "Space Grotesk", "Segoe UI", "Trebuchet MS", sans-serif;
  --body:    "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, Consolas, monospace;

  /* --- type scale --------------------------------------------------------
     Floors are lower than the prototype's so a long word cannot overflow a
     320px viewport; the desktop end of every clamp is unchanged. */
  --fs-hero:    clamp(2.9rem, 7.2vw, 8.4rem);
  --fs-display: clamp(2.4rem, 6.2vw, 7rem);    /* section openings          */
  --fs-h2:      clamp(1.8rem, 3.2vw, 2.8rem);  /* ordinary section headings */
  --fs-h3:      clamp(1.5rem, 2.2vw, 2.1rem);  /* ledger / feature headings */
  --fs-h4:      1.12rem;
  --fs-lede:    clamp(1.08rem, 1.4vw, 1.34rem);
  --fs-body:    1rem;
  --fs-sm:      0.9rem;
  --fs-xs:      0.8rem;
  --fs-mono:    0.73rem;

  /* --- spacing ----------------------------------------------------------- */
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-5:  20px;
  --s-6:  28px;
  --s-7:  36px;
  --s-8:  48px;
  --s-9:  64px;
  --s-10: 88px;
  --s-11: 120px;

  /* --- layout ------------------------------------------------------------ */
  --container:    min(91vw, 1500px);
  --gutter:       max(4.5vw, 1.25rem);
  --measure:      64ch;
  --measure-tight: 56ch;
  --header-h:     84px;
  --header-h-sm:  68px;
  --section-y:    clamp(5rem, 10vw, 11rem);
  --section-y-lg: clamp(7rem, 13vw, 15rem);

  /* --- shape (inherited spec) -------------------------------------------- */
  --r-control:    10px;   /* controls 8–12px       */
  --r-control-sm: 8px;
  --r-panel:      20px;   /* product panels 18–24px */
  --r-panel-lg:   24px;
  --r-shell:      28px;   /* editorial shells       */
  --r-pill:       999px;

  --shadow: 0 48px 120px rgba(0, 0, 0, .46);
  --shadow-near: 0 18px 50px rgba(0, 0, 0, .35);

  /* --- motion (inherited) ------------------------------------------------ */
  --ease:     cubic-bezier(.2, .8, .2, 1);
  --t-ui:     200ms;   /* micro-interactions 180–240ms */
  --t-reveal: 650ms;   /* entry reveals 550–750ms      */
  --stagger:  75ms;    /* sibling stagger 60–90ms      */
}


/* ===========================================================================
   2  INK AND PAPER GROUNDS

   The site alternates dark technical sections with warm editorial ones. Every
   component below is written against these semantic aliases rather than the
   raw palette, so a component dropped into either ground is automatically
   readable. Never hard-code --paper or --ink inside a component.

     --bg          section ground
     --bg-raised   raised surface (cards)
     --bg-panel    deeper / secondary surface
     --fg          body text
     --fg-strong   headings and emphasis
     --fg-muted    secondary text
     --rule        decorative hairline
     --edge        control boundary (3:1 guaranteed)
     --signal-text the red that is legible as text on this ground
   =========================================================================== */

:root,
.on-ink {
  --bg:          var(--ink);
  --bg-raised:   var(--ink-2);
  --bg-panel:    var(--ink-3);
  --fg:          var(--paper);
  --fg-strong:   var(--white);
  --fg-muted:    var(--muted);
  --rule:        var(--line);
  --edge:        var(--edge-ink);
  --signal-text: var(--signal);
  --wash:        rgba(255, 255, 255, .05);
}

/* .slab-paper is the paper editorial slab (§6.17). It is listed here rather
   than repeating the token block, so there stays exactly one definition of
   what "the paper ground" means. */
.on-paper,
.slab-paper {
  --bg:          var(--paper);
  --bg-raised:   var(--white);
  --bg-panel:    var(--paper-2);
  --fg:          var(--ink);
  --fg-strong:   var(--ink);
  --fg-muted:    var(--muted-dark);
  --rule:        var(--rule-paper);
  --edge:        var(--edge-paper);
  --signal-text: var(--signal-deep);
  --wash:        rgba(11, 13, 16, .05);
}

.on-ink,
.on-paper,
.slab-paper {
  color: var(--fg);
  background: var(--bg);
}


/* ===========================================================================
   3  RESET AND BASE
   =========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background: var(--ink);
  scrollbar-gutter: stable;
}

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, ol, ul {
  margin: 0;
}

ul[class],
ol[class] {
  list-style: none;
  padding: 0;
}

body {
  /* overflow-x: clip is a safety net. It does not create a scroll container,
     so the sticky header and the sticky sequences still work. */
  overflow-x: clip;
  min-height: 100vh;
  color: var(--paper);
  background: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.55;
  /* Guards against one long word — a display heading, an email address, a
     product code — pushing past a 320px viewport. */
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

img { height: auto; }

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

button { cursor: pointer; }

a { color: var(--signal-text, var(--signal)); text-underline-offset: .18em; }
a:hover { color: var(--signal); }
.on-paper a:hover,
.slab-paper a:hover { color: var(--signal-deep); }

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

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: var(--s-8) 0;
}

:target { scroll-margin-top: calc(var(--header-h) + var(--s-4)); }

/* --- focus -----------------------------------------------------------------
   A single colour cannot clear 3:1 on ink, on paper and on a red button fill,
   so the ring is two-tone: a --white inner ring (19.1:1 on ink, 3.4:1 on the
   signal fill) inside a --ink outer ring (16.9:1 on paper, 5.6:1 on signal).
   One of the two always contrasts, whatever it lands on. */
:focus-visible {
  outline: 3px solid var(--white);
  outline-offset: 0;
  box-shadow: 0 0 0 6px var(--ink);
  border-radius: var(--r-control-sm);
}

@supports not selector(:focus-visible) {
  a:focus,
  button:focus,
  input:focus,
  select:focus,
  textarea:focus,
  summary:focus,
  [tabindex]:focus {
    outline: 3px solid var(--white);
    box-shadow: 0 0 0 6px var(--ink);
  }
}

::selection {
  color: var(--white);
  background: var(--signal-deep);
}


/* ===========================================================================
   4  TYPOGRAPHY
   =========================================================================== */

h1, h2, h3, h4, h5, h6 {
  font-family: var(--display);
  color: var(--fg-strong);
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: .86;
  letter-spacing: -.068em;
}

h2 {
  font-size: var(--fs-h2);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -.04em;
}

h3 {
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -.035em;
}

h4 {
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.01em;
}

/* .display — section openings. Genuinely large, short lines, heavy negative
   space. This is the §5.1 "type at scale" instrument. */
.display {
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: .9;
  letter-spacing: -.062em;
  max-width: 16ch;
}
.display-wide { max-width: 24ch; }
.display em,
h1 em,
h2 em { font-style: normal; color: var(--signal-text); }

p { margin: 0 0 1rem; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

/* --- eyebrows --------------------------------------------------------------
   Three variants, all mono and letterspaced.
     .eyebrow        plain label
     .section-index  the prototype's numbered convention: 01 / What we do
     .kicker         label with a short red rule in front of it            */
.eyebrow,
.section-index,
.kicker {
  display: block;
  margin: 0 0 1.4rem;
  max-width: none;
  font-family: var(--mono);
  font-size: var(--fs-mono);
  font-weight: 600;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.section-index .n {
  color: var(--signal-text);
  margin-right: .35em;
}

.kicker {
  display: flex;
  align-items: center;
  gap: .7rem;
  color: var(--fg);
}
.kicker::before {
  content: "";
  flex: 0 0 auto;
  width: 35px;
  height: 2px;
  background: var(--signal);
}

.lede {
  max-width: var(--measure-tight);
  color: var(--fg-muted);
  font-size: var(--fs-lede);
  line-height: 1.58;
}
.lede-bright { color: var(--paper-2); }
.on-paper .lede-bright,
.slab-paper .lede-bright { color: var(--muted-dark); }

.small { font-size: var(--fs-sm); }
.mono  { font-family: var(--mono); font-variant-numeric: tabular-nums; }

ul.list,
ol.list {
  max-width: var(--measure);
  margin: 0 0 1rem;
  /* The reset at the top strips markers from every ul[class]/ol[class]. This is
     the one list that wants them back, so restore explicitly. */
  list-style: revert;
  padding-left: 1.15em;
  color: var(--fg-muted);
}
ul.list li,
ol.list li { margin-bottom: .5rem; }
ul.list li::marker { color: var(--signal-text); }

blockquote {
  max-width: var(--measure);
  padding-left: var(--s-5);
  border-left: 2px solid var(--signal);
  color: var(--fg-muted);
}

code, kbd, samp {
  padding: .12em .38em;
  border: 1px solid var(--rule);
  border-radius: 5px;
  background: var(--bg-panel);
  color: var(--fg-strong);
  font-family: var(--mono);
  font-size: .88em;
}


/* ===========================================================================
   5  LAYOUT PRIMITIVES
   =========================================================================== */

/* The inherited container is min(91vw, 1500px) — wider than a typical
   marketing site, on purpose. The max-width floor keeps a usable gutter at
   320px, where 91vw would leave only 14px a side. */
.wrap {
  width: var(--container);
  max-width: calc(100% - 40px);
  margin-inline: auto;
}

/* The narrow reading column. It used to set width only, on the assumption it
   would always be written as class="wrap wrap-narrow" and inherit the centring
   from .wrap. legal.html wrote it on its own and the whole page sat flush
   against the left edge with no gutter. It now stands up by itself; pairing it
   with .wrap is still correct and changes nothing. */
.wrap-narrow {
  width: min(91vw, 900px);
  max-width: calc(100% - 40px);
  margin-inline: auto;
}
.wrap-bleed  { padding-inline: var(--gutter); }

.section { padding-block: var(--section-y); }
.section-lg { padding-block: var(--section-y-lg); }
.section-tight { padding-block: clamp(2.5rem, 5vw, 4.5rem); }

/* Full-bleed moment: edge to edge, near viewport height. Sparingly. */
.section-full {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: min(86vh, 940px);
  min-height: min(86svh, 940px);
  padding-block: var(--section-y-lg);
}

/* Section head: index/eyebrow in a narrow left column, the statement beside
   it. Collapses to one column below 820px. */
.section-head {
  display: grid;
  grid-template-columns: .44fr 1.56fr;
  gap: 4vw;
  margin-bottom: clamp(3rem, 7vw, 8rem);
}
.section-head h2,
.section-head .display { margin: 0; }
.section-head .lede { margin-top: var(--s-5); }

.section-head-stacked {
  display: block;
  max-width: var(--measure);
}

/* Centred opener. The outer max-width is in rem, not ch: a ch on this element
   is measured in the body font, which would crush the display type inside it
   to two or three characters a line. The heading keeps its own ch measure,
   which resolves correctly against the display font size. */
.section-head-center {
  display: block;
  max-width: min(100%, 62rem);
  margin-inline: auto;
  text-align: center;
}
.section-head-center .display,
.section-head-center h1,
.section-head-center h2,
.section-head-center .lede { margin-inline: auto; }

/* --- grid helpers --------------------------------------------------------
   min() guards every minimum track so nothing can overflow at 320px. */
.grid-2 {
  display: grid;
  gap: clamp(1.5rem, 3vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
}

.grid-3 {
  display: grid;
  gap: clamp(1.2rem, 2.4vw, 2.4rem);
  grid-template-columns: repeat(auto-fit, minmax(min(290px, 100%), 1fr));
}

.grid-4 {
  display: grid;
  gap: clamp(1rem, 1.8vw, 1.6rem);
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
}

.split {
  display: grid;
  gap: clamp(2.5rem, 6vw, 7rem);
  grid-template-columns: 1fr 1fr;
  align-items: center;
}
.split-lead  { grid-template-columns: .85fr 1.15fr; }
.split-trail { grid-template-columns: 1.15fr .85fr; }

/* Grid and flex children take a content-based minimum size by default, so one
   unbreakable string can blow a track out and scroll the whole page sideways.
   min-width:0 lets the track shrink; the overflow-wrap on <body> then breaks
   the string. Verified at 320px in headless Chrome. */
.hero-inner > *,
.split > *,
.grid-2 > *,
.grid-3 > *,
.grid-4 > *,
.card-grid > *,
.download-grid > *,
.feature-row > *,
.section-head > *,
.sticky-seq-inner > *,
.site-header-inner > *,
.site-footer-grid > *,
.ledger-row > *,
.spec-list dt,
.spec-list dd,
.download-card > * {
  min-width: 0;
}

.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-7); }

.cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem;
  margin-top: var(--s-8);
}


/* ===========================================================================
   6  COMPONENTS
   =========================================================================== */

/* --- 6.1  skip link ------------------------------------------------------ */
.skip-link {
  position: fixed;
  top: .75rem;
  left: .75rem;
  z-index: 100;
  padding: .7rem 1rem;
  border-radius: var(--r-control-sm);
  color: var(--ink);
  background: var(--paper);
  font-weight: 700;
  text-decoration: none;
  transform: translateY(-180%);
  transition: transform var(--t-ui) var(--ease);
}
.skip-link:focus {
  transform: translateY(0);
  color: var(--ink);
}


/* --- 6.2  site header ---------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  border-bottom: 1px solid transparent;
  background: var(--ink);
  transition:
    background-color var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease);
}

@supports (backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px)) {
  .site-header {
    background: rgba(11, 13, 16, .88);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    backdrop-filter: blur(18px) saturate(140%);
  }
}

.site-header.is-scrolled { border-color: rgba(255, 255, 255, .09); }

/* Same container as .wrap, so the header gutters line up with the content
   gutters at every width. Alignment is the point. */
.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1rem, 3vw, 2.5rem);
  width: var(--container);
  max-width: calc(100% - 40px);
  margin-inline: auto;
  min-height: var(--header-h);
  transition: min-height var(--t-ui) var(--ease);
}
.site-header.is-scrolled .site-header-inner { min-height: var(--header-h-sm); }

.brand {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  width: 132px;
  text-decoration: none;
}
.brand-logo { width: 100%; height: auto; }

.nav-toggle-wrap { display: flex; align-items: center; gap: var(--s-4); }


/* --- 6.3  nav ------------------------------------------------------------ */
.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(.7rem, 1.8vw, 2.2rem);
}

.nav-link {
  position: relative;
  display: inline-block;
  padding: .55rem 0;
  color: var(--muted);          /* 8.2:1 on --ink */
  font-size: .85rem;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  transition: color var(--t-ui) var(--ease);
}
.nav-link:hover { color: var(--paper); }

.nav-link::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: .1rem;
  left: 0;
  height: 2px;
  background: var(--signal);
  opacity: 0;
  transition: opacity var(--t-ui) var(--ease);
}

.nav-link.is-current {
  color: var(--white);
  font-weight: 600;
}
.nav-link.is-current::after { opacity: 1; }

/* Hidden when JavaScript is off, so the wrapped .nav is used instead. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--edge-ink);   /* 4.1:1 boundary */
  border-radius: var(--r-control-sm);
  background: none;
  color: var(--paper);
}
.nav-toggle:hover { border-color: var(--paper); }

.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 20px;
  height: 1px;
  background: currentColor;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: "";
  position: absolute;
  left: 0;
  transition: transform var(--t-ui) var(--ease);
}
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after  { top: 6px; }

.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { top: 0; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after  { top: 0; transform: rotate(-45deg); }


/* --- 6.4  mobile drawer --------------------------------------------------
   Closed by default. visibility:hidden takes it out of the tab order without
   depending on JavaScript or on [inert] support. */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(6, 8, 10, .74);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-ui) var(--ease), visibility 0s linear var(--t-ui);
}

.nav-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 80;
  width: min(360px, 88vw);
  padding: var(--s-6) var(--s-6) var(--s-8);
  overflow-y: auto;
  overscroll-behavior: contain;
  border-left: 1px solid var(--line);
  background: var(--ink-2);
  transform: translateX(102%);
  visibility: hidden;
  transition: transform 260ms var(--ease), visibility 0s linear 260ms;
}

.nav-drawer.is-open,
.nav-scrim.is-open {
  visibility: visible;
  transition-delay: 0s;
}
.nav-drawer.is-open { transform: translateX(0); }
.nav-scrim.is-open  { opacity: 1; }

.nav-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 46px;
  margin-bottom: var(--s-6);
}
.nav-drawer-head .eyebrow { margin-bottom: 0; }

.nav-drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border: 1px solid var(--edge-ink);
  border-radius: var(--r-control-sm);
  background: none;
  color: var(--paper);
  font-size: 1.1rem;
  line-height: 1;
}
.nav-drawer-close:hover { border-color: var(--paper); }

.nav-drawer-list {
  display: flex;
  flex-direction: column;
  margin: 0 0 var(--s-6);
  padding: 0;
  list-style: none;
}

.nav-drawer-link {
  display: block;
  padding: .9rem 0;
  border-bottom: 1px solid var(--line);
  border-left: 2px solid transparent;
  color: var(--muted);
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 500;
  text-decoration: none;
  transition: color var(--t-ui) var(--ease), padding-left var(--t-ui) var(--ease);
}
.nav-drawer-link:hover {
  color: var(--paper);
  padding-left: .5rem;
}
.nav-drawer-link.is-current {
  color: var(--white);
  font-weight: 600;
  border-left-color: var(--signal);
  padding-left: .7rem;
}

.nav-drawer-contact {
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--fs-sm);
}
.nav-drawer-contact a { display: inline-block; padding: .3rem 0; }

body.is-locked { overflow: hidden; }


/* --- 6.5  buttons --------------------------------------------------------
   .btn-primary is the signal fill with an --ink label at 5.6:1. White on
   --signal is only 3.4:1 and fails AA, so the inherited prototype's
   white-on-red is corrected here. Where white-on-red is wanted, .btn-deep
   uses --signal-deep, which carries white at 5.5:1. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  min-height: 50px;
  padding: .85rem 1.35rem;
  border: 1px solid transparent;
  border-radius: var(--r-control);
  background: none;
  color: var(--fg);
  font-family: var(--body);
  font-size: .95rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  transition:
    transform var(--t-ui) var(--ease),
    background-color var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease),
    color var(--t-ui) var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0) scale(.985); }

.btn-primary {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--ink);
}
.btn-primary:hover {
  background: #ff5b55;
  border-color: #ff5b55;
  color: var(--ink);
}

.btn-deep {
  background: var(--signal-deep);
  border-color: var(--signal-deep);
  color: var(--white);
}
.btn-deep:hover { background: #e02f2a; border-color: #e02f2a; color: var(--white); }

.btn-secondary {
  border-color: var(--edge);      /* 3:1+ boundary on either ground */
  color: var(--fg-strong);
}
.btn-secondary:hover {
  border-color: var(--fg);
  background: var(--wash);
  color: var(--fg-strong);
}

/* Solid light button, for use on an ink or signal ground. */
.btn-light {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}
.btn-light:hover { background: var(--white); border-color: var(--white); color: var(--ink); }

.btn-ghost {
  padding-inline: .4rem;
  border-color: transparent;
  color: var(--signal-text);
}
.btn-ghost:hover { color: var(--signal); background: var(--wash); }
.on-paper .btn-ghost:hover,
.slab-paper .btn-ghost:hover { color: var(--signal-deep); }

.btn-lg { min-height: 56px; padding: 1rem 1.7rem; font-size: 1.02rem; }
.btn-block { display: flex; width: 100%; }

.btn[aria-disabled="true"],
.btn:disabled {
  opacity: .55;
  cursor: not-allowed;
  transform: none;
}

/* Underlined text link with a red arrow, from the prototype. */
.text-link {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  min-height: 44px;
  color: var(--fg-strong);
  font-weight: 600;
  text-decoration: none;
}
.text-link .ext,
.text-link .arrow {
  color: var(--signal-text);
  transition: transform var(--t-ui) var(--ease);
}
.text-link:hover { color: var(--fg-strong); }
.text-link:hover .arrow { transform: translateX(4px); }
.text-link:hover .ext { transform: translate(3px, -3px); }

.ext {
  font-family: var(--mono);
  font-size: .85em;
  line-height: 1;
}


/* --- 6.6  hero ----------------------------------------------------------- */
.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--ink);
}

.hero-inner {
  /* The column gap is capped. An uncapped 6vw kept growing after --container
     had already hit its 1500px ceiling, so the text column got *narrower* as
     the viewport got wider: 637px at 1920, 619px at 2560. Capped at 5rem the
     text column settles at a constant 653px from 1648px upwards. */
  --hero-gap: clamp(2rem, 6vw, 5rem);
  /* The width the text column actually gets: the container, less the gap,
     times its share of the two tracks. Used below to size the h1 against its
     own column instead of against the viewport. */
  --hero-col: calc((var(--container) - var(--hero-gap)) * .46);

  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  align-items: center;
  gap: clamp(2.5rem, 5vw, 5rem) var(--hero-gap);
  width: var(--container);
  max-width: calc(100% - 40px);
  margin-inline: auto;
  padding-block: clamp(3.5rem, 8vw, 7.5rem);
}

/* Two-column hero only. --fs-hero tracks the viewport and caps at 8.4rem,
   but the text column stops growing once --container caps at 1500px, so past
   about 1700px a long word ("commission") broke mid-line. Cap the display
   size against --hero-col so the heading always keeps a usable measure.
   Below 1024px the grid is one column and the 7.2vw term is always the
   smaller of the two, so nothing about the small-screen hero changes. */
.hero-inner:not(.hero-inner-center) h1 {
  font-size: clamp(2.9rem, min(7.2vw, calc(var(--hero-col) * .175)), 8.4rem);
}

.hero-inner-center {
  grid-template-columns: 1fr;
  max-width: min(91vw, 1100px);
  text-align: center;
  justify-items: center;
}
.hero-inner-center h1 { max-width: 18ch; }
.hero-inner-center .lede { margin-inline: auto; }

.hero h1 { margin-bottom: 1.6rem; }
.hero .lede { max-width: 42ch; margin-bottom: 0; }

/* Signature glow. Rebuilt in the inherited palette: the brief's original
   snippet predates this system and used a blue and a green that the brand
   spec forbids ("no decorative tech gradients or unrelated accent hues").
   Red, deep red and a faint warm white only. Homepage once; TouchTakeaway
   optionally once. */
.glow {
  position: absolute;
  inset: -30%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(36% 42% at 22% 28%, rgba(255, 65, 58, .20), transparent 70%),
    radial-gradient(32% 38% at 78% 20%, rgba(255, 253, 248, .055), transparent 70%),
    radial-gradient(38% 44% at 62% 82%, rgba(200, 39, 35, .17), transparent 72%);
  filter: blur(38px);
}

@keyframes glow-drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2.2%, -1.8%, 0) scale(1.06); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* Pure CSS — needs no JavaScript and is switched off by the media query. */
@media (prefers-reduced-motion: no-preference) {
  .glow { animation: glow-drift 34s ease-in-out infinite; }
}

/* Faint engineering grid behind the hero, from the prototype. */
.hero-grid {
  position: absolute;
  inset: -10% 0 0;
  z-index: 0;
  pointer-events: none;
  opacity: .13;
  background-image:
    linear-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .16) 1px, transparent 1px);
  background-size: 84px 84px;
  -webkit-mask-image: linear-gradient(90deg, #000, transparent 82%);
  mask-image: linear-gradient(90deg, #000, transparent 82%);
}

@keyframes grid-parallax {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, 64px, 0); }
}

/* Scroll-driven parallax on the grid layer only — never on text, and it never
   touches the scroll position. Pure CSS, so it needs no script. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero-grid {
      animation: grid-parallax linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }
  }
}


/* --- 6.7  cards ---------------------------------------------------------- */
.card-grid {
  display: grid;
  gap: clamp(1rem, 1.8vw, 1.6rem);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}

.card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--rule);
  border-radius: var(--r-panel);
  background: var(--bg-raised);
  transition:
    transform var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease),
    background-color var(--t-ui) var(--ease);
}

.card-link {
  color: var(--fg);
  text-decoration: none;
}
.card-link:hover {
  transform: translateY(-2px);
  border-color: var(--edge);
  color: var(--fg);
}

.card-signal { border-left: 3px solid var(--signal); }

/* Default .card-media is a photographic top — screenshots, scenes. */
.card-media {
  aspect-ratio: 16 / 9;
  border-bottom: 1px solid var(--rule);
  background: var(--bg-panel);
}
.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* .is-cutout switches it to the light-panel treatment for product shots
   photographed on white. */
.card-media.is-cutout {
  aspect-ratio: 4 / 3;
  padding: clamp(1rem, 3vw, 1.8rem);
  border-bottom-color: var(--paper-2);
  background: var(--white);
}
.card-media.is-cutout img { object-fit: contain; }

.card-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: .6rem;
  padding: var(--s-6);
}
.card-body p {
  margin: 0;
  color: var(--fg-muted);
  font-size: var(--fs-sm);
}
.card-body .eyebrow { margin-bottom: .2rem; }
.card-foot { margin-top: auto; padding-top: var(--s-4); }


/* --- 6.8  product card ---------------------------------------------------
   Product photography is cut out on white. The image sits in a light panel,
   aspect-ratio boxed with generous padding, so it is never floated raw on a
   dark ground. The panel is always --white regardless of the section ground,
   so a catalogue looks identical in an ink section and a paper one. */
.product-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--rule);
  border-radius: var(--r-panel);
  background: var(--bg-raised);
  transition:
    transform var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease),
    background-color var(--t-ui) var(--ease);
}
.product-card:hover {
  transform: translateY(-2px);
  border-color: var(--edge);
}

.product-media {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  margin: var(--s-3) var(--s-3) 0;
  padding: clamp(1rem, 3.2vw, 1.9rem);
  overflow: hidden;
  border: 1px solid var(--paper-2);
  border-radius: var(--r-panel);
  background: var(--white);
}
.product-media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}
.product-media.is-wide { aspect-ratio: 3 / 2; }

.product-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: .45rem;
  padding: var(--s-5) var(--s-6) var(--s-6);
}

.product-brand {
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .62rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.product-title {
  margin: 0;
  font-family: var(--display);
  font-size: 1.12rem;
  font-weight: 600;
  line-height: 1.24;
  letter-spacing: -.02em;
  color: var(--fg-strong);
}
.product-title a { color: inherit; text-decoration: none; }
.product-title a:hover { color: var(--signal-text); }

.product-desc {
  margin: 0;
  color: var(--fg-muted);
  font-size: var(--fs-sm);
}

.product-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: auto;
  padding-top: var(--s-4);
}

/* There are no prices on this site. This is the standard price line. */
.product-price {
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: var(--fs-xs);
}


/* --- 6.9  feature row ---------------------------------------------------- */
.feature-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(2rem, 5vw, 6rem);
}
.feature-row + .feature-row { margin-top: var(--section-y); }

.feature-media img {
  width: 100%;
  border-radius: var(--r-panel-lg);
  border: 1px solid var(--rule);
}

.feature-media.is-cutout {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  padding: clamp(1.5rem, 4vw, 3.5rem);
  border: 1px solid var(--paper-2);
  border-radius: var(--r-panel-lg);
  background: var(--white);
}
.feature-media.is-cutout img {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
  object-fit: contain;
}

.feature-text > * + * { margin-top: var(--s-5); }
.feature-text p { color: var(--fg-muted); }

/* Alternate rows by adding .is-flipped to every other one. */
.feature-row.is-flipped .feature-media { order: 2; }


/* --- 6.10  editorial ledger ----------------------------------------------
   The prototype's service ledger: a numbered rule-separated list that reads
   like a contents page. Strongest on a paper ground. */
.ledger { margin: 0; padding: 0; list-style: none; }

.ledger-row {
  display: grid;
  grid-template-columns: 70px .85fr 1.15fr .6fr;
  align-items: start;
  gap: clamp(1.2rem, 4vw, 4rem);
  padding: 2.4rem 0;
  border-top: 1px solid var(--rule);
}
.ledger:last-child .ledger-row:last-child,
.ledger-row:last-child { border-bottom: 1px solid var(--rule); }

.ledger-n {
  color: var(--signal-text);
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
}
.ledger-row h3 { margin: 0; }
.ledger-row p { margin: 0; color: var(--fg-muted); font-size: 1rem; }

.ledger-tags {
  display: grid;
  gap: .35rem;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.ledger-tags li { padding-bottom: .35rem; border-bottom: 1px solid var(--rule); }

/* A ledger with no tags anywhere drops the reserved .6fr track rather than
   leaving roughly a quarter of every row blank. integrations.html is that
   case — it uses the ledger purely as a numbered contents list. A ledger
   where only some rows carry tags keeps the track, so those rows still line
   up with each other. Scoped to the wide layout on purpose: below 1024px the
   ledger is already three columns and the media queries in §9 own it, and
   this selector is specific enough to beat them if it were left unscoped. */
@media (min-width: 1024px) {
  .ledger:not(:has(.ledger-tags)) .ledger-row {
    grid-template-columns: 70px .85fr 1.15fr;
  }
}


/* --- 6.11  stat row and spec list ----------------------------------------
   Both take <dl> markup with <div><dt>…</dt><dd>…</dd></div> groups. */
.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin: 0;
}
.stat-row > div { display: flex; flex-direction: column; }
.stat-row dt {
  order: 2;
  margin-top: .4rem;
  color: var(--fg-muted);
  font-size: var(--fs-xs);
}
.stat-row dd {
  order: 1;
  margin: 0;
  color: var(--fg-strong);
  font-family: var(--mono);
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.stat-row dd .unit { color: var(--signal-text); }

.spec-list {
  display: grid;
  grid-template-columns: minmax(120px, 34%) 1fr;
  margin: 0;
  border-top: 1px solid var(--rule);
}
.spec-list > div { display: contents; }
.spec-list dt {
  padding: .8rem var(--s-4) .8rem 0;
  border-bottom: 1px solid var(--rule);
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.spec-list dd {
  padding: .8rem 0;
  margin: 0;
  border-bottom: 1px solid var(--rule);
  color: var(--fg-strong);
  font-size: var(--fs-sm);
}


/* --- 6.12  pills, tags and filter chips ---------------------------------- */
.pill,
.tag {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .35rem .75rem;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  background: var(--bg-panel);
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .64rem;
  font-weight: 600;
  letter-spacing: .1em;
  line-height: 1.5;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}

.tag-signal {
  color: var(--signal-text);
  border-color: rgba(255, 65, 58, .42);
  background: rgba(255, 65, 58, .1);
}
/* Deadline semantics only. Ink grounds only — both fail on paper. */
.tag-deadline { color: var(--amber); border-color: rgba(242, 182, 61, .42); background: rgba(242, 182, 61, .1); }
.tag-healthy  { color: var(--green); border-color: rgba(55, 204, 131, .42); background: rgba(55, 204, 131, .1); }

/* .pill as a filter chip. Interactive, so it gets a 3:1 boundary and a
   pressed state that is not colour alone — the fill and weight change too. */
button.pill {
  min-height: 40px;
  border-color: var(--edge);
  cursor: pointer;
  transition:
    background-color var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease),
    color var(--t-ui) var(--ease);
}
button.pill:hover { color: var(--fg-strong); border-color: var(--fg); }
button.pill[aria-pressed="true"] {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--ink);          /* 5.6:1 */
  font-weight: 700;
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: var(--s-5);
}

.filter-status {
  margin: 0 0 var(--s-6);
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: var(--fs-xs);
}

.filter-empty {
  padding: var(--s-9);
  border: 1px dashed var(--edge);
  border-radius: var(--r-panel);
  color: var(--fg-muted);
  text-align: center;
}


/* --- 6.13  callout -------------------------------------------------------
   Colour discipline: red is identity and urgency; amber and green are
   reserved for Help Desk deadline states and must not be used for success or
   "included" messaging. Use .callout-note for neutral emphasis and
   .callout-signal for anything urgent or brand-led.
   .callout-info / .callout-success / .callout-warning are kept as aliases so
   nothing renders unstyled, but prefer the names below. */
.callout {
  max-width: var(--measure);
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--edge);
  border-radius: var(--r-control);
  background: var(--bg-raised);
  color: var(--fg-muted);
}
.callout > :last-child { margin-bottom: 0; }

.callout-title {
  display: block;
  margin: 0 0 .4rem;
  color: var(--fg-strong);
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 600;
}

.callout-note,
.callout-info,
.callout-success { border-left-color: var(--edge); }

.callout-signal,
.callout-warning { border-left-color: var(--signal); }
.callout-signal .callout-title,
.callout-warning .callout-title { color: var(--signal-text); }

/* Help Desk deadline states only. */
.callout-deadline { border-left-color: var(--amber); }
.callout-deadline .callout-title { color: var(--amber); }
.callout-healthy { border-left-color: var(--green); }
.callout-healthy .callout-title { color: var(--green); }


/* --- 6.14  download card ------------------------------------------------- */
.download-grid {
  display: grid;
  gap: clamp(1rem, 1.8vw, 1.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}

.download-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--s-5);
  padding: var(--s-6);
  border: 1px solid var(--rule);
  border-radius: var(--r-panel);
  background: var(--bg-raised);
  color: var(--fg);
  text-decoration: none;
  transition:
    transform var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease);
}
.download-card:hover {
  transform: translateY(-2px);
  border-color: var(--signal);
  color: var(--fg);
}

.download-icon {
  display: grid;
  flex-shrink: 0;
  place-items: center;
  width: 48px;
  height: 48px;
  border: 1px solid rgba(255, 65, 58, .38);
  border-radius: var(--r-control);
  background: rgba(255, 65, 58, .11);
  color: var(--signal-text);
  font-family: var(--mono);
  font-size: .95rem;
  font-weight: 600;
}
.download-icon svg { width: 22px; height: 22px; }

.download-text { min-width: 0; }

.download-title {
  display: block;
  margin: 0 0 .25rem;
  color: var(--fg-strong);
  font-family: var(--display);
  font-size: 1.1rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.02em;
}

.download-desc {
  max-width: 46ch;
  margin: 0;
  color: var(--fg-muted);
  font-size: var(--fs-sm);
}

.download-ext {
  align-self: center;
  color: var(--fg-muted);
  font-family: var(--mono);
  transition: color var(--t-ui) var(--ease), transform var(--t-ui) var(--ease);
}
.download-card:hover .download-ext {
  color: var(--signal-text);
  transform: translate(3px, -3px);
}


/* --- 6.15  forms --------------------------------------------------------- */
.form { max-width: 680px; }

.form-field { display: block; margin-bottom: var(--s-6); }

.form-label {
  display: block;
  margin-bottom: .45rem;
  color: var(--fg-strong);
  font-size: .92rem;
  font-weight: 600;
}
.form-label .req { color: var(--signal-text); }

.form-hint {
  display: block;
  max-width: var(--measure);
  margin: .4rem 0 0;
  color: var(--fg-muted);
  font-size: var(--fs-xs);
}

.form-input,
.form-textarea,
.form-select {
  display: block;
  width: 100%;
  padding: .85rem 1rem;
  border: 1px solid var(--edge);     /* 3:1+ boundary on either ground */
  border-radius: var(--r-control-sm);
  background: var(--bg-raised);
  color: var(--fg-strong);
  font-size: 1rem;
  line-height: 1.4;
  transition: border-color var(--t-ui) var(--ease);
}

.form-input::placeholder,
.form-textarea::placeholder { color: var(--fg-muted); opacity: 1; }

.form-input:hover,
.form-textarea:hover,
.form-select:hover { border-color: var(--fg); }

.form-input:focus,
.form-textarea:focus,
.form-select:focus { border-color: var(--signal); }

.form-textarea { min-height: 170px; resize: vertical; }

.form-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.6rem;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23a6a8ab' d='M1 2h10L6 7z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 12px 8px;
}
.on-paper .form-select,
.slab-paper .form-select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23565d67' d='M1 2h10L6 7z'/%3E%3C/svg%3E");
}

.form-input[aria-invalid="true"],
.form-textarea[aria-invalid="true"],
.form-select[aria-invalid="true"] { border-color: var(--signal); }

.form-error {
  display: block;
  margin-top: .45rem;
  color: var(--signal-text);   /* 5.6:1 on ink, 4.8:1 on paper */
  font-size: var(--fs-xs);
  font-weight: 600;
}
.form-error:empty { display: none; }

/* The error is never signalled by colour alone. */
.form-error::before {
  content: "! ";
  font-family: var(--mono);
  font-weight: 700;
}

/* Confirmation is neutral, not green: green is reserved for Help Desk
   deadline states by the inherited brand spec. */
.form-success {
  margin-bottom: var(--s-6);
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--signal);
  border-radius: var(--r-control);
  background: var(--bg-raised);
  color: var(--fg-strong);
}
.form-success:empty { display: none; }

/* Visually hidden. The markup must also carry aria-hidden="true" and
   tabindex="-1" so it is hidden from assistive technology and the tab order. */
.honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

fieldset {
  min-width: 0;              /* stops fieldsets forcing horizontal overflow */
  margin: 0 0 var(--s-6);
  padding: var(--s-6);
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
}
legend {
  padding: 0 .5rem;
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 600;
}


/* --- 6.16  breadcrumb ---------------------------------------------------- */
.breadcrumb {
  padding-block: var(--s-5);
  font-family: var(--mono);
  font-size: var(--fs-xs);
}
.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.breadcrumb li {
  display: flex;
  align-items: center;
  gap: .4rem;
  color: var(--fg-muted);
}
.breadcrumb li + li::before { content: "/"; color: var(--fg-muted); }
.breadcrumb a { color: var(--fg-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--signal-text); text-decoration: underline; }
.breadcrumb [aria-current="page"] { color: var(--fg-strong); }


/* --- 6.17  sticky sequence -----------------------------------------------
   Class contract (see docs/DESIGN-NOTES.md):
     section.sticky-seq[data-sticky-seq]
       .sticky-seq-inner
         .sticky-seq-stage                        becomes sticky when wired
           .sticky-seq-panel[data-seq-panel="1"]  one per step
         ol.sticky-seq-steps
           li.sticky-seq-step[data-seq-step="1"]
   The default state — no JavaScript, or prefers-reduced-motion — is plain
   stacked content that reads top to bottom. main.js sets data-seq="on" only
   when it has wired the section AND motion is allowed. */
.sticky-seq { padding-block: var(--section-y); }

.sticky-seq-inner { display: grid; gap: var(--s-8); }
.sticky-seq-stage { min-width: 0; }

.sticky-seq-panel {
  margin-bottom: var(--s-5);
  padding: var(--s-6);
  border: 1px solid var(--rule);
  border-radius: var(--r-panel-lg);
  background: var(--bg-raised);
}
.sticky-seq-panel:last-child { margin-bottom: 0; }

.sticky-seq-steps { margin: 0; padding: 0; list-style: none; counter-reset: seq; }

.sticky-seq-step {
  position: relative;
  counter-increment: seq;
  padding: var(--s-6) 0 var(--s-6) var(--s-9);
  border-top: 1px solid var(--rule);
}
.sticky-seq-step::before {
  content: counter(seq, decimal-leading-zero);
  position: absolute;
  top: var(--s-6);
  left: 0;
  color: var(--signal-text);
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .1em;
}
.sticky-seq-step h3 { margin-bottom: .4rem; font-size: var(--fs-h4); }
.sticky-seq-step p { margin: 0; color: var(--fg-muted); font-size: var(--fs-sm); }

@media (min-width: 900px) {
  .sticky-seq[data-seq="on"] .sticky-seq-inner {
    grid-template-columns: 1fr 1fr;
    gap: clamp(2.5rem, 6vw, 6rem);
    align-items: start;
  }

  .sticky-seq[data-seq="on"] .sticky-seq-stage {
    position: sticky;
    top: calc(var(--header-h) + var(--s-7));
    display: grid;
  }

  /* Panels share one cell; the active one shows. Only opacity and a small
     rise change — the scroll position is never touched. */
  .sticky-seq[data-seq="on"] .sticky-seq-panel {
    grid-area: 1 / 1;
    margin-bottom: 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(14px);
    transition:
      opacity 420ms var(--ease),
      transform 420ms var(--ease),
      visibility 0s linear 420ms;
  }
  .sticky-seq[data-seq="on"] .sticky-seq-panel.is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }

  .sticky-seq[data-seq="on"] .sticky-seq-step {
    opacity: .4;
    transition: opacity 320ms var(--ease), border-color 320ms var(--ease);
  }
  .sticky-seq[data-seq="on"] .sticky-seq-step.is-active {
    opacity: 1;
    border-top-color: var(--signal);
  }
}


/* --- 6.18  product canvas ------------------------------------------------
   The prototype's signature: a browser shell whose stage switches between
   named views. Tabs are real buttons, the stage is aria-live, and with no
   JavaScript every view is simply shown in order. */
.canvas-shell {
  overflow: hidden;
  border: 1px solid #303641;
  border-radius: var(--r-shell);
  background: #12161b;
  box-shadow: var(--shadow);
}

.canvas-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 72px;
  padding: .8rem 1.2rem;
  border-bottom: 1px solid #2b3139;
  background: #0c0f13;
}

.canvas-label {
  color: var(--muted);
  font-family: var(--mono);
  font-size: .74rem;
}
.canvas-label strong { margin-right: .4rem; color: var(--paper); font-family: var(--display); }

.view-switcher {
  display: flex;
  gap: .2rem;
  padding: .25rem;
  border-radius: var(--r-control);
  background: #181d23;
}
.view-switcher button {
  min-height: 40px;
  padding: .45rem .9rem;
  border: 0;
  border-radius: var(--r-control-sm);
  background: transparent;
  color: var(--muted);
  font-size: .78rem;
  font-weight: 500;
}
.view-switcher button:hover { color: var(--paper); }
.view-switcher button.is-active {
  color: var(--white);
  background: #2a3038;
  box-shadow: inset 0 0 0 1px #3a424d;
}
/* Keep the outer half of the focus ring when the active tab is focused —
   its own inset shadow would otherwise replace it. */
.view-switcher button.is-active:focus-visible {
  box-shadow: inset 0 0 0 1px #3a424d, 0 0 0 6px var(--ink);
}

.canvas-stage { min-height: 320px; }
.canvas-caption {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .4rem;
  margin: var(--s-4) 0 0;
  /* Ground-agnostic: --muted is ink-only and drops to 2.1:1 on paper, so read
     the alias instead and let .on-paper rebind it. */
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .66rem;
}
.canvas-caption span { color: var(--fg-strong); }


/* --- 6.19  site footer --------------------------------------------------- */
.site-footer {
  padding-block: var(--s-10) var(--s-7);
  border-top: 1px solid var(--line);
  background: var(--ink);
  color: var(--muted);
}

.site-footer-grid {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr;
  align-items: start;
  gap: clamp(2rem, 4vw, 4rem);
  width: var(--container);
  max-width: calc(100% - 40px);
  margin-inline: auto;
}

.footer-brand .brand-logo { width: 120px; margin-bottom: var(--s-5); }
.footer-brand p { max-width: 34ch; color: var(--muted); font-size: var(--fs-sm); }

.footer-head {
  margin: 0 0 var(--s-5);
  color: var(--muted);
  font-family: var(--mono);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.footer-list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}
.footer-list a {
  display: inline-block;
  padding: .3rem 0;
  color: var(--muted);
  font-size: var(--fs-sm);
  text-decoration: none;
}
.footer-list a:hover { color: var(--paper); }

.footer-helpdesk {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  min-height: 44px;
  margin-top: var(--s-5);
  padding: .6rem 1rem;
  border: 1px solid var(--signal);
  border-radius: var(--r-control-sm);
  color: var(--signal);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--t-ui) var(--ease), color var(--t-ui) var(--ease);
}
.footer-helpdesk:hover { background: var(--signal); color: var(--ink); }

.site-footer-base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4) var(--s-6);
  width: var(--container);
  max-width: calc(100% - 40px);
  margin: var(--s-9) auto 0;
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-family: var(--mono);
  font-size: var(--fs-xs);
}
.site-footer-base a { color: var(--muted); text-decoration: none; }
.site-footer-base a:hover { color: var(--paper); }


/* --- 6.20  misc blocks --------------------------------------------------- */
.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: var(--r-control);
}
.table-scroll table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.table-scroll th,
.table-scroll td {
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--rule);
  text-align: left;
}
.table-scroll th {
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .64rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.table-scroll td { color: var(--fg-muted); }
.table-scroll td:first-child { color: var(--fg-strong); font-weight: 600; }
.table-scroll tr:last-child td { border-bottom: 0; }

/* A single product presented, not decorated: centred, given room, no drop
   shadow, no tilt. This is for cut-out product photography only. If you want
   an editorial block lifted onto its own surface, use .slab below. */
.figure-panel {
  padding: clamp(1.5rem, 5vw, 4.5rem);
  border: 1px solid var(--paper-2);
  border-radius: var(--r-shell);
  background: var(--white);
}

/* An editorial slab: a run of text, a ledger or a list lifted onto its own
   surface inside a section. Ground-agnostic — it reads the aliases, so it is
   legible on ink and on paper. Editorial shell radius, and less padding than
   .figure-panel because it holds words rather than a single object. */
.slab {
  padding: clamp(1.5rem, 4vw, 3.5rem);
  border: 1px solid var(--rule);
  border-radius: var(--r-shell);
  background: var(--bg-raised);
}

/* The paper variant, for a slab dropped into an ink section. It is listed in
   the .on-paper token block in §2, so it rebinds the whole alias set exactly
   as .on-paper does and everything inside it stays readable with no extra
   markup. This rule only re-states the surface, because .slab's --bg-raised
   background would otherwise win on source order. */
.slab-paper {
  border-color: var(--paper-2);
  background: var(--bg);
}

.figure-shot img {
  width: 100%;
  border: 1px solid var(--rule);
  border-radius: var(--r-panel-lg);
}

/* Portrait media box. Tall captures — the TouchTakeaway phone screenshots are
   860x2800, a 1:3.26 strip — would otherwise run to their full height and
   drag a card down the page. This boxes the image to a phone-shaped ratio and
   anchors the crop to the top, so the shop's own branded header is always the
   part you see. Put it on the <img>, alongside .figure-shot on the <figure>. */
.media-portrait {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  object-position: top center;
  background: var(--bg-panel);
}
.figure-shot figcaption {
  margin-top: .7rem;
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.closer {
  position: relative;
  overflow: hidden;
  padding: clamp(2.5rem, 6vw, 5rem);
  border: 1px solid var(--rule);
  border-radius: var(--r-shell);
  background: var(--bg-raised);
  text-align: center;
}
.closer p { max-width: 52ch; margin-inline: auto; color: var(--fg-muted); }
.closer .cta-row { justify-content: center; }


/* ===========================================================================
   7  UTILITIES
   =========================================================================== */

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

.text-muted  { color: var(--fg-muted); }
.text-signal { color: var(--signal-text); }
.text-strong { color: var(--fg-strong); }
.text-center { text-align: center; }
.text-balance { text-wrap: balance; }

.measure       { max-width: var(--measure); }
.measure-tight { max-width: var(--measure-tight); }
.center-x      { margin-inline: auto; }

.mt-0 { margin-top: 0; }
.mt-4 { margin-top: var(--s-4); }
.mt-6 { margin-top: var(--s-6); }
.mt-8 { margin-top: var(--s-8); }
.mt-9 { margin-top: var(--s-9); }
.mb-0 { margin-bottom: 0; }
.mb-6 { margin-bottom: var(--s-6); }

.no-wrap { white-space: nowrap; }

/* JavaScript-dependent controls stay hidden until JavaScript proves itself. */
.no-js .nav-toggle,
.no-js .filter-bar,
.no-js .filter-status,
.no-js .view-switcher { display: none; }

/* Declared after the components so it always wins at equal specificity.
   The catalogue filter and the product canvas both rely on it. */
[hidden] { display: none; }


/* --- 7.1  scroll reveal --------------------------------------------------
   Content is visible by default. Nothing here depends on JavaScript to be
   readable. Two motion paths:
     a) browsers with scroll-driven animations run the reveal in pure CSS
     b) everything else gets the IntersectionObserver path, which main.js
        opts into by setting data-motion="io" on <html>
   Either way .is-visible locks the element visible for good, so a reveal
   fires once and never re-hides on scroll back up. Rise is 20px, inside the
   16–24px the brief asks for; easing is the inherited --ease. */

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

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-rise 1s var(--ease) both;
      animation-timeline: view();
      animation-range-start: entry calc(2% + var(--reveal-i, 0) * 7%);
      animation-range-end:   entry calc(58% + var(--reveal-i, 0) * 7%);
    }
    .reveal.is-visible {
      animation: none;
      opacity: 1;
      transform: none;
    }
  }
}

@media (prefers-reduced-motion: no-preference) {
  [data-motion="io"] .reveal {
    opacity: 0;
    transform: translateY(20px);
    transition:
      opacity var(--t-reveal) var(--ease),
      transform var(--t-reveal) var(--ease);
    transition-delay: calc(var(--reveal-i, 0) * var(--stagger));
  }
  [data-motion="io"] .reveal.is-visible {
    opacity: 1;
    transform: none;
  }
}

/* Safety nets. The scroll-driven path is pure CSS and cannot fail the way a
   missing script can, but where scripting is unavailable there is no reason
   to make anyone scroll to read. .no-js covers browsers without the
   (scripting) media feature. */
@media (scripting: none) {
  .reveal { opacity: 1; transform: none; animation: none; }
}

.no-js .reveal { opacity: 1; transform: none; animation: none; }

/* Stagger siblings: .reveal-group on the parent, .reveal on each child. */
.reveal-group > :nth-child(1) { --reveal-i: 0; }
.reveal-group > :nth-child(2) { --reveal-i: 1; }
.reveal-group > :nth-child(3) { --reveal-i: 2; }
.reveal-group > :nth-child(4) { --reveal-i: 3; }
.reveal-group > :nth-child(5) { --reveal-i: 4; }
.reveal-group > :nth-child(6) { --reveal-i: 5; }
.reveal-group > :nth-child(n + 7) { --reveal-i: 6; }


/* ===========================================================================
   8  RESPONSIVE REFINEMENTS
   =========================================================================== */

/* Drawer breakpoint. Above it the horizontal nav fits comfortably. */
@media (max-width: 1023px) {
  .js .nav { display: none; }
  .js .nav-toggle { display: inline-flex; }

  /* Without JavaScript the full nav stays in the header and wraps. A wrapped
     nav is tall, so it stops being sticky rather than eating the viewport. */
  .no-js .site-header { position: static; }
  .no-js .site-header-inner { flex-wrap: wrap; padding-block: var(--s-3); }
  .no-js .nav { gap: .2rem 1rem; }
}

@media (min-width: 1024px) {
  .nav-drawer,
  .nav-scrim { display: none; }
}

@media (max-width: 1023px) {
  .hero-inner,
  .split,
  .split-lead,
  .split-trail { grid-template-columns: 1fr; }
  .ledger-row { grid-template-columns: 60px .9fr 1.1fr; }
  .ledger-tags {
    grid-column: 2 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: 1.2rem;
  }
}

@media (max-width: 899px) {
  .feature-row { grid-template-columns: 1fr; }
  .feature-row.is-flipped .feature-media { order: 0; }
  .section-head { grid-template-columns: 1fr; gap: 1rem; }
  .site-footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}

@media (max-width: 819px) {
  body { font-size: 16px; }
  :root { --header-h: 70px; --header-h-sm: 64px; }
  .brand { width: 112px; }
  .ledger-row { grid-template-columns: 45px 1fr; }
  .ledger-row p,
  .ledger-tags { grid-column: 2; }
  .display { max-width: 20ch; }
}

@media (max-width: 599px) {
  .site-footer-grid { grid-template-columns: 1fr; }
  .download-card { grid-template-columns: auto 1fr; }
  .download-ext { grid-column: 2; justify-self: start; }
  .ledger-row { grid-template-columns: 34px 1fr; gap: 1rem; }
  .ledger-tags { display: grid; gap: .35rem; }
}

@media (max-width: 520px) {
  .spec-list { grid-template-columns: 1fr; }
  .spec-list dt { padding: .8rem 0 0; border-bottom: 0; }
  .spec-list dd { padding-top: .2rem; }
  .cta-row { flex-direction: column; align-items: stretch; }
  .btn { width: 100%; }
  .canvas-bar { justify-content: flex-start; }
  .view-switcher { width: 100%; }
  .view-switcher button { flex: 1; padding-inline: .5rem; }
}


/* ===========================================================================
   9  REDUCED MOTION AND PRINT
   =========================================================================== */

/* prefers-reduced-motion is absolute. Everything is visible, nothing moves,
   and the sticky sequence collapses to plain stacked content — main.js also
   refuses to wire it, so data-seq is never set. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .001ms;
    animation-iteration-count: 1;
    transition-duration: .001ms;
    scroll-behavior: auto;
  }

  .glow,
  .hero-grid { animation: none; transform: none; }

  .reveal { opacity: 1; transform: none; animation: none; }

  .btn:hover,
  .btn:active,
  .card-link:hover,
  .product-card:hover,
  .download-card:hover,
  .download-card:hover .download-ext,
  .text-link:hover .arrow,
  .text-link:hover .ext,
  .nav-drawer-link:hover { transform: none; }
}

@media print {
  :root,
  .on-ink,
  .on-paper,
  .slab-paper {
    --bg: #ffffff;
    --bg-raised: #ffffff;
    --bg-panel: #ffffff;
    --fg: #000000;
    --fg-strong: #000000;
    --fg-muted: #333333;
    --rule: #bbbbbb;
    --edge: #999999;
    --signal-text: #a81f1a;
  }

  body { color: #000000; background: #ffffff; }

  .site-header,
  .nav-drawer,
  .nav-scrim,
  .skip-link,
  .glow,
  .hero-grid,
  .filter-bar,
  .filter-status,
  .view-switcher,
  .cta-row,
  .footer-helpdesk { display: none; }

  /* A scroll-driven reveal has no scroll progress on paper. */
  .reveal { opacity: 1; transform: none; animation: none; }

  .sticky-seq-stage { position: static; }
  .sticky-seq-panel { opacity: 1; visibility: visible; transform: none; }
  .sticky-seq-step { opacity: 1; }
  .canvas-stage [hidden] { display: block; }

  a { color: #000000; text-decoration: underline; }
  main a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: .85em;
    word-break: break-all;
  }

  .card,
  .product-card,
  .callout,
  .download-card,
  .canvas-shell {
    border: 1px solid #bbbbbb;
    box-shadow: none;
    break-inside: avoid;
  }

  .section,
  .section-lg,
  .section-full { padding-block: 18px; min-height: 0; }

  h1, h2, h3 { break-after: avoid; }
}
