/* ===== Reset ===== */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  overflow-x: clip;
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: var(--black);
  background-color: var(--light-bg);
}

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

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

ul, ol {
  list-style: none;
}

button {
  font: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

/* ===== Custom Properties ===== */
:root {
  --orange: #e8602c;
  /* Orange for TEXT. The brand orange measures 2.8-3.4:1 on the light
     surfaces — under AA for body copy, and under even the 3:1 large-text
     floor on two of them — so type uses this darker mix (4.8:1 on the light
     bg, 5.9 on white, 5.0 on the grey panel). Graphics, borders, buttons and
     the logo keep the brand orange itself. */
  --orange-text: #b04012;
  --black: #1a1a1a;
  --white: #ffffff;
  --light-bg: #ece9e1;
  /* Darkened from #3c85c9 during the accessibility pass: white body copy on
     the old value measured 3.89:1, under AA. This clears it at 4.78:1. */
  --blue: #3576b3;
  --teal: #4db8a4;
  --peach: #e8b87c;
  --gold: #d4a574;
  --yellow: #ecd02c;
  --dark: #0d0d0d;
  --gray-placeholder: #9e9e9e;

  --container: 1640px;
  --section-pad: clamp(60px, 8vw, 120px);
  --header-height: 64px;

  --font-display: 'Syne', system-ui, sans-serif;

  /* Client request (2026-07): one shared corner radius for ALL images
     (homepage, reel, work cards, case studies). XD spec is 20px — tune
     here once if the client decides it reads too large. */
  --radius-img: 20px;
}

/* ===== Typography ===== */
h1, h2, h3, h4, h5, h6 {
  font-family: 'Manrope', 'Inter', system-ui, sans-serif;
  font-weight: 600;
  line-height: 1.1;
}

/* Client request (2026-07): Syne is the display face for every all-caps
   element, CTA, and decorative element (rotating/vertical type, marquees,
   buttons). Add new uppercase elements to this list. */
.btn,
.text-upper,
.side-label__text,
.placeholder,
.marquee__text,
.featured__client-name,
.cta-form__field label,
.contact-form__field label,
.expanded__category-label,
.project-info__meta-label,
.project-info__section-title {
  font-family: var(--font-display);
}

strong, b {
  font-weight: 600;
}

/* Client request (2026-07): all titles share one size, matching the
   homepage hero tagline ("Brands built to perform. Made to last."). */
.h-display {
  font-size: clamp(36px, 5vw, 60px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.h-xl {
  font-size: clamp(36px, 5vw, 60px);
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.h-lg {
  font-size: clamp(36px, 5vw, 60px);
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.h-md {
  font-size: clamp(1.5rem, 3vw, 2.5rem);
  letter-spacing: -0.01em;
}

.h-sm {
  font-size: clamp(1.125rem, 2vw, 1.5rem);
}

/* ===== Layout ===== */
.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 48px);
}

.section {
  padding: var(--section-pad) 0;
}

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 16px 40px;
  border: 1.5px solid var(--black);
  border-radius: 100px;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background 0.3s, color 0.3s, border-color 0.3s;
  white-space: nowrap;
}

.btn:hover {
  background: var(--black);
  color: var(--white);
}

.btn--light {
  border-color: var(--white);
  color: var(--white);
}

.btn--light:hover {
  background: var(--white);
  color: var(--black);
}

.btn--filled {
  background: var(--orange);
  color: var(--white);
  border-color: var(--orange);
}

.btn--filled:hover {
  background: #d4531f;
  border-color: #d4531f;
}

/* ===== Side Label ===== */
.side-label {
  position: fixed;
  right: 10px; /* Right 10 per Gil (client note, Aug 10) */
  top: 50%;
  transform: translateY(-50%);
  /* Back to the brand orange (client note, Aug 31) — the client chose it over
     the AA-passing --orange-text the Aug 21 ADA sweep put here. */
  color: var(--orange);
  padding: 12px 10px;
  z-index: 50;
  pointer-events: none;
  transition: color 0.2s ease, opacity 0.2s ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.side-label__text {
  writing-mode: vertical-rl;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.side-label__arrow {
  display: inline-flex;
  animation: sideLabelBounce 1.6s ease-in-out infinite;
}

@keyframes sideLabelBounce {
  0%, 100% { transform: translateY(0); }
  50%     { transform: translateY(6px); }
}

/* The label is fixed 10px off the right edge (client note, Aug 10), which on a
   phone lands it on top of the body column — it overlapped the About hero
   headline, the Work card titles and the Contact address. There is no gutter
   to move it into at this width, so it steps out below the drawer breakpoint
   and keeps its desktop placement everywhere else. */
@media (max-width: 768px) {
  .side-label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .side-label__arrow { animation: none; }
}

.side-label.is-hidden {
  opacity: 0;
}

/* ===== Placeholder Images ===== */
.placeholder {
  background: var(--gray-placeholder);
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ===== Utility ===== */
.text-center { text-align: center; }
.text-upper { text-transform: uppercase; }
.fw-600 { font-weight: 600; }
.fw-700 { font-weight: 600; }

/* ===== Scroll Reveal ===== */
[data-reveal] {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}

[data-reveal].revealed {
  opacity: 1;
  transform: translateY(0);
}

[data-reveal="delay-1"] { transition-delay: 0.1s; }
[data-reveal="delay-2"] { transition-delay: 0.2s; }
[data-reveal="delay-3"] { transition-delay: 0.3s; }

@media (max-width: 768px) {
  :root {
    --section-pad: 48px;
  }
}

/* ================================================================= *
 * COMPONENTS — folded in from the original Astro component styles
 * (Phase 2 port). Per-page styles (services/about/contact/work) are
 * added as those templates are ported.
 * ================================================================= */

/* ===== Header ===== */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  transition: background 0.3s;
}

.header.scrolled {
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 48px);
  height: var(--header-height);
}

.header__logo {
  display: flex;
  align-items: center;
  color: var(--black);
}

.logo-svg {
  height: 28px;
  width: auto;
}

.header--solid {
  background: var(--light-bg);
}

.header.intro .header__logo {
  position: fixed;
  top: calc((var(--header-height) - 28px) / 2);
  left: clamp(20px, 4vw, 48px);
  z-index: 200;
}

.header.intro .logo-svg {
  height: auto;
  width: calc(100vw - 2 * clamp(20px, 4vw, 48px));
}

.header.intro .header__nav,
.header.intro .header__burger {
  opacity: 0;
  pointer-events: none;
}

/* Scroll-scrubbed shrink state: JS drives translateX/width each frame, so
   no transitions here — the scroll position IS the animation timeline. */
.header.intro-scrub .header__logo {
  position: fixed;
  top: calc((var(--header-height) - 28px) / 2);
  left: 0;
  z-index: 200;
}

.header.intro-scrub .logo-svg {
  height: auto;
}

.header__nav {
  display: flex;
  align-items: center;
  gap: clamp(24px, 3vw, 48px);
}

.header__link {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--black);
  transition: opacity 0.2s;
  position: relative;
}

.header__link:hover { opacity: 0.7; }

.header__link.active {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.header__link--cta { color: var(--orange-text); }

/* Set by the header probe when even the accessible orange can't clear AA
   against the section behind the bar (the services peach, for one). */
.header--cta-plain .header__link--cta { color: var(--black); }

/* Only the mobile drawer has a scrim; see the breakpoint block below. */
.header__scrim { display: none; }

/* Drawer-only: at desktop widths `.header__nav` is the horizontal bar. */
.header__nav-contact { display: none; }

.header__burger {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* 44px box around 28px bars: the control measured 28x18, under the 24px
     minimum target size. The bars are unchanged. */
  width: 44px;
  height: 44px;
  padding: 0;
  margin-right: -8px; /* keep the bars on the same optical line as before */
}

.header__burger span { width: 28px; }

.header__burger span {
  display: block;
  height: 2px;
  background: var(--black);
  border-radius: 2px;
  transition: transform 0.3s, opacity 0.3s;
}

/* Reactive header (client note, Aug 10): white logo + white menu text over
   dark sections. The class is toggled by the background probe in mhd.js;
   the "Let's chat" CTA keeps its orange. */
.header__logo,
.header__link,
.header__burger span {
  transition-property: color, background, opacity, transform;
  transition-duration: 0.3s;
}

.header--dark .header__logo { color: var(--white); }
.header--dark .header__link { color: var(--white); }
.header--dark .header__link--cta { color: var(--orange); }
.header--dark .header__burger span { background: var(--white); }

.header--dark.scrolled {
  background: rgba(26, 26, 26, 0.25);
}

@media (max-width: 768px) {
  /* The burger is the only navigation on a phone, so it has to sit at the
     right edge and stay live even while the homepage logo intro is running
     (the logo and drawer are both out of flow, leaving it the lone flex
     item). */
  .header__burger {
    display: flex;
    margin-left: auto;
    /* Above the open drawer (210) so it stays tappable as the close control. */
    position: relative;
    z-index: 220;
  }

  .header.intro .header__nav,
  .header.intro .header__burger,
  .header.intro-scrub .header__nav,
  .header.intro-scrub .header__burger {
    opacity: 1;
    pointer-events: auto;
  }

  .header__nav {
    position: fixed;
    top: 0;
    right: 0;
    width: 280px;
    height: 100dvh;
    background: var(--white);
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    padding: 100px 32px 32px;
    gap: 24px;
    /* Slide with a transform and hide it outright when closed. Parking the
       panel at `right: -100%` widened the page's scroll region, which let the
       whole site pan sideways; `visibility` also keeps its links out of the
       tab order while the drawer is shut. */
    transform: translateX(100%);
    visibility: hidden;
    transition: transform 0.3s ease, visibility 0s linear 0.3s;
    box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
    /* Above the homepage intro logo (z-index 200), which is fixed and would
       otherwise print itself across the open drawer. */
    z-index: 210;
  }

  .header__nav.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.3s ease, visibility 0s;
  }

  /* Same glass as `.header.scrolled` — the bar's blur, extended over the whole
     page while the drawer is open. Sits under the drawer (210) and the burger
     (220) but over the homepage intro logo (200), so the wordmark blurs with
     everything else. Left click-through on purpose: it is a visual treatment,
     not an overlay, so tapping past it behaves exactly as it did before. */
  .header__scrim {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    /* Sized explicitly rather than `inset: 0`: once the bar is scrolled it has
       its own backdrop-filter, which makes it the containing block for fixed
       children, and `inset: 0` would collapse this to the 64px bar. */
    width: 100vw;
    height: 100dvh;
    /* Under the header (100), which owns the drawer and the burger, and over
       the cookie notice (90) and side label (50) so they blur with the page. */
    z-index: 95;
    background: rgba(255, 255, 255, 0.2);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.3s ease, visibility 0s linear 0.3s;
  }

  .menu-open .header__scrim {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.3s ease, visibility 0s;
  }

  /* Over a dark section the bar tints dark instead (`.header--dark.scrolled`);
     the scrim follows so the glass never fights the section behind it. */
  .menu-open .header--dark + .header__scrim {
    background: rgba(26, 26, 26, 0.25);
  }

  .header__link {
    font-size: 1.125rem;
    display: block;
    padding: 3px 0; /* 22px tall was under the 24px target minimum */
  }

  /* Contact details pinned to the foot of the drawer (client note, Aug 21).
     `margin-top: auto` pushes them down the full-height panel, away from the
     link list. */
  .header__nav-contact {
    display: block;
    margin-top: auto;
    padding-top: 24px;
    border-top: 1px solid rgba(26, 26, 26, 0.15);
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--black);
    opacity: 0.75;
  }

  .header__nav-contact p { margin-bottom: 10px; }

  .header__nav-contact p:last-child { margin-bottom: 0; }

  .header__nav-contact a {
    display: inline-block;
    padding: 4px 0; /* 17px tall was under the 24px target minimum */
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  /* The mobile drawer is always white — dark-mode link colors don't apply
     inside it. */
  .header--dark .header__nav .header__link { color: var(--black); }
  .header--dark .header__nav .header__link--cta { color: var(--orange-text); } /* the drawer panel is white */

  /* ...and neither do the white burger bars, which would vanish against the
     open panel when the section behind the header is dark. */
  .header__nav.open ~ .header__burger span { background: var(--black); }

  /* Cross the bars while the drawer is open so the control reads as "close".
     The two spans sit 6px apart, so each travels half that plus its own 2px. */
  .header__nav.open ~ .header__burger span:first-child {
    transform: translateY(4px) rotate(45deg);
  }

  .header__nav.open ~ .header__burger span:last-child {
    transform: translateY(-4px) rotate(-45deg);
  }
}

/* ===== Footer ===== */
.footer {
  background: #393939;
  color: var(--white);
  overflow: hidden;
}

.footer__top {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1.5fr;
  gap: 32px;
  padding-top: 60px;
  padding-bottom: 40px;
}

.footer__col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 0.85rem;
  opacity: 0.85;
}

.footer__col a {
  display: inline-block;
  padding: 2px 0; /* 22px links were under the 24px target minimum */
}

.footer__col a:hover { opacity: 0.7; }

.footer__col--copy {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.footer__copyright {
  font-size: 0.75rem;
  opacity: 0.6;
}

.footer__col--address p {
  margin-bottom: 4px;
  line-height: 1.5;
}

.footer__col--address a {
  display: inline-block;
  padding: 4px 0; /* target size (WCAG 2.5.8) */
}

.footer__logo-icon svg {
  /* Full lockup (icon + tagline), 384×144 viewBox — size by height. */
  width: auto;
  height: 74px;
}

.footer__logo-text {
  padding: 0 clamp(20px, 4vw, 48px);
  padding-top: clamp(40px, 6vw, 80px);
  padding-bottom: 20px;
  text-align: center;
  overflow: hidden;
}

.footer__logo-svg--large {
  width: 100%;
  height: auto;
}

/* Bottom bar under the wordmark (client note, Aug 21): copyright on one side,
   the cookie control on the other. */
.footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 20px;
  padding-bottom: 28px;
}

/* Privacy / Terms / Cookie settings cluster (client note, Aug 31). */
.footer__bottom-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap; /* three links don't fit one row on a 320px phone */
  gap: 4px 20px;
}

@media (max-width: 768px) {
  .footer__top { grid-template-columns: 1fr 1fr; gap: 32px; }
}

@media (max-width: 480px) {
  .footer__top { grid-template-columns: 1fr; }

  .footer__bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
}

/* ===== Service Marquee ===== */
.marquee {
  background: var(--orange);
  color: var(--black);
  overflow: hidden;
  padding: 18px 0;
  white-space: nowrap;
}

.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 90s linear infinite; /* 90s = same px/s as the old 2-copy 30s loop */
}

.marquee__text {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  flex-shrink: 0;
}

@keyframes marquee-scroll {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* ===== Circular Text ===== */
.circular {
  background: var(--light-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  /* The ring is decorative and it spins, so its box grows toward its own
     diagonal. Left unclipped on a phone (the wrapper's clamp floors at 400px,
     wider than the screen) that widened the layout viewport: the page panned
     sideways, the fixed header stretched past the right edge and took the
     burger with it. `clip` keeps it contained without making a scroll box. */
  overflow: hidden;
  overflow: clip;
}

.circular__wrapper {
  position: relative;
  /* Scaled with the 500→704 viewBox change (×1.4) so the ring text keeps
     its on-screen size now that two copies of the phrase fit the circle. */
  width: clamp(400px, 77vw, 1050px);
  height: clamp(400px, 77vw, 1050px);
  margin: 0 auto;
}

.circular__ring {
  width: 100%;
  height: 100%;
  animation: spin 40s linear infinite reverse;
}

.circular__svg-text {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  fill: rgba(0, 0, 0, 0.12);
}

.circular__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 25%;
  gap: 16px;
}

.circular__center h2 {
  white-space: nowrap;
}

/* On a phone the ring is deliberately bigger than the screen (client note,
   Aug 21): sized to the viewport it scaled the SVG down until its text was
   unreadably small, and the ring is a backdrop, so running off both edges
   costs nothing — `.circular` clips it. The heading drops its nowrap and
   breaks over two lines at the same size as every other title, and the copy
   column is pinned to the viewport rather than the oversized ring. */
@media (max-width: 768px) {
  .circular__wrapper {
    width: min(160vw, 760px);
    height: min(160vw, 760px);
    /* `.circular` is a flex row, so without this the wrapper shrinks back to
       the viewport and the ring text stays small — the whole point here. */
    flex-shrink: 0;
  }

  .circular__center {
    inset: 0 auto;
    left: 50%;
    transform: translateX(-50%);
    width: calc(100vw - 48px);
    padding: 0;
  }

  .circular__center h2 {
    white-space: normal;
    /* Breaks it "Design built / around you." instead of leaving "you." alone
       on the second line. Ignored by older browsers, which just get the
       default break. */
    text-wrap: balance;
  }
}

.circular__sub {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  opacity: 0.7;
  /* ~75% of the headline's rendered width — "Design built around you."
     measures ~660px at 1440 (client note, Aug 31: 70-80%, was 300px). */
  max-width: 500px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ===== Case Study ===== */
.case-study {
  background: var(--blue);
  color: var(--white);
  padding: clamp(60px, 8vw, 100px) 0;
  overflow: hidden;
}

.case-study__inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 48px);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 4vw, 80px);
  align-items: center;
}

.case-study__client {
  font-size: 1rem; /* preheader size (client note, Aug 10) */
  font-weight: 600;
  letter-spacing: 0.05em;
  margin-bottom: 12px;
  opacity: 1; /* was 0.85 — 3.28:1 over the band */
}

.case-study__headline { margin-bottom: 24px; }

/* The client name, headline, tagline and image link to the case study
   alongside the LEARN HOW / See More links (client note, Aug 31) — the text
   keeps its surrounding typography, the pointer is the affordance. */
.case-study__text-link {
  color: inherit;
  text-decoration: none;
}

.case-study__image-link {
  display: block;
}

.case-study__link,
.project-card__link,
.pillar__link {
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  display: inline-flex;
  align-items: center;
  gap: 0;
  text-decoration: none;
  min-height: 24px; /* target size (WCAG 2.5.8) — these measured 20px */
}

.case-study__arrow,
.project-card__arrow,
.pillar__arrow {
  flex-shrink: 0;
  margin-left: 0;
  width: 0;
  height: 14px;
  opacity: 0;
  transform: translateX(-8px);
  overflow: visible;
  transition: width 0.3s ease, opacity 0.3s ease, transform 0.3s ease, margin-left 0.3s ease;
}

.case-study__link:hover .case-study__arrow,
.project-card__link:hover .project-card__arrow,
.pillar__link:hover .pillar__arrow {
  width: 18px;
  margin-left: 10px;
  opacity: 1;
  transform: translateX(0);
}

.case-study__image {
  border-radius: var(--radius-img);
  overflow: hidden;
}

.case-study__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

@media (max-width: 768px) {
  .case-study__inner { grid-template-columns: 1fr; }
  /* Image leads once these stack, whichever side it takes on desktop (client
     note, Aug 21). This used to be scoped to `--img-left`, so the alternating
     blocks put their text first on a phone. */
  .case-study__image { order: -1; }
}

/* ===== Homepage: Hero ===== */
.hero {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--light-bg);
  padding: calc(18px + 14vw + 60px) 20px clamp(120px, 14vw, 200px);
  position: relative;
  overflow: hidden;
}

/* Opening screen on a phone (client note, Aug 21): the title sits high, the
   portfolio video comes next, and the arrow closes the screen underneath it.
   `.hero` drops to display:contents so its tagline and arrow become items of
   .site-main alongside `.featured` — the two are already siblings in the
   markup, so the order changes in the box tree with no duplicated embed and no
   JS reparenting (moving an iframe in the DOM reloads it). Inherited styles
   still reach the tagline: display:contents removes the box, not the element.
   Everything past the opening screen keeps document order at 4. */
@media (max-width: 768px) {
  .site-main {
    display: flex;
    flex-direction: column;
  }

  .site-main > * { order: 4; }

  /* The page h1 is sr-only but should still lead the reading order. */
  .site-main > .sr-only { order: 0; }

  .hero { display: contents; }

  /* Opening screen order per the client's Aug 31 mock: space, video, space,
     headline — no arrow. The gaps are viewport-proportional so the rhythm
     holds on a small phone as on a large one, and the next section peeks as
     the scroll cue. Child selectors for weight throughout: the base
     `.featured { min-height: 100vh }`, `.hero__tagline { animation-delay }`
     and `.hero__arrow { position: absolute }` rules sit further down the
     sheet and would otherwise win. */
  .site-main > .featured {
    order: 1;
    min-height: 0;
    padding: calc(var(--header-height) + 12dvh) 0 0;
    /* Same fade-up as the headline, on the Frame 1 beats (client note,
       Aug 31): video 0:01, headline 0:02. */
    opacity: 0;
    transform: translateY(20px);
    animation: heroContentIn 0.8s ease 1s forwards;
  }

  .hero > .hero__tagline {
    order: 2;
    padding: 10dvh 20px 12dvh;
    animation-delay: 2s;
  }

  /* No arrow on the phone opening screen (client mock, Aug 31). */
  .hero > .hero__arrow { display: none; }
}

/* Short phones (SE-class, ~568px tall): the title wraps to more lines and the
   fixed content is a far bigger share of the screen, so the same gaps are
   tightened rather than letting the headline fall below the fold. */
@media (max-width: 768px) and (max-height: 700px) {
  .site-main > .featured { padding-top: calc(var(--header-height) + 6dvh); }

  .hero > .hero__tagline {
    padding-top: 6dvh;
    padding-bottom: 8dvh;
  }
}

.hero__tagline {
  font-family: 'Manrope', 'Inter', system-ui, sans-serif;
  font-size: clamp(36px, 5vw, 60px);
  font-weight: 600;
  line-height: 1.15;
  color: var(--black);
  opacity: 0;
  transform: translateY(20px);
  animation: heroContentIn 0.8s ease forwards;
  animation-delay: 1s; /* Frame 1: logo 0:00, headline 0:01 (client note, Aug 31) */
}

.hero__arrow {
  position: absolute;
  bottom: clamp(32px, 5vw, 60px);
  color: var(--black);
  opacity: 0;
  transition: opacity 0.3s;
  animation: heroContentIn 0.8s ease forwards;
  animation-delay: 2s; /* Frame 1: scroll button 0:02 (client note, Aug 31) */
}

.hero__arrow:hover { opacity: 0.8; }

/* 64px on the opening screen (client note, Aug 21) — the mark's native size is
   the SVG's own 111.93. display:block drops the inline descender gap so the
   box measures a true 64. The scroll animation is in viewBox units, so it
   scales with the mark. */
.hero__arrow svg {
  display: block;
  width: 64px;
  height: 64px;
}

.hero__arrow-scroll {
  animation: heroArrowScroll 3.2s linear infinite;
  transform-box: view-box;
}

@keyframes heroArrowScroll {
  from { transform: translateY(0); }
  to   { transform: translateY(172px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__arrow-scroll { animation: none; }
}

@keyframes heroContentIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ===== Homepage: Featured Work ===== */
.featured {
  background: var(--light-bg);
  padding: 0;
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.featured__inner {
  width: 100%;
  position: relative;
}

.featured__video {
  margin: 0 auto;
  overflow: hidden;
  max-width: 60%;
  border-radius: var(--radius-img);
  transition: max-width 0.1s linear;
  will-change: max-width;
  /* cursor: none;  — re-enable together with the client overlay below */
}

.featured__client-overlay {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
  z-index: 2;
  white-space: nowrap;
  transform: translate(-50%, -50%);
}

/* Client-name cursor overlay — DISABLED per client request (2026-07).
   Re-enable by restoring opacity: 1 here and cursor: none above. */
.featured__video:hover .featured__client-overlay { opacity: 0; }

/* The Vimeo iframe would otherwise capture all mouse events (it's a separate
   browsing context), so mousemove never reaches the wrapper and the cursor
   overlay can't follow. The reel is decorative autoplay — no interaction needed. */
.featured__video iframe {
  pointer-events: none;
}

.featured__client-icon { flex-shrink: 0; }

.featured__client-name {
  font-size: 15px;
  font-weight: 600;
  color: #f0f0f0;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* ===== Homepage: Value Prop ===== */
.value-prop {
  background: var(--light-bg);
  padding: clamp(80px, 12vw, 200px) 0;
}

.value-prop__pretitle {
  /* Matches .case-study__client typography per client request, but orange */
  font-size: 1rem; /* preheader size (client note, Aug 10) */
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--orange-text);
  margin-bottom: 20px;
}

.value-prop__heading {
  max-width: 1100px;
  margin: 0 auto;
  font-size: clamp(36px, 5vw, 60px);
  line-height: 1.15;
}

/* ===== CTA Banner ===== */
.cta-banner {
  color: var(--white);
  padding: clamp(60px, 8vw, 100px) 0;
  /* The rule at the footer seam. Solid white read as a hard edge against the
     #393939 band, so it's dropped to a quarter-strength white (client note,
     Aug 21). */
  border-bottom: 1px solid rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

.cta-banner__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 32px;
  flex-wrap: wrap;
  padding-bottom: 0;
}

.cta-banner__headline { white-space: nowrap; }

#cta-toggle { cursor: pointer; gap: 10px; }

.cta-toggle-arrow {
  width: 20px;
  height: auto;
  transition: transform 0.3s ease;
  transform: rotate(-90deg);
}

.cta-toggle-btn:hover .cta-toggle-arrow { transform: rotate(0deg); }

.cta-banner__form-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.6s cubic-bezier(0.4, 0, 0.2, 1), padding 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  padding: 0;
}

.cta-banner__form-panel.open {
  max-height: 600px;
  padding: clamp(40px, 6vw, 80px) 0;
}

.cta-banner__form-inner { padding-top: 0; }

.cta-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 83.33%;
  margin: 0 auto;
}

.cta-form__row { display: flex; gap: 24px; }

.cta-form__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}

.cta-form__field label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.7;
}

.cta-form__field input,
.cta-form__field select,
.cta-form__field textarea {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  padding: 12px 14px;
  color: var(--white);
  font-family: inherit;
  font-size: 0.9rem;
  outline: none;
  transition: border-color 0.2s;
}

.cta-form__field input::placeholder,
.cta-form__field textarea::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

.cta-form__field input:focus,
.cta-form__field select:focus,
.cta-form__field textarea:focus {
  border-color: var(--orange);
}

.cta-form__field textarea { resize: vertical; }

.cta-form__submit {
  display: flex;
  justify-content: flex-end;
}

@media (max-width: 768px) {
  .cta-banner__inner { flex-direction: column; align-items: flex-start; }
  .cta-banner__headline { white-space: normal; } /* nowrap clips on small screens */
  .cta-form__row { flex-direction: column; }
}

/* ===== Services: Hero + Pillars (sticky staircase) ===== */
.hero-pillars {
  background: #FAA668;
  height: 360vh;
  position: relative;
}

.hero-pillars__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  grid-template-rows: 70vh 30vh;
  overflow: hidden;
}

.hero-pillars__sticky::before {
  content: '';
  position: absolute;
  inset: 0;
  /* The gradient spans the FULL 360vh section (not just the 100vh sticky
     window): --pillars-progress (set by JS scroll handler) slides the window
     through it, so scrolling descends from lightest into the deep orange.
     Top layer: client-spec depth gradient (#EF632A, 0%→80% opacity) to
     exaggerate scrolling down into the depths; under it, the original
     peach→orange flood the client asked to keep. */
  background:
    linear-gradient(180deg, rgba(239, 99, 42, 0) 0%, rgba(239, 99, 42, 0.8) 100%),
    linear-gradient(180deg, #EBBD87 0%, #FAA668 100%);
  background-size: 100% 360vh;
  background-repeat: no-repeat;
  background-position: 0 calc(var(--pillars-progress, 0) * -260vh);
  clip-path: circle(0% at 50% 50%);
  z-index: 0;
  pointer-events: none;
  animation: orangeReveal 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.8s forwards;
}

@keyframes orangeReveal {
  from { clip-path: circle(0% at 50% 50%); }
  to   { clip-path: circle(120% at 50% 50%); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-pillars__sticky::before {
    animation: none;
    clip-path: circle(120% at 50% 50%);
  }
}

.hero-pillars__title {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: var(--header-height);
  z-index: 2;
  pointer-events: none;
  will-change: opacity, transform;
}

.services-hero__heading {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
  color: var(--black);
  font-weight: 600;
  position: relative;
  z-index: 2;
  /* Offset -150px (client note, Aug 10) — clears the corner circle below. */
  transform: translateY(-150px);
}

/* Underline on the hero's "moment" removed per client (2026-07); the span
   stays in the markup should they want a treatment there again. */
.services-hero__underline {
  text-decoration: none;
}

/* Rotating circular text (same ring as the homepage circular-text
   component), pinned to the panel's bottom-right corner. The corner
   offset lives on the wrapper — the svg's own transform is consumed
   by the spin animation. */
.services-circle-wrap {
  position: absolute;
  right: 0;
  bottom: 0;
  /* At least as big as the homepage circle (client note, Aug 10). */
  width: clamp(400px, 77vw, 1050px);
  aspect-ratio: 1;
  transform: translate(42%, 42%);
  pointer-events: none;
  z-index: 1;
}

.services-circle .circular__svg-text {
  fill: rgba(0, 0, 0, 0.25);
}

/* Circled scroll arrow under the services title (same mark as the
   homepage hero arrow). */
.services-arrow {
  position: absolute;
  bottom: clamp(32px, 5vw, 60px);
  left: 50%;
  transform: translateX(-50%);
  display: block;
  /* Flat 64px to match the hero mark (client note, Aug 21) — was a
     64→96px clamp, so this only changes anything above ~800px wide. */
  width: 64px;
  color: var(--black);
  z-index: 2;
  will-change: opacity;
}

.services-arrow svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Client request (2026-08): statements are stacked down the middle of the
   panel; JS shows one per scroll phase. */
.pillars__inner {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 100%;
  z-index: 2;
}

.pillar {
  grid-area: 1 / 1;
  max-width: 900px;
  padding: 0 24px;
  text-align: left;
  opacity: 0;
  will-change: opacity, transform;
  pointer-events: none;
}

.pillar__title {
  font-weight: 600;
  margin-bottom: 16px;
  font-size: 36px; /* orange-section headlines: 36px (client note, Aug 10) */
}

.pillar__title-link {
  color: inherit;
  text-decoration: none;
  display: inline-block;
  transition: color 0.2s ease;
}

/* No hover reaction on the orange-section headlines (client note, Aug 10);
   keep the keyboard focus cue. */
.pillar__title-link:focus-visible .pillar__title {
  color: var(--orange);
}

.pillar__desc {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  line-height: 1.7;
  opacity: 0.85;
  /* Statement width: 450px (client note, Aug 10; was 400px). */
  max-width: 450px;
  margin-bottom: 24px;
}

/* ===== Services: Proof ===== */
.proof {
  background: var(--blue);
  color: var(--white); /* all copy white in the blue section (client note, Aug 10) */
  padding: clamp(60px, 8vw, 100px) 0;
}

.proof__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: center;
}

.proof__heading {
  margin-bottom: 20px;
  color: var(--white); /* client note, Aug 10 */
}

/* Headline & body text box reduced to 400px (client note, Aug 10). */
.proof__content {
  max-width: 400px;
}

.proof__text {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  line-height: 1.6;
  margin-bottom: 24px;
  /* Was 0.85, which measured 3.28:1 on the blue — the dim was the difference
     between marginal and failing. */
  opacity: 1;
}

.proof__link {
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  display: inline-flex;
  align-items: center;
  gap: 0;
  text-decoration: none;
  min-height: 24px; /* target size (WCAG 2.5.8) */
}

.proof__arrow {
  flex-shrink: 0;
  margin-left: 0;
  width: 0;
  height: 14px;
  opacity: 0;
  transform: translateX(-8px);
  overflow: visible;
  transition: width 0.3s ease, opacity 0.3s ease, transform 0.3s ease, margin-left 0.3s ease;
}

.proof__link:hover .proof__arrow {
  width: 18px;
  margin-left: 10px;
  opacity: 1;
  transform: translateX(0);
}

.proof__image {
  border-radius: var(--radius-img);
  overflow: hidden;
}

.proof__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* ===== Services: Expanded list ===== */
.expanded {
  background: #ededed;
}

.expanded__inner {
  display: flex;
  flex-direction: column;
  gap: clamp(32px, 5vw, 64px);
}

.expanded__left {
  width: 100%;
  min-height: clamp(140px, 18vw, 240px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.expanded__quote {
  font-size: clamp(1.5rem, 3.5vw, 2.75rem);
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-align: center;
  color: var(--black);
  transition: opacity 0.3s;
  max-width: 1000px;
  margin: 0;
}

.expanded__right {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: start;
}

.expanded__category {
  display: flex;
  flex-direction: column;
  transition: opacity 0.3s ease;
  scroll-margin-top: calc(var(--header-height) + clamp(200px, 24vw, 340px));
}

.expanded__category--dimmed {
  opacity: 0.3;
}

.expanded__category-label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--orange-text);
  padding: 0 0 8px;
}

/* Plain text on desktop; mhd.js takes it out of the tab order there. */
.expanded__category-toggle {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: default;
}

.expanded__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 0;
  border-bottom: 1px solid rgba(0,0,0,0.18);
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.2s, font-weight 0.2s;
}

.expanded__item:first-of-type {
  border-top: 1px solid rgba(0,0,0,0.18);
}

.expanded__item:hover,
.expanded__item.active {
  opacity: 1;
  font-weight: 600;
}

/* Resting state was a 0.6 dim, which measured 4.35:1 — just under AA. The
   emphasis still reads, but from a compliant floor. */
.expanded__item:not(.active):not(:hover) {
  opacity: 0.78;
}

.expanded__item-arrow {
  /* Grey section: arrows removed (client note, Aug 10). */
  display: none;
}

@media (max-width: 768px) {
  .pillars__inner { position: static; grid-template-columns: 1fr; gap: 48px; place-items: initial; }
  .hero-pillars__title { position: static; pointer-events: auto; }
  .services-hero__heading { transform: none; } /* -150px offset is desktop-only */
  /* Static here, so it becomes a grid item and would otherwise stretch to the
     row height — a 173px tap target around a 64px mark. */
  .services-arrow { position: static; transform: none; margin: 40px auto; align-self: start; }
  .pillar { grid-area: auto; opacity: 1; pointer-events: auto; max-width: none; }
  /* 24px once the three stack (client note, Aug 21); the 36px above is the
     desktop spec from the Aug 10 round. */
  .pillar__title { font-size: 24px; }
  .hero-pillars { height: auto; }
  /* `grid-template-rows: 70vh 30vh` is the desktop sticky window; with fixed
     rows here the ring overflowed its 30vh row and landed on the arrow. */
  /* `grid-template-rows: 70vh 30vh` is the desktop sticky window; with fixed
     rows here the ring overflowed its 30vh row and landed on the arrow. The
     column is pinned to minmax(0, 1fr) for the same reason in the other axis:
     the oversized ring would otherwise widen the single implicit column and
     drag the title off-centre with it. */
  .hero-pillars__sticky {
    position: relative;
    height: auto;
    gap: 60px;
    grid-template-rows: none;
    grid-template-columns: minmax(0, 1fr);
    /* Bottom breathing room for the stacked pillars (client note, Aug 21) —
       on the STICKY, not the section: the gradient is this element's ::before
       at `inset: 0`, so padding here is covered by it. Padding on the section
       instead left a band of the flat `#FAA668` below where the gradient
       stopped. */
    padding-bottom: var(--section-pad);
  }

  /* The title gets the room; the ring is allowed to run past the fold for it
     (client note, Aug 21). */
  .hero-pillars__title { padding-top: calc(var(--header-height) + 16dvh); }
  /* The corner ring can't stay in the corner once the pillars stack — its text
     ran straight through the body copy. It sits in the flow instead, between
     the title and the arrow (client note, Aug 21), wider than the screen so it
     still bleeds off both edges, but with its own row so nothing crops it
     vertically: scroll and the bottom of the circle comes into view. The
     sticky wrapper's `overflow: hidden` clips the sideways bleed. */
  .services-circle-wrap {
    display: block;
    position: static;
    transform: none;
    width: min(130vw, 640px);
    /* Shares its cell with the arrow (client note, Aug 21): both centred in
       row 2, so the arrow sits on the ring's centre line — top half of the
       circle above it, bottom half below. */
    grid-row: 2;
    grid-column: 1;
    /* Off to the right rather than centred, keeping the corner-motif read it
       has on desktop (client note, Aug 21) — it leaves the right edge and the
       arrow crosses it left of centre. */
    justify-self: center;
    align-self: center;
    transform: translateX(30%);
  }

  .services-arrow {
    grid-row: 2;
    grid-column: 1;
    justify-self: center;
    align-self: center;
    margin: 0;
    z-index: 2; /* grid items take z-index without needing position */
  }

  .pillars__inner { grid-row: 3; }
  /* No sticky window on mobile — the gradient just spans the section. */
  .hero-pillars__sticky::before { background-size: 100% 100%; background-position: 0 0; }
  .proof__inner { grid-template-columns: 1fr; }
  .proof__image { order: -1; } /* image first once stacked (client note, Aug 21) */
  .expanded__right { grid-template-columns: 1fr; gap: 0; }

  /* Accordion at this width (client note, Aug 21): one category open at a
     time, the rest collapsed to their labels. */
  .expanded__category { border-bottom: 1px solid rgba(0, 0, 0, 0.18); }

  .expanded__category-label { padding: 0; }

  .expanded__category-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 0;
    cursor: pointer;
  }

  /* Chevron, drawn rather than shipped as another asset. */
  .expanded__category-toggle::after {
    content: '';
    flex-shrink: 0;
    width: 9px;
    height: 9px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.25s ease;
  }

  .expanded__category.is-open .expanded__category-toggle::after {
    transform: translateY(2px) rotate(-135deg);
  }

  /* Open category's title (client note, Aug 21); the closed ones stay orange. */
  .expanded__category.is-open .expanded__category-label { color: #1e1a17; }

  .expanded__category-panel { display: none; }

  .expanded__category.is-open .expanded__category-panel {
    display: block;
    padding-bottom: 12px;
  }

  /* The category already carries the divider between rows. */
  .expanded__item:first-of-type { border-top: 0; }

  /* The 60% dim is a hover affordance, and nothing hovers here — it just left
     every item permanently muted. */
  .expanded__item:not(.active):not(:hover) { opacity: 1; }
}

/* ===== About ===== */
.about-hero {
  background: #F4E256; /* client-spec warm yellow (was #F4E356) */
  padding-top: calc(var(--header-height) + clamp(60px, 10vw, 140px));
  padding-bottom: clamp(60px, 10vw, 140px);
}

.about-hero__heading { color: var(--black); }

.founder__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: center;
}

.founder__quote { display: flex; align-items: center; }

.founder__blockquote {
  font-family: 'Manrope', 'Inter', system-ui, sans-serif; /* match the title face (was inheriting body Inter) */
  font-size: clamp(1.5rem, 3vw, 2.2rem);
  font-weight: 600;
  line-height: 1.3;
  color: var(--black);
}

.founder__blockquote cite {
  display: block;
  margin-top: 16px;
  font-family: 'Inter', system-ui, -apple-system, sans-serif; /* attribution stays body-text */
  font-size: 0.9rem;
  font-weight: 500;
  font-style: normal;
  opacity: 0.7;
}

.founder__text {
  font-size: clamp(1rem, 1.2vw, 1.1rem); /* universal body copy floor (client note, Aug 10) */
  line-height: 1.7;
  opacity: 0.85;
}

.founder__text p + p { margin-top: 16px; }

.statement {
  background: var(--light-bg);
  padding: clamp(80px, 12vw, 180px) 0;
}

.statement__text { max-width: 700px; margin: 0 auto; }

/* About CTA — the all-caps link style from other pages; the arrow appears
   on hover like the homepage links (client note, Aug 10). */

@media (max-width: 768px) {
  .founder__inner { grid-template-columns: 1fr; }
}

/* ===== Contact ===== */
.contact-page {
  padding-top: calc(var(--header-height) + clamp(60px, 8vw, 120px));
  padding-bottom: clamp(80px, 10vw, 160px);
  background: var(--light-bg);
}

.contact__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(48px, 6vw, 100px);
  align-items: start;
}

.contact__heading { color: var(--black); margin-bottom: 32px; }

.contact__details {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  line-height: 1.7;
  margin-bottom: 24px;
}

.contact__details p { margin-bottom: 8px; }

.contact__company { font-weight: 600; }

.contact__blurb {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  line-height: 1.7;
  opacity: 0.8;
  margin-bottom: 12px;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.contact-form__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.contact-form__field label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* 0.6 landed at 4.26:1 on the light canvas — under AA for a 12px label. */
  opacity: 0.8;
}

.contact-form__field input,
.contact-form__field textarea {
  background: transparent;
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 4px;
  padding: 12px 14px;
  color: var(--black);
  font-family: inherit;
  font-size: 0.9rem;
  outline: none;
  transition: border-color 0.2s;
}

.contact-form__field input:focus,
.contact-form__field textarea:focus {
  border-color: var(--orange);
}

.contact-form__field textarea { resize: vertical; }

.contact-form__submit {
  display: flex;
  justify-content: flex-end;
}

@media (max-width: 768px) {
  .contact__inner { grid-template-columns: 1fr; }
}

/* ===== Work archive: landing + cards ===== */
.work-page {
  padding-top: calc(var(--header-height) + 20px);
  padding-bottom: var(--section-pad);
  background: var(--light-bg);
}

.work-page__grid {
  display: flex;
  flex-direction: column;
  gap: clamp(48px, 7vw, 112px);
}

/* Client note (Aug 10): every row is video/image LEFT, text (headline,
   copy, CTA arrow) RIGHT — no more alternating sides. */
.project-card {
  display: grid;
  grid-template-columns: 3fr 1fr;
  gap: clamp(32px, 4vw, 80px);
  align-items: center;
}

.project-card__media {
  display: block;
  position: relative;
  border-radius: var(--radius-img);
  overflow: hidden;
  aspect-ratio: 17 / 5; /* video frame height (client note, Aug 10; was 24/5) */
  transition: transform 0.3s;
}

.project-card__media:hover { transform: scale(0.99); }

.project-card__bg { position: absolute; inset: 0; }

.project-card__bg--gray { background: #666; }

.project-card__content { max-width: 560px; }

.project-card__title { margin-bottom: 8px; }

.project-card__desc {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  opacity: 0.85;
  max-width: 500px;
  margin-bottom: 20px;
}

/* Unlike the homepage links, the services pillar arrows are always visible.
   (Work-card arrows appear on hover like the homepage blue section —
   client note, Aug 10 — so they keep the shared hidden-until-hover base.) */
.pillar__arrow {
  width: 18px;
  margin-left: 10px;
  opacity: 1;
  transform: none;
}

/* CTA hover: no color change, the arrow turns down like the homepage
   toggle (client note, Aug 10). */
.pillar__link:hover .pillar__arrow {
  transform: rotate(90deg);
}

/* Touch has no hover, so the arrow that slides in on hover is an affordance
   these links never get (client note, Aug 21). They carry an underline
   instead, and the arrows — including the pillars' always-visible ones and
   their rotate-on-hover — step out. */
@media (max-width: 768px) {
  .case-study__link,
  .project-card__link,
  .pillar__link,
  .proof__link {
    text-decoration: underline;
    text-underline-offset: 4px;
    text-decoration-thickness: 1px;
  }

  .case-study__arrow,
  .project-card__arrow,
  .pillar__arrow,
  .proof__arrow {
    display: none;
  }

  .project-card { grid-template-columns: 1fr; gap: 20px; }
  /* Stacked: image above its text, held inside the container gutters on both
     sides (client note, Aug 21 — it used to bleed to the left screen edge per
     the earlier Work — Mobile note, so all four corners are rounded again).
     16:9 matches the mobile cuts the client supplied; at the old 16/10 the
     frame was narrower than the source and cover threw away 10% of every
     video. */
  .project-card__media {
    aspect-ratio: 16 / 9;
  }
}

/* ===== Case study: hero gallery carousel ===== */
.project-hero {
  --pi-left: #7CC7B3;
  --pi-right: #A4E6D5;
  /* The info-band panels extend up behind the bottom ~1/3 of the slides:
     light canvas on top, split color panels rising from the bottom edge
     (continuing seamlessly into .project-info below). */
  background:
    linear-gradient(180deg, var(--light-bg) 0%, var(--light-bg) 60%, rgba(236, 233, 225, 0) 60%),
    linear-gradient(to right, var(--pi-left) 50%, var(--pi-right) 50%);
  padding-top: var(--header-height);
  padding-bottom: clamp(12px, 1.5vw, 20px); /* tight: the info-band text sits close below */
  position: relative;
  overflow: hidden;
}

.project-hero__viewport { position: relative; }

.project-hero__track {
  display: flex;
  gap: clamp(12px, 1.5vw, 24px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: clamp(24px, 4vw, 48px) 12%;
  scrollbar-width: none;
}

.project-hero__track::-webkit-scrollbar { display: none; }

.project-hero__slide {
  flex: 0 0 76%;
  scroll-snap-align: center;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-img);
  background: rgba(0, 0, 0, 0.06); /* light placeholder while media loads */
  transition: transform 0.4s ease;
  transform: scale(0.94);
}

/* 1:1 media keeps its square ratio; width scaled (76% × 9/16) so every
   slide shares the same height in the track. */
.project-hero__slide--square {
  aspect-ratio: 1 / 1;
  flex-basis: 42.75%;
}

.project-hero__slide.is-active { transform: scale(1); }

.project-hero__slide img,
.project-hero__slide video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.project-hero__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--white);
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 2;
  padding: 8px;
}

.project-hero__nav:hover { opacity: 1; transform: translateY(-50%) scale(1.05); }

.project-hero__nav--prev { left: clamp(12px, 2vw, 32px); }
.project-hero__nav--next { right: clamp(12px, 2vw, 32px); }

/* ===== Case study: info band ===== */
.project-info {
  --pi-left: #7CC7B3;
  --pi-right: #A4E6D5;
  background: linear-gradient(to right, var(--pi-left) 50%, var(--pi-right) 50%);
  /* No section padding: the panels already gained height by extending up
     behind the slider, and the columns carry their own padding. */
  padding: 0;
}

.project-info__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}

/* On wide browsers the outer padding grows so the text columns line up
   with the .container margins (matching Let's chat / About), instead of
   running to the viewport edges (client request 2026-08). */
.project-info__left {
  background: var(--pi-left);
  color: var(--white); /* left panel all white text; right stays black (client note, Aug 10) */
  padding: clamp(24px, 3vw, 40px)
    clamp(20px, 4vw, 48px)
    clamp(48px, 6vw, 80px)
    max(clamp(20px, 4vw, 48px), calc((100vw - var(--container)) / 2 + clamp(20px, 4vw, 48px)));
}

.project-info__left .project-info__headline {
  color: var(--white);
}

.project-info__right {
  background: var(--pi-right);
  padding: clamp(24px, 3vw, 40px)
    max(clamp(20px, 4vw, 48px), calc((100vw - var(--container)) / 2 + clamp(20px, 4vw, 48px)))
    clamp(48px, 6vw, 80px)
    clamp(20px, 4vw, 48px);
}

.project-info__name {
  font-size: 0.85rem;
  font-weight: 500;
  margin-bottom: 12px;
}

.project-info__subtitle {
  font-size: 0.95rem;
  font-weight: 500;
  margin-bottom: 16px;
  opacity: 0.8;
}

.project-info__headline {
  margin-bottom: 32px;
  color: var(--black);
}

.project-info__meta {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.project-info__meta-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 4px;
  /* 0.6 measured 2.38:1 against the band — the worst reading on the site. */
  opacity: 1;
}

.project-info__meta-item p:last-child { font-size: 1rem; /* universal body copy (client note, Aug 10) */ }

.project-info__section { margin-bottom: 32px; }

.project-info__section-title {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 12px;
  opacity: 1; /* was 0.6 — 3.85:1 on the light panel */
}

.project-info__section p {
  font-size: 1rem; /* universal body copy (client note, Aug 10) */
  line-height: 1.7;
  opacity: 0.9;
}

.project-info__section p + p { margin-top: 16px; }

/* Case study: optional stat callouts (Upstart) */
.project-info__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: clamp(24px, 4vw, 56px);
  padding-top: clamp(40px, 6vw, 72px);
  /* The band carries no padding of its own, so without this the stat labels
     sat flush on its bottom edge (client note, Aug 21: nothing should touch
     the edge of a section). */
  padding-bottom: clamp(40px, 6vw, 72px);
}

.project-stat__value {
  font-family: 'Manrope', 'Inter', system-ui, sans-serif;
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  font-weight: 600;
  line-height: 1;
  color: var(--black);
}

.project-stat__label {
  font-size: 0.85rem;
  line-height: 1.4;
  margin-top: 8px;
  opacity: 1; /* was 0.75 — 3.22:1 over the band */
}

@media (max-width: 768px) {
  /* Stat callouts tile as a checkerboard on a phone (client note, Aug 21).
     They used to sit on the band's left/right gradient, so both rows read
     dark-left / light-right — vertical stripes. Each cell now carries its own
     panel colour and the second row flips, alternating every four cells so
     the pattern holds for six or eight stats too. Full-bleed (the grid pulls
     back out of `.container`'s padding) and gapless, or the gradient would
     show through as grout. */
  .project-info__stats {
    grid-template-columns: 1fr 1fr;
    /* Equal tiles: without this the rows size to their own content and the
       checkerboard comes out uneven, since the labels differ in length. */
    grid-auto-rows: 1fr;
    gap: 0;
    padding-top: 0;
    padding-bottom: 0; /* tiles are full-bleed here; their text is padded */
    width: auto;
    margin-left: calc(-1 * clamp(20px, 4vw, 48px));
    margin-right: calc(-1 * clamp(20px, 4vw, 48px));
  }

  .project-stat {
    background: var(--pi-right);
    padding: clamp(20px, 5vw, 28px) clamp(16px, 4vw, 24px);
  }

  .project-stat:nth-child(4n + 1),
  .project-stat:nth-child(4n + 4) {
    background: var(--pi-left);
  }

  /* The band's panels stack at this width, so what follows the slider is one
     full-width colour — but the split behind the slider still ran dark-left /
     light-right and read as a mistake (client note, Aug 21). One colour here,
     matching the panel it flows into. */
  .project-hero {
    background:
      linear-gradient(180deg, var(--light-bg) 0%, var(--light-bg) 60%, rgba(236, 233, 225, 0) 60%),
      /* Painted as a layer, not as `background: ... , var(--pi-left)`: that
         shorthand would set the background-COLOR, and the reactive header
         samples computed background colours — it flipped the logo to white
         over the light top of this section. */
      linear-gradient(var(--pi-left), var(--pi-left));
  }

  .project-info { background: var(--pi-left); }

  .project-hero__track { padding: 24px 6%; }
  .project-hero__slide { flex-basis: 86%; }
  .project-hero__slide--square { flex-basis: 48.375%; } /* 86% × 9/16 */
  .project-hero__nav { display: none; }
  .project-info__inner { grid-template-columns: 1fr; }
}

/* ===== Ad break (full-bleed promo) ===== */
.ad-break {
  display: block;
  position: relative;
  width: 100%;
  line-height: 0;
  background: var(--light-bg);
}

.ad-break__media {
  width: 100%;
  height: auto;
  max-height: 100vh;
  object-fit: cover;
  display: block;
}

/* ===== Cookie notice (client note, Aug 20: "basic cookie consent") =====
   Bottom bar rather than a modal: nothing here is a decision the visitor has
   to make before reading, and a blocking overlay on a portfolio site reads as
   an ad. Sits above the side label (50) and below the header (100). */
.cookie-notice {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 3vw, 40px);
  padding: 18px clamp(20px, 4vw, 48px);
  background: #393939; /* matches the footer */
  color: var(--white);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
}

.cookie-notice[hidden] { display: none; }

.cookie-notice__text {
  font-size: 0.9375rem;
  line-height: 1.5;
  margin: 0;
  max-width: 68ch;
}

.cookie-notice__text a {
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.cookie-notice__actions {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}

.cookie-notice__btn {
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--white);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 999px;
  padding: 10px 22px;
  cursor: pointer;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.cookie-notice__btn:hover { border-color: var(--white); }

.cookie-notice__btn--accept {
  background: var(--orange);
  border-color: var(--orange);
}

.cookie-notice__btn--accept:hover {
  background: #d4551f;
  border-color: #d4551f;
}

/* The way back to a declined choice, under the footer copyright. */
.footer__cookie-link {
  font-family: inherit;
  font-size: 0.75rem;
  color: inherit;
  opacity: 0.6;
  background: none;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer__cookie-link:hover { opacity: 1; }

@media (max-width: 768px) {
  .cookie-notice {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
    padding: 16px clamp(20px, 4vw, 48px) 20px;
  }

  .cookie-notice__actions > .cookie-notice__btn { flex: 1; }
}

/* ===== Work card: hover video ===== */
.project-card__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Always visible: the paused first frame is the card cover (the bg image
     underneath is just a fallback while the frame loads). */
  opacity: 1;
  z-index: 0;
  pointer-events: none;
}


/* ===== Form notices + Turnstile ===== */
.form-notice {
  padding: 12px 14px;
  border-radius: 4px;
  font-size: 0.9rem;
  line-height: 1.4;
  margin: 0 0 4px;
}

.form-notice--ok {
  background: rgba(77, 184, 164, 0.18);
  border: 1px solid var(--teal);
}

.form-notice--err {
  background: rgba(232, 96, 44, 0.15);
  border: 1px solid var(--orange);
}

.cf-turnstile { margin: 4px 0; }

/* ===== Accessibility: reduced motion (global QA pass) ===== */
@media (prefers-reduced-motion: reduce) {
  .marquee__track,
  .circular__ring,
  .hero__arrow-scroll,
  .side-label__arrow {
    animation: none !important;
  }

  .hero__tagline,
  .hero__arrow,
  .site-main > .featured {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  html { scroll-behavior: auto; }
}

/* ===== Accessibility utilities ===== */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link reveals on focus (keyboard) over the .sr-only base. */
.skip-link:focus {
  position: fixed;
  top: 8px;
  left: 8px;
  width: auto;
  height: auto;
  margin: 0;
  clip: auto;
  overflow: visible;
  white-space: normal;
  z-index: 100000;
  padding: 10px 16px;
  background: var(--white);
  color: var(--black);
  border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}

/* Visible keyboard focus (hover-driven design has no focus cue otherwise).
   Gated behind html.kbd-nav — set by mhd.js only while the visitor is
   actually driving with the keyboard. :focus-visible alone was not enough:
   the drawer moves focus programmatically (open focuses the first link,
   close focuses the burger) and Safari restores focus on back-navigation,
   so touch and mouse users saw orange rings they never asked for (client
   note, Aug 31 — "remove the orange surround"). Keyboard users keep it. */
html.kbd-nav a:focus-visible,
html.kbd-nav button:focus-visible,
html.kbd-nav input:focus-visible,
html.kbd-nav textarea:focus-visible,
html.kbd-nav [tabindex]:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Outside keyboard mode the browser's own default ring must go too, or the
   orange surround is merely swapped for the UA's blue one on the same
   programmatic/restored focus. Keyboard users are covered above. */
html:not(.kbd-nav) a:focus,
html:not(.kbd-nav) button:focus,
html:not(.kbd-nav) [tabindex]:focus {
  outline: none;
}

/* Form fields already answer focus by colouring their own border, so the ring
   above drew a second orange line around it — a double border (client note,
   Aug 21). Browsers count a click into a text field as focus-visible, so it
   showed for mouse users too. One band instead: the border colours, and a
   flush 1px shadow carries it to the same 2px weight the ring had, with no
   layout shift. */
.cta-form__field input:focus-visible,
.cta-form__field select:focus-visible,
.cta-form__field textarea:focus-visible,
.contact-form__field input:focus-visible,
.contact-form__field textarea:focus-visible {
  outline: none;
  box-shadow: 0 0 0 1px var(--orange);
}
