/* ============================================
   1. TOKENS — the "street-art" direction: dark near-black
   green base, white/lime type, orange + mint accents.
   Replaces the earlier glassmorphism/blob palette entirely.
   ============================================ */

:root {
  --ink-dark: #10201A;   /* near-black-green — hero/stats/header/footer background */
  --cream:    #EDE6D8;   /* "What We Do" band background */
  --ink-text: #14231C;   /* headings/body text on cream */
  --white:    #FFFFFF;

  --lime:   #C3F73A;  /* headline accent, icon chips */
  --orange: #F2941F;  /* primary CTA, kicker labels, accents */
  --orange-hover: #DB8412;
  --mint:   #8FEAD1;  /* secondary accent, outline buttons, icon chips */
  --form-error: #FF6B57;  /* contact form validation only — kept out of the
                             main accent rotation so it never gets reused
                             as a "just another brand color" elsewhere */

  --text-muted-dark:  rgba(255, 255, 255, 0.68);  /* body copy on --ink-dark */
  --text-muted-cream: rgba(20, 35, 28, 0.65);     /* body copy on --cream */
  --hairline-dark:  rgba(255, 255, 255, 0.14);
  --hairline-cream: rgba(20, 35, 28, 0.14);

  --font-display: 'Bricolage Grotesque', sans-serif;
  --font-body: 'Work Sans', sans-serif;
  --font-mono: 'Space Mono', monospace;
  --font-marker: 'Permanent Marker', cursive; /* hand-marker accent — used sparingly */

  /* Torn-paper sticker edge, shared by .spray-badge (hero) and .work-badge
     (portfolio card corners) — a percentage-based polygon so the same
     jaggedness scales correctly regardless of the badge's own box size. */
  --torn-edge: polygon(
    0% 8%, 6% 1%, 14% 6%, 24% 0%, 34% 7%, 46% 1%, 58% 6%, 70% 0%, 82% 7%, 92% 2%, 100% 9%,
    94% 20%, 100% 32%, 95% 44%, 100% 56%, 94% 68%, 100% 80%, 96% 92%, 100% 100%,
    88% 95%, 76% 100%, 64% 94%, 52% 100%, 40% 95%, 28% 100%, 16% 94%, 4% 100%, 0% 93%,
    6% 80%, 0% 68%, 5% 56%, 0% 44%, 6% 32%, 0% 20%
  );

  --header-h: 66px; /* fallback estimate; corrected by script.js once the header renders */
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  color: var(--ink-text);
  background-color: var(--ink-dark);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.01em;
}

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

.container {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 32px;
}

/* Small uppercase mono labels — used sparingly, as quiet utility text */
.mono-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  display: block;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--orange);
  margin-bottom: 22px;
}


/* ============================================
   2. BUTTONS — solid orange primary (the reference's CTA
   color), outline secondary. Inverted from the old outline-
   only system: orange is the loud, confident default now.
   ============================================ */

.btn {
  display: inline-block;
  padding: 16px 32px;
  font-weight: 700;
  font-family: var(--font-body);
  font-size: 1rem;
  cursor: pointer;
  border-radius: 999px;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.btn-primary {
  background: var(--orange);
  color: var(--white);
  border: 2px solid var(--orange);
}

.btn-primary:hover {
  background: var(--orange-hover);
  border-color: var(--orange-hover);
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 12px 26px -10px rgba(242, 148, 31, 0.55);
}

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

.btn-secondary {
  background: transparent;
  color: var(--white);
  border: 2px solid var(--mint);
}

.btn-secondary:hover {
  background: rgba(143, 234, 209, 0.12);
  transform: translateY(-2px) scale(1.02);
}

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

/* Hero-only "sticker" treatment, matching the spray-badge tags around the
   swallow mark. Scoped to .hero-actions (not the base .btn-primary/
   .btn-secondary) so every other button on the site — pricing cards, the
   header CTA, contact CTAs — keeps its existing pill look untouched.
   This only overrides resting-state background/border/shadow/rotation;
   .btn-primary:hover, .btn-secondary:hover, :focus-visible, and .btn's
   shared `transition` above are completely unchanged, so hover/focus
   still animate exactly as they did — the rotation just straightens out
   as part of that same existing transform transition. */
.hero-actions .btn-primary {
  background: #F5A623;
  color: #0B2B25;
  border: 3px solid #0B2B25;
  border-radius: 10px;
  box-shadow: 5px 5px 0 #0B2B25;
  transform: rotate(-2deg);
}

.hero-actions .btn-secondary {
  background: transparent;
  color: var(--white);
  border: 3px solid var(--white);
  border-radius: 10px;
  box-shadow: 5px 5px 0 rgba(255, 255, 255, 0.15);
  transform: rotate(1.5deg);
}

/* .hero-actions .btn-primary and .btn-primary:hover carry equal CSS
   specificity (two class-level selectors each), so without this, the rest-
   state rotation above — being later in the file — would silently win even
   while hovering, killing the lift/scale animation. These two rules exist
   only to out-specificity that collision; the values are copied verbatim
   from the untouched .btn-primary:hover / .btn-secondary:hover rules above,
   not new behavior. */
.hero-actions .btn-primary:hover {
  transform: translateY(-2px) scale(1.02);
}

.hero-actions .btn-secondary:hover {
  transform: translateY(-2px) scale(1.02);
}


/* ============================================
   3. HEADER — a plain full-width bar (not a floating
   pill), sharing the hero's exact background color so the
   two blend seamlessly instead of needing a scroll-state
   background swap.
   ============================================ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--ink-dark);
  border-bottom: 1px solid var(--hairline-dark);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 0;
  gap: 24px;
}

.logo {
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--white);
  display: inline-flex;
  align-items: center;
  gap: 9px;
}

/* logo.png is a solid black silhouette — inverted to white so it reads
   against the dark header/footer instead of disappearing into it. */
.logo-mark {
  height: 30px;
  width: auto;
  display: block;
  filter: brightness(0) invert(1);
}

.logo-chip {
  width: 12px;
  height: 12px;
  background: var(--lime);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  margin-left: 4px;
  transform: translateY(-10px) rotate(8deg);
}

.logo--small {
  font-size: 1.1rem;
}

.nav {
  display: flex;
  gap: 32px;
}

.nav a {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--white);
}

.nav a span {
  position: relative;
  padding-bottom: 4px;
}

.nav a span::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: var(--lime);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
}

.nav a:hover span::after {
  transform: scaleX(1);
}

/* Marks the current page in the nav — same underline mechanism as hover,
   just permanently on instead of triggered by :hover. */
.nav a.is-current span::after {
  transform: scaleX(1);
}

.header-inner .btn {
  padding: 12px 26px;
  font-size: 0.85rem;
}

/* Hamburger — hidden entirely on desktop (display:none removes it from the
   header-inner flex layout completely, so it has zero effect on the
   logo/nav/button space-between arrangement above this breakpoint). Only
   becomes a visible flex item inside the mobile media query below. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 34px;
  height: 34px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: 22px;
  height: 2.5px;
  border-radius: 2px;
  background: var(--white);
  transition: transform 0.25s ease, opacity 0.2s ease;
}

/* Morphs the three bars into an X while the menu is open. */
.site-header.nav-open .nav-toggle span:nth-child(1) {
  transform: translateY(7.5px) rotate(45deg);
}

.site-header.nav-open .nav-toggle span:nth-child(2) {
  opacity: 0;
}

.site-header.nav-open .nav-toggle span:nth-child(3) {
  transform: translateY(-7.5px) rotate(-45deg);
}


/* ============================================
   4. HERO — full-bleed dark section, headline + CTAs on
   the left, the swallow mark plus scattered spray-paint
   tag badges and doodle marks on the right. No rounded
   floating card, no min-height:100vh — sized to its content,
   matching the reference's proportions.
   ============================================ */

.hero {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  overflow: hidden;
  padding: 64px 0 90px;
}

.hero .container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 32px;
}

.kicker {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--orange);
  margin-bottom: 20px;
  opacity: 0;
}

.hero-headline {
  font-size: clamp(2.6rem, 5.6vw, 4.4rem);
  line-height: 1.03;
  letter-spacing: -0.02em;
}

.hero-headline .line {
  display: block;
  opacity: 0;
  color: var(--white);
  text-transform: uppercase;
}

/* The one hand-marker accent line — used exactly once per headline so it
   stays a signature, not a habit. */
.hero-headline .line:nth-child(2) {
  font-family: var(--font-marker);
  font-weight: 400;
  color: var(--lime);
}

.hero-foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 26px;
  margin-top: 28px;
  opacity: 0;
}

.hero-sub {
  max-width: 46ch;
  font-size: 1.05rem;
  color: var(--text-muted-dark);
}

.hero-actions {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

.hero-visual {
  position: relative;
  min-height: 620px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The whole box is the badge orbit's drag-to-spin surface (script.js
     binds pointer/touch listeners here, not just on .badge-orbit, so
     grabbing the bird itself also works). touch-action: none hands touch
     gestures starting here entirely to that drag logic instead of letting
     the browser try to scroll the page with them. */
  touch-action: none;
  cursor: grab;
}

.hero-visual.is-dragging {
  cursor: grabbing;
}

/* Orbit ring — fills the same box the badges used to scatter across.
   Absolutely positioned (removed from .hero-visual's flex flow) so it
   doesn't disturb the bird's centering; script.js measures this box's
   own size each resize to compute the orbit radius, and treats it as
   the rotation's coordinate origin. */
.badge-orbit {
  position: absolute;
  inset: 0;
}

/* Each item is a zero-size anchor point sitting dead-center in
   .badge-orbit; script.js drives its transform/opacity/filter every frame
   from x/y computed directly on an ellipse (not a circle — the vertical
   radius is ~45% of the horizontal one) so the ring reads as tilted into
   3D rather than flat. scale (0.6x back -> 1.3x front), opacity (0.35
   back -> 1.0 front), and blur (3px back -> 0px front) are all driven off
   the same badge-orbit position each frame, so shrinking/fading/blurring
   read as one continuous depth effect, not three separate animations.
   z-index is also script.js-driven, per-frame, based on which half of
   the ellipse the badge is currently in (see .hero-bird's z-index below
   for the split point). transform/opacity/filter are all compositor/
   paint-only — this never triggers layout reflow no matter how often it
   runs. */
.orbit-item {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  will-change: transform, opacity, filter;
}

/* Recolors the flat black PNG to white via the same brightness(0) invert(1)
   trick used on .logo-mark. (A mask-image: url() version of this was tried
   first — cleaner in theory, since background-color would be fully
   controllable — but Chromium blocks it as a cross-origin load under the
   null origin file:// pages get, even though a plain <img src> to the same
   file works fine. Verified: mask-image threw a CORS console error, this
   doesn't.) */
.hero-bird {
  width: min(78%, 440px);
  height: auto;
  display: block;
  filter: brightness(0) invert(1);
  opacity: 0.95;
  /* Fixed mid-stack z-index so the orbit can read as 3D: script.js gives
     badges in the top half of the ellipse (passing "behind" the bird) a
     lower z-index than this, and badges in the bottom half (passing "in
     front") a higher one. BADGE_ORBIT_BIRD_Z in script.js must match this
     number — it's not read from CSS, just kept in sync by convention. */
  position: relative;
  z-index: 50;
}

/* ---- Spray-paint tag badges — real links now, not decoration, so they
   need to visibly read as clickable. Each badge's tilt lives in a --rot
   custom property (set per badge-* modifier below) rather than directly
   in `transform`, so hover/focus can layer an extra rotation + scale on
   top of that base tilt instead of overwriting it outright — the "sticker
   being lifted off the surface" effect: it tilts further, scales up, and
   its shadow deepens as if peeling up. */
/* Torn-paper edge: a percentage-based polygon (now a shared :root token,
   --torn-edge, since .work-badge on the portfolio cards reuses it too),
   so the same jaggedness scales correctly whether the badge is short
   ("Branding") or long ("Google Ads") — each vertex is a fraction of that
   badge's own box, not a fixed pixel offset. clip-path cuts the border
   along with the fill, which is what gives the 3px border its jagged
   (not rounded-rect) outline for free, without any extra masking layer. */
.spray-badge {
  position: absolute;
  top: 0;
  left: 0;
  display: inline-flex;
  align-items: center;
  background: var(--cream);
  border: 3px solid var(--ink-dark);
  clip-path: var(--torn-edge);
  padding: 12px 20px;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: 0.85rem;
  letter-spacing: -0.01em;
  color: var(--ink-dark);
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.4);
  white-space: nowrap;
  z-index: 2;
  cursor: pointer;
  /* translate(-50%,-50%) centers the badge on .orbit-item's zero-size
     anchor point (top:0;left:0 above puts its own top-left corner there
     otherwise). Everything after that is the badge's own tilt/hover
     animation, untouched by and independent of the orbit's transform on
     the parent .orbit-item. */
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(1);
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.25s ease, background-color 0.2s ease;
}

/* Two small lime paint-spatter flecks bleeding past the torn edge, near
   the bottom corners (top-right is already the icon chip's spot). */
.spray-badge::before,
.spray-badge::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  background: var(--lime);
  pointer-events: none;
  z-index: 1;
}

.spray-badge::before {
  width: 9px;
  height: 9px;
  bottom: -5px;
  left: -5px;
}

.spray-badge::after {
  width: 5px;
  height: 5px;
  bottom: -7px;
  left: 16px;
}

.spray-badge:hover,
.spray-badge:focus-visible {
  transform: translate(-50%, -50%) rotate(calc(var(--rot, 0deg) + 5deg)) scale(1.12);
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.45);
  background: var(--white);
  z-index: 4;
}

.spray-badge:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 4px;
}

.badge-icon {
  position: absolute;
  top: -16px;
  right: -16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2.5px solid var(--ink-dark);
  z-index: 3;
}

.badge-icon svg {
  width: 17px;
  height: 17px;
}

.badge-icon.is-orange { background: var(--orange); color: var(--white); }
.badge-icon.is-mint   { background: var(--mint);   color: var(--ink-dark); }
.badge-icon.is-lime   { background: var(--lime);   color: var(--ink-dark); }

/* Position (top/left/right/bottom) used to be set per badge here to
   scatter them by hand around the bird; now script.js's orbit engine
   places every badge on the circle instead (see .badge-orbit/.orbit-item
   above), evenly spaced by count rather than these fixed spots. Each
   badge keeps its own --rot "sticker" tilt though — that's layered on
   top of the orbit's rotation-cancelling transform (see .spray-badge
   above), independent of where the badge currently sits on the ring. */
.badge-websites         { --rot: -6deg; }
.badge-branding         { --rot: 4deg; }
.badge-meta-ads         { --rot: -4deg; }
.badge-google-ads       { --rot: 5deg; }
.badge-strategy         { --rot: -3deg; }
.badge-content-creation { --rot: 6deg; }

/* All five hero badges now use hand-illustrated sticker PNGs instead of
   the CSS-drawn torn-paper shape — strip the drawn chrome for all of them.
   Position, rotation (--rot), the hover/focus transform, and the
   transition are all inherited untouched from .spray-badge above; only
   background/border/clip-path/shadow/padding are reset here. Compound
   selectors (.spray-badge.badge-websites, not a bare class) so this
   reliably wins over the mobile breakpoint's .spray-badge padding
   override regardless of source order. */
.spray-badge.badge-websites,
.spray-badge.badge-branding,
.spray-badge.badge-meta-ads,
.spray-badge.badge-google-ads,
.spray-badge.badge-strategy,
.spray-badge.badge-content-creation {
  background: none;
  border: none;
  clip-path: none;
  box-shadow: none;
  padding: 0;
}

.spray-badge.badge-websites::before,
.spray-badge.badge-websites::after,
.spray-badge.badge-branding::before,
.spray-badge.badge-branding::after,
.spray-badge.badge-meta-ads::before,
.spray-badge.badge-meta-ads::after,
.spray-badge.badge-google-ads::before,
.spray-badge.badge-google-ads::after,
.spray-badge.badge-strategy::before,
.spray-badge.badge-strategy::after,
.spray-badge.badge-content-creation::before,
.spray-badge.badge-content-creation::after {
  content: none;
}

.spray-badge-img {
  display: block;
  width: 210px;
  height: auto;
}

/* badge-googleads.png's artwork sits noticeably shorter within its canvas
   than the other four badges', so at the shared 210px width it reads as
   visibly smaller — bumped 1.25x (210px -> 262.5px) to bring it in line
   with Strategy, the closest match in size. Position/rotation (--rot,
   top/left/right/bottom on .badge-google-ads) are untouched. */
.spray-badge.badge-google-ads .spray-badge-img {
  width: 262.5px;
}

/* .spray-badge:hover still sets background/box-shadow (the cream-badge
   "lift" look) — without this, hovering these would flash a white
   rectangle in behind the transparent PNG. The transform (the actual
   lift/rotate/scale animation) is untouched here, so the hover motion
   stays exactly as it was; only the reappearing background/shadow shape
   is suppressed, consistent with removing it at rest above. */
.spray-badge.badge-websites:hover,
.spray-badge.badge-branding:hover,
.spray-badge.badge-meta-ads:hover,
.spray-badge.badge-google-ads:hover,
.spray-badge.badge-strategy:hover,
.spray-badge.badge-content-creation:hover,
.spray-badge.badge-websites:focus-visible,
.spray-badge.badge-branding:focus-visible,
.spray-badge.badge-meta-ads:focus-visible,
.spray-badge.badge-google-ads:focus-visible,
.spray-badge.badge-strategy:focus-visible,
.spray-badge.badge-content-creation:focus-visible {
  background: none;
  box-shadow: none;
}

/* ---- Scattered doodle marks (X / star / lightning) ---- */
.doodle-mark {
  position: absolute;
  pointer-events: none;
  z-index: 1;
}

.mark-1 { top: 14%;  left: 30%; width: 22px; color: var(--mint); transform: rotate(-10deg); }
.mark-2 { top: 46%;  left: 6%;  width: 20px; color: var(--lime); transform: rotate(12deg); }
.mark-3 { top: 2%;   right: 14%; width: 24px; color: var(--white); opacity: 0.6; transform: rotate(8deg); }
.mark-4 { bottom: 30%; right: 6%; width: 22px; color: var(--lime); transform: rotate(-8deg); }
.mark-5 { bottom: 6%;  left: 32%; width: 20px; color: var(--orange); transform: rotate(15deg); }


/* ============================================
   4b. PAGE HERO — the compact title band used by every
   inner page instead of the homepage's full hero. Reuses
   .kicker/.hero-headline/.hero-foot verbatim (same markup,
   same class names) so the existing on-load GSAP entrance
   timeline in script.js animates it automatically, with
   zero JS changes.
   ============================================ */

.page-hero {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  padding: 72px 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.page-hero .hero-headline {
  font-size: clamp(2.1rem, 4.6vw, 3.4rem);
}

.page-hero .hero-foot {
  margin-top: 24px;
}


/* ============================================
   5. WHAT WE DO — cream band, full-bleed, five icon
   columns. A torn-paper zigzag marks the transition down
   from the dark hero, kept modest rather than literal.
   ============================================ */

.services {
  position: relative;
  z-index: 1;
  background: var(--cream);
  padding: 90px 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.services::before {
  content: '';
  position: absolute;
  top: -18px;
  left: 0;
  right: 0;
  height: 18px;
  background: var(--cream);
  clip-path: polygon(
    0% 100%, 5% 30%, 10% 100%, 15% 30%, 20% 100%, 25% 30%, 30% 100%, 35% 30%,
    40% 100%, 45% 30%, 50% 100%, 55% 30%, 60% 100%, 65% 30%, 70% 100%, 75% 30%,
    80% 100%, 85% 30%, 90% 100%, 95% 30%, 100% 100%
  );
}

.services .eyebrow { color: var(--orange); }

.services h2 {
  color: var(--ink-text);
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  text-transform: uppercase;
}

/* ---- "What We Do" only — scoped so this doesn't bleed onto the other
   pages that reuse .services (services.html, pricing.html, work.html). ---- */

/* Hand-marker heading, matching the reference's rough painted-label look,
   with a small rotated underline stroke standing in for a marker swipe. */
.what-we-do .eyebrow {
  font-family: var(--font-marker);
  font-size: 1.7rem;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-text);
  margin-bottom: 34px;
  position: relative;
  display: inline-block;
}

.what-we-do .eyebrow::after {
  content: '';
  display: block;
  width: 92px;
  height: 5px;
  background: var(--ink-text);
  border-radius: 3px;
  margin-top: 4px;
  transform: rotate(-1.5deg);
}

/* A second torn edge at the bottom, mirroring the existing top one
   (.services::before, shared) but scoped only to this section so other
   .services-using pages are unaffected. */
.what-we-do::after {
  content: '';
  position: absolute;
  bottom: -18px;
  left: 0;
  right: 0;
  height: 18px;
  background: var(--cream);
  clip-path: polygon(
    0% 0%, 5% 70%, 10% 0%, 15% 70%, 20% 0%, 25% 70%, 30% 0%, 35% 70%,
    40% 0%, 45% 70%, 50% 0%, 55% 70%, 60% 0%, 65% 70%, 70% 0%, 75% 70%,
    80% 0%, 85% 70%, 90% 0%, 95% 70%, 100% 0%
  );
}

.icon-row {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0;
  margin-top: 40px;
}

.icon-col {
  position: relative;
  padding: 0 22px;
  border-left: 1px solid var(--hairline-cream);
  transition: transform 0.3s ease, filter 0.3s ease;
}

.icon-col:first-child {
  border-left: none;
  padding-left: 0;
}

/* Whole card lifts with a soft shadow on hover/focus — .icon-col has no
   background/border of its own (just the divider rule), so filter:
   drop-shadow (which follows the content's silhouette) reads better here
   than box-shadow (which would draw a hard rectangle behind transparent
   padding).
   transform needs !important: .icon-col is also a GSAP [data-reveal-item]
   scroll-reveal target (see script.js), and GSAP leaves an inline
   transform (translate3d(0,0,0)) on the element once that intro tween
   finishes — an inline style beats a plain stylesheet rule on the same
   property regardless of :hover, so without this the lift would silently
   never apply after the reveal had already played. filter isn't part of
   that tween, so it doesn't need the same override. */
.icon-col:hover,
.icon-col:focus-within {
  transform: translateY(-4px) !important;
  filter: drop-shadow(0 10px 18px rgba(20, 35, 28, 0.2));
}

/* Bare icon, no badge/container — sits directly on the cream background.
   Black outline at rest (fill:none, so only the stroke shows); on
   hover/focus (triggered on the whole .icon-col, so the effect reads as
   tied to the card, not just this small icon) the main shape "fills in"
   with the item's accent color. --chip-accent driving the fill is set
   per-variant below. */
.icon-chip {
  display: inline-flex;
  margin-bottom: 32px;
  color: var(--ink-text);
  transition: color 0.25s ease;
}

.icon-chip svg {
  width: 36px;
  height: 36px;
  fill: none;
}

.icon-chip svg * {
  transition: fill 0.25s ease, stroke 0.25s ease;
}

.icon-col:hover .icon-chip,
.icon-col:focus-within .icon-chip {
  color: var(--chip-accent);
}

/* stroke:none here too — the outline's stroke-width (3, in a 24-unit
   viewBox) is thick relative to the glyph, and leaving it on at the same
   color as the new fill rounds off sharp corners into a blobby
   silhouette (most visible on Branding's tag shape). Dropping it once
   the shape is filled lets the fill alone define a crisp outline; the
   line divider rule below still wins on specificity for its own stroke. */
.icon-col:hover .icon-chip svg,
.icon-col:focus-within .icon-chip svg {
  fill: var(--chip-accent);
  stroke: none;
}

/* A few glyphs (Web Design, Branding, Ad Creation) carry small detail
   marks — nav dots, a tag's punch-hole, a play triangle — drawn
   fill="currentColor" on top of a larger outline shape. Once that larger
   shape fills solid with the accent color on hover, a detail mark
   tracking the same accent (the default currentColor behavior) would
   vanish into it; punching these through in the page's own cream
   background instead keeps them reading as a cutout against the fill.
   The Strategy & Growth bars are deliberately excluded (no .icon-punch
   class) — they're solid-filled main glyphs in their own right, not
   detail marks over another shape, so they should track the accent
   color like everything else, not go cream. */
.icon-col:hover .icon-chip svg .icon-punch,
.icon-col:focus-within .icon-chip svg .icon-punch {
  fill: var(--cream);
}

/* Web Design's two divider lines have no fill of their own (a line has
   no area to fill) — their visibility is purely their stroke, which
   would otherwise inherit the same animating accent color as the frame
   it's drawn over and disappear the same way. <line> is only ever used
   for these dividers across the icon set, so the tag selector alone is
   precise enough without needing its own class. */
.icon-col:hover .icon-chip svg line,
.icon-col:focus-within .icon-chip svg line {
  stroke: var(--cream);
}

/* Per-item accent token driving both the hover fill and (via currentColor)
   the glyph's own solid-filled details. */
.icon-chip.is-mint   { --chip-accent: var(--mint); }
.icon-chip.is-orange { --chip-accent: var(--orange); }
.icon-chip.is-lime   { --chip-accent: var(--lime); }

/* .is-orange/.is-lime are also bare utility classes elsewhere (see
   .service-dot's use of them) that set a solid background — .icon-chip
   itself sets no background of its own anymore (the whole point of this
   component now: no badge behind the icon), so without this guard those
   bare rules would be the only thing setting background at all and
   would paint a solid block behind the glyph. Compound selector so it
   wins regardless of source order, the same fix used for the hero
   badges' chrome-stripping earlier in this file. */
.icon-chip.is-mint,
.icon-chip.is-orange,
.icon-chip.is-lime {
  background: transparent;
}

@media (prefers-reduced-motion: reduce) {
  .icon-col,
  .icon-chip,
  .icon-chip svg * {
    transition-duration: 0.15s;
    transition-delay: 0s;
  }

  .icon-col:hover,
  .icon-col:focus-within {
    transform: none !important;
    filter: none;
  }
}

.icon-col h3 {
  font-size: 1.05rem;
  color: var(--ink-text);
  text-transform: uppercase;
  margin-bottom: 8px;
}

.icon-col p {
  font-size: 0.92rem;
  color: var(--text-muted-cream);
}


/* ============================================
   6. SERVICE / PRICING CARDS — bold white "sticker" cards:
   thick dark border, hard offset shadow, no soft blur.
   Reads as graphic and confident rather than the old
   translucent-glass tint.
   ============================================ */

.service-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 12px;
}

.service-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 20px;
  background: var(--white);
  border: 3px solid var(--ink-dark);
  box-shadow: 6px 6px 0 rgba(16, 32, 26, 0.85);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.service-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: 9px 9px 0 rgba(16, 32, 26, 0.85);
}

/* Services page's cards are real links (whole card clickable through to
   pricing), so this is scoped to the tag+class combo — higher specificity
   than .service-card:hover above, which work.html's plain <div> cards
   (not links) still use untouched. Straight vertical lift instead of the
   diagonal one, per this page's own hover spec.
   transform needs !important: .service-card is also a GSAP
   [data-reveal-item] scroll-reveal target (see script.js), and GSAP
   leaves an inline transform on the element once that intro tween
   finishes — an inline style beats a plain stylesheet rule on the same
   property regardless of :hover, so without this the lift would silently
   never apply after the reveal had already played (same issue fixed for
   .icon-col's hover lift earlier in this file). */
a.service-card:hover,
a.service-card:focus-visible {
  transform: translateY(-6px) !important;
  box-shadow: 12px 12px 0 rgba(16, 32, 26, 0.85);
}

a.service-card:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 4px;
}

.service-photo {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  flex-shrink: 0;
}

/* Website Design card's screenshot only — the matcha site's real content
   (product grid) sits mid-page, so a default center crop was showing that
   instead of the header/hero band. Scoped to this one image via modifier
   class rather than changing the shared .service-photo rule, so the
   other two cards' crops are unaffected. */
.service-photo--top {
  object-position: top;
}

/* Color scheme driven entirely by --card-bg/--card-fg/--card-fg-muted,
   left unset (falling back to the original white/ink-text/muted-cream
   trio) unless a .service-card--* modifier below sets them — same
   padding/font-size/spacing regardless, only the three color values
   change. Reusable: a future card just needs one new modifier rule
   setting these three properties, no other CSS to touch.
   flex: 1 (against .service-card's own flex-column above) makes this
   fill whatever height the grid row stretches the card to, whatever its
   own text content's natural height is — without it, a card with
   shorter copy than its row-mates fell short of the card's full
   (grid-stretched) height, leaving .service-card's own white background
   exposed as a bar beneath the rounded bottom corners. */
.service-card-body {
  display: flex;
  flex-direction: column;
  padding: 26px 24px 28px;
  background: var(--card-bg, var(--white));
  transition: background-color 0.25s ease;
  flex: 1;
}

.service-card h3 {
  font-size: clamp(1.2rem, 2vw, 1.5rem);
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  text-transform: uppercase;
  color: var(--card-fg, var(--ink-text));
}

.service-card p {
  color: var(--card-fg-muted, var(--text-muted-cream));
  font-size: 0.98rem;
}

/* Per-card palette, sampled from each card's own image — Website Design's
   matcha green, a warm terracotta pulled from the sunglasses ad's lens/
   earring tones, and a neutral near-black matching the frosty ad's own
   black-and-white treatment. */
.service-card--matcha {
  --card-bg: #1F3A28;
  --card-fg: var(--white);
  --card-fg-muted: rgba(255, 255, 255, 0.72);
}

.service-card--sunglasses {
  --card-bg: #8A3B24;
  --card-fg: var(--white);
  --card-fg-muted: rgba(255, 255, 255, 0.72);
}

.service-card--frosty {
  --card-bg: #121212;
  --card-fg: var(--white);
  --card-fg-muted: rgba(255, 255, 255, 0.72);
}

/* The CTA reuses the dot's is-lime/is-orange/is-ink color unmodified on
   every card except this one: .is-ink's near-black text (fine on the
   card's old white body) would nearly disappear against the new
   terracotta background, so it's the one case needing a lighter accent
   here — scoped to this card only, the shared .service-cta.is-ink rule
   (and the dot, left untouched per spec) still renders its normal color
   everywhere else. */
.service-card--sunglasses .service-cta.is-ink {
  color: var(--mint);
}

/* "See pricing" indicator — part of the card's own content (not a
   separate button), just visually signaling the whole card links out.
   Color matches that card's dot via the same is-lime/is-orange/is-ink
   modifier reused on this element; the compound override right below
   guards against .is-orange/.is-lime/.is-ink also existing as bare
   utility classes elsewhere (.service-dot, .work-card) that set
   background/opacity — the same cross-component collision fixed for
   .icon-chip and .work-card earlier in this file.
   margin-top: auto (against .service-card-body's own flex-column) pins
   this to the bottom of the body regardless of how many lines the
   description above it wraps to, so all three cards' links land on the
   same horizontal line even though their copy lengths differ. padding-top
   keeps the old 16px minimum gap above it in the (currently hypothetical)
   case where the description is long enough to leave no free space for
   the auto margin to consume. */
.service-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;
  padding-top: 16px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.service-cta.is-lime,
.service-cta.is-orange,
.service-cta.is-ink {
  background: none;
  opacity: 1;
}

.service-cta.is-lime   { color: var(--lime); }
.service-cta.is-orange { color: var(--orange); }
.service-cta.is-ink    { color: var(--ink-dark); }

.service-cta-arrow {
  display: inline-block;
  transition: transform 0.25s ease;
}

a.service-card:hover .service-cta-arrow,
a.service-card:focus-visible .service-cta-arrow {
  transform: translateX(4px);
}

.service-dot {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 10px;
  vertical-align: middle;
  transform: translateY(-1px);
}

.is-orange { background: var(--orange); }
.is-lime   { background: var(--lime); }
.is-ink    { background: var(--ink-dark); opacity: 0.6; }


/* ============================================
   7. ABOUT PAGE — five acts: a point-of-view hero, the
   origin story (with animated stat counters), an "old way
   vs Doulve way" comparison, a 4-step process timeline, and
   a founder spotlight, closing into the shared .contact CTA.
   Editorial padding-driven sections (not min-height:100vh
   like the Work page) — this page is meant to read like a
   chapter you scroll through, not a sequence of full-screen
   moments, so it doesn't feel like a re-skin of Work.
   ============================================ */

/* ---- Act 1: hero / point of view ----
   Single-column (no visual/badge-orbit column like the homepage hero —
   there's no matching asset for this page), so the headline gets to run
   wide and carry the section on its own. Reuses .kicker/.hero-headline/
   .hero-foot verbatim, so the existing on-load GSAP entrance timeline in
   script.js animates it automatically, zero JS changes. */
.about-hero {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  overflow: hidden;
  padding: 100px 0 110px;
}

.about-hero-inner {
  position: relative;
  z-index: 1;
  max-width: 880px;
}

.about-hero-headline {
  font-size: clamp(2.6rem, 6vw, 5rem);
  line-height: 1.02;
}

.about-hero-headline .line:nth-child(3) {
  color: var(--lime);
}

.about-hero .hero-sub {
  max-width: 56ch;
  font-size: 1.15rem;
}

.about-hero .hero-actions {
  margin-top: 32px;
}

/* ---- Act 2: origin story — cream band, photo + narrative + counters ---- */
.origin-section {
  position: relative;
  z-index: 1;
  background: var(--cream);
  padding: 90px 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.origin-panel {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 56px;
  align-items: center;
}

.origin-photo-wrap {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 3px solid var(--ink-dark);
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.25);
}

.origin-photo {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

.origin-photo-note {
  position: absolute;
  left: 14px;
  bottom: 14px;
  margin: 0;
  background: rgba(16, 32, 26, 0.85);
  color: var(--white);
  border-color: rgba(255, 255, 255, 0.25);
}

.origin-section .eyebrow { color: var(--orange); }

.origin-section h2 {
  color: var(--ink-text);
  font-size: clamp(1.8rem, 3.2vw, 2.5rem);
  text-transform: uppercase;
  max-width: 18ch;
}

.origin-lede {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.3rem, 2.2vw, 1.6rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--ink-text);
  margin-top: 18px;
}

.origin-body {
  color: var(--text-muted-cream);
  font-size: 1.02rem;
  margin-top: 16px;
  max-width: 60ch;
}

/* The animated-counter row — this page's signature device. Numbers sit
   idle at their real, correct value (see the HTML comment above
   .origin-stats) until initStatCounters() in script.js resets them to 0
   and counts back up once scrolled into view; motionDisabled/no-JS users
   simply never see them move; the number was always right either way. */
.origin-stats {
  display: flex;
  gap: 36px;
  flex-wrap: wrap;
  margin-top: 36px;
  padding-top: 28px;
  border-top: 1px solid var(--hairline-cream);
}

.origin-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.origin-stat-number {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.8rem, 3vw, 2.4rem);
  color: var(--ink-text);
  letter-spacing: -0.02em;
}

.origin-stat-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted-cream);
  max-width: 16ch;
}

/* ---- Act 3: differentiators — dark band, old-way/new-way comparison ---- */
.diff-section .eyebrow { color: var(--orange); }

.diff-section h2 {
  color: var(--white);
  font-size: clamp(1.8rem, 3.6vw, 2.6rem);
  text-transform: uppercase;
  max-width: 16ch;
}

.diff-section .section-sub {
  color: var(--text-muted-dark);
  max-width: 58ch;
  margin-top: 18px;
  font-size: 1.02rem;
}

.diff-table {
  margin-top: 44px;
  display: flex;
  flex-direction: column;
}

.diff-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  padding: 22px 0;
  border-top: 1px solid var(--hairline-dark);
}

.diff-row--head {
  padding-bottom: 14px;
}

.diff-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.diff-label--old { color: var(--text-muted-dark); }
.diff-label--new { color: var(--lime); }

.diff-old,
.diff-new {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 1rem;
  line-height: 1.4;
}

.diff-old {
  color: var(--text-muted-dark);
  opacity: 0.75;
}

.diff-new {
  color: var(--white);
}

.diff-icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 2px;
}

.diff-old .diff-icon { color: rgba(255, 255, 255, 0.35); }
.diff-new .diff-icon { color: var(--lime); }

/* ---- Act 4: process — cream band, 4-step timeline ----
   A real sequence (discover -> build -> launch -> grow), so numbering
   here earns its place rather than decorating an unordered list. A
   connecting line runs behind the steps on desktop like a timeline;
   collapses to a plain vertical stack on mobile. */
.process-section {
  position: relative;
  z-index: 1;
  background: var(--cream);
  padding: 90px 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.process-section .eyebrow { color: var(--orange); }

.process-section h2 {
  color: var(--ink-text);
  font-size: clamp(1.8rem, 3.6vw, 2.6rem);
  text-transform: uppercase;
  max-width: 16ch;
}

.process-section .section-sub {
  color: var(--text-muted-cream);
  max-width: 58ch;
  margin-top: 18px;
  font-size: 1.02rem;
}

.process-track {
  position: relative;
  margin-top: 52px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 32px;
}

.process-track::before {
  content: '';
  position: absolute;
  top: 22px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--hairline-cream);
}

.process-step {
  position: relative;
}

.process-number {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--cream);
  border: 3px solid var(--ink-dark);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--ink-text);
}

.process-step h3 {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--ink-text);
  text-transform: uppercase;
  font-size: 1.15rem;
  margin-top: 18px;
}

.process-step p {
  color: var(--text-muted-cream);
  margin-top: 8px;
  font-size: 0.95rem;
}

/* ---- Act 5: founder spotlight — dark band, photo + pull-quote ---- */
.founder-panel {
  display: grid;
  grid-template-columns: 0.7fr 1.3fr;
  gap: 56px;
  align-items: center;
}

.founder-photo-wrap {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 3px solid var(--hairline-dark);
}

.founder-photo {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 5 / 6;
  object-fit: cover;
}

.founder-photo-note {
  position: absolute;
  left: 14px;
  bottom: 14px;
  margin: 0;
}

.founder-section .eyebrow { color: var(--orange); }

.founder-section h2 {
  color: var(--white);
  font-size: clamp(1.6rem, 2.8vw, 2.2rem);
  text-transform: uppercase;
  max-width: 20ch;
}

.founder-quote {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.3rem, 2.4vw, 1.8rem);
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--white);
  margin-top: 26px;
  max-width: 46ch;
}

.founder-attribution {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--mint);
  margin-top: 16px;
}


/* ============================================
   8. CONTACT
   ============================================ */

.contact {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  padding: 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

.contact-inner {
  display: flex;
  justify-content: center;
  padding: 90px 0;
}

.contact .eyebrow { color: var(--orange); }

.contact h2 {
  font-size: clamp(2rem, 4vw, 2.8rem);
  margin: 4px 0 24px;
  color: var(--white);
  text-transform: uppercase;
}

.contact-note {
  color: var(--text-muted-dark);
  font-size: 1.02rem;
  max-width: 40ch;
}

/* ---- The single boxed form card ----
   Same border/radius/fill/padding the old "Prefer email?" sidebar card
   used (see .contact-alt-card below, now retired) — the form lives
   inside it instead of a separate alternative-contact block. Capped
   width + centered rather than stretching to the full 1180px container,
   since a form this wide would read as an odd, overly long line length. */
.contact-form-card {
  width: 100%;
  max-width: 640px;
  background: rgba(255, 255, 255, 0.04);
  border: 2px solid var(--hairline-dark);
  border-radius: 18px;
  padding: 32px;
}

.contact-form-wrap .eyebrow {
  margin-bottom: 10px;
}

/* ---- The form itself ---- */
.contact-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* Author styles always beat the UA stylesheet's default [hidden] {
   display: none }, even at equal selector specificity — so setting
   form.hidden = true in script.js would do nothing on its own against
   the display: flex above without this explicit override. */
.contact-form[hidden] {
  display: none;
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
}

/* Grid items default to min-width: auto, which lets a track refuse to
   shrink past its content's min-content size (here, "Company / Business
   (optional)" as one unbreakable label) — 1fr alone doesn't cap a column
   at 50% against that, so without this the row pushes past its container
   instead of wrapping. */
.field-row > .field {
  min-width: 0;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: none;
  padding: 0;
  margin: 0;
}

.contact-form label,
.contact-form legend {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted-dark);
  padding: 0;
}

.field-optional {
  text-transform: none;
  letter-spacing: normal;
  opacity: 0.7;
}

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form textarea {
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--hairline-dark);
  padding: 8px 2px;
  color: var(--white);
  font-family: var(--font-body);
  font-size: 1.05rem;
  transition: border-color 0.2s ease;
}

.contact-form textarea {
  resize: vertical;
}

.contact-form input:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--lime);
}

/* One consistent focus accent (lime) rather than a different color per
   field — calmer, and doesn't imply the color means anything. */
.field.has-error input,
.field.has-error textarea {
  border-color: var(--form-error, #ff6b57);
}

.field-error {
  display: block;
  min-height: 1em;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--form-error, #ff6b57);
}

/* ---- Project-type chips: real radio inputs, visually hidden but still
   focusable/keyboard-operable — the label is the visible "chip", styled
   per :checked/:focus-visible on its sibling input. ---- */
.chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 2px;
}

.chip-option {
  display: inline-flex;
}

.chip-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 9px 18px;
  border-radius: 999px;
  border: 2px solid var(--hairline-dark);
  color: var(--text-muted-dark);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.92rem;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}

.chip-input:hover + .chip {
  border-color: var(--mint);
  color: var(--white);
}

.chip-input:checked + .chip {
  background: var(--lime);
  border-color: var(--lime);
  color: var(--ink-dark);
}

.chip-input:focus-visible + .chip {
  outline: 2px solid var(--lime);
  outline-offset: 2px;
}

/* Honeypot — off-screen, not display:none (bots that check computed
   visibility can skip display:none fields; this still fools simple ones
   while staying invisible/unreachable for real visitors and screen
   readers alike). */
.field-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact-form button {
  margin-top: 8px;
  align-self: flex-start;
}

.form-status {
  min-height: 1.2em;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--form-error, #ff6b57);
}

.form-status.is-pending {
  color: var(--text-muted-dark);
}

/* ---- Success state — swaps in for the form, no page reload/redirect ---- */
.form-success {
  text-align: center;
  padding: 48px 24px;
}

.form-success-icon {
  width: 56px;
  height: 56px;
  color: var(--lime);
  margin-bottom: 20px;
}

.form-success h2 {
  margin: 0 0 12px;
}

.form-success p {
  color: var(--text-muted-dark);
  font-size: 1.02rem;
  max-width: 40ch;
  margin: 0 auto;
}


/* Homepage-style contact TEASER — single-column, centered, no form. Still
   used by the inner pages' closing CTA sections (services/work/about). */
.contact-teaser {
  max-width: 640px;
  margin: 0 auto;
  padding: 80px 64px;
  text-align: center;
}

.contact-teaser .eyebrow {
  display: block;
}

.contact-teaser h2 {
  font-size: clamp(2rem, 4vw, 2.8rem);
  margin: 4px 0 20px;
  color: var(--white);
}

.contact-teaser .contact-note {
  margin: 0 auto 32px;
}

.contact-teaser .btn-primary {
  display: inline-block;
}


/* ============================================
   8b. PRICING
   ============================================ */

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 12px;
}

.pricing-card {
  display: flex;
  flex-direction: column;
  border-radius: 20px;
  background: var(--white);
  border: 3px solid var(--ink-dark);
  box-shadow: 6px 6px 0 rgba(16, 32, 26, 0.85);
  padding: 32px 28px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.pricing-card:hover {
  transform: translate(-3px, -3px);
  box-shadow: 9px 9px 0 rgba(16, 32, 26, 0.85);
}

.pricing-card h3 {
  font-size: 1.35rem;
  margin-bottom: 8px;
  text-transform: uppercase;
}

.pricing-price {
  font-family: var(--font-display);
  font-size: 2.1rem;
  font-weight: 800;
  color: var(--orange);
  margin-bottom: 4px;
}

.pricing-price span {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--text-muted-cream);
}

.pricing-note {
  color: var(--text-muted-cream);
  font-size: 0.95rem;
  margin-bottom: 24px;
}

.pricing-features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 28px;
  flex-grow: 1;
}

.pricing-features li {
  font-size: 0.95rem;
  color: var(--ink-text);
  padding-left: 22px;
  position: relative;
}

.pricing-features li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--orange);
}

.pricing-card .btn-primary {
  align-self: flex-start;
}

/* The premium tier — an orange border/badge instead of a layout change,
   plus a lime-filled button (distinct from every other card's orange
   button), so it still reads as the featured option now that solid-orange
   is the *default* button everywhere, not a special state. */
.pricing-card--featured {
  position: relative;
  border: 3px solid var(--orange);
  box-shadow: 6px 6px 0 var(--orange);
}

.pricing-card--featured:hover {
  box-shadow: 9px 9px 0 var(--orange);
}

.pricing-badge {
  position: absolute;
  top: -14px;
  right: 28px;
  background: var(--orange);
  color: var(--white);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 6px 14px;
  border-radius: 999px;
}

.pricing-card--featured .btn-primary {
  background: var(--lime);
  color: var(--ink-dark);
  border-color: var(--lime);
}

.pricing-card--featured .btn-primary:hover {
  background: #AEDF2E;
  border-color: #AEDF2E;
  color: var(--ink-dark);
}


/* ============================================
   8b. WORK + CONTENT SHOWCASE — two dark full-bleed bands
   (Previous Work, Content Creation) between What We Do and
   the closing stats. Shared .dark-panel base + a
   .placeholder-tile grid: the tiles reuse the hero badges'
   "sticker on dark ground" treatment (cream fill, thick dark
   border, hard offset shadow) as blank stand-ins for real
   screenshots/mockups later.
   ============================================ */

.dark-panel {
  position: relative;
  z-index: 1;
  overflow: hidden;
  background: var(--ink-dark);
  padding: 90px 0;
  scroll-margin-top: calc(var(--header-h) + 16px);
}

/* Two of these land back-to-back (Previous Work -> Content Creation),
   with no cream band between them to break things up visually — a thin
   hairline stands in for that separation instead. */
.dark-panel + .dark-panel {
  border-top: 1px solid var(--hairline-dark);
}

.dark-panel .eyebrow {
  color: var(--orange);
}

/* Section-level "view more" CTA — the eyebrow/heading/subtext are grouped
   into .section-header-text so the button can sit alongside that whole
   block instead of just the eyebrow line, which read as floating above
   the copy rather than level with it. Bottom-aligned so the button sits
   level with the paragraph (and toward the grid/belt below it) rather
   than up near the heading — both sections' buttons land at the same
   vertical position this way. No margin-bottom needed here:
   .section-header-text's own children keep their normal margins, and
   .work-grid/.content-belt's own margin-top still provides the gap down
   to the grid/belt below, same as before this row wrapper existed. */
.section-header-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
}

.section-header-text {
  min-width: 0;
}

/* Same sticker colors as .hero-actions' .btn-primary (solid-orange,
   dark text) — reused rather than redefined, just scaled down ~35%
   (padding, font-size, border, shadow) since this is a small corner CTA,
   not a main hero button. Compound selector so it outranks the base
   .btn-primary's own background/color/border. */
.btn-view-more {
  padding: 10px 20px;
  font-size: 0.68rem;
  border-radius: 8px;
  flex-shrink: 0;
}

.section-header-row .btn-primary.btn-view-more {
  background: #F5A623;
  color: #0B2B25;
  border: 2px solid #0B2B25;
  box-shadow: 3px 3px 0 #0B2B25;
}

.dark-panel h2 {
  color: var(--white);
  font-size: clamp(1.8rem, 3.6vw, 2.6rem);
  text-transform: uppercase;
  max-width: 16ch;
}

.dark-panel .section-sub {
  color: var(--text-muted-dark);
  max-width: 58ch;
  margin-top: 18px;
  font-size: 1.02rem;
}

.dark-panel .placeholder-note {
  margin-top: 20px;
  color: var(--text-muted-dark);
  background: rgba(242, 148, 31, 0.14);
  border-color: rgba(242, 148, 31, 0.35);
}

/* Scattered doodle accents, same idea as the hero's — decorative only,
   never in the way of real content. */
.section-doodle {
  position: absolute;
  pointer-events: none;
  z-index: 0;
}

/* ---- Content Creation infinite scroll belt ----
   Two rows, each row's 9 DOM tiles are really 3 unique images repeated
   3 times total (the 2nd and 3rd copies marked aria-hidden). Two copies
   isn't enough width: the row's visible width (up to ~1116px, since
   .container caps at 1180px) is wider than one set's translated
   distance, so with only one duplicate the track would run out of
   tiles and flash empty background for part of every loop, reading as
   a stutter. Animating .belt-track by --belt-shift (the pixel width of
   one unique set, measured in JS — see initContentBelt() in script.js)
   moves exactly one set-width per loop, so the next identical copy
   lands right where the previous one started and the loop point is
   invisible, while the 3rd copy stays in reserve to cover the visible
   width for the whole slide. This also isn't the same as a flat -50%:
   with flexbox `gap`, N-1 gaps are spread across 3N tiles, so a flat
   fraction of the total track width doesn't land on the true set
   boundary — a small but real seam. --belt-shift defaults to 50% only
   until JS overwrites it on load. */
.content-belt {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 44px;
}

.belt-row {
  overflow: hidden;
  /* Fades the row to transparent at its own left/right edges instead of
     hard-clipping a tile mid-image against the section's edge. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
}

.belt-track {
  display: flex;
  width: max-content;
  gap: 24px;
  /* Fallback only — flexbox `gap` is applied N-1 times across the 2N
     tiles, not proportionally to each half, so 50% of the full track
     width lands short of the true set boundary by half a gap. script.js
     measures the real offset (the first duplicated tile's offsetLeft)
     and overwrites this via setProperty on load/resize; this value only
     shows if that measurement hasn't run yet. */
  --belt-shift: 50%;
  animation: belt-scroll-left 30s linear infinite;
}

.belt-row--right .belt-track {
  animation-name: belt-scroll-right;
}

@keyframes belt-scroll-left {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1 * var(--belt-shift))); }
}

@keyframes belt-scroll-right {
  from { transform: translateX(calc(-1 * var(--belt-shift))); }
  to   { transform: translateX(0); }
}

.belt-tile {
  flex: 0 0 auto;
  width: 200px;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border: 3px solid var(--ink-dark);
  border-radius: 14px;
  box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.4);
}

.belt-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

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

/* ---- "Our Work" portfolio grid ----
   A separate component from the .content-belt scroller above — this one
   needs actual variable card sizes and a dark "elevated panel" card
   instead of a cream box, so it gets its own classes rather than
   overloading the shared ones.

   PERMANENTLY LOCKED SHAPE — 7 tiles, 4 columns x 3 rows, no empty
   cells. Every tile has its own dedicated class below with an explicit
   grid-column/grid-row; there is no grid-auto-flow and nothing here is
   generated from a loop, so the shape cannot drift or duplicate no
   matter what content is swapped into a slot. Only the image/label
   inside a .work-card--* class should ever change — the class list
   itself (which slot exists, its span, its position) is final:

     .work-card--energy        cols 1-2, rows 1-2 (featured, top-left)
     .work-card--skate-top     col 3,    row 1
     .work-card--tattoo        col 4,    row 1
     .work-card--auto-repair   col 4,    rows 2-3 (tall)
     .work-card--skate-under   col 1,    row 3 (under Energy)
     .work-card--matcha        col 2,    row 3 (under Energy)
     .work-card--beauty        col 3,    rows 2-3 (tall)
   ============================================ */
.work-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 170px);
  gap: 24px;
  margin-top: 44px;
}

/* Mobile-only carousel position indicator — real rules live in the
   max-width: 640px breakpoint below; hidden by default everywhere else
   since only that breakpoint turns .work-grid into a scrollable row. */
.work-grid-dots {
  display: none;
}

.work-card--energy      { grid-column: 1 / span 2; grid-row: 1 / span 2; }
.work-card--skate-top   { grid-column: 3; grid-row: 1; }
.work-card--tattoo      { grid-column: 4; grid-row: 1; }
.work-card--auto-repair { grid-column: 4; grid-row: 2 / span 2; }
.work-card--skate-under { grid-column: 1; grid-row: 3; }
.work-card--matcha      { grid-column: 2; grid-row: 3; }
.work-card--beauty      { grid-column: 3; grid-row: 2 / span 2; }

/* "Elevated dark panel" card — a touch lighter than the section's own
   --ink-dark so it still reads as a distinct surface sitting on it,
   rather than the light cream/thick-border "sticker" treatment used for
   the hero badges and the Content Creation placeholder tiles (those sit
   fine as bright accents scattered around a dark section; a whole grid
   of them here would read as too loud/flat). The colored border is the
   accent instead — rotated per card via the .is-* modifiers below. */
.work-card {
  position: relative;
  overflow: hidden;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.05);
  border: 2px solid var(--hairline-dark);
  transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

.work-card.is-orange { border-color: var(--orange); }
.work-card.is-mint   { border-color: var(--mint); }
.work-card.is-lime   { border-color: var(--lime); }

.work-card.is-orange:hover { box-shadow: 0 0 26px -6px rgba(242, 148, 31, 0.6); }
.work-card.is-mint:hover   { box-shadow: 0 0 26px -6px rgba(143, 234, 209, 0.6); }
.work-card.is-lime:hover   { box-shadow: 0 0 26px -6px rgba(195, 247, 58, 0.6); }

.work-card:hover {
  transform: translateY(-3px);
}

/* Corner sticker — same torn-edge/hard-shadow "spray-paint tag" language
   as the hero badges, just smaller and pinned to a card corner instead
   of scattered and orbiting. Shows the project name in place of the old
   generic "Project 01" label. */
.work-badge {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  background: var(--cream);
  color: var(--ink-dark);
  border: 2px solid var(--ink-dark);
  clip-path: var(--torn-edge);
  padding: 7px 13px;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: 0.68rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.4);
}

.work-card--energy .work-badge {
  font-size: 0.85rem;
  padding: 9px 16px;
}

/* Real screenshot slot — sits inside the dark card, object-fit:cover +
   object-position:top so the tile always shows each site's header/hero
   band rather than a squashed full-page shot, regardless of how the
   card's own aspect ratio (featured/tall/standard all differ) crops it.
   The image is the thing that scales on hover (see .work-card:hover
   below); overflow:hidden on .work-card above keeps that zoom from
   spilling past the card's own rounded corners. */
.work-card-media {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.03);
}

.work-card-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  transition: transform 0.3s ease;
}

.work-card:hover .work-card-media img {
  transform: scale(1.07);
}

@media (prefers-reduced-motion: reduce) {
  .work-card,
  .work-card-media img {
    transition: none;
  }
}


/* ============================================
   9. CLOSING STATS + CTA — dark band, headline, four
   stat blocks, mint CTA pill.
   ============================================ */

.stats-section {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  padding: 80px 0;
}

.stats-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 40px;
  flex-wrap: wrap;
}

.stats-headline {
  max-width: 460px;
}

.stats-headline h2 {
  color: var(--white);
  font-size: clamp(1.6rem, 2.8vw, 2.2rem);
  text-transform: uppercase;
}

.stats-headline h2 .accent {
  font-family: var(--font-marker);
  color: var(--lime);
  text-transform: none;
  font-weight: 400;
}

.stats-headline p {
  color: var(--text-muted-dark);
  margin-top: 14px;
  max-width: 40ch;
}

.stats-row {
  display: flex;
  gap: 32px;
  flex-wrap: wrap;
}

.stat-block {
  display: flex;
  align-items: center;
  gap: 12px;
}

.stat-icon {
  width: 30px;
  height: 30px;
  color: var(--orange);
  flex-shrink: 0;
}

.stat-number {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  color: var(--white);
  display: block;
}

.stat-label {
  font-size: 0.72rem;
  color: var(--text-muted-dark);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stats-cta {
  background: var(--mint);
  color: var(--ink-dark);
  border-radius: 20px;
  padding: 22px 30px;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: 1rem;
  line-height: 1.3;
  display: inline-block;
  transition: transform 0.2s ease;
}

.stats-cta:hover {
  transform: translateY(-2px);
}

.stats-cta .accent {
  font-family: var(--font-marker);
  color: var(--orange);
  text-transform: none;
  font-weight: 400;
}


/* ============================================
   10. FOOTER
   ============================================ */

.site-footer {
  position: relative;
  z-index: 1;
  background: var(--ink-dark);
  border-top: 1px solid var(--hairline-dark);
  padding: 32px 0;
}

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}

.footer-inner .mono-label {
  color: var(--text-muted-dark);
}

/* Small sitemap row — gives every page a path to every other page
   (including Pricing, which is deliberately not in the primary nav). */
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

.footer-nav a {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted-dark);
  transition: color 0.2s ease;
}

.footer-nav a:hover {
  color: var(--white);
}

/* Small pill flagging inner-page body content as a temporary stand-in,
   so it's unmistakable which parts still need real copy. */
.placeholder-note {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted-cream);
  background: rgba(242, 148, 31, 0.12);
  border: 1px solid rgba(242, 148, 31, 0.3);
  border-radius: 999px;
  padding: 6px 16px;
  margin-bottom: 24px;
}


/* ============================================
   11. MOTION PREFERENCES
   script.js adds this class when the user prefers reduced motion
   OR the GSAP CDN failed to load — one flag, one fallback state,
   so content can never end up stuck invisible.
   ============================================ */

.motion-disabled {
  scroll-behavior: auto;
}

.motion-disabled .kicker,
.motion-disabled .hero-headline .line,
.motion-disabled .hero-foot,
.motion-disabled .reveal-panel,
.motion-disabled [data-reveal-item],
.motion-disabled .about-inner > *,
.motion-disabled .about-photo,
.motion-disabled .contact-intro > *,
.motion-disabled .contact-form .field,
.motion-disabled .contact-form button {
  opacity: 1 !important;
  transform: none !important;
}

.motion-disabled .btn-primary,
.motion-disabled .nav a span::after,
.motion-disabled .service-card {
  transition: none;
}


/* ============================================
   12. WORK PAGE — THREE-ACT SHOWCASE
   Three distinct full-viewport "moments" (websites carousel,
   illustrated brand boards, ads results) rather than one grid
   style repeated three times. All three share .dark-panel and
   stay dark (--ink-dark) end to end for an unbroken "gallery in
   low light" feel, separated by the existing .dark-panel +
   .dark-panel hairline rule.
   ============================================ */

.web-showcase,
.brand-showcase,
.ads-showcase {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Scales with viewport height instead of a fixed px gap, so the heading
     and the main showcase content read as one deliberately-composed group
     that fills more of a short/medium viewport, rather than a small fixed
     cluster floating in a lot of empty space on a tall one. */
  gap: clamp(32px, 6vh, 72px);
}

/* .container's own `margin: 0 auto` is what centers it everywhere else on
   the site, but a flex item with auto cross-axis margins doesn't stretch
   to fill the flex container the way align-items: stretch (the default)
   normally would — the auto margins take over and the item instead
   shrinks to fit its own content first, THEN centers that shrunk box.
   For a short text block (the heading) that shrunk width was visibly
   narrower than the page hero's .container above it, pulling the
   heading's left/right edge in from the true page margin. Forcing
   width: 100% here gives the item a definite width before margin:auto
   ever gets a say, so max-width: 1180px + margin: 0 auto centers the
   full-width box exactly like every other .container on the site,
   putting its edge back in line with the page hero's. */
.web-showcase > .container,
.brand-showcase > .container,
.ads-showcase > .container {
  width: 100%;
}

/* Websites is now the first section on the page, sitting directly under
   the sticky header — centering its whole heading+carousel group in the
   middle of the viewport (like Branding/Ads do) would leave a large dead
   gap above the heading before it ever comes into view. Anchoring the
   heading near the section's own top instead gives it just enough
   padding to clear the header without feeling cramped; the carousel
   wrapper below picks up its own flex:1 + centering so it still settles
   nicely in whatever space is left, rather than the whole section
   suddenly reading top-heavy. */
.web-showcase {
  justify-content: flex-start;
  padding-top: clamp(56px, 9vh, 100px);
}

.web-showcase-carousel {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Shared heading block (eyebrow, headline, subtext, badge) for all three
   showcase sections — one rhythm defined once so they read as the same
   system rather than three independently-tuned blocks. Overrides the
   generic .eyebrow/.placeholder-note margins (tuned for cream sections
   using their own spacing) with a single, consistent top-down stack:
   every element only ever sets margin-top on itself (never a bottom
   margin fighting the next element's top margin), so the gaps are exactly
   what they say and can't silently drift apart per-section. */
.showcase-heading {
  text-align: left;
}

.showcase-heading .eyebrow {
  margin-bottom: 0;
}

.showcase-heading h2 {
  margin-top: 14px;
}

.showcase-heading .section-sub {
  margin-top: 14px;
}

.showcase-heading .placeholder-note {
  margin-top: 20px;
  margin-bottom: 0;
}

/* Branding's own heading runs a shade larger than the shared .dark-panel
   h2 clamp above (2.6rem ceiling) — with only a handful of illustrated
   cards below it instead of a cinematic carousel or a tall case-study
   list, the section needs its headline carrying a bit more weight on its
   own to read as equally commanding as Websites/Ads. */
.brand-showcase .showcase-heading h2 {
  font-size: clamp(2.1rem, 4.6vw, 3.1rem);
}

/* Websites is the first section a visitor lands on right after the page
   hero, so its headline gets the biggest bump of the three — a bold,
   dominant opening statement rather than a compact block. */
.web-showcase .showcase-heading h2 {
  font-size: clamp(2.4rem, 5.2vw, 3.6rem);
}

/* Two-column hero: heading text left, a large "feature" card right —
   the feature card is a real .brand-board (see .brand-board--feature
   below), just placed as this grid's second column instead of inside
   .brand-grid, so it inherits all of that component's styling/hover/
   is-* accent handling for free. align-items: stretch makes both
   columns match the row's height, which is what lets the feature
   card's canvas grow to fill roughly the same height as the heading
   text instead of using the grid cards' fixed aspect-ratio. */
.brand-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
  gap: 48px;
}

/* Visually flipped via order (not DOM order) — heading stays first in
   the markup/reading order, it just renders in the right-hand column,
   right-aligned so it reads as intentionally anchored to that edge
   rather than left-aligned text that's simply been shoved rightward.
   This alternates with Websites (left) and Ads (left) either side of
   it. Reset to the natural order at the single-column breakpoint below,
   so "heading first" on mobile isn't affected by this desktop-only flip. */
.brand-hero .showcase-heading {
  display: flex;
  flex-direction: column;
  justify-content: center;
  order: 2;
  text-align: right;
}

.brand-hero .brand-board--feature {
  order: 1;
}

.brand-board--feature {
  display: flex;
  flex-direction: column;
}

.brand-board--feature .brand-board-canvas {
  flex: 1;
  aspect-ratio: auto;
  min-height: 320px;
}

.brand-board--feature .brand-board-icon {
  width: 30px;
  height: 30px;
  top: 24px;
  right: 24px;
}

.brand-board--feature .brand-board-mark {
  font-size: clamp(2.6rem, 4vw, 4rem);
}

.brand-board--feature .brand-board-palette span {
  width: 26px;
  height: 26px;
}

.brand-board--feature .brand-board-caption {
  margin-top: 20px;
}

.brand-board--feature .brand-board-caption h3 {
  font-size: 1.15rem;
}

.brand-board--feature .brand-board-caption p {
  max-width: 42ch;
}

/* Shared by the websites carousel and the ads case-study snippets — a
   result number with a small, unmissable "still placeholder" flag next
   to it, so nobody mistakes a stand-in metric for a real one. The flag
   is meant to be deleted the day a real number replaces it. */
.stat-pill {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 7px;
  font-size: 0.86rem;
  color: var(--text-muted-dark);
}

.stat-pill strong {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1rem;
  color: var(--white);
}

.stat-pill em {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted-dark);
  opacity: 0.75;
  border: 1px solid var(--hairline-dark);
  border-radius: 999px;
  padding: 2px 8px;
}

/* ---- Section 1: websites — cinematic horizontal carousel ----
   The track is a direct child of the section (not .container), so its
   8vw side padding lets the current slide sit centered while the next
   one peeks in at the edge — the visual cue that there's more to scroll
   to. scroll-snap-type handles touch/trackpad drag on its own; the
   arrows/dots below (see initWorkShowcase() in script.js) are for mouse
   users, since dragging a horizontal track isn't an obvious affordance
   without them. */
.web-showcase-carousel {
  position: relative;
}

.web-showcase-track {
  display: flex;
  gap: 32px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 4px 8vw 28px;
  scrollbar-width: none;
}

.web-showcase-track::-webkit-scrollbar {
  display: none;
}

.web-slide {
  flex: 0 0 min(78vw, 760px);
  scroll-snap-align: center;
}

.web-slide-media {
  border-radius: 20px;
  overflow: hidden;
  border: 3px solid var(--hairline-dark);
  aspect-ratio: 16 / 10;
  background: rgba(255, 255, 255, 0.03);
}

.web-slide-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

.web-slide-body {
  margin-top: 22px;
}

.web-slide-body h3 {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--white);
  font-size: 1.35rem;
  text-transform: uppercase;
}

.web-slide-body p {
  color: var(--text-muted-dark);
  margin-top: 6px;
  max-width: 48ch;
}

.web-slide-body .stat-pill {
  margin-top: 16px;
}

.web-showcase-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-top: 4px;
}

.web-showcase-arrow {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--hairline-dark);
  background: transparent;
  color: var(--white);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.web-showcase-arrow:hover {
  border-color: var(--lime);
  color: var(--lime);
  transform: translateY(-2px);
}

.web-showcase-arrow:focus-visible {
  outline: 2px solid var(--lime);
  outline-offset: 2px;
}

.web-showcase-dots {
  display: flex;
  gap: 9px;
}

.web-showcase-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--hairline-dark);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.web-showcase-dot.is-active {
  background: var(--lime);
  transform: scale(1.35);
}

/* ---- Section 2: branding — illustrated brand boards ----
   Six CSS/SVG-built "brand board" tiles standing in for real client
   identity work (no branding photography exists yet, same reasoning as
   the hero badges/icon chips being illustrated rather than photographed).
   Same .container-scoped width as Websites/Ads (not full-bleed) and the
   same shared .showcase-heading rhythm, so this reads as the third
   parallel chapter rather than a leftover "everything else" grid. */
.brand-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  width: 100%;
}

.brand-board-canvas {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: 20px;
  border: 3px solid var(--hairline-dark);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  overflow: hidden;
  padding: 24px;
  transition: border-color 0.25s ease, transform 0.25s ease;
}

.brand-board.is-orange:hover .brand-board-canvas { border-color: var(--orange); transform: translateY(-3px); }
.brand-board.is-mint:hover .brand-board-canvas   { border-color: var(--mint); transform: translateY(-3px); }
.brand-board.is-lime:hover .brand-board-canvas   { border-color: var(--lime); transform: translateY(-3px); }

/* .is-orange/.is-lime are also bare utility classes elsewhere (.service-dot)
   that set a solid background — same specificity as this rule, so whichever
   is later in the file wins the collision by accident. This compound
   selector reliably wins regardless of source order (same fix used for
   .icon-chip and .work-card earlier in this file) — .brand-board itself
   should never have a background of its own; only .brand-board-canvas does. */
.brand-board.is-orange,
.brand-board.is-mint,
.brand-board.is-lime {
  background: transparent;
}

.brand-board-icon {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 22px;
  height: 22px;
  opacity: 0.5;
}

.brand-board-mark {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.7rem, 2.6vw, 2.3rem);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  text-align: center;
}

.brand-board-palette {
  display: flex;
  gap: 8px;
}

.brand-board-palette span {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.18);
}

.brand-board-caption {
  margin-top: 16px;
}

.brand-board-caption h3 {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--white);
  font-size: 1rem;
  text-transform: uppercase;
}

.brand-board-caption p {
  color: var(--text-muted-dark);
  margin-top: 4px;
  max-width: 34ch;
}

/* Per-brand personality — the same recurring fictional clients used in
   the Websites/Ads sections, so the page reads as "one client, full
   service" rather than a new unrelated cast for every chapter. Each
   canvas's own colors are deliberately distinct from the shared site
   palette (these are the CLIENTS' brand colors, not Doulve's), while the
   card border/hover accent above still uses the site's own lime/mint/
   orange. */
.brand-board--voltage .brand-board-canvas {
  background: #0b0b0f;
  color: var(--lime);
}

.brand-board--voltage .brand-board-mark {
  font-style: italic;
}

.brand-board--matcha .brand-board-canvas {
  background: var(--cream);
  color: #4b6b3f;
}

.brand-board--matcha .brand-board-mark {
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: none;
}

.brand-board--inkwell .brand-board-canvas {
  background: #1a0a0a;
  color: #e8c77a;
}

.brand-board--inkwell .brand-board-mark {
  font-family: var(--font-marker);
  font-weight: 400;
  text-transform: none;
  font-size: clamp(1.9rem, 3vw, 2.6rem);
}

.brand-board--drift .brand-board-canvas {
  background: #0d0d0d;
  color: #ff2e9a;
}

.brand-board--drift .brand-board-mark {
  transform: rotate(-3deg);
}

.brand-board--aster .brand-board-canvas {
  background: #f6efe9;
  color: #b98a6a;
}

.brand-board--aster .brand-board-mark {
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: none;
}

.brand-board--auto .brand-board-canvas {
  background: #111214;
  color: var(--orange);
}

.brand-board--auto .brand-board-mark {
  letter-spacing: -0.02em;
  font-size: clamp(1.3rem, 2vw, 1.8rem);
}

/* ---- Section 3: ads — results-focused case-study snippets + close ---- */

.case-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.case-snippet {
  display: flex;
  align-items: flex-start;
  gap: 26px;
  background: rgba(255, 255, 255, 0.04);
  border: 2px solid var(--hairline-dark);
  border-radius: 18px;
  padding: 18px;
  transition: border-color 0.25s ease, transform 0.25s ease;
}

.case-snippet.is-orange:hover { border-color: var(--orange); transform: translateY(-2px); }
.case-snippet.is-mint:hover   { border-color: var(--mint); transform: translateY(-2px); }
.case-snippet.is-lime:hover   { border-color: var(--lime); transform: translateY(-2px); }

/* 2/3 matches the real screenshots' actual pixel dimensions (1024x1536)
   exactly, so object-fit: contain shows the whole mockup with zero
   letterboxing for these specific assets — contain (not cover) is what
   stops the crop; the matching ratio is what stops it from needing to
   letterbox in the first place. White background is a safety net for
   any future image that doesn't match perfectly (or has transparency),
   so a letterboxed edge blends with the screenshot's own white chrome
   instead of showing as a visible gap. */
.case-snippet-media {
  position: relative;
  flex: 0 0 190px;
  aspect-ratio: 2 / 3;
  border-radius: 12px;
  overflow: hidden;
  background: var(--white);
}

.case-snippet-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.case-snippet-body {
  flex: 1;
  min-width: 0;
}

.case-snippet-body h3 {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--white);
  font-size: 1.05rem;
  text-transform: uppercase;
}

/* The strategy line — how the campaign was built — reads a shade
   brighter than the outcome line below it, so the two read as distinct
   "approach, then result" beats rather than two identical paragraphs. */
.case-snippet-approach {
  color: var(--white);
  opacity: 0.82;
  margin-top: 8px;
  max-width: 52ch;
}

.case-snippet-outcome {
  color: var(--text-muted-dark);
  margin-top: 6px;
  max-width: 52ch;
}

.case-snippet-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 12px;
}

.ads-cta {
  margin-top: 52px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-top: 36px;
  border-top: 1px solid var(--hairline-dark);
}

.ads-cta p {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--white);
  font-size: 1.3rem;
  text-transform: uppercase;
  max-width: 22ch;
}

@media (prefers-reduced-motion: reduce) {
  .web-showcase-track {
    scroll-behavior: auto;
  }

  .brand-board-canvas,
  .web-showcase-arrow,
  .web-showcase-dot,
  .case-snippet {
    transition: none;
  }
}


/* ============================================
   13. RESPONSIVE
   ============================================ */

@media (max-width: 900px) {
  .hero .container {
    grid-template-columns: 1fr;
  }

  .hero-visual {
    min-height: 460px;
    order: -1;
  }

  .hero-bird {
    width: min(60%, 300px);
  }

  .spray-badge {
    font-size: 0.72rem;
    padding: 8px 14px;
  }

  .icon-row {
    grid-template-columns: repeat(2, 1fr);
    gap: 32px 0;
  }

  .icon-col {
    border-left: none;
    padding-left: 0;
  }

  .icon-col:nth-child(odd) {
    border-right: 1px solid var(--hairline-cream);
    padding-right: 22px;
  }

  .stats-inner {
    flex-direction: column;
    align-items: flex-start;
  }

  .service-grid {
    grid-template-columns: 1fr;
  }

  /* The fixed 4-column desktop placement doesn't fit a 2-column layout —
     drop every tile's explicit column/row and let them stack in plain
     source order instead. */
  .work-grid {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
    grid-auto-rows: 150px;
  }

  .work-card--energy,
  .work-card--skate-top,
  .work-card--tattoo,
  .work-card--auto-repair,
  .work-card--skate-under,
  .work-card--matcha,
  .work-card--beauty {
    grid-column: auto;
    grid-row: auto;
  }

  .pricing-grid {
    grid-template-columns: 1fr;
  }

  .about-panel {
    grid-template-columns: 1fr;
  }

  .about-photo-wrap {
    min-height: 280px;
  }

  .about-inner {
    padding: 40px;
  }

  .web-slide {
    flex-basis: min(86vw, 640px);
  }

  /* Three columns of a big wordmark + palette get cramped well before
     the site's usual 2-up tablet breakpoint, so this drops straight to a
     single column rather than squeezing into 2. */
  .brand-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .brand-board-canvas {
    aspect-ratio: 16 / 9;
  }

  /* Heading, then the feature card below it — DOM order already puts
     the heading first, so dropping to a single column is enough. The
     desktop flip's `order`/text-align:right are reset here so the
     stacked mobile layout always reads heading-first, left-aligned,
     regardless of which side it's pinned to on desktop. */
  .brand-hero {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .brand-hero .showcase-heading {
    order: initial;
    text-align: left;
  }

  .brand-hero .brand-board--feature {
    order: initial;
  }

  .brand-board--feature .brand-board-canvas {
    min-height: 260px;
  }

  .case-snippet-media {
    flex-basis: 140px;
  }

  .origin-panel,
  .founder-panel {
    grid-template-columns: 1fr;
    gap: 36px;
  }

  .origin-photo,
  .founder-photo {
    aspect-ratio: 16 / 10;
  }

  .diff-row {
    grid-template-columns: 1fr;
    gap: 14px;
  }

  .diff-row--head {
    display: none;
  }

  .process-track {
    grid-template-columns: repeat(2, 1fr);
    row-gap: 40px;
  }

  .process-track::before {
    display: none;
  }
}

@media (max-width: 640px) {
  .container {
    padding: 0 20px;
  }

  .section-header-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }

  .hero {
    padding: 24px 0 40px;
  }

  /* Tightens the row gap between the stacked badge/logo area and the
     headline below it (this becomes a single-column stack at this width
     — see .hero .container's grid-template-columns override elsewhere). */
  .hero .container {
    gap: 16px;
  }

  /* "Our Services"/"View Our Work" — sized for a desktop hero, too big
     for this column. Targets both via the shared base .btn's own
     padding/font-size (the sticker color/border/radius/shadow overrides
     on .hero-actions .btn-primary/.btn-secondary are untouched). Scoped
     to .hero-actions specifically so every other button on the site
     (pricing cards, header CTA, contact CTAs) keeps its current size. */
  .hero-actions .btn {
    padding: 10px 20px;
    font-size: 0.65rem;
  }

  /* Decorative-only scatter marks, unrelated to the badges — stay hidden
     at this width regardless of the orbit change below. */
  .doodle-mark {
    display: none;
  }

  /* The orbit runs at every width, all six badges — a smaller
     MOBILE_RADIUS_FACTOR (see script.js) plus a smaller badge base width
     here keep the ring's front-most badges (scaled up to 1.3x at their
     closest point, same FRONT_SCALE desktop uses) from overlapping each
     other or crowding the bird inside this narrow column; an earlier
     revision pushed both up for a "bolder" look, which is exactly what
     was overlapping.

     min-height only needs to clear the orbit's actual vertical reach
     (radiusY + the front badge's own half-height at its 1.3x scale) —
     260px comfortably covers that at the smaller radius below without
     leaving a lot of dead flex-centered space above/below the ring,
     since the ellipse is intentionally much flatter than it is wide. */
  .hero-visual {
    min-height: 260px;
  }

  .hero-bird {
    width: min(82%, 320px);
  }

  .spray-badge-img {
    width: 84px;
  }

  /* Keeps the same 1.25x-over-the-others ratio as the desktop override,
     just against this breakpoint's smaller 84px base. */
  .spray-badge.badge-google-ads .spray-badge-img {
    width: 105px;
  }

  .icon-row {
    grid-template-columns: 1fr;
  }

  .icon-col,
  .icon-col:nth-child(odd) {
    border: none;
    padding: 0;
  }

  .services,
  .stats-section,
  .dark-panel,
  .page-hero {
    padding: 56px 0;
  }

  .about-hero {
    padding: 56px 0 64px;
  }

  .origin-section,
  .process-section {
    padding: 56px 0;
  }

  .belt-tile {
    width: 130px;
  }

  .content-belt {
    gap: 14px;
  }

  .belt-track {
    gap: 14px;
  }

  /* Horizontal scroll-snap carousel instead of a stacked column — reset
     every tile's explicit grid placement (same as before) but swap the
     grid itself for a single scrollable row. 82% card width leaves ~18%
     of the next card peeking in at the right edge (~1.2 cards visible),
     the same "obviously more to swipe" cue .web-showcase-track used to
     use for Work page's site carousel. Scrollbar hidden since the dot
     row below is the visible position indicator. */
  .work-grid {
    display: flex;
    grid-template-columns: none;
    grid-template-rows: none;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }

  .work-grid::-webkit-scrollbar {
    display: none;
  }

  .work-card,
  .work-card--energy,
  .work-card--skate-top,
  .work-card--tattoo,
  .work-card--auto-repair,
  .work-card--skate-under,
  .work-card--matcha,
  .work-card--beauty {
    grid-column: auto;
    grid-row: auto;
    flex: 0 0 82%;
    aspect-ratio: 4 / 3;
    scroll-snap-align: start;
  }

  /* Position dots, one per card — script.js's initWorkGridDots() keeps
     .is-active synced to whichever card is nearest the scroll position.
     Hidden entirely outside this breakpoint (see the base .work-grid-dots
     rule); desktop's grid isn't scrollable so there's nothing for dots to
     indicate there. */
  .work-grid-dots {
    display: flex;
    justify-content: center;
    gap: 9px;
    margin-top: 20px;
  }

  .work-grid-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--hairline-dark);
    border: none;
    padding: 0;
    cursor: pointer;
    transition: background-color 0.2s ease, transform 0.2s ease;
  }

  .work-grid-dot.is-active {
    background: var(--lime);
    transform: scale(1.35);
  }

  .web-slide {
    flex-basis: 88vw;
  }

  .web-showcase-track {
    padding: 4px 6vw 24px;
    gap: 20px;
  }

  .brand-board-canvas {
    aspect-ratio: 4 / 3;
    padding: 20px;
  }

  .case-snippet {
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
  }

  /* Capped rather than full-width — at a 2/3 aspect ratio, full-width
     would make each thumbnail 400px+ tall on a phone, a lot of scroll
     distance for three cards. A fixed, centered size keeps it reading as
     a framed thumbnail above the text instead of a full-bleed image. */
  .case-snippet-media {
    width: 160px;
    flex-basis: auto;
  }

  .ads-cta {
    flex-direction: column;
    align-items: flex-start;
  }

  .contact-inner,
  .contact-teaser {
    padding: 56px 0;
  }

  .field-row {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .header-inner .btn {
    padding: 10px 18px;
    font-size: 0.75rem;
  }

  /* Logo + CTA no longer fit on one row with the full nav at this width
     (logo + nav + button was overflowing the viewport horizontally).
     Collapse the nav behind a hamburger instead of forcing every link
     onto a cramped single line: .nav-toggle becomes visible, .nav is
     hidden by default and only reappears (as a full-width column below
     row 1) once .site-header gets the .nav-open class from script.js. */
  .header-inner {
    flex-wrap: wrap;
    row-gap: 10px;
    justify-content: flex-start;
    column-gap: 14px;
  }

  /* Pushes whichever is the first "right-side" control (the CTA button
     on the homepage, or the toggle itself on pages with no header CTA)
     flush to the right edge, leaving the logo alone on the left — the
     same visual result as the old space-between, without needing it. */
  .header-inner .btn,
  .nav-toggle {
    margin-left: auto;
  }

  /* When both the CTA button and the toggle are present (homepage only),
     the button already claims the auto-margin above; this keeps the
     toggle snug right next to it instead of also grabbing free space and
     splitting the two apart. */
  .header-inner .btn + .nav-toggle {
    margin-left: 14px;
  }

  .nav-toggle {
    display: flex;
  }

  .nav {
    order: 3;
    width: 100%;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin-top: 6px;
    padding-top: 10px;
    border-top: 1px solid var(--hairline-dark);
  }

  .site-header.nav-open .nav {
    display: flex;
  }

  .nav a {
    padding: 10px 4px;
    font-size: 0.95rem;
    letter-spacing: normal;
  }

  .logo {
    font-size: 1.15rem;
    gap: 6px;
  }

  .logo-mark {
    height: 22px;
  }

  .about-hero .hero-actions {
    margin-top: 24px;
  }

  .origin-stats {
    gap: 24px;
    margin-top: 28px;
    padding-top: 22px;
  }

  .process-track {
    grid-template-columns: 1fr;
    row-gap: 32px;
  }

  .founder-quote {
    font-size: 1.2rem;
  }
}
