/* ==========================================================
   Bulletproof — foundation tokens + landing page styles
   Source of truth: Figma "BP | Web" → 2. Production →
   Landing Page V0.1 (file AlDsIjLpY0E5o94S7zJfgd, node 53:200)
   ========================================================== */

/* ----------------------------------------------------------
   Webfonts.

   OT Neue Montreal and OT Brut are licensed Pangram Pangram
   faces — the .woff2 files are NOT in this repo. Drop them in
   assets/fonts/ with the names below and every rule here picks
   them up. Until then the fallback stacks render the page.
   Pinyon Script (the display "T" in the hero) is SIL OFL and
   IS self-hosted here.
   ---------------------------------------------------------- */
@font-face {
  font-family: 'OT Neue Montreal';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/ot-neue-montreal-medium-semisqueezed.woff2') format('woff2');
}
@font-face {
  font-family: 'OT Neue Montreal';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/ot-neue-montreal-semibold-semisqueezed.woff2') format('woff2');
}
@font-face {
  font-family: 'OT Neue Montreal Display';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/ot-neue-montreal-bold-squeezed.woff2') format('woff2');
}
@font-face {
  font-family: 'OT Brut';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/ot-brut-regular-mono.woff2') format('woff2');
}
@font-face {
  font-family: 'Pinyon Script';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/pinyon-script-latin.woff2') format('woff2');
  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;
}

:root {
  /* ============================================================
     1. Palette. Every colour in the design is one of these.
     ============================================================ */
  --black-100: #0d0d0d;   /* page ground */
  --black-95:  #191919;   /* blog card fill (hover lifts to black-90) */
  --black-90:  #252525;   /* blog panels, cards, category pills */
  --black-80:  #3d3d3d;   /* About panel, footer legals */
  --black-67:  #545454;   /* blog card meta dot separators */
  --black-50:  #868686;   /* eyebrows, secondary heading half */
  --black-33:  #afafaf;   /* nav links, mono body copy */
  --black-15:  #dbdbdb;   /* stat figures, FAQ questions */
  --black-5:   #f3f3f3;   /* near-white — hero pull-quote */
  --white-100: #fcfcfc;
  --spearmint: #89ffb0;   /* the one accent — every primary CTA */

  /* Translucent surfaces layered over the ground */
  --surface-05: rgba(255, 255, 255, 0.05);
  --surface-10: rgba(255, 255, 255, 0.10);
  --surface-20: rgba(255, 255, 255, 0.20);
  --hairline:   rgba(255, 255, 255, 0.10);
  --dim-50:     rgba(255, 255, 255, 0.50);
  --dim-33:     rgba(255, 255, 255, 0.33);

  /* Cover stand-in — the spearmint-tinted gradient shown when a post ships no
     cover art. Shared by the blog-card thumbnails and the post-detail banner
     so a coverless post reads the same in the grid and on the page. */
  --cover-gradient: linear-gradient(135deg,
    var(--black-80) 0%, var(--black-100) 60%, #12362a 100%);

  /* Diagonal white keyline gradient — the "shimmer" stroke on the fill
     button and cards. Painted as a masked ring (see .shimmer), so it works
     over the translucent card surfaces. Two strengths: the card sits over
     near-black, the fill button over spearmint.
     The card stroke runs corner-to-corner (135°) and is bright only at the
     two ends — the top-left (0%) and bottom-right (100%) corners — fading to
     transparent through the middle, which also drops out the top-right and
     bottom-left corners (they sit at the mid-point of that diagonal axis). */
  --stroke-shimmer-card: linear-gradient(135deg,
    rgba(255, 255, 255, 0.18) 0%,
    rgba(255, 255, 255, 0) 50%,
    rgba(255, 255, 255, 0.18) 100%);
  --stroke-shimmer-fill: linear-gradient(135deg,
    rgba(255, 255, 255, 0.60) 0%,
    rgba(255, 255, 255, 0.12) 45%,
    rgba(255, 255, 255, 0.12) 100%);

  /* ============================================================
     2. Type. Display face is squeezed, UI face semi-squeezed,
        mono is OT Brut.
     ============================================================ */
  --font-display: 'OT Neue Montreal Display', 'OT Neue Montreal',
    'Helvetica Neue Condensed', 'Arial Narrow', 'Helvetica Neue', Arial, sans-serif;
  --font-ui: 'OT Neue Montreal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-mono: 'OT Brut', ui-monospace, 'SF Mono', SFMono-Regular, Menlo,
    Consolas, monospace;
  --font-script: 'Pinyon Script', 'Snell Roundhand', cursive;

  /* ============================================================
     3. Layout. 1512 canvas, 60px gutters, 1392 content width.
     ============================================================ */
  --page-max: 1512px;
  --gutter: 60px;
  --radius-card: 8px;
  --radius-panel: 10px;
  --radius-pill: 100px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* Nav jumps land below the sticky navbar rather than under it. */
[id] { scroll-margin-top: 90px; }

body {
  margin: 0;
  background: var(--black-100);
  color: var(--white-100);
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg { display: block; max-width: 100%; }
p, figure, figcaption, blockquote { margin: 0; }
h1, h2, h3 { margin: 0; font-weight: 700; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link */
.skip-link {
  position: absolute;
  left: 16px; top: -60px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: var(--radius-card);
  background: var(--spearmint);
  color: var(--black-100);
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 16px; }

/* ----------------------------------------------------------
   Shared primitives
   ---------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ==========================================================
   Type scale — the single source of truth for text styles.
   Mirrors the Figma "BP | Web" text styles one-to-one. Colour
   is deliberately excluded (it's a separate token) and case
   (uppercase) is applied at the usage site, as the design does.
   Figma letterSpacing -1.5 = -1.5% = -0.015em. Font properties
   inherit, so applying a class to a container styles its prose.
   ========================================================== */
.t-heading-1    { font-family: var(--font-display); font-size: 72px; font-weight: 700; line-height: 1.05; letter-spacing: 0; }
.t-heading-2    { font-family: var(--font-display); font-size: 56px; font-weight: 700; line-height: 1.1;  letter-spacing: 0; }
.t-subheading-2 { font-family: var(--font-ui); font-size: 36px; font-weight: 600; line-height: 1.2;  letter-spacing: 0; }
.t-subheading-3 { font-family: var(--font-ui); font-size: 32px; font-weight: 600; line-height: 1.25; letter-spacing: 0; }
.t-subheading-4 { font-family: var(--font-ui); font-size: 24px; font-weight: 600; line-height: 1.35; letter-spacing: 0; }
.t-body-1       { font-family: var(--font-ui); font-size: 40px; font-weight: 500; line-height: 1.2;  letter-spacing: 0; }
.t-body-2       { font-family: var(--font-ui); font-size: 24px; font-weight: 500; line-height: 1.35; letter-spacing: 0; }
.t-body-3       { font-family: var(--font-ui); font-size: 20px; font-weight: 500; line-height: 1.35; letter-spacing: 0; }
.t-body-4       { font-family: var(--font-ui); font-size: 18px; font-weight: 500; line-height: 1.35; letter-spacing: 0; }
.t-body-5       { font-family: var(--font-ui); font-size: 16px; font-weight: 500; line-height: 1.4;  letter-spacing: 0; }
.t-body-6       { font-family: var(--font-ui); font-size: 14px; font-weight: 500; line-height: 1.4;  letter-spacing: 0; }
.t-caption-1    { font-family: var(--font-ui); font-size: 13px; font-weight: 500; line-height: 1.4;  letter-spacing: 0; }
.t-body-alt-2   { font-family: var(--font-mono); font-size: 13px; font-weight: 400; line-height: 1.5;  letter-spacing: 0; }
.t-body-alt-3   { font-family: var(--font-mono); font-size: 12px; font-weight: 400; line-height: 1.35; letter-spacing: 0; }
.t-body-alt-4   { font-family: var(--font-mono); font-size: 11px; font-weight: 400; line-height: 1.35; letter-spacing: 0; }
.t-label-1      { font-family: var(--font-ui); font-size: 16px; font-weight: 600; line-height: 1.2;  letter-spacing: -0.015em; }
.t-label-2      { font-family: var(--font-ui); font-size: 14px; font-weight: 600; line-height: 1.25; letter-spacing: -0.015em; }

/* Small uppercase section label — "How we can help", "Team", "FAQs".
   Type from .t-label-1; this keeps only the case + colour. */
.eyebrow {
  text-transform: uppercase;
  color: var(--black-50);
}

/* Two-tone body copy: the second half drops to 50% white. */
.dim { color: var(--dim-50); }

/* Pill badge — "Popular". Type from .t-body-alt-3. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface-20);
  color: var(--black-15);
}

/* Spearmint role badge with a leading dot — "Tech Lead".
   Type from .t-label-2; this keeps the case + chrome. */
.role-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 20px 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--spearmint);
  color: #000;
  text-transform: uppercase;
}
.role-badge img { width: 8px; height: 8px; }

/* Animatable angle for the primary button's circling border light.
   Registering the property lets it tween smoothly; browsers without
   @property fall back to the static keyline (no animation). */
@property --shimmer-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@keyframes shimmer-spin { to { --shimmer-angle: 360deg; } }
/* Sweeps the sheen left→right, dead-centre on the label at 50% of the cycle
   — the moment the border light is at top-centre (angle 180°). The holds at
   each end compress the visible glint into a quick zip rather than a slow
   glide; the first background layer (the static edge envelope) stays put. */
@keyframes text-shimmer {
  0%,  18%  { background-position: 0 0, 100% center; }
  82%, 100% { background-position: 0 0, 0% center; }
}

/* --- Buttons ------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.24px;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform 0.15s ease, background-color 0.15s ease,
    border-color 0.15s ease;
}
.btn img { width: 16px; height: 16px; flex: none; }
.btn:hover { transform: translateY(-1px); }
/* Trailing chevron — the last child, present on the wide CTAs and the capped
   service-card button (never the leading icon, which sits before the label).
   Nudges right on hover to echo the "go" gesture. */
.btn > img:last-child { transition: transform 0.15s ease, filter 0.15s ease; }
.btn:hover > img:last-child,
.btn:focus-visible > img:last-child { transform: translateX(3px); }

/* Fill — spearmint, shimmer keyline. Figma "Button Primary / Fill". */
.btn--primary {
  position: relative;
  padding: 12px 20px;
  border: 2px solid transparent;
  border-radius: var(--radius-panel);
  background: var(--spearmint);
  color: var(--black-100);
}
.btn--primary::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  padding: 2px;
  background: var(--stroke-shimmer-fill);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
/* No fill swap on hover — the button keeps the global lift and the trailing
   chevron slides right (see .btn > img:last-child). */
/* Hover/focus settles the button: the shimmer stops and the border ring drops
   away entirely. Higher specificity than the animated ::after below, so it wins
   for both motion and reduced-motion users. */
.btn--primary:hover::after,
.btn--primary:focus-visible::after {
  background: none;
  animation: none;
}

/* Light circles the border continuously, and speeds up on hover/focus.
   Over the bright spearmint fill a plain white arc barely registers, so
   while animating the ring gets a dark trough — the bright-white peak then
   reads clearly as a light travelling the edge. Reduced-motion users keep
   the static keyline above. */
@media (prefers-reduced-motion: no-preference) {
  .btn--primary::after {
    /* Single smooth peak: dark green → white → dark green over the full
       360°. Start/end colours match, so the sweep has no seam and the
       light glides round the edge instead of stepping through stops. */
    background:
      conic-gradient(from var(--shimmer-angle),
        rgba(8, 38, 22, 0.55) 0deg,
        #ffffff 150deg,
        #ffffff 210deg,
        rgba(8, 38, 22, 0.55) 360deg),
      var(--stroke-shimmer-fill);
    animation: shimmer-spin 3s linear infinite;
  }

  /* A faint light sheen sweeps across the label on the same 3s cadence as
     the border, so text and edge shimmer in step. Guarded by
     background-clip:text support so the label never turns invisible. */
  @supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .btn--primary .btn-label {
      background:
        /* Edge envelope (static): text-colour at the two ends fading to
           transparent in the middle, layered over the sheen so the glint
           fades out as it reaches the start/end of the string. */
        linear-gradient(90deg,
          var(--black-100) 0%,
          transparent 7%,
          transparent 93%,
          var(--black-100) 100%),
        /* Travelling sheen. */
        linear-gradient(100deg,
          var(--black-100) 0%,
          var(--black-100) 46%,
          rgba(255, 255, 255, 0.55) 50%,
          var(--black-100) 54%,
          var(--black-100) 100%);
      /* Envelope spans the label (100%); the sheen is 300% and, with
         no-repeat, sits off-screen at the cycle ends and centred at 50%, so
         it phase-locks to the border light (both share the 3s / 1.3s beat). */
      background-size: 100% 100%, 300% auto;
      background-position: 0 0, 100% center;
      background-repeat: no-repeat;
      -webkit-background-clip: text;
              background-clip: text;
      -webkit-text-fill-color: transparent;
      animation: text-shimmer 3s linear infinite;
    }
    /* Hover/focus settles the label too: the sheen stops and the text
       returns to a solid fill, matching the solid white border. */
    .btn--primary:hover .btn-label,
    .btn--primary:focus-visible .btn-label {
      animation: none;
      background: none;
      -webkit-text-fill-color: currentColor;
    }
  }
}

/* Outline — grey keyline on transparent. Figma "Button Primary / Outline". */
.btn--outline {
  padding: 12px 20px;
  border: 1.5px solid var(--black-50);
  border-radius: var(--radius-panel);
  background: rgba(252, 252, 252, 0);
  color: var(--black-15);
}
.btn--outline img { transition: filter 0.15s ease; }
/* Hover brightens the keyline, text, and icons one step up the grey scale
   (text black-15 → white; mail #AFAFAF → #DBDBDB, chevron #DBDBDB → white via
   brightness). On the wide button the trailing chevron also slides right. */
.btn--outline:hover,
.btn--outline:focus-visible {
  border-color: var(--black-15);
  color: var(--white-100);
}
.btn--outline:hover img,
.btn--outline:focus-visible img { filter: brightness(1.25); }

/* Pill — small navbar badge. Figma "Button Secondary". */
.btn--pill {
  position: relative;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  color: var(--black-33);
  font-size: 14px;
  letter-spacing: -0.21px;
  transition: transform 0.15s ease, background-color 0.15s ease,
    color 0.15s ease;
}
/* Masked ring keyline — same technique as the primary button so the border
   survives the translucent fill. Solid white at rest (reduced-motion). */
.btn--pill::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: #fff;
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.btn--pill img { width: 9px; height: 9px; }
/* Pill stays put on hover — no lift. The shimmer keeps circling (border
   unchanged); instead the fill bumps up ~0.05 and the text brightens a notch
   back to black-15. */
.btn--pill:hover,
.btn--pill:focus-visible {
  transform: none;
  background: rgba(255, 255, 255, 0.09);
  color: var(--black-15);
}

/* Light circles the pill keyline on the same 3s cadence as the primary
   button. The trough dims to translucent white so the dark navbar shows
   through and the bright peak reads as a light travelling the edge.
   Reduced-motion users keep the solid white keyline above. */
@media (prefers-reduced-motion: no-preference) {
  .btn--pill::after {
    background:
      conic-gradient(from var(--shimmer-angle),
        rgba(255, 255, 255, 0.05) 0deg,
        rgba(255, 255, 255, 0.05) 90deg,
        rgba(255, 255, 255, 0.67) 170deg,
        rgba(255, 255, 255, 0.67) 190deg,
        rgba(255, 255, 255, 0.05) 270deg,
        rgba(255, 255, 255, 0.05) 360deg);
    animation: shimmer-spin 3s linear infinite;
  }
}

/* Wide — full-width CTA (booking card). Content is centred as one group
   ([icon] label › ) per Figma, not split to the edges. */
.btn--wide {
  width: 100%;
  justify-content: center;
  gap: 16px;
}
.btn--wide .btn-lead { display: inline-flex; align-items: center; gap: 8px; }

/* Capped — 200px max, e.g. the service card CTA. Inherits .btn--primary. */
.btn--capped { max-width: 200px; width: 100%; }

/* --- Person row: 48px avatar + name/detail ------------------ */
.person {
  display: flex;
  align-items: center;
  gap: 16px;
}
.person img {
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
}
/* .person-name → .t-subheading-4, .person-detail → .t-body-5 */
.person-name { color: var(--white-100); }
.person-detail { color: var(--dim-50); }

/* Hairline divider */
.rule {
  height: 1px;
  width: 100%;
  border: 0;
  margin: 0;
  background: var(--hairline);
}

/* ==========================================================
   Navbar
   ========================================================== */
.navbar {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-block: 20px 20px;
  isolation: isolate;
}
/* Tint gradient sits on the bar itself and fades to nothing below it. */
.navbar::before {
  content: "";
  position: absolute;
  inset: 0 0 -28px 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(13, 13, 13, 1) 0%,
    rgba(13, 13, 13, 0.6) 55%,
    rgba(13, 13, 13, 0) 100%
  );
}
/* Progressive blur: five stacked layers with growing blur radii, each
   masked to its own band so they overlap and ramp from heavy blur at the
   top to sharp at the bottom — no single hard cut-off line. */
.navbar-blur {
  position: absolute;
  inset: 0 0 -28px 0;
  z-index: -1;
  pointer-events: none;
}
.navbar-blur span {
  position: absolute;
  inset: 0;
}
.navbar-blur span:nth-child(1) {
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent 30%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent 30%);
}
.navbar-blur span:nth-child(2) {
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 8%, #000 22%, #000 34%, transparent 48%);
          mask-image: linear-gradient(to bottom, transparent 8%, #000 22%, #000 34%, transparent 48%);
}
.navbar-blur span:nth-child(3) {
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 26%, #000 40%, #000 52%, transparent 66%);
          mask-image: linear-gradient(to bottom, transparent 26%, #000 40%, #000 52%, transparent 66%);
}
.navbar-blur span:nth-child(4) {
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 44%, #000 58%, #000 70%, transparent 84%);
          mask-image: linear-gradient(to bottom, transparent 44%, #000 58%, #000 70%, transparent 84%);
}
.navbar-blur span:nth-child(5) {
  backdrop-filter: blur(1.5px);
  -webkit-backdrop-filter: blur(1.5px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 62%, #000 76%, #000 90%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 62%, #000 76%, #000 90%, transparent 100%);
}
.navbar > .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
/* Full chrome wordmark (raster). The 760:254 lockup is taller than the old
   flat SVG, so it's sized by height to sit cleanly in the bar; width follows. */
.navbar-logo { height: 44px; width: auto; }
.navbar-trailing { display: flex; align-items: center; gap: 8px; }
/* Type from .t-label-2; this keeps the layout + case + colour. */
.navbar-links {
  display: flex;
  align-items: center;
  gap: 48px;
  padding: 10px 30px;
  text-transform: uppercase;
  color: var(--black-33);
}
.navbar-links a { transition: color 0.15s ease; }
.navbar-links a:hover { color: var(--white-100); }
/* Hamburger — hidden on desktop, shown ≤860 where it toggles .navbar-links. */
.navbar-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.navbar-toggle-bar {
  display: block;
  width: 20px;
  height: 2px;
  margin: 0 auto;
  background: var(--black-33);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
/* Open: the two bars cross into an X (gap 5px + 2px bar → 3.5px to meet). */
.navbar-toggle[aria-expanded="true"] .navbar-toggle-bar:nth-child(1) {
  transform: translateY(3.5px) rotate(45deg);
}
.navbar-toggle[aria-expanded="true"] .navbar-toggle-bar:nth-child(2) {
  transform: translateY(-3.5px) rotate(-45deg);
}

/* ==========================================================
   Hero
   ========================================================== */
/* Page-load reveal: the above-the-fold hero fades in with the same
   rise + soft-blur vocabulary as the intro scroll reveal, cascading in
   reading order (nav → heading → sub → booking card) so first paint
   settles rather than snaps. Time-based (no scroll timeline). Guarded by
   prefers-reduced-motion so reduced-motion users get the static layout,
   and the stylesheet is render-blocking so elements start hidden — no
   flash of the finished state before the animation begins.
   NB: we animate the navbar's inner .shell, not .navbar itself — a filter
   on the sticky bar would leave a containing block that breaks the
   progressive backdrop-blur layers. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes hero-rise {
    from { opacity: 0; filter: blur(4px); transform: translateY(16px); }
    to   { opacity: 1; filter: blur(0);   transform: translateY(0); }
  }
  .navbar > .shell,
  .hero-heading,
  .hero-callout,
  .hero-right {
    animation: hero-rise 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  }
  .navbar > .shell { animation-delay: 0.05s; }
  .hero-heading,
  .hero-callout    { animation-delay: 0.15s; }
  .hero-right      { animation-delay: 0.40s; }
}
.hero > .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 60px;
  padding-block: 160px 90px;
}
.hero-left {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 32px;
  flex: 1 1 0;
  min-width: 0;
  max-width: 900px;
}

/* Type from .t-heading-1; this keeps the layout + case + colour. */
.hero-heading {
  position: relative;
  width: 100%;
  text-transform: uppercase;
  color: var(--white-100);
}
/* Line 1 carries the weight in full white; line 2 drops back to grey. */
.hero-heading .line-1 { display: block; }
.hero-heading .line-2 { display: block; position: relative; color: var(--black-33); }
/* The script "T" that completes "ransform" — real text, not decoration,
   so the heading still reads "…behind. Transform your business with AI." */
.hero-heading .script-t {
  position: absolute;
  left: -80px;
  top: -13px;
  font-family: var(--font-script);
  font-size: 120px;
  font-weight: 400;
  line-height: 1;
  text-transform: none;
}

/* Hero pull-quote callout (Figma node 150:1665). A translucent panel lit
   from the top by a 1.5px white keyline. Quote type from .t-body-2, the
   attribution from .t-label-1. */
.hero-callout {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: 520px;
  margin: 0;
  padding: 20px 24px;
  overflow: hidden;
  border-radius: var(--radius-panel);
  background: var(--surface-05);
}
/* Shimmer keyline — the same masked gradient ring as the booking card, drawn
   inside the clipped corners so it survives overflow: hidden. The callout uses
   a softer variant: dimmer bright corners and a wider transparent fade band. */
.hero-callout::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 0.18) 0%,
    rgba(255, 255, 255, 0) 30%,
    rgba(255, 255, 255, 0) 70%,
    rgba(255, 255, 255, 0.18) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.hero-callout-quote {
  margin: 0;
  color: var(--black-5);
}
.hero-callout-cite {
  text-transform: uppercase;
  color: var(--black-50);
}

/* ==========================================================
   Booking card — hero (full) and closing CTA (mini)
   ========================================================== */
.booking-card {
  position: relative;
  width: 402px;
  max-width: 100%;
  flex: none;
  overflow: hidden;
  border-radius: var(--radius-card);
  /* Top-lit fill: a soft vertical gradient from surface-10 at the top to
     surface-05 at the bottom (Figma node 46:873). */
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0.10) 0%,
    rgba(255, 255, 255, 0.05) 100%);
  transition: background 0.2s ease, box-shadow 0.2s ease;
}
/* Hover darkens the fill a touch. The gradient can't tween smoothly, so the
   darken rides on a fading inset overlay (box-shadow) instead — it paints over
   the fill but below the content, and transitions cleanly. Both the full and
   mini cards share this effect. */
.booking-card:hover {
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.20);
}
/* Shimmer keyline — a masked gradient ring, drawn inside the clipped
   corners so it survives overflow: hidden. */
.booking-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--stroke-shimmer-card);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.booking-card-top {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 20px;
}
/* .booking-card-title → .t-subheading-3, .booking-card-sub → .t-body-5 */
.booking-card-title { color: var(--white-100); }
.booking-card-sub { color: var(--dim-50); }
.booking-card-bottom {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 20px;
}
.booking-card-actions {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Mini variant: no header, single action. Shares the full card's corner
   shimmer ring (via .booking-card::after) instead of its own solid border. */
.booking-card-mini {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding: 30px 24px;
  background: var(--surface-05);
}
.booking-card-mini .person { justify-content: center; }

/* ==========================================================
   Intro
   ========================================================== */
.intro > .shell {
  padding-block: 100px;
  display: flex;
  justify-content: center;
}
/* Type from .t-body-1. */
.intro p {
  max-width: 820px;
  text-align: center;
  color: #fff;
}
/* The intro line's scroll reveal lives in the page-wide Scroll reveal
   system below (it's a text-reveal target). */

/* ==========================================================
   Services
   ========================================================== */
.services > .shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 60px;
  padding-block: 100px;
}
.services-list {
  display: flex;
  flex-direction: column;
  gap: 60px;
  width: 100%;
}

.service-card {
  position: relative;
  display: flex;
  align-items: stretch;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--surface-10);
  box-shadow: 0 8px 15px 0 rgba(255, 255, 255, 0.03);
}
/* Shimmer keyline — the same masked gradient ring as the booking card
   (--stroke-shimmer-card, 1px), drawn inside the clipped corners so it
   survives overflow: hidden. */
.service-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--stroke-shimmer-card);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Left half: photo, bottom-scrim, title, blurb, CTA */
.service-lead {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  flex: 1 1 0;
  min-width: 0;
  max-width: 600px;
  min-height: 520px;
  padding: 20px 20px 30px;
  isolation: isolate;
}
.service-lead-media {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}
.service-lead-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Scrim: the gradient fill's own stops are 0%/100%, but its box is offset
   above the image, so the transparent stop lands at -36.58% (image already
   partly darkened at the top) and reaches near-black at 76.13%. */
.service-lead-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) -36.58%,
    rgba(0, 0, 0, 0.99) 76.13%
  );
}
.service-lead-body {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  flex: 1;
  gap: 24px;
}
/* Type from .t-heading-2; this keeps the width + case + colour. */
.service-title {
  max-width: 362px;
  text-transform: uppercase;
  color: var(--white-100);
}
.service-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}
/* Type from .t-body-4. */
.service-text p {
  opacity: 0.67;
}

/* Right half: three labelled rows, hairline separated */
.service-rows {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 20px;
  flex: 1 1 0;
  min-width: 0;
  padding: 30px;
}
.service-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.service-row-heading {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* Row icon. The isometric SVGs (Isocons) have varied, non-square viewBoxes,
   so size by height and let width follow via object-fit: contain. The empty
   <span> keeps the old placeholder-square styling as a fallback. */
.service-row-heading .icon {
  height: 28px;
  width: 28px;
  flex: none;
}
.service-row-heading img.icon {
  object-fit: contain;
  object-position: left center;
}
.service-row-heading span.icon {
  border-radius: 5px;
  background: var(--surface-10);
  height: 20px;
  width: 20px;
}
/* .service-row-heading h3 → .t-subheading-4, .service-row p → .t-body-alt-2 */
.service-row-heading h3 { color: var(--white-100); }
.service-row p { color: var(--black-33); }

/* Schedule-call CTA. It lives after the rows in the DOM, but on the wide
   two-column card it's lifted out of flow and pinned to the card's bottom-left,
   sitting over the image exactly where it used to when it was inside the lead.
   .service-lead-body reserves ~72px (button ≈48px + its old 24px gap) so the
   blurb clears it. When the card stacks (≤1200) the CTA drops back to static
   flow beneath the rows and the reserve is released (see the stacked block). */
.service-cta {
  position: absolute;
  left: 20px;
  bottom: 30px;
  z-index: 1;
}
.service-lead-body { padding-bottom: 72px; }

/* ==========================================================
   About / Team
   ========================================================== */
.about {
  padding-block: 120px;
}
.about-panel {
  display: flex;
  flex-direction: column;
  gap: 60px;
  padding: 40px;
  border-radius: var(--radius-panel);
  background: var(--black-80);
}
.about-header {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 600px;
}
/* Type from .t-subheading-2. */
.about-header h2 { color: #fff; }
.team-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px;
}
.team-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--surface-10);
  box-shadow: 0 8px 15px 0 rgba(255, 255, 255, 0.03);
}
.team-card-photo {
  aspect-ratio: 496.32 / 394.9;
  background: #fff;
}
.team-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-panel);
}
.team-card-body {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 24px;
}
.team-card-title {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
/* Type from .t-subheading-3. */
.team-card-title h3 { color: var(--white-100); }
/* Type from .t-body-alt-2 on the container; the bios inherit it. */
.team-card-bio {
  display: flex;
  flex-direction: column;
  gap: 12px;
  color: var(--white-100);
}

/* ==========================================================
   Closing CTA
   ========================================================== */
.closing > .shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 50px;
  padding-block: 120px 100px;
}
/* Type from .t-heading-2; this keeps the width + alignment + case. */
.closing h2 {
  width: 800px;
  max-width: 100%;
  text-align: center;
  text-transform: uppercase;
  color: var(--white-100);
}
.closing h2 .line-2 { display: block; color: var(--black-50); }
/* Type from .t-subheading-3. */
.closing-email {
  text-align: center;
  color: var(--dim-33);
  transition: color 0.15s ease;
}
.closing-email:hover { color: var(--dim-50); }

/* ==========================================================
   Scroll reveal system
   Each element fades + rises into place the first time it enters the
   viewport; text also un-blurs (the accent carried over from the hero
   load animation), while media cards get fade + rise only so imagery never
   smears. Driven by a small IntersectionObserver (see base.njk): it adds
   .is-revealed once per element then stops watching, so elements animate in
   and STAY — no reverse on scroll-out. The hidden start state is gated on
   .reveal-ready, which the script adds only when motion is allowed and IO
   is supported; reduced-motion, no-IO and no-JS visitors never get it and
   so just see the finished content. The hero (reveals on load) and the intro
   copy (scroll-driven — see the block below) are handled separately. Selectors
   here mirror the script's target list; :where() keeps the hidden state at low
   specificity so .is-revealed cleanly wins. */
@media (prefers-reduced-motion: no-preference) {
  /* Hidden start state — fade + rise (services eyebrow is scoped to the
     section so it doesn't also catch the About eyebrow, which reveals as
     part of the whole .about-header block). */
  .reveal-ready :where(
    .services > .shell > .eyebrow,
    .about-header,
    .closing h2,
    .closing-email,
    .service-card,
    .team-card,
    .booking-card-mini
  ) {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.7s ease, transform 0.7s ease, filter 0.7s ease;
  }
  /* Text targets start as a faint, softly blurred ghost (not fully hidden)
     and sharpen up to full on reveal; media cards stay sharp and fade from
     nothing. This rule follows the base one, so its opacity wins for text. */
  .reveal-ready :where(
    .services > .shell > .eyebrow,
    .about-header,
    .closing h2,
    .closing-email
  ) {
    opacity: 0.5;
    filter: blur(4px);
  }
  /* Observer has fired: settle to the finished state and stay there. */
  .reveal-ready .is-revealed {
    opacity: 1;
    filter: none;
    transform: none;
  }
}

/* Intro copy is the one reveal tied to scroll position rather than a one-shot
   trigger. A scroll-driven (view-timeline) animation scrubs it in — and back
   out — as the paragraph tracks up through the viewport, so its state always
   matches where you've scrolled. Pure CSS, runs off the main thread, and needs
   no JS, so it's independent of the IntersectionObserver system above. @supports
   gates it: browsers without view-timelines just show the finished copy, as do
   reduced-motion visitors. The mask (twice the box tall, transparent→solid)
   melts the paragraph's top edge into the page while faint, then slides away
   (position 0 0 → 0 100%) as the reveal completes. Range runs from early in the
   paragraph's entry to partway through cover, so it's fully in by mid-viewport.
   Desktop only (min-width 861): on mobile the intro reveals once like every
   other section, via the IntersectionObserver system above. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 861px) {
    @keyframes intro-scrub {
      from {
        opacity: 0.5;
        filter: blur(4px);
        -webkit-mask-position: 0 0;
        mask-position: 0 0;
      }
      to {
        opacity: 1;
        filter: blur(0);
        -webkit-mask-position: 0 100%;
        mask-position: 0 100%;
      }
    }
    .intro p {
      -webkit-mask-image: linear-gradient(to bottom, transparent, #000 50%);
      mask-image: linear-gradient(to bottom, transparent, #000 50%);
      -webkit-mask-size: 100% 200%;
      mask-size: 100% 200%;
      animation: intro-scrub linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 45%;
    }
  }
}

/* On mobile the intro copy has no scroll timeline, so it joins the one-shot
   reveal like the other text targets: a faint, blurred, risen ghost that
   settles when the observer adds .is-revealed. :where() keeps it at zero
   specificity so the global .reveal-ready .is-revealed rule still wins. */
@media (max-width: 860px) and (prefers-reduced-motion: no-preference) {
  .reveal-ready :where(.intro p) {
    opacity: 0.5;
    filter: blur(4px);
    transform: translateY(16px);
    transition: opacity 0.7s ease, transform 0.7s ease, filter 0.7s ease;
  }
}

/* ==========================================================
   Footer
   ========================================================== */
.footer {
  padding-block: 10px;
}
.footer > .shell {
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.footer-wordmark {
  width: 100%;
  height: auto;
  opacity: 0.42;
}
/* Type from .t-body-alt-4. */
.footer-legals {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  color: var(--black-80);
}
.footer-legals a { transition: color 0.15s ease; }
.footer-legals a:hover { color: var(--black-50); }

/* Site credits — a text trigger that reveals a CC BY attribution tooltip on
   hover or keyboard focus. CSS-only: the tip is a child of the trigger, so
   moving the pointer onto it (to click the links) keeps :hover alive, and
   :focus-within covers keyboard users. */
.footer-credits {
  position: relative;
  cursor: pointer;
  transition: color 0.15s ease;
}
.footer-credits:hover,
.footer-credits:focus-visible { color: var(--black-50); }
.footer-credits-tip {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  width: 200px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--black-100);
  border: 1px solid var(--hairline);
  color: var(--black-15);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  z-index: 2;
}
.footer-credits-tip a {
  color: var(--white-100);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.footer-credits-tip a:hover { text-decoration-style: solid; }
.footer-credits:hover .footer-credits-tip,
.footer-credits:focus-within .footer-credits-tip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* ==========================================================
   Blog — index (/blog) and posts (/blog/<slug>)
   Deliberately quiet: reachable from the footer, never from
   the primary nav. Reuses the shared navbar + footer; type
   comes from the .t-* scale, colour and layout live here.
   ========================================================== */
/* Page load fade — opacity only, so the fill-mode doesn't leave a lingering
   transform that would break the sticky sidebar's containing block. Guarded
   for reduced-motion. */
@keyframes page-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.blog,
.post {
  animation: page-fade-in 0.5s ease both;
}
@media (prefers-reduced-motion: reduce) {
  .blog,
  .post { animation: none; }
}

/* ---- Index + category archive: two-column shell ----
   Source of truth: Figma node 176:364 (blog index). Sidebar carries the
   title + subtitle; the main column holds a row of category filter pills
   above the post grid. Sidebar sticks while the grid scrolls. */
.blog > .shell.blog-layout {
  display: grid;
  grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
  gap: 60px;
  align-items: start;
  padding-block: 80px 120px;
}
.blog-sidebar {
  position: sticky;
  top: 120px;
  display: flex;
  flex-direction: column;
  gap: 30px;
}
.blog-sidebar-head { display: flex; flex-direction: column; gap: 30px; }
/* Masthead — display "Bulletproof" with the script "Blog" tucked beneath
   (Figma 197:846). The script's tight leading pulls it up under the wordmark. */
.blog-title {
  display: flex;
  flex-direction: column;
  color: var(--white-100);
}
.blog-title-main { text-transform: uppercase; }
.blog-title-script {
  font-family: var(--font-script);
  font-size: 76px;
  line-height: 0.6;
}
/* Two-tone lede — lead sentence bright, the rest drops to grey (Figma 172:2526). */
.blog-subtitle { color: var(--black-50); }
.blog-subtitle-lead { color: #f3f3f3; }
/* Category filter — a single wrapping row above the grid. */
.blog-cats { margin-bottom: 24px; }
.blog-cat-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.blog-cat-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--black-90);
  color: var(--black-15);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.blog-cat-pill:hover { background: var(--surface-20); }
.blog-cat-pill.is-active {
  background: var(--spearmint);
  color: var(--black-100);
}
.blog-cat-count { color: var(--black-50); }
.blog-cat-pill.is-active .blog-cat-count { color: var(--black-80); }

.blog-main { min-width: 0; }
.blog-empty { color: var(--black-33); }

/* Grid — two up, with the featured Wide card spanning the full width. */
.post-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px;
  width: 100%;
}
.post-grid-featured { grid-column: 1 / -1; }

/* ---- Post card (Figma node 172:2306): regular / compact / wide ---- */
.post-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--black-95);
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.post-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  background: var(--black-90);
}
/* Shimmer keyline — the same masked gradient ring as the home-page booking
   card (--stroke-shimmer-card, 1px), drawn inside the clipped corners so it
   survives overflow: hidden. */
.post-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  padding: 1px;
  /* Same 135° corner sheen as the shared token, but with the bright ends
     pulled in (fading out by 25% / back in at 75%) so each corner reads
     shorter. */
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 0.18) 0%,
    rgba(255, 255, 255, 0) 12%,
    rgba(255, 255, 255, 0) 88%,
    rgba(255, 255, 255, 0.18) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
/* Hover sheen — a second, brighter corner ring (same 135° axis) layered over
   the base keyline and faded in on hover to intensify the top-left and
   bottom-right corners. */
.post-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 0.55) 0%,
    rgba(255, 255, 255, 0) 30%,
    rgba(255, 255, 255, 0) 70%,
    rgba(255, 255, 255, 0.55) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.post-card:hover::before { opacity: 1; }
.post-card-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: inherit;
}

/* Cover — 16:9 crop with a bottom gradient so the badge reads over art. */
.post-card-cover {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--black-100);
}
.post-card-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.post-card-cover::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(13, 13, 13, 0) 55%, rgba(13, 13, 13, 0.55) 100%);
  pointer-events: none;
}
/* No inline diagram — a quiet spearmint-tinted gradient stands in. */
.post-card-cover--empty {
  background: var(--cover-gradient);
}
.post-card-cover--empty::after { display: none; }

.post-card-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
  flex: 1;
}
/* Badge over the panel drops to the darker chip from the design. */
.post-card .badge {
  align-self: flex-start;
  background: var(--black-80);
  color: var(--black-15);
}
.post-card-title { color: var(--white-100); }
.post-card-excerpt {
  color: var(--black-33);
  line-height: 1.4;
}

/* Byline — avatar · name · date · read time. Caption/1 (UI font, uppercase),
   all one grey; pinned to the bottom so uneven cards still align. */
.post-card-byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 4px;
  text-transform: uppercase;
  color: var(--black-50);
}
.post-card-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  object-fit: cover;
}
.post-card-divider {
  width: 1px;
  height: 12px;
  background: currentColor;
  opacity: 0.5;
}

/* Wide featured — cover left, body right. The row sets the 420px floor
   (Figma node 172:2457); the cover just stretches to fill it. */
.post-card--wide .post-card-link {
  flex-direction: row;
  min-height: 420px;
}
.post-card--wide .post-card-cover {
  flex: 0 0 50%;
  aspect-ratio: auto;
}
.post-card--wide .post-card-body {
  justify-content: center;
  gap: 16px;
  padding: 40px;
}
/* Featured title steps one above the regular card (36 → 40). */
.post-card--wide .post-card-title { font-size: 40px; }

/* Compact — tighter card for the more-posts row, no excerpt. Title size
   comes from the variant's t-subheading-4 class. */
.post-card--compact .post-card-body { padding: 20px 24px; gap: 12px; }
/* Read time sits in the trailing slot of the compact byline (no divider). */
.post-card--compact .post-card-readtime { margin-left: auto; }

/* ---- Post detail (Figma node 180:221) ----
   A single centred 960px column (Figma "Page Container"): back link, the
   article card, then the CTA callout — each 30px apart. The article lives in
   a bordered card (Figma "Post Card" 172:1886). The 960px width is the shell
   content box, so max-width carries the gutter on either side. */
.post > .shell.post-layout {
  max-width: calc(960px + 2 * var(--gutter));
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding-block: 40px 80px;
}
/* Back / All posts — Figma "Button Secondary" (172:1874, 172:2382): a ghost
   pill, chevron-left + label. Hover fills faintly and brightens the label and
   icon. The more-posts "All posts" link reuses the exact same treatment. */
.post-back,
.more-posts-all {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0);
  text-transform: uppercase;
  color: var(--black-50);
  transition: background-color 0.15s ease, color 0.15s ease;
}
.post-back img,
.more-posts-all img { display: block; transition: filter 0.15s ease, transform 0.15s ease; }
.post-back:hover,
.more-posts-all:hover { background: var(--surface-05); color: var(--white-100); }
.post-back:hover img,
.more-posts-all:hover img { filter: brightness(1.7); transform: translateX(-2px); }
/* All-posts chevron sits after the label, so nudge it the other way on hover. */
.more-posts-all:hover img { transform: translateX(2px); }

/* Article card (Figma "Post Card" 172:1886) — white/5 fill, 20px radius.
   Vertical padding on the card; each section owns its own 50px inset so the
   banner runs full-bleed. */
.post-card-detail {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 40px;
  padding-block: 60px;
  background: var(--surface-05);
  border-radius: 20px;
  overflow: hidden;
}
/* Shimmer keyline — the same masked gradient ring as the booking and service
   cards (--stroke-shimmer-card, 1px), drawn inside the clipped corners so it
   survives overflow: hidden. */
.post-card-detail::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--stroke-shimmer-card);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.post-header { display: flex; flex-direction: column; gap: 20px; padding-inline: 50px; }
.post-header .badge { align-self: flex-start; }
.post-title {
  text-transform: uppercase;
  color: var(--white-100);
}
.post-lede { color: var(--dim-50); }

/* Post Bottom Bar (Figma 194:613) — byline on the left, share actions on the
   right, split across the header's full width. */
.post-bottom-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* Byline — Label/2 (UI font, uppercase), one grey. Author | date | read-time,
   the three separated by thin vertical rules (Figma 172:2173), all on one row. */
.post-byline {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  text-transform: uppercase;
  color: var(--black-50);
}
.post-byline-author { display: inline-flex; align-items: center; gap: 8px; }
.post-byline-divider {
  width: 1px;
  height: 14px;
  background: currentColor;
  opacity: 0.5;
}
.post-byline-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
}

/* Post Actions (Figma 194:614) — copy-link + LinkedIn as ghost icon buttons.
   Each button keeps a 24px hit area; the vector inside scales down to 20px.
   Copy-link is a real <button> (JS clipboard); LinkedIn a plain link. Hover
   brightens the icon toward white, matching the back button. */
.post-actions { display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
.post-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--black-50);
  cursor: pointer;
}
.post-action img { display: block; width: 20px; height: 20px; transition: filter 0.15s ease; }
.post-action:hover img { filter: brightness(1.7); }
.post-action.is-copied img { filter: brightness(1.9); }
/* Copy-link confirmation — a small tooltip above the button, shown for ~1.5s
   after a successful copy (the .is-copied class is toggled in base.njk's JS). */
.post-action[data-copy-url] { position: relative; }
.post-action[data-copy-url]::after {
  content: "Link copied";
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  padding: 5px 10px;
  border-radius: 6px;
  background: var(--black-80);
  color: var(--white-100);
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, 4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}
.post-action[data-copy-url].is-copied::after {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Banner (Figma "Banner Image" 172:1887) — a fixed 400px, full-bleed block
   inside the card. Shows the cover when a post ships one; otherwise the shared
   cover gradient, so it matches the coverless thumbnail on the index. */
.post-banner {
  width: 100%;
  height: 400px;
  overflow: hidden;
  background: var(--cover-gradient);
}
.post-banner picture { display: block; width: 100%; height: 100%; }
.post-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Prose body. Type comes from the container (.t-body-3, 20/1.35) plus the
   heading rules below; the 30px top padding lifts the prose off the banner
   (Figma Post Body pt-30). Vertical rhythm is margin-based, not a flat gap,
   so headings can hug their copy and runs of body text can tighten. */
.post-body {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding-block: 30px 0;
  padding-inline: 50px;
}

/* Block rhythm — the margin-top on each block sets the gap above it. Defaults
   to 40px (Figma Post Body gap); runs of body copy tighten to 20px. A heading
   hugs the copy beneath it: 24px under an h2, 16px under a sub-heading. The
   gap ABOVE a heading is fixed on the heading itself so it never depends on
   (or doubles up with) the preceding block: 40px above an h2, 24px above a
   sub-heading. First child clears its margin — the top padding owns the gap
   to the banner. */
.post-body > * + * { margin-top: 40px; }
.post-body > p + p,
.post-body > p + ul,
.post-body > p + ol,
.post-body > ul + p,
.post-body > ol + p { margin-top: 20px; }
.post-body > h2 + * { margin-top: 24px; }
.post-body > h3 + *,
.post-body > h4 + * { margin-top: 16px; }
.post-body > h2 { margin-top: 40px; }
.post-body > h3,
.post-body > h4 { margin-top: 24px; }
.post-body > :first-child { margin-top: 0; }

.post-body p { color: var(--black-33); }
/* Section heading — Subheading/1 (40/600/1.2). */
.post-body h2 {
  font-family: var(--font-ui);
  font-size: 40px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--white-100);
}
/* Subheading — Subheading/4 (24/600/1.35, white). */
.post-body h3 {
  font-family: var(--font-ui);
  font-size: 24px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--white-100);
}
/* Sub-subheading — Label/1 (16px uppercase, -0.015em, white). */
.post-body h4 {
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: -0.015em;
  color: var(--white-100);
}
.post-body a {
  color: var(--white-100);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.post-body a:hover { text-decoration-style: solid; }
.post-body ul, .post-body ol {
  margin: 0;
  padding-left: 20px;
  color: var(--black-33);
}
.post-body ul { list-style: disc; }
.post-body ol { list-style: decimal; }
.post-body li { margin-top: 8px; }
.post-body li:first-child { margin-top: 0; }
/* Inline source references — a small superscript number after the claim; the
   full source name + link lives only in the Sources footer. Kept from bloating
   the line-height. A linked reference jumps to that section. */
.post-body sup {
  font-size: 0.65em;
  line-height: 0;
  vertical-align: super;
  margin-left: 1px;
}
.post-body sup a {
  color: var(--dim-50);
  text-decoration: none;
}
.post-body sup a:hover { color: var(--white-100); }

/* Inline diagrams. Generated art is halftoned onto the page ground by
   tools/inline_image.py in the content engine. Inside the panel the frame
   sits over --black-90; the hairline is the only chrome. */
.post-body figure.post-figure {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}
.post-body figure.post-figure img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  background: var(--black-100);
}
.post-body figure.post-figure figcaption {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  color: var(--black-50);
}

/* Tables inside the prose — the receptionist comparison grid. Wrapped in a
   scroll container (see the postBody filter) so wide grids scroll sideways on
   narrow viewports instead of clipping, with a visible slim scrollbar. */
.post-body .table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--surface-20) transparent;
}
.post-body .table-scroll::-webkit-scrollbar { height: 8px; }
.post-body .table-scroll::-webkit-scrollbar-track {
  background: var(--surface-05);
  border-radius: 4px;
}
.post-body .table-scroll::-webkit-scrollbar-thumb {
  background: var(--surface-20);
  border-radius: 4px;
}
/* Edge fades — a mask softens whichever side still has content to scroll to.
   The can-left / can-right classes are toggled by the table-scroll script in
   base.njk, so a fade only shows when that direction is actually scrollable. */
.post-body .table-scroll.can-right:not(.can-left) {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
}
.post-body .table-scroll.can-left:not(.can-right) {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 48px);
          mask-image: linear-gradient(to right, transparent, #000 48px);
}
.post-body .table-scroll.can-left.can-right {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
          mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
}
.post-body table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-family: var(--font-ui);
  font-size: 16px;
}
.post-body th, .post-body td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid var(--hairline);
  color: var(--black-33);
  vertical-align: top;
}
.post-body th { color: var(--white-100); }

/* Post footer (Figma "Post Footer" 172:1991) — sources + disclaimer, each
   set off by a hairline rule. All copy sits at the dim black/67. */
.post-footer {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding-inline: 50px;
}
/* Dashed hairline — Figma draws these as a 2/2 dotted stroke in --black-80,
   not a solid rule. A repeating gradient matches the dash rhythm exactly. */
.post-footer-rule {
  width: 100%;
  height: 1px;
  margin: 0;
  border: 0;
  background-image: repeating-linear-gradient(
    to right, var(--black-80) 0 2px, transparent 2px 4px);
}
.post-footer-block { display: flex; flex-direction: column; gap: 20px; }
/* Figma "Source Info" (172:1992): heading Label/1 uppercase and body Body/5,
   both in the muted --black-67 (#545454) — a quiet footnote, not the prose. */
.post-footer-heading { color: var(--black-67); text-transform: uppercase; }
.post-footer-text { color: var(--black-67); }
/* Dotted underline — the house style for inline links (.post-body a). Links
   lift one step brighter than the muted body so they read as links. */
.post-footer-text a {
  color: var(--black-33);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.post-footer-text a:hover { color: var(--white-100); text-decoration-style: solid; }
/* Numbered sources — the global ul reset strips markers, so restore decimal
   numbering (matching the inline <sup> references) and space the entries. */
.post-footer-sources ol {
  list-style: decimal;
  padding-left: 1.6em;
}
.post-footer-sources li { margin-top: 6px; }
.post-footer-sources li:first-child { margin-top: 0; }

/* ---- CTA Callout (Figma "CTA Callout" 172:2107) — the post-page CTA, distinct
   from the booking card. A halftone image rail, then heading + body + two
   full-width buttons on a dark, dashed panel. ---- */
.cta-callout {
  display: flex;
  align-items: stretch;
  /* Sits inside the post card between the prose and the footer, so it carries
     the same 50px horizontal inset as the other card sections. */
  /* Extra breathing room above so the callout separates clearly from the prose;
     the 40px card gap alone read too tight against the body copy. */
  margin-top: 24px;
  margin-inline: 50px;
  background: var(--black-90);
  border-radius: 12px;
  overflow: hidden;
}
.cta-callout-lead {
  flex: 1 1 0;
  max-width: 240px;
  align-self: stretch;
  background-color: var(--surface-05);
  background-image:
    linear-gradient(to bottom, rgba(0, 0, 0, 0) 37%, rgba(0, 0, 0, 0.66) 76%),
    url("/assets/img/cta-callout.jpg");
  background-size: cover;
  background-position: center;
}
.cta-callout-body {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 40px;
}
.cta-callout-title { color: var(--white-100); }
.cta-callout-copy { display: flex; flex-direction: column; gap: 18px; color: var(--black-33); }
/* Dotted underline — the house style for inline links (see .post-body a). */
.cta-callout-copy a {
  color: var(--white-100);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.cta-callout-copy a:hover { text-decoration-style: solid; }
/* Two shared buttons sit side by side (Figma "Buttons Wrap"). They're the
   same ui.button primary/outline used by the booking card, so the shimmer
   keyline, text sheen and trailing-chevron slide come for free — each just
   flexes to fill half the row. */
.cta-callout-actions { display: flex; gap: 12px; width: 100%; }
.cta-callout-actions .btn { flex: 1 1 0; min-width: 0; }

/* ---- More posts — full container width, below the centred post column. ---- */
.more-posts {
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: 100%;
  padding-bottom: 120px;
}
.more-posts-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}
.more-posts-title { color: var(--white-100); }
.more-posts-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

/* Reading progress ring — fixed in the bottom-right, the accent stroke fills
   as the article scrolls. Fades in once past the top (via .is-visible, set by
   the scroll-progress script). The SVG is rotated so the fill starts at 12
   o'clock; the bar's dashoffset is driven in JS. */
.scroll-progress {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 20;
  width: 32px;
  height: 32px;
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.scroll-progress.is-visible {
  opacity: 1;
}
.scroll-progress svg {
  display: block;
  transform: rotate(-90deg);
}
.scroll-progress-track { stroke: var(--surface-20); }
.scroll-progress-bar {
  stroke: var(--white-100);
  stroke-linecap: round;
  transition: stroke-dashoffset 0.1s linear;
}

/* ==========================================================
   Legal pages — Terms, Privacy
   Source of truth: Figma "BP | Web" node 104:851 (TERMS / PRIVACY).
   Reuses the shared navbar + footer; only the header and body
   text styles below are page-specific.
   ========================================================== */
/* One centred column: header on top, body beneath. */
.legal > .shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 60px;
  padding-block: 120px;
}
/* Page Header — centred */
.legal-header {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: 720px;
  text-align: center;
}
/* Heading/2 — type from .t-heading-2; this keeps the case + colour. */
.legal-title {
  text-transform: uppercase;
  color: var(--white-100);
}
/* Body Alt/2 — type from .t-body-alt-2. */
.legal-updated { color: var(--black-33); }

/* Page Body — centred 720px column, copy left-aligned within it.
   Type from .t-body-5 on the container; prose (p, li) inherits it. */
.legal-body {
  display: flex;
  flex-direction: column;
  gap: 40px;
  width: 100%;
  max-width: 720px;
}
/* One numbered section: heading, optional subheadings, copy. */
.legal-block {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* Subheading/3 — type from .t-subheading-3. */
.legal-heading { color: var(--white-100); }
/* Label/1 — type from .t-label-1; this keeps the case + colour. */
.legal-subheading {
  text-transform: uppercase;
  color: var(--white-100);
}
/* Body copy — loosened to 1.5 for readable paragraphs. */
.legal-body p {
  line-height: 1.5;
  color: var(--black-33);
}
.legal-body a {
  color: var(--white-100);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.legal-body a:hover { text-decoration-style: solid; }
.legal-body ul, .legal-body ol {
  margin: 0;
  padding-left: 20px;
  line-height: 1.5;
  color: var(--black-33);
}
.legal-body ul { list-style: disc; }
.legal-body ol { list-style: decimal; }
.legal-body li { margin-top: 8px; }
.legal-body li:first-child { margin-top: 0; }

/* ==========================================================
   Responsive. The canvas is a 1512 desktop; these steps keep
   it intact down to a phone without inventing new layout.
   ========================================================== */
@media (max-width: 1200px) {
  :root { --gutter: 40px; }

  .hero > .shell {
    flex-direction: column;
    align-items: flex-start;
    gap: 60px;
    padding-block: 140px 100px;
  }
  .hero-heading { font-size: 60px; }
  .hero-heading .script-t { font-size: 100px; left: -60px; top: -22px; }
  .booking-card { width: 100%; max-width: 402px; }

  .service-card { flex-direction: column; }
  .service-lead { max-width: none; min-height: 420px; }
  .service-rows { padding: 24px; }
  /* Card is stacked now: the CTA rejoins the flow beneath the rows and the
     lead no longer reserves space for an overlaid button. */
  .service-cta { position: static; padding: 0 24px 24px; }
  .service-lead-body { padding-bottom: 0; }

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

  /* Blog: sidebar drops above the grid and stops sticking; grid stays 2-up
     and the wide featured card keeps its horizontal split. */
  .blog > .shell.blog-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }
  .blog-sidebar { position: static; top: auto; }

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

@media (max-width: 860px) {
  :root { --gutter: 24px; }

  .navbar-toggle { display: inline-flex; }
  .navbar > .shell { gap: 12px; }
  /* Links collapse into a glassy dropdown under the bar, toggled by the
     hamburger; the Get Started button stays in the bar. Anchored to .navbar
     (sticky = positioned), so it hangs just below the bar at the gutter. */
  .navbar-links {
    position: absolute;
    top: calc(100% + 8px);
    right: var(--gutter);
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding: 16px 22px;
    border-radius: var(--radius-panel);
    background: rgba(13, 13, 13, 0.85);
    border: 1px solid var(--hairline);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  }
  .navbar-links.is-open { opacity: 1; visibility: visible; transform: none; }

  .hero > .shell { padding-block: 60px 48px; gap: 32px; }
  .hero-left { gap: 16px; }
  .hero-heading { font-size: 44px; }
  /* The script "T" hangs to the LEFT of "ransform" (absolute, offset off the
     line-2 box). A big negative left is what pulls it clear of the word — a
     near-zero value just stacks it on top. The tail bleeds slightly into the
     gutter, same as desktop. */
  .hero-heading .script-t { font-size: 58px; left: -34px; top: -6px; }
  .hero-callout { max-width: none; padding: 16px 20px; }
  .hero-callout-quote { font-size: 20px; }
  /* Booking card goes edge-to-edge; the hero-right column has to stretch first
     (it's a flex item that otherwise shrinks to content). Title + person name
     each drop one step (title t-subheading-3 → -4, name t-subheading-4 → -3). */
  .hero-right { width: 100%; }
  .booking-card { max-width: none; }
  .booking-card-title { font-size: 24px; line-height: 1.35; }
  .person-name { font-size: 20px; }

  .intro > .shell { padding-block: 44px; justify-content: flex-start; }
  .intro p { font-size: 26px; text-align: left; }

  .services > .shell { padding-block: 44px; gap: 40px; }
  .services-list { gap: 24px; }
  /* Text-over-image overlay: shorter 300px floor on phones. Group the title +
     blurb together at the bottom of the lead instead of pushing them apart. */
  .service-lead { min-height: 300px; }
  .service-lead-body { justify-content: flex-end; }
  .service-title { font-size: 40px; }
  .service-text p { font-size: 16px; }
  .service-row-heading h3 { font-size: 20px; }

  .about { padding-block: 48px; }
  .about-panel { gap: 40px; padding: 24px; }
  .about-header h2 { font-size: 26px; }
  .team-list { grid-template-columns: minmax(0, 1fr); }
  .team-card-title h3 { font-size: 26px; }

  .closing > .shell { padding-block: 48px; gap: 36px; }
  .closing h2 { font-size: 36px; }
  .closing-email { font-size: 22px; }

  .footer-legals { flex-direction: column; gap: 8px; text-align: center; }

  .legal > .shell { padding-block: 100px; gap: 40px; }
  .legal-title { font-size: 40px; }
  .legal-heading { font-size: 26px; }
  .blog > .shell.blog-layout { padding-block: 30px 80px; gap: 32px; }
  .post > .shell.post-layout { padding-block: 30px 0; gap: 32px; }
  .blog-title-main { font-size: 48px; }
  .blog-title-script { font-size: 52px; }
  .post-grid { grid-template-columns: minmax(0, 1fr); gap: 20px; }

  /* Wide featured card stacks: cover back on top, body beneath. */
  .post-card--wide .post-card-link { flex-direction: column; }
  .post-card--wide .post-card-cover {
    flex: none;
    aspect-ratio: 16 / 9;
    min-height: 0;
  }
  .post-card--wide .post-card-body { padding: 24px; }
  /* Wide + regular sit side by side on the index, so their titles match at
     one shared size on phones (the compact more-posts card keeps its own). */
  .post-card--wide .post-card-title,
  .post-card--regular .post-card-title { font-size: 24px; }

  /* Detail tightens: card padding shrinks, banner is shorter, sections drop
     to a 24px inset, byline wraps, and the CTA callout stacks image-over-copy. */
  .post-card-detail { padding-block: 40px; gap: 32px; }
  .post-header, .post-body, .post-footer { padding-inline: 24px; }
  .post-banner { height: 220px; }
  /* Stack the header bottom bar: byline over actions, so the share buttons
     drop to their own row instead of crowding the meta. */
  .post-bottom-bar { flex-direction: column; align-items: flex-start; gap: 20px; }
  .post-byline { flex-wrap: wrap; justify-content: flex-start; }
  .post-title { font-size: 34px; }
  .post-body h2 { font-size: 28px; }

  .cta-callout { flex-direction: column; margin-inline: 24px; }
  .cta-callout-lead { max-width: none; width: 100%; min-height: 200px; }
  .cta-callout-body { padding: 24px; }
  .cta-callout-title { font-size: 28px; }
  .cta-callout-actions { flex-direction: column; }

  /* More posts stacks to a single column, and gets its own top spacing since
     the post shell above it has no bottom padding at this width. */
  .more-posts { padding-top: 40px; }
  .more-posts-row { grid-template-columns: minmax(0, 1fr); }
}
