/* ============================================================
   A2S FINANCIAL — "Fine Ledger" Design System
   Premium corporate × modern fintech. Forest-ink + deep emerald.
   Newsreader (display serif) · Hanken Grotesk (body) · Libre Franklin (figures)
   ============================================================ */

/* ----------------------------------------------------------------
   1. TOKENS
   ---------------------------------------------------------------- */
:root {
  /* Surfaces */
  --bg: #F4F6F5;
  --surface: #FFFFFF;
  --surface-2: #EEF1EF;
  --dark: #0A1310;
  --dark-2: #0F1C17;
  --dark-3: #14271F;

  /* Ink */
  --ink: #0C1512;
  --ink-soft: #404B45;
  --muted: #515C56;
  --on-dark: #F3F6F4;
  --on-dark-soft: #AEBCB4;
  --on-dark-muted: #7E8E85;

  /* Accent — deep emerald */
  --accent: #0E6E4C;
  --accent-strong: #0B5A3E;
  --accent-bright: #2BC089;
  --accent-wash: #E7F1EC;

  /* Lines */
  --line: rgba(12, 21, 18, 0.10);
  --line-soft: rgba(12, 21, 18, 0.06);
  --line-dark: rgba(255, 255, 255, 0.10);
  --line-dark-soft: rgba(255, 255, 255, 0.06);

  /* Radii */
  --r-sm: 0.625rem;
  --r-md: 1rem;
  --r-lg: 1.5rem;
  --r-xl: 2rem;
  --r-pill: 999px;

  /* Shadows — soft, diffused, never harsh */
  --shadow-xs: 0 1px 2px rgba(12, 21, 18, 0.04);
  --shadow-sm: 0 4px 16px -6px rgba(12, 21, 18, 0.10);
  --shadow-md: 0 18px 50px -24px rgba(12, 21, 18, 0.20);
  --shadow-lg: 0 40px 90px -40px rgba(12, 21, 18, 0.30);
  --shadow-accent: 0 18px 40px -18px rgba(14, 110, 76, 0.45);

  /* Motion */
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 180ms;
  --t-med: 420ms;
  --t-slow: 720ms;

  /* Layout */
  --container: 1200px;
  --container-narrow: 920px;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);

  /* Type */
  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --font-sans: "Hanken Grotesk", system-ui, -apple-system, sans-serif;
  --font-figure: "Libre Franklin", "Hanken Grotesk", system-ui, sans-serif;

  /* Z-index scale — systemic only */
  --z-base: 1;
  --z-sticky: 100;
  --z-nav: 200;
  --z-overlay: 300;
  --z-toast: 400;
}

/* ----------------------------------------------------------------
   2. RESET / BASE
   ---------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  color-scheme: light; /* fixed light theme — keep native controls/scrollbars light */
}

body {
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

a { color: inherit; text-decoration: none; }

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

ul { list-style: none; padding: 0; }

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

::selection {
  background: var(--accent);
  color: #fff;
}

/* Skip-to-content link — off-screen until keyboard focus brings it in. */
.skip-link {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: var(--z-toast);
  padding: 0.7rem 1.15rem;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.95rem;
  box-shadow: var(--shadow-md);
  transform: translateY(calc(-100% - 1.5rem));
  transition: transform var(--t-fast) var(--ease);
}
.skip-link:focus { transform: translateY(0); }

/* Lucide icon refinement — keep strokes light & precise */
svg.lucide { stroke-width: 1.6px; }

/* ----------------------------------------------------------------
   3. TYPOGRAPHY
   ---------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 540;
  line-height: 1.04;
  letter-spacing: -0.025em;
  text-wrap: balance;
  color: var(--ink);
}

.display {
  /* 7px smaller across the whole fluid range vs. the previous
     clamp(2.85rem, 6.5vw, 6rem) — min and max both shifted down by
     0.4375rem (7px), vw slope reduced proportionally to match. */
  font-size: clamp(2.41rem, 6.03vw, 5.56rem);
  letter-spacing: -0.035em;
  line-height: 0.98;
}

.h-xl { font-size: clamp(2.25rem, 4.4vw, 3.5rem); }
.h-lg { font-size: clamp(1.9rem, 3.4vw, 2.85rem); }
.h-md { font-size: clamp(1.35rem, 2.2vw, 1.75rem); letter-spacing: -0.02em; }
.h-sm { font-size: 1.2rem; letter-spacing: -0.015em; }

p { text-wrap: pretty; }

.lead {
  font-size: clamp(1.1rem, 1.5vw, 1.3rem);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 60ch;
}

.prose { max-width: 68ch; }
.prose p + p { margin-top: 1.15em; }

.measure { max-width: 65ch; }

/* Ledger label — the signature alternative to a generic eyebrow.
   A leading rule + small tracked-uppercase tag. Used sparingly, not on every section. */
.ledger-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-strong);
  font-weight: 600;
}
.ledger-tag::before {
  content: "";
  width: 2.2rem;
  height: 1px;
  background: var(--accent);
}
.ledger-tag--on-dark { color: var(--accent-bright); }
.ledger-tag--on-dark::before { background: var(--accent-bright); }
.ledger-tag--center { justify-content: center; }

.text-accent { color: var(--accent-strong); }
.serif-italic { font-family: var(--font-display); font-style: italic; }

/* ----------------------------------------------------------------
   4. LAYOUT PRIMITIVES
   ---------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: var(--container-narrow); }

.section { padding-block: clamp(4.5rem, 9vw, 8.5rem); }
/* Why A2S Financial (home) — padding cut so the total band height (not just
   the padding value) is ~15% shorter; content height is fixed, so the
   padding itself is cut proportionally harder to net that reduction. */
.section--why-compact { padding-block: clamp(2.6rem, 5.2vw, 4.9rem); }
.section--tight { padding-block: clamp(3rem, 6vw, 5rem); }
/* Services, Industries, About, Contact only — section padding cut 25% vs.
   the site default, so body content sits closer to the (now-shorter) hero
   and to itself between sections. Home is unaffected. */
.main--tight-sections .section { padding-block: clamp(3.375rem, 6.75vw, 6.375rem); }
.main--tight-sections .section--tight { padding-block: clamp(2.25rem, 4.5vw, 3.75rem); }
/* The section immediately after the page hero (Industries, Services, Contact)
   matches the hero-to-content gap on About, where the equivalent first
   section uses .section--industry-rhythm's clamp(3rem, 6vw, 5rem) top pad
   instead of the taller site-wide .section default above. */
.main--tight-sections .hero--page + .section { padding-top: clamp(3rem, 6vw, 5rem); }
/* Halves the gap between the last content section and the CTA banner that
   follows it, standardized to the same final amount across every page that
   has this content-then-CTA pattern (Industries, Home, Services, About). */
.main--tight-sections .section--cta-lead { padding-bottom: clamp(1.6875rem, 3.375vw, 3.1875rem); }
.main--tight-sections .cta-banner--tight-top { padding-top: clamp(1.125rem, 2.25vw, 1.875rem); }
/* Home isn't scoped by .main--tight-sections and starts from a different
   baseline (both sides at the site-wide .section--tight value); calibrated
   separately here to land on the same final gap as the rule above. */
.section--cta-lead { padding-bottom: clamp(1.41rem, 2.81vw, 2.34rem); }
.cta-banner--tight-top { padding-top: clamp(1.41rem, 2.81vw, 2.34rem); }
.section--rule-top > .container {
  border-top: 1px solid var(--line);
  padding-top: clamp(3rem, 6vw, 5rem);
}
/* Matches the rhythm between industry blocks on the Industries page:
   content -> padding-bottom -> divider (flush, no extra gap) -> padding-top
   -> content, using the same clamp(3rem, 6vw, 5rem) on both sides. The
   divider lives on .container (width-constrained), so when paired with
   --rule-top the section's own leading padding collapses to 0 and the full
   gap moves after the line via the rule above, keeping the border flush
   against the previous section's trailing edge instead of floating
   mid-gap. */
.section--industry-rhythm { padding-block: clamp(3rem, 6vw, 5rem); }
.section--industry-rhythm.section--rule-top { padding-top: 0; }
/* Home: gap between the pull quote and the testimonials that follow it,
   trimmed 50% from the site default. Scoped via :has() to this specific
   section pair so it doesn't touch .section--industry-rhythm's other Home
   uses (Who we serve, Services teaser) or .section--tight's use on the CTA
   banner below the testimonials. */
.section--industry-rhythm:has(+ .section--cta-lead) { padding-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.section--cta-lead:has(.testi-grid) { padding-top: clamp(1.5rem, 3vw, 2.5rem); }
.section--dark {
  background: var(--dark);
  color: var(--on-dark);
}
.section--dark h1,
.section--dark h2,
.section--dark h3,
.section--dark h4 { color: var(--on-dark); }

.section-head { max-width: 56ch; }
/* Spans the full container width (flush with the wordmark on the left and
   the nav CTA on the right) instead of the default readable-prose cap —
   used where the heading (and its sub-line) should read as one full-width
   line rather than wrap. */
.section-head--wide { max-width: none; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head__title { margin-top: 1.1rem; }
.section-head__sub { margin-top: 1.25rem; color: var(--ink-soft); font-size: 1.1rem; }
.section--dark .section-head__sub { color: var(--on-dark-soft); }

.stack-sm > * + * { margin-top: 0.75rem; }
.stack > * + * { margin-top: 1.25rem; }
.stack-lg > * + * { margin-top: 2rem; }

/* ----------------------------------------------------------------
   5. BUTTONS & LINKS
   ---------------------------------------------------------------- */
.btn {
  --btn-bg: var(--accent);
  --btn-fg: #fff;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  min-height: 2.5rem; /* >=40px hit area */
  padding: 0.8rem 0.85rem 0.8rem 1.5rem;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 600;
  font-size: 0.98rem;
  letter-spacing: -0.01em;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform var(--t-fast) var(--ease),
              box-shadow var(--t-med) var(--ease),
              background var(--t-fast) var(--ease);
  box-shadow: var(--shadow-accent);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0) scale(0.96); } /* tactile press */

.btn__icon {
  width: 2rem;
  height: 2rem;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.18);
  display: grid;
  place-items: center;
  flex: none;
  transition: transform var(--t-med) var(--ease), background var(--t-fast) var(--ease);
}
.btn__icon svg { width: 1rem; height: 1rem; }
.btn:hover .btn__icon { transform: translate(3px, -2px); background: rgba(255, 255, 255, 0.28); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line);
}
.btn--ghost .btn__icon { background: rgba(12, 21, 18, 0.06); }
.btn--ghost:hover { box-shadow: inset 0 0 0 1px var(--ink); }
.btn--ghost:hover .btn__icon { background: rgba(12, 21, 18, 0.10); }

.btn--on-dark {
  --btn-bg: #fff;
  --btn-fg: var(--ink);
  box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.6);
}
.btn--on-dark .btn__icon { background: rgba(12, 21, 18, 0.08); }

.btn--sm { padding: 0.55rem 0.6rem 0.55rem 1.1rem; font-size: 0.9rem; }
.btn--sm .btn__icon { width: 1.65rem; height: 1.65rem; }
.btn--sm .btn__icon svg { width: 0.85rem; height: 0.85rem; }

.btn--block { width: 100%; justify-content: center; }

/* Text link with sliding arrow */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 600;
  color: var(--accent-strong);
  letter-spacing: -0.01em;
  transition: gap var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.link-arrow svg { width: 1.1em; height: 1.1em; transition: transform var(--t-med) var(--ease); }
.link-arrow:hover { gap: 0.7rem; }
.link-arrow:hover svg { transform: translateX(3px); }
.link-arrow--on-dark { color: var(--accent-bright); }

/* ----------------------------------------------------------------
   6. NAVIGATION
   ---------------------------------------------------------------- */
.nav-sentinel {
  position: absolute;
  top: 0;
  height: 1px;
  width: 1px;
  pointer-events: none;
}

.nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  transition: background var(--t-med) var(--ease),
              box-shadow var(--t-med) var(--ease),
              border-color var(--t-med) var(--ease);
  border-bottom: 1px solid transparent;
}
.nav__inner {
  display: flex;
  align-items: center;
  /* The link group and CTA cluster together on the right (see .brand's
     margin-right below); this is the gap between that cluster's own parts
     — nav links to each other, and the last link to the CTA — kept tight
     and uniform so nothing reads as adrift in the middle of the bar. */
  gap: 0.5rem;
  height: 78px;
}

/* Brand — text-only wordmark, sized to match the nav CTA button's own
   height so it reads as an equally prominent element in the bar. Pinned to
   the left edge via margin-right:auto, which pushes the nav links + CTA
   into one right-aligned cluster instead of leaving the links floating in
   the bar's free space. */
.brand {
  display: inline-flex;
  align-items: center;
  position: relative;
  top: 3px; /* optical nudge down from the font's own metrics */
  margin-right: auto;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 2.145rem;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--on-dark);
  transition: color var(--t-med) var(--ease);
}
.brand b { font-weight: 600; }
.brand span { font-weight: 400; opacity: 0.85; }

/* Links */
.nav__links {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem; /* >=40px hit area */
  padding: 0.5rem 0.9rem;
  font-size: 0.94rem;
  font-weight: 500;
  color: var(--on-dark-soft);
  border-radius: var(--r-sm);
  transition: color var(--t-fast) var(--ease);
}
.nav__link:hover { color: var(--on-dark); }
.nav__link::after {
  content: "";
  position: absolute;
  left: 0.9rem;
  right: 0.9rem;
  bottom: 0.2rem;
  height: 2px;
  background: var(--accent-bright);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-med) var(--ease);
}
.nav__link[aria-current="page"] { color: var(--on-dark); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

.nav__right { display: flex; align-items: center; gap: 0.75rem; }

/* Scrolled state — solid white, dark text, soft shadow */
.nav.is-scrolled {
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  box-shadow: var(--shadow-sm);
  border-bottom-color: var(--line-soft);
}
.nav.is-scrolled .brand { color: var(--ink); }
.nav.is-scrolled .nav__link { color: var(--muted); }
.nav.is-scrolled .nav__link:hover,
.nav.is-scrolled .nav__link[aria-current="page"] { color: var(--ink); }
.nav.is-scrolled .nav__link::after { background: var(--accent); }

/* Hamburger */
.nav__toggle {
  display: none;
  width: 2.75rem;
  height: 2.75rem;
  background: none;
  border: none;
  cursor: pointer;
  position: relative;
  border-radius: var(--r-sm);
}
.nav__toggle span {
  position: absolute;
  left: 0.6rem;
  right: 0.6rem;
  height: 1.6px;
  background: var(--on-dark);
  border-radius: 2px;
  transition: transform var(--t-med) var(--ease), opacity var(--t-fast) var(--ease), background var(--t-med) var(--ease);
}
.nav__toggle span:nth-child(1) { top: 1rem; }
.nav__toggle span:nth-child(2) { top: 1.35rem; }
.nav__toggle span:nth-child(3) { top: 1.7rem; }
.nav.is-scrolled .nav__toggle span { background: var(--ink); }

body.menu-open .nav__toggle span:nth-child(1) { transform: translateY(0.35rem) rotate(45deg); background: var(--on-dark); }
body.menu-open .nav__toggle span:nth-child(2) { opacity: 0; }
body.menu-open .nav__toggle span:nth-child(3) { transform: translateY(-0.35rem) rotate(-45deg); background: var(--on-dark); }

/* ----------------------------------------------------------------
   7. MOBILE MENU OVERLAY
   ---------------------------------------------------------------- */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: rgba(10, 19, 16, 0.92);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 6rem var(--gutter) 3rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-med) var(--ease), visibility var(--t-med) var(--ease);
}
body.menu-open .mobile-menu { opacity: 1; visibility: visible; }

.mobile-menu__links { display: flex; flex-direction: column; gap: 0.25rem; }
.mobile-menu__link {
  font-family: var(--font-display);
  font-size: clamp(2rem, 9vw, 2.85rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--on-dark);
  padding: 0.4rem 0;
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
.mobile-menu__link[aria-current="page"] { color: var(--accent-bright); }
body.menu-open .mobile-menu__link {
  opacity: 1;
  transform: translateY(0);
  transition-delay: calc(120ms + var(--i, 0) * 70ms);
}
.mobile-menu__foot {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line-dark);
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
body.menu-open .mobile-menu__foot {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 0.55s;
}
.mobile-menu__foot a { color: var(--on-dark-soft); font-family: var(--font-sans); font-size: 0.9rem; }

/* ----------------------------------------------------------------
   8. HERO
   ---------------------------------------------------------------- */
.hero {
  position: relative;
  background: var(--dark);
  color: var(--on-dark);
  overflow: hidden;
  isolation: isolate;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(60% 50% at 50% -8%, rgba(43, 192, 137, 0.16), transparent 70%),
    radial-gradient(40% 40% at 88% 10%, rgba(43, 192, 137, 0.08), transparent 70%);
}
/* Content-driven height — was min-height: 100dvh, which forces the hero to
   always fill exactly one full screen. Since the content itself is a fixed
   height, that meant the empty space around it grew or shrank with the
   viewer's own monitor (e.g. visibly taller/emptier on a QHD display than
   on 1080p). Fixed padding keeps the hero a consistent size everywhere.
   Top padding reuses .hero--page's own clamp formula so the kicker tag
   lines up at the same height as "The full engagement" / "Industry
   expertise" / "Who we are" / "Free consultation" on the other four pages
   at every viewport width, not just one. Bottom padding matches
   .hero__stats' own padding-top (2.25rem — the gap between the grey rule
   and the stats) so the stat row sits centered between two equal gaps:
   rule-to-stats above, stats-to-bottom below. */
.hero--home {
  padding-block: clamp(3rem, 5.6vw, 4.4rem) 2.25rem;
}
/* Padding cut ~32% off the total band height vs. the original (content
   height is fixed, so the padding itself is cut proportionally harder to
   hit that overall reduction). */
.hero--page { padding-block: clamp(3rem, 5.6vw, 4.4rem) clamp(1.4rem, 2.8vw, 2.4rem); }
/* Contact's title is short enough to stay on one line where Services'
   wraps to two at the same width, leaving its hero shorter. This adds the
   difference back so all four page heroes read the same height. */
.hero--page--tall { padding-bottom: calc(clamp(1.4rem, 2.8vw, 2.4rem) + 3.64rem); }

.hero__inner { position: relative; z-index: var(--z-base); max-width: 56rem; }
.hero__title { margin-top: 1.6rem; color: var(--on-dark); }
.hero__title .accent-word { color: var(--accent-bright); font-style: italic; }
/* Rotating first line — width is locked to the widest word in JS (matching
   the same measure-then-lock technique used for the hero stat counters) so
   the line never reflows as shorter/longer words cycle through. The exit/
   enter animation itself is driven via direct inline-style transitions in
   scripts.js (slide + fade + blur), not CSS classes, so it can sequence a
   forced-reflow "swap" between the two phases. */
/* 10px smaller than the inherited .display size, on this line only — all
   three clamp terms offset by 0.625rem (10px) so the gap holds at every
   viewport width, not just at the clamped min/max ends. */
.hero__rotator { display: inline-block; color: var(--on-dark); font-size: clamp(1.785rem, calc(6.03vw - 0.625rem), 4.935rem); }
.hero__title-static { color: var(--accent-bright); }
.hero__sub {
  margin-top: 1.7rem;
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  line-height: 1.7; /* light-on-dark needs more leading */
  color: var(--on-dark);
  max-width: 46ch;
}
.hero__actions {
  margin-top: 2.5rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}

/* ----------------------------------------------------------------
   HERO DISPLAY CARDS — native recreation of a shadcn "display-cards"
   pattern (three fanned, skewed cards) in plain HTML/CSS. No React, no
   Tailwind, no build step: everything below is the same visual language
   (skewY, per-card translate offsets, grid-stacking, grayscale-until-
   hover on the back two) reproduced with the site's own tokens.
   Desktop only — three transform-offset cards don't translate cleanly to
   a narrow viewport; mobile keeps the existing single-column hero. */
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}
@media (max-width: 960px) {
  .hero__grid { grid-template-columns: 1fr; }
}

.hero-cards {
  display: none;
  position: relative;
  height: 19rem;
}
@media (min-width: 961px) {
  .hero-cards { display: grid; place-items: center; }
}

.hero-card {
  grid-area: 1 / 1;
  position: relative;
  width: clamp(14rem, 22vw, 21rem);
  height: 9rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0.3rem;
  padding: 0.85rem 1.1rem;
  border-radius: var(--r-lg);
  border: 1.5px solid var(--line-dark);
  background: rgba(20, 39, 31, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--on-dark);
  user-select: none;
  overflow: hidden; /* clips ::after's gradient to the card's own rounded
                        corners so the top-right stays a clean curve
                        instead of the gradient's straight rectangular
                        edge cutting across it. */
  transform: skewY(-8deg) translate(var(--tx, 0), var(--ty, 0));
  transition: transform var(--t-slow) var(--ease),
              filter var(--t-slow) var(--ease),
              border-color var(--t-med) var(--ease),
              background var(--t-med) var(--ease);
}
.hero-card::after {
  content: "";
  position: absolute;
  top: -5%;
  right: -0.25rem;
  width: 14rem;
  height: 110%;
  background: linear-gradient(to left, var(--dark), transparent);
  opacity: 0.3; /* full-strength gradient was dark enough to dim the
                   __date tag sitting in the top-right corner above it */
  pointer-events: none;
}
.hero-card:hover { border-color: rgba(255, 255, 255, 0.22); background: rgba(20, 39, 31, 0.9); }

/* Stack order, back to front. Positions fan up-left to down-right;
   z-index matches so later cards visually sit on top of earlier ones. */
.hero-card--0 { --tx: -6rem; --ty: -5rem;   z-index: 0; }
.hero-card--1 { --tx: -3rem; --ty: -2.5rem; z-index: 1; }
.hero-card--2 { --tx: 0rem;  --ty: 0rem;    z-index: 2; }
.hero-card--3 { --tx: 3rem;  --ty: 2.5rem;  z-index: 3; }
.hero-card--4 { --tx: 6rem;  --ty: 5rem;    z-index: 4; }

/* Exactly one card is ever shown in color: the front card (--4) by
   default, or whichever card is currently hovered — every other card,
   including the front one, turns grayscale with a dark scrim the moment
   any card in the stack is hovered. */
.hero-card { filter: grayscale(1); }
.hero-card--4 { filter: none; }
.hero-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid var(--line-dark);
  background: rgba(10, 19, 16, 0.55);
  opacity: 1;
  transition: opacity var(--t-slow) var(--ease);
  pointer-events: none;
}
.hero-card--4::before { opacity: 0; }

.hero-cards:hover .hero-card { filter: grayscale(1); }
.hero-cards:hover .hero-card::before { opacity: 1; }
.hero-cards .hero-card:hover { filter: grayscale(0); }
.hero-cards .hero-card:hover::before { opacity: 0; }

.hero-card--0:hover { transform: skewY(-8deg) translate(-6rem, -7.5rem); }
.hero-card--1:hover { transform: skewY(-8deg) translate(-3rem, -5rem); }
.hero-card--2:hover { transform: skewY(-8deg) translate(0rem, -2.5rem); }
.hero-card--3:hover { transform: skewY(-8deg) translate(3rem, -0.25rem); }
.hero-card--4:hover { transform: skewY(-8deg) translate(6rem, 2.5rem); }

.hero-card__top { display: flex; align-items: center; gap: 0.55rem; }
.hero-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--accent-strong);
  color: var(--accent-bright);
  flex: none;
}
.hero-card__icon svg { width: 0.85rem; height: 0.85rem; }
/* nowrap on both title and description locks every card's text block to
   the same height regardless of copy length — overflow fades into the
   card's own right-edge gradient (::after) rather than wrapping, so all
   5 cards read at the identical text angle/rhythm off the shared skew. */
.hero-card__title { font-weight: 600; font-size: 1rem; color: var(--accent-bright); white-space: nowrap; }
.hero-card__desc { font-size: 0.95rem; color: var(--on-dark); white-space: nowrap; }
/* Status timestamp/tag — top-right corner, notification-style. Brightens to
   full white on the active card, same trigger as the dot glow below. */
.hero-card__date {
  position: absolute;
  top: 0.7rem;
  right: 1rem;
  font-size: 0.78rem;
  color: var(--on-dark-soft);
  transition: color var(--t-med) var(--ease);
}
.hero-cards:not(:hover) .hero-card--4 .hero-card__date,
.hero-cards .hero-card:hover .hero-card__date {
  color: var(--on-dark);
}
/* Status dot — bottom-left, same visual language as .ledger-card__dot on
   the Industries page. Color is per-card via --dot-color; it only glows
   while its card is the active/colored one (default front card, or
   whichever is hovered) — inactive cards keep a plain, unlit dot. */
.hero-card__dot {
  position: absolute;
  left: 1.1rem;
  bottom: 0.85rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--dot-color, var(--accent-bright));
  opacity: 0.85;
  transition: box-shadow var(--t-med) var(--ease), opacity var(--t-med) var(--ease);
}
.hero-cards:not(:hover) .hero-card--4 .hero-card__dot,
.hero-cards .hero-card:hover .hero-card__dot {
  opacity: 1;
  box-shadow: 0 0 8px 2px var(--dot-color, var(--accent-bright));
}

/* Hero stats inline (home) */
.hero__stats {
  margin-top: 3.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.5rem, 5vw, 4rem);
  padding-top: 2.25rem;
  border-top: 1px solid var(--line-dark);
}
.hstat__num {
  /* Hanken Grotesk (var(--font-sans)) rather than Libre Franklin here on
     purpose: Libre Franklin's webfont ships without tabular figures, so its
     digits change width as the counter animates (the "shake"). Hanken has
     true tabular numerals, so every digit is equal-width and the count-up
     stays rock-steady. */
  font-family: var(--font-sans);
  font-size: clamp(1.9rem, 3.2vw, 2.7rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--on-dark);
  font-variant-numeric: tabular-nums; /* stable digit width while counters animate */
}
.hstat__label {
  margin-top: 0.35rem;
  font-size: 0.92rem;
  color: var(--on-dark-soft);
  max-width: 22ch;
}

/* ----------------------------------------------------------------
   9. REVEAL ANIMATION
   ---------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(2rem);
  filter: blur(6px);
  transition: opacity var(--t-slow) var(--ease-out),
              transform var(--t-slow) var(--ease-out),
              filter var(--t-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}
[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  will-change: auto;
}

/* ----------------------------------------------------------------
   QUICKBOOKS EXPERTISE — badge strip (home)
   ----------------------------------------------------------------
   Overdrive: a single soft light sweeps left-to-right across the row
   (.badge-row__sweep, purely decorative) while each badge clears from
   dim/desaturated to full color on the same staggered delay the sweep
   would be passing over it — one continuous reveal, not 7 separate
   entrances. Content visibility itself still rides the site's proven
   [data-reveal]/.is-visible mechanism (opacity/rise/blur); the
   grayscale-to-color clearing is a reduced-motion-gated flourish layered
   on top, and the sweep light can never hide content even if it fails to
   render or animate. */
.badge-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(1.5rem, 4vw, 2.75rem);
  margin-top: 2.625rem;
}
/* Registers the row with the reveal observer (for timing the sweep) without
   letting it also take on the generic fade/rise/blur treatment — each
   badge already handles its own reveal individually. */
.badge-row[data-reveal] {
  opacity: 1;
  transform: none;
  filter: none;
  transition: none;
}
.badge-row__sweep {
  position: absolute;
  inset: -1rem 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}
.badge-row__sweep::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -14%;
  width: 14%;
  background: radial-gradient(ellipse 55% 100% at 50% 50%, rgba(43, 192, 137, 0.4), transparent 72%);
  filter: blur(14px);
  opacity: 0;
}
.badge-row__item {
  position: relative;
  z-index: 0;
  width: clamp(84px, 9vw, 122px);
  height: clamp(84px, 9vw, 122px);
  object-fit: contain;
  /* Filter duration is deliberately a full, visible fade (--t-med) rather
     than a snap to color — the last badge (fires at the 3000ms
     reveal-delay, see index.html) settles just after the sweep's own 3.1s
     travel time (see badge-sweep below) finishes, which reads fine since
     the sweep itself is already fading out over its final 12%. */
  transition: transform var(--t-med) var(--ease), filter var(--t-med) var(--ease-out);
  transition-delay: 0ms, var(--reveal-delay, 0ms);
}
/* Cancels the generic [data-reveal] translateY(2rem) hidden-state offset.
   IntersectionObserver measures an element's painted (transformed) position,
   so leaving that offset in place shifted each badge's own visibility
   threshold later than the row's — the row (already geometry-stable, see
   .badge-row[data-reveal] above) would cross 12% visible and fire the
   sweep, while the still-offset badges needed extra scroll before THEIR
   thresholds crossed, decoupling the sweep from the badge reveal. Keeping
   badges geometrically stable from first paint (reveal driven by opacity +
   filter only) keeps both triggers locked to the same scroll position. */
.badge-row__item[data-reveal] { transform: none; }
.badge-row__item:hover { transform: translateY(-3px); }

@media (prefers-reduced-motion: no-preference) {
  .badge-row__item { filter: grayscale(1) brightness(0.55) blur(3px); }
  .js .badge-row__item.is-visible { filter: none; }
  .js .badge-row.is-visible .badge-row__sweep::before {
    animation: badge-sweep 3.1s linear both;
  }
}
@keyframes badge-sweep {
  0%   { left: -14%; opacity: 0; }
  10%  { opacity: 0.85; }
  88%  { opacity: 0.85; }
  100% { left: 104%; opacity: 0; }
}

/* ----------------------------------------------------------------
   11. SERVICE TEASER (home) + bento
   ---------------------------------------------------------------- */
.teaser-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
  margin-top: 2.625rem;
}
.feature-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  padding: 2.25rem 2rem;
  box-shadow: var(--shadow-xs);
  transition: transform var(--t-med) var(--ease), box-shadow var(--t-med) var(--ease), border-color var(--t-med) var(--ease);
  overflow: hidden;
}
.feature-card::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 3px;
  background: linear-gradient(to right, var(--accent), var(--accent-bright));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-slow) var(--ease);
}
.feature-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: var(--line);
}
.feature-card:hover::before { transform: scaleX(1); }
.feature-card__icon {
  width: 3rem;
  height: 3rem;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  background: var(--accent-wash);
  color: var(--accent-strong);
  margin-bottom: 1.4rem;
}
.feature-card__icon svg { width: 1.4rem; height: 1.4rem; }
.feature-card__title { margin-bottom: 0.65rem; }
.feature-card__text { color: var(--muted); font-size: 0.98rem; }

.teaser-foot { margin-top: 2.75rem; }

/* The 3 cards and the "view all" link no longer carry their own
   [data-reveal] — they reveal in lockstep with the section's headline
   block instead of on their own separate scroll-triggered timing (which
   put them a beat or two behind the heading landing). Driven off the
   heading's own .is-visible via sibling combinator, since it's the one
   thing still individually observed. */
.teaser-grid .feature-card,
.teaser-foot {
  opacity: 0;
  transform: translateY(2rem);
  filter: blur(6px);
  transition: opacity var(--t-slow) var(--ease-out),
              transform var(--t-slow) var(--ease-out),
              filter var(--t-slow) var(--ease-out);
}
.js .section-head.is-visible ~ .teaser-grid .feature-card,
.js .section-head.is-visible ~ .teaser-foot {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}

/* ----------------------------------------------------------------
   12. WHO WE SERVE (split)
   ---------------------------------------------------------------- */
.audience-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
  margin-top: 2.625rem;
}
.audience-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-xl);
  padding: clamp(2rem, 3.5vw, 3rem);
  box-shadow: var(--shadow-sm);
  transition: transform var(--t-med) var(--ease), box-shadow var(--t-med) var(--ease);
}
.audience-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.audience-card__kicker {
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  letter-spacing: 0.1em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--accent-strong);
}
.audience-card__title { margin-top: 0.85rem; }
.audience-card__text { margin-top: 1rem; color: var(--muted); }
.painlist { margin-top: 1.75rem; display: flex; flex-direction: column; gap: 0.7rem; }
.painlist li {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  font-size: 0.96rem;
  color: var(--ink-soft);
}
.painlist svg {
  width: 1.15rem; height: 1.15rem;
  flex: none;
  margin-top: 0.18rem;
  color: var(--accent);
}

/* ----------------------------------------------------------------
   13. WHY A2S (dark feature rows)
   ---------------------------------------------------------------- */
.why-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
  margin-top: 3.5rem;
}
.why-cell {
  padding: clamp(2rem, 3.5vw, 2.5rem) 1.75rem;
  background: var(--dark-2);
  border: 1px solid var(--line-dark);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  transition: transform var(--t-med) var(--ease), box-shadow var(--t-med) var(--ease), background var(--t-med) var(--ease);
}
.why-cell:hover { transform: translateY(-4px); background: var(--dark-3); box-shadow: var(--shadow-lg); }
.why-cell__icon {
  width: 2.85rem;
  height: 2.85rem;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  background: rgba(43, 192, 137, 0.12);
  color: var(--accent-bright);
  margin-bottom: 1.25rem;
  transition: transform var(--t-med) var(--ease), background var(--t-med) var(--ease);
}
.why-cell__icon svg { width: 1.3rem; height: 1.3rem; }
.why-cell:hover .why-cell__icon { transform: translateY(-3px); background: rgba(43, 192, 137, 0.2); }
.why-cell__title { margin-top: 0; color: var(--on-dark); }
.why-cell__text { margin-top: 0.6rem; color: var(--on-dark-soft); font-size: 0.94rem; line-height: 1.6; }

/* ----------------------------------------------------------------
   14. PULL QUOTE — giant editorial type
   ----------------------------------------------------------------
   The site's one deliberate "moment," but built from typographic scale and
   whitespace rather than a color break or scroll-linked effects — stays on
   the same light ground as its neighbors. The passage splits into three
   lines so "It is the backbone." can run dramatically larger than a normal
   pull-quote (up near the hero's own display-heading ceiling) while the
   surrounding lines stay at a calm, readable size — the contrast in scale
   IS the drama. Each line reveals on its own delay off the site's shared
   on-load fade+rise (data-reveal) — lede, then peak, then tail — so the
   passage arrives with a little rhythm instead of landing as one flat
   block. The peak line gets one extra flourish on top of the shared
   fade+rise: it settles in from a slight tilt and undersize rather than a
   plain rise, so the "punch line" arrives with a touch more life than its
   neighbors — still the same confident ease-out curve as everything else
   on the site, no bounce/elastic. No scroll-scrubbed or per-word
   choreography. */
.pullquote {
  text-align: center;
  max-width: 50rem;
  margin-inline: auto;
}
.pullquote__lede,
.pullquote__tail {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 480;
  font-size: clamp(1.3rem, 2.1vw, 1.7rem);
  line-height: 1.44;
  letter-spacing: -0.015em;
  color: var(--ink-soft);
  text-wrap: balance;
}
.pullquote__peak {
  margin-block: clamp(1.25rem, 3vw, 2rem);
  font-family: var(--font-display);
  font-weight: 620;
  font-size: clamp(2.75rem, 7.4vw, 5.5rem);
  line-height: 1.02;
  letter-spacing: -0.03em;
  color: var(--accent-strong);
  text-wrap: balance;
}
/* Overrides just the hidden/visible transform values from the shared
   [data-reveal] rule — same transition, timing and easing as every other
   reveal on the site, just a livelier starting pose for this one line. */
.pullquote__peak[data-reveal] { transform: translateY(1.1rem) scale(0.9) rotate(-1.75deg); }
.pullquote__peak[data-reveal].is-visible { transform: none; }

/* ----------------------------------------------------------------
   15. TESTIMONIALS
   ---------------------------------------------------------------- */
.testi-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem;
  margin-top: 3.5rem;
  max-width: 820px;
  margin-inline: auto;
}
.testi {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  padding: 2rem;
  box-shadow: var(--shadow-xs);
}
.testi__stars { display: flex; gap: 0.15rem; color: var(--accent); margin-bottom: 1.1rem; }
.testi__stars svg { width: 1.05rem; height: 1.05rem; fill: currentColor; }
.testi__quote { font-size: 1.02rem; line-height: 1.6; color: var(--ink-soft); flex: 1; }
.testi__who {
  margin-top: 1.6rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: 0.85rem;
}
.testi__avatar {
  width: 2.6rem; height: 2.6rem;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-strong);
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  flex: none;
}
.testi__name { display: block; font-weight: 600; font-size: 0.95rem; line-height: 1.4; }
.testi__role { display: block; font-size: 0.85rem; color: var(--muted); line-height: 1.4; }
.testi__role--dark { color: var(--ink); }

/* The 3 testimonial cards no longer carry their own [data-reveal] — they
   no longer wait for their own separate scroll-crossing. Instead the whole
   one-after-another cascade is keyed off the section's headline block
   ("Operators who stopped worrying about their books.") via sibling
   combinator: the moment the heading triggers, the cards stagger in right
   behind it (110ms apart, matching the original stagger step) rather than
   each card watching the scrollbar on its own. */
.testi-grid .testi {
  opacity: 0;
  transform: translateY(2rem);
  filter: blur(6px);
  transition: opacity var(--t-slow) var(--ease-out),
              transform var(--t-slow) var(--ease-out),
              filter var(--t-slow) var(--ease-out);
}
.testi-grid .testi:nth-child(1) { transition-delay: 0ms; }
.testi-grid .testi:nth-child(2) { transition-delay: 110ms; }
.testi-grid .testi:nth-child(3) { transition-delay: 220ms; }
.js .section-head.is-visible ~ .testi-grid .testi {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}

/* ----------------------------------------------------------------
   16. CTA BANNER
   ---------------------------------------------------------------- */
.cta-banner { position: relative; }
.cta-banner__inner {
  position: relative;
  background: var(--dark);
  border-radius: var(--r-xl);
  padding: clamp(3rem, 6vw, 5.5rem) clamp(1.75rem, 5vw, 4rem);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
}
.cta-banner__inner::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(50% 80% at 50% 0%, rgba(43, 192, 137, 0.18), transparent 70%),
    repeating-linear-gradient(to bottom, transparent 0 35px, rgba(255,255,255,0.03) 35px 36px);
  -webkit-mask-image: linear-gradient(to bottom, #000, transparent);
  mask-image: linear-gradient(to bottom, #000, transparent);
}
.cta-banner__title { color: var(--on-dark); max-width: 18ch; margin-inline: auto; }
.cta-banner__sub {
  margin-top: 1.25rem;
  color: var(--on-dark-soft);
  font-size: 1.1rem;
  max-width: 48ch;
  margin-inline: auto;
}
.cta-banner__actions { margin-top: 2.25rem; display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; }

/* ----------------------------------------------------------------
   17. SERVICES PAGE — numbered ledger list
   ---------------------------------------------------------------- */
.svc-list { margin-top: 3.5rem; border-top: 1px solid var(--line); }
.svc-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr);
  /* Center (not baseline) so the number + title block sits on the vertical
     midline of the description column, which usually wraps to 2-3 lines and
     would otherwise leave the shorter primary block stranded at the top. */
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3rem);
  padding: clamp(1.75rem, 3.5vw, 2.75rem) 0.5rem;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: background var(--t-med) var(--ease), transform var(--t-med) var(--ease);
}
.svc-item:hover { background: var(--surface); transform: translateX(0.75rem); }
.svc-item__primary {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.svc-item__num {
  font-family: var(--font-figure);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--accent-strong);
  font-variant-numeric: tabular-nums;
  flex: none;
  position: relative;
  top: calc(-0.1em - 1px); /* optical nudge up off dead-center against the title */
}
.svc-item__title { font-size: clamp(1.3rem, 2.4vw, 1.85rem); }
.svc-item__text { color: var(--muted); line-height: 1.6; }

/* ----------------------------------------------------------------
   18. INDUSTRIES PAGE
   ---------------------------------------------------------------- */
.industry {
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: clamp(2rem, 6vw, 5rem);
  align-items: center;
  padding-block: clamp(3rem, 6vw, 5rem);
  border-bottom: 1px solid var(--line);
}
.industry:last-of-type {
  border-bottom: none;
  /* Otherwise this block's own trailing padding stacks on top of the
     section's padding-bottom before the CTA banner, leaving Industries'
     gap to its CTA card nearly double Services' equivalent gap. */
  padding-bottom: 0;
}
.industry:first-of-type {
  /* The section has no separate heading/kicker row on this page — the first
     industry's label doubles as the section intro, so it shouldn't carry its
     own top padding on top of the section's. The 0.6rem residual accounts for
     the inline-flex .ledger-tag's line-height strut on Services/About (a
     block-level <p> here doesn't have that offset) — keeps this label level
     with "The ledger" on Services and "Our story" on About. */
  padding-top: 0.6rem;
}
.industry--flip .industry__visual { order: -1; }
.industry__index {
  font-family: var(--font-sans);
  font-size: 0.97rem;
  letter-spacing: 0.1em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--accent-strong);
}
.industry__title {
  margin-top: 1rem;
  /* Sized down from the base .h-lg scale so the longest of the three
     headlines ("Fitness, Wellness, & Hospitality") still fits on one line;
     all three share this size so they read consistently. The vw-based
     preferred value grows with viewport width, but the column it must fit
     is capped by the container's own max-width (1200px) — past that point
     the column stops growing while a naively-tuned vw value keeps climbing
     toward its ceiling, breaking the one-line fit at wide viewports (e.g.
     1920px). The max is set with real margin below the column's actual
     width so it holds at any viewport, not just the design-reference one.
     Wraps normally at narrow viewports rather than forcing nowrap, which
     would overflow once the column collapses to full width on mobile. */
  font-size: clamp(1.77rem, 2.98vw, 2.5rem);
}
.industry__text { margin-top: 1.25rem; color: var(--ink-soft); font-size: 1.08rem; }
.industry__pains-label {
  margin-top: 2rem;
  font-family: var(--font-sans);
  font-size: 0.91rem;
  letter-spacing: 0.1em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--muted);
}
.chips { margin-top: 1rem; display: flex; flex-wrap: wrap; gap: 0.55rem; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.95rem;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--ink-soft);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.chip svg { width: 0.95rem; height: 0.95rem; color: var(--accent); }
.chip:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-wash); }

/* Industry visual — ledger card stack */
.industry__visual { position: relative; }
.ledger-card {
  position: relative;
  background: var(--dark);
  border-radius: var(--r-xl);
  padding: 2.25rem;
  color: var(--on-dark);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  isolation: isolate;
}
.ledger-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(60% 60% at 80% 0%, rgba(43,192,137,0.06), transparent 70%);
}
.ledger-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 1.25rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--line-dark);
}
.ledger-card__head span { font-family: var(--font-sans); font-size: 0.9375rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--on-dark); }
.ledger-card__dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--accent-bright); opacity: 0.85; }
.ledger-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.7rem 0;
  font-family: var(--font-figure);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
.ledger-row span:first-child { color: var(--on-dark); }
.ledger-row span:last-child { color: var(--on-dark); font-weight: 500; }
.ledger-row--total {
  margin-top: 0.5rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-dark);
}
.ledger-row--total span:last-child { color: var(--accent-bright); }

/* Ledger cards: rows "tally in" sequentially as the card scrolls into view —
   entries posting to the books, the total landing last.
   The from-hidden state lives only inside the keyframe, applied solely while
   the reveal class is present. If the observer never fires (no JS, hidden tab,
   headless renderer) or under reduced-motion, the rows show at their normal
   visible state — the motion only ever enhances an already-visible default. */
@keyframes ledger-tally {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
  .js .is-visible .ledger-row {
    animation: ledger-tally 0.55s var(--ease-out) both;
  }
  .js .is-visible .ledger-row:nth-child(2) { animation-delay: 0.16s; }
  .js .is-visible .ledger-row:nth-child(3) { animation-delay: 0.24s; }
  .js .is-visible .ledger-row:nth-child(4) { animation-delay: 0.32s; }
  .js .is-visible .ledger-row:nth-child(5) { animation-delay: 0.40s; }
  .js .is-visible .ledger-row:nth-child(6) { animation-delay: 0.50s; }
}

/* ----------------------------------------------------------------
   19. ABOUT PAGE
   ---------------------------------------------------------------- */
.about-intro { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 6vw, 5rem); align-items: start; }
.about-intro__body p { color: var(--ink-soft); font-size: 1.1rem; }
.about-intro__body p + p { margin-top: 1.25rem; }

/* Founder */
.founder {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-xl);
  padding: clamp(2rem, 4vw, 3.5rem);
  box-shadow: var(--shadow-sm);
}
/* Loads in immediately on page open (data-reveal-immediate), but slowed
   well past the site's shared --t-slow (0.72s) — an extra 1.5s so the
   card settles in gradually rather than snapping in the instant the page
   is ready. Overrides only the duration; property list, easing, and delay
   var still come from the shared [data-reveal] rule. */
.founder[data-reveal] { transition-duration: 2.22s; }
.founder__body {
  max-width: 44rem;
  margin-inline: auto;
}
.founder__role { font-family: var(--font-sans); font-size: 0.9375rem; letter-spacing: 0.1em; font-weight: 600; text-transform: uppercase; color: var(--accent-strong); }
.founder__name {
  margin-top: 0.6rem;
  /* Scoped, one-off flourish for the founder's name only — not a system
     font. Beau Rivage is a single-weight, fine-stroked script, so it's
     sized up further than a bolder script would need, and de-tracked from
     the base heading rule since connected letterforms break under negative
     letter-spacing. */
  font-family: "Beau Rivage", var(--font-display), cursive;
  font-weight: 400;
  font-size: clamp(1.655rem, 5.1vw, 3.155rem);
  letter-spacing: 0;
  line-height: 1.2;
}
/* Credential callout — three concrete numbers pulled from the bio below,
   framed with hairline rules (matching the hero stats' own top-border
   treatment) rather than nested cards, so the card reads with real
   hierarchy and a credibility hook before the reader gets to the prose. */
.founder__creds {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.75rem, 4vw, 3rem);
  margin-top: 1.25rem;
  padding-block: 1.5rem;
  border-block: 1px solid var(--line);
}
.founder__cred { flex: 1 1 auto; min-width: 8rem; }
.founder__cred-num {
  display: block;
  font-family: var(--font-figure);
  font-weight: 600;
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  letter-spacing: -0.02em;
  color: var(--accent-strong);
  font-variant-numeric: tabular-nums;
}
.founder__cred-label {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.4;
}
/* Matches .founder__creds' own margin-top, so the gap from the bottom
   gray rule down to the prose equals the gap from the name down to the
   top gray rule — the credential row reads as evenly framed, not lopsided. */
.founder__text { margin-top: 1.25rem; color: var(--ink-soft); }
.founder__text p + p { margin-top: 1rem; }
.founder__sig-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: 1.75rem;
}
.founder__sig {
  position: relative;
  display: inline-block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.4rem;
  color: var(--accent-strong);
  /* Safe default: fully visible. The "unsigned" state lives only inside the
     keyframe below, so the line is never hidden if the reveal observer
     doesn't fire (no JS, hidden tab, headless render). */
  clip-path: inset(0 0 0 0);
}
.founder__sig::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.2em;
  height: 1px;
  background: var(--accent);
  transform: scaleX(1);
  transform-origin: left;
}
/* The seal: a static ledger-stamp mark (double ring + check) that "presses"
   in right after the signature finishes writing. Safe default: fully
   visible/settled — the held-up, unstamped pose lives only in the keyframe
   below. */
.founder__seal {
  display: inline-flex;
  flex: none;
  color: var(--accent-strong);
  opacity: 0.85;
}

/* Signature "ink-wipe": the line reveals left-to-right, as if being signed,
   with the underline drawing in step, then the seal presses in beside it —
   one continuous "sign and stamp" beat. The card itself now loads in
   immediately on page open, but this row keeps its own independent
   [data-reveal] (no data-reveal-immediate) so it stays blank and only
   plays when the reader actually scrolls this far down the page —
   otherwise the moment would already be finished off-screen before
   anyone saw it. */
@keyframes sig-ink-wipe {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0% 0 0); }
}
@keyframes sig-underline-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes seal-press {
  0%   { opacity: 0; transform: scale(0.5) rotate(-28deg); }
  62%  { opacity: 0.85; transform: scale(1.06) rotate(-4deg); }
  100% { opacity: 0.85; transform: scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: no-preference) {
  .js .founder__sig-row.is-visible .founder__sig {
    animation: sig-ink-wipe 0.85s var(--ease-out) 0.45s both;
  }
  .js .founder__sig-row.is-visible .founder__sig::after {
    animation: sig-underline-draw 0.85s var(--ease-out) 0.45s both;
  }
  .js .founder__sig-row.is-visible .founder__seal {
    animation: seal-press 0.55s var(--ease-out) 1.15s both;
  }
}

/* ----------------------------------------------------------------
   20. CONTACT PAGE
   ---------------------------------------------------------------- */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: clamp(2.5rem, 6vw, 5rem);
  /* stretch (not start): lets .contact-aside grow to match the form
     column's height, so the map can fill the remaining space and its
     bottom edge lands flush with the form card's bottom. */
  align-items: stretch;
}
.contact-aside { display: flex; flex-direction: column; }
.contact-aside__title { margin-top: 1.1rem; }
.contact-aside__text { margin-top: 1.25rem; color: var(--ink-soft); font-size: 1.1rem; }
.contact-points { margin-top: 2.5rem; display: flex; flex-direction: column; gap: 1.5rem; }
.contact-map {
  /* Same gap as .contact-points above (2.5rem) — matches the space between
     the call-centers paragraph and the email block, per spec. flex:1 fills
     the rest of the stretched column so the bottom lines up with the form.
     max-width is calibrated so the right edge lands on the "d" in "and" —
     the last word of the aside paragraph's second line — at the reference
     desktop viewport; it will drift slightly at other widths since that
     anchor point is inherently tied to this text's specific line wrap. */
  margin-top: 2.5rem;
  flex: 1;
  min-height: 12rem;
  max-width: 29.5rem;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  transition: transform var(--t-med) var(--ease),
              box-shadow var(--t-med) var(--ease),
              border-color var(--t-med) var(--ease);
}
.contact-map:hover {
  transform: translateY(-3px);
  border-color: var(--accent);
  box-shadow: var(--shadow-md);
}
.contact-map iframe { width: 100%; height: 100%; display: block; border: 0; }

/* The map is the last thing to land in the aside's reveal, and it's the one
   piece that isn't text — a scale/blur "focus pull" reads as the location
   snapping into resolution, distinct from (and arriving just after) the
   surrounding fade+rise text so the card gets its own small moment instead
   of appearing as a flat continuation of the paragraph above it. Piggybacks
   on the aside's own [data-reveal] trigger rather than a second observer.
   Safe by default: the pre-focus state lives only inside the keyframe. */
@keyframes contact-map-focus-in {
  from { opacity: 0; transform: scale(0.96); filter: blur(10px); }
  to   { opacity: 1; transform: scale(1); filter: blur(0); }
}
@media (prefers-reduced-motion: no-preference) {
  .js .is-visible .contact-map {
    animation: contact-map-focus-in 0.65s var(--ease-out) both;
    animation-delay: 0.32s;
  }
}
.contact-point { display: flex; gap: 1rem; align-items: flex-start; }
.contact-point__icon {
  width: 2.75rem; height: 2.75rem; flex: none;
  border-radius: var(--r-md);
  display: grid; place-items: center;
  background: var(--accent-wash);
  color: var(--accent-strong);
}
.contact-point__icon svg { width: 1.25rem; height: 1.25rem; }
.contact-point__label { font-family: var(--font-sans); font-size: 0.9375rem; letter-spacing: 0.1em; font-weight: 600; text-transform: uppercase; color: var(--muted); }
.contact-point__value { margin-top: 0.2rem; font-weight: 600; font-size: 1.05rem; }
.contact-point__value a:hover { color: var(--accent-strong); }

/* Form */
.form-card {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-xl);
  padding: clamp(1.75rem, 4vw, 3rem);
  box-shadow: var(--shadow-md);
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
.field { display: flex; flex-direction: column; margin-bottom: 1.25rem; }
.field--full { grid-column: 1 / -1; }
.field__label {
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: var(--ink);
}
.field__label .req { color: var(--accent-strong); }
.field__opt { font-weight: 400; color: var(--muted); }
.input,
.select,
.textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: 0.98rem;
  color: var(--ink);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.input::placeholder,
.textarea::placeholder { color: #626c66; } /* ≥4.5:1 on the input bg (WCAG AA) */
.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 4px rgba(14, 110, 76, 0.12);
}
.textarea { resize: vertical; min-height: 7.5rem; }
.select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23515C56' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  padding-right: 2.75rem;
  cursor: pointer;
}
.field.has-error .input,
.field.has-error .select,
.field.has-error .textarea {
  border-color: #c0392b;
  box-shadow: 0 0 0 4px rgba(192, 57, 43, 0.10);
}
.field__error {
  margin-top: 0.45rem;
  font-size: 0.82rem;
  color: #c0392b;
  display: none;
  align-items: center;
  gap: 0.35rem;
}
.field.has-error .field__error { display: flex; }
.field__error svg { width: 0.95rem; height: 0.95rem; }

.form-foot { margin-top: 0.5rem; }
/* Turnstile widget — centered above the submit button, with reserved height
   so the form doesn't jump when the challenge iframe loads in. */
.form-captcha {
  display: flex;
  justify-content: center;
  min-height: 65px;
  margin-bottom: 1.1rem;
}
.form-error {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 1rem;
  font-size: 0.85rem;
  color: #c0392b;
  text-align: center;
}
.form-error.is-visible { display: flex; }
.form-error svg { width: 0.95rem; height: 0.95rem; flex: none; }
.form-note { margin-top: 1rem; font-size: 0.85rem; color: var(--muted); text-align: center; }

.form-success {
  display: none;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2rem 1rem;
}
.form-success.is-active { display: flex; }
.form-success__badge {
  width: 4rem; height: 4rem;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-strong);
  display: grid; place-items: center;
  margin-bottom: 1.5rem;
  animation: pop var(--t-slow) var(--ease) both;
}
.form-success__badge svg { width: 1.85rem; height: 1.85rem; }
@keyframes pop {
  from { transform: scale(0.6); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
.form.is-hidden { display: none; }

/* ----------------------------------------------------------------
   21. FOOTER
   ---------------------------------------------------------------- */
.footer {
  background: var(--dark);
  color: var(--on-dark-soft);
  padding-block: clamp(2.25rem, 4vw, 3rem) 1.75rem;
}
.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  padding-bottom: 1.75rem;
  border-bottom: 1px solid var(--line-dark);
}
/* The "Get in touch" block is narrower than its 1fr track, so left-aligned
   text inside it was hugging the track's near edge and reading as stranded
   in the middle of the footer. justify-self anchors the block itself to the
   track's outer (right) edge; the text inside stays left-aligned relative
   to that block, it's just the block's position that moves. */
.footer__grid > div:last-child { justify-self: end; }
.footer .brand { color: #fff; }
.footer__tagline {
  margin-top: 1.25rem;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.3rem;
  color: var(--on-dark);
  max-width: 20ch;
}
.footer__meta { margin-top: 0.85rem; font-size: 0.9rem; color: var(--on-dark-muted); }
.footer__col-title {
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  letter-spacing: 0.1em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--on-dark-soft);
  margin-bottom: 1.25rem;
}
.footer__email { color: var(--on-dark); font-weight: 600; font-size: 1.05rem; }
.footer__email:hover { color: var(--accent-bright); }
.footer__copy { margin-top: 1.25rem; font-size: 0.85rem; color: var(--on-dark-muted); }
.footer__bottom {
  margin-top: 1.5rem;
  display: grid;
  /* Mirrors .footer__grid's column template so this row's second column
     (the right-hand text) lines up with the "Get in touch" column above it.
     minmax(0, Nfr) forces strict proportional widths on both grids — a bare
     fr still honors each track's own content min-width, which let the two
     grids' columns drift out of alignment when their content differed. */
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  font-size: 0.82rem;
  color: var(--on-dark-muted);
}
.footer__bottom .footer__tag-mono { grid-column: 1; font-family: var(--font-sans); font-weight: 600; letter-spacing: 0.1em; }
.footer__bottom span:last-child { grid-column: 2; justify-self: end; text-align: left; }

/* ----------------------------------------------------------------
   22. RESPONSIVE
   ---------------------------------------------------------------- */
@media (max-width: 980px) {
  .teaser-grid,
  .testi-grid,
  .why-grid { grid-template-columns: repeat(2, 1fr); }
  .contact-grid,
  .about-intro,
  .industry { grid-template-columns: 1fr; }
  .industry--flip .industry__visual { order: 0; }
}

@media (max-width: 760px) {
  .nav__links,
  .nav__cta { display: none; }
  .nav__toggle { display: block; }
  .teaser-grid,
  .audience-grid,
  .testi-grid,
  .why-grid { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; gap: 0; }
  .svc-item { grid-template-columns: 1fr; gap: 0.75rem; }
  .hero__actions .btn,
  .hero__actions .link-arrow { width: 100%; justify-content: center; }
}

@media (max-width: 460px) {
  .hero__stats { flex-direction: column; gap: 1.5rem; }
}

/* ----------------------------------------------------------------
   23. REDUCED MOTION
   ---------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; filter: none; }
}
