/* ==========================================================================
   Austin Nguyen — portfolio hub
   assets/css/portfolio.css

   Property token group: "hub". Dark-first, near-monochrome, one lime accent.
   The four case-study cards supply all of the page's color.

   The token block below is GENERATED from design-system/tokens.json.
   Do not hand-edit values here — edit tokens.json, re-run
   tools/check_tokens.py, and regenerate.

   Every rule below the token block consumes var(--semantic-role) only.
   No raw color literals, no primitive references outside the token block.
   ========================================================================== */


/* ==========================================================================
   1. TOKENS  (generated from design-system/tokens.json)
   ========================================================================== */

:root {

  /* --- primitives: primitive.hub --------------------------------------- */
  --hub-ink-950: #0A0E13;
  --hub-ink-900: #111720;
  --hub-ink-850: #161D27;
  --hub-ink-800: #1D2530;
  --hub-ink-700: #2A3441;
  --hub-ink-600: #5A6875;
  --hub-ink-400: #8D9BAA;
  --hub-ink-300: #AFBCC9;
  --hub-ink-100: #DCE4EC;
  --hub-ink-050: #EFF4F9;
  --hub-lime-300: #D4F96B;
  --hub-lime-400: #C3F53C;
  --hub-lime-600: #8FBF12;
  --hub-white: #FFFFFF;

  /* --- primitives: borrowed for the case-study cards --------------------
     The work grid shows four demo sites, and each card carries its own
     demo's accent so the four read as four different design languages.
     These are lifted verbatim from the other property groups in
     tokens.json — primitive.terra, primitive.iron, primitive.lumen and
     primitive.maison — not invented here. They are bound to the
     --case-* semantic roles below and used nowhere else.
     --------------------------------------------------------------------- */
  --terra-clay-600: #A8421D;
  --terra-clay-500: #C4562B;
  --terra-olive-600: #55643C;
  --iron-amber-700: #B45309;
  --iron-amber-500: #F59E0B;
  --iron-navy-800: #1E3A8A;
  --lumen-teal-700: #0E7490;
  --lumen-teal-600: #0891B2;
  --lumen-mint-200: #BFE3DD;
  --maison-gold-400: #D4BC7E;
  --maison-noir-700: #33302B;

  /* --- semantic roles: semantic.hub ------------------------------------ */
  --bg: var(--hub-ink-950);
  --surface: var(--hub-ink-900);
  --surface-raised: var(--hub-ink-850);
  --surface-inset: var(--hub-ink-800);
  --border-subtle: var(--hub-ink-700);
  --border-interactive: var(--hub-ink-600);
  --text-primary: var(--hub-ink-050);
  --text-secondary: var(--hub-ink-300);
  --text-muted: var(--hub-ink-400);
  --action-primary: var(--hub-lime-400);
  --action-primary-hover: var(--hub-lime-300);
  --on-action-primary: var(--hub-ink-950);
  --accent-graphic: var(--hub-lime-600);
  --focus-ring: var(--hub-lime-400);
  --border-interactive-hover: var(--hub-ink-400);
  --text-emphasis: var(--hub-ink-100);
  --text-max: var(--hub-white);

  /* --- semantic roles: case-study accents ------------------------------
     The -dot roles are the readable sibling of each -accent. The accent
     itself is a 4px rule and an SVG fill; the dot is a glyph on the card
     surface, where clay-600, amber-700 and teal-700 all fall under 3:1.
     --------------------------------------------------------------------- */
  --case-terra-accent: var(--terra-clay-600);
  --case-terra-graphic: var(--terra-olive-600);
  --case-terra-dot: var(--terra-clay-500);
  --case-iron-accent: var(--iron-amber-700);
  --case-iron-graphic: var(--iron-navy-800);
  --case-iron-dot: var(--iron-amber-500);
  --case-lumen-accent: var(--lumen-teal-700);
  --case-lumen-graphic: var(--lumen-mint-200);
  --case-lumen-dot: var(--lumen-teal-600);
  --case-maison-accent: var(--maison-gold-400);
  --case-maison-graphic: var(--maison-noir-700);
  --case-maison-dot: var(--maison-gold-400);

  /* --- shared scale: space (scales.space, 4px grid) -------------------- */
  --space-0: 0px;
  --space-px: 2px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;
  --space-32: 128px;
  --space-40: 160px;

  /* --- shared scale: type (scales.type) -------------------------------- */
  --type-xs: 12px;
  --type-sm: 14px;
  --type-base: 16px;
  --type-md: 18px;
  --type-lg: 20px;
  --type-xl: 24px;
  --type-2xl: 30px;
  --type-3xl: 36px;
  --type-4xl: 48px;
  --type-5xl: 60px;
  --type-6xl: 72px;
  --type-7xl: 96px;

  /* --- shared scale: leading (scales.leading) -------------------------- */
  --leading-tight: 1.05;
  --leading-snug: 1.2;
  --leading-normal: 1.5;
  --leading-relaxed: 1.65;
  --leading-loose: 1.75;

  /* --- shared scale: weight (scales.weight) ---------------------------- */
  --weight-light: 300;
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-black: 800;

  /* --- shared scale: radius (scales.radius) ----------------------------
     The hub binds md 8 and lg 12 only (MASTER section 5).
     --------------------------------------------------------------------- */
  --radius-none: 0px;
  --radius-xs: 2px;
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-2xl: 20px;
  --radius-3xl: 24px;
  --radius-4xl: 32px;
  --radius-full: 999px;

  /* --- shared scale: motion (scales.motion) ---------------------------- */
  --dur-instant: 120ms;
  --dur-fast: 180ms;
  --dur-base: 240ms;
  --dur-slow: 320ms;
  --dur-slower: 480ms;
  --dur-slowest: 640ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* --- shared scale: z (scales.z) -------------------------------------- */
  --z-base: 0;
  --z-raised: 10;
  --z-sticky: 20;
  --z-overlay: 40;
  --z-modal: 100;
  --z-toast: 1000;

  /* --- shared scale: container (scales.container) ---------------------- */
  --container-prose: 680px;
  --container-narrow: 880px;
  --container-default: 1160px;
  --container-wide: 1360px;

  /* --- typography stacks (typography.hub) ------------------------------ */
  --font-display: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-body: 'Space Grotesk', 'Segoe UI', system-ui, -apple-system, sans-serif;

  /* --- derived layout values -------------------------------------------
     Composed from the scales above, never from fresh numbers. The hairline
     is the one exception: it is deliberately one device pixel, not a
     spacing step.
     --------------------------------------------------------------------- */
  --gutter: var(--space-5);
  --rule-hairline: 1px;
  --header-height: var(--space-16);

  /* Breakpoints live in the media queries themselves because custom
     properties cannot be used in media conditions. The token values from
     scales.breakpoint are 480, 768, 1024, 1280 and 1536. */
}


/* ==========================================================================
   2. RESET AND BASE
   ========================================================================== */

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

* {
  margin: 0;
}

html {
  background-color: var(--bg);
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + var(--space-6));
}

/* Lenis (smooth wheel scrolling; assets/js/lenis.min.js, MIT): the rules
   its own lenis.css ships, which the script needs. html.lenis is set only
   while Lenis runs. */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis:not(.lenis-autoToggle).lenis-stopped {
  overflow: clip;
}

.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch],
.lenis [data-lenis-prevent-vertical],
.lenis [data-lenis-prevent-horizontal] {
  overscroll-behavior: contain;
}

.lenis.lenis-smooth iframe {
  pointer-events: none;
}

/* A deep link (/#pricing, or /seo-aeo/#pricing from the other page) lands
   straight on its target; smooth scrolling is for clicks within the page.
   The <head> script sets .is-jumping when the URL has a fragment and clears
   it once the page has loaded. */
html.is-jumping {
  scroll-behavior: auto;
}

body {
  background-color: transparent;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--type-base);
  font-weight: var(--weight-regular);
  line-height: var(--leading-relaxed);
  min-height: 100%;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

svg {
  height: auto;
}

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

button {
  background: none;
  border: 0;
  cursor: pointer;
}

/* Scoped to classed lists so an unclassed <ul> in prose keeps its markers.
   Every classed list in the markup carries role="list", because Safari with
   VoiceOver drops list semantics from any list whose list-style is none. */
ul[class],
ol[class] {
  list-style: none;
  padding: 0;
}

a {
  color: var(--action-primary);
  text-decoration-color: var(--action-primary);
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition: color var(--dur-fast) var(--ease-out),
              text-decoration-thickness var(--dur-fast) var(--ease-out);
}

a:hover {
  color: var(--action-primary-hover);
  text-decoration-thickness: 2px;
}

strong,
b {
  color: var(--text-primary);
  font-weight: var(--weight-semibold);
}

/* No border-radius here: a bare :focus-visible radius reshapes every
   element that does not declare its own (the skip link, .brand, inline
   links, the mobile nav links) at the moment it receives focus. Elements
   that want a rounded ring carry their own radius in their own rule. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

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


/* ==========================================================================
   3. TYPOGRAPHY ROLES
   ========================================================================== */

h1, h2, h3, h4 {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  line-height: var(--leading-snug);
  text-wrap: balance;
}

/* MASTER section 9 item 5, settled: Space Grotesk stays on headings and UI,
   long-form body copy is set in Archivo. --font-display is Archivo, so the
   prose selectors below take it and everything else - body, buttons,
   eyebrows, labels, nav, badges, metadata - stays on --font-body. */
.hero__lede,
.t-lead,
.case__problem,
.work__notice-text,
.capability__text,
.deliver__text,
.proof__text,
.step__role-text,
.process__total-text,
.faq__panel p,
.faq__foot,
.terms-card__text,
.terms-card__list li,
.tier__who,
.contact__direct-text,
.form__intro,
.form__success-text,
.site-footer__blurb {
  font-family: var(--font-display);
}

/* Typography roles from MASTER section 6. The page binds the ones it uses;
   the hero binds display sizes directly on .hero__name because it is the
   only element on the site that runs the full 36 -> 96 ramp. */
.t-h2 {
  font-size: var(--type-2xl);
  letter-spacing: -0.025em;
  line-height: var(--leading-snug);
}

.t-h3 {
  font-size: var(--type-xl);
  letter-spacing: -0.02em;
}

.t-lead {
  color: var(--text-emphasis);
  font-size: var(--type-md);
  line-height: var(--leading-relaxed);
  max-width: var(--container-prose);
}

.t-eyebrow {
  align-items: center;
  color: var(--text-muted);
  display: flex;
  font-family: var(--font-body);
  font-size: var(--type-xs);
  font-weight: var(--weight-medium);
  gap: var(--space-3);
  letter-spacing: 0.16em;
  line-height: var(--leading-normal);
  text-transform: uppercase;
}

.t-eyebrow__num {
  color: var(--action-primary);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
}

.t-eyebrow__num::after {
  background-color: var(--border-subtle);
  content: "";
  display: inline-block;
  height: 1px;
  margin-inline-start: var(--space-3);
  vertical-align: middle;
  width: var(--space-6);
}

code {
  background-color: var(--surface-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.875em;
  padding: var(--space-px) var(--space-2);
  white-space: nowrap;
}

.t-num {
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   4. LAYOUT PRIMITIVES
   ========================================================================== */

.container {
  margin-inline: auto;
  max-width: var(--container-default);
  padding-inline: var(--gutter);
  width: 100%;
}

/* The work gallery runs wider than the rest of the page so the four cards
   can go two-up without crushing. Every other section stays at 1160. */
.container--wide {
  max-width: var(--container-wide);
}

.section {
  padding-block: var(--space-16);
  position: relative;
}

.section--tight {
  padding-block: var(--space-12);
}

.section__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-10);
}

.section__head .t-lead {
  margin-top: var(--space-1);
}

.section--surface {
  background-color: var(--surface);
}


/* ==========================================================================
   5. UTILITIES
   ========================================================================== */

.skip-link {
  background-color: var(--action-primary);
  border-radius: var(--radius-md);
  color: var(--on-action-primary);
  font-family: var(--font-body);
  font-size: var(--type-sm);
  font-weight: var(--weight-bold);
  left: var(--space-4);
  padding: var(--space-3) var(--space-5);
  position: fixed;
  text-decoration: none;
  top: var(--space-4);
  transform: translateY(-200%);
  z-index: var(--z-toast);
}

.skip-link:focus-visible {
  color: var(--on-action-primary);
  transform: translateY(0);
}

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

.grain {
  inset: 0;
  mix-blend-mode: overlay;
  opacity: 0.05;
  pointer-events: none;
  position: fixed;
  z-index: -1;
}


/* ==========================================================================
   6. COMPONENT — BUTTON
   Contract: MASTER section 6. Every state declared, never defaulted.
   Sizes: sm 36 (desktop only), md 44, lg 52. Touch floor is 44px.
   ========================================================================== */

.btn {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  display: inline-flex;
  font-family: var(--font-body);
  font-size: var(--type-base);
  font-weight: var(--weight-semibold);
  gap: var(--space-2);
  justify-content: center;
  letter-spacing: -0.01em;
  line-height: var(--leading-normal);
  min-height: 44px;
  padding-inline: var(--space-5);
  text-align: center;
  text-decoration: none;
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out),
              transform var(--dur-instant) var(--ease-out);
}

.btn--lg {
  font-size: var(--type-md);
  min-height: 52px;
  padding-inline: var(--space-6);
}

.btn--sm {
  font-size: var(--type-sm);
  min-height: 36px;
  padding-inline: var(--space-3);
}

.btn--primary {
  background-color: var(--action-primary);
  color: var(--on-action-primary);
}

.btn--primary:hover {
  background-color: var(--action-primary-hover);
  color: var(--on-action-primary);
}

.btn--primary:active {
  transform: scale(0.98);
}

.btn--secondary {
  background-color: transparent;
  border-color: var(--action-primary);
  color: var(--action-primary);
}

.btn--secondary:hover {
  background-color: var(--surface-raised);
  color: var(--action-primary-hover);
}

.btn--secondary:active {
  transform: scale(0.98);
}

.btn--ghost {
  background-color: transparent;
  border-color: var(--border-subtle);
  color: var(--text-primary);
}

.btn--ghost:hover {
  background-color: var(--surface-raised);
  border-color: var(--border-interactive);
  color: var(--text-primary);
}

.btn--ghost:active {
  transform: scale(0.98);
}

.btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

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

.btn__arrow {
  flex: none;
  height: 14px;
  transition: transform var(--dur-base) var(--ease-out);
  width: 14px;
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}


/* ==========================================================================
   7. COMPONENT — LINK, BADGE, SURFACE
   ========================================================================== */

.link-inline {
  color: var(--action-primary);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.link-quiet {
  color: var(--text-secondary);
  text-decoration: none;
}

.link-quiet:hover {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.badge {
  align-items: center;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
  color: var(--text-muted);
  display: inline-flex;
  font-size: var(--type-xs);
  font-weight: var(--weight-medium);
  gap: var(--space-2);
  letter-spacing: 0.08em;
  line-height: var(--leading-tight);
  padding: var(--space-2) var(--space-3);
  text-transform: uppercase;
  white-space: nowrap;
}

.badge__dot {
  background-color: currentColor;
  border-radius: var(--radius-full);
  flex: none;
  height: 6px;
  width: 6px;
}


/* ==========================================================================
   8. SITE HEADER AND NAV
   ========================================================================== */

.site-header {
  background-color: var(--bg);
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
}

.site-header__inner {
  align-items: center;
  display: flex;
  gap: var(--space-4);
  justify-content: space-between;
  min-height: var(--header-height);
}

.brand {
  align-items: center;
  color: var(--text-primary);
  display: inline-flex;
  gap: var(--space-3);
  min-height: 44px;
  text-decoration: none;
}

.brand:hover {
  color: var(--text-primary);
}

.brand__mark {
  flex: none;
  height: 32px;
  width: 32px;
}

.brand__mark-frame {
  fill: none;
  stroke: var(--border-interactive);
  stroke-width: 2;
}

.brand__mark-glyph {
  fill: var(--action-primary);
}

.brand__name {
  font-family: var(--font-display);
  font-size: var(--type-base);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  line-height: var(--leading-tight);
}

.brand__role {
  color: var(--text-muted);
  display: block;
  font-family: var(--font-body);
  font-size: var(--type-xs);
  font-weight: var(--weight-regular);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.nav-toggle {
  align-items: center;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  display: inline-flex;
  gap: var(--space-2);
  font-size: var(--type-sm);
  font-weight: var(--weight-medium);
  justify-content: center;
  min-height: 44px;
  padding-inline: var(--space-4);
}

.nav-toggle:hover {
  background-color: var(--surface-raised);
  border-color: var(--border-interactive);
}

.nav-toggle__bars {
  display: inline-flex;
  flex-direction: column;
  gap: var(--space-1);
  width: var(--space-5);
}

.nav-toggle__bars span {
  background-color: currentColor;
  display: block;
  height: var(--space-px);
  transition: transform var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
  width: 100%;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* The mobile nav is shown and hidden with a class rather than the hidden
   attribute, so that the desktop override below cannot leave it visible on
   screen but missing from the accessibility tree. */
.site-nav {
  background-color: var(--surface);
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: none;
  left: 0;
  padding-block: var(--space-2);
  position: absolute;
  right: 0;
  top: 100%;
  z-index: var(--z-overlay);
}

.site-nav.is-open {
  display: block;
}

.site-nav__list {
  display: flex;
  flex-direction: column;
  margin-inline: auto;
  max-width: var(--container-default);
  padding-inline: var(--gutter);
  width: 100%;
}

.site-nav__link {
  align-items: center;
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
  color: var(--text-secondary);
  display: flex;
  font-size: var(--type-base);
  font-weight: var(--weight-medium);
  min-height: 52px;
  text-decoration: none;
  width: 100%;
}

.site-nav__list li:last-child .site-nav__link {
  border-bottom: 0;
}

.site-nav__link:hover {
  color: var(--text-primary);
}

.site-nav__link[aria-current="true"] {
  color: var(--action-primary);
}

.site-header__cta {
  display: none;
}


/* ==========================================================================
   9. HERO
   ========================================================================== */

.hero {
  overflow: hidden;
  padding-block: var(--space-16) var(--space-16);
  position: relative;
}

.hero__backdrop {
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  position: absolute;
  z-index: var(--z-base);
}

.hero__grid {
  background-image:
    repeating-linear-gradient(to right, var(--border-subtle) 0 1px, transparent 1px 80px),
    repeating-linear-gradient(to bottom, var(--border-subtle) 0 1px, transparent 1px 80px);
  inset: 0;
  -webkit-mask-image: radial-gradient(120% 90% at 12% 0%, var(--bg) 0%, transparent 72%);
  mask-image: radial-gradient(120% 90% at 12% 0%, var(--bg) 0%, transparent 72%);
  opacity: 0.55;
  position: absolute;
}

.hero__glow {
  background-image:
    radial-gradient(58% 48% at 8% 4%, var(--accent-graphic) 0%, transparent 62%),
    radial-gradient(46% 40% at 96% 22%, var(--surface-inset) 0%, transparent 68%);
  inset: -20% -10% auto -10%;
  height: 120%;
  opacity: 0.28;
  position: absolute;
}

.hero__inner {
  position: relative;
  z-index: var(--z-raised);
}

.hero__title {
  margin-top: var(--space-6);
}

.hero__name {
  display: block;
  font-size: var(--type-3xl);
  font-weight: var(--weight-black);
  letter-spacing: -0.04em;
  line-height: var(--leading-tight);
}

.hero__role {
  color: var(--text-secondary);
  display: block;
  font-size: var(--type-lg);
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
  line-height: var(--leading-snug);
  margin-top: var(--space-3);
  max-width: 11.48em; /* 20ch in Archivo 500; em for the reason at .hero__lede */
}

.hero__rule {
  background-color: var(--action-primary);
  height: var(--space-1);
  margin-block: var(--space-6);
  width: var(--space-20);
}

.hero__lede {
  color: var(--text-secondary);
  font-size: var(--type-md);
  line-height: var(--leading-relaxed);
  /* em, not ch: the hero text animates in while Archivo swaps in, and a
     ch measure resolved in the fallback font can stick (27.5em = 48ch in
     Archivo). */
  max-width: 27.5em;
}

.hero__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

.hero__meta {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-10);
  padding-top: var(--space-5);
}

.hero__meta-item {
  color: var(--text-muted);
  display: flex;
  font-size: var(--type-sm);
  gap: var(--space-3);
  line-height: var(--leading-normal);
}

.hero__meta-key {
  color: var(--text-primary);
  flex: none;
  font-weight: var(--weight-medium);
  min-width: 9ch;
}


/* ==========================================================================
   10. CAPABILITY STRIP
   ========================================================================== */

.capability {
  background-color: var(--surface);
  border-block: var(--rule-hairline) solid var(--border-subtle);
}

.capability__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  margin-top: var(--space-8);
}

.capability__item {
  border-top: 2px solid var(--border-subtle);
  padding-top: var(--space-4);
}

.capability__item:first-child {
  border-top-color: var(--action-primary);
}

.capability__label {
  align-items: baseline;
  display: flex;
  gap: var(--space-3);
}

.capability__num {
  color: var(--action-primary);
  font-family: var(--font-body);
  font-size: var(--type-xs);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
  letter-spacing: 0.1em;
}

.capability__title {
  font-size: var(--type-md);
  font-weight: var(--weight-bold);
  letter-spacing: -0.015em;
}

.capability__text {
  color: var(--text-muted);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-2);
}


/* ==========================================================================
   11. WORK — CASE STUDY CARDS
   ========================================================================== */

.work__notice {
  align-items: flex-start;
  background-color: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--action-primary);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-10);
  padding: var(--space-5);
}

.work__notice-title {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: var(--type-base);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
}

.work__notice-text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  max-width: 72ch;
}

.work__grid {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
}

.case {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  border-top-width: 4px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
}

.case:hover {
  background-color: var(--surface-raised);
  border-color: var(--border-interactive);
  transform: translateY(-2px);
}

.case--terra {
  border-top-color: var(--case-terra-accent);
}

.case--iron {
  border-top-color: var(--case-iron-accent);
}

.case--lumen {
  border-top-color: var(--case-lumen-accent);
}

.case--maison {
  border-top-color: var(--case-maison-accent);
}

.case__art {
  background-color: var(--surface-inset);
  border-bottom: 1px solid var(--border-subtle);
  padding: var(--space-5);
}

.case__svg {
  display: block;
  height: auto;
  width: 100%;
}

/* Art fills. Every shape class resolves through a semantic role, so the
   four abstractions differ only by which case modifier wraps them. */
.art-field {
  fill: var(--surface-raised);
}

.art-line {
  fill: var(--border-subtle);
}

.art-ink {
  fill: var(--border-interactive);
}

.art-stroke {
  fill: none;
  stroke: var(--border-interactive);
  stroke-width: 1.5;
}

.case--terra .art-accent { fill: var(--case-terra-accent); }
.case--terra .art-second { fill: var(--case-terra-graphic); }
.case--terra .art-accent-stroke { fill: none; stroke: var(--case-terra-accent); stroke-width: 1.5; }

.case--iron .art-accent { fill: var(--case-iron-accent); }
.case--iron .art-second { fill: var(--case-iron-graphic); }
.case--iron .art-accent-stroke { fill: none; stroke: var(--case-iron-accent); stroke-width: 1.5; }

.case--lumen .art-accent { fill: var(--case-lumen-accent); }
.case--lumen .art-second { fill: var(--case-lumen-graphic); }
.case--lumen .art-accent-stroke { fill: none; stroke: var(--case-lumen-accent); stroke-width: 1.5; }

.case--maison .art-accent { fill: var(--case-maison-accent); }
.case--maison .art-second { fill: var(--case-maison-graphic); }
.case--maison .art-accent-stroke { fill: none; stroke: var(--case-maison-accent); stroke-width: 1.5; }

.case__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-5) var(--space-6);
}

.case__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.case__tags {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.case__vertical {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.case__name {
  font-size: var(--type-xl);
  font-weight: var(--weight-black);
  letter-spacing: -0.03em;
  line-height: var(--leading-snug);
}

/* The dot is a glyph on --surface, not a rule on --bg, so it binds the
   -dot role rather than the -accent one. Each card keeps its own demo's
   color; all four now clear 3:1 on the card (4.04 / 8.38 / 4.89 / 9.68). */
.case--terra .case__name-dot { color: var(--case-terra-dot); }
.case--iron .case__name-dot { color: var(--case-iron-dot); }
.case--lumen .case__name-dot { color: var(--case-lumen-dot); }
.case--maison .case__name-dot { color: var(--case-maison-dot); }

.case__problem {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
}

.case__subhead {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.case__specs {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.case__specs li {
  color: var(--text-secondary);
  display: flex;
  font-size: var(--type-base);
  gap: var(--space-3);
  line-height: var(--leading-normal);
}

.case__specs li::before {
  color: var(--action-primary);
  content: "\002B";
  flex: none;
  font-weight: var(--weight-bold);
  line-height: inherit;
}

.case__notes {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-top: var(--space-4);
}

.case__notes-row {
  display: flex;
  gap: var(--space-3);
}

.case__notes dt {
  color: var(--text-muted);
  flex: none;
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.1em;
  line-height: var(--leading-loose);
  text-transform: uppercase;
  width: 7ch;
}

.case__notes dd {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-normal);
  margin: 0;
}

.case__foot {
  margin-top: auto;
  padding-top: var(--space-5);
}

.case__link {
  width: 100%;
}

.case__url {
  color: var(--text-muted);
  display: block;
  font-size: var(--type-xs);
  letter-spacing: 0.04em;
  margin-top: var(--space-3);
  overflow-wrap: anywhere;
}

/* --- checkable facts block (this is where social proof would sit) ------ */

.proof {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  margin-top: var(--space-16);
  padding-top: var(--space-10);
}

.proof__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  margin-top: var(--space-8);
}

.proof__item {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: var(--space-5);
}

.proof__check {
  color: var(--action-primary);
  display: block;
  height: 20px;
  margin-bottom: var(--space-3);
  width: 20px;
}

.proof__title {
  font-size: var(--type-base);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
}

.proof__text {
  color: var(--text-muted);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-2);
}


/* ==========================================================================
   12. WHAT YOU GET
   ========================================================================== */

.deliver__grid {
  counter-reset: deliver;
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
  margin-top: var(--space-8);
}

.deliver__item {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  counter-increment: deliver;
  display: grid;
  gap: var(--space-2) var(--space-5);
  grid-template-columns: auto 1fr;
  padding-block: var(--space-5);
}

.deliver__item:last-child {
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
}

.deliver__item::before {
  color: var(--action-primary);
  content: counter(deliver, decimal-leading-zero);
  font-family: var(--font-body);
  font-size: var(--type-sm);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
  grid-row: span 2;
  letter-spacing: 0.08em;
  line-height: var(--leading-relaxed);
  padding-top: var(--space-px);
}

.deliver__title {
  font-size: var(--type-md);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.015em;
}

.deliver__text {
  color: var(--text-muted);
  font-size: var(--type-base);
  grid-column: 2;
  line-height: var(--leading-relaxed);
  max-width: 68ch;
}


/* ==========================================================================
   13. PRICING
   ========================================================================== */

.pricing__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  margin-top: var(--space-10);
}

.tier {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  padding: var(--space-6) var(--space-5);
  position: relative;
}

.tier--featured {
  background-color: var(--surface-raised);
  border-color: var(--action-primary);
  border-width: 2px;
}

.tier__flag {
  background-color: var(--action-primary);
  border-radius: var(--radius-full);
  color: var(--on-action-primary);
  font-size: var(--type-xs);
  font-weight: var(--weight-bold);
  left: var(--space-5);
  letter-spacing: 0.1em;
  line-height: var(--leading-tight);
  padding: var(--space-2) var(--space-3);
  position: absolute;
  text-transform: uppercase;
  top: 0;
  transform: translateY(-50%);
}

.tier__name {
  font-size: var(--type-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
}

.tier__price {
  align-items: baseline;
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.tier__amount {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: var(--type-4xl);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-black);
  letter-spacing: -0.04em;
  line-height: var(--leading-tight);
}

.tier--featured .tier__amount {
  color: var(--text-max);
}

.tier__unit {
  color: var(--text-muted);
  font-size: var(--type-sm);
}

.tier__who {
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
  color: var(--text-emphasis);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-4);
  padding-bottom: var(--space-5);
}

.tier__scope {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.12em;
  margin-top: var(--space-5);
  text-transform: uppercase;
}

.tier__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.tier__list li {
  color: var(--text-secondary);
  display: flex;
  font-size: var(--type-base);
  gap: var(--space-3);
  line-height: var(--leading-normal);
}

.tier__tick {
  color: var(--action-primary);
  flex: none;
  height: 16px;
  margin-top: 4px;
  width: 16px;
}

.tier__time {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  color: var(--text-muted);
  font-size: var(--type-sm);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
}

.tier__time strong {
  color: var(--text-primary);
}

.tier__cta {
  margin-top: var(--space-5);
  width: 100%;
}

.pricing__terms {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  margin-top: var(--space-10);
}

.terms-card {
  background-color: var(--surface-inset);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
}

.terms-card--guarantee {
  border: 1px solid var(--action-primary);
}

.terms-card__title {
  font-size: var(--type-md);
  font-weight: var(--weight-bold);
  letter-spacing: -0.015em;
}

.terms-card__text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-3);
}

.terms-card__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.terms-card__list li {
  color: var(--text-muted);
  display: flex;
  font-size: var(--type-base);
  gap: var(--space-3);
  line-height: var(--leading-normal);
}

.terms-card__list li::before {
  color: var(--text-muted);
  content: "\2014";
  flex: none;
}


/* ==========================================================================
   14. PROCESS
   ========================================================================== */

.process__list {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
  margin-top: var(--space-10);
  position: relative;
}

.step {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: auto 1fr;
  position: relative;
}

.step__marker {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.step__num {
  align-items: center;
  background-color: var(--surface-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  color: var(--action-primary);
  display: flex;
  flex: none;
  font-family: var(--font-body);
  font-size: var(--type-sm);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
  height: 44px;
  justify-content: center;
  width: 44px;
}

.step__spine {
  background-color: var(--border-subtle);
  flex: 1;
  min-height: var(--space-6);
  width: 1px;
}

.step:last-child .step__spine {
  display: none;
}

.step__body {
  padding-bottom: var(--space-2);
}

.step__when {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.step__title {
  font-size: var(--type-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  margin-top: var(--space-2);
}

.step__split {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
  margin-top: var(--space-4);
}

.step__role {
  border-top: 2px solid var(--border-subtle);
  padding-top: var(--space-3);
}

.step__role--mine {
  border-top-color: var(--action-primary);
}

.step__role-label {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.step__role-text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-2);
}

.process__total {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  margin-top: var(--space-10);
  padding: var(--space-5);
}

.process__total-text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
}

.process__total-text strong {
  color: var(--action-primary);
}


/* ==========================================================================
   15. FAQ
   ========================================================================== */

.faq__list {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  margin-top: var(--space-8);
  max-width: var(--container-narrow);
}

.faq__item {
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
}

.faq__trigger {
  align-items: flex-start;
  color: var(--text-primary);
  display: flex;
  font-family: var(--font-display);
  font-size: var(--type-md);
  font-weight: var(--weight-semibold);
  gap: var(--space-4);
  justify-content: space-between;
  letter-spacing: -0.015em;
  line-height: var(--leading-normal);
  min-height: 52px;
  padding-block: var(--space-4);
  text-align: left;
  transition: color var(--dur-fast) var(--ease-out);
  width: 100%;
}

.faq__trigger:hover {
  color: var(--action-primary);
}

.faq__icon {
  flex: none;
  height: 20px;
  margin-top: 3px;
  stroke: currentColor;
  transition: transform var(--dur-base) var(--ease-out);
  width: 20px;
}

.faq__trigger[aria-expanded="true"] {
  color: var(--action-primary);
}

.faq__trigger[aria-expanded="true"] .faq__icon {
  transform: rotate(45deg);
}

.faq__panel {
  padding-bottom: var(--space-6);
  padding-right: var(--space-8);
}

.faq__panel[hidden] {
  display: none;
}

.faq__panel p + p {
  margin-top: var(--space-3);
}

.faq__panel p {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  max-width: 68ch;
}

.faq__foot {
  color: var(--text-muted);
  font-size: var(--type-base);
  margin-top: var(--space-6);
  max-width: var(--container-prose);
}


/* ==========================================================================
   16. CONTACT
   ========================================================================== */

.contact__layout {
  display: grid;
  gap: var(--space-10);
  grid-template-columns: 1fr;
  margin-top: var(--space-8);
}

/* Grid items default to min-width: auto, so the long email address in
   .contact__mail stretched the column past a 320px screen. */
.contact__layout > * {
  min-width: 0;
}

.contact__direct {
  background-color: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
}

.contact__direct-title {
  font-size: var(--type-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
}

.contact__direct-text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-3);
}

.contact__mail {
  margin-top: var(--space-5);
  overflow-wrap: anywhere;
  width: 100%;
}

.contact__address {
  color: var(--text-muted);
  display: block;
  font-size: var(--type-sm);
  margin-top: var(--space-4);
  overflow-wrap: anywhere;
}

.contact__promises {
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
}

.contact__promises li {
  color: var(--text-muted);
  display: flex;
  font-size: var(--type-base);
  gap: var(--space-3);
  line-height: var(--leading-normal);
}

.contact__promises li::before {
  color: var(--action-primary);
  content: "\2192";
  flex: none;
}


/* --- Field primitive (MASTER section 6) -------------------------------- */

.form {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
}

.form[hidden] {
  display: none;
}

.form__legend {
  font-family: var(--font-display);
  font-size: var(--type-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  padding: 0;
}

.form__intro {
  color: var(--text-muted);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-3);
}

.form__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: 1fr;
  margin-top: var(--space-6);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field--wide {
  grid-column: 1 / -1;
}

.field__label {
  color: var(--text-primary);
  font-size: var(--type-sm);
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
}

.field__hint {
  color: var(--text-muted);
  font-size: var(--type-xs);
  font-weight: var(--weight-regular);
}

.field__control {
  background-color: var(--surface-inset);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--type-base);
  line-height: var(--leading-normal);
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
  width: 100%;
}

.field__control::placeholder {
  color: var(--text-muted);
  opacity: 1;
}

.field__control:hover {
  border-color: var(--border-interactive-hover);
}

.field__control:focus-visible {
  border-color: var(--action-primary);
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.field__control[disabled] {
  cursor: not-allowed;
  opacity: 0.45;
}

.field__control[readonly] {
  background-color: var(--surface-raised);
}

textarea.field__control {
  line-height: var(--leading-relaxed);
  min-height: var(--space-32);
  resize: vertical;
}

select.field__control {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position:
    calc(100% - 20px) calc(50% + 2px),
    calc(100% - 14px) calc(50% + 2px);
  background-repeat: no-repeat;
  background-size: 6px 6px, 6px 6px;
  cursor: pointer;
  padding-right: var(--space-10);
}

/* The hub group in tokens.json ships no danger role, and MASTER section 8
   says not to invent a primitive for it. Validation messages therefore use
   the accent, which already carries "look here" on this page, and never
   rely on color alone: the message is text, tied to the field by
   aria-describedby, and announced with role="alert". */
.field__error {
  color: var(--action-primary);
  font-size: var(--type-sm);
  font-weight: var(--weight-medium);
  line-height: var(--leading-normal);
}

.field__error[hidden] {
  display: none;
}

.field__control[aria-invalid="true"] {
  border-color: var(--action-primary);
}

.form__foot {
  align-items: flex-start;
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
}

.form__note {
  color: var(--text-muted);
  font-size: var(--type-sm);
  line-height: var(--leading-relaxed);
}

.form__submit {
  width: 100%;
}

.form__success {
  align-items: flex-start;
  background-color: var(--surface-inset);
  border: 1px solid var(--action-primary);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-5);
}

.form__success[hidden] {
  display: none;
}

.form__success-icon {
  color: var(--action-primary);
  height: var(--space-8);
  width: var(--space-8);
}

.form__success-title {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: var(--type-lg);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  line-height: var(--leading-snug);
}

.form__success-text {
  color: var(--text-secondary);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
}

/* --- Form result panels (cloned from <template id="form-panels">) ------- */

.form__copy {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
  width: 100%;
}

.form__copy .btn {
  flex: 1 1 auto;
}

.form__copied {
  color: var(--text-primary);
  font-size: var(--type-sm);
  font-weight: var(--weight-medium);
  line-height: var(--leading-normal);
  overflow-wrap: anywhere;
}

/* Empty until a copy button reports back. It stays rendered (a live region
   has to exist before its text changes to be announced), so the panel's
   flex gap is cancelled instead of hiding it. */
.form__copied:empty {
  margin-top: calc(var(--space-3) * -1);
}

.form__addr {
  overflow-wrap: anywhere;
}

.form__manual {
  margin-top: var(--space-1);
}

.form__manual[hidden] {
  display: none;
}

.form__panel-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-5);
}

.form__textbtn {
  color: var(--text-secondary);
  font-size: var(--type-sm);
  min-height: 44px;
  padding: 0;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--dur-fast) var(--ease-out);
}

.form__textbtn:hover {
  color: var(--text-primary);
  text-decoration-thickness: 2px;
}

/* A failed send in live mode. Sits inside the form, above the submit row,
   so the typed answers stay on screen beside it. */
.form__alert {
  background-color: var(--surface-inset);
  border: 1px solid var(--action-primary);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding: var(--space-4);
}

.form__alert[hidden] {
  display: none;
}

.form__alert-text {
  color: var(--text-primary);
  font-size: var(--type-base);
  line-height: var(--leading-normal);
}

/* The pop-up's form sits on the dialog's own surface. */
.form.form--bare {
  background-color: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
}

.form--bare .form__intro {
  margin-top: 0;
}


/* ==========================================================================
   16b. QUOTE DIALOG
   Native <dialog> opened with showModal() from the pricing CTAs. Phones get
   a near-full-screen sheet with a 16px gutter; the header stays put and the
   body scrolls, so the close button is always in reach.
   ========================================================================== */

html.is-dialog-open {
  overflow: hidden;
  scrollbar-gutter: stable;
}

/* With html clipped, body's overflow-x: hidden would make body its own
   scroll box and carry the sticky header off-screen while the pop-up is
   open. html still clips sideways, so body can let go. */
html.is-dialog-open body {
  overflow-x: visible;
}

.quote {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  color: var(--text-secondary);
  margin: auto; /* the reset's margin: 0 would pin it to the top-left */
  max-height: calc(100vh - var(--space-8));
  max-height: calc(100dvh - var(--space-8));
  max-width: none;
  overflow: hidden;
  padding: 0;
  width: calc(100% - var(--space-8));
}

/* Browsers without <dialog> never get the pop-up (the CTAs stay mailto:
   links), and they do not hide a closed dialog on their own. */
.quote:not([open]) {
  display: none;
}

.quote[open] {
  animation: quote-in var(--dur-base) var(--ease-out);
  display: flex;
  flex-direction: column;
}

.quote::backdrop {
  background-color: var(--bg);
  opacity: 0.8;
}

.quote[open]::backdrop {
  animation: quote-fade-in var(--dur-base) var(--ease-out);
}

/* Exit runs at 75% of the entrance, on the exit easing (MASTER section 4). */
.quote.is-closing {
  animation: quote-out var(--dur-fast) var(--ease-in) forwards;
}

.quote.is-closing::backdrop {
  animation: quote-fade-out var(--dur-fast) var(--ease-in) forwards;
}

.quote__head {
  align-items: flex-start;
  background-color: var(--surface);
  border-bottom: var(--rule-hairline) solid var(--border-subtle);
  display: flex;
  flex: none;
  gap: var(--space-3);
  justify-content: space-between;
  padding: var(--space-4) var(--space-3) var(--space-4) var(--space-5);
}

.quote__heading {
  min-width: 0;
  padding-top: var(--space-1);
}

.quote__title {
  font-size: var(--type-lg);
  letter-spacing: -0.02em;
}

.quote__price {
  color: var(--text-muted);
  font-size: var(--type-sm);
  line-height: var(--leading-normal);
  margin-top: var(--space-1);
}

.quote__price strong {
  color: var(--text-primary);
}

.quote__close {
  align-items: center;
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  display: inline-flex;
  flex: none;
  height: 44px;
  justify-content: center;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
  width: 44px;
}

.quote__close:hover {
  background-color: var(--surface-raised);
  color: var(--text-primary);
}

.quote__close svg {
  height: 20px;
  width: 20px;
}

.quote__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-5);
}

@keyframes quote-in {
  from {
    opacity: 0;
    transform: translateY(var(--space-4)) scale(0.98);
  }
}

@keyframes quote-out {
  to {
    opacity: 0;
    transform: translateY(var(--space-2)) scale(0.98);
  }
}

@keyframes quote-fade-in {
  from { opacity: 0; }
}

@keyframes quote-fade-out {
  to { opacity: 0; }
}

@media (min-width: 480px) {

  .quote__title {
    font-size: var(--type-xl);
  }
}

@media (min-width: 768px) {

  .quote {
    max-height: calc(100vh - var(--space-16));
    max-height: calc(100dvh - var(--space-16));
    width: min(calc(100% - var(--space-16)), var(--container-prose));
  }

  .quote__head {
    padding: var(--space-5) var(--space-5) var(--space-5) var(--space-8);
  }

  .quote__title {
    font-size: var(--type-2xl);
  }

  .quote__body {
    padding: var(--space-6) var(--space-8) var(--space-8);
  }
}

@media (prefers-reduced-motion: reduce) {

  .quote[open],
  .quote[open]::backdrop,
  .quote.is-closing,
  .quote.is-closing::backdrop {
    animation: none;
  }
}

@media print {

  .quote {
    display: none !important;
  }
}


/* ==========================================================================
   17. FOOTER
   ========================================================================== */

.site-footer {
  background-color: var(--surface);
  border-top: var(--rule-hairline) solid var(--border-subtle);
  padding-block: var(--space-12) var(--space-8);
}

.site-footer__grid {
  display: grid;
  gap: var(--space-8);
  grid-template-columns: 1fr;
}

.site-footer__blurb {
  color: var(--text-muted);
  font-size: var(--type-base);
  line-height: var(--leading-relaxed);
  margin-top: var(--space-4);
  max-width: 46ch;
}

.site-footer__heading {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: var(--type-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.site-footer__list {
  display: flex;
  flex-direction: column;
  margin-top: var(--space-3);
}

.site-footer__list a {
  align-items: center;
  color: var(--text-secondary);
  display: flex;
  font-size: var(--type-sm);
  min-height: 44px;
  text-decoration: none;
}

.site-footer__list a:hover {
  color: var(--action-primary);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.site-footer__bottom {
  align-items: flex-start;
  border-top: var(--rule-hairline) solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-10);
  padding-top: var(--space-6);
}

.site-footer__legal {
  color: var(--text-muted);
  font-size: var(--type-xs);
  line-height: var(--leading-relaxed);
  max-width: 76ch;
}


/* ==========================================================================
   18. SCROLL REVEAL
   Applied only when JS is running, so the page is fully readable without it.
   ========================================================================== */

.js .reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}


/* ==========================================================================
   19. IMAGERY
   All art on this page is hand-authored inline SVG plus layered CSS
   gradients. There are no image files anywhere in this build.

   --- PHOTO SWAP POINTS -------------------------------------------------
   Replace the gradient/SVG art on these selectors with real photography:
     .hero__backdrop   -> 1920x1080 backdrop, dark, low detail behind type
     .hero__grid       -> remove entirely once a photo backdrop is in
     .case__art        -> 800x600 each, one screenshot per demo site
     .brand__mark      -> keep as SVG; it is the logotype, not an image
   Each swap point already reserves its own aspect ratio, so dropping a
   real asset in does not move the layout. Set width and height attributes
   on the replacement and add loading="lazy" to anything below the fold.
   ---------------------------------------------------------------------- */


/* ==========================================================================
   20. RESPONSIVE — mobile-first, at the token breakpoints
   scales.breakpoint: sm 480, md 768, lg 1024, xl 1280, 2xl 1536
   ========================================================================== */

/* --- sm: 480px -------------------------------------------------------- */
@media (min-width: 480px) {

  :root {
    --gutter: var(--space-6);
  }

  .hero__name {
    font-size: var(--type-4xl);
  }

  .hero__role {
    font-size: var(--type-xl);
  }

  .hero__actions {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .hero__actions .btn {
    flex: 1 1 auto;
    min-width: var(--space-40);
  }

  .t-h2 {
    font-size: var(--type-3xl);
  }

  .capability__grid {
    gap: var(--space-6) var(--space-8);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .proof__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .step__split {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .case__link {
    width: auto;
  }

  .form__submit,
  .contact__mail {
    width: auto;
  }
}

/* --- md: 768px -------------------------------------------------------- */
@media (min-width: 768px) {

  :root {
    --gutter: var(--space-8);
    --header-height: calc(var(--space-16) + var(--space-2));
  }

  .section {
    padding-block: var(--space-20);
  }

  .section--tight {
    padding-block: var(--space-16);
  }

  .hero {
    padding-block: var(--space-20) var(--space-20);
  }

  .hero__name {
    font-size: var(--type-5xl);
  }

  .hero__role {
    font-size: var(--type-2xl);
    max-width: 13.77em; /* 24ch in Archivo 500 */
  }

  .hero__lede {
    font-size: var(--type-lg);
  }

  .hero__meta {
    flex-direction: row;
    gap: var(--space-10);
  }

  .hero__meta-item {
    flex-direction: column;
    gap: var(--space-1);
  }

  .hero__meta-key {
    color: var(--text-muted);
    font-size: var(--type-xs);
    letter-spacing: 0.12em;
    min-width: 0;
    text-transform: uppercase;
  }

  .hero__meta-val {
    color: var(--text-secondary);
  }

  .t-h2 {
    font-size: var(--type-4xl);
  }

  .t-h3 {
    font-size: var(--type-2xl);
  }

  .t-lead {
    font-size: var(--type-lg);
  }

  .work__grid {
    gap: var(--space-8);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .case__body {
    padding: var(--space-8) var(--space-6);
  }

  .case__name {
    font-size: var(--type-2xl);
  }

  .work__notice {
    flex-direction: row;
    gap: var(--space-6);
    padding: var(--space-6);
  }

  .work__notice-title {
    flex: none;
    width: 18ch;
  }

  .proof__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .deliver__grid {
    column-gap: var(--space-10);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .pricing__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .tier--premium {
    grid-column: span 2;
  }

  .pricing__terms {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .terms-card {
    padding: var(--space-8) var(--space-6);
  }

  .process__list {
    gap: var(--space-10);
  }

  .step {
    gap: var(--space-6);
  }

  .step__title {
    font-size: var(--type-xl);
  }

  .faq__trigger {
    font-size: var(--type-lg);
    padding-block: var(--space-5);
  }

  .contact__layout {
    align-items: start;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--space-8);
  }

  .form {
    padding: var(--space-8) var(--space-6);
  }

  .form__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .form__foot {
    align-items: center;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
  }

  /* Note and button share a row only when both fit; otherwise the button
     drops below at full width instead of squeezing the note. */
  .form__foot .form__note {
    flex: 1 1 16rem;
  }

  .form__foot .form__submit {
    flex: 1 0 auto;
    white-space: nowrap;
    width: auto;
  }

  .form__note {
    max-width: 46ch;
  }

  .site-footer__grid {
    grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
    gap: var(--space-10);
  }

  .site-footer__bottom {
    align-items: center;
    flex-direction: row;
    justify-content: space-between;
  }
}

/* --- lg: 1024px ------------------------------------------------------- */
@media (min-width: 1024px) {

  :root {
    --gutter: var(--space-10);
  }

  .section {
    padding-block: var(--space-24);
  }

  .section--tight {
    padding-block: var(--space-20);
  }

  .nav-toggle {
    display: none;
  }

  .site-nav,
  .site-nav.is-open {
    background-color: transparent;
    border: 0;
    display: block;
    padding: 0;
    position: static;
  }

  .site-nav__list {
    align-items: center;
    flex-direction: row;
    gap: var(--space-1);
    max-width: none;
    padding-inline: 0;
    width: auto;
  }

  .site-nav__link {
    border-bottom: 0;
    border-radius: var(--radius-md);
    font-size: var(--type-sm);
    min-height: 44px;
    padding-inline: var(--space-3);
    width: auto;
  }

  .site-nav__link:hover {
    background-color: var(--surface-raised);
  }

  .site-header__cta {
    display: inline-flex;
  }

  .hero {
    padding-block: var(--space-24) var(--space-24);
  }

  .hero__name {
    font-size: var(--type-6xl);
  }

  .hero__role {
    font-size: var(--type-3xl);
  }

  .hero__inner {
    max-width: 76ch;
  }

  .section__head {
    margin-bottom: var(--space-12);
  }

  .capability__grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-8);
  }

  .proof__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .pricing__grid {
    align-items: start;
    gap: var(--space-6);
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .tier--premium {
    grid-column: auto;
  }

  .tier--featured {
    margin-block: calc(var(--space-4) * -1);
    padding-block: var(--space-10);
  }

  .tier {
    padding-inline: var(--space-6);
  }

  .process__list {
    gap: var(--space-12);
  }

  .step {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .step__split {
    gap: var(--space-8);
  }

  .contact__layout {
    gap: var(--space-12);
  }

  .site-footer__grid {
    grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
  }
}

/* --- xl: 1280px ------------------------------------------------------- */
@media (min-width: 1280px) {

  :root {
    --gutter: var(--space-12);
  }

  .work__grid {
    gap: var(--space-10);
  }

  .case__body {
    padding: var(--space-10) var(--space-8);
  }

  .deliver__grid {
    column-gap: var(--space-16);
  }
}

/* --- 2xl: 1536px ------------------------------------------------------ */
@media (min-width: 1536px) {

  .section {
    padding-block: var(--space-32);
  }

  .hero {
    padding-block: var(--space-32) var(--space-24);
  }
}


/* ==========================================================================
   21. REDUCED MOTION
   Disables every transform and animation, per BRIEF section 4.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-delay: 0ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-delay: 0ms !important;
    transition-duration: 1ms !important;
  }

  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .case:hover,
  .btn:active,
  .btn--primary:active,
  .btn--secondary:active,
  .btn--ghost:active {
    transform: none;
  }

  .btn:hover .btn__arrow {
    transform: none;
  }

  /* The FAQ icon's 45deg turn and the nav-toggle's X are state shape cues,
     not decoration: they are what distinguishes open from closed. The
     transition-duration override above already makes both instantaneous,
     which is what prefers-reduced-motion asks for, so the end states stay. */

  .grain {
    display: none;
  }
}


/* ==========================================================================
   22. PRINT
   ========================================================================== */

/* Browsers suppress background painting when printing, so the dark surfaces
   this page is built on simply do not arrive on paper. Every colour the
   screen design sets for a dark ground therefore has to be reset here, or
   the sheet prints near-white text on white. --bg is the near-black ink the
   page normally paints behind everything, which makes it the correct print
   foreground; --on-action-primary means "label sitting on the lime button"
   (MASTER section 6, Button) and is not a page-text role. */

@media print {

  .site-header,
  .site-nav,
  .nav-toggle,
  .grain,
  .hero__backdrop,
  .skip-link,
  .case__art,
  .form {
    display: none !important;
  }

  html,
  body {
    background: none !important;
  }

  body,
  h1, h2, h3, h4,
  p, li, dt, dd, span, strong, code,
  a, button, .btn {
    color: var(--bg) !important;
  }

  .section--surface,
  .capability,
  .site-footer,
  .case,
  .tier,
  .terms-card,
  .proof__item,
  .contact__direct,
  .process__total,
  .work__notice,
  .form__success,
  .step__num,
  .tier__flag,
  .btn {
    background: none !important;
    border-color: var(--border-interactive) !important;
  }

  .proof__check,
  .tier__tick,
  .form__success-icon,
  .faq__icon,
  .btn__arrow,
  .step__num {
    color: var(--bg) !important;
  }

  .brand__mark-glyph {
    fill: var(--bg) !important;
  }

  .brand__mark-frame {
    stroke: var(--border-interactive) !important;
  }

  /* Script collapses all but the first answer; on paper they all belong. */
  .faq__panel[hidden] {
    display: block !important;
  }

  .section {
    padding-block: var(--space-6);
  }
}

/* Cross-link between the build pricing and the SEO & AEO monthly pricing */
.pricing-crosslink{
  margin-top: var(--space-4);
  max-width: var(--container-prose);
  color: var(--text-secondary);
  font-size: var(--type-base);
}
.pricing-crosslink a{
  color: var(--action-primary);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  font-weight: 500;
}
.pricing-crosslink a:hover{ color: var(--action-primary-hover); }

/* Service switcher — Web Design / SEO & AEO, present on both pages */
.svc-switch{
  display:inline-flex; align-items:center; gap:2px; padding:2px;
  margin:var(--space-2) var(--space-4) var(--space-1);
  background:var(--surface-raised);
  border:1px solid var(--border-subtle);
  border-radius:999px; width:max-content;
}
.svc-switch__item{
  display:inline-flex; align-items:center; min-height:44px; padding:0 14px;
  border-radius:999px; font-size:var(--type-sm); font-weight:500; line-height:1;
  color:var(--text-secondary); text-decoration:none; white-space:nowrap;
  transition:color .16s ease, background-color .16s ease;
}
.svc-switch__item:hover{ color:var(--text-primary); }
.svc-switch__item.is-active{ background:var(--action-primary); color:var(--on-action-primary); font-weight:600; }
@media (min-width:1024px){
  .site-nav{ display:flex; align-items:center; gap:var(--space-4); }
  .svc-switch{ margin:0; }
}


/* ==========================================================================
   23. LOAD CHOREOGRAPHY, AMBIENT MOTION, VIEW TRANSITIONS
   Progressive enhancement only. The hero's text ([data-rise]: eyebrow,
   headline, lede) enters by CSS from the very first frame, so it never
   waits for a script download. An inline <head> script adds .is-loading,
   which hides the other [data-load] hero pieces; assets/js/portfolio.js
   removes the class on its first frame and builds them in under a second
   at the hub's 240ms / 40ms-stagger tempo. If the script never runs, the
   failsafe animation shows everything at 2.5s. seo-aeo/styles.css carries
   the same block, so the two pages move as one site.
   ========================================================================== */

@view-transition {
  navigation: auto;
}

/* Shared by both service pages, so they stay put while the content
   crossfades. Scoped to the header: the footer carries a second .brand,
   and a transition name must be unique on the page. */
.site-header .brand {
  view-transition-name: site-brand;
}

.svc-switch {
  view-transition-name: service-switch;
}

/* "Stays put" needs the switcher at the same x on both pages. Under
   space-between it floated with each page's link count and CTA width, so
   on desktop it now sits a fixed gap after the brand while the links take
   the free space; from xl up the brand also reserves a width that fits
   either page's role line. */
@media (min-width: 1024px) {
  .site-nav {
    flex: 1 1 auto;
  }

  .site-nav__list {
    margin-inline: auto 0;
  }
}

@media (min-width: 1280px) {
  .site-header .brand {
    min-width: 14rem;
  }
}

::view-transition-group(*) {
  animation-duration: var(--dur-slow);
  animation-timing-function: var(--ease-out);
}

/* The shared pieces keep their snapshot size, so a small width change (the
   active pill's weight, the brand's role line) never scales their text. */
::view-transition-old(site-brand),
::view-transition-new(site-brand),
::view-transition-old(service-switch),
::view-transition-new(service-switch) {
  height: 100%;
  object-fit: none;
  object-position: left;
  width: auto;
}

.is-loading [data-load] {
  opacity: 0;
  animation: load-failsafe 0s 2.5s forwards;
}

/* A view-transition arrival from the sibling page captures the hero as it
   stands, so nothing is hidden while the transition runs; the script then
   skips the build and the content really crossfades to content. */
.is-loading:active-view-transition [data-load] {
  opacity: 1;
}

@keyframes load-failsafe {
  to { opacity: 1; }
}

/* The hero text counts as painted in the first frame. The lede is the
   Largest Contentful Paint (on /seo-aeo/ at desktop widths a headline line
   is), and Chrome does not count text held at opacity 0: hidden until the
   script arrived, it cost up to a second of LCP on a slow phone. So the
   lede never fades, it only rises, and the headline lines are hidden by
   clip-path, which LCP ignores, wiping in top to bottom so a wrapped line
   never shows before the one above it. The delays cover the time between
   these animations starting (first style) and the first frame reaching the
   screen, so the wipe is seen, not finished off-screen. Only the small
   eyebrow fades. .is-rising comes from the <head> script, so without
   JavaScript the hero is simply static. The same script removes it before
   the first frame (pagereveal) on a view-transition arrival from the
   sibling page and on a load already past 2.2s; the page script removes it
   whenever it skips its build (a slow load, a deep link), because the rest
   of the hero then appears at once and the headline must not land after
   it. */
@media (prefers-reduced-motion: no-preference) {
  .is-rising [data-rise="eyebrow"] {
    animation: load-rise-in var(--dur-base) var(--ease-out) 120ms backwards;
  }

  .is-rising [data-rise="title"] > * {
    animation: load-wipe var(--dur-slow) var(--ease-out) 160ms backwards;
  }

  .is-rising [data-rise="title"] > * + * {
    animation-delay: 200ms;
  }

  .is-rising [data-rise="lede"] {
    animation: load-rise var(--dur-slowest) var(--ease-out) backwards;
  }
}

@keyframes load-rise {
  from { transform: translateY(12px); }
}

@keyframes load-rise-in {
  from { opacity: 0; transform: translateY(12px); }
}

@keyframes load-wipe {
  from { clip-path: inset(-0.3em -0.15em 100%); transform: translateY(12px); }
  to { clip-path: inset(-0.3em -0.15em); transform: none; }
}

/* Sections that scroll-reveal while the hero is still building wait for
   it to land, instead of competing with it. */
.is-load-running .reveal {
  transition-delay: calc(var(--reveal-delay, 0ms) + 720ms);
}

.hero__rule {
  transform-origin: left center;
}

/* Count-up figures. While counting, data-hold carries the final figure into
   a zero-height ::before, so the box keeps that width in whichever font is
   live; tabular digits keep every step the same width. Nothing beside the
   number moves, even if a webfont swaps in mid-count. */
.count {
  display: inline-block;
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.count[data-hold]::before {
  content: attr(data-hold);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
}

.hero__canvas {
  color: var(--action-primary);
  height: 100%;
  inset: 0;
  -webkit-mask-image: linear-gradient(to bottom, var(--bg) 55%, transparent);
  mask-image: linear-gradient(to bottom, var(--bg) 55%, transparent);
  pointer-events: none;
  position: absolute;
  width: 100%;
}

/* Hover physics (fine pointers only). The script writes the cursor's offset
   from centre, -1..1, as --mx / --my and adds .is-leaning. While tracking,
   a short ease-out smooths each update; on leave the spring easing brings
   the element home with a slight overshoot. .lean-ready arrives on first
   hover, so a card keeps the reveal system's transition until it is shown. */
.btn.lean-ready {
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out),
              transform var(--dur-instant) var(--ease-out),
              translate var(--dur-slow) var(--ease-spring);
}

/* Primary CTAs lean toward the cursor. The script clamps the offset to the
   unit circle, so the pull tops out at 6px in any direction, diagonals
   included. The individual translate property leaves the :active scale
   intact. */
.btn.lean-ready.is-leaning {
  transition-duration: var(--dur-base), var(--dur-base), var(--dur-base), var(--dur-instant), var(--dur-fast);
  transition-timing-function: var(--ease-out);
  translate: calc(var(--mx, 0) * 6px) calc(var(--my, 0) * 6px);
}

/* Case cards tilt up to 4deg toward the cursor, with the spec's 2px lift. */
.js .case.lean-ready {
  transition: border-color var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out),
              transform var(--dur-slow) var(--ease-spring);
}

/* Once a card has leaned, its resting transform lists the same functions
   as the lean, so the spring-back interpolates each one in turn instead of
   decomposing a matrix and overshooting the perspective term. */
.js .case.lean-ready.is-visible {
  transform: perspective(1000px) translateY(0) rotateX(0deg) rotateY(0deg);
}

.js .case.lean-ready.is-leaning {
  transform: perspective(1000px) translateY(-2px)
             rotateX(calc(var(--my, 0) * -4deg)) rotateY(calc(var(--mx, 0) * 4deg));
  transition-duration: var(--dur-base), var(--dur-base), var(--dur-fast);
  transition-timing-function: var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {

  @view-transition {
    navigation: none;
  }

  .hero__canvas {
    display: none;
  }

  .btn.lean-ready.is-leaning {
    translate: none;
  }

  .js .case.lean-ready.is-visible,
  .js .case.lean-ready.is-leaning {
    transform: none;
  }
}


/* ==========================================================================
   24. SHOWCASE — AI-RENDERED PRODUCTS, TAKEN APART ON SCROLL
   Static first: without .can-scrub (no JavaScript, reduced motion) the
   section is a grid of four posters with their copy. With .can-scrub (set
   by the <head> script) the track is tall, the stage pins, the posters
   step aside for the canvas and one item's copy shows at a time;
   assets/js/portfolio.js draws the frames into the space the copy leaves
   and sets .is-active. The frames' background is --hub-ink-950 exactly
   (tools/showcase_frames.py), so the canvas has no visible edge.
   ========================================================================== */

.showcase {
  padding-block: var(--space-16) var(--space-12);
  position: relative;
}

.showcase__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-10);
}

.showcase__title {
  max-width: 22ch;
}

.showcase__note {
  color: var(--text-muted);
  font-size: var(--type-sm);
}

.showcase__track {
  margin-inline: auto;
  max-width: var(--container-wide);
  padding-inline: var(--gutter);
}

.showcase__canvas,
.showcase__progress {
  display: none;
}

.showcase__list {
  display: grid;
  gap: var(--space-12) var(--space-8);
  list-style: none;
  margin: 0;
  padding: 0;
}

.showcase__poster {
  aspect-ratio: 16 / 9;
  background-color: var(--bg);
  display: block;
  height: auto;
  width: 100%;
}

/* The copy is a size container, so the trade word can be sized from the
   width it actually has: RESTAURANTS, the longest, is 7.5em wide in
   Archivo and a little wider in the fallback font, and 12.4cqi keeps it
   inside its column either way. Browsers without container units keep the
   plain sizes. */
.showcase__copy {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.showcase__num {
  color: var(--action-primary);
  font-size: var(--type-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.16em;
}

.showcase__word {
  color: var(--text-max);
  font-family: var(--font-display);
  font-size: var(--type-3xl);
  font-weight: var(--weight-black);
  letter-spacing: -0.04em;
  line-height: 0.95;
  text-transform: uppercase;
}

.showcase__text {
  color: var(--text-secondary);
  max-width: 36ch;
}

.showcase__link {
  align-self: flex-start;
  margin-top: var(--space-2);
}

@media (min-width: 768px) {
  .showcase__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@supports (width: 1cqi) {
  .showcase__word {
    font-size: min(var(--type-4xl), 12.4cqi);
  }
}

/* ---- Scroll mode ---------------------------------------------------- */

.can-scrub .showcase {
  padding-bottom: 0;
}

/* Four stages of one screen each, plus the pinned stage itself. */
.can-scrub .showcase__track {
  height: 500vh;
  height: 500svh;
  max-width: none;
  padding-inline: 0;
  position: relative;
}

.can-scrub .showcase__stage {
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  position: sticky;
  top: 0;
}

.can-scrub .showcase__canvas {
  display: block;
  height: 100%;
  inset: 0;
  position: absolute;
  width: 100%;
}

.can-scrub .showcase__list {
  display: block;
  inset: 0;
  pointer-events: none;
  position: absolute;
}

/* Stacked (portrait): the copy sits at the bottom, the product above it.
   Inactive items only fade (never visibility: hidden), so a keyboard user
   can still Tab to their links; the script then scrolls that stage in. */
.can-scrub .showcase__item {
  bottom: max(var(--space-12), 7vh);
  bottom: max(var(--space-12), 7svh);
  left: var(--gutter);
  opacity: 0;
  position: absolute;
  right: calc(var(--gutter) + var(--space-6)); /* clear of the progress bar */
  transform: translateY(var(--space-4));
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

.can-scrub .showcase__item.is-active {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

/* Keyboard focus wins over scroll position: if the visitor scrolls on
   while a link in another item has keyboard focus, that item stays visible
   (and the stage's own copy steps back, out of the way of clicks too)
   until focus moves, so the focus ring is never lost. The script sets
   these classes only for focus that follows a Tab press; mouse and touch
   focus leave the stage alone. */
.can-scrub .showcase__list.has-kb-focus .showcase__item.is-active:not(.is-kb-focus) {
  opacity: 0;
  pointer-events: none;
}

.can-scrub .showcase__item.is-kb-focus {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}

/* The canvas replaces the posters; they stay in the accessibility tree
   for their alt text. */
.can-scrub .showcase__poster {
  block-size: 1px;
  clip-path: inset(50%);
  inline-size: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

.can-scrub .showcase__copy {
  margin-top: 0;
}

.can-scrub .showcase__word {
  font-size: clamp(2rem, 9vw, 3rem);
}

@supports (width: 1cqi) {
  .can-scrub .showcase__word {
    font-size: min(clamp(2.5rem, 11vw, 4rem), 12.4cqi);
  }
}

.can-scrub .showcase__progress {
  background-color: var(--border-subtle);
  bottom: 18vh;
  bottom: 18svh;
  display: block;
  position: absolute;
  right: var(--gutter);
  top: max(18vh, calc(var(--header-height) + var(--space-6)));
  top: max(18svh, calc(var(--header-height) + var(--space-6)));
  width: 2px;
}

.can-scrub .showcase__progress-fill {
  background-color: var(--action-primary);
  display: block;
  height: 100%;
  transform: scaleY(var(--showcase-progress, 0));
  transform-origin: top;
  width: 100%;
}

/* Side by side (any landscape screen from 560px wide; the script checks
   this same media query): the copy takes a column on the left, centred in
   the height the fixed header leaves, and the product fills the space
   between it and the progress bar. */
@media (min-aspect-ratio: 1/1) and (min-width: 560px) {
  .can-scrub .showcase__item {
    bottom: auto;
    left: max(var(--gutter), calc((100vw - var(--container-wide)) / 2 + var(--gutter)));
    right: auto;
    top: calc(var(--header-height) + (100% - var(--header-height)) / 2);
    transform: translateY(calc(-50% + var(--space-4)));
    width: min(34vw, 30rem);
  }

  .can-scrub .showcase__item.is-active {
    transform: translateY(-50%);
  }

  .can-scrub .showcase__item.is-kb-focus {
    transform: translateY(-50%);
  }

  .can-scrub .showcase__word {
    font-size: clamp(1.5rem, 3.4vw, 3.75rem);
  }
}

/* A short landscape screen (a phone on its side): tighter copy, so all of
   it fits between the header and the bottom edge. */
@media (min-aspect-ratio: 1/1) and (min-width: 560px) and (max-height: 420px) {
  .can-scrub .showcase__copy {
    gap: var(--space-1);
  }

  .can-scrub .showcase__text {
    font-size: var(--type-sm);
  }

  .can-scrub .showcase__link {
    margin-top: 0;
  }
}

/* The build film is a wide scene: on a landscape screen it runs across the
   stage above a caption at the bottom, unless the script finds it draws
   larger beside the caption (data-side, on short wide windows). */
@media (min-aspect-ratio: 1/1) and (min-width: 560px) {
  .can-scrub .showcase--build:not([data-side]) .showcase__item {
    bottom: max(var(--space-8), 5vh);
    bottom: max(var(--space-8), 5svh);
    left: max(var(--gutter), calc((100vw - var(--container-wide)) / 2 + var(--gutter)));
    right: calc(var(--gutter) + var(--space-6));
    top: auto;
    transform: translateY(var(--space-4));
    width: auto;
  }

  .can-scrub .showcase--build:not([data-side]) .showcase__item.is-active {
    transform: none;
  }

  .can-scrub .showcase--build:not([data-side]) .showcase__item.is-kb-focus {
    transform: none;
  }

  .can-scrub .showcase--build:not([data-side]) .showcase__copy {
    max-width: 44rem;
  }

  .can-scrub .showcase--build:not([data-side]) .showcase__word {
    font-size: clamp(1.75rem, 3vw, 3rem);
  }
}

/* The film's posters (no JavaScript, reduced motion) are whole frames of a
   scene that runs to its edges: fade their sides into the page, as the
   canvas does. */
.showcase--build .showcase__poster {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

@supports (width: 1cqi) {
  @media (min-aspect-ratio: 1/1) and (min-width: 560px) {
    .can-scrub .showcase__word {
      font-size: min(clamp(1.5rem, 4.6vw, 5.25rem), 12.4cqi);
    }
  }
}

@media print {
  .showcase {
    display: none;
  }
}
