/* Urverk — Nordic precision-mechanism. Scandinavian minimalism meets fine
   watchmaking: generous whitespace, a precise modular grid, hairline rules like
   watch-face indices, one brass accent, and one place per page where the
   mechanism comes alive. The eight palette tokens are the only hex literals in
   this file; every other colour is derived via var()/color-mix() so the palette
   guard holds. Light is the committed default; a dark theme is built from the
   same tokens under prefers-color-scheme. */

/* Self-hosted fonts — no third-party request, no render-blocking CDN, and a
   strict font-src 'self' CSP. Only the latin subset is committed (the site is
   English). Space Grotesk is the engineered display face; Inter carries body
   and UI. unicode-range copied from the Google latin @font-face blocks so glyph
   coverage is identical. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/space-grotesk-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/space-grotesk-latin-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/inter-latin-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/inter-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/inter-latin-600.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light; /* native controls / scrollbars track the theme (t-020) */
  /* --- the eight palette tokens (the only hex literals) --------------------- */
  --graphite:      #0f1113; /* dark ground / mechanism */
  --graphite-2:    #171a1d; /* raised graphite panel */
  --snow:          #f6f4ef; /* light page ground */
  --paper:         #e7e2d8; /* raised panel on light */
  --ink:           #1c1e22; /* text on snow */
  --brass:         #b98a3e; /* the one accent — indices, rules, the mechanism */
  --brass-hi:      #d8b877; /* brass highlight — jewels, hover */
  --steel:         #8b9198; /* cool grey — labels, secondary text */

  /* --- semantic tokens (light, the committed default) ----------------------- */
  --ground:  var(--snow);
  --surface: var(--paper);
  --fg:      var(--ink);
  --accent:  var(--brass);
  --accent-hi: var(--brass-hi);
  /* Brass as *text* on the page ground: full brass is only 2.8:1 on snow, so
     darken it toward ink to clear WCAG AA (5.6:1). The dark theme restores full
     brass, which already passes on graphite (6:1). Used for accent-coloured copy
     that sits on --ground, e.g. form-field error messages. */
  --accent-text: color-mix(in srgb, var(--brass) 58%, var(--ink));

  --rule:       color-mix(in srgb, var(--ink) 13%, transparent);
  --rule-brass: color-mix(in srgb, var(--brass) 50%, transparent);
  /* 64% ink keeps the muted labels clearly quieter than body copy while
     clearing WCAG AA (4.86:1 on snow); 58% read 4.0:1 and failed. */
  --label:      color-mix(in srgb, var(--ink) 64%, var(--snow));
  --body-soft:  color-mix(in srgb, var(--ink) 90%, var(--snow));

  /* The mechanism grounds — always dark, in both themes. Reassigned a touch
     lighter under dark so they still lift off the graphite page. */
  --movement:   var(--graphite);
  --movement-2: var(--graphite-2);
  --on-dark:    var(--snow);
  --on-dark-soft: color-mix(in srgb, var(--snow) 78%, var(--graphite));
  --on-dark-faint: color-mix(in srgb, var(--snow) 60%, var(--graphite));
  --rule-dark:  color-mix(in srgb, var(--snow) 15%, transparent);

  /* Agent-panel surface — always dark in both themes. Built from the raw
     palette tokens (defined once, never theme-overridden), so the panel
     reads identically light/dark and every value passes the palette guard. */
  --ap-panel:   color-mix(in srgb, var(--graphite) 90%, var(--snow));
  --ap-bar:     color-mix(in srgb, var(--graphite) 97%, var(--snow));
  --ap-mark-bg: color-mix(in srgb, var(--graphite) 94%, var(--snow));
  --ap-fg:      color-mix(in srgb, var(--snow) 92%, var(--graphite));
  --ap-dim:     color-mix(in srgb, var(--snow) 66%, var(--graphite));
  --ap-faint:   color-mix(in srgb, var(--snow) 60%, var(--graphite));
  --ap-accent:  color-mix(in srgb, var(--brass) 45%, var(--brass-hi));
  --ap-active:  color-mix(in srgb, var(--brass) 22%, transparent);
  --ap-rule:    color-mix(in srgb, var(--snow) 14%, transparent);
  --ap-dash:    color-mix(in srgb, var(--snow) 20%, transparent);

  --font-display: "Space Grotesk", "SF Pro Display", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", "Helvetica Neue", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;

  /* Vertical rhythm and type scale — the whitespace is the design. */
  --measure: 66ch;
  --step-0: 1.0625rem;
  --step-1: clamp(1.15rem, 1.05rem + 0.5vw, 1.35rem);
  --step-2: clamp(1.35rem, 1.2rem + 0.9vw, 1.7rem);
  --step-3: clamp(1.7rem, 1.4rem + 1.6vw, 2.35rem);
  --step-4: clamp(2.3rem, 1.75rem + 2.9vw, 3.5rem);
  --step-5: clamp(2.9rem, 2rem + 4.6vw, 4.6rem);
  --step-6: clamp(3.4rem, 2.3rem + 6vw, 6rem);

  /* --- The movement: one frequency, everything subdivides it ---------------
     A watch runs at a fixed beat; so does this site. --beat is the escapement
     half-period every duration below is a whole multiple of, so every
     transition and animation is in phase — a shared cadence, not thirty
     unrelated milliseconds. Two easings only, both mechanical: decisive
     travel, and an arrival that stops dead. Never an overshoot — this brand
     ticks, it does not wobble. Reduced-motion collapses all of it (see the
     global reduce reset at the foot of the file). */
  --beat: 130ms;
  --t-1: var(--beat);                    /* 130ms  — detents: colour, hairline, border */
  --t-2: calc(var(--beat) * 2);          /* 260ms  — sweeps: nav + link underlines */
  --t-3: calc(var(--beat) * 3);          /* 390ms  — winds: button fill, glyph turn */
  --t-4: calc(var(--beat) * 4);          /* 520ms  — draws: brass index ticks */
  --t-5: calc(var(--beat) * 5);          /* 650ms  — settles: reveals, headings */
  --t-wind: calc(var(--beat) * 7);       /* 910ms  — the wordmark winding up */
  --tick-period: calc(var(--beat) * 8);  /* 1040ms — the escapement: caret + spinner + orbit */

  --ease-mech: cubic-bezier(0.22, 0.85, 0.24, 1);   /* general mechanical travel */
  --ease-settle: cubic-bezier(0.16, 1, 0.3, 1);     /* arrival that stops dead, no overshoot */

  /* --- Hero band + Astrolabium face (theme-adaptive, t-049) ----------------
     The hero is no longer a permanently dark band: in light theme it is a
     DAYLIGHT face on the light page ground (ink hands/indices, brass bezel,
     steel hairlines); in dark theme it stays a graphite NIGHT face. These
     defaults are the DAY values; the two dark blocks below override them to the
     NIGHT values (kept byte-identical between the media query and the attribute
     selector). Only the eight tokens appear as hex; every face colour is a
     var()/color-mix() of them, so the palette guard holds in both themes. */
  --hero-band:        var(--ground);
  --hero-band-fg:     var(--fg);
  --hero-band-soft:   var(--body-soft);
  --hero-band-faint:  var(--label);
  --hero-band-accent: var(--accent-text);
  --hero-band-rule:   var(--rule);

  --face-line:        color-mix(in srgb, var(--ink) 62%, transparent);
  --face-line-soft:   color-mix(in srgb, var(--ink) 30%, transparent);
  --face-brass:       var(--brass);
  --face-brass-soft:  color-mix(in srgb, var(--brass) 42%, transparent);
  --face-hand:        var(--ink);
  --face-hand-edge:   color-mix(in srgb, var(--snow) 85%, var(--paper));
  --face-sun:         var(--brass);
  --face-sun-ring:    var(--ink);
  --face-moon-lit:    color-mix(in srgb, var(--snow) 60%, var(--steel));
  --face-moon-dark:   var(--ink);
  --face-moon-rim:    var(--brass);
  --face-text:        var(--ink);
  --face-window:      var(--ground);
  --face-gear:        color-mix(in srgb, var(--ink) 50%, transparent);
  --face-gear-hi:     color-mix(in srgb, var(--brass) 72%, var(--ink));
  --face-gear-opacity: 0.28;
}

/* Dark theme — rebuilt from the same tokens. The mechanism grounds lift a step
   so the dark sections still separate from the now-graphite page.

   Two entry points, one token set (t-020). The media query paints dark when the
   OS asks for it AND the visitor has not pinned light; the attribute selector
   paints dark when the visitor pins it, whatever the OS says. A media query
   cannot join an attribute selector in one selector list, so the declarations
   are duplicated on purpose — keep the two blocks byte-identical. On <html>,
   data-theme unset means "follow the system", data-theme="light" pins light even
   under an OS dark preference (theme.js), and data-theme="dark" pins dark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ground:  var(--graphite);
    --surface: var(--graphite-2);
    --fg:      var(--snow);
    --accent-text: var(--brass);  /* full brass reads 6:1 on graphite */
    --rule:       color-mix(in srgb, var(--snow) 14%, transparent);
    --rule-brass: color-mix(in srgb, var(--brass) 55%, transparent);
    --label:      color-mix(in srgb, var(--snow) 62%, var(--graphite));
    --body-soft:  color-mix(in srgb, var(--snow) 90%, var(--graphite));

    --movement:   var(--graphite-2);
    --movement-2: color-mix(in srgb, var(--graphite-2) 82%, var(--snow));
    --on-dark-soft: color-mix(in srgb, var(--snow) 80%, var(--graphite-2));
    --on-dark-faint: color-mix(in srgb, var(--snow) 62%, var(--graphite-2));

    /* NIGHT face (t-049) — keep byte-identical with the [data-theme="dark"] block. */
    --hero-band:        var(--graphite);
    --hero-band-fg:     var(--snow);
    --hero-band-soft:   color-mix(in srgb, var(--snow) 78%, var(--graphite));
    --hero-band-faint:  color-mix(in srgb, var(--snow) 60%, var(--graphite));
    --hero-band-accent: var(--brass-hi);
    --hero-band-rule:   var(--rule-dark);

    --face-line:        color-mix(in srgb, var(--snow) 55%, transparent);
    --face-line-soft:   color-mix(in srgb, var(--steel) 55%, transparent);
    --face-brass:       var(--brass);
    --face-brass-soft:  color-mix(in srgb, var(--brass) 40%, transparent);
    --face-hand:        var(--snow);
    --face-hand-edge:   var(--graphite);
    --face-sun:         var(--brass);
    --face-sun-ring:    var(--brass-hi);
    --face-moon-lit:    color-mix(in srgb, var(--snow) 85%, var(--steel));
    --face-moon-dark:   var(--graphite);
    --face-moon-rim:    color-mix(in srgb, var(--brass) 55%, transparent);
    --face-text:        var(--snow);
    --face-window:      var(--graphite);
    --face-gear:        color-mix(in srgb, var(--brass) 62%, transparent);
    --face-gear-hi:     var(--brass-hi);
    --face-gear-opacity: 0.42;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --ground:  var(--graphite);
  --surface: var(--graphite-2);
  --fg:      var(--snow);
  --accent-text: var(--brass);  /* full brass reads 6:1 on graphite */
  --rule:       color-mix(in srgb, var(--snow) 14%, transparent);
  --rule-brass: color-mix(in srgb, var(--brass) 55%, transparent);
  --label:      color-mix(in srgb, var(--snow) 62%, var(--graphite));
  --body-soft:  color-mix(in srgb, var(--snow) 90%, var(--graphite));

  --movement:   var(--graphite-2);
  --movement-2: color-mix(in srgb, var(--graphite-2) 82%, var(--snow));
  --on-dark-soft: color-mix(in srgb, var(--snow) 80%, var(--graphite-2));
  --on-dark-faint: color-mix(in srgb, var(--snow) 62%, var(--graphite-2));

  /* NIGHT face (t-049) — keep byte-identical with the media-query block above. */
  --hero-band:        var(--graphite);
  --hero-band-fg:     var(--snow);
  --hero-band-soft:   color-mix(in srgb, var(--snow) 78%, var(--graphite));
  --hero-band-faint:  color-mix(in srgb, var(--snow) 60%, var(--graphite));
  --hero-band-accent: var(--brass-hi);
  --hero-band-rule:   var(--rule-dark);

  --face-line:        color-mix(in srgb, var(--snow) 55%, transparent);
  --face-line-soft:   color-mix(in srgb, var(--steel) 55%, transparent);
  --face-brass:       var(--brass);
  --face-brass-soft:  color-mix(in srgb, var(--brass) 40%, transparent);
  --face-hand:        var(--snow);
  --face-hand-edge:   var(--graphite);
  --face-sun:         var(--brass);
  --face-sun-ring:    var(--brass-hi);
  --face-moon-lit:    color-mix(in srgb, var(--snow) 85%, var(--steel));
  --face-moon-dark:   var(--graphite);
  --face-moon-rim:    color-mix(in srgb, var(--brass) 55%, transparent);
  --face-text:        var(--snow);
  --face-window:      var(--graphite);
  --face-gear:        color-mix(in srgb, var(--brass) 62%, transparent);
  --face-gear-hi:     var(--brass-hi);
  --face-gear-opacity: 0.42;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

main {
  flex: 1 0 auto;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}

p {
  margin: 0 0 1.15em;
  max-width: var(--measure);
}

h2 + p {
  margin-top: 0.75em;
}

a {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-brass);
  transition: border-color var(--t-1) var(--ease-mech), color var(--t-1) var(--ease-mech);
}

a:hover {
  border-bottom-color: var(--accent);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 1px;
}

::selection {
  background: color-mix(in srgb, var(--brass) 30%, transparent);
  color: inherit;
}

/* Underline-growth: a resting hairline that a solid brass rule extends over on
   hover, like a watch hand sweeping to the mark. */
.link-grow {
  position: relative;
  display: inline-block;
  color: var(--fg);
  border-bottom: 1px solid var(--rule-brass);
}

.link-grow::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  height: 1px;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-2) var(--ease-settle);
}

.link-grow:hover::after,
.link-grow:focus-visible::after {
  transform: scaleX(1);
}

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

/* --- Layout scaffolding ------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: 74rem;
  margin-inline: auto;
  padding-inline: clamp(1.5rem, 5vw, 4rem);
}

.section {
  padding-block: clamp(4rem, 9vw, 7.5rem);
}

/* Eyebrow: a data label, carried by a short brass index tick — the watch-face
   vocabulary that recurs across the site. */
.eyebrow {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--label);
  margin: 0 0 1.5rem;
}

.eyebrow::before {
  content: "";
  width: 1.4rem;
  height: 1px;
  background: var(--accent);
  flex: none;
}

.lede {
  font-size: var(--step-2);
  line-height: 1.36;
  max-width: 34ch;
  color: var(--body-soft);
}

.honesty-note {
  margin-top: clamp(2rem, 5vw, 3rem);
  max-width: 58ch;
  font-size: 0.9rem;
  color: var(--label);
}

/* --- Skip link ---------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: 1rem;
  top: -3.5rem;
  z-index: 20;
  background: var(--movement);
  color: var(--on-dark);
  padding: 0.6rem 1rem;
  border: 0;
  transition: top var(--t-1) var(--ease-mech);
}

.skip-link:focus {
  top: 1rem;
}

/* --- Header / nav ------------------------------------------------------- */

.site-header {
  border-bottom: 1px solid var(--rule);
  background: var(--ground);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 1.25rem;
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 0;
  color: var(--fg);
}

/* The wordmark mark — a "U" whose counter is an escape-wheel silhouette. The
   escape wheel indexes to its mark once on first load, then rests: the whole
   instrument coming alive, on every page, in phase with the movement beat.
   Gated on html.js-enter (tick.js, loaded in <head> before first paint) so a
   no-JS visitor gets the static mark with nothing hidden. */
.brand-mark {
  width: 1.55rem;
  height: 1.55rem;
  display: block;
  flex: none;
  color: var(--accent);
}

html.js-enter .brand-mark .mark-wheel {
  transform-box: fill-box;
  transform-origin: center;
}

@media (prefers-reduced-motion: no-preference) {
  html.js-enter .brand-mark .mark-wheel {
    animation: mark-tick var(--t-wind) var(--ease-mech) 1 both;
  }
}

@keyframes mark-tick {
  0%   { transform: rotate(-38deg); opacity: 0; }
  60%  { opacity: 1; }
  100% { transform: rotate(0deg); opacity: 1; }
}

.brand .brand-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.16rem;
  letter-spacing: 0.02em;
}

/* Contact promoted to the top level (t-077): the primary nav is a single
   Contact chip beside the theme toggle — the wordmark is the way home, so the
   Home link went with the list. Styled in the hero CTA's language (mono,
   uppercase, brass rule) so header and hero speak with one voice; fills brass
   on hover/focus like the CTA. WCAG target size via the chip's min-height. */
.site-nav {
  display: flex;
  align-items: center;
}

.nav-contact {
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem;
  padding: 0 0.95rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-text);
  border: 1px solid var(--rule-brass);
  border-radius: 4px;
  white-space: nowrap;
  transition: color var(--t-2) var(--ease-mech), background-color var(--t-2) var(--ease-mech),
    border-color var(--t-2) var(--ease-mech);
}

.nav-contact:hover,
.nav-contact:focus-visible {
  background: var(--accent);
  color: var(--graphite);
  border-color: var(--accent);
}

/* Ring clears the brass fill in both themes (mirrors .hero-cta's focus fix). */
.nav-contact:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
}

/* On the contact page itself the chip reads as location, not a repeat action. */
.nav-contact[aria-current="page"] {
  border-color: var(--accent);
  color: var(--fg);
}

/* --- Theme toggle (top-right; brass in both themes) --------------------- */

/* Push the brand hard-left so the nav and the toggle sit together on the right
   as one group — no header-markup wrapper needed on any of the 14 pages (t-020). */
.site-header .brand {
  margin-right: auto;
}

/* Hidden until theme.js wires it (html.js-theme), so a no-JS visitor never meets
   a control that would do nothing — the page still follows prefers-color-scheme. */
.theme-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--rule-brass);
  border-radius: 50%;
  cursor: pointer;
  transition: border-color var(--t-1) var(--ease-mech), color var(--t-1) var(--ease-mech);
}

html.js-theme .theme-toggle {
  display: inline-flex;
}

.theme-toggle:hover {
  border-color: var(--accent);
  color: var(--accent-hi);
}

/* A half-moon crescent, brass stroke. It rotates a half-turn when dark is
   engaged — waxing to waning; transform-only, so reduced-motion simply snaps it. */
.theme-glyph {
  width: 1.15rem;
  height: 1.15rem;
  display: block;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--t-3) var(--ease-settle);
}

.theme-toggle[aria-pressed="true"] .theme-glyph {
  transform: rotate(180deg);
}

.theme-crescent {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* (t-077) The header no longer wraps on mobile: with the nav reduced to the
   single Contact chip, brand + chip + toggle share one row at every width. */

/* --- Hero: the movement (the mechanism comes alive here) ---------------- */

/* --- Hero: agent-forward (t-061) ----------------------------------------
   Left column states the offer; the right column is an always-dark agent
   panel — a code-block surface that reads dark in both themes, rotating
   through four illustrative sample agents (inbox/support/outbound/
   reporting). The panel is authored complete for JS off (a finished inbox
   run); agents.js winds it into rotation, exactly as the old dial was drawn
   static and wound by watch.js. Every panel colour is a var()/color-mix()
   of the eight tokens — no raw hex, no green — so the palette guard holds. */
.hero {
  background: var(--ground);
  border-bottom: 1px solid var(--rule);
  /* Tighter top than bottom (t-072 round-4): the split-flap sits close under the
     navbar so the top of the page reads composed, not sparse. */
  padding-block: clamp(0.9rem, 2.2vw, 1.5rem) clamp(1.75rem, 4vw, 3.25rem);
}

.hero-grid {
  display: grid;
  gap: clamp(1.1rem, 3.2vw, 1.9rem);
  align-content: center;
}

@media (min-width: 54rem) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
    grid-template-areas:
      "motto motto"
      "head  panel"
      "body  panel";
    column-gap: clamp(1.75rem, 4vw, 3.5rem);
    row-gap: 1.05rem;
    align-items: stretch;
    min-height: min(40vh, 23rem);
  }
  .hero-motto { grid-area: motto; }
  .hero-head { grid-area: head; align-self: end; }
  .agent-panel { grid-area: panel; align-self: center; }
  .hero-body { grid-area: body; align-self: start; }
}

/* The split-flap motto board, moved to the top of the hero (t-072). A slim
   brand strip above the two hero columns. JS off / reduced motion shows the
   static "Som ett urverk / like clockwork" text; flap.js swaps in tiles. On the
   light hero ground the static motto uses the AA-safe accent text token. */
.hero-motto {
  margin: 0;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  min-height: 1.6em;
}
.hero-motto .detail-motto {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--accent-text);
  letter-spacing: 0.02em;
  margin-right: 0.6rem;
}
.hero-motto .detail-gloss {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--label);
}

/* The size/weight live on the <h1> itself, not the .hero-head wrapper: the
   global h1,h2,h3 rule sets no font-size (so the UA h1{font-size:2em} would
   double a wrapper value) and pins font-weight:500. Targeting the h1 directly
   fixes both — matches the reference mockup's ~64px bold two-line headline. */
.hero-head h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.2rem, 5.2vw, 4rem);
  line-height: 1.03;
  letter-spacing: -0.015em;
  color: var(--fg);
}

.hero-head .em { color: var(--accent-text); }

/* Optional who-it's-for line under the headline (t-072 round-7). One short,
   understated line — body-soft, narrow measure — so it qualifies the headline
   without re-cluttering the round-4 decluttered hero. Animates with .hero-head
   as one unit (no separate entrance keyframe). */
.hero-whofor {
  margin: 0.85rem 0 0;
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--body-soft);
  max-width: 34ch;
}

.hero-body {
  max-width: 34rem;
}

.hero-sub {
  margin: 0;
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--body-soft);
  max-width: 34ch;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  /* The value-prop paragraph that used to sit above this was re-homed (t-072
     round-4), so the actions sit closer under the headline. */
  margin-top: clamp(0.9rem, 2vw, 1.4rem);
}

.hero-actions .button { margin: 0; }

/* Monospace CTA (t-072 round-5): the primary hero action rendered like a CLI
   command — monospace, uppercase, brass. Inverts to a filled brass chip on
   hover/focus. (The round-4 decorative [ ] bracket pseudo-elements were removed;
   the accessible name was always just "Deploy Agent".) */
.hero-cta {
  display: inline-flex;
  align-items: center;
  margin: 0;
  padding: 0.72rem 1.15rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-text);
  background: transparent;
  border: 1px solid var(--rule-brass);
  border-radius: 4px;
  white-space: nowrap;
  transition: color var(--t-2) var(--ease-mech), background-color var(--t-2) var(--ease-mech),
    border-color var(--t-2) var(--ease-mech);
}
.hero-cta:hover,
.hero-cta:focus-visible {
  background: var(--accent);
  color: var(--graphite);
  border-color: var(--accent);
}
/* Distinct keyboard focus ring: on focus the CTA fills brass, so a brass outline
   read as merely a thicker border (round-4 nit). The ring uses the band
   foreground — ink on the day hero, snow on the night hero — so it clears the
   brass chip in both themes, offset so it reads as a ring, not an edge. */
.hero-cta:focus-visible {
  outline: 2px solid var(--hero-band-fg);
  outline-offset: 3px;
}

/* Filled brass primary; inverts to outline on hover (the base .button is the
   reverse — outline that fills on hover — so the modifier flips both states). */
.button.button-primary {
  background: var(--accent);
  color: var(--graphite);
}
.button.button-primary::after { display: none; }
.button.button-primary:hover {
  background: transparent;
  color: var(--fg);
}

.button.button-ghost { border-color: var(--rule-brass); }  /* visible outline in both themes (was --rule, near-invisible on light) */
.button.button-ghost::after { display: none; }
.button.button-ghost:hover {
  background: transparent;
  color: var(--accent);
  border-color: var(--accent);
}
.button.button-ghost .arrow {
  color: var(--accent);
  transition: transform var(--t-1) var(--ease-mech);
}
.button.button-ghost:hover .arrow { transform: translateX(4px); }

.hero-trust {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 1.3rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--label);
}
.hero-trust::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

/* --- The agent panel: a labelled SAMPLE agent run (t-072) ----------------
   Revamped into a real working surface, not a small card. Always-dark in both
   themes (a code-block ground). A run moves through three phase lamps
   (Read → Decide → Act), four tasks transition ○ → ▸ → ✓, an activity trace
   logs each step, and on completion the proof-run checks (tests · evals ·
   security) tick — tying the panel to the open-movement proof language. It is
   an ILLUSTRATIVE sample: no client telemetry, no invented numbers. Authored
   complete in the HTML (JS-off shows a finished run); agents.js winds it into
   rotation. Every colour is a token/color-mix — palette guard holds; the OK
   state is brass, never green. */
.agent-panel {
  width: 100%;
  max-width: 33rem;
  background: var(--ap-panel);
  color: var(--ap-fg);
  border: 1px solid var(--ap-rule);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 34px 80px -44px color-mix(in srgb, var(--graphite) 80%, transparent);
  font-family: var(--font-mono);
}

/* Header: sample tag, agent name, live state. */
.ap-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  background: var(--ap-bar);
  border-bottom: 1px solid var(--ap-rule);
}
.ap-tag {
  font-size: 0.58rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--movement);
  background: var(--ap-accent);
  padding: 0.2rem 0.5rem;
  border-radius: 3px;
  flex: none;
  font-weight: 600;
}
.ap-headline {
  color: var(--ap-fg);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ap-live {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ap-accent);
}

.ap-spin {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--ap-accent);
  border-top-color: transparent;
  flex: none;
}
@media (prefers-reduced-motion: no-preference) {
  .ap-spin { animation: ap-spin var(--tick-period) linear infinite; }
}
@keyframes ap-spin { to { transform: rotate(360deg); } }

.ap-live.is-paused { color: var(--ap-dim); }
.ap-live.is-paused .ap-spin {
  animation: none;
  border-color: var(--ap-dim);
  border-top-color: var(--ap-dim);
  opacity: 0.5;
}

/* At phone widths the header's three parts don't share one line without the
   subtitle truncating (round-5 nit — it used to ellipsis at 375px). Drop the
   subtitle onto its own full-width line beneath the tag + live lamp, where it
   reads in full, no ellipsis. */
@media (max-width: 24rem) {
  .ap-headline {
    order: 3;
    flex-basis: 100%;
    white-space: normal;
    overflow: visible;
  }
}

/* Roster tabs: the six agent kinds as real clickable tabs. The active tab
   carries a brass underline and a lit glyph; the rest are quiet. Keyboard: a
   roving tabindex (JS) + arrow keys move between them. */
.ap-tabs {
  display: flex;
  border-bottom: 1px solid var(--ap-rule);
  /* Six agents: the row fills the panel at desktop and, if the panel is ever
     narrower than the tabs, scrolls within the tablist — never the page. */
  overflow-x: auto;
  scrollbar-width: none;
}
.ap-tabs::-webkit-scrollbar { display: none; }
.ap-tab {
  appearance: none;
  background: transparent;
  border: none;
  border-right: 1px solid var(--ap-rule);
  border-bottom: 2px solid transparent;
  margin: 0;
  padding: 0.7rem 0.3rem 0.6rem;
  flex: 1 0 auto;
  /* Floor sized so all SIX roster tabs fit a 375px viewport without the
     tablist scrolling; the widest label (Research) sets its own width. */
  min-width: 3.2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.34rem;
  cursor: pointer;
  color: var(--ap-faint);
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
  transition: color var(--t-2) var(--ease-mech), background var(--t-2) var(--ease-mech), border-color var(--t-2) var(--ease-mech);
}
.ap-tab:last-child { border-right: none; }
.ap-tab-glyph { width: 24px; height: 24px; display: block; }
.ap-glyph {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ap-tab-label { line-height: 1; }
.ap-tab:hover { color: var(--ap-fg); background: var(--ap-active); }
.ap-tab:focus-visible {
  outline: 2px solid var(--ap-accent);
  outline-offset: -2px;
}
.ap-tab[aria-selected="true"] {
  color: var(--ap-fg);
  border-bottom-color: var(--accent-hi);
  background: var(--ap-active);
}
.ap-tab[aria-selected="true"] .ap-glyph { stroke: var(--accent-hi); }

/* Progress rail: a work token that travels agent to agent as the roster
   advances. Position is transform-only (CSP: no inline style) — the node is
   one-sixth wide and steps by its own width per stage, driven by the panel's
   data-stage attribute. */
.ap-rail {
  position: relative;
  height: 22px;
  margin: 0 1rem;
}
.ap-rail-track {
  position: absolute;
  left: 10%;
  right: 10%;
  top: 50%;
  height: 1px;
  background: var(--ap-rule);
}
.ap-rail-node {
  position: absolute;
  top: 50%;
  left: 0;
  width: calc(100% / 6);
  height: 0;
  transform: translateX(0);
  transition: transform var(--t-3) var(--ease-settle);
}
.ap-rail-node::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--accent-hi);
  box-shadow: 0 0 0 4px var(--ap-active);
}
.agent-panel[data-stage="1"] .ap-rail-node { transform: translateX(100%); }
.agent-panel[data-stage="2"] .ap-rail-node { transform: translateX(200%); }
.agent-panel[data-stage="3"] .ap-rail-node { transform: translateX(300%); }
.agent-panel[data-stage="4"] .ap-rail-node { transform: translateX(400%); }
.agent-panel[data-stage="5"] .ap-rail-node { transform: translateX(500%); }

/* Stage: what the active station did to the work. A large role glyph beside the
   sample copy — the classified message, then the hand-off it produced. Swapping
   stations cross-fades the copy (opacity only). */
.ap-stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 1rem;
  padding: 0.4rem 1.1rem 1.05rem;
  min-height: 7.3rem;
}
.ap-stage:focus-visible {
  outline: 2px solid var(--ap-accent);
  outline-offset: 2px;
  border-radius: 8px;
}
.ap-stage-glyph {
  position: relative;
  width: 60px;
  height: 60px;
  flex: none;
}
.ap-stage-mark { position: absolute; inset: 0; width: 60px; height: 60px; display: none; }
.ap-stage-mark.is-on { display: block; }
.ap-mark-ring { fill: var(--ap-mark-bg); }
.ap-mark-rim { fill: none; stroke: var(--ap-accent); stroke-opacity: 0.5; stroke-width: 1.2; }
.ap-mark-glyph {
  fill: none;
  stroke: var(--accent-hi);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ap-stage-copy { min-width: 0; }
.ap-stage-kicker {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ap-accent);
  margin-bottom: 0.3rem;
}
/* Ordered subtasks for the active station: each ticks ○ → ▸ → ✓ on the --beat,
   and the station advances once all are ✓. Markers are drawn from the row's
   state class (decorative, aria-hidden). */
.ap-tasks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.36rem;
}
.ap-task {
  display: flex;
  align-items: center;
  gap: 0.6ch;
  font-size: 0.84rem;
  line-height: 1.3;
  color: var(--ap-faint);
  transition: color var(--t-2) var(--ease-mech);
}
.ap-task-mk {
  flex: none;
  width: 1.1em;
  text-align: center;
  color: var(--ap-dim);
  transition: color var(--t-1) var(--ease-mech);
}
.ap-task.is-todo .ap-task-mk::before { content: "\25CB"; }   /* ○ */
.ap-task.is-active .ap-task-mk::before { content: "\25B8"; } /* ▸ */
.ap-task.is-done .ap-task-mk::before { content: "\2713"; }   /* ✓ */
.ap-task.is-active { color: var(--ap-fg); font-weight: 600; }
.ap-task.is-active .ap-task-mk { color: var(--accent-hi); }
.ap-task.is-done { color: var(--ap-dim); }
.ap-task.is-done .ap-task-mk { color: var(--accent-hi); }
@media (prefers-reduced-motion: no-preference) {
  .agent-panel.is-winding .ap-stage-copy,
  .agent-panel.is-winding .ap-stage-glyph { transition: opacity var(--t-2) var(--ease-settle); }
  .agent-panel.is-swapping .ap-stage-copy,
  .agent-panel.is-swapping .ap-stage-glyph { opacity: 0; }
}

/* Footer: the proof-run checks tick on completion, tying the panel to the
   open-movement proof language; then the honest sample disclaimer. */
.ap-foot {
  border-top: 1px dashed var(--ap-dash);
  padding: 0.85rem 1.1rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.ap-proof {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.8rem;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
}
.ap-proof-label {
  color: var(--ap-accent);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.6rem;
}
.ap-check {
  display: inline-flex;
  align-items: center;
  gap: 0.4ch;
  color: var(--ap-faint);
  transition: color var(--t-2) var(--ease-mech);
}
.ap-check-mk {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid var(--ap-dim);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 8px;
  line-height: 1;
  flex: none;
  transition: border-color var(--t-1) var(--ease-mech), background var(--t-1) var(--ease-mech), color var(--t-1) var(--ease-mech);
}
.ap-check.is-ok { color: var(--ap-fg); }
.ap-check.is-ok .ap-check-mk {
  border-color: var(--accent-hi);
  background: var(--accent-hi);
  color: var(--movement);
}
.ap-check.is-ok .ap-check-mk::before { content: "\2713"; }
.ap-verified { color: var(--ap-dim); font-size: 0.72rem; }
.ap-verified b { color: var(--ap-accent); font-weight: 600; }

/* Entrance: gated on html.js-enter (tick.js) so JS-off is fully static —
   the site keeps first-paint-hidden states behind the JS flag on purpose. */
@media (prefers-reduced-motion: no-preference) {
  html.js-enter .hero-motto,
  html.js-enter .hero-head,
  html.js-enter .agent-panel,
  html.js-enter .hero-body > * {
    opacity: 0;
    transform: translateY(10px);
    animation: hero-rise var(--t-5) var(--ease-settle) forwards;
  }
  html.js-enter .hero-motto { animation-delay: 0s; }
  /* Orchestrated on the beat: the head leads, the body cascades one beat at a
     time, and the panel settles in with the actions so the two columns arrive
     as one movement rather than everything at once. */
  html.js-enter .hero-head { animation-delay: 0s; }
  html.js-enter .hero-body .hero-actions { animation-delay: var(--beat); }
  html.js-enter .hero-body .hero-trust { animation-delay: calc(var(--beat) * 2); }
  html.js-enter .agent-panel { animation-delay: calc(var(--beat) * 2); }
}
@keyframes hero-rise { to { opacity: 1; transform: none; } }

@media (max-width: 53.99rem) {
  .agent-panel { justify-self: start; max-width: 30rem; }
}

/* --- Pillars ------------------------------------------------------------ */

.pillars {
  display: grid;
  gap: clamp(2.25rem, 5vw, 3.5rem);
}

@media (min-width: 52rem) {
  .pillars {
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(2rem, 4vw, 3.5rem);
  }
}

/* Each pillar hangs from a hairline index rule with a brass tick — a watch
   face's marker, reused as structure. */
.pillar {
  position: relative;
  border-top: 1px solid var(--rule);
  padding-top: 1.5rem;
}

.pillar::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 2.5rem;
  height: 2px;
  background: var(--accent);
}

.pillar h3 {
  font-size: var(--step-2);
  margin-bottom: 0.7rem;
}

.pillar p {
  margin-bottom: 0;
  color: var(--body-soft);
}

/* --- Demonstrations card grid (t-017) ----------------------------------- */

.demos-grid {
  display: grid;
  gap: clamp(1.75rem, 4vw, 2.5rem) clamp(1.5rem, 4vw, 2.5rem);
  margin-top: clamp(2rem, 5vw, 3rem);
}

@media (min-width: 40rem) {
  .demos-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 60rem) {
  .demos-grid { grid-template-columns: repeat(3, 1fr); }
}

/* Each card hangs from a hairline index rule with a brass tick — the watch-face
   vocabulary the pillars use. */
.demo-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  border-top: 1px solid var(--rule);
  padding-top: 1.4rem;
}

.demo-card::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 2.5rem;
  height: 2px;
  background: var(--accent);
}

/* The persistent honesty chip — a brass-tinted badge on every card (t-017). */
.demo-chip {
  align-self: flex-start;
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--accent-text);
  background: color-mix(in srgb, var(--brass) 10%, transparent);
  border: 1px solid var(--rule-brass);
  border-radius: 4px;
  padding: 0.3rem 0.6rem;
}

.demo-context {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--label);
}

.demo-flag {
  margin-left: 0.5rem;
  font-weight: 600;
  color: var(--accent-text);
}

.demo-title {
  margin: 0;
  font-size: var(--step-1);
}

.demo-desc {
  margin: 0;
  color: var(--body-soft);
}

.demo-tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.2rem 0 0;
  padding: 0;
}

.demo-tags li {
  font-family: var(--font-body);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--label);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0.22rem 0.55rem;
}

/* --- Proof-run (mechanism ground) ------------------------------------------- */

.proof-run {
  background: var(--movement);
  color: var(--on-dark);
}

.proof-run .eyebrow {
  color: var(--accent-hi);
}

.proof-run .eyebrow::before {
  background: var(--accent-hi);
}

.proof-run h2 {
  font-size: var(--step-3);
  max-width: 22ch;
  margin-bottom: 1.5rem;
  color: var(--on-dark);
}

.proof-run p {
  color: var(--on-dark-soft);
  max-width: 62ch;
}

.proof-run .runs {
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--rule-dark);
  border: 1px solid var(--rule-dark);
}

@media (min-width: 40rem) {
  .proof-run .runs {
    grid-template-columns: 1fr 1fr;
  }

  /* The services "wrapper" panel holds exactly three terms — a 3-col grid at the
     same breakpoint fills the row cleanly instead of leaving an empty quadrant. */
  .proof-run .runs-trio {
    grid-template-columns: repeat(3, 1fr);
  }
}

.proof-run .runs li {
  background: var(--movement);
  padding: 1.3rem 1.4rem;
}

.proof-run .runs .run-name {
  display: block;
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--accent-hi);
  margin-bottom: 0.35rem;
}

.proof-run .runs .run-desc {
  color: var(--on-dark-soft);
  font-size: 0.95rem;
}

.proof-run .proof-run-stamp {
  margin: clamp(1.5rem, 4vw, 2rem) 0 0;
  font-size: 0.9rem;
  color: var(--on-dark-soft);
  font-variant-numeric: tabular-nums;
}

.proof-run .proof-run-stamp strong {
  color: var(--accent-hi);
  letter-spacing: 0.08em;
}

.proof-run .proof-run-stamp a {
  color: var(--accent-hi);
  border-bottom-color: transparent;
}

.proof-run .proof-run-stamp a:hover,
.proof-run .proof-run-stamp a:focus-visible {
  border-bottom-color: var(--accent-hi);
}

/* --- The open movement: case-flip proof section (t-072) ----------------- */
/* The proof-run section carries a watch that turns over in 3D — a closed
   complication dial on the front, the exposed gear train (the exhibition
   caseback) on the back. The stage always sits on the dark mechanism ground
   (.proof-run), so the face tokens are pinned to their NIGHT values in BOTH
   themes here (snow hands, brass gears, steel hairlines) — the same always-dark
   treatment the agent panel uses. Every value is token-derived; the palette
   guard holds. Transform/opacity/clip-path only. */
.open-movement {
  --face-line:        color-mix(in srgb, var(--snow) 55%, transparent);
  --face-line-soft:   color-mix(in srgb, var(--steel) 55%, transparent);
  --face-brass:       var(--brass);
  --face-brass-soft:  color-mix(in srgb, var(--brass) 40%, transparent);
  --face-hand:        var(--snow);
  --face-hand-edge:   var(--graphite);
  --face-sun:         var(--brass);
  --face-gear:        color-mix(in srgb, var(--brass) 66%, transparent);
  --face-gear-hi:     var(--brass-hi);
}

.open-movement .om-lede {
  font-size: var(--step-1);
  max-width: 60ch;
}

.open-movement .om-runs {
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
}

.open-movement .om-report {
  margin-top: clamp(1.5rem, 3vw, 2rem);
  font-size: 0.95rem;
}

.open-movement .om-report b {
  color: var(--accent-hi);
}

/* Stage: the flip scene centred, with the turn-the-case affordance beneath. */
.om-stage {
  margin-top: clamp(2rem, 5vw, 3rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.4rem;
}

.flip-scene {
  position: relative;
  width: 100%;
  max-width: 30rem;
}

.flip-card {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
}

/* The two layers of the hunter case: the movement seated in the case body, and
   the complication dial as a hinged lid over it. Both fill the stage; the lid
   pivots on its LEFT edge. */
.case-body,
.case-lid {
  position: absolute;
  inset: 0;
}

/* Base + no-JS + reduced-motion default: the case is already open — the
   movement is the static face (the copy is about visible gears), fully
   readable, no 3D. The lid is out of the picture until JS arms the hinge. */
.case-lid { display: none; }
.case-body { display: block; }

/* The lid has two faces stacked in the lid's own 3D space: the dial (front) and
   the engraved cuvette (back, pre-rotated 180°). Both hide their own back side,
   so as the lid swings PAST 90° the dial turns away and the inside face turns to
   the viewer — nothing goes edge-on and disappears. Inert until js-flip arms the
   hinge (the whole lid is display:none with JS off). */
.lid-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
}
/* (t-076) The inside of the lid is the engraved cuvette, not the old mirrored
   dial: the 180° pre-rotation nets out against the open swing, so the engraving
   is authored upright and reads upright on the standing-open lid. Styles live
   in the watch-furniture block below. */
.lid-back { transform: rotateY(180deg); }

/* Visible hinge barrel joining the lid to the case on the left edge (t-072
   round-4). It sits on the case rim at 9 o'clock — the axis the lid pivots on —
   so the open lid and the case body never read as two floating pieces. A brass
   cylinder with two knuckle divisions; token colours only, so the palette guard
   holds. Shown in every state (JS-off, reduced motion, and the live swing). */
.case-hinge {
  position: absolute;
  left: calc(18.3% - 5px);
  top: 35%;
  bottom: 35%;
  width: 10px;
  border-radius: 5px;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--brass) 48%, var(--graphite)),
    var(--brass-hi) 48%,
    color-mix(in srgb, var(--brass) 54%, var(--graphite))
  );
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--graphite) 55%, transparent),
    0 3px 7px -3px color-mix(in srgb, var(--graphite) 70%, transparent);
  z-index: 4;
}
.case-hinge::before,
.case-hinge::after {
  content: "";
  position: absolute;
  left: 1px;
  right: 1px;
  height: 1px;
  background: color-mix(in srgb, var(--graphite) 60%, transparent);
}
.case-hinge::before { top: 34%; }
.case-hinge::after { bottom: 34%; }

.clockwork-face {
  display: block;
  width: 100%;
  height: 100%;
  color: var(--face-brass);
  overflow: visible;
}

/* JS + motion-OK: the real hunter-case opening. The lid is hinged on the case's
   left rim (transform-origin ~18.3%, where the visible bezel meets the hinge
   barrel) and swings open toward the viewer, stopping past 90° so it stands open
   as a raking panel. backface-visibility:hidden keeps exactly one lid face
   showing — the front dial when closed, the engraved cuvette when open. The movement
   sits a hair behind so the lid reads as ON it, not coplanar. */
@media (prefers-reduced-motion: no-preference) {
  html.js-flip .flip-scene { perspective: 1500px; perspective-origin: 50% 44%; }
  html.js-flip .flip-card { transform-style: preserve-3d; }
  html.js-flip .case-body { transform: translateZ(-2px); }
  html.js-flip .case-lid {
    display: block;
    transform-style: preserve-3d;
    transform-origin: 18.3% center;
    transform: rotateY(0deg);
    transition: transform calc(var(--beat) * 7) var(--ease-settle);
  }
  /* Past 90°: the lid stands open leaning back, its engraved-cuvette inside face
     turned to the viewer, the dial (front) facing away. */
  html.js-flip .flip-card.is-open .case-lid { transform: rotateY(-110deg); }
}

/* JS + reduced motion: the same hinge, but no travel — the global reduce reset
   drops the transition, so JS opens it to the standing-lid state instantly. */
@media (prefers-reduced-motion: reduce) {
  html.js-flip .flip-scene { perspective: 1500px; }
  html.js-flip .flip-card { transform-style: preserve-3d; }
  html.js-flip .case-body { transform: translateZ(-2px); }
  html.js-flip .case-lid {
    display: block;
    transform-style: preserve-3d;
    transform-origin: 18.3% center;
    transform: rotateY(0deg);
  }
  html.js-flip .flip-card.is-open .case-lid { transform: rotateY(-110deg); }
}

/* --- Watch furniture: real complication dial + exhibition caseback (t-072) -
   Both the lid and the body share viewBox 0 0 600 400, case centre (300,200)
   and an identical brass bezel band, so lid + body read as one physical object.
   The lid is a cream complication dial (Roman numerals, moon/sun/date/day); the
   body is the movement — barrel, meshing going train, escapement + balance,
   jewels — clipped to the case circle so nothing spills the case. Every
   fill/stroke is token-derived; the palette guard holds. The section sits on the
   dark mechanism ground, so the cream dial reads identically light/dark. */

/* Shared bezel band — the substantial round case rim, drawn on both faces. */
.bezel-band { fill: var(--brass); }
.bezel-groove {
  fill: none;
  stroke: color-mix(in srgb, var(--graphite) 60%, transparent);
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
}

/* ---- Front: the complication dial ------------------------------------- */
.dial { fill: var(--paper); }
.guilloche-line {
  stroke: color-mix(in srgb, var(--ink) 24%, transparent);
  stroke-width: 0.5;
}
.dial-guilloche { fill: url(#guilloche); opacity: 0.55; }
.dial-flange {
  fill: none;
  stroke: color-mix(in srgb, var(--ink) 14%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.chap-min {
  fill: none;
  stroke: color-mix(in srgb, var(--ink) 42%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.chap-hour {
  fill: none;
  stroke: var(--brass);
  stroke-width: 2.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.rn {
  fill: var(--ink);
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  /* Smaller, finer numerals set near the chapter ring — a refined classic dial
     (t-072), not the earlier oversized bold ones. */
  font-size: 21px;
  font-weight: 400;
  letter-spacing: 0;
  text-anchor: middle;
  dominant-baseline: central;
}

/* Subdials — cream faces, brass rings, tiny mono labels. */
.subdial-face { fill: color-mix(in srgb, var(--paper) 80%, var(--snow)); }
.subdial-ring {
  fill: none;
  stroke: var(--brass);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}
.sub-label {
  fill: color-mix(in srgb, var(--ink) 58%, var(--paper));
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.14em;
  text-anchor: middle;
}
/* Moonphase: night sky, a half-lit moon, two stars. */
.moon-sky { fill: var(--graphite); }
.moon-lit { fill: color-mix(in srgb, var(--snow) 76%, var(--steel)); }
.moon-dark { fill: var(--graphite); }
.moon-star { fill: var(--brass-hi); }
/* Sun / landscape: pale sky, brass sun, ridged foreground. */
.sun-sky { fill: color-mix(in srgb, var(--snow) 82%, var(--steel)); }
.sun-body { fill: var(--brass); }
.sun-ground { fill: color-mix(in srgb, var(--ink) 58%, var(--paper)); }
.sun-hill { fill: color-mix(in srgb, var(--brass) 42%, var(--ink)); }
/* Framed date aperture. */
.date-window { fill: var(--snow); }
.date-frame {
  fill: none;
  stroke: var(--brass);
  stroke-width: 1.8;
  vector-effect: non-scaling-stroke;
}
.date-num {
  fill: var(--ink);
  font-family: "Iowan Old Style", Palatino, Georgia, serif;
  font-size: 20px;
  font-weight: 600;
  text-anchor: middle;
  dominant-baseline: central;
}
/* Day / season subdial. */
.day-day {
  fill: var(--ink);
  font-family: "Iowan Old Style", Palatino, Georgia, serif;
  font-size: 13px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}
.day-season {
  fill: color-mix(in srgb, var(--ink) 58%, var(--paper));
  font-family: var(--font-mono);
  font-size: 7px;
  letter-spacing: 0.12em;
  text-anchor: middle;
  dominant-baseline: central;
}

/* Hands: filled dauphine hour/minute in ink with a snow edge; a fine brass-hi
   seconds needle with a counterweight (the reference's red maps to brass-hi). */
.h-hour,
.h-min {
  fill: var(--ink);
  stroke: color-mix(in srgb, var(--snow) 72%, var(--paper));
  stroke-width: 1;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.h-sec-arm {
  stroke: var(--brass-hi);
  stroke-width: 2;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.h-sec-cw { fill: var(--brass-hi); }
.dial-cap {
  fill: var(--brass);
  stroke: color-mix(in srgb, var(--snow) 60%, var(--paper));
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.dial-cap-hi { fill: var(--brass-hi); }

/* ---- Inside of the lid: the engraved cuvette (t-076) ------------------- */
/* A hunter case's inner lid is where the maker engraves. Polished old-gold
   ground, engine-turned like the dial, two engraved border rings, the motto
   arced over the engraved brand mark with its gloss beneath — so the open case
   reads the studio's promise where a maker would sign. Replaces the mirrored-
   dial lid back (t-072 round-4), which repeated the front face and carried no
   message. Decorative (the SVG is aria-hidden); every colour token-derived,
   and the low engraving contrast is deliberate — it is an engraving, not copy;
   the readable text lives in the proof-run list below the stage. */
.cuv-ground { fill: color-mix(in srgb, var(--brass) 62%, var(--graphite)); }
.cuv-engine-line {
  stroke: color-mix(in srgb, var(--graphite) 38%, transparent);
  stroke-width: 0.5;
}
.cuv-engine-fill { fill: url(#cuv-engine); opacity: 0.5; }
.cuv-ring {
  fill: none;
  stroke: color-mix(in srgb, var(--graphite) 48%, transparent);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}
.cuv-ring-fine { stroke-width: 0.8; }
.cuv-arc {
  fill: color-mix(in srgb, var(--graphite) 86%, var(--brass));
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  font-size: 27px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-anchor: middle;
}
.cuv-gloss {
  fill: color-mix(in srgb, var(--graphite) 66%, var(--brass));
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.34em;
  text-anchor: middle;
}
.cuv-mark {
  fill: none;
  stroke: color-mix(in srgb, var(--graphite) 86%, var(--brass));
}

/* ---- Back: the exhibition caseback (movement, clipped to the case) ----- */
.movement-ground { fill: color-mix(in srgb, var(--graphite) 90%, var(--snow)); }
.mainspring {
  fill: none;
  stroke: color-mix(in srgb, var(--brass) 42%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.barrel-arbor { fill: var(--brass-hi); }

.gear-teeth {
  fill: none;
  stroke: color-mix(in srgb, var(--brass) 66%, transparent);
  stroke-width: 1.3;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.gear-pitch {
  fill: none;
  stroke: color-mix(in srgb, var(--brass) 30%, transparent);
  stroke-width: 0.6;
  vector-effect: non-scaling-stroke;
}
.gear-hub {
  fill: none;
  stroke: color-mix(in srgb, var(--brass) 66%, transparent);
  stroke-width: 1.1;
  vector-effect: non-scaling-stroke;
}
.gear-spokes {
  fill: none;
  stroke: color-mix(in srgb, var(--brass-hi) 80%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.gear-jewel { fill: var(--brass-hi); }

.balance-ring,
.balance-ring2 {
  fill: none;
  stroke: color-mix(in srgb, var(--brass) 66%, transparent);
  stroke-width: 1.3;
  vector-effect: non-scaling-stroke;
}
.balance-arm {
  fill: none;
  stroke: color-mix(in srgb, var(--brass-hi) 78%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.balance-screw { fill: var(--brass); }
.balance-jewel { fill: var(--brass-hi); }
.pallet-fork {
  fill: none;
  stroke: color-mix(in srgb, var(--brass-hi) 80%, transparent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.pallet-jewel { fill: var(--brass-hi); }

/* Jewels sit at the pivots, anchoring the going train on the open movement.
   (The translucent brass bridge plates were removed — founder ruling t-072:
   they read as distracting yellow overlays on the gears.) */
.pivot-jewel { fill: var(--brass-hi); }
.pivot-jewel-ring {
  fill: none;
  stroke: var(--brass);
  stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
}
.case-wall {
  fill: none;
  stroke: var(--brass);
  stroke-width: 3;
  vector-effect: non-scaling-stroke;
}
.case-wall-inner {
  fill: none;
  stroke: color-mix(in srgb, var(--steel) 40%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Per-gear periods, proportional to tooth count so meshed wheels stay meshed
   (tooth-passing rate teeth/dur is equal across the train: 44/12.32 = 22/6.16 =
   26/7.28 = 18/5.04 = 15/4.20). A deliberate exception to the --beat grid — the
   mesh ratio governs, not the escapement beat. Set in CSS, never inline
   (style-src 'self'). Gears spin only under JS + motion, while the caseback
   faces you. */
.gear-b { --dur: 12.32s; }
.gear-c { --dur: 6.16s; }
.gear-t { --dur: 7.28s; }
.gear-f { --dur: 5.04s; }
.gear-e { --dur: 4.20s; }
.gear-spin {
  transform-box: fill-box;
  transform-origin: center;
}
.balance-spin {
  transform-box: fill-box;
  transform-origin: center;
}
@media (prefers-reduced-motion: no-preference) {
  /* Declared paused so gears idle while the caseback is hidden behind the front
     dial; the open state runs them. */
  html.js-flip .gear-spin {
    animation: gear-rotate var(--dur, 8s) linear infinite;
    animation-play-state: paused;
  }
  html.js-flip .gear.ccw .gear-spin { animation-direction: reverse; }
  html.js-flip .balance-spin {
    animation: balance-osc 0.9s linear infinite;
    animation-play-state: paused;
  }
  html.js-flip .flip-card.is-open .gear-spin,
  html.js-flip .flip-card.is-open .balance-spin { animation-play-state: running; }
}
@keyframes gear-rotate {
  to { transform: rotate(360deg); }
}
/* The balance BEATS — long holds, a fast snap between limits: an escapement
   tick, never a quartz glide. */
@keyframes balance-osc {
  0%,  44% { transform: rotate(-15deg); }
  50%, 94% { transform: rotate(15deg); }
  100%     { transform: rotate(-15deg); }
}

/* --- Balance bridge: a steel bridge over the balance (t-072 r5) ------------- */
.brg-plate {
  fill: color-mix(in srgb, var(--graphite) 58%, var(--steel));
  stroke: color-mix(in srgb, var(--steel) 60%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.brg-screw {
  fill: var(--steel);
  stroke: color-mix(in srgb, var(--graphite) 55%, transparent);
  stroke-width: 0.8;
  vector-effect: non-scaling-stroke;
}
.brg-screw-slot {
  fill: none;
  stroke: color-mix(in srgb, var(--graphite) 70%, transparent);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Bridge engravings (t-077): each plate bridge is signed for the proof check
   it stands for — TESTS / EVALS / SECURITY — machined lettering catching the
   light, tying the open movement to the proof-run trio beside the stage.
   Names only, never readouts (the founder's no-data-on-the-watch ruling). */
.brg-label {
  fill: color-mix(in srgb, var(--snow) 55%, var(--steel));
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-anchor: middle;
  dominant-baseline: central;
}
/* The ratchet-side bridge is the shortest plate; its longer word cuts smaller. */
.brg-label-sm { font-size: 8px; }

/* Turn-the-case affordance — a ghost brass button on the mechanism ground.
   Hidden until flip.js unhides it (no flip is possible with JS off). */
.flip-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-hi);
  background: transparent;
  border: 1px solid var(--rule-brass);
  border-radius: 3px;
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  transition: border-color var(--t-1) var(--ease-mech), color var(--t-1) var(--ease-mech), transform var(--t-1) var(--ease-mech);
}
.flip-toggle[hidden] { display: none; }
.flip-toggle:hover,
.flip-toggle:focus-visible {
  border-color: var(--accent-hi);
  color: var(--brass-hi);
}
.flip-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* A satisfying mechanical press. */
.flip-toggle:active { transform: translateY(1px) scale(0.99); }

/* Animated affordance: a small brass gear that slowly turns on the --beat,
   signalling the button is interactive; it winds faster on hover/focus. The
   gear is a stroke-dash toothed ring (the same vocabulary as the wordmark
   mark). Motion only — reduced motion leaves it static. */
.flip-toggle-gear {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  display: inline-flex;
  color: var(--accent-hi);
}
.flip-toggle-gear-svg { width: 100%; height: 100%; display: block; overflow: visible; }
.flip-toggle-gear-spin { transform-box: fill-box; transform-origin: center; }
@media (prefers-reduced-motion: no-preference) {
  .flip-toggle-gear-spin { animation: gear-rotate calc(var(--beat) * 44) linear infinite; }
  .flip-toggle:hover .flip-toggle-gear-spin,
  .flip-toggle:focus-visible .flip-toggle-gear-spin { animation-duration: calc(var(--beat) * 20); }
}

@media (max-width: 30rem) {
  /* The lid hinges on the left and opens past 90°, so on a narrow screen it
     needs room to its left. Keep the case compact but a touch larger than round-4
     (14rem) so the movement stays legible on a phone; opening the lid a few more
     degrees foreshortens the standing lid-back so both still fit. */
  .flip-scene { max-width: 16rem; }
  html.js-flip .flip-card.is-open .case-lid { transform: rotateY(-108deg); }
  .rn { font-size: 23px; }
}

/* --- Split-flap motto (Solari board, t-072) ----------------------------- */
/* The footer motto flips like a departure board between "SOM ETT URVERK" and
   "LIKE CLOCKWORK". Server-rendered static text is the whole feature with JS
   off / reduced motion; flap.js swaps in tiles that flip on the --beat. */
.solari-board {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
}
.flap-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0.94em;
  height: 1.55em;
  padding: 0 0.08em;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1;
  color: var(--accent-hi);
  background: color-mix(in srgb, var(--graphite) 78%, var(--snow));
  border: 1px solid var(--ap-rule);
  border-radius: 2px;
  transform-origin: center;
  backface-visibility: hidden;
}
.flap-tile.is-blank {
  /* Same footprint as a lettered tile so every column is a fixed width: the
     board is one stable grid and its right edge never shifts between phrases
     (which carry different space counts). Only the surface goes invisible. */
  background: transparent;
  border-color: transparent;
}
@media (prefers-reduced-motion: no-preference) {
  .flap-tile.flap { animation: flap-turn var(--t-2) var(--ease-mech); }
}
@keyframes flap-turn {
  0%   { transform: rotateX(0deg); }
  49%  { transform: rotateX(-90deg); }
  51%  { transform: rotateX(90deg); }
  100% { transform: rotateX(0deg); }
}

/* --- Pricing ------------------------------------------------------------ */

.engagement h2 {
  font-size: var(--step-3);
  margin-bottom: 0.75rem;
}

.engagement .intro {
  color: var(--label);
  max-width: 48ch;
  margin-bottom: 3rem;
}

.terms {
  margin-top: 2.25rem;
  padding: 0;
  list-style: none;
  color: var(--label);
  max-width: 58ch;
}

.terms li {
  padding-left: 1.4rem;
  position: relative;
  margin-bottom: 0.6rem;
}

.terms li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 0.8rem;
  height: 1px;
  background: var(--accent);
}

/* --- CTA ---------------------------------------------------------------- */

.cta {
  border-top: 1px solid var(--rule);
}

.cta h2 {
  font-size: var(--step-3);
  margin-bottom: 1rem;
  max-width: 18ch;
}

.cta p {
  color: var(--label);
  margin-bottom: 2rem;
}

/* The pill button "winds" on hover: a brass fill sweeps in and the button
   settles up a hair. */
.button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step-1);
  color: var(--fg);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: 0.8rem 1.7rem;
  cursor: pointer;
  transition: color var(--t-2) var(--ease-mech), background-color var(--t-2) var(--ease-mech),
    transform var(--t-1) var(--ease-mech);
}

.button::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 1px solid currentColor;
  opacity: 0.6;
  transition: transform var(--t-3) var(--ease-settle);
}

.button:hover {
  background: var(--accent);
  color: var(--graphite);
  transform: translateY(-1px);
}

.button:hover::after {
  transform: rotate(180deg) scale(1.15);
}

/* --- Contact action: form beside detail (t-016) ------------------------- */

.contact-grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: start;
}

/* Two columns only once the form is live (html.js-contact) and there is room —
   otherwise the always-visible detail column carries the full width, so a no-JS
   visitor never meets an empty form gap. */
@media (min-width: 52rem) {
  html.js-contact .contact-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.contact-detail > :last-child {
  margin-bottom: 0;
}

.contact-email {
  font-size: var(--step-1);
}

.contact-email a {
  font-family: var(--font-display);
}

/* --- Contact structured form (progressive enhancement) ------------------ */

.contact-form-section {
  display: none;
}

html.js-contact .contact-form-section {
  display: block;
}

.contact-form-title {
  font-size: var(--step-3);
  margin-bottom: 1rem;
  max-width: 22ch;
}

.contact-form-lede {
  color: var(--label);
  max-width: 52ch;
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.contact-form .field {
  max-width: 52ch;
  margin-bottom: 1.5rem;
}

.contact-form label {
  display: block;
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--label);
  margin-bottom: 0.5rem;
}

.contact-form input,
.contact-form textarea {
  display: block;
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.7rem 0.85rem;
  transition: border-color var(--t-1) var(--ease-mech);
}

.contact-form input:hover,
.contact-form textarea:hover {
  border-color: var(--rule-brass);
}

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

.contact-form textarea {
  min-height: 4.5rem;
  resize: vertical;
}

.contact-form .field-error {
  margin: 0.4rem 0 0;
  font-size: 0.9rem;
  color: var(--accent-text);
}

.contact-form button {
  margin-top: 0.5rem;
}

/* Visible plaintext-email fallback beside the compose button: on a machine with
   no mail client the mailto compose appears to do nothing, so the address is
   spelled out as selectable text (an anchor whose visible text is the address).
   Muted like the form lede so it reads as a quiet fallback, not a second CTA. */
.contact-form-fallback {
  margin: 0.85rem 0 0;
  font-size: 0.9rem;
  color: var(--label);
}

/* --- Footer (mechanism ground) — the quiet closing path to contact (t-077) --
   The footer must not dead-end: wordmark + motto on the left, the contact
   prompt + studio email on the right, the copyright alone on the final
   baseline. The footer nav was removed (t-077) — the header carries Contact
   and the wordmark is the way home, so the echo column read as clutter. Two
   blocks on wide screens, a clean stack on a phone; same movement/on-dark/
   accent tokens, so it composes in both themes at 1440 and 375. */
.site-footer {
  flex-shrink: 0;
  background: var(--movement);
  color: var(--on-dark-soft);
  border-top: 1px solid var(--rule-dark);
}

.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2rem 3rem;
  padding-block: clamp(2.75rem, 5vw, 4rem);
  font-size: 0.9rem;
}

/* Left block: wordmark stacked over the motto. */
.site-footer .foot-lead {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.site-footer .foot-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  border: 0;
}

.site-footer .foot-brand .brand-mark {
  width: 1.35rem;
  height: 1.35rem;
  color: var(--accent);
}

.site-footer .foot-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.05rem;
  color: var(--on-dark);
  letter-spacing: 0.02em;
}

.site-footer .foot-motto {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--on-dark-faint);
}

.site-footer .foot-motto .detail-motto {
  font-family: var(--font-display);
  color: var(--accent-hi);
  margin-right: 0.5rem;
}

/* Closing contact path: the prompt keeps a visitor who reached the bottom from
   dead-ending now that the pilot CTA is gone; the email is the direct affordance. */
.site-footer .foot-contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  max-width: 24ch;
}

.site-footer .foot-prompt {
  margin: 0;
  color: var(--on-dark-soft);
  text-wrap: balance;
}

.site-footer .foot-link {
  font-size: 1rem;
}

/* Links (nav + email) share the footer's quiet brass underline-on-hover. */
.site-footer a:not(.foot-brand) {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
  color: var(--accent-hi);
  border-bottom: 0;
  text-decoration-line: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color var(--t-1) var(--ease-mech), color var(--t-1) var(--ease-mech);
}

.site-footer a:not(.foot-brand):hover,
.site-footer a:not(.foot-brand):focus-visible {
  text-decoration-color: var(--accent-hi);
}

/* Copyright sits alone on the final baseline, faint, spanning the row above a
   hairline rule. */
.site-footer .foot-note {
  flex-basis: 100%;
  order: 1;
  margin: 0;
  padding-top: 1rem;
  border-top: 1px solid var(--rule-dark);
  color: var(--on-dark-faint);
  font-size: 0.78rem;
}

@media (max-width: 39.999rem) {
  /* On a phone the three blocks stack into one clean column (space-between has
     nothing to spread), and the copyright rule spans the full width above it. */
  .site-footer .wrap {
    flex-direction: column;
    gap: 1.75rem;
  }
  .site-footer .foot-note {
    align-self: stretch;
    padding-top: 1.25rem;
  }
  .site-footer a:not(.foot-brand) { min-height: 2.75rem; }
}

/* --- Interior page intro ------------------------------------------------ */

.page-intro h1 {
  font-size: var(--step-4);
  line-height: 1.04;
  margin-bottom: 1.25rem;
  max-width: 18ch;
}

.page-intro .lede {
  color: var(--body-soft);
  max-width: 38ch;
}

/* Interior page-intro density (t-070). At mobile the intro flows as a plain
   stack and the decorative index is hidden — nothing is absolutely positioned,
   so nothing can overlap the heading at narrow widths. At desktop the heading
   column is balanced by a quiet right-side index scale: the watch-face tick
   vocabulary that already carries the .eyebrow and the hero. The scale is
   aria-hidden and purely decorative (no JS, no motion) — it carries nothing a
   reader would miss — and the band's vertical padding is tightened so the
   heading is not marooned at the top of an empty section. */
.page-index {
  display: none;
}

@media (min-width: 60rem) {
  .page-intro {
    display: grid;
    grid-template-columns: minmax(0, 42ch) 1fr;
    grid-template-areas:
      "eyebrow index"
      "title   index"
      "lede    index";
    align-items: start;
    column-gap: clamp(2rem, 6vw, 5rem);
    padding-block: clamp(3.25rem, 6vw, 5.5rem);
  }

  .page-intro > .eyebrow { grid-area: eyebrow; }
  .page-intro > h1       { grid-area: title; }
  .page-intro > .lede    { grid-area: lede; }

  .page-index {
    grid-area: index;
    justify-self: end;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1.1rem;
    width: 100%;
    max-width: 15rem;
    min-height: 11rem;
  }

  /* A running-head folio, mirroring the eyebrow across the band. */
  .page-index-tag {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.26em;
    text-transform: uppercase;
    color: var(--label);
  }

  /* Watch-face index scale: a brass hairline spine with evenly spaced ticks,
     drawn entirely from the palette tokens — no asset, no request, no motion. */
  .page-index-scale {
    flex: 1 1 auto;
    width: 100%;
    min-height: 9rem;
    border-right: 1px solid var(--rule-brass);
    background-image: linear-gradient(to bottom, var(--rule) 1px, transparent 1px);
    background-size: 1.4rem 2.4rem;
    background-position: right top;
    background-repeat: repeat-y;
  }

  /* On load, the index draws itself in — a clip-path wipe down the spine so the
     brass hairline and its ticks appear top-to-bottom, like a hand sweeping
     down the scale, settling just after the heading has landed. Desktop only
     (the scale is hidden below 60rem), gated on html.js-enter, and purely
     decorative (the group is aria-hidden) — a no-JS visitor gets it static.
     Reduced motion is handled by the global reset, which lands it visible. */
  @media (prefers-reduced-motion: no-preference) {
    html.js-enter .page-index {
      animation: index-scale-draw var(--t-wind) var(--ease-settle) calc(var(--beat) * 2) both;
    }
  }
}

@keyframes index-scale-draw {
  from { clip-path: inset(0 0 100% 0); opacity: 0; }
  to   { clip-path: inset(0 0 0 0);   opacity: 1; }
}

/* --- Journal ------------------------------------------------------------ */

.journal-list {
  padding-block-start: clamp(1rem, 3vw, 2rem);
}

.piece-provenance {
  margin-top: clamp(2rem, 5vw, 3rem);
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
  font-size: 0.85rem;
  color: var(--label);
}

.piece-index {
  list-style: none;
  margin: 0;
  padding: 0;
}

.piece-entry {
  position: relative;
  border-top: 1px solid var(--rule);
  padding-block: clamp(1.5rem, 4vw, 2rem);
}

.piece-entry::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 2.5rem;
  height: 2px;
  background: var(--accent);
}

.piece-meta {
  margin: 0 0 0.35rem;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.piece-title {
  margin: 0 0 0.5rem;
  font-size: var(--step-2);
  line-height: 1.2;
}

.piece-title a {
  border-bottom: 0;
}

.piece-title a:hover {
  color: var(--accent);
}

.piece-summary {
  margin: 0;
  max-width: 62ch;
  color: var(--label);
}

.piece h1 {
  font-size: var(--step-4);
  line-height: 1.08;
}

.piece h2 {
  font-size: var(--step-2);
  margin-top: 2.5rem;
}

.piece-header {
  margin-bottom: 2.5rem;
}

.piece-byline {
  font-size: 0.85rem;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}

.piece code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.86em;
  padding: 0.1em 0.35em;
  background: var(--surface);
  border-radius: 3px;
}

.piece ul {
  list-style: none;
  margin: 1rem 0;
  padding: 0;
}

.piece ul li {
  padding-left: 1.4rem;
  position: relative;
  margin-bottom: 0.6rem;
}

.piece ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 0.8rem;
  height: 1px;
  background: var(--accent);
}

/* --- Numbered method (prose, not cards) --------------------------------- */

.method {
  list-style: none;
  counter-reset: step;
  margin: 2.5rem 0 0;
  padding: 0;
  max-width: var(--measure);
  display: grid;
  gap: clamp(1.75rem, 4vw, 2.5rem);
}

.method > li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 0 1.25rem;
  align-items: baseline;
}

/* The step number reads as a watch index: monospaced tabular, brass. */
.method > li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  line-height: 1.7;
}

.method p {
  margin-bottom: 0;
  color: var(--body-soft);
}

/* --- Delivery ledger table ---------------------------------------------- */

.ledger-wrap {
  overflow-x: auto;
  margin-top: 2.5rem;
}

.ledger {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  text-align: left;
  font-variant-numeric: tabular-nums;
}

.ledger th:nth-child(1) { width: 20%; }
.ledger th:nth-child(2) { width: 24%; }
.ledger th:nth-child(3) { width: 19%; }
.ledger th:nth-child(4) { width: 16%; }
.ledger th:nth-child(5) { width: 21%; }

.ledger caption {
  text-align: left;
  color: var(--label);
  margin-bottom: 1.5rem;
  max-width: 58ch;
}

.ledger th {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--label);
  border-bottom: 1px solid var(--rule-brass);
  padding: 0 1.25rem 0.75rem 0;
  vertical-align: bottom;
  white-space: nowrap;
}

.ledger td {
  padding: 1.75rem 1.25rem 1.75rem 0;
  border-bottom: 1px solid var(--rule);
  color: var(--body-soft);
}

.ledger .ledger-empty {
  font-style: italic;
  max-width: 60ch;
  color: var(--label);
}

@media (max-width: 39.999rem) {
  .ledger {
    table-layout: auto;
  }
  .ledger th {
    white-space: normal;
  }
  .ledger:has(.ledger-empty) thead {
    display: none;
  }
}

/* --- Assay client view (a structural artifact, not an app mock) --------- */

.proof-run-view-section h2 {
  font-size: var(--step-3);
  margin-bottom: 0.9rem;
  max-width: 18ch;
}

.proof-run-view-section .view-intro {
  color: var(--label);
  max-width: 54ch;
  margin-bottom: 0;
}

.proof-run-view {
  position: relative;
  margin: clamp(2.75rem, 6vw, 4rem) 0 0;
  padding: clamp(1.75rem, 4vw, 2.75rem);
  background: var(--movement);
  color: var(--on-dark);
}

/* The index rule crosses the ground -> movement boundary at the panel's top,
   binding the intro to the artifact. */
.proof-run-view-joint {
  position: absolute;
  top: 0;
  left: clamp(1.75rem, 4vw, 2.75rem);
  right: clamp(1.75rem, 4vw, 2.75rem);
  width: auto;
  height: auto;
  max-height: 1.6rem;
  margin: 0;
  transform: translateY(-52%);
}

.proof-run-view-grid {
  display: grid;
  gap: 1px;
  background: var(--rule-dark);
  border: 1px solid var(--rule-dark);
}

@media (min-width: 40rem) {
  .proof-run-view-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.view-panel {
  background: var(--movement);
  padding: 1.3rem 1.4rem;
}

.view-panel .panel-label {
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-hi);
  margin: 0 0 0.95rem;
}

.view-panel .panel-states {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
}

.view-panel .panel-states li {
  position: relative;
  padding-left: 1.2rem;
  max-width: 34ch;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--on-dark-soft);
}

.view-panel .panel-states li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 0.6rem;
  height: 1px;
  background: var(--accent);
}

.proof-run-view-caption {
  margin-top: 1.5rem;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--on-dark-faint);
  font-variant-numeric: tabular-nums;
}

.proof-run-view-caption a {
  color: var(--accent-hi);
  border-bottom-color: transparent;
}

.proof-run-view-caption a:hover,
.proof-run-view-caption a:focus-visible {
  border-bottom-color: var(--accent-hi);
}

/* --- FAQ (definition list, no disclosure gimmicks) ---------------------- */

.faq h2 {
  font-size: var(--step-3);
  max-width: 20ch;
}

.faq-joint {
  display: block;
  width: clamp(14rem, 48vw, 32rem);
  height: auto;
  max-height: 1.8rem;
  margin: 1.5rem 0 0;
}

.faq-list {
  margin: 0.75rem 0 0;
  border-top: 1px solid var(--rule);
}

.faq-item {
  padding-block: 1.75rem;
  border-bottom: 1px solid var(--rule);
}

@media (min-width: 44rem) {
  .faq-item {
    display: grid;
    grid-template-columns: minmax(0, 19rem) minmax(0, 1fr);
    gap: 0 clamp(1.5rem, 4vw, 3rem);
    align-items: baseline;
  }
}

.faq-item dt {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--step-1);
  color: var(--fg);
  margin: 0 0 0.6rem;
}

@media (min-width: 44rem) {
  .faq-item dt {
    margin-bottom: 0;
  }
}

.faq-item dd {
  margin: 0;
  max-width: 54ch;
  color: var(--body-soft);
}

/* --- Entrance: the lead heading winds up (t-023) ------------------------ */

/* One mechanical "comes alive" moment per interior page: the lead eyebrow's
   brass index tick draws to its mark, then the heading (and its lede/byline)
   settle up into place — like a hand sweeping to the hour and stopping dead.
   Mechanical ease (var(--ease-mech), no overshoot — the brand ticks, it does not
   boing); transform/opacity only. index keeps the hero movement as its one
   moment and has no .page-intro, so it is untouched here. The lead CTA sits
   lower on every page, so its first render is never gated on this animation.

   Double-gated exactly like the scroll reveals: the hidden start state exists
   ONLY under html.js-enter (set by tick.js) AND no-preference, so a no-JS or
   reduced-motion visitor gets the fully-visible static heading with nothing
   hidden that JS cannot restore. */
@media (prefers-reduced-motion: no-preference) {
  html.js-enter :is(.page-intro, .piece-header) > .eyebrow::before {
    transform-origin: left center;
    animation: enter-tick var(--t-4) var(--ease-settle) both;
  }

  html.js-enter :is(.page-intro, .piece-header) h1 {
    animation: enter-settle var(--t-5) var(--ease-settle) calc(var(--beat) * 0.5) both;
  }

  html.js-enter .page-intro > .lede,
  html.js-enter .piece-header > .piece-byline {
    animation: enter-settle var(--t-5) var(--ease-settle) var(--beat) both;
  }
}

@keyframes enter-tick {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes enter-settle {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* --- Motion: scroll reveals -------------------------------------------- */

/* Double-gated: content is fully visible by default; the hidden state exists
   ONLY inside both guards, so any browser without view() timelines (or with
   reduced motion) renders the complete static page with nothing hidden. */
/* One vocabulary at every scale: a brass index tick draws to its mark, then the
   content settles up into place behind it — the same "hand sweeping to the
   hour, stopping dead" the interior headings do on load, now scroll-linked.
   Each drawable rides its OWN view() timeline, so section eyebrows and the
   hairline ticks under each pillar/card index in one at a time as they scroll
   past, a natural stagger with no JS and no per-element delays. Double-gated
   exactly like before: the hidden start state exists ONLY inside no-preference
   + supports(view()), so any browser without scroll timelines (or with reduced
   motion) renders the complete static page with nothing hidden. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal {
      animation: reveal-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 28%;
    }

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

    /* The section's own brass tick draws first (a tight early range), so the
       mark is set before the copy behind it finishes settling. */
    .reveal .eyebrow::before {
      transform-origin: left center;
      animation: index-draw linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }

    /* The hairline index tick under each pillar / card / journal entry draws on
       its own entry, so a grid of them indexes in as you scroll, not all at once. */
    .reveal :is(.pillar, .demo-card, .piece-entry)::before {
      transform-origin: left center;
      animation: index-draw linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 45%;
    }
  }
}

@keyframes index-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

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

/* --- Progressive enhancement (JS reveal fallback) ----------------------- */

/* Fallback for browsers without scroll-driven animations. Only under
   html.js-reveal (set by reveal.js exactly when it commits to observing) and
   only where view() is unsupported, so the CSS-only path stays primary and the
   no-JS / reduced-motion render is fully visible. */
@media (prefers-reduced-motion: no-preference) {
  @supports not (animation-timeline: view()) {
    html.js-reveal .reveal {
      opacity: 0;
      transform: translateY(14px);
      transition: opacity var(--t-5) var(--ease-settle), transform var(--t-5) var(--ease-settle);
    }

    html.js-reveal .reveal.revealed {
      opacity: 1;
      transform: none;
    }

    /* Same index-tick vocabulary in the fallback: the marks draw when the
       section reveals (observed together here, not per-element — the scroll
       stagger above is the primary path). */
    html.js-reveal .reveal .eyebrow::before,
    html.js-reveal .reveal :is(.pillar, .demo-card, .piece-entry)::before {
      transform-origin: left center;
      transform: scaleX(0);
      transition: transform var(--t-4) var(--ease-settle);
    }

    html.js-reveal .reveal.revealed .eyebrow::before,
    html.js-reveal .reveal.revealed :is(.pillar, .demo-card, .piece-entry)::before {
      transform: scaleX(1);
    }
  }
}

/* --- Cross-document view transitions ------------------------------------ */

@view-transition {
  navigation: auto;
}

.brand-name {
  view-transition-name: wordmark;
}

main {
  view-transition-name: page-main;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* --- Print -------------------------------------------------------------- */

@media print {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    background: transparent !important;
    color: var(--ink) !important;
    box-shadow: none !important;
  }

  .skip-link,
  .site-nav,
  .hero-art,
  .faq-joint,
  .proof-run-view-joint {
    display: none !important;
  }

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

  main a[href]:not(.button):not(.hero-cta)::after {
    content: " (" attr(href) ")";
    font-size: 0.82em;
    word-break: break-word;
  }
}
