/* ==========================================================================
   Thyra Tech — design tokens & base architecture
   θύρα (thyra) = Greek for "door/threshold." Every recurring visual idea on
   this site ties back to that: the gateway glyph, the door-motif hero, the
   "which door" language. See README.md for the full system rationale.
   ========================================================================== */

:root {
  /* ---------- Color tokens ----------
     Pulled from the real Thyra Tech logo pack (assets/branding/) — the
     orange is the brand's actual mark color, not a proposed one. Two of the
     five "core" hues (accent, neutral) need an AA-safe text variant because
     they fail contrast in one direction — see the comments below each pair.
     Contrast computed with the WCAG relative-luminance formula, not eyeballed:
       accent on paper      2.79:1  (fails — needs the darkened accent-ink variant)
       accent on ink         6.42:1  (passes)
       ink on accent (btn)   6.42:1  (passes — buttons use ink text, not paper text)
       paper on accent       2.79:1  (fails — do not use paper text on accent fills)
       neutral on paper      4.92:1  (passes, AA body text)
       neutral-ink on ink    7.92:1  (passes)
       accent-2 on ink       6.22:1  (passes — accent-2 is decorative/ink-only)
       error on paper        5.85:1  (passes)
       error on paper-2      5.26:1  (passes — form status inside a .card)
       error on ink          3.06:1  (FAILS body text — error is paper-only.
                                      The contact form is the only consumer and
                                      it lives in a .section--paper. Add a
                                      lightened error-ink variant before using
                                      this token on any dark section.) */
  --color-ink: #08080a;        /* dominant dark background */
  --color-ink-2: #101013;      /* elevated dark surface — banding within ink sections */
  --color-paper: #f4f2ef;      /* light/paper background */
  --color-paper-2: #eae6e0;    /* elevated paper surface — cards on paper */
  --color-accent: #ff5a1f;     /* primary accent — Thyra Tech brand orange */
  --color-accent-ink: #b23e15; /* accent, darkened for AA text-on-paper (5.2:1) */
  --color-accent-2: #4f8cff;   /* secondary accent — cool tech contrast, decorative/ink-only */
  --color-neutral: #6e6862;    /* muted body text on paper */
  --color-neutral-ink: #a8a29b;/* muted body text on ink */
  --color-error: #b3261e;      /* form error text — paper backgrounds only, see above */

  /* ---------- Type tokens ----------
     Display serif for headings (editorial weight, matches "θύρα" as a real
     word rather than a logo trick), a clean sans for body copy, and an
     actual developer monospace for labels/eyebrows/utility text — not a
     decorative choice, Thyra Tech is a dev shop, so a real code font in the
     UI is honest branding, not just a "tech" affectation. */
  --font-display: "Source Serif 4", Georgia, serif;
  --font-body: "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
}

/* ---------- Self-hosted fonts ---------- */
@font-face { font-family: "Source Serif 4"; src: url("/assets/fonts/source-serif-4-regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Source Serif 4"; src: url("/assets/fonts/source-serif-4-italic.woff2") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Source Serif 4"; src: url("/assets/fonts/source-serif-4-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Source Serif 4"; src: url("/assets/fonts/source-serif-4-600italic.woff2") format("woff2"); font-weight: 600; font-style: italic; font-display: swap; }
@font-face { font-family: "Archivo"; src: url("/assets/fonts/archivo-regular.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Archivo"; src: url("/assets/fonts/archivo-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Archivo"; src: url("/assets/fonts/archivo-600.woff2") format("woff2"); font-weight: 600; font-display: swap; }
@font-face { font-family: "Archivo"; src: url("/assets/fonts/archivo-700.woff2") format("woff2"); font-weight: 700; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("/assets/fonts/jetbrains-mono-regular.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("/assets/fonts/jetbrains-mono-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "JetBrains Mono"; src: url("/assets/fonts/jetbrains-mono-700.woff2") format("woff2"); font-weight: 700; font-display: swap; }

/* ---------- Reset / base ---------- */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-ink);
  background: var(--color-paper);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img, video, svg { max-width: 100%; display: block; }
input, textarea, button, select { font: inherit; color: inherit; }
/* Anchors don't get a `color` reset by any browser default stylesheet the
   way form controls do — an <a> with no explicit color/class falls back to
   the UA default link blue (#0000EE), invisible in a screenshot if every
   child happens to set its own color, but wrong the moment a plain <a>
   shows up without one. Caught via computed-style sweep, 2026-08-08. */
a { color: inherit; }
::selection { background: var(--color-accent); color: var(--color-ink); }

/* ---------- Layout primitives ---------- */
.container {
  width: 100%;
  max-width: 1240px;
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.75rem);
}
.section { padding-block: clamp(3.5rem, 9vw, 8rem); position: relative; }

.section--ink { background: var(--color-ink); color: var(--color-paper); }
.section--ink-alt { background: linear-gradient(var(--color-ink), var(--color-ink-2), var(--color-ink)); color: var(--color-paper); }
.section--paper { background: var(--color-paper); color: var(--color-ink); }

.section--ink h1, .section--ink h2, .section--ink h3,
.section--ink-alt h1, .section--ink-alt h2, .section--ink-alt h3 { color: var(--color-paper); }

/* ---------- Typography ---------- */
h1, h2, h3 { font-family: var(--font-display); font-weight: 400; line-height: 1.04; letter-spacing: -0.02em; margin: 0 0 0.5em; text-wrap: balance; }
/* The vh term is load-bearing, not decoration. Sized on width alone (7vw), a
   1280x720 laptop — an extremely common size — rendered this at 89.6px, which
   pushed the homepage headline to six lines and cut the last one off at the
   fold. A clipped headline reads as a broken page, not as a big one. Taking
   the smaller of the width- and height-derived sizes keeps the type large on
   roomy screens and lets it finish on short ones. Measured 2026-08-15. */
h1 { font-size: clamp(2.75rem, min(7vw, 9vh), 5.75rem); line-height: 0.98; }
h2 { font-size: clamp(1.9rem, 4vw, 3.1rem); }
h3 { font-size: clamp(1.3rem, 2.3vw, 1.85rem); font-weight: 500; font-family: var(--font-body); }
p { margin: 0 0 1em; }
.lede { font-size: clamp(1.05rem, 1.4vw, 1.3rem); line-height: 1.65; color: var(--color-neutral); max-width: 46ch; }
.section--ink .lede, .section--ink-alt .lede { color: var(--color-neutral-ink); }

/* Accent italic word — the "OPEN." / "which door" treatment. Only ever sits
   inside a heading on an ink background, so plain --color-accent (6.42:1) is
   always safe here; do not reuse this class for body-size text on paper. */
.accent {
  font-style: italic;
  font-weight: 600;
  color: var(--color-accent);
}
.section--paper .accent { color: var(--color-accent-ink); }

.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.28em;
  font-size: 0.72rem;
  color: var(--color-accent-ink);
  margin-bottom: 1.4em;
}
/* Eyebrow text sits on both themes — ink-section eyebrows can use the vivid
   accent directly (6.42:1); paper-section eyebrows need the darkened
   accent-ink variant (5.2:1) since raw accent only hits 2.79:1 on paper.
   See the --color-accent-ink definition above for the math. */
.section--ink .eyebrow, .section--ink-alt .eyebrow { color: var(--color-accent); }
.eyebrow .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--color-accent);
  flex-shrink: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .eyebrow .dot { animation: th-blink 2.6s infinite; }
}
@keyframes th-blink { 0%, 45% { opacity: 1; } 50%, 95% { opacity: 0.2; } 100% { opacity: 1; } }

/* ---------- Signature motif ----------
   The gateway glyph is Thyra Tech's real logomark (assets/gateway-mark.svg,
   from the client-supplied logo pack) — not an invented character. Used as
   a CSS mask so it recolors with currentColor via the accent variable and
   works identically on ink or paper sections. */
.motif::before {
  content: "";
  display: inline-block;
  width: 0.62em;
  height: 0.8em;
  margin-right: 0.32em;
  vertical-align: -0.08em;
  background-color: var(--color-accent);
  -webkit-mask: url("/assets/gateway-mark.svg") center / contain no-repeat;
  mask: url("/assets/gateway-mark.svg") center / contain no-repeat;
}
.section--paper .motif::before, .section--paper.motif::before { background-color: var(--color-accent-ink); }

/* Hairline grid — scoped to the hero's door graphic only, not a page-wide
   decorative background. A full-viewport grid-line overlay is a recurring
   generated-UI tell (flagged by /impeccable's detector, 2026-08-08); the
   exception is a real blueprint/schematic surface, which the door diagram
   actually is, so it stays there and nowhere else. */
.grid-overlay {
  position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0.5;
  background-image:
    linear-gradient(rgba(244,242,239,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(244,242,239,.045) 1px, transparent 1px);
  background-size: 88px 88px;
  mask-image: radial-gradient(circle at 68% 45%, #000 10%, transparent 65%);
}
.scan-line {
  position: fixed; left: 0; right: 0; top: 0; height: 1px; z-index: 2; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(255,90,31,.55), transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .scan-line { animation: th-scan 9s linear infinite; }
}
@media (prefers-reduced-motion: reduce) {
  .grid-overlay, .scan-line { display: none; }
}
@keyframes th-scan { from { transform: translateY(-10vh); } to { transform: translateY(110vh); } }

/* ---------- Scroll reveal ---------- */
.reveal { transition: opacity 0.6s ease, transform 0.6s ease; }
.js .reveal { opacity: 0; transform: translateY(24px); }
.js .reveal.in-view { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- Nav ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: linear-gradient(rgba(8,8,10,.94), rgba(8,8,10,.86));
  backdrop-filter: blur(14px);
  color: var(--color-paper);
  border-bottom: 1px solid rgba(244,242,239,.08);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; padding-block: 0.9rem; }
.wordmark { display: flex; align-items: center; gap: 0.85rem; text-decoration: none; padding-block: 0.85rem; margin-block: -0.85rem; }
.wordmark-icon { width: 28px; height: 28px; flex-shrink: 0; }
.wordmark-icon .gate { fill: var(--color-accent); }
.wordmark-text { font-family: var(--font-display); font-size: 1.15rem; color: var(--color-paper); letter-spacing: -0.01em; }
.wordmark-greek { font-family: var(--font-mono); font-size: 0.75rem; color: var(--color-accent); letter-spacing: 0.08em; padding-left: 0.85rem; margin-left: 0.15rem; border-left: 1px solid rgba(244,242,239,.25); }
@media (max-width: 560px) { .wordmark-greek { display: none; } }

.nav-links--desktop { display: flex; align-items: center; gap: clamp(1rem, 2.4vw, 1.75rem); }
.nav-links--desktop a, .mobile-nav a {
  color: var(--color-neutral-ink);
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  display: inline-block;
  padding-block: 0.9rem;
  position: relative;
}
.nav-links--desktop a:hover, .nav-links--desktop a.active, .mobile-nav a.active { color: var(--color-paper); }
.nav-links--desktop a.active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0.45rem; height: 2px; background: var(--color-accent);
}
.nav-cta {
  background: var(--color-accent);
  color: var(--color-ink) !important;
  font-weight: 600;
  padding: 0.85rem 1.3rem !important;
  letter-spacing: 0.14em !important;
}
.nav-cta:hover { background: #ff7440; color: var(--color-ink) !important; }

.nav-toggle { display: none; place-items: center; min-width: 44px; min-height: 44px; background: none; border: 0; color: var(--color-paper); font-size: 1.4rem; cursor: pointer; line-height: 1; padding: 0; }
/* Grid-rows collapse instead of max-height: animating a bounded grid track
   avoids the layout-thrash a max-height transition causes (no guessed pixel
   ceiling, and modern browsers can interpolate the fr track without forcing
   the same reflow cost as height). The inner wrapper carries the real
   content so it can be clipped to 0 height without clipping mid-line. */
/* display:none by default, not just visually collapsed. `overflow: hidden`
   clips a thing out of sight but leaves it in the tab order, so above 800px —
   where the hamburger that opens this menu isn't even rendered — a keyboard
   user tabbed from "Start a project" into three invisible duplicate links
   (About / Services / Contact) before reaching the hero. Focus simply
   disappeared for three presses. Measured on the deployed site: tab stops
   8, 9, 10. WCAG 2.4.3 / 2.4.7. */
.mobile-nav { display: none; grid-template-rows: 0fr; transition: grid-template-rows 0.3s ease; }
.mobile-nav.open { grid-template-rows: 1fr; }
.mobile-nav-inner { overflow: hidden; min-height: 0; display: flex; flex-direction: column; gap: 0.25rem; padding-bottom: 1rem; }
@media (max-width: 800px) {
  .nav-toggle { display: grid; }
  .nav-links--desktop { display: none; }
  .mobile-nav { display: grid; }
  /* Same problem one breakpoint down: collapsed to 0fr the links are clipped
     but still tabbable, so a phone keyboard user reaches a menu they never
     opened. visibility:hidden drops them from the tab order and still lets the
     grid-track transition run; the delay holds it visible through the close
     animation so the menu doesn't vanish mid-collapse. */
  .mobile-nav-inner { visibility: hidden; transition: visibility 0s linear 0.3s; }
  .mobile-nav.open .mobile-nav-inner { visibility: visible; transition-delay: 0s; }
}

/* ---------- Home hero — coded door motif, not a stock/AI photo ---------- */
.hero-motif {
  position: relative;
  min-height: 92vh;
  display: flex;
  align-items: center;
  /* The 7rem floor on the top pad was spending 112px of a 720px viewport
     before the eyebrow even started. vh-first, with a smaller floor, so short
     screens get that space back for content. */
  padding-block: clamp(4.5rem, 12vh, 10rem) clamp(3rem, 8vh, 5rem);
  overflow: hidden;
}
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.85fr); gap: clamp(2rem, 5vw, 4rem); align-items: center; position: relative; z-index: 2; }
@media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; } }
.hero-motif h1 { margin-bottom: 0.55em; }
.hero-status { position: absolute; right: clamp(1.25rem, 4vw, 2.75rem); bottom: clamp(1.5rem, 4vh, 2.5rem); display: flex; align-items: center; gap: 0.9rem; font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.3em; text-transform: uppercase; color: var(--color-neutral-ink); z-index: 2; }
.hero-status .rule { width: 40px; height: 1px; background: var(--color-accent); }
@media (max-width: 700px) { .hero-status { display: none; } }

.hero-cta-row { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 2.5rem; }

.door-graphic { position: relative; width: 100%; max-width: 420px; aspect-ratio: 3 / 4; margin-inline: auto; perspective: 1400px; }
.door-frame {
  position: relative; width: 100%; height: 100%;
  border: 2px solid var(--color-accent);
  border-radius: 3px;
  overflow: hidden;
  background: var(--color-ink-2);
  box-shadow: 0 50px 90px -40px rgba(255,90,31,.35);
}
.door-glow {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 100% at 30% 15%, rgba(255,90,31,.55), transparent 55%),
    radial-gradient(90% 70% at 80% 90%, rgba(79,140,255,.35), transparent 60%),
    linear-gradient(200deg, #12131a, #0a0a0d 70%);
  opacity: 0; transition: opacity 1.6s ease .35s;
}
.door-graphic.is-open .door-glow { opacity: 1; }
.door-graphic.is-resetting .door-glow { transition: opacity .5s ease; }
.door-circuit-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.door-circuit { stroke-dasharray: 420; stroke-dashoffset: 420; transition: stroke-dashoffset 1.7s cubic-bezier(.16,1,.3,1) .55s; }
.door-graphic.is-open .door-circuit { stroke-dashoffset: 0; }
.door-graphic.is-resetting .door-circuit { transition: stroke-dashoffset .6s cubic-bezier(.16,1,.3,1); }
.door-leaf {
  position: absolute; inset: 0;
  background: linear-gradient(155deg, #1c1c20, #0d0d10 65%);
  border-right: 1px solid rgba(255,90,31,.45);
  background-image:
    linear-gradient(155deg, #1c1c20, #0d0d10 65%),
    repeating-linear-gradient(90deg, rgba(244,242,239,.03) 0 2px, transparent 2px 34px);
  transform-origin: left center;
  transform: rotateY(0deg);
  transition: transform 1.5s cubic-bezier(.16,1,.3,1);
  backface-visibility: hidden;
  display: flex; align-items: center; justify-content: center;
}
.door-graphic.is-open .door-leaf { transform: rotateY(-72deg); }
.door-graphic.is-resetting .door-leaf { transition-duration: .65s; }
.door-leaf-mark {
  width: 34%; aspect-ratio: 1;
  background-color: rgba(244,242,239,.14);
  -webkit-mask: url("/assets/gateway-mark.svg") center / contain no-repeat;
  mask: url("/assets/gateway-mark.svg") center / contain no-repeat;
}
.door-handle {
  position: absolute; right: 8%; top: 50%; width: 8px; height: 8px;
  border-radius: 50%; background: var(--color-accent);
  box-shadow: 0 0 10px rgba(255,90,31,.7);
}
.door-replay {
  position: absolute; z-index: 3; left: 30%; top: 50%;
  width: 44px; height: 44px; margin: 0; padding: 0;
  transform: translate(-50%, -50%);
  border: 0; border-radius: 50%; background: transparent;
  cursor: pointer; opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease;
}
.door-graphic.is-open .door-replay { opacity: 1; pointer-events: auto; }
.door-graphic.is-open .door-replay:disabled { opacity: 0; pointer-events: none; }
.door-replay::before {
  content: ""; position: absolute; inset: 12px; border: 1px solid var(--color-paper);
  border-radius: 50%; opacity: 0; transition: opacity 0.2s ease;
}
.door-replay:hover::before, .door-replay:focus-visible::before { opacity: 0.65; }
.door-replay:focus-visible { outline: 2px solid var(--color-paper); outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) {
  .door-leaf { transition: none; transform: rotateY(-72deg); }
  .door-glow { transition: none; opacity: 1; }
  .door-circuit { transition: none; stroke-dashoffset: 0; }
  .door-replay { display: none; }
}

/* ---------- Ticker ---------- */
/* The strip sits between two .section--ink blocks (hero and flow) and uses
   on-ink tokens throughout: --color-neutral-ink text and paper-tinted
   hairlines. It had no background of its own, so it fell through to body
   paper — a light gash between two dark sections, with the muted on-ink text
   landing at 2.26:1 (AA needs 4.5:1) and both borders effectively invisible.
   The background is what was missing; the tokens were always right.
   Caught by computed-style contrast sweep, 2026-08-15. */
.ticker-strip { background: var(--color-ink); color: var(--color-paper); border-top: 1px solid rgba(244,242,239,.1); border-bottom: 1px solid rgba(244,242,239,.1); overflow: hidden; padding-block: 1.1rem; }
.ticker-track { display: flex; width: max-content; }
@media (prefers-reduced-motion: no-preference) { .ticker-track { animation: th-ticker 32s linear infinite; } }
@media (prefers-reduced-motion: reduce) { .ticker-track { flex-wrap: wrap; } }
.ticker-set { display: flex; gap: 3rem; padding-right: 3rem; font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.24em; text-transform: uppercase; color: var(--color-neutral-ink); white-space: nowrap; }
.ticker-set span.sep { color: var(--color-accent); }
@keyframes th-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---------- Services ---------- */
.services-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(2rem, 4vw, 3.5rem); margin-top: 2.5rem; }
.service-card .service-index { display: flex; align-items: baseline; gap: 1rem; margin-bottom: 1.1rem; }
.service-card .service-index .num { font-family: var(--font-display); font-size: clamp(2.4rem, 4vw, 3.2rem); font-weight: 400; color: var(--color-accent); line-height: 1; }
.service-card .service-index .label { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.24em; text-transform: uppercase; color: var(--color-neutral); }
.section--ink .service-card .service-index .label, .section--ink-alt .service-card .service-index .label { color: var(--color-neutral-ink); }
.service-card h3 { margin-bottom: 0.6em; }
.service-card p { color: var(--color-neutral); max-width: 42ch; }
.section--ink .service-card p, .section--ink-alt .service-card p { color: var(--color-neutral-ink); }
.service-list { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: 0.75rem; max-width: 40ch; }
.service-list li { display: flex; gap: 0.8rem; align-items: baseline; padding-bottom: 0.75rem; border-bottom: 1px solid rgba(110,104,98,.18); font-size: 0.92rem; }
.section--ink .service-list li, .section--ink-alt .service-list li { border-bottom-color: rgba(244,242,239,.12); }
.service-list li::before { content: "\2192"; color: var(--color-accent); flex-shrink: 0; }

/* ---------- First-call confidence ---------- */
.confidence-section { padding-top: 0; }
.confidence-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.75rem, 3vw, 3rem); margin-top: 2.75rem; }
.confidence-item { border-top: 1px solid rgba(110,104,98,.28); padding-top: 1.25rem; }
.confidence-label { display: block; margin-bottom: 0.85rem; color: var(--color-accent-ink); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.2em; text-transform: uppercase; }
.confidence-item h3 { margin-bottom: 0.55em; font-size: 1.25rem; }
.confidence-item p { max-width: 36ch; color: var(--color-neutral); }
@media (max-width: 800px) { .confidence-grid { grid-template-columns: 1fr; } }

/* ---------- Process ---------- */
.process-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: clamp(1.75rem, 3vw, 3rem); margin-top: 2.5rem; }
.process-step { border-top: 1px solid rgba(244,242,239,.16); padding-top: 1.4rem; }
.process-step.is-final { border-top-color: var(--color-accent); }
.process-step .step-label { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.24em; color: var(--color-neutral-ink); margin-bottom: 1rem; }
.process-step.is-final .step-label { color: var(--color-accent); }
/* h3, not h4 — these sit directly under the section's h2, so h4 skipped a
   level and broke heading navigation for screen readers. The explicit
   font-size here keeps the rendered size identical to before the change. */
.process-step h3 { font-family: var(--font-body); font-weight: 600; font-size: 1.15rem; margin: 0 0 0.6em; }
.process-step p { color: var(--color-neutral-ink); font-size: 0.94rem; margin: 0; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  position: relative; overflow: hidden;
  margin-top: 0; padding: 0.95rem 1.9rem;
  background: var(--color-accent);
  color: var(--color-ink);
  font-family: var(--font-mono);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.78rem;
  text-decoration: none;
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 18px 40px -16px rgba(255,90,31,.7); }
.btn--outline {
  background: transparent;
  color: inherit;
  border: 1px solid rgba(110,104,98,.4);
}
.section--ink .btn--outline, .section--ink-alt .btn--outline { border-color: rgba(244,242,239,.28); }
.btn--outline:hover { border-color: var(--color-accent); color: var(--color-accent); box-shadow: none; }
.section--paper .btn--outline:hover { border-color: var(--color-accent-ink); color: var(--color-accent-ink); }
/* A 320px content column cannot hold the longest CTA as one unbroken line
   with desktop-size side padding. Let it wrap on narrow phones instead of
   widening the whole document and exposing a horizontal scrollbar. */
@media (max-width: 400px) {
  .btn { max-width: 100%; padding-inline: 1.25rem; letter-spacing: 0.12em; white-space: normal; }
}
.btn-risk-note { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.04em; color: var(--color-neutral); margin-top: 0.9rem; }
.section--ink .btn-risk-note, .section--ink-alt .btn-risk-note { color: var(--color-neutral-ink); }

/* ---------- Page hero (About / Services / Contact) ---------- */
/* Interior page heroes are a compact band, not the homepage's full-height
   statement, so they get their own type scale and spacing rhythm.

   What was wrong (measured 2026-08-08): they inherited the global h1 —
   92px at line-height .98 — inside a 383px band, with 16px of clearance
   above it. On mobile the same headline wrapped to two lines with 16px
   above and 15px below, so every interval was effectively identical. Equal
   intervals around an oversized element is exactly what reads as "stacked".

   The fix is rhythm, not just more air: the eyebrow sits CLOSE to the h1
   because a label and its title are one group, and the lede sits FAR from
   it because it's a separate thought. Roughly 1:3 rather than 1:1. */
.page-hero { background: var(--color-ink); color: var(--color-paper); padding-block: clamp(4rem, 10vw, 7rem) clamp(3rem, 7vw, 5rem); }
.page-hero h1 {
  font-size: clamp(2.25rem, 5.2vw, 4rem);
  /* .98 is a display setting for a headline that never wraps. These do
     (two lines on About and Contact at phone widths), and at .98 the
     wrapped lines nearly collide. */
  line-height: 1.06;
  margin-bottom: 0.68em;
}
.page-hero .eyebrow { margin-bottom: 1.05em; }
.page-hero .lede { margin: 0; }

/* ---------- Split layout (About bio, Services intro) ---------- */
.split-layout { display: grid; grid-template-columns: 1.15fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (max-width: 860px) { .split-layout { grid-template-columns: 1fr; } }
.split-layout img { width: 100%; border-radius: 4px; object-fit: cover; max-height: 460px; }
.about-portrait { aspect-ratio: 3 / 4; object-position: center top; }

.section-followup { margin-top: 2.5rem; }

/* ---------- Differentiators (About) ---------- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: clamp(1.5rem, 3vw, 2.5rem); margin-top: 2.5rem; }
.stat-item { border-top: 1px solid rgba(110,104,98,.25); padding-top: 1.2rem; }
.section--ink .stat-item, .section--ink-alt .stat-item { border-top-color: rgba(244,242,239,.16); }
.stat-item .stat-word { font-family: var(--font-display); font-size: clamp(1.75rem, 3vw, 2.5rem); color: var(--color-accent-ink); line-height: 1; }
.section--ink .stat-item .stat-word, .section--ink-alt .stat-item .stat-word { color: var(--color-accent); }
.stat-item p { margin-top: 0.7rem; font-size: 0.95rem; color: var(--color-neutral); }
.section--ink .stat-item p, .section--ink-alt .stat-item p { color: var(--color-neutral-ink); }

/* Full-width supporting image band */
.photo-band { width: 100%; max-width: 760px; margin: 2.75rem auto 0; border-radius: 4px; object-fit: cover; }

/* ---------- Contact ---------- */
.contact-layout { display: grid; grid-template-columns: 1.3fr 1fr; gap: clamp(2rem, 5vw, 3.5rem); margin-top: 2.5rem; align-items: start; }
@media (max-width: 860px) { .contact-layout { grid-template-columns: 1fr; } }
.contact-form { display: flex; flex-direction: column; gap: 1.3rem; }
.contact-form label { display: flex; flex-direction: column; gap: 0.5rem; font-family: var(--font-mono); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.16em; color: var(--color-neutral); }
.contact-form input, .contact-form textarea {
  font-family: var(--font-body); font-size: 1rem; padding: 0.85rem 0.2rem;
  border: 0; border-bottom: 1px solid rgba(110,104,98,.4);
  background: transparent; color: var(--color-ink);
  border-radius: 0; transition: border-color 0.25s ease;
}
.contact-form input:focus, .contact-form textarea:focus { outline: none; border-bottom-color: var(--color-accent); }
.contact-form input:focus-visible, .contact-form textarea:focus-visible {
  outline: 2px solid var(--color-accent-ink);
  outline-offset: 2px;
}
.contact-form .btn { align-self: flex-start; margin-top: 0.5rem; }
/* The form is a flex column with gap:1.3rem, so the gap alone should set the
   rhythm. These two are <p>s, which meant the global `p { margin: 0 0 1em }`
   AND .btn-risk-note's own margin-top stacked on top of that gap — the
   "No obligation" note ended up 48px from the status line in a form that is
   otherwise a steady 21px, so it read as detached from the button it
   qualifies. Let the gap own it. */
.contact-form .form-status, .contact-form .btn-risk-note { margin: 0; }
/* Point-of-submit privacy line. Same mono treatment as the risk note, but the
   policy link has to read as a link at 0.72rem — underline, not colour alone,
   since --color-neutral on paper is too low-contrast to carry the affordance. */
.form-disclosure { max-width: 46ch; line-height: 1.55; }
.form-disclosure a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; }
.form-disclosure a:hover, .form-disclosure a:focus-visible { color: var(--color-accent-ink); }
.form-status { font-family: var(--font-mono); font-size: 0.82rem; min-height: 1.3em; }
.form-status.is-success { color: var(--color-accent-ink); }
/* Error used --color-ink, i.e. the same colour as ordinary body copy, so a
   failed submission was distinguished from the success message only by its
   wording. --color-error is a genuinely different hue that still clears AA on
   paper (5.85:1), and the left rule gives a non-colour cue for anyone who
   can't separate the two by colour alone (WCAG 1.4.1). */
.form-status.is-error {
  color: var(--color-error);
  font-weight: 600;
  border-left: 2px solid var(--color-error);
  padding-left: 0.7rem;
}
.form-status.is-success { border-left: 2px solid var(--color-accent-ink); padding-left: 0.7rem; }

.contact-details { display: grid; gap: 1.5rem; }
.contact-details .field { border-top: 1px solid rgba(110,104,98,.25); padding-top: 0.9rem; }
.contact-details .field-label { display: block; font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--color-neutral); margin-bottom: 0.4rem; }
.contact-details a { color: var(--color-ink); text-decoration: none; font-size: 1.1rem; display: inline-block; padding-block: 0.5rem; border-bottom: 1px solid transparent; }
.contact-details a:hover { color: var(--color-accent-ink); border-bottom-color: var(--color-accent-ink); }
.contact-details .field-value { margin: 0.5rem 0 0; font-size: 1.1rem; }
.field-optional { text-transform: none; letter-spacing: 0; opacity: 0.7; }

/* ---------- Cards used across Services page ---------- */
.card { background: var(--color-paper-2); padding: clamp(1.5rem, 3vw, 2.25rem); border-radius: 4px; }
.fit-card { padding: clamp(1.5rem, 3vw, 2.25rem); border-radius: 4px; }
.fit-card h3 { color: var(--color-paper); }
.fit-card--good { background: rgba(255,90,31,.08); border: 1px solid rgba(255,90,31,.3); }
.fit-card--bad { background: rgba(244,242,239,.04); border: 1px solid rgba(244,242,239,.14); }
.fit-grid { margin-top: 2rem; }

/* ---------- Centered section headings (closing CTAs, pull-quotes) ---------- */
.heading-tight { max-width: 22ch; }
.section--center { text-align: center; }
.section--center h2, .section--center .heading-tight { margin-inline: auto; }
.section--center .btn { margin-top: 2.25rem; }
.section--center .pull-quote { margin-bottom: 0; }
.pull-quote {
  margin: 0 auto; max-width: 26ch;
  font-family: var(--font-display); font-style: italic; font-weight: 600;
  font-size: clamp(1.75rem, 4vw, 3rem); line-height: 1.15;
}

/* ---------- Legal pages (Privacy) ---------- */
.legal-content { max-width: 68ch; margin-inline: auto; }
.legal-content .legal-updated { font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.06em; color: var(--color-neutral); margin-bottom: 2.5rem; }
.legal-content h2 { font-size: clamp(1.3rem, 2.2vw, 1.6rem); margin: 2.5rem 0 0.9rem; }
.legal-content h2:first-of-type { margin-top: 0; }
.legal-content p { color: var(--color-ink); }
.legal-content a { color: var(--color-accent-ink); text-decoration: underline; text-underline-offset: 0.15em; }
.legal-content a:hover { color: var(--color-ink); }
.legal-content code { font-family: var(--font-mono); font-size: 0.88em; background: var(--color-paper-2); padding: 0.1em 0.4em; border-radius: 3px; }

/* ---------- Footer ---------- */
.site-footer { border-top: 1px solid rgba(244,242,239,.08); padding-block: clamp(2.5rem, 5vw, 4rem); }
.footer-inner { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: 2rem; text-align: left; }
.footer-brand { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.footer-brand .wordmark-icon { width: 34px; height: 34px; }
.footer-tagline { font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.06em; color: var(--color-neutral-ink); }
.footer-links { display: flex; flex-wrap: wrap; gap: clamp(1rem, 3vw, 2rem); font-family: var(--font-mono); font-size: 0.74rem; letter-spacing: 0.18em; text-transform: uppercase; }
.footer-links a { color: var(--color-neutral-ink); text-decoration: none; display: inline-flex; align-items: center; min-height: 44px; padding-block: 0.85rem; }
.footer-links a:hover { color: var(--color-accent); }
.footer-socials { display: none; } /* no socials on file yet — see README */

/* ---------- Accessibility ---------- */
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 100;
  padding: 0.85rem 1rem; background: var(--color-accent); color: var(--color-ink);
  font-family: var(--font-mono); font-size: 0.85rem; text-decoration: none;
  transition: top 0.18s ease;
}
.skip-link:focus-visible { top: 8px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
/* Genuinely hidden from everyone, assistive tech included. Use this — not
   .sr-only — for the contact honeypot: .sr-only deliberately KEEPS content in
   the accessibility tree, which is exactly wrong for a spam trap. */
.is-hidden { display: none; }
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px;
}
