/* ================================================================
   LONETECS — "BONE & INK"

   Rebuilt to stop reading as a generated template. The previous
   revision had every tell: violet-to-cyan gradient headlines on
   near-black, six accent colours, and every single element rendered
   as the same rounded glass card. This one takes the opposite
   position on all three.

   THE RULES, and they are rules:
     1. ONE accent. Signal orange. If something needs emphasis and
        orange is already doing a job nearby, use scale or weight —
        do not reach for a second hue.
     2. NO gradient text, NO glassmorphism, NO glow, NO blur.
     3. Structure comes from RULES and WHITESPACE, not from boxes.
        A hairline and 80px of air beats a bordered card.
     4. Radii stay tiny (0-4px). Big soft corners are the fastest
        way back to looking generated.
     5. There is no photography, by choice — so typography carries
        the entire page. Scale jumps must be large and deliberate.

   Contrast is measured, not guessed. Ratios are on --paper.
   ================================================================ */

:root {
  --font-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  /* Named --font-sans, NOT --font-body: the chatbot shim below has to
     expose a property literally called --font-body, and a custom
     property that references itself is "guaranteed-invalid" — every
     var() using it silently falls back to Times New Roman. */
  --font-sans:    'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --font-accent:  'Instrument Serif', Georgia, serif;
  --font-mono:    'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ---- Paper ---- */
  --paper:   #FAF8F5;
  --paper-2: #F2EFE8;   /* alternating band */
  --paper-3: #E9E5DC;   /* wells, input fills */

  /* ---- Ink. Ratios measured on --paper. ---- */
  --ink:   #0B0B0B;   /* 18.8:1  display + headings */
  --ink-2: #3A3A38;   /* 10.4:1  body copy */
  --ink-3: #6B6B66;   /*  5.3:1  secondary, labels */
  --ink-4: #8A8A83;   /*  3.6:1  FLOOR — rules and decoration ONLY,
                                 never text */

  /* ---- Rules. Structure lives here instead of in card borders. ---- */
  --rule:        rgba(11, 11, 11, 0.13);
  --rule-strong: rgba(11, 11, 11, 0.26);
  --rule-ink:    rgba(250, 248, 245, 0.16);   /* on dark bands */

  /* ---- The one accent ----
     --signal is bright and reads at display size (3.4:1 — large
     text and graphics only). --signal-deep is the text-safe form
     (4.8:1 on paper, and 5.1:1 white-on-orange for buttons). Use
     --signal-deep for anything under 24px. */
  --signal:      #E8541F;
  --signal-deep: #C2410C;
  --signal-wash: rgba(232, 84, 31, 0.09);

  /* ---- Dark bands. The page inverts twice, for contrast. ---- */
  --band:    #111110;
  --band-2:  #1C1C1A;

  /* ---- 4px base ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;
  --sp-12: 48px; --sp-16: 64px; --sp-20: 80px; --sp-24: 96px;

  /* Fluid, not fixed. At a flat 1240px a 1920px screen threw away 35%
     of its width as empty margin (340px a side). This keeps a sane
     gutter on large displays without letting the page sprawl — line
     length is governed separately by max-width on the text itself. */
  --container: min(1440px, 92vw);
  /* Was clamp(88px, 9vw, 152px), applied top AND bottom: ~300px between
     consecutive sections, measured as twelve empty bands of 240–360px
     that made up 38% of the page at 1920.
     The gap applies on BOTH sides of a section boundary, so the visible
     space between two sections is twice this plus any trailing padding
     inside the last element — measured at ~36px. 64px keeps the total
     under a 160px ceiling, and the ground change between paper and band
     does the separating that raw height used to. */
  --section-gap: clamp(44px, 4vw, 64px);

  /* Deliberately tiny. See rule 4. */
  --r-sm: 2px;
  --r-md: 3px;
  --r-lg: 4px;

  --t: 160ms cubic-bezier(.22, 1, .36, 1);
  --t-slow: 460ms cubic-bezier(.22, 1, .36, 1);
  --ease-out: cubic-bezier(.22, 1, .36, 1);

  /* ================================================================
     CHATBOT COMPATIBILITY SHIM — do not delete.
     chatbot.css has no :root and reads these 14 properties from
     here. They are literal values, not var() aliases, so they must
     be retuned by hand. Retuned for a LIGHT page: the widget was
     previously dark-on-dark and would have floated as a black slab
     on cream.
     ================================================================ */
  --font-primary: var(--font-display);
  --font-body: var(--font-sans);
  --clr-bg: #FAF8F5;
  --clr-bg-3: #E9E5DC;
  --clr-surface: #FFFFFF;
  --clr-text: #0B0B0B;
  --clr-text-3: #6B6B66;
  --clr-text-muted: #8A8A83;
  --clr-primary: #C2410C;
  --clr-primary-light: #E8541F;
  --clr-accent: #C2410C;
  /* chatbot.css referenced these two with hard-coded #FF4757/#FF6B81
     fallbacks — the retired red palette, which is a second accent the
     system does not have. Defined here so the chat input's error state
     matches the page's own form, which uses --signal-deep. */
  --clr-error: #C2410C;
  --clr-error-light: #E8541F;
  --radius: 4px;
  --transition: all 160ms cubic-bezier(.22, 1, .36, 1);
  --transition-fast: all 160ms cubic-bezier(.22, 1, .36, 1);
}

/* ================================================================
   RESET & BASE
   ================================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  font-size: 16px;
  scroll-behavior: smooth;
  scroll-padding-top: 110px;   /* clears the 88px nav */
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-sans);
  font-size: 16.5px;
  line-height: 1.62;
  color: var(--ink-2);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
body.menu-open { overflow: hidden; }

a { color: inherit; text-decoration: none; }
ul { list-style: none; }
img { max-width: 100%; height: auto; display: block; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
::selection { background: var(--signal); color: #fff; }

:focus-visible {
  outline: 2px solid var(--signal-deep);
  outline-offset: 3px;
}

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--sp-8);
}
.section { padding: var(--section-gap) 0; position: relative; }

/* Two plain sections in a row stack bottom-gap onto top-gap — ~290px
   of nothing at 1600px, which under a thin ruled band (the stats
   table) reads as a hole rather than as air. Sections that CHANGE
   GROUND keep the full gap: there the space is doing a job, separating
   two different colours. */
.section:not(.section--band):not(.section--ink)
  + .section:not(.section--band):not(.section--ink) {
  padding-top: calc(var(--section-gap) * .55);
}

/* Alternating band. Section rhythm comes from ground changes, not
   from wrapping everything in a card. */
.section--band { background: var(--paper-2); }
.section--ink {
  background: var(--band);
  color: rgba(250, 248, 245, .82);
}

.icon {
  width: 20px; height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

/* The bloom layer from the dark revision is gone. Kept as a no-op
   so the markup can stay put without leaving a stray visible box. */
.bloom { display: none; }

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

   Three registers, each with a job:
     Archivo         — display and body. Tight, heavy, Swiss.
     Instrument Serif — italic accents ONLY. The jump from an 800
                        grotesk to a light serif italic is the
                        single most editorial move on the page.
     Geist Mono      — data, labels, numbering.
   ================================================================ */
.display {
  font-family: var(--font-display);
  font-size: clamp(3rem, 8.2vw, 7rem);
  font-weight: 800;
  line-height: .94;
  letter-spacing: -0.045em;
  color: var(--ink);
  text-wrap: balance;
}
.display em,
h2.heading em {
  font-family: var(--font-accent);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--signal-deep);
}

h2.heading {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 4.6vw, 3.75rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.038em;
  color: var(--ink);
  text-wrap: balance;
}
.section--ink h2.heading,
.section--ink .display { color: var(--paper); }
.section--ink .display em,
.section--ink h2.heading em { color: var(--signal); }

h3 {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--ink);
}
h4 {
  font-family: var(--font-display);
  font-size: .9375rem;
  font-weight: 700;
  letter-spacing: -0.012em;
  color: var(--ink);
}

/* Numbered eyebrow — "01 / SERVICES". The slash-numbering reads as
   a considered index rather than a decorative label. */
.eyebrow {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--ink-3);
  margin-bottom: var(--sp-6);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--rule);
}
.section--ink .eyebrow { color: rgba(250,248,245,.6); border-bottom-color: var(--rule-ink); }

.lede {
  font-size: 1.125rem;
  color: var(--ink-3);
  /* Archivo runs ~0.62em per character, so 52ch of max-width yields
     well under 52 rendered characters. Set from the measured value. */
  max-width: 60ch;
  line-height: 1.66;
  text-wrap: pretty;
}
.section--ink .lede { color: rgba(250,248,245,.66); }

/* Section head.
   ALIGNMENT IS THE WHOLE POINT OF THIS LAYOUT. Structure comes from
   rules and edges, so every heading must share the left edge of the
   content it introduces.
   The earlier version put the label in a 200px column and pushed the
   headline into column two, landing it 264px inboard while its own
   service rows, metrics and quotes stayed flush — and three other
   sections were flush anyway, so it read as a mistake rather than a
   system. Label now sits ABOVE, spanning; headline is flush; the lede
   takes the right column. */
/* Split head: eyebrow + headline left (7/12), lede right (5/12),
   bottom-aligned.
   A right-hand lede was tried before and dropped because it fell to
   ~32 characters a line — but that was inside a narrower container.
   At the 1440px container the 5/12 column is ~520px, ~46 characters of
   18px Archivo, which is a comfortable measure. Stacked, every section
   head left its right half empty and cost ~150px of height.
   Below 1024px responsive.css stacks it again. */
.section-head {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--sp-5) var(--sp-16);
  align-items: end;
  margin-bottom: var(--sp-12);
}
.section-head .eyebrow { margin-bottom: var(--sp-5); }
.section-head-side .lede { max-width: 52ch; }
h2.heading + .lede { margin-top: var(--sp-6); }

/* ================================================================
   BUTTONS
   ================================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: 15px var(--sp-8);
  border-radius: var(--r-sm);
  font-family: var(--font-display);
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  position: relative;
  transition: background var(--t), color var(--t), border-color var(--t),
              transform 380ms var(--ease-out);
}
.btn.is-magnetic { transition: background var(--t), color var(--t), border-color var(--t); }
.btn .icon { width: 16px; height: 16px; stroke-width: 2; transition: transform var(--t); }
.btn:hover .icon { transform: translateX(4px); }

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

.btn-secondary {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule-strong);
}
.btn-secondary:hover { border-color: var(--ink); background: rgba(11,11,11,.04); }

.section--ink .btn-secondary { color: var(--paper); border-color: var(--rule-ink); }
.section--ink .btn-secondary:hover { border-color: var(--paper); background: rgba(250,248,245,.08); }

/* ================================================================
   REVEAL BASE STATES
   Scoped to .js so a script failure still shows everything.
   The class main.js adds is `.is-in` — renaming one without
   the other leaves every reveal permanently invisible.
   ================================================================ */
.js .reveal,
.js .reveal-left,
.js .reveal-right,
.js .reveal-scale {
  opacity: 0;
  transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out);
}
.js .reveal       { transform: translateY(22px); }
.js .reveal-left  { transform: translateX(-24px); }
.js .reveal-right { transform: translateX(24px); }
.js .reveal-scale { transform: none; }

.js .reveal.is-in,
.js .reveal-left.is-in,
.js .reveal-right.is-in,
.js .reveal-scale.is-in { opacity: 1; transform: none; }

/* ================================================================
   NAVIGATION
   ================================================================ */
.nav {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 88px;
  z-index: 1000;
  display: flex;
  align-items: center;
  /* Defined from the first paint. It used to start at .82 with no
     border, which over the same cream as the hero read as a faded bar
     until the scrolled state gave it an edge. */
  background: rgba(250, 248, 245, .95);
  /* NO backdrop-filter. A non-none backdrop-filter makes this element the
     containing block for its fixed-position descendants, which sized the
     mobile menu overlay against the 88px nav instead of the viewport —
     only the first link was visible. The background is .95 opaque, so the
     blur was doing nothing anyway, and the design system bans blur. */
  border-bottom: 1px solid var(--rule);
  transition: background var(--t), border-color var(--t), height var(--t), box-shadow var(--t);
}
.nav.scrolled {
  height: 68px;
  box-shadow: 0 1px 20px rgba(11, 11, 11, .05);
}
.nav-inner { display: flex; align-items: center; justify-content: space-between; width: 100%; }

.nav-logo { display: flex; align-items: center; padding: 4px 0; }
/* Was 28px, which left the mark small and washed out against cream.
   The slight saturate/contrast lifts the sage and gold without
   recolouring them — it is the same artwork, just not muted. */
.nav-logo-img {
  height: 44px;
  width: auto;
  filter: saturate(1.18) contrast(1.06);
  transition: height var(--t), transform var(--t);
}
.nav.scrolled .nav-logo-img { height: 36px; }
.nav-logo:hover .nav-logo-img { transform: translateY(-1px); }
.footer-logo-img { height: 40px; }

.nav-links { display: flex; align-items: center; gap: var(--sp-10); }
.nav-links a {
  font-size: clamp(15.5px, .9vw, 17px);
  font-weight: 500;
  color: var(--ink);
  position: relative;
  padding: 4px 0;
  transition: color var(--t);
}
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 0; height: 2px;
  background: var(--signal);
  border-radius: 2px;
  transition: width var(--t-slow);
}
.nav-links a:hover, .nav-links a.is-active { color: var(--ink); }
.nav-links a:hover::after, .nav-links a.is-active::after { width: 100%; }
/* No index numbers on nav links — removed along with the numbered
   section eyebrows. */

/* Compound selector on purpose. `.nav-links a` is (0,1,1) and this
   button lives inside it, so a bare `.nav-cta` at (0,1,0) LOST the
   cascade: the button inherited `padding: 4px 0` and rendered with no
   horizontal padding at all — the orange box hugging the text — and
   silently dropped back to font-weight 500. */
.nav-links a.nav-cta {
  padding: 12px 26px;
  border-radius: var(--r-sm);
  background: var(--signal-deep);
  color: #fff;
  font-weight: 600;
  transition: background var(--t);
}
.nav-links a.nav-cta::after { display: none; }
.nav-links a.nav-cta:hover { background: var(--ink); color: #fff; }

/* 44px box: the bars are 22px, but the touch target must not be. */
.nav-toggle {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  margin-right: -8px;          /* keeps the bars optically aligned to the edge */
}
.nav-toggle span {
  width: 22px; height: 1.5px;
  background: var(--ink);
  transition: transform var(--t), opacity var(--t);
}
.nav-toggle.active span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav-toggle.active span:nth-child(2) { opacity: 0; }
.nav-toggle.active span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

.scroll-progress {
  position: absolute;
  left: 0; bottom: -1px;
  height: 2px; width: 0;
  background: var(--signal);
  opacity: 0;
  transition: opacity var(--t);
  pointer-events: none;
}
.nav.scrolled .scroll-progress { opacity: 1; }

/* ================================================================
   SHARED SMALL PARTS — chip, text link, stats
   ================================================================ */
.chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-2);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm);
  padding: 4px 8px;
  white-space: nowrap;
}
.chip--signal {
  color: var(--signal-deep);
  border-color: rgba(194, 65, 12, .38);
  background: var(--signal-wash);
}

.text-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
  font-size: 15px;
  font-weight: 600;
  color: var(--signal-deep);
  border-bottom: 1px solid currentColor;
  /* Top padding is touch target; the underline needs the bottom tight. */
  padding: 8px 0 2px;
}
.text-link .icon { width: 15px; height: 15px; stroke-width: 2; transition: transform var(--t); }
.text-link:hover .icon { transform: translateX(3px); }

.stat-number {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 3.8vw, 3.5rem);
  font-weight: 800;
  line-height: .92;
  letter-spacing: -0.05em;
  color: var(--ink);
  margin-bottom: var(--sp-3);
}
.stat-label {
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-3);
}

ol.how-steps, ol.start-steps { list-style: none; }

/* ================================================================
   HERO
   Copy left, robot stage right, both CENTRED on the row.

   The previous grid used `align-items: end` against a much taller
   right column, which pushed ~200px of slack above a three-line
   headline — that plus 196px of top padding and the badge row was
   the empty band at the top of the page.
   ================================================================ */
.hero {
  position: relative;
  padding: calc(88px + clamp(20px, 2.5vw, 40px)) 0 clamp(32px, 3vw, 48px);
  overflow: hidden;
}
.hero-inner {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--sp-12);
  align-items: center;
  /* Fills the fold on a normal laptop, but capped so a tall monitor
     does not pad the copy with slack above and below it. */
  min-height: min(calc(100svh - 170px), 640px);
}
.hero-copy { position: relative; z-index: 2; }

/* Sized to the COLUMN, not the viewport maximum. "eCommerce sites" is
   the longest line (~8.1em); at 5.2vw it fits the 7/12 column from
   1280px up to the 92px cap. max-width 15ch is what forces the break
   after "We build" instead of balancing the lines unevenly. */
.hero .display {
  font-size: clamp(3rem, 5.2vw, 5.75rem);
  max-width: 15ch;
  margin-bottom: var(--sp-8);
}
.hero-sub {
  font-size: clamp(1.0625rem, 1.3vw, 1.25rem);
  color: var(--ink-2);
  max-width: 50ch;
  line-height: 1.6;
  text-wrap: pretty;
}
.hero-buttons { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-8); }

.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-8);
  margin-top: var(--sp-10);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--rule);
}
.hero-trust li {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-2);
}
.hero-trust .icon { width: 14px; height: 14px; stroke-width: 2.4; color: var(--signal-deep); }

/* ---- Robot stage ----
   --stage-robot is the size of the #cb-robot box while staged.
   --plinth-y is where the robot's feet land, measured from the bottom.
   The model fills ~73.5% of its box, centred, so its feet sit 13.25%
   up from the box's bottom edge — hence the offset on .stage-slot. */
.hero-stage {
  --stage-robot: clamp(320px, 30vw, 460px);
  --plinth-y: 76px;
  position: relative;
  align-self: stretch;
  min-height: clamp(460px, 40vw, 580px);
}
/* Drafting-plate dot grid behind the robot, masked to a soft ellipse —
   ties the hero visual to the Fig. 01 schematic further down. */
.hero-stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(var(--rule-strong) 1px, transparent 1.3px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(ellipse 58% 52% at 50% 58%, #000 25%, transparent 74%);
  mask-image: radial-gradient(ellipse 58% 52% at 50% 58%, #000 25%, transparent 74%);
  pointer-events: none;
}
.stage-slot {
  position: absolute;
  left: 50%;
  bottom: calc(var(--plinth-y) - var(--stage-robot) * .1325);
  width: var(--stage-robot);
  height: var(--stage-robot);
  transform: translateX(-50%);
  z-index: 2;
}
.stage-plinth {
  position: absolute;
  left: 50%;
  bottom: var(--plinth-y);
  width: calc(var(--stage-robot) * 1.02);
  aspect-ratio: 4 / 1;
  transform: translate(-50%, 50%);
  z-index: 1;
}
.plinth-ring { position: absolute; border-radius: 50%; }
.plinth-ring--outer { inset: 0; border: 1px solid var(--rule-strong); }
.plinth-ring--inner {
  inset: 20% 16%;
  border: 1px solid rgba(232, 84, 31, .42);
  background: radial-gradient(ellipse at center, rgba(232, 84, 31, .07), transparent 70%);
}
.plinth-ticks {
  position: absolute;
  inset: -16% -8%;
  border-radius: 50%;
  border: 1px dashed var(--rule-strong);
  opacity: .7;
}
.stage-caption {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 9px 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm);
  white-space: nowrap;
  transition: border-color var(--t), color var(--t);
}
.stage-caption:hover { border-color: var(--ink); color: var(--ink); }
/* Long label by default; responsive.css swaps to .cap-short on phones. */
.stage-caption .cap-short { display: none; }
/* On phones the robot's speech label occupies this same slot, so the
   caption steps aside while a line is showing. chatbot.js sets
   .is-speaking on #heroStage; above 768px both can coexist. */
@media (max-width: 768px) {
  /* The chip waits for the card to finish fading (.3s) before coming
     back, so the two never share the slot. Measured: without the delay
     both were fully visible for ~200ms at the end of every line. */
  .stage-caption {
    transition: opacity var(--t) .3s, border-color var(--t), color var(--t);
  }
  .hero-stage.is-speaking .stage-caption {
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--t) 0s;
  }
}
.stage-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #1E7A46;
  box-shadow: 0 0 0 3px rgba(30, 122, 70, .16);
}

/* ================================================================
   PROOF BAR — stats and platforms share one ruled band
   ================================================================ */
.proof { border-top: 1px solid var(--rule-strong); border-bottom: 1px solid var(--rule-strong); }
.proof-inner {
  display: grid;
  grid-template-columns: minmax(0, 8fr) minmax(0, 4fr);
}
.proof-stats { display: grid; grid-template-columns: repeat(4, 1fr); }
.proof .stat-cell {
  padding: var(--sp-8) var(--sp-6) var(--sp-8) 0;
  border-right: 1px solid var(--rule);
}
.proof .stat-cell:not(:first-child) { padding-left: var(--sp-6); }
.proof-platforms {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-4);
  padding: var(--sp-8) 0 var(--sp-8) var(--sp-8);
}
.proof-platforms-label {
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-3);
}
/* Three per row, so the six names sit 3 + 3 instead of wrapping 4 + 2.
   responsive.css turns this back into a wrapping row at 1024 and below. */
.proof-platforms-list {
  display: grid;
  grid-template-columns: repeat(3, max-content);
  gap: var(--sp-2) var(--sp-6);
}
.proof-platforms-list li {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
  color: var(--ink-2);
}

/* ================================================================
   SELECTED WORK — the evidence
   Cards are the ONE deliberate exception to "rules, not boxes": a
   case study is a discrete artefact, and framing it as one is what
   makes it read as a real piece of work rather than a claim.
   ================================================================ */
.case-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: var(--sp-6);
}
.case {
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--paper);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color var(--t);
}
.case:hover { border-color: var(--ink); }
.case--feature { grid-row: span 2; }
/* The stacked pair go horizontal so together they match the feature
   card's height instead of leaving a gap under the second one. */
.case:not(.case--feature) {
  grid-template-rows: none;
  grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
}

.case-media {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--band);
  color: var(--paper);
  min-height: 380px;
}
.case-media--compact { min-height: 0; }

/* ---- Plate title block ----
   A drafting header, NOT a browser chrome bar: three window dots made
   a chart of real numbers look like a screenshot of an app, which is
   the one thing these plates must not imply. Height stays 40px —
   .case-shot is positioned under it. */
.case-frame-bar {
  flex: none;
  height: 40px;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0 var(--sp-4);
  border-bottom: 1px solid var(--rule-ink);
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: rgba(250, 248, 245, .5);
}
.case-fig {
  color: var(--signal);
  padding-right: var(--sp-3);
  border-right: 1px solid var(--rule-ink);
  align-self: stretch;
  display: flex;
  align-items: center;
}

/* The three figures share the plate's drafting-paper grid. */
.case-chart,
.case-units,
.case-hours {
  flex: 1;
  background-image: radial-gradient(rgba(250, 248, 245, .09) 1px, transparent 1.3px);
  background-size: 18px 18px;
}

/* ---- Fig. 01 — two measured columns and the ratio between them ---- */
.case-chart {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 7%;
  padding: 58px 10% 46px;
}
/* Baseline. Sits exactly on the columns' feet, i.e. the bottom padding. */
.case-chart::after {
  content: '';
  position: absolute;
  left: 7%; right: 7%;
  bottom: 46px;
  height: 1px;
  background: rgba(250, 248, 245, .28);
}
.case-media--compact .case-chart { padding: 44px 12% 34px; min-height: 190px; }
.case-bar {
  position: relative;
  width: 29%;
  height: var(--h, 50%);
  min-height: 26px;
  border: 1px solid rgba(250, 248, 245, .34);
  border-bottom: 0;
}
/* Measured tick where the column meets the baseline. */
.case-bar::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -7px;
  width: 1px;
  height: 7px;
  background: rgba(250, 248, 245, .4);
}
.case-bar.is-after {
  background: rgba(232, 84, 31, .85);
  border-color: var(--signal);
  border-top-width: 2px;
}
.case-bar.is-after::after { background: var(--signal); }
.case-bar b {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1rem, 1.5vw, 1.5rem);
  letter-spacing: -0.03em;
  color: var(--paper);
  white-space: nowrap;
}
.case-bar.is-after b { color: var(--signal); }
.case-bar small {
  position: absolute;
  left: 50%;
  top: calc(100% + 8px);
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: rgba(250, 248, 245, .5);
  white-space: nowrap;
}

/* ---- Fig. 01 dimension line ----
   A stretched flex item, so its own box is exactly as tall as the
   columns' 100%. That is what lets `bottom: 23%` land on the short
   column's top edge: percentages here resolve against the same height
   the columns use. Positioning it absolutely against the padding box
   would measure from a different origin and drift. */
.case-delta {
  position: relative;
  flex: none;
  align-self: stretch;
  width: 34px;
  margin-left: -3%;            /* sits against the column it measures */
}
.case-delta::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 0;
  bottom: 23%;
  border-left: 1px dashed rgba(232, 84, 31, .8);
}
/* Caps at the two column tops it measures between. */
.case-delta::after,
.case-delta span::before {
  content: '';
  position: absolute;
  left: 0;
  width: 15px;
  height: 1px;
  background: var(--signal);
}
.case-delta::after { top: 0; }
.case-delta span::before { bottom: -1px; }
.case-delta span {
  position: absolute;
  left: 13px;
  bottom: 61.5%;               /* midway between the two column tops */
  transform: translateY(50%);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--signal);
  white-space: nowrap;
}

/* ---- Fig. 02 — a multiplier drawn as units ---- */
.case-units,
.case-hours {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-5);
  padding: var(--sp-6) var(--sp-5);
  min-height: 190px;
}
.case-unit-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-2) var(--sp-3);
  align-items: center;
}
.case-unit-label {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: rgba(250, 248, 245, .5);
}
.case-unit-row b {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  color: rgba(250, 248, 245, .8);
}
.case-unit-row.is-after b { color: var(--signal); }
.case-unit-blocks {
  grid-column: 1 / -1;
  grid-row: 2;
  display: flex;
  gap: 8px;
}
.case-unit-blocks i {
  width: 36px;
  height: 36px;
  border: 1px solid rgba(250, 248, 245, .34);
  border-radius: var(--r-sm);
}
.is-after .case-unit-blocks i { background: rgba(232, 84, 31, .85); border-color: var(--signal); }

/* ---- Fig. 03 — one square per hour returned ----
   Twenty squares for "20+ hrs", the last one a plus. Deliberately no
   grid of a full working week behind them: the site states the hours
   saved, not the size of the week they came out of. */
.case-hour-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  width: 100%;
  max-width: 184px;
}
.case-hour-grid i {
  aspect-ratio: 1;
  background: rgba(232, 84, 31, .85);
  border: 1px solid var(--signal);
  border-radius: var(--r-sm);
}
.case-hour-grid i.is-plus {
  display: grid;
  place-items: center;
  background: none;
  border-style: dashed;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 13px;
  line-height: 1;
  color: var(--signal);
}
.case-figure-note {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: .04em;
  /* .45 measured 4.34:1 on the plate — under the 4.5 floor for small text. */
  color: rgba(250, 248, 245, .55);
}

/* A real screenshot, once cleared, covers the chart below the bar. */
.case-shot {
  position: absolute;
  top: 40px; left: 0;
  width: 100%;
  height: calc(100% - 40px);
  object-fit: cover;
  object-position: top center;
}

.case-body { display: flex; flex-direction: column; padding: var(--sp-8); }
/* In the narrow stacked cards the chip drops to its own line rather
   than breaking the client label in two. */
.case-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}
.case-client {
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  white-space: nowrap;
}
.case-metric {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2rem, 3.2vw, 3rem);
  line-height: .95;
  letter-spacing: -0.045em;
  color: var(--ink);
}
.case--feature .case-metric { font-size: clamp(2.5rem, 4.4vw, 4.25rem); color: var(--signal-deep); }
.case-metric-label { font-size: 15px; color: var(--ink-3); margin-top: var(--sp-2); }
/* The feature card is tall, so the bullets get pushed to the bottom. A
   rule and a label turn that distance into structure rather than a gap. */
.case-did-wrap {
  margin-top: auto;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--rule);
}
.case-did-label {
  display: block;
  margin-bottom: var(--sp-4);
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
}
.case-did { display: grid; gap: var(--sp-2); }
.case-did li { position: relative; padding-left: 18px; font-size: 14.5px; color: var(--ink-2); line-height: 1.45; }
.case-did li::before {
  content: '';
  position: absolute;
  left: 0; top: .66em;
  width: 9px; height: 1.5px;
  background: var(--signal-deep);
}

/* ================================================================
   SERVICES — ruled 4×2 grid
   ================================================================ */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--rule-strong);
  border-left: 1px solid var(--rule);
}
.svc {
  display: flex;
  flex-direction: column;
  padding: var(--sp-8) var(--sp-6);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  transition: background var(--t);
}
.svc:hover { background: var(--paper); }
.svc-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-8); }
.svc-tag {
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-3);
}
.svc-tag--hot { color: var(--signal-deep); }
.svc-top .icon { width: 22px; height: 22px; color: var(--ink); }
.svc h3 { font-size: 1.25rem; line-height: 1.15; letter-spacing: -0.025em; margin-bottom: var(--sp-3); }
.svc p { font-size: 14.5px; color: var(--ink-3); line-height: 1.55; }
.svc-caps { display: grid; gap: 6px; margin-top: auto; padding-top: var(--sp-6); }
.svc-caps li { font-family: var(--font-mono); font-size: 12px; color: var(--ink-2); }
.svc-caps li::before { content: '\2014\00a0'; color: var(--signal-deep); }

/* ================================================================
   HOW WE WORK — schematic + steps + facts
   ================================================================ */
.how-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-16);
  align-items: center;
}
.how-copy .lede { margin-top: var(--sp-6); }
.how-steps {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--sp-8);
  margin-top: var(--sp-8);
  border-top: 1px solid var(--rule-strong);
}
.how-step {
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: var(--sp-3);
  padding: var(--sp-6) 0;
  border-bottom: 1px solid var(--rule);
}
.how-num { font-family: var(--font-mono); font-size: 12px; letter-spacing: .1em; color: var(--signal-deep); padding-top: 3px; }
.how-step h3 { font-size: 1.125rem; letter-spacing: -0.02em; margin-bottom: 4px; }
.how-step p { font-size: 14px; color: var(--ink-3); line-height: 1.55; }

.how-facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin-top: var(--sp-12);
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule-strong);
}
.how-facts > div { padding: var(--sp-6) var(--sp-6) var(--sp-6) 0; border-right: 1px solid var(--rule); }
.how-facts > div:not(:first-child) { padding-left: var(--sp-6); }
.how-facts > div:last-child { border-right: 0; }
.how-facts dt {
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.how-facts dd {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  color: var(--ink);
}

/* ================================================================
   CLIENT STORIES
   ================================================================ */
.stories-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-6); }
.story {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: var(--sp-8);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg);
}
.story-stars {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: .2em;
  color: var(--signal-deep);
  margin-bottom: var(--sp-5);
}
.story-text {
  flex: 1;
  margin: 0 0 var(--sp-8);
  font-family: var(--font-accent);
  font-size: clamp(1.125rem, 1.45vw, 1.3125rem);
  line-height: 1.45;
  letter-spacing: -0.008em;
  color: var(--ink);
  text-wrap: pretty;
}
/* Hanging opening quote. */
.story-text::before { content: '\201C'; color: var(--signal); margin-left: -0.42em; }
.story-by {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}
.story-who { font-size: 14px; font-weight: 700; color: var(--ink); }

/* ================================================================
   FAQ
   <details>/<summary>: works without JS, keyboard-accessible, and the
   `name` attribute makes it a one-open-at-a-time accordion where
   supported (and harmlessly independent where not).
   ================================================================ */
.faq-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--sp-16);
  align-items: start;
}
.faq-intro { position: sticky; top: 112px; }
.faq-intro .lede { margin: var(--sp-6) 0 var(--sp-8); }
.faq-list { border-top: 1px solid var(--rule-strong); }
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--sp-6) 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.0625rem, 1.4vw, 1.25rem);
  letter-spacing: -0.02em;
  color: var(--ink);
  transition: color var(--t);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--signal-deep); }
.faq-item summary::after {
  content: '+';
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 18px;
  color: var(--signal-deep);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm);
}
.faq-item[open] summary::after { content: '\2212'; background: var(--signal-wash); }
.faq-item p {
  max-width: 64ch;
  padding: 0 var(--sp-12) var(--sp-6) 0;
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--ink-3);
}

/* ================================================================
   START A PROJECT — ink band, pitch left, form card right
   ================================================================ */
.start-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-16);
  align-items: start;
}
.start-copy .lede { margin-top: var(--sp-6); }
.start-steps { margin: var(--sp-10) 0; border-top: 1px solid var(--rule-ink); }
.start-steps li {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: var(--sp-3);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--rule-ink);
}
.start-steps span { font-family: var(--font-mono); font-size: 12px; letter-spacing: .1em; color: var(--signal); padding-top: 3px; }
.start-steps div { font-size: 14.5px; line-height: 1.5; color: rgba(250, 248, 245, .6); }
.start-steps b {
  display: block;
  margin-bottom: 2px;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--paper);
}
.start-contact { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-8); }
.start-email {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  color: var(--paper);
  border-bottom: 1px solid var(--rule-ink);
  padding: 6px 0 2px;
  transition: color var(--t), border-color var(--t);
}
.start-email:hover { color: var(--signal); border-color: var(--signal); }
.start-email .icon { width: 18px; height: 18px; color: var(--signal); }
.start-note {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: rgba(250, 248, 245, .55);
}
.start-note .icon { width: 14px; height: 14px; }

.contact-social { display: flex; gap: var(--sp-2); margin-top: var(--sp-8); }
.social-link {
  width: 44px; height: 44px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  border: 1px solid var(--rule-strong);
  transition: background var(--t), border-color var(--t);
}
.social-link svg { width: 16px; height: 16px; fill: var(--ink-2); }
.social-link:hover { background: var(--ink); border-color: var(--ink); }
.social-link:hover svg { fill: var(--paper); }
.section--ink .social-link { border-color: var(--rule-ink); }
.section--ink .social-link svg { fill: rgba(250, 248, 245, .75); }
.section--ink .social-link:hover { background: var(--paper); border-color: var(--paper); }
.section--ink .social-link:hover svg { fill: var(--ink); }

/* The form stays a LIGHT card on the ink band: dark inputs on a dark
   ground cost contrast exactly where the visitor is typing. The card
   resets the section's light text colour so nothing inside inherits it. */
.section--ink .contact-form { background: var(--paper); color: var(--ink-2); }

/* ================================================================
   HOW WE WORK - dark panel chrome for the Fig. 01 schematic
   ================================================================ */
.code-panel {
  background: var(--band);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.code-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--rule-ink);
}
.code-dots { display: flex; gap: 6px; }
.code-dots span { width: 9px; height: 9px; border-radius: 50%; background: rgba(250,248,245,.2); }
.code-dots span:first-child { background: var(--signal); }
.code-name { font-family: var(--font-mono); font-size: 11.5px; color: rgba(250,248,245,.5); }
/* The .code-body / .code-ln / syntax-token rules that lived here went
   with the YAML listing this panel used to hold. .code-panel and
   .code-bar are still in use — the schematic reuses both for its dark
   ground and header strip. */

/* ================================================================
   SCHEMATIC — the drafting plate that replaced the code listing

   Reuses .code-panel for the dark ground and .code-bar for the
   header strip; everything below styles the SVG itself. Kept on the
   dark ground on purpose: this panel is the only inverted element
   in a paper band, and it is what stops the section going flat.
   ================================================================ */
.schema-live {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--signal);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.schema-live::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--signal);
  animation: schema-blink 2.4s ease-in-out infinite;
}
@keyframes schema-blink { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

.schema-body { padding: var(--sp-5) var(--sp-4) var(--sp-4); }
.schema { display: block; width: 100%; height: auto; }

.schema-grid-dot { fill: rgba(250, 248, 245, .085); }

.schema-mark path {
  stroke: rgba(250, 248, 245, .3);
  stroke-width: 1;
  fill: none;
}

.schema-wire path {
  stroke: rgba(250, 248, 245, .2);
  stroke-width: 1;
  fill: none;
}

/* Each pulse path carries pathLength="100" in the markup, so the
   dash geometry is expressed in percent-of-path and every wire
   shows exactly one pulse per cycle whatever its real length. */
.schema-pulse path {
  stroke: var(--signal);
  stroke-width: 1.6;
  fill: none;
  stroke-linecap: round;
  stroke-dasharray: 7 93;
  stroke-dashoffset: 100;
  animation: schema-flow 3.6s linear infinite;
}
@keyframes schema-flow { to { stroke-dashoffset: 0; } }

.schema-node rect {
  fill: rgba(250, 248, 245, .028);
  stroke: rgba(250, 248, 245, .19);
  stroke-width: 1;
}

.schema-label text {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: .09em;
  fill: rgba(250, 248, 245, .88);
}
.schema-sub text {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .07em;
  fill: rgba(250, 248, 245, .55);
}

.schema-core {
  fill: rgba(232, 84, 31, .1);
  stroke: var(--signal);
  stroke-width: 1.2;
}
.schema-core-name {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .1em;
  fill: #FAF8F5;
}
.schema-core-rule { stroke: rgba(232, 84, 31, .5); stroke-width: 1; }
.schema-core-sub {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: .16em;
  fill: rgba(250, 248, 245, .55);
}
.schema-core-dots circle { fill: var(--signal); }
.schema-core-dots .sd1 { animation: schema-blink 1.5s ease-in-out infinite; }
.schema-core-dots .sd2 { animation: schema-blink 1.5s ease-in-out .25s infinite; }
.schema-core-dots .sd3 { animation: schema-blink 1.5s ease-in-out .5s infinite; }

.schema-dim path {
  stroke: rgba(250, 248, 245, .26);
  stroke-width: 1;
  fill: none;
}
.schema-dim text {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  fill: rgba(250, 248, 245, .5);
}

/* ---- Form ---- */
.contact-form { background: var(--paper-2); padding: var(--sp-10); border-radius: var(--r-lg); }

.form-note {
  font-family: var(--font-mono);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-3);
  margin-bottom: var(--sp-8);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--rule);
}
.req { color: var(--signal-deep); font-weight: 700; }

.form-group { margin-bottom: var(--sp-5); }
.form-group label {
  display: block;
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
  margin-bottom: var(--sp-2);
  transition: color var(--t);
}
.form-group input,
.form-group textarea,
.form-group select {
  width: 100%;
  padding: 13px var(--sp-4);
  /* background-color, never the `background` shorthand — the select
     chevron lives in background-image and the shorthand erases it. */
  background-color: var(--paper);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  outline: none;
  transition: border-color var(--t), box-shadow var(--t);
}
.form-group input:hover,
.form-group textarea:hover,
.form-group select:hover { border-color: var(--ink-3); }
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
  border-color: var(--signal-deep);
  box-shadow: 0 0 0 3px var(--signal-wash);
}
.form-group input::placeholder,
.form-group textarea::placeholder { color: var(--ink-4); }
.form-group textarea { resize: vertical; min-height: 124px; line-height: 1.6; }

.form-group select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B6B66' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 15px 15px;
}
.form-group select:focus {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C2410C' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

.form-error {
  display: block;
  font-size: 12px;
  color: var(--signal-deep);
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height var(--t-slow), opacity var(--t), margin-top var(--t-slow);
}
.form-group.has-error .form-error { max-height: 44px; opacity: 1; margin-top: 6px; }
.form-group.has-error label { color: var(--signal-deep); }
.form-group.has-error input,
.form-group.has-error textarea,
.form-group.has-error select {
  border-color: var(--signal-deep);
  background-color: var(--signal-wash);
  animation: shake .32s var(--ease-out);
}
.form-group.has-error select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23C2410C' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
@keyframes shake {
  0%,100% { transform: translateX(0); }
  25%     { transform: translateX(-4px); }
  75%     { transform: translateX(4px); }
}

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }

.form-submit {
  width: 100%;
  padding: 15px;
  margin-top: var(--sp-4);
  border-radius: var(--r-sm);
  background: var(--signal-deep);
  color: #fff;
  font-family: var(--font-display);
  font-size: 14.5px;
  font-weight: 600;
  transition: background var(--t), opacity var(--t);
}
.form-submit:hover:not(:disabled) { background: var(--ink); }
.form-submit:disabled { cursor: not-allowed; opacity: .6; }
.form-submit.is-success { background: #1E7A46; opacity: 1; }
.form-submit.is-error { background: var(--ink); opacity: 1; }

/* ================================================================
   FOOTER — ink band, closes the page with the CTA
   ================================================================ */
.footer {
  background: var(--band);
  color: rgba(250,248,245,.6);
  /* The contact section above is also ink, so the two used to run
     together as ~190px of undivided dark. A rule marks the boundary and
     lets the footer start closer. */
  border-top: 1px solid var(--rule-ink);
  padding: var(--sp-12) 0 var(--sp-8);
}
.footer-top {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--sp-12);
  padding-bottom: var(--sp-12);
  border-bottom: 1px solid var(--rule-ink);
}
.footer-brand p {
  font-size: 14px;
  color: rgba(250,248,245,.5);
  line-height: 1.68;
  margin-top: var(--sp-5);
  max-width: 44ch;   /* measured 30 ch/line at 32ch */
}
/* The mark is dark-on-light artwork; lift it out of the ink band. */
.footer-logo-img { filter: brightness(0) invert(1); opacity: .9; }
.footer-col h3 {
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: rgba(250,248,245,.58);
  margin-bottom: var(--sp-5);
}
.footer-col ul li { margin-bottom: var(--sp-3); }
.footer-col ul a {
  display: inline-block;
  padding: 4px 0;              /* touch target, not decoration */
  font-size: 14px;
  color: rgba(250,248,245,.72);
  transition: color var(--t);
}
.footer-col ul a:hover { color: var(--signal); }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--sp-8);
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: rgba(250,248,245,.58);
}

/* ================================================================
   SCROLL-DRIVEN ENHANCEMENTS
   Additive over the IntersectionObserver reveals — never the only
   way something becomes visible. Firefox has no animation-timeline
   support, hence the @supports gate.
   ================================================================ */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .case-metric {
      animation: metric-in linear both;
      animation-timeline: view();
      animation-range: entry 12% entry 58%;
    }
    @keyframes metric-in {
      from { opacity: .25; transform: translateY(18px); }
      to   { opacity: 1;   transform: none; }
    }
  }
}

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

  /* The schematic's pulses are dashes travelling a path. Freezing the
     animation would strand a 7% dash at the start of every wire, which
     reads as a glitch — so drop the dash entirely and let the wire show
     as a solid faint signal line instead. */
  .schema-pulse path {
    animation: none !important;
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    opacity: .3;
  }
  .schema-live::before,
  .schema-core-dots circle { animation: none !important; opacity: 1; }
  .js .reveal, .js .reveal-left, .js .reveal-right, .js .reveal-scale {
    opacity: 1 !important;
    transform: none !important;
  }
  .btn:hover, .social-link:hover { transform: none !important; }
}

/* ================================================================
   RESPONSIVE — see responsive.css
   ================================================================ */
