/* Saucinco — ported from the Claude Design export "Website Design Sau5.ai"
   (design/saucinco.css, 24 Aug 2026). That export is the source of truth for
   layout, type and shape. Added here: the pieces the generator needs that a
   single-page mockup did not have — forms, legal outlines, the mobile drawer,
   the language switch and the spec table.

   Two deliberate departures from the export, both documented inline:
     1. Instrument Sans is self-hosted, not pulled from the Google Fonts CDN.
        The rest of the site makes no external requests; keep it that way.
     2. Mocha is 3.36:1 on the page ground. The export sets small text in it
        (.label b, .num, .role, the wordmark) which fails WCAG AA. Small text
        uses --accent-text; mocha keeps every decorative and large-text use, so
        the look is unchanged. Measured numbers are on each token below. */

@font-face {
  font-family: 'Instrument Sans';
  src: url('/fonts/instrument-sans-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  src: url('/fonts/instrument-sans-latin-ext.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Palette: Subtle Contrasts. Ratios measured 24 Aug 2026 against --paper.
     --ink         #2c2420  13.28:1  body copy, headings
     --mocha       #a47864   3.36:1  DECORATIVE + large text only (>=18.66px/700)
     --accent-text #8d6250   4.59:1  small text that wants to read as brown
     --quartz      #695650   6.02:1  buttons (white label 6.89:1), footer ground
     --tapestry    #436374   5.60:1  links and the focus ring
     --meta        #6f655c   4.97:1  secondary copy
     --oak         #918c7e   3.36:1 on white — control borders, meets 1.4.11
   --taupe and --hair are fills and hairlines: both under 3:1, never text and
   never the boundary of an interactive control. */
:root {
  --paper: #f4efe6;
  --white: #ffffff;
  --ink: #2c2420;
  --mocha: #a47864;
  --accent-text: #8d6250;
  --accent-deep: #7d5344;
  --quartz: #695650;
  --tapestry: #436374;
  --taupe: #af9483;
  --oak: #918c7e;
  --arona: #869aa3;
  --meta: #6f655c;
  --wash: #ede5d8;
  --footer-muted: #e3d9cc;
  --hair: #ddd2c2;

  --text: var(--ink);
  --bg: var(--paper);
  --action: var(--quartz);
  --action-hover: var(--ink);
  --link: var(--tapestry);
  --border: var(--hair);
  /* Not --oak: the fields sit on bone inside a white panel, and oak clears 3:1
     against the panel but only 2.93 against the fill. This clears both. */
  --field-border: #87826f;

  --fs-body: 1.0625rem;
  --pad: clamp(1.25rem, 5vw, 4.5rem);
  --gap: clamp(2.5rem, 6vw, 5rem);
  --max: 76rem;
  --font: 'Instrument Sans', -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* Axis shapes (styleui.dev/template/axis) on the existing palette: rounded
     surfaces and a floating pill nav, kept light rather than Axis's dark. */
  --r-btn: .5rem;
  --r-card: 1rem;
  --r-panel: 1.25rem;
  --r-pill: 999px;

  /* Motion, per emilkowalski/skill: never ease-in for UI, never `all`, and
     UI stays under 300ms. */
  --ease-out: cubic-bezier(.23, 1, .32, 1);
  --t-press: 130ms;
  --t-ui: 200ms;
  --t-panel: 280ms;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font: var(--fs-body)/1.6 var(--font);
  font-optical-sizing: auto; text-wrap: pretty; -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { margin: 0; font-weight: 500; letter-spacing: -0.02em; line-height: 1.08; text-wrap: balance; }
p { margin: 0; }
a { color: var(--link); text-decoration: none; text-underline-offset: .22em; text-decoration-thickness: 1px; }
a:hover { color: var(--ink); text-decoration: underline; }
:focus-visible { outline: 2px solid var(--tapestry); outline-offset: 3px; border-radius: var(--r-btn); }
/* `height: auto` is not optional now that every <img> ships width/height
   attributes. Without it, an image scaled down by `max-width` keeps the
   attribute's height and renders squashed. Rules that deliberately set a
   height (the object-fit covers) are more specific and still win. */
img { max-width: 100%; height: auto; display: block; }
hr { border: 0; border-top: 1px solid var(--border); margin: 2.5rem 0; }

.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;
}
.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem; z-index: 99; background: var(--ink); color: var(--paper);
  padding: .6rem 1rem; border-radius: var(--r-btn);
}

/* ------------------------------------------------------------------ layout */
.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--pad); }
.sec { padding-block: var(--gap); border-top: 1px solid var(--border); }
.sec:first-of-type { border-top: 0; }
.lede { font-size: clamp(1.125rem, 1.8vw, 1.375rem); line-height: 1.5; color: var(--ink); max-width: 34ch; }
.body-copy { max-width: 64ch; }
.body-copy + .body-copy { margin-top: 1.1em; }
.meta { color: var(--meta); font-size: .9375rem; }

.label {
  display: flex; align-items: baseline; gap: .7rem; margin-bottom: 1.6rem;
  font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; color: var(--meta);
}
.label b {
  display: inline-block; min-width: 1.6em;
  font-size: 1.1rem; font-weight: 500; letter-spacing: 0;
  color: var(--accent-text); font-variant-numeric: tabular-nums;
}
h1 { font-size: clamp(2.1rem, 4.4vw, 3.5rem); }
h2 { font-size: clamp(1.75rem, 3.4vw, 2.75rem); }
h2 + .body-copy, h2 + .lede, h2 + .intro { margin-top: 1.1rem; }
.intro { color: var(--meta); max-width: 56ch; }
.sec-head {
  display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
  align-items: end; margin-bottom: 2.75rem;
}
@media (max-width: 860px) { .sec-head { grid-template-columns: 1fr; align-items: start; margin-bottom: 2rem; } }
/* Intro under the heading rather than beside it. */
.sec-head.is-stacked { grid-template-columns: 1fr; align-items: start; gap: .9rem; }
.sec-head.is-stacked .intro { max-width: 62ch; }

/* ----------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; gap: .55rem; padding: .85rem 1.35rem;
  border-radius: var(--r-btn); font-size: 1rem; font-weight: 500; letter-spacing: -0.01em;
  border: 1px solid transparent; cursor: pointer; font-family: inherit;
  transition: background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out),
              border-color var(--t-ui) var(--ease-out), transform var(--t-press) ease-out;
  text-decoration: none;
}
/* A button with no press state feels dead. Feedback on pointer-down, not release. */
.btn:active { transform: scale(.97); }
.btn-primary { background: var(--action); color: var(--paper); }
.btn-primary:hover { background: var(--action-hover); color: var(--paper); text-decoration: none; }
.btn-outline { background: transparent; color: var(--ink); border-color: var(--quartz); }
.btn-outline:hover { background: var(--wash); color: var(--ink); border-color: var(--ink); text-decoration: none; }
.btn-sm { padding: .55rem 1rem; font-size: .9375rem; }
.cta-row { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }
.arrow-link { display: inline-flex; align-items: center; gap: .4rem; font-weight: 500; color: var(--tapestry); }
.arrow-link svg { width: .85em; height: .85em; transition: transform var(--t-ui) var(--ease-out); }
.arrow-link:hover svg { transform: translateX(3px); }

/* --------------------------------------------------------------------- nav */
/* Axis-style floating nav: a pill detached from the top edge, sitting over the
   page rather than banded across it. Light, not Axis's dark. */
.nav { position: sticky; top: 0; z-index: 50; padding: .75rem var(--pad) 0; background: transparent; border: 0; }
.nav-in {
  max-width: var(--max); margin: 0 auto; padding: 0 1.25rem; height: 4rem;
  display: flex; align-items: center; gap: 2rem;
  background: rgba(244, 239, 230, .82);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid var(--hair); border-radius: var(--r-pill);
  transition: height var(--t-ui) var(--ease-out), box-shadow var(--t-ui) var(--ease-out),
              background var(--t-ui) var(--ease-out);
}
.nav[data-scrolled="true"] .nav-in {
  height: 3.4rem;
  background: rgba(244, 239, 230, .94);
  box-shadow: 0 12px 30px -22px rgba(44, 36, 32, .85);
}
/* The nav floats over the hero, so the hero starts at the very top of the page.
 *
 * These were `.nav + .hero-full` until 25 Aug and had NEVER MATCHED: the nav is
 * followed by .drawer and then <main>, so the hero is not its sibling. The hero
 * had been sitting below the nav on a strip of paper this whole time. The
 * selector now targets what is actually there. */
main > .hero-full:first-child,
main > .hero-photo:first-child { margin-top: calc(-4.75rem - 1px); }
main > .hero-full:first-child { min-height: 100svh; }
main > .hero-photo:first-child .hero-in { padding-top: calc(clamp(2.5rem, 6vh, 4rem) + 4.75rem); }
@media (max-width: 1020px) {
  .nav { padding-top: .5rem; }
  main > .hero-full:first-child,
  main > .hero-photo:first-child { margin-top: calc(-4.5rem - 1px); }
}
/* Wordmark: "Sau" brown, "cinco" ink — the export's split, identical in both
   languages. 1.375rem/600 = 22px, under the 18.66px/700 large-text threshold,
   so the brown is --accent-text (4.59:1) rather than raw mocha (3.36:1). Same
   shape, same split, contrast that passes. */
.mark {
  font-size: 1.375rem; font-weight: 600; letter-spacing: -0.035em;
  color: var(--ink); text-decoration: none; flex: none;
}
.mark:hover { text-decoration: none; color: var(--ink); }
.mark i { font-style: normal; color: var(--accent-text); }

/* Brand lockup: wordmark plus the tagline beside it, divided by a hairline.
   The tagline is aria-hidden — the wordmark link already carries the accessible
   name, and a screen reader announcing "Saucinco Clarity Quality Confidence"
   as the home link reads as noise. It drops out under 1180px, where the nav
   pill has to fit links, the language toggle and the CTA. */
.brand { display: flex; align-items: baseline; gap: .7rem; flex: none; min-width: 0; }
.tagline {
  font-style: italic; font-size: .8125rem; letter-spacing: .01em;
  color: var(--quartz); white-space: nowrap;
  padding-left: .7rem; border-left: 1px solid var(--taupe);
}
@media (max-width: 1180px) { .tagline { display: none; } }
.nav-links { display: flex; align-items: center; gap: 1.5rem; margin-left: auto; font-size: .9375rem; }
.nav-links a { color: var(--ink); position: relative; }
.nav-links a:hover { color: var(--accent-text); text-decoration: none; }
.nav-links a[aria-current="page"] { color: var(--accent-text); }
.nav-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -.35rem; height: 1px;
  background: var(--mocha); transform: scaleX(0); transform-origin: left;
  transition: transform .28s cubic-bezier(.16, 1, .3, 1);
}
.nav-links a:hover::after, .nav-links a[aria-current="page"]::after { transform: scaleX(1); }
.nav-tools { display: flex; align-items: center; gap: .85rem; flex: none; }
/* Spanish is the canonical language and is always the left, filled half. The
   right half is the link out to the other language — never both active. */
.langtog { display: flex; border: 1px solid var(--field-border); border-radius: var(--r-pill); overflow: hidden; font-size: .8125rem; letter-spacing: .06em; line-height: 1; }
.langtog a, .langtog span { padding: .45rem .6rem; font-weight: 600; color: var(--meta); text-decoration: none; }
/* The available language is the darkest of the three states — it is the only
   one you can act on, so it should read as a link. */
.langtog a { color: var(--ink); }
.langtog a:hover { background: var(--wash); color: var(--ink); text-decoration: none; }
.langtog .cur { background: var(--quartz); color: var(--paper); cursor: default; }
/* --meta (4.97:1), NOT --oak. Oak is 3.36:1 on white but 2.93:1 on the bone
   ground this sits on, which the sweep caught on the 404 page — the one page
   that renders this state, because it has no translated twin. The disabled
   look now comes from being lighter than the live link above, not from a
   colour that fails to read. */
.langtog .off { color: var(--meta); cursor: not-allowed; }
.burger {
  display: none; margin-left: auto; background: transparent; border: 1px solid var(--field-border);
  border-radius: var(--r-pill); padding: .5rem .8rem; cursor: pointer; color: var(--ink);
  font: 500 .875rem/1 inherit;
}
.drawer { display: none; }
@media (max-width: 1020px) {
  .nav-links, .nav-tools { display: none; }
  .burger { display: block; }
  .drawer[data-open="true"] {
    display: block; border-bottom: 1px solid var(--border); background: var(--paper);
    position: sticky; top: 4.5rem; z-index: 49;
  }
  .drawer-in { max-width: var(--max); margin: 0 auto; padding: 1.25rem var(--pad) 1.75rem; display: grid; gap: .15rem; }
  .drawer-in a { display: block; padding: .7rem 0; border-bottom: 1px solid var(--border); color: var(--ink); font-size: 1.0625rem; }
  .drawer-foot { display: flex; align-items: center; gap: .85rem; padding-top: 1.1rem; flex-wrap: wrap; }
}

/* -------------------------------------------------------------------- hero */
.hero { padding-block: clamp(3rem, 7vw, 6rem) clamp(2.5rem, 5vw, 4.5rem); }
.eyebrow {
  display: flex; align-items: center; gap: .7rem; margin-bottom: 1.35rem;
  font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; color: var(--meta);
}
.eyebrow::before { content: ""; width: 1.75rem; height: 1px; background: var(--mocha); flex: none; }
.hero-grid { display: grid; gap: clamp(1.75rem, 4vw, 3.5rem); }
.hero-cta { margin-top: 1.75rem; }

/* Full-height hero: photo + an oblique frosted veil on the RIGHT that sweeps
   in from the right edge, with the copy set on it. Ported verbatim from the
   export (saucinco.css:173-194) — note the diagonal band across the middle is
   the <=900px fallback, not the desktop treatment. */
.hero-full {
  position: relative; isolation: isolate; min-height: calc(100svh - 4.5rem);
  display: grid; align-items: center; overflow: hidden;
}
.hero-bg {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: 50% 38%; z-index: 0; animation: kenburns 24s ease-out both;
}
@keyframes kenburns { from { transform: scale(1.06); } to { transform: scale(1); } }

/* ------------------------------------------------ nav over a full hero */
/* Duncan, 25 Aug: on the home page the nav pill is gone and the nav sits
   directly on the video. It only does this while the hero is behind it —
   past the hero the pill comes back, because dark-on-paper is the readable
   state everywhere else on the site.
 *
 * The state is set by main.js as `data-over-hero`. With no JS the pill simply
 * stays, which is the safe end of the trade. */
.nav[data-over-hero="true"] .nav-in {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* Light type over the footage. --paper rather than pure white: white on a warm
   grade reads cold, and paper is the same tone the veil is made of. */
.nav[data-over-hero="true"] .nav-links a,
.nav[data-over-hero="true"] .mark,
.nav[data-over-hero="true"] .tagline,
.nav[data-over-hero="true"] .burger { color: var(--paper); }
.nav[data-over-hero="true"] .mark i { color: var(--taupe); }
.nav[data-over-hero="true"] .tagline { border-left-color: rgba(244, 239, 230, .45); }
.nav[data-over-hero="true"] .nav-links a:hover { color: var(--taupe); }
.nav[data-over-hero="true"] .nav-links a::after { background: var(--paper); }
.nav[data-over-hero="true"] .burger { border-color: rgba(244, 239, 230, .55); }
/* The language toggle keeps its shape but inverts. */
.nav[data-over-hero="true"] .langtog { border-color: rgba(244, 239, 230, .55); }
.nav[data-over-hero="true"] .langtog a { color: var(--paper); }
.nav[data-over-hero="true"] .langtog a:hover { background: rgba(244, 239, 230, .16); }
.nav[data-over-hero="true"] .langtog .cur { background: rgba(244, 239, 230, .92); color: var(--ink); }
.nav[data-over-hero="true"] .langtog .off { color: rgba(244, 239, 230, .72); }
/* The CTA carries its own opaque fill, so it needs nothing — it is the one
   element that reads the same on either ground. */

/* The strip of shade that makes the light nav type legible over any frame.
   Sits above the footage and below the veil, and only over the top of the
   hero. Without it the nav would be at the mercy of whatever the video is
   doing that second. */
.hero-full::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 11rem; z-index: 3;
  /* The nav's ground. With the veil gone this is the only thing between the
     light nav type and the footage, so it is load-bearing. .90 at the crown
     was the lightest that cleared AA when a light wash still sat underneath;
     without that wash the ground is darker and the margin is wider. It fades
     to nothing by 176px, well above the hero copy, so it reads as grading
     rather than a bar. Measured; re-measure if you touch it. */
  background: linear-gradient(to bottom, rgba(38, 31, 27, .90) 0%, rgba(38, 31, 27, .58) 45%, rgba(38, 31, 27, 0) 100%);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .nav-in { transition: none; }
}

/* --------------------------------------------------------- hero video reel */
/* Three clips cross-fading behind the hero copy. The reel sits directly on top
   of .hero-bg and below the veil, so the still image is the poster underneath
   and any gap — first paint, a stalled download, a codec the browser refuses —
   simply shows the photo that was always there.

   The Ken Burns animation is dropped when the reel is live: the clips carry
   their own camera move, and two motions fighting each other reads as drift. */
.hero-reel { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero-reel video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 38%;
  opacity: 0; transition: opacity 1.1s ease-in-out;
}
.hero-reel video.is-on { opacity: 1; }
.hero-full.has-reel .hero-bg { animation: none; }

@media (prefers-reduced-motion: reduce) {
  /* main.js does not build the reel at all under reduced motion; this is the
     belt to that braces, for a preference that changes after load. */
  .hero-reel { display: none; }
}
.hero-full::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  /* With the glass panel gone this is the ONLY thing making the hero copy
     legible, so it is load-bearing and its stops are measured, not chosen.
     Weighted through the middle band where the centred copy sits, easing off
     top and bottom so the footage still reads at the edges. */
  background: linear-gradient(to bottom, rgba(30, 24, 21, .32) 0%, rgba(30, 24, 21, .63) 30%, rgba(30, 24, 21, .66) 62%, rgba(30, 24, 21, .36) 100%);
}
/* The full hero has no veil any more. Duncan, 25 Aug: the copy sits on a glass
   panel instead, so the footage is unobstructed everywhere except behind the
   words themselves. A wash across half the frame was hiding the thing it was
   laid over. .hero-photo (inner pages) still uses its own ::before wash — that
   is a different, static treatment and is unaffected. */
.hero-in {
  position: relative; z-index: 4; width: 100%; max-width: var(--max);
  margin: 0 auto; padding: 0 var(--pad); display: grid; justify-content: center;
}
/* The glass panel. This is the only thing standing between the hero copy and
   the video, so its alpha is measured, not chosen — see the handover. The blur
   is what makes it read as glass rather than as a flat card: it carries the
   colour and the movement of the footage behind it without the detail. */
.hero-full .hero-copy {
  /* Duncan, 25 Aug: wide and shallow, centred — reads across the frame instead
     of stacking down it. The CTAs move to their own column so the panel fills
     horizontally rather than growing taller. */
  /* Duncan, 25 Aug: the glass is gone. The copy sits directly on the footage
     now and carries its own contrast through ::before — see the scrim below.
     The landscape geometry stays: wide, centred, headline left, CTAs right. */
  width: min(72rem, 88vw);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: clamp(1.75rem, 4vw, 3.5rem);
  row-gap: 1.1rem;
}
.hero-copy { width: min(33rem, 46vw); animation: plate .95s cubic-bezier(.16, 1, .3, 1) .18s both; }
@keyframes plate { from { opacity: 0; transform: translateY(1.5rem); } }
/* --ink, not the --meta every other eyebrow uses. On the hero composite --meta
   measures 2.47 against the ORIGINAL veil and 1.67 against the lighter one —
   it has never passed here, on video or on the still. This is the pre-existing
   failure the video work surfaced; the lighter veil forced the fix. Elsewhere
   the eyebrow sits on a flat ground where --meta is correct, so this is scoped
   to the hero only. */
/* Light on the footage now that nothing sits behind it. --paper rather than
   pure white: white on a warm grade reads cold. */
.hero-full .hero-copy .eyebrow { color: var(--paper); }
.hero-full .hero-copy .eyebrow::before { background: var(--taupe); }
.hero-full h1 { color: var(--paper); }
/* Panel interior: the eyebrow and headline take the left column, the buttons
   sit bottom-right in their own. That is what turns a tall stack into a wide
   band — the CTAs were the tallest thing pushing it down. */
.hero-full .hero-copy > .eyebrow { grid-column: 1; margin-bottom: .2rem; }
.hero-full .hero-copy > h1 { grid-column: 1; }
.hero-full .hero-cta { grid-column: 2; grid-row: 2; margin: 0; flex-wrap: nowrap; }
/* Wider measure now there is room across; keeps the headline to two lines
   rather than three, which is the other half of making the panel shallow. */
.hero-full h1 { max-width: 17ch; }

@media (max-width: 900px) {
  /* One column on a phone: the two-column band has nowhere to go. */
  /* One column, and an explicit width: `100%` has nothing to resolve
     against inside a content-sized centred grid track, which left the panel
     419px wide and off-centre at tablet size. */
  .hero-full .hero-copy {
    grid-template-columns: 1fr;
    width: min(38rem, 100%);
    align-items: start;
  }
  .hero-full .hero-in { justify-content: center; width: 100%; }
  .hero-full .hero-cta { grid-column: 1; grid-row: auto; flex-wrap: wrap; margin-top: .4rem; }
  .hero-full h1 { max-width: 14ch; }
}
/* Hero CTAs.
 *
 * Purpose: feedback on press, state indication on hover. A hero CTA is seen
 * occasionally, not tens of times a day, so a standard-tier animation is
 * warranted — but kept under 300ms and to transform only.
 *
 * Tool is a plain CSS transition: it is a hover/press state, which is the
 * cheapest thing that does the job. Properties are transform and colour only,
 * named individually — never `all`. Curve is the existing --ease-out token
 * (cubic-bezier(.23,1,.32,1)), not a new one.
 *
 * The lift is gated behind a real pointer: touch fires a false hover on tap
 * and would leave the button stuck in its raised state. */
.hero-full .hero-cta .btn {
  transition:
    transform var(--t-ui) var(--ease-out),
    box-shadow var(--t-ui) var(--ease-out),
    background var(--t-ui) var(--ease-out),
    border-color var(--t-ui) var(--ease-out);
  will-change: transform;
}
.hero-full .hero-cta .btn-primary {
  background: var(--action);
  border-color: transparent;
  box-shadow: 0 10px 26px -14px rgba(24, 18, 16, .8);
}
.hero-full .hero-cta .btn-outline {
  background: rgba(244, 239, 230, .96);
  border-color: rgba(244, 239, 230, .96);
  color: var(--ink);
  box-shadow: 0 10px 26px -14px rgba(24, 18, 16, .8);
}
@media (hover: hover) and (pointer: fine) {
  .hero-full .hero-cta .btn:hover { transform: translateY(-2px); }
  .hero-full .hero-cta .btn-primary:hover {
    background: var(--action-hover);
    box-shadow: 0 18px 38px -16px rgba(24, 18, 16, .9);
  }
  .hero-full .hero-cta .btn-outline:hover {
    background: var(--white);
    box-shadow: 0 18px 38px -16px rgba(24, 18, 16, .9);
  }
}
/* Press beats hover: the shorter duration is what makes it feel like contact
   rather than a slide. scale() takes the label with it, which is what reads as
   physical. */
.hero-full .hero-cta .btn:active {
  transform: scale(.97);
  transition-duration: var(--t-press);
}
@media (prefers-reduced-motion: reduce) {
  /* Fewer and gentler, not none: the colour change still confirms the hover,
     the movement goes. */
  .hero-full .hero-cta .btn { transition: background var(--t-ui) var(--ease-out), border-color var(--t-ui) var(--ease-out); }
  .hero-full .hero-cta .btn:hover,
  .hero-full .hero-cta .btn:active { transform: none; }
}
.hero-copy .lede { max-width: none; margin-top: 1.35rem; font-size: clamp(1rem, 1.25vw, 1.1875rem); color: var(--ink); }
/* Duncan, 25 Aug: the home hero is the headline alone now — the standfirst
   was removed, so the line takes the room it freed. */
.hero-full h1 { font-size: clamp(3rem, 6vw, 5.6rem); line-height: .98; letter-spacing: -0.03em; max-width: 13ch; }

/* Narrow screens: the panel goes full width at the base of the frame. */
@media (max-width: 900px) {
  .hero-full { align-items: end; }
  .hero-in { justify-content: start; padding-bottom: clamp(2.25rem, 6vh, 4rem); }
  .hero-copy { max-width: none; width: auto; }
}

.scrollcue {
  position: absolute; right: var(--pad); bottom: 1.75rem; display: flex; align-items: center;
  gap: .6rem; color: var(--quartz); font-size: .75rem; letter-spacing: .14em;
  text-transform: uppercase; z-index: 4;
}
.scrollcue:hover { color: var(--ink); text-decoration: none; }
.scrollcue .cue-bar { width: 1px; height: 2.25rem; background: currentColor; opacity: .7; animation: cue 2.4s ease-in-out infinite; transform-origin: top; }
@keyframes cue { 0%, 100% { transform: scaleY(.35); opacity: .4; } 50% { transform: scaleY(1); opacity: .9; } }
@media (max-width: 1020px) { .scrollcue { display: none; } }

/* secondary hero: blurred photo band, heading reads through */
.hero-photo {
  position: relative; isolation: isolate; overflow: hidden; display: grid;
  align-items: center; min-height: clamp(23rem, 50svh, 31rem); border-bottom: 1px solid var(--hair);
}
.hero-photo .hero-bg {
  object-position: 62% 34%; filter: blur(7px) saturate(.95); transform: scale(1.06); animation: none;
}
.hero-photo::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg, rgba(244, 239, 230, .95) 0%, rgba(244, 239, 230, .87) 32%, rgba(244, 239, 230, .56) 66%, rgba(244, 239, 230, .44) 100%);
}
.hero-photo .hero-in {
  z-index: 2; display: block; padding: clamp(2.5rem, 6vh, 4rem) var(--pad); justify-content: initial;
}
.hero-photo h1 { font-size: clamp(2.1rem, 4.4vw, 3.5rem); }
.hero-photo .hero-copy { width: auto; }
/* Where the photo is the point — people on the About page — it stays sharp and
   the copy sits on the light half instead of the whole frame being blurred. */
.hero-photo.is-sharp .hero-bg { filter: none; transform: none; }
/* The veil has to stay dense right across the copy column (~52% of the frame)
   or the eyebrow drops under AA where it thins. Measured against the photo. */
.hero-photo.is-sharp::before {
  background: linear-gradient(96deg, rgba(244, 239, 230, .97) 0%, rgba(244, 239, 230, .96) 42%, rgba(244, 239, 230, .92) 56%, rgba(244, 239, 230, .45) 82%, rgba(244, 239, 230, .22) 100%);
}
.hero-photo.is-sharp .hero-copy { width: min(34rem, 52vw); }
@media (max-width: 900px) {
  .hero-photo.is-sharp .hero-copy { width: auto; }
  .hero-photo.is-sharp::before { background: linear-gradient(180deg, rgba(244, 239, 230, .9) 0%, rgba(244, 239, 230, .96) 55%, rgba(244, 239, 230, .98) 100%); }
}

/* ------------------------------------------------------------------- cards */
/* Axis shapes: separated rounded cards rather than a hairline-divided slab. */
.grid { display: grid; gap: 1rem; background: transparent; border: 0; }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 860px) { .grid-3, .grid-2 { grid-template-columns: 1fr; } }
.cell {
  background: var(--white); padding: clamp(1.35rem, 2.4vw, 2rem);
  display: flex; flex-direction: column; gap: .7rem;
  border: 1px solid var(--hair); border-radius: var(--r-card);
}
/* Cards carrying a photo get it as an oblique wash in the top-right corner,
   masked away from the text and pushed toward the palette with a warm filter.
   Set per card as --cell-img. Kept faint on purpose: it is colour and texture,
   not a picture to look at, and the copy must stay the thing you read. */
.cell:has(> .cell-art) { position: relative; overflow: hidden; isolation: isolate; }
.cell-art {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: var(--cell-img); background-size: cover; background-position: 68% 40%;
  clip-path: polygon(54% 0, 100% 0, 100% 100%, 26% 100%);
  /* brightness lifts the darkest pixels so the composite never drops the copy
     below AA; measured, not guessed — see the note at the top of this file. */
  filter: sepia(.42) saturate(1.05) contrast(.78) brightness(1.42);
  opacity: .16;
  -webkit-mask-image: linear-gradient(100deg, transparent 46%, #000 88%, #000 100%);
  mask-image: linear-gradient(100deg, transparent 46%, #000 88%, #000 100%);
}
.cell:has(> .cell-art) > *:not(.cell-art) { position: relative; z-index: 1; }
a.cell:hover .cell-art { opacity: .2; }
/* A washed card's ground is no longer pure white, so the copy on it is set one
   step darker. --meta (5.69:1 on white) and --link (6.41:1) both have too
   little headroom to survive the wash. These were measured against the real
   composite — image, filter, mask and clip — at the hover opacity, which is
   the worst case, not estimated from the flat colours. */
.cell:has(> .cell-art) p { color: #5f564e; }
.cell:has(> .cell-art) .num { color: var(--accent-deep); }
.cell:has(> .cell-art) .arrow-link { color: #3d5a6a; }
@media (prefers-reduced-motion: no-preference) { .cell-art { transition: opacity .3s; } }
.cell h3 { font-size: 1.1875rem; letter-spacing: -0.015em; }
.cell p { color: var(--meta); }
.cell .num { font-size: .8125rem; letter-spacing: .08em; color: var(--accent-text); font-variant-numeric: tabular-nums; }
.cell .arrow-link { margin-top: auto; padding-top: 1.25rem; }
/* Points under a card headline — the three engagement models carry the same
   shape as the home page split cards. */
.cell-points { margin-top: .85rem; }
/* The deliverable line of an engagement step, set apart from the body copy so
   it reads as what you get rather than more description. */
.row .receive { margin-top: .7rem; color: var(--ink); }
/* Infosys-style hover: the card grows above its neighbours, the photo fills it
   and the read-more slides in. State indication + spatial consistency — it
   shows there is a page behind the card. Kept to 280ms and scale 1.045: the
   gate rejects slow, showy expansion on a hover-frequency element. */
a.cell {
  color: inherit; z-index: 0;
  transition: background var(--t-ui) var(--ease-out),
              transform var(--t-panel) var(--ease-out),
              box-shadow var(--t-panel) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
a.cell:hover, a.cell:focus-visible {
  background: var(--white); text-decoration: none;
  transform: scale(1.045); z-index: 2;
  border-color: var(--taupe);
  box-shadow: 0 22px 48px -28px rgba(44, 36, 32, .55);
}
a.cell:active { transform: scale(1.02); }
/* --accent-deep, not --accent-text: on hover the photo fills the card, and the
   lighter brown lands at 3.98:1 over it. Measured on the real composite. */
a.cell:hover h3, a.cell:focus-visible h3 { color: var(--accent-deep); }
/* the wash grows from a corner sliver to filling the card */
a.cell .cell-art {
  transition: opacity var(--t-panel) var(--ease-out),
              clip-path var(--t-panel) var(--ease-out),
              transform var(--t-panel) var(--ease-out);
}
a.cell:hover .cell-art, a.cell:focus-visible .cell-art {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  transform: scale(1.04);
}
/* the read-more only commits on hover */
a.cell .arrow-link {
  opacity: .65;
  transition: opacity var(--t-ui) var(--ease-out), transform var(--t-panel) var(--ease-out);
}
a.cell:hover .arrow-link, a.cell:focus-visible .arrow-link { opacity: 1; transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  a.cell, a.cell .cell-art, a.cell .arrow-link { transition: none; }
  a.cell:hover, a.cell:focus-visible { transform: none; }
  a.cell:hover .cell-art { transform: none; }
  a.cell:hover .arrow-link { transform: none; }
}

/* Non-link cards opt into the same lift via `hover: true` on the block. */
.grid.is-liftable .cell {
  z-index: 0;
  transition: background var(--t-ui) var(--ease-out),
              transform var(--t-panel) var(--ease-out),
              box-shadow var(--t-panel) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .grid.is-liftable .cell:hover {
    background: var(--white);
    transform: scale(1.045); z-index: 2;
    border-color: var(--taupe);
    box-shadow: 0 22px 48px -28px rgba(44, 36, 32, .55);
  }
  .grid.is-liftable .cell:hover .cell-art {
    opacity: .2;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
    transform: scale(1.04);
  }
}
@media (prefers-reduced-motion: reduce) {
  .grid.is-liftable .cell { transition: none; }
  .grid.is-liftable .cell:hover { transform: none; }
  .grid.is-liftable .cell:hover .cell-art { transform: none; }
}

/* numbered rows */
.rows { border-top: 1px solid var(--border); }
.row {
  display: grid; grid-template-columns: 3.5rem minmax(0, 16rem) minmax(0, 1fr);
  gap: 1.5rem; padding: 1.6rem 0; border-bottom: 1px solid var(--border); align-items: start;
  transition: padding-left .3s cubic-bezier(.16, 1, .3, 1);
}
.row:hover { padding-left: .5rem; }
.row > b { font-weight: 500; color: var(--accent-text); font-variant-numeric: tabular-nums; }
.row:hover > b { color: var(--ink); }
.row h3 { font-size: 1.125rem; }
.row p { color: var(--meta); }
.row .status { font-size: .8125rem; color: var(--meta); margin-top: .4rem; }
@media (max-width: 760px) { .row { grid-template-columns: 2.5rem minmax(0, 1fr); gap: .5rem 1rem; } .row p { grid-column: 2; } }

/* facts */
.facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; background: transparent; border: 0; margin: 0; }
@media (max-width: 760px) { .facts { grid-template-columns: 1fr; } }
.fact { background: var(--white); padding: 1.35rem clamp(1.25rem, 2.2vw, 1.75rem); border: 1px solid var(--hair); border-radius: var(--r-card); }
.fact dt { font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--meta); margin-bottom: .4rem; }
.fact dd { margin: 0; font-size: 1.0625rem; }

/* [COMPLETAR] chips — visible on purpose until the fact is real */
.todo {
  display: inline-block; background: var(--wash); border: 1px dashed var(--taupe);
  border-radius: var(--r-btn); padding: .05em .5em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8125rem; color: var(--accent-deep); letter-spacing: -0.01em;
}
.independence { margin-top: 1.75rem; padding-left: 1.25rem; border-left: 2px solid var(--mocha); font-size: clamp(1.0625rem, 1.6vw, 1.25rem); max-width: 52ch; }

/* people */
.people { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.25rem, 3vw, 2.5rem); }
@media (max-width: 760px) { .people { grid-template-columns: 1fr; } }
.person { border: 1px solid var(--hair); background: var(--white); border-radius: var(--r-card); overflow: hidden; }
.person .shot {
  width: clamp(4.5rem, 7vw, 5.75rem); aspect-ratio: 1/1;
  border-radius: 50%; overflow: hidden;
  background: var(--wash); border: 1px solid var(--hair);
  margin: clamp(1.25rem, 2.2vw, 1.75rem) clamp(1.25rem, 2.2vw, 1.75rem) 0;
}
.person .shot img { width: 100%; height: 100%; object-fit: cover; }
.person-body { padding: 1.35rem clamp(1.25rem, 2.2vw, 1.75rem); display: grid; gap: .55rem; }
.person:has(.shot) .person-body { padding-top: .95rem; }
.person-body h3 { font-size: 1.25rem; }
.person-body .role { font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--accent-text); }
.person-body p { color: var(--meta); }
/* Person cards lift on hover. Deliberately gentler than a.cell: that treatment
   grows the card and is scoped to anchors, where it promises a page behind the
   click. A person card is NOT a link — the only interaction inside it is the
   LinkedIn arrow — so this indicates a live surface and points at that arrow
   without pretending the whole card is a target. Hence translateY, not scale.
   :focus-within gives the keyboard the same affordance when the arrow is
   tabbed to. Properties are named individually; the sheet has no `all`. */
.person {
  transition: transform var(--t-panel) var(--ease-out),
              box-shadow var(--t-panel) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
.person .arrow-link svg { transition: transform var(--t-panel) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .person:hover {
    transform: translateY(-3px);
    border-color: var(--taupe);
    box-shadow: 0 16px 34px -24px rgba(44, 36, 32, .45);
  }
  .person:hover .arrow-link svg { transform: translateX(3px); }
}
.person:focus-within {
  border-color: var(--taupe);
  box-shadow: 0 16px 34px -24px rgba(44, 36, 32, .45);
}
@media (prefers-reduced-motion: reduce) {
  /* The shadow and border still confirm the hover; the movement goes. */
  .person, .person .arrow-link svg { transition: none; }
  .person:hover { transform: none; }
  .person:hover .arrow-link svg { transform: none; }
}

/* --------------------------------------------------------------------- faq */
.faq { border-top: 1px solid var(--border); max-width: 60rem; }
.faq details { border-bottom: 1px solid var(--border); }
.faq summary {
  cursor: pointer; list-style: none; padding: 1.25rem 2.5rem 1.25rem 0; position: relative;
  font-size: clamp(1.0625rem, 1.5vw, 1.1875rem); font-weight: 500; letter-spacing: -0.015em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; position: absolute; right: .25rem; top: 1.1rem; font-size: 1.35rem; font-weight: 400; color: var(--accent-text); line-height: 1; }
.faq details[open] summary::after { content: "–"; }
.faq summary:hover { color: var(--accent-text); }
.faq .ans { padding: 0 3rem 1.5rem 0; color: var(--meta); max-width: 66ch; }
.faq .ans a { color: var(--link); }

/* ---------------------------------------------------------------- cta band */
.band { background: var(--wash); border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); }
.band-in {
  max-width: var(--max); margin: 0 auto; padding: clamp(3rem, 6vw, 5rem) var(--pad);
  display: grid; gap: 1.5rem; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
}
@media (max-width: 860px) { .band-in { grid-template-columns: 1fr; } }
.band h2 { max-width: 22ch; }
.band p { color: var(--meta); margin-top: .9rem; max-width: 46ch; }

/* ------------------------------------------------------------ careers band */
/* Infosys reference: a full-bleed photo near the foot of the page carrying a
   short pitch and one button through to Careers.

   The photo is lightened by a paper veil and the copy stays ink, which is the
   same move the hero makes. White-on-photo was not used: the palette rule that
   keeps biting is text over a photo losing its headroom, and a light veil with
   dark type is the version this site has already measured. */
.careers-band {
  position: relative; isolation: isolate; overflow: hidden;
  border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair);
}
.careers-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 40%; z-index: 0;
}
/* Opaque enough that the copy sits on a known ground rather than on whatever
   the photo happens to be doing underneath it. Measured as a composite, not
   by the flat-colour sweep — see the handover, §6. */
/* The stops are not a taste decision: .88 at 42% put the end of the paragraph
   at 4.47:1 against --quartz, just under AA. Holding ~.93 across the whole
   copy column and only opening up past it measures 5.5:1 with the photo still
   reading on the right. Re-run tools/contrast-composite.js if these move. */
.careers-veil {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg, rgba(244, 239, 230, .95) 0%, rgba(244, 239, 230, .93) 50%, rgba(244, 239, 230, .58) 100%);
  backdrop-filter: blur(3px) saturate(1.05);
  -webkit-backdrop-filter: blur(3px) saturate(1.05);
}
@media (max-width: 860px) {
  .careers-veil { background: linear-gradient(to bottom, rgba(244, 239, 230, .93), rgba(244, 239, 230, .9)); }
}
.careers-in {
  position: relative; z-index: 2; max-width: var(--max); margin: 0 auto;
  padding: clamp(3.5rem, 8vw, 6.5rem) var(--pad);
}
.careers-copy { max-width: 34rem; display: grid; gap: 1rem; justify-items: start; }
.careers-copy h2 { font-size: clamp(2rem, 4vw, 3.1rem); letter-spacing: -0.025em; max-width: 16ch; }
.careers-copy p { color: var(--quartz); font-size: clamp(1rem, 1.35vw, 1.1875rem); max-width: 44ch; }
.careers-copy .btn { margin-top: .6rem; }

/* Glass button: translucent fill over the photo with a blur behind it. The
   border is --quartz rather than a translucent white — a control boundary has
   to clear 3:1 against its own fill (WCAG 1.4.11), and a white-on-glass edge
   does not. */
.btn-glass {
  background: rgba(255, 255, 255, .58); color: var(--ink);
  border-color: var(--quartz);
  backdrop-filter: blur(10px) saturate(1.15);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  box-shadow: 0 10px 30px -18px rgba(44, 36, 32, .75);
}
.btn-glass:hover {
  background: rgba(255, 255, 255, .82); color: var(--ink);
  border-color: var(--ink); text-decoration: none;
}
.btn-glass svg { width: .9em; height: .9em; transition: transform var(--t-ui) var(--ease-out); }
.btn-glass:hover svg { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .btn-glass svg { transition: none; } }

/* ----------------------------------------------------------- statement etc */
.statement { padding-block: clamp(3.5rem, 9vw, 7rem); border-top: 1px solid var(--border); }
.statement p { font-size: clamp(1.75rem, 5vw, 3.5rem); line-height: 1.06; letter-spacing: -0.028em; max-width: 26ch; font-weight: 500; }
.statement .rule { width: 3.5rem; height: 2px; background: var(--mocha); margin-bottom: clamp(1.5rem, 3vw, 2.5rem); }
/* A sentence rather than a headline: wider measure, one step down in size. */
.statement.is-long p { font-size: clamp(1.5rem, 3.6vw, 2.75rem); line-height: 1.14; max-width: 34ch; }

/* Word-by-word rise. Note the order: the words are VISIBLE by default and only
   hidden once main.js adds .armed. With scripting off, or before the observer
   runs, the statement simply reads — it never starts life invisible waiting for
   JS that might not arrive. */
.statement .w { display: inline-block; overflow: hidden; vertical-align: bottom; padding-bottom: .06em; }
.statement .w i {
  display: inline-block; font-style: normal;
  transition: transform .85s cubic-bezier(.16, 1, .3, 1);
  transition-delay: calc(var(--i, 0) * var(--step, 55ms));
}
.statement p.armed .w i { transform: translateY(110%); }
.statement p.armed.in .w i { transform: none; }
/* 28 words at 55ms would take 1.5s to finish landing. */
.statement.is-long p { --step: 26ms; }

.quad { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; background: transparent; border: 0; }
@media (max-width: 760px) { .quad { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.quad a {
  background: var(--white); padding: clamp(1.25rem, 2.4vw, 1.85rem); display: flex;
  flex-direction: column; gap: .9rem; justify-content: space-between; min-height: 9.5rem;
  color: var(--ink); position: relative; overflow: hidden; isolation: isolate;
  border: 1px solid var(--hair); border-radius: var(--r-card);
  transition: background var(--t-ui) var(--ease-out), color var(--t-ui) var(--ease-out),
              transform var(--t-panel) var(--ease-out), box-shadow var(--t-panel) var(--ease-out);
}
.quad a > *:not(.quad-art) { position: relative; z-index: 1; }
/* The photo fills the tile rather than sitting in a corner — a door should
   show a little of what is behind it. Kept faint at rest, measured on hover. */
.quad-art {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: var(--cell-img); background-size: cover; background-position: 55% 45%;
  filter: sepia(.42) saturate(1.05) contrast(.78) brightness(1.42);
  opacity: .12;
  transition: opacity var(--t-panel) var(--ease-out), transform var(--t-panel) var(--ease-out);
}
/* The text stays ink on hover. The export tinted it mocha, but with a photo
   behind it and the wash ground under it that lands at 2.94:1 — measured.
   The lift, the shadow, the brightening photo and the arrow carry the hover
   instead, and the copy stays at 7.9:1. */
.quad a:hover, .quad a:focus-visible {
  background: var(--wash); text-decoration: none;
  transform: translateY(-5px);
  box-shadow: 0 14px 28px -20px rgba(44, 36, 32, .7);
}
.quad a:hover .quad-art, .quad a:focus-visible .quad-art { opacity: .2; transform: scale(1.07); }
.quad .k {
  font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--meta);
  transition: color .22s;
}
.quad a:hover .k, .quad a:focus-visible .k { color: var(--ink); }
.quad .go { color: var(--accent-deep); }
.quad b {
  font-size: clamp(1.0625rem, 1.5vw, 1.25rem); font-weight: 500; letter-spacing: -0.015em;
  display: flex; align-items: center; gap: .45rem;
}
/* The arrow is parked off to the left and slides out on hover. */
.quad .go {
  display: inline-flex; opacity: 0; transform: translateX(-6px);
  transition: opacity var(--t-ui) var(--ease-out), transform var(--t-panel) var(--ease-out);
}
.quad a:hover .go, .quad a:focus-visible .go { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .quad a, .quad-art, .quad .go { transition: none; }
  .quad a:hover { transform: none; }
  .quad a:hover .quad-art { transform: none; }
  .quad .go { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------- carousel */
/* Split slide: image left, copy right. Scroll-snap does the work, so it is
   swipeable on touch and keyboard-scrollable with no JS; the buttons are an
   enhancement, not the mechanism. */
.carousel { position: relative; }
/* Heading left, counter and controls right, on one row — the slides then start
   immediately underneath rather than after a section head. */
.carousel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.carousel-head h2 { flex: 1 1 20rem; }
.carousel-head .carousel-count { margin-left: auto; }
.carousel-count { font-size: .8125rem; letter-spacing: .1em; color: var(--meta); font-variant-numeric: tabular-nums; }
.carousel-nav { display: flex; gap: .5rem; }
.carousel-btn {
  display: grid; place-items: center; width: 2.5rem; height: 2.5rem;
  /* --field-border, not --oak: oak is 2.93:1 on the bone ground and these are
     interactive controls, so the boundary has to clear 3:1 (WCAG 1.4.11). */
  border: 1px solid var(--field-border); border-radius: var(--r-pill);
  background: transparent; color: var(--ink); cursor: pointer;
  transition: background var(--t-ui) var(--ease-out), border-color var(--t-ui) var(--ease-out),
              transform var(--t-press) ease-out, opacity var(--t-ui) var(--ease-out);
}
.carousel-btn:hover { background: var(--wash); border-color: var(--ink); }
.carousel-btn:active { transform: scale(.94); }
.carousel-btn[disabled] { opacity: .35; cursor: default; }
.carousel-btn[disabled]:hover { background: transparent; border-color: var(--oak); }

/* Flex, not grid: `minmax(0, …)` tracks let the slides shrink to fit instead of
   overflowing, which collapses the carousel into slivers. A fixed flex basis
   with no shrink is what makes it actually scroll. */
.carousel-track {
  display: flex; gap: 1rem; margin: 0; list-style: none;
  /* Room above and below so a hovered slide's lift is not clipped: overflow-x
     on a scroller forces overflow-y to clip too. */
  padding: .5rem .5rem 1.5rem; margin-inline: -.5rem;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: none;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track:focus-visible { outline: 2px solid var(--tapestry); outline-offset: 4px; }

.slide {
  scroll-snap-align: start;
  flex: 0 0 88%; min-width: 0;
  display: grid; grid-template-columns: minmax(0, 38%) minmax(0, 1fr);
  background: var(--white); border: 1px solid var(--hair); border-radius: var(--r-panel);
  overflow: hidden; min-height: 17rem;
}
@media (min-width: 861px) { .slide { flex-basis: 72%; } }
@media (max-width: 700px) { .slide { grid-template-columns: 1fr; grid-template-rows: 11rem auto; } }
/* Hover: the slide lifts and the photo pushes in. State indication — it says
   the slide leads somewhere. 280ms and scale 1.015: the gate wants hover-tier
   motion fast and subtle, not a showy expansion. */
.slide {
  transition: transform var(--t-panel) var(--ease-out),
              box-shadow var(--t-panel) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
.slide:hover, .slide:focus-within {
  transform: translateY(-4px) scale(1.015);
  border-color: var(--taupe);
  box-shadow: 0 24px 50px -30px rgba(44, 36, 32, .6);
}
.slide-art { background: var(--wash); overflow: hidden; }
.slide-art img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-panel) var(--ease-out);
}
.slide:hover .slide-art img, .slide:focus-within .slide-art img { transform: scale(1.06); }
.slide .slide-more svg { transition: transform var(--t-ui) var(--ease-out); }
.slide:hover .slide-more svg, .slide:focus-within .slide-more svg { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) {
  .slide, .slide-art img, .slide .slide-more svg { transition: none; }
  .slide:hover, .slide:focus-within { transform: none; }
  .slide:hover .slide-art img { transform: none; }
}
.slide-body {
  padding: clamp(1.5rem, 3vw, 2.5rem); display: flex; flex-direction: column;
  gap: .7rem; justify-content: center;
}
.slide-kicker { font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--meta); }
.slide-body h3 { font-size: clamp(1.25rem, 2vw, 1.6rem); letter-spacing: -0.015em; }
.slide-body p { color: var(--meta); max-width: 52ch; }
.slide-more { margin-top: .35rem; }

@media (prefers-reduced-motion: reduce) {
  .carousel-track { scroll-behavior: auto; }
  .carousel-btn { transition: none; }
}

/* -------------------------------------------------- card carousel (posts) */
/* Same scroll-snap track as the story carousel, but the slides are portrait
   cards — photo over copy — and several sit in view at once. Used for the
   latest writing on the home page, where it auto-advances right to left. */
.carousel-intro {
  flex: 1 1 100%; order: 3; color: var(--meta); max-width: 62ch;
  font-size: clamp(1rem, 1.35vw, 1.125rem); margin: -.15rem 0 .35rem;
}
.carousel-all { margin-top: 1.5rem; }

/* The heading of a card carousel is the section's own heading, so it carries
   the section head's size rather than the story carousel's. */
.is-cards .carousel-head h2 { font-size: clamp(1.75rem, 3.4vw, 2.75rem); letter-spacing: -0.02em; }
.post-track { gap: clamp(1rem, 1.8vw, 1.5rem); scroll-snap-type: x proximity; }

/* Flex basis, not a grid track: a minmax() column lets a card shrink to fit
   and the track stops scrolling (same trap as .slide). Two up, and two up at
   every width above 700px — Duncan asked for these larger than the two-up
   "Why" grid they replaced. A three-up rule was tried and removed: .wrap caps
   the column at ~1100px whatever the viewport, so three-up made the cards
   *smaller* on a wider screen, which reads as a bug. */
.postcard {
  scroll-snap-align: start;
  flex: 0 0 86%; min-width: 0; list-style: none; display: flex;
}
@media (min-width: 700px) { .postcard { flex-basis: calc((100% - 1.5rem) / 2); } }

.postcard-in {
  display: flex; flex-direction: column; width: 100%; color: inherit;
  background: var(--white); border: 1px solid var(--hair); border-radius: var(--r-panel);
  overflow: hidden;
  transition: transform var(--t-panel) var(--ease-out),
              box-shadow var(--t-panel) var(--ease-out),
              border-color var(--t-ui) var(--ease-out);
}
/* Infosys card hover: the card grows above its neighbours. The track carries
   .5rem of internal padding so the lift is not clipped by overflow-x. */
.postcard-in:hover, .postcard-in:focus-visible {
  transform: translateY(-6px) scale(1.015);
  border-color: var(--taupe);
  box-shadow: 0 26px 52px -30px rgba(44, 36, 32, .6);
  text-decoration: none;
}
.postcard-art { background: var(--wash); aspect-ratio: 16 / 10; overflow: hidden; }
.postcard-art img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--t-panel) var(--ease-out);
}
.postcard-in:hover .postcard-art img, .postcard-in:focus-visible .postcard-art img { transform: scale(1.06); }
.postcard-copy {
  display: flex; flex-direction: column; gap: .6rem; flex: 1 1 auto;
  padding: clamp(1.25rem, 2vw, 1.75rem);
}
.postcard-kicker { font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--meta); }
.postcard-copy h3 { font-size: clamp(1.25rem, 2vw, 1.6rem); letter-spacing: -0.015em; }
.postcard-in:hover h3, .postcard-in:focus-visible h3 { color: var(--accent-text); }
.postcard-text { color: var(--meta); font-size: 1rem; max-width: 46ch; }
/* Pushed to the foot so the read-more sits on one line across cards of
   different copy lengths. */
.postcard-more { margin-top: auto; padding-top: .5rem; }
.postcard-more svg { transition: transform var(--t-ui) var(--ease-out); }
.postcard-in:hover .postcard-more svg, .postcard-in:focus-visible .postcard-more svg { transform: translateX(4px); }

@media (prefers-reduced-motion: reduce) {
  .postcard-in, .postcard-art img, .postcard-more svg { transition: none; }
  .postcard-in:hover, .postcard-in:focus-visible { transform: none; }
  .postcard-in:hover .postcard-art img { transform: none; }
}

.bands { border-top: 1px solid var(--border); }
.band-row { display: grid; grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); gap: 1.1rem 3rem; padding: 1.85rem 0; border-bottom: 1px solid var(--border); }
.band-row h3 { font-size: clamp(1.125rem, 1.9vw, 1.5rem); }
.band-row p { color: var(--meta); max-width: 62ch; }
@media (max-width: 760px) { .band-row { grid-template-columns: 1fr; gap: .55rem; } }

/* ------------------------------------------------------- service page bits */
.crumbs { font-size: .8125rem; color: var(--meta); padding-top: 2.25rem; }
.crumbs ol { display: flex; gap: .5rem; flex-wrap: wrap; list-style: none; margin: 0; padding: 0; }
.crumbs a { color: var(--meta); }
.two-col { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(1.75rem, 4vw, 3.5rem); }
@media (max-width: 860px) { .two-col { grid-template-columns: 1fr; } }
.col-h { font-size: clamp(1.5rem, 2.6vw, 2rem); margin-bottom: 1.5rem; }
.tick { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.tick li { padding-left: 1.6rem; position: relative; color: var(--meta); }
.tick li::before { content: ""; position: absolute; left: 0; top: .6em; width: .6rem; height: 1px; background: var(--mocha); }
.tick.no li::before { top: .62em; width: .6rem; height: .6rem; border-radius: 50%; border: 1px solid var(--oak); background: transparent; }
.note { margin-top: 1.5rem; padding: 1.1rem 1.25rem; background: var(--wash); border-left: 2px solid var(--taupe); font-size: .9375rem; color: var(--quartz); max-width: 60ch; }
.note p + p { margin-top: .9rem; }
.note em { color: var(--ink); }
.next { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; background: transparent; border: 0; }
@media (max-width: 760px) { .next { grid-template-columns: 1fr; } }

/* -------------------------------------------------------- blog / recursos */
.posts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.25rem, 3vw, 2.25rem); }
@media (max-width: 860px) { .posts { grid-template-columns: 1fr; } }
.post { display: grid; gap: .7rem; align-content: start; border-top: 2px solid var(--ink); padding-top: 1.1rem; color: inherit; transition: border-color .25s; }
.post:hover { border-color: var(--mocha); text-decoration: none; }
.post:hover h3 { color: var(--accent-text); }
.post .kicker { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; font-size: .75rem; letter-spacing: .12em; text-transform: uppercase; color: var(--meta); }
.chip { background: var(--wash); border: 1px solid var(--oak); border-radius: 99px; padding: .15em .65em; font-size: .6875rem; letter-spacing: .08em; color: var(--quartz); text-transform: none; }
.chip.draft { border-style: dashed; }
.post h3 { font-size: clamp(1.125rem, 1.7vw, 1.375rem); }
.post p { color: var(--meta); font-size: .9375rem; }
.post .arrow-link { padding-top: .35rem; }
.post-lead { grid-column: span 3; border-top: 2px solid var(--mocha); }
@media (max-width: 860px) { .post-lead { grid-column: auto; } }
.post-lead h3 { font-size: clamp(1.5rem, 3vw, 2.25rem); max-width: 32ch; }
.post-lead p { font-size: 1.0625rem; max-width: 56ch; }

/* ------------------------------------------------ spec table (build only) */
/* Named .spec-table, not .grid: the export uses .grid for the card grid. */
.table-wrap { overflow-x: auto; margin: 1.5rem 0 0; }
.spec-table { border-collapse: collapse; width: 100%; min-width: 30rem; font-size: .96875rem; }
.spec-table th, .spec-table td { text-align: left; padding: .85rem 1rem .85rem 0; border-top: 1px solid var(--border); vertical-align: top; }
.spec-table th { font-weight: 500; white-space: nowrap; padding-right: 1.75rem; }
.spec-table td { color: var(--meta); }
.spec-table .st { color: var(--meta); font-size: .875rem; white-space: nowrap; }

/* --------------------------------------------- legal outline (build only) */
/* The old decimal list indented its content 22px past every other section on
   the page, which is what read as the layout drifting on scroll. The number
   now sits in a fixed gutter and the text starts on the same line as the rest. */
.legal-outline { list-style: none; counter-reset: lo; margin: 1.6rem 0 0; padding: 0; }
.legal-outline li {
  padding: .65rem 0 .65rem 2.5rem; border-top: 1px solid var(--border);
  position: relative; counter-increment: lo;
}
.legal-outline li::before {
  content: counter(lo, decimal-leading-zero);
  position: absolute; left: 0; top: .65rem;
  font-size: .8125rem; color: var(--accent-text); font-variant-numeric: tabular-nums;
  line-height: 1.6;
}
.legal-outline h2, .legal-outline h3 { font-size: 1.0625rem; font-weight: 500; letter-spacing: 0; max-width: none; margin: 0; }
.pending { color: var(--meta); margin: .25rem 0 0; font-size: .9375rem; }

/* Article sections that carry a list or a table alongside their prose. */
.prose-list { margin-top: 1.25rem; max-width: 68ch; }
.sec .table-wrap + .body-copy { margin-top: 1.5rem; }
.sec .spec-table thead th {
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--quartz);
  white-space: normal;
}

/* ----------------------------------------------------------- split cards */
/* Large rectangles: picture one side, short copy the other, alternating down
   the page. Replaced the numbered rows for "How to hire us". */
.splits { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); }
.split {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  background: var(--white); border: 1px solid var(--hair);
  border-radius: var(--r-panel); overflow: hidden;
  transition: border-color var(--t-ui) var(--ease-out), box-shadow var(--t-panel) var(--ease-out);
}
.split:hover { border-color: var(--taupe); box-shadow: 0 26px 52px -34px rgba(44, 36, 32, .55); }
/* Alternate which side the picture sits on. `order` rather than reversing the
   column list, so the DOM stays picture-then-copy and a screen reader hears
   the same sequence on every card. */
.split.is-flipped .split-art { order: 2; }
.split-art { background: var(--wash); min-height: 17rem; overflow: hidden; }
.split-art img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--t-panel) var(--ease-out);
}
.split:hover .split-art img { transform: scale(1.04); }
.split-body {
  padding: clamp(1.75rem, 3.4vw, 3rem);
  display: flex; flex-direction: column; justify-content: center; gap: .55rem;
}
.split-n {
  font-size: .8125rem; letter-spacing: .12em; color: var(--accent-text);
  font-variant-numeric: tabular-nums; margin-bottom: .2rem;
}
.split-body h3 { font-size: clamp(1.5rem, 2.6vw, 2.125rem); letter-spacing: -0.022em; }
.split-lede { color: var(--meta); font-size: clamp(1rem, 1.2vw, 1.0625rem); max-width: 42ch; }
.split-points { margin-top: .8rem; gap: .5rem; }
.split-points li { font-size: .9375rem; }
.split-more { margin-top: 1.1rem; }
.splits-close { margin-top: 1.75rem; max-width: 62ch; }
@media (max-width: 820px) {
  .split { grid-template-columns: 1fr; }
  /* On one column the picture always leads, flipped or not. */
  .split.is-flipped .split-art { order: 0; }
  .split-art { min-height: 13rem; aspect-ratio: 3 / 2; }
}
@media (prefers-reduced-motion: reduce) {
  .split, .split-art img { transition: none; }
  .split:hover .split-art img { transform: none; }
}

/* --------------------------------------------------------- foundation band */
/* Sits under the four services and says what runs beneath all of them.
   Distinct from the `.band` CTA on purpose: a heavier mocha rule along the top
   edge so it reads as a floor rather than another offer, and no buttons. */
.foundation {
  background: var(--wash);
  border-top: 3px solid var(--mocha);
  border-bottom: 1px solid var(--hair);
}
.foundation-in {
  max-width: var(--max); margin: 0 auto;
  padding: clamp(2.75rem, 5.5vw, 4.5rem) var(--pad);
}
.foundation .eyebrow { margin-bottom: 1rem; }
/* One long sentence, so it is set as a lede rather than a headline — a
   38-word line at headline size is a wall, not a statement. */
.foundation h2 {
  font-size: clamp(1.25rem, 2.1vw, 1.75rem); line-height: 1.42;
  letter-spacing: -0.012em; font-weight: 500; max-width: 58ch; color: var(--ink);
}
.foundation-more { margin-top: 1.5rem; }

/* ------------------------------------------------------------- sitemap */
/* The whole site as grouped columns, on the Infosys pattern. Columns fill by
   available width rather than a fixed count, so the group list can grow. */
.sitemap {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: clamp(1.75rem, 3.5vw, 3rem);
}
.sm-group { min-width: 0; border-top: 2px solid var(--ink); padding-top: 1.1rem; }
.sm-group h2 {
  font-size: .75rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text); font-weight: 500; margin-bottom: 1rem;
}
.sm-intro { color: var(--meta); font-size: .875rem; margin: -.5rem 0 1rem; }
.sm-group ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.sm-group li { border-bottom: 1px solid var(--border); }
.sm-group li:first-child { border-top: 1px solid var(--border); }
.sm-group a {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  padding: .7rem 0; color: var(--ink); font-size: .96875rem;
  transition: color var(--t-ui) var(--ease-out), padding-left var(--t-ui) var(--ease-out);
}
.sm-group a:hover { color: var(--accent-text); text-decoration: none; padding-left: .35rem; }
/* The arrow is the affordance; it only commits on hover so the columns stay
   quiet when you are scanning them. */
.sm-group a svg { flex: none; opacity: 0; transform: translateX(-3px); transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out); }
.sm-group a:hover svg, .sm-group a:focus-visible svg { opacity: 1; transform: none; }
.sm-note {
  display: block; font-size: .6875rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--meta); padding: 0 0 .6rem;
}
.sm-foot { margin-top: 2.5rem; color: var(--meta); font-size: .9375rem; max-width: 60ch; }
@media (prefers-reduced-motion: reduce) {
  .sm-group a, .sm-group a svg { transition: none; }
  .sm-group a:hover { padding-left: 0; }
}

/* ------------------------------------------------- published policy pages */
/* The identification block a Colombian policy has to carry: who the controller
   is, where, how to reach them, when the text was last changed. */
.policy-meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem 2rem; margin: 0 0 2rem; padding: 1.25rem 1.5rem;
  background: var(--wash); border-radius: var(--r-card);
}
.policy-meta div { min-width: 0; }
.policy-meta dt {
  font-size: .6875rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--quartz); margin-bottom: .3rem;
}
.policy-meta dd { margin: 0; font-size: .9375rem; color: var(--ink); overflow-wrap: anywhere; }
.policy-intro { margin: 1.75rem 0 0; }

/* Sections carry real prose now, so they need room the outline never did. */
.policy-list li { padding-block: 1.5rem; }
.policy-list h2 {
  font-size: clamp(1.125rem, 1.6vw, 1.3125rem); font-weight: 600;
  letter-spacing: -0.01em; margin-bottom: .7rem;
}
.policy-list p { color: var(--meta); max-width: 68ch; }
.policy-list p + p { margin-top: .85rem; }
.policy-list .tick { margin-top: .9rem; }
.policy-list .table-wrap { margin-top: 1.1rem; }
.policy-list .spec-table th { white-space: normal; min-width: 9rem; }
.policy-list .spec-table thead th {
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--quartz);
}
@media (max-width: 560px) { .policy-list li { padding-left: 0; } .policy-list li::before { position: static; display: block; margin-bottom: .35rem; } }

/* ------------------------------------------------------- form (build only) */
/* The contact page is a lede, an email and this form — nothing else. The form
   is set as a white panel on the bone ground so it reads as the page's one
   piece of business rather than as leftovers in a wide column. */
/* On a page that is only a lede, an email and a form, the standard section
   rhythm leaves ~100px holes between each. Pull the whole run together. */
.sec-form { padding-block: 0 var(--gap); }
.sec-form .sec-head { margin-bottom: 1.1rem; }
.hero:has(+ .sec-form) { padding-block: clamp(1.25rem, 2.5vw, 2rem) clamp(1.5rem, 3vw, 2.25rem); }
.hero:has(+ .sec-form) .hero-cta { margin-top: 1.35rem; }
.crumbs:has(~ .hero + .sec-form) { padding-top: 1.5rem; }
.contact-form {
  margin: 0; max-width: 46rem;
  background: var(--white); border: 1px solid var(--hair); border-radius: var(--r-panel);
  padding: clamp(1.5rem, 3vw, 2.5rem);
}
.contact-form fieldset { border: 0; margin: 0; padding: 0; }
.field { margin: 0 0 1rem; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 640px) { .field-row { grid-template-columns: 1fr; } }
.field label { display: block; font-size: .875rem; font-weight: 500; margin-bottom: .375rem; }
.req { color: var(--accent-text); }
.field input, .field select, .field textarea {
  width: 100%; font-family: inherit; font-size: .96875rem; color: var(--ink);
  padding: .75rem .875rem; background: var(--paper);
  border: 1px solid var(--field-border); border-radius: var(--r-btn);
}
.field textarea { resize: vertical; min-height: 8rem; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--tapestry); box-shadow: 0 0 0 3px rgba(67, 99, 116, .18);
}
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"], .field select[aria-invalid="true"] {
  border-color: #b3391f; box-shadow: 0 0 0 3px rgba(179, 57, 31, .12);
}
.consent { margin-top: 1.4rem; }
.check { display: flex; gap: .7rem; align-items: flex-start; font-weight: 400; }
.check input { width: 1.125rem; height: 1.125rem; flex: none; margin-top: .2rem; accent-color: var(--quartz); }
.check span { font-size: .875rem; color: var(--meta); line-height: 1.55; }
.check a { color: var(--link); }
.legal-draft { display: block; margin: .625rem 0 0 1.8rem; font-size: .8125rem; color: #8a5a12; background: var(--wash); padding: .5rem .75rem; border-radius: 2px; }
.form-ok { color: #1f6b3b; }
.form-err { color: #b3391f; }

/* ------------------------------------------------------------------ footer */
footer { background: var(--quartz); color: var(--paper); }
footer a { color: var(--footer-muted); }
footer a:hover { color: var(--paper); }
.foot-in {
  max-width: var(--max); margin: 0 auto; padding: clamp(2.75rem, 5vw, 4rem) var(--pad) 2rem;
  display: grid; gap: clamp(1.75rem, 4vw, 3rem); grid-template-columns: 1.25fr .9fr .9fr 1.35fr;
}
@media (max-width: 900px) { .foot-in { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .foot-in { grid-template-columns: 1fr; } }
.foot-col h2 { font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; color: var(--footer-muted); margin-bottom: 1rem; font-weight: 500; }
.foot-follow { margin-top: 1.75rem; }
.social { display: flex; flex-wrap: wrap; gap: .45rem; }
.social a {
  display: grid; place-items: center; width: 2rem; height: 2rem;
  border: 1px solid rgba(244, 239, 230, .3); border-radius: 50%; color: var(--footer-muted);
  transition: background .2s, color .2s, border-color .2s, transform .25s cubic-bezier(.16, 1, .3, 1);
}
.social a:hover { background: var(--paper); color: var(--quartz); border-color: var(--paper); transform: translateY(-2px); }
.social svg { width: 1.05rem; height: 1.05rem; }
/* A channel with no account yet: same circle, dimmed, not clickable. */
.social .is-pending {
  display: grid; place-items: center; width: 2rem; height: 2rem;
  border: 1px dashed rgba(244, 239, 230, .28); border-radius: 50%;
  color: rgba(244, 239, 230, .45); cursor: default;
}
.foot-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; font-size: .9375rem; }
/* On the quartz footer the brown half would vanish, so "Sau" takes the light
   warm tone there — same two-part split, legible on a dark ground. */
/* Brand lockup: the Sau5 mark, then the name. */
.foot-brand .mark {
  color: var(--paper); font-size: 1.5rem;
  display: inline-flex; align-items: center; gap: .6rem;
}
.foot-brand .mark i { color: var(--footer-muted); }
.mark .sau5 { height: .82em; width: auto; display: block; flex: none; }
/* The footer ground is --quartz, so the supplied dark-ink artwork has to be
   repainted or it disappears. The counter inside the "a" is a hole and takes
   the ground it sits on. */
.foot-brand .s5-ink { fill: var(--paper); }
.foot-brand .s5-five { fill: var(--footer-muted); }
.foot-brand .s5-hole { fill: var(--quartz); }
.foot-brand p { margin-top: .9rem; color: var(--footer-muted); font-size: .9375rem; max-width: 26ch; }
.foot-brand .short { margin-top: .75rem; color: var(--footer-muted); font-size: .875rem; }
.foot-bar {
  border-top: 1px solid #8a7871; max-width: var(--max); margin: 0 auto;
  padding: 1.25rem var(--pad) 2.5rem; display: flex; flex-wrap: wrap; gap: .75rem 1.5rem;
  justify-content: space-between; align-items: baseline; font-size: .8125rem; color: var(--footer-muted);
}
.legal-links { display: flex; flex-wrap: wrap; gap: .4rem 1.15rem; list-style: none; margin: 0; padding: 0; }
.foot-copy { white-space: nowrap; }

/* --------------------------------------------------- motion / transitions */
.reveal {
  opacity: 0; transform: translateY(1.75rem);
  transition: opacity .8s cubic-bezier(.16, 1, .3, 1), transform .8s cubic-bezier(.16, 1, .3, 1);
  transition-delay: var(--d, 0s);
}
.reveal.in { opacity: 1; transform: none; }
main { animation: page-enter .55s cubic-bezier(.16, 1, .3, 1) both; }
@keyframes page-enter { from { opacity: 0; transform: translateY(1.75rem); } }
.hero-copy > * { animation: heroIn .85s cubic-bezier(.16, 1, .3, 1) both; }
.hero-copy > .eyebrow { animation-delay: .42s; }
.hero-copy > h1 { animation-delay: .56s; }
.hero-copy > .hero-grid, .hero-copy > .lede { animation-delay: .74s; }
.hero-copy > .cta-row { animation-delay: .9s; }
@keyframes heroIn { from { opacity: 0; transform: translateY(1.25rem); } to { opacity: 1; transform: none; } }
.hero-copy .eyebrow::before { transform: scaleX(0); transform-origin: left; animation: drawRule .75s cubic-bezier(.16, 1, .3, 1) .62s both; }
@keyframes drawRule { to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .hero-bg, .hero-copy, .hero-copy > *, .hero-copy .eyebrow::before, .scrollcue .cue-bar { animation: none; }
  main { animation: none; }
  /* Beats .armed on specificity, so a reduced-motion reader gets the sentence
     whole even if JS armed it before the query was evaluated. */
  .statement p .w i, .statement p.armed .w i { transform: none; transition: none; }
  .hero-copy .eyebrow::before { transform: none; }
  .hero-copy { animation: none; }
}
