/* ============================================================
   Laara Digital — Site stylesheet
   Built on top of brand/laara-brand-tokens.css — every value
   below pulls from --laara-* tokens. No one-off hex/px values.
   ============================================================ */

* { box-sizing: border-box; }
img { max-width: 100%; display: block; }
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

.skip-link {
  position: absolute;
  left: var(--laara-space-4);
  top: -100px;
  background: var(--laara-ink);
  color: var(--laara-paper);
  padding: var(--laara-space-3) var(--laara-space-4);
  border-radius: var(--laara-radius);
  z-index: 100;
  transition: top var(--laara-dur-micro) var(--laara-ease-out);
}
.skip-link:focus { top: var(--laara-space-4); }

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

/* ============================================================
   UNIVERSAL CARD SYSTEM
   Cards carrying .laara-card--wash share one hover language:
   background washes to brand green, text/icons flip to ink for
   contrast, subtle lift, CTA arrow nudges right.

   This is opt-IN. It used to be opt-OUT — a
   .laara-card:not(.work-card):not(.glow-card)... chain repeated
   31 times, so every new card variant meant editing 31 selectors
   and hoping none were missed. The variants that used to be
   excluded (glow-card, quote-form-card, enquiry-form-card,
   wizard-summary-card, wizard-success) simply don't get the class;
   .work-card was in that chain but is not a .laara-card at all,
   so it was already doing nothing.

   Adding a new card? It gets no hover wash unless you add
   .laara-card--wash. That is the intended default.

   The selectors below are written .laara-card.laara-card--wash,
   doubled on purpose. Most of these cards are also .reveal, and
   `.reveal.is-visible { transform: translateY(0) }` further down
   this file is (0,2,0). A single-class .laara-card--wash:hover is
   also (0,2,0), so it would tie and lose on source order: the
   hover lift would silently stop working on every revealed card
   while the colour wash carried on, which is exactly the kind of
   half-broken state a screenshot at rest never shows. Doubling
   makes these (0,3,0), so they win on their own merit instead of
   depending on where they sit in the file. Don't "simplify" it back.
   ============================================================ */

.laara-card.laara-card--wash:hover,
.laara-card.laara-card--wash:focus-within {
  background: var(--laara-accent);
  color: var(--laara-on-accent);
  text-decoration: none;
  transform: translateY(-4px) scale(1.01);
  box-shadow: var(--laara-shadow-lg);
}
.laara-card.laara-card--wash:active { transform: translateY(-1px) scale(1); }

.laara-card.laara-card--wash:hover h3,
.laara-card.laara-card--wash:focus-within h3,
.laara-card.laara-card--wash:hover p,
.laara-card.laara-card--wash:focus-within p,
.laara-card.laara-card--wash:hover .pricing-price,
.laara-card.laara-card--wash:focus-within .pricing-price,
.laara-card.laara-card--wash:hover .pricing-price span,
.laara-card.laara-card--wash:focus-within .pricing-price span,
.laara-card.laara-card--wash:hover .card-link,
.laara-card.laara-card--wash:focus-within .card-link,
.laara-card.laara-card--wash:hover .laara-list li,
.laara-card.laara-card--wash:focus-within .laara-list li {
  color: var(--laara-on-accent);
}
.laara-card.laara-card--wash:hover .laara-list li::before,
.laara-card.laara-card--wash:focus-within .laara-list li::before {
  background: var(--laara-on-accent);
}
.laara-card.laara-card--wash:hover .laara-dot,
.laara-card.laara-card--wash:focus-within .laara-dot {
  background: var(--laara-on-accent);
}
.laara-card.laara-card--wash:hover .laara-dot svg,
.laara-card.laara-card--wash:focus-within .laara-dot svg {
  color: var(--laara-green);
}
.laara-card.laara-card--wash:hover .pricing-card-badge,
.laara-card.laara-card--wash:focus-within .pricing-card-badge {
  background: var(--laara-on-accent);
  color: var(--laara-green);
}

.laara-card .card-link,
.laara-card .laara-btn {
  display: inline-flex;
  align-items: center;
  margin-top: auto;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out);
}
.laara-card.laara-card--wash:hover .card-link,
.laara-card.laara-card--wash:focus-within .card-link {
  transform: translateX(4px);
}

@media (prefers-reduced-motion: reduce) {
  .laara-card.laara-card--wash:hover,
  .laara-card.laara-card--wash:focus-within,
  .laara-card.laara-card--wash:hover .card-link,
  .laara-card.laara-card--wash:focus-within .card-link {
    transform: none;
  }
}

@media (hover: none) {
  .laara-card.laara-card--wash:active {
    background: var(--laara-accent);
    color: var(--laara-on-accent);
    transform: scale(0.99);
    transition-duration: var(--laara-dur-micro);
  }
}

/* Adds a transform transition to primary CTA buttons so the JS magnetic-pull
   effect (main.js) eases smoothly instead of jumping between mousemove ticks. */
.laara-btn--primary {
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              color var(--laara-dur-micro) var(--laara-ease-in-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}

/* ============================================================
   HEADER
   ============================================================ */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--laara-bg-alt);
  border-bottom: 1.5px solid var(--laara-border);
  transition: transform var(--laara-dur-section) var(--laara-ease-in-out);
}
.site-header.header--hidden { transform: translateY(-100%); }

/* The logo link wraps an <img> capped at 40px tall, so the anchor itself was
   a 40px target — 4px under the 44px minimum, on every page. Grow the link's
   hit area, not the mark: the logo keeps its tier-2 sizing and the extra
   height is padding around it. */
.site-header .laara-container > a:first-child {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
}

.site-header .laara-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--laara-space-4);
  height: 100%;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: var(--laara-space-2);
}

.icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--laara-touch-min);
  height: var(--laara-touch-min);
  border-radius: var(--laara-radius);
  color: var(--laara-ink);
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out);
}
.icon-link:hover,
.icon-link:focus-visible { background: var(--laara-surface); }
.icon-link svg { width: 22px; height: 22px; }

.main-nav {
  position: relative;
  display: none;
  gap: var(--laara-space-6);
}
.main-nav a {
  position: relative;
  z-index: 1;
  color: var(--laara-text);
  text-decoration: none;
  font-weight: var(--laara-fw-semibold);
}
.main-nav a:hover,
.main-nav a:focus-visible { color: var(--laara-green-text); }

.nav-toggle {
  width: var(--laara-touch-min);
  height: var(--laara-touch-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--laara-ink);
}
.nav-toggle svg { width: 24px; height: 24px; flex-shrink: 0; }

.header-cta { display: none; }

@media (max-width: 899px) {
  .main-nav--open {
    display: flex;
    flex-direction: column;
    gap: var(--laara-space-1);
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--laara-bg-alt);
    border-bottom: 1.5px solid var(--laara-border);
    padding: var(--laara-space-4) var(--laara-page-x) var(--laara-space-6);
  }
  .main-nav--open a {
    padding: var(--laara-space-3) 0;
    min-height: var(--laara-touch-min);
    display: flex;
    align-items: center;
  }
}

/* Desktop header always shows the full nav inline — the hamburger is
   mobile-only and gets replaced by a converting CTA instead. This block
   must come after the unconditional .nav-toggle rule above: same
   specificity, so source order decides, and the override needs to win. */
@media (min-width: 900px) {
  .main-nav {
    display: flex;
    align-items: center;
    gap: var(--laara-space-1);
  }
  .nav-toggle { display: none; }
  .header-cta { display: inline-flex; }

  /* Sliding pill: text stays permanently white and blend-inverts against
     whatever sits behind it, so it reads dark over paper and light over the
     ink capsule (.nav-indicator) without any hover colour-swap logic.
     Selectors are prefixed with .main-nav (not just the bare class) so this
     beats the later, unscoped base .nav-dropdown-toggle rule on specificity
     rather than relying on source order. Child combinator (> a), not a bare
     descendant selector, so this targets only the top-level nav items —
     .nav-dropdown-link anchors inside the flyout panel sit two levels
     deeper and must stay untouched: mix-blend-mode flattens all descendant
     content before blending, so applying it there differenced their ink
     text against the panel's white background and washed it out to grey. */
  .main-nav > a,
  .main-nav .nav-dropdown-toggle {
    display: inline-flex;
    align-items: center;
    min-height: var(--laara-touch-min);
    padding: var(--laara-space-2) var(--laara-space-4);
    border-radius: 999px;
    color: var(--laara-white);
    mix-blend-mode: difference;
  }
  .main-nav > a:hover,
  .main-nav > a:focus-visible,
  .main-nav .nav-dropdown-toggle:hover,
  .main-nav .nav-dropdown-toggle:focus-visible,
  .main-nav .nav-dropdown.is-open .nav-dropdown-toggle {
    color: var(--laara-white);
  }
}

/* ---------- Services nav dropdown ----------
   Mobile-first as a disclosure inside .main-nav--open (grid-template-rows
   0fr -> 1fr collapse, same technique as the wizard's conditional fields);
   overridden at desktop to a floating panel under the toggle. */
.nav-dropdown { position: relative; display: flex; flex-direction: column; }

.nav-dropdown-toggle {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-1);
  background: none;
  border: none;
  padding: 0;
  font-family: var(--laara-font);
  font-size: var(--laara-fs-body);
  color: var(--laara-text);
  font-weight: var(--laara-fw-semibold);
  cursor: pointer;
}
@media (max-width: 899px) {
  .nav-dropdown-toggle:hover,
  .nav-dropdown-toggle:focus-visible,
  .nav-dropdown.is-open .nav-dropdown-toggle { color: var(--laara-green-text); }
}

.nav-dropdown-chevron {
  width: 16px;
  height: 16px;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out);
}
.nav-dropdown.is-open .nav-dropdown-chevron { transform: rotate(180deg); }

.nav-dropdown-panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  overflow: hidden;
  transition: grid-template-rows var(--laara-dur-section) var(--laara-ease-out),
              opacity var(--laara-dur-reveal) var(--laara-ease-out);
}
.nav-dropdown-panel-inner { min-height: 0; }
.nav-dropdown.is-open .nav-dropdown-panel { grid-template-rows: 1fr; opacity: 1; }

.nav-dropdown-link {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--laara-space-3) 0 var(--laara-space-3) var(--laara-space-4);
  min-height: var(--laara-touch-min);
  justify-content: center;
  text-decoration: none;
  border-radius: var(--laara-radius);
}
.nav-dropdown-link-title { font-weight: var(--laara-fw-bold); color: var(--laara-text); }
.nav-dropdown-link-desc { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
.nav-dropdown-link:hover,
.nav-dropdown-link:focus-visible { text-decoration: none; background: var(--laara-surface); }
.nav-dropdown-link:hover .nav-dropdown-link-title,
.nav-dropdown-link:focus-visible .nav-dropdown-link-title { color: var(--laara-green-text); }

@media (max-width: 899px) {
  .main-nav--open .nav-dropdown-toggle {
    width: 100%;
    justify-content: space-between;
    padding: var(--laara-space-3) 0;
    min-height: var(--laara-touch-min);
  }
}

@media (min-width: 900px) {
  .nav-dropdown-panel {
    position: absolute;
    top: calc(100% + 14px);
    left: 50%;
    display: block;
    grid-template-rows: none;
    width: 320px;
    background: var(--laara-white);
    border: 1.5px solid var(--laara-border);
    border-radius: var(--laara-radius-lg);
    box-shadow: var(--laara-shadow-lg);
    padding: var(--laara-space-2);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    overflow: visible;
    transform: translateX(-50%) translateY(4px);
    transition: opacity var(--laara-dur-micro) var(--laara-ease-out),
                transform var(--laara-dur-micro) var(--laara-ease-out),
                visibility 0s linear var(--laara-dur-micro);
    z-index: 5;
  }
  .nav-dropdown.is-open .nav-dropdown-panel {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateX(-50%) translateY(0);
    transition-delay: 0s;
  }
  .nav-dropdown-link { padding-left: var(--laara-space-4); }
}

/* ============================================================
   HERO
   ============================================================ */

.hero {
  position: relative;
  background: var(--laara-bg);
  color: var(--laara-text);
  overflow: hidden;
  /* Fluid top padding so the hero gets real breathing room on large screens
     (was a fixed 96px, too tight to let the display type dominate the fold
     on 1440px+) while staying compact on small phones. Bottom padding reuses
     the same --laara-section-y every other section boundary uses, so the
     seam into Trust Strip keeps the page's established vertical rhythm. */
  padding-block: clamp(3.5rem, 2rem + 7vw, 7rem) var(--laara-section-y);
}
/* Hairline fade at the foot of the hero — a deliberate, understated seam
   into Trust Strip rather than the two near-identical light backgrounds
   butting together with no boundary at all. */
.hero::after {
  content: '';
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--laara-border) 50%, transparent);
}
.hero-fluid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Base gradient shows instantly and stays as the fallback if WebGL never
     initialises (reduced motion, no WebGL, save-data). --hero-glow is a
     space-separated "R G B" triple set per page (see page-hero accent
     classes below); unset on the homepage, so it falls back to pink. */
  background: radial-gradient(120% 90% at 78% 12%, rgb(var(--hero-glow, var(--laara-hero-pink)) / 0.16) 0%, transparent 60%),
              radial-gradient(100% 80% at 15% 85%, rgb(var(--hero-glow, var(--laara-hero-pink)) / 0.10) 0%, transparent 65%),
              var(--laara-bg);
}
.hero-content {
  position: relative;
  z-index: 1;
  /* Wide enough that the display-weight H1 wraps to 3 lines rather than 5 at
     desktop widths — a narrower container here pushed hero content past the
     viewport height and under the fixed .consent-banner on first visit. */
  max-width: 52rem;
}

/* ---------- Fluid hero on inner pages (Services/Work/Pricing/About/Contact) ----------
   Modifier applied alongside .page-hero, not a replacement for it — bare
   .page-hero (used by the other 11 pages) and .laara-inverse (final-CTA
   bands, stat band) are untouched. Flips that one hero to light so the
   fluid + white-text reveal reads the same as the homepage, replaces the
   green glow blob with the fluid canvas, and centers the shorter inner-page
   copy to match the centered oversized background word. */
.page-hero.page-hero--fluid {
  background: var(--laara-bg);
  color: var(--laara-text);
}
.page-hero.page-hero--fluid::before { display: none; }
.page-hero.page-hero--fluid h1 { color: var(--laara-text); }
.page-hero.page-hero--fluid p { color: var(--laara-text-secondary); }
.page-hero.page-hero--fluid > .laara-container {
  text-align: center;
  max-width: 40rem;
  margin-inline: auto;
}
.page-hero.page-hero--fluid .hero-actions { justify-content: center; }

.page-hero--services { --hero-glow: var(--laara-hero-blue); }
.page-hero--work { --hero-glow: var(--laara-hero-amber); }
.page-hero--pricing { --hero-glow: var(--laara-hero-teal); }
.page-hero--about { --hero-glow: var(--laara-hero-coral); }
.page-hero--contact { --hero-glow: var(--laara-green-rgb); }  /* the brand green itself */

/* ---------- Oversized fluid-revealed background typography ----------
   Sits above the fluid canvas, below the hero content, coloured the same
   as the canvas's idle backdrop (paper). Against the flat idle canvas it
   has ~1:1 contrast and reads as almost nothing; where the saturated fluid
   passes beneath it the same white glyphs suddenly read clearly — the
   reveal is just natural contrast against a variable-colour backdrop, no
   blend-mode trickery needed. Decorative only: aria-hidden + pointer-events
   none, never competes with the real h1. */
.hero-type {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  pointer-events: none;
}
.hero-type span {
  font-family: var(--laara-font);
  font-weight: var(--laara-fw-black);
  font-size: clamp(4.5rem, 1rem + 15vw, 13rem);
  line-height: 1;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--laara-paper);
  user-select: none;
}
@media (max-width: 640px) {
  .hero-type span {
    font-size: clamp(3.25rem, 1rem + 20vw, 6rem);
    letter-spacing: -0.02em;
  }
}

/* ---------- Ambient background motion (dark sections) ---------- */
.page-hero::before,
.laara-inverse::before {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
  z-index: 0;
}
.page-hero::before {
  width: 26rem;
  height: 26rem;
  top: -8rem;
  right: -6rem;
  background: radial-gradient(circle, var(--laara-green) 0%, transparent 70%);
  opacity: 0.22;
  animation: drift-a 34s ease-in-out infinite;
}
.laara-inverse::before {
  width: 24rem;
  height: 24rem;
  top: -7rem;
  left: -5rem;
  background: radial-gradient(circle, var(--laara-green) 0%, transparent 70%);
  opacity: 0.16;
  animation: drift-a 38s ease-in-out infinite;
}
@keyframes drift-a {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(2.5rem, 1.75rem); }
}
@media (prefers-reduced-motion: reduce) {
  .page-hero::before, .laara-inverse::before { animation: none; }
}
.hero-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--laara-space-3);
  margin-bottom: var(--laara-space-6);
}
.hero-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-2);
  padding: var(--laara-space-2) var(--laara-space-4);
  border-radius: 999px;
  background: rgb(var(--laara-ink-rgb) / 0.05);
  border: 1.5px solid rgb(var(--laara-ink-rgb) / 0.12);
  color: var(--laara-ink);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
}
.hero-content h1 { color: var(--laara-text); margin: 0 0 var(--laara-space-6); }
.hero-sub {
  font-size: var(--laara-fs-body-lg);
  color: var(--laara-text-secondary);
  margin: 0 0 var(--laara-space-8);
  max-width: 34rem;
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--laara-space-4);
  margin-bottom: var(--laara-space-6);
}
.hero-trust {
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  color: var(--laara-text-secondary);
  letter-spacing: 0.02em;
}
@media (max-width: 480px) {
  /* Full-width stacked CTAs read as more deliberate and are easier to hit
     one-handed than two half-width buttons squeezed onto a small phone. */
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .laara-btn { width: 100%; }
}

/* ============================================================
   TRUST STRIP
   ============================================================ */

.trust-strip { background: var(--laara-surface); }

/* ---------- Marquee ---------- */
.marquee {
  display: flex;
  overflow: hidden;
  padding-block: var(--laara-space-8);
}
.marquee-track {
  display: flex;
  align-items: center;
  gap: var(--laara-space-6);
  list-style: none;
  padding: 0;
  margin: 0;
  width: max-content;
  animation: marquee 32s linear infinite;
}
.marquee-track + .marquee-track { margin-left: var(--laara-space-6); }
@keyframes marquee {
  to { transform: translateX(calc(-100% - var(--laara-space-6))); }
}
.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }
.marquee-track li {
  display: flex;
  white-space: nowrap;
  flex-shrink: 0;
}
.marquee-track li a {
  display: flex;
  align-items: center;
  gap: var(--laara-space-3);
  text-decoration: none;
  padding: var(--laara-space-3) var(--laara-space-4);
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius);
  box-shadow: var(--laara-shadow-sm);
  transition: color var(--laara-dur-micro) var(--laara-ease-in-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out),
              transform var(--laara-dur-micro) var(--laara-ease-out),
              box-shadow var(--laara-dur-micro) var(--laara-ease-out);
}
.marquee-track li a:hover,
.marquee-track li a:focus-visible {
  color: var(--laara-green-text);
  border-color: var(--laara-green);
  box-shadow: var(--laara-shadow);
  transform: translateY(-2px);
}
.marquee-track li a:active { transform: translateY(0); box-shadow: var(--laara-shadow-sm); }
.marquee-track li a:focus-visible { outline: 2px solid var(--laara-ink); outline-offset: 2px; }
.marquee-track svg { width: 26px; height: 26px; color: var(--laara-green-deep); flex-shrink: 0; }
.marquee-track span { font-size: var(--laara-fs-body-lg); font-weight: var(--laara-fw-semibold); color: var(--laara-text); }
.marquee-track li a:hover span,
.marquee-track li a:focus-visible span { color: inherit; }

@media (prefers-reduced-motion: reduce) {
  .marquee { overflow: visible; }
  .marquee-track {
    animation: none;
    flex-wrap: wrap;
    width: 100%;
    gap: var(--laara-space-6);
  }
  .marquee-track + .marquee-track { display: none; }
}

/* ============================================================
   SECTION HEADINGS
   ============================================================ */

.section-alt { background: var(--laara-surface); }

.section-head {
  max-width: 42rem;
  margin: 0 0 var(--laara-space-12);
}
.section-head h2 { margin: 0 0 var(--laara-space-3); }
.section-head p { color: var(--laara-text-secondary); font-size: var(--laara-fs-body-lg); margin: 0; }
.section-eyebrow {
  display: block;
  margin-bottom: var(--laara-space-3);
}

/* ============================================================
   SERVICES
   ============================================================ */

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
}
@media (min-width: 640px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
}
/* ---------- Services bento: one dominant tile, four smaller ones ----------
   Flagship becomes a full-width banner from tablet width up (icon + heading
   side by side), then at 1080px+ the grid gains a 4th column and flagship
   spans 2x2 — the other four cards auto-flow into the remaining 2x2 block
   beside it, giving genuine asymmetry instead of one wide card sitting
   above a uniform row. */
@media (min-width: 640px) {
  .card-grid--5 .service-card--flagship {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: start;
    column-gap: var(--laara-space-6);
  }
  .card-grid--5 .service-card--flagship .laara-dot { grid-row: 1; margin-bottom: 0; width: 3.5rem; height: 3.5rem; }
  .card-grid--5 .service-card--flagship .laara-dot svg { width: 28px; height: 28px; }
  .card-grid--5 .service-card--flagship h3 { font-size: var(--laara-fs-h2); grid-column: 2; }
  .card-grid--5 .service-card--flagship p { grid-column: 2; font-size: var(--laara-fs-body-lg); }
  .card-grid--5 .service-card--flagship .card-link { grid-column: 2; }
}
@media (min-width: 1080px) {
  .card-grid--5 { grid-template-columns: repeat(4, 1fr); }
  .card-grid--5 .service-card--flagship {
    grid-column: span 2;
    grid-row: span 2;
    background: var(--laara-ink);
    border-color: rgb(var(--laara-paper-rgb) / 0.12);
  }
  .card-grid--5 .service-card--flagship h3 { color: var(--laara-paper); }
  .card-grid--5 .service-card--flagship p { color: var(--laara-on-dark-muted); }
  .card-grid--5 .service-card--flagship .card-link { color: var(--laara-link-on-dark); }
  .card-grid--5 .service-card--flagship .laara-dot { background: rgb(var(--laara-paper-rgb) / 0.1); }
}
@media (hover: hover) and (pointer: fine) and (min-width: 1080px) {
  .card-grid--5 .service-card--flagship:hover h3,
  .card-grid--5 .service-card--flagship:focus-within h3 {
    /* The generic glow-card hover heading colour (--laara-green-text) is
       tuned for text-on-light contrast; on the flagship's ink background
       that pairing is wrong, so it swaps to the on-ink-safe --laara-green. */
    color: var(--laara-green);
  }
}

.service-card .laara-dot,
.sector-card .laara-dot { margin-bottom: var(--laara-space-4); width: 2.5rem; height: 2.5rem; border-radius: var(--laara-radius); display: flex; align-items: center; justify-content: center; background: var(--laara-ink); }
.service-card .laara-dot svg,
.sector-card .laara-dot svg { width: 22px; height: 22px; color: var(--laara-green); }
.service-card h3,
.sector-card h3 { margin: 0 0 var(--laara-space-2); }
.service-card p,
.sector-card p { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-4); font-size: var(--laara-fs-body); flex-grow: 1; }
.service-card .card-link,
.sector-card .card-link { color: var(--laara-link); font-weight: var(--laara-fw-semibold); font-size: var(--laara-fs-small); }

/* ---------- Sector cards (About: "Who we work with") ----------
   Deliberately the same component as the services grid rather than a
   second card system: .laara-card + .glow-card means the hover lift,
   pointer-tracked glow, heading colour shift, icon-tile ring and
   card-link nudge all come from the shared rules above, and the icon
   tile shares one definition with .service-card. Only two things are
   new — the three-across desktop grid, and the "who this covers"
   line, which is pushed to the bottom of the card above a hairline so
   it reads as a footnote rather than a second identical paragraph.
   The pitch keeps flex-grow: 1 from .sector-card p, so that hairline
   and the link land on the same baseline across every card in a row. */
@media (min-width: 1024px) {
  .card-grid--3 { grid-template-columns: repeat(3, 1fr); }
}
.sector-card .sector-card-who {
  flex-grow: 0;
  margin: 0 0 var(--laara-space-4);
  padding-top: var(--laara-space-4);
  border-top: 1.5px solid var(--laara-border);
  color: var(--laara-text-secondary);
}
/* Two columns up, the trade lists wrap to different line counts, which put
   the hairline at a different height on each card in a row even though the
   links below them lined up. Reserving two lines pins every hairline to the
   same baseline. Measured, not guessed: the longest list wraps to two lines
   at the narrowest multi-column card width (293px at 1024). */
@media (min-width: 640px) {
  /* Box-sizing is border-box, so the reserved height has to include this
     element's own padding and rule, or it only buys 1.3 lines of text. */
  .sector-card .sector-card-who { min-height: calc(2em * var(--laara-lh-body) + var(--laara-space-4) + 1.5px); }
}

/* ============================================================
   GLOW CARDS — cursor-reactive glow + icon micro-interactions
   Shared by every icon-led card on the site: the services grid
   (.service-card), the plain differentiator cards on the homepage
   and about page (.laara-card--plain), and the contact method
   cards (.contact-method) — add "glow-card" alongside their own
   class to opt in. These simply never carry .laara-card--wash, so
   they sit outside the universal green-wash system above, in favour of
   a quieter, premium treatment: soft pointer-tracked glow, a
   brand-tinted border/elevation, and a small per-icon animation
   that means something for that icon. Pricing cards deliberately
   keep the bold green-wash instead — that's a "pick me" moment,
   not a browse-and-compare one. Full glow effect is fine-pointer/
   hover only (see the (hover: hover) query below) — touch devices
   keep the default card look plus a light tap state.
   ============================================================ */

/* The three transform rules below are written .glow-card.glow-card on
   purpose, for the same reason .laara-card--wash is doubled at the top of
   this file: these cards are almost always .reveal too, and
   `.reveal.is-visible { transform: translateY(0) }` further down is (0,2,0).
   A single-class .glow-card:hover ties it and loses on source order, so the
   lift silently stopped working on every revealed card while the glow and
   border tint carried on — invisible in a screenshot at rest. Don't
   "simplify" it back. */
.glow-card {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--laara-dur-reveal) var(--laara-ease-out),
              border-color var(--laara-dur-section) var(--laara-ease-out),
              box-shadow var(--laara-dur-section) var(--laara-ease-out);
}

/* Cursor-tracked radial glow. --mx/--my are written by main.js on
   pointermove (rAF-throttled); they default to the card centre so
   keyboard focus still shows a centred glow with no JS coordinate. */
.glow-card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: radial-gradient(260px circle at var(--mx, 50%) var(--my, 50%),
              rgb(var(--laara-green-rgb) / 0.11), transparent 60%);
  opacity: 0;
  transition: opacity var(--laara-dur-section) var(--laara-ease-out);
  pointer-events: none;
}
.glow-card > * { position: relative; z-index: 1; }

@media (hover: hover) and (pointer: fine) {
  .glow-card.glow-card:hover,
  .glow-card.glow-card:focus-within {
    transform: translateY(-2px);
    border-color: rgb(var(--laara-green-rgb) / 0.4);
    box-shadow: var(--laara-shadow), 0 20px 44px -26px rgb(var(--laara-green-rgb) / 0.45);
    text-decoration: none;
  }
  .glow-card:hover::before,
  .glow-card:focus-within::before {
    opacity: 1;
  }
  .glow-card:hover h3,
  .glow-card:focus-within h3 {
    color: var(--laara-green-text);
    transition: color var(--laara-dur-section) var(--laara-ease-out);
  }
  .glow-card:hover .card-link,
  .glow-card:focus-within .card-link {
    transform: translateX(4px);
  }
  .glow-card .laara-dot,
  .glow-card .contact-method-icon {
    transition: box-shadow var(--laara-dur-section) var(--laara-ease-out),
                transform var(--laara-dur-section) var(--laara-ease-out);
  }
  .glow-card:hover .laara-dot,
  .glow-card:focus-within .laara-dot,
  .glow-card:hover .contact-method-icon,
  .glow-card:focus-within .contact-method-icon {
    box-shadow: 0 0 0 6px rgb(var(--laara-green-rgb) / 0.10);
    transform: translateY(-1px);
  }
}

/* Light, honest tap feedback on touch — no lingering fake hover. */
@media (hover: none) {
  .glow-card.glow-card:active {
    transform: scale(0.99);
    border-color: rgb(var(--laara-green-rgb) / 0.35);
    transition-duration: var(--laara-dur-micro);
  }
}

@media (prefers-reduced-motion: reduce) {
  .glow-card.glow-card:hover,
  .glow-card.glow-card:focus-within {
    transform: none;
  }
  .glow-card .laara-dot,
  .glow-card .contact-method-icon,
  .glow-card:hover .laara-dot,
  .glow-card:focus-within .laara-dot,
  .glow-card:hover .contact-method-icon,
  .glow-card:focus-within .contact-method-icon {
    transform: none;
  }
}

/* ---------- Per-icon micro-animations ----------
   Each is a small element the base icon doesn't have at rest
   (a checkmark, a ping, a pulse line) that draws in on hover and
   reverses smoothly on leave — nothing loops, nothing is added
   for icons that don't have a natural motion to borrow. */

/* 1. Website design & build — page content "typing in" */
.icon-build-line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 600ms var(--laara-ease-out);
}
.icon-build-line--2 { transition-delay: var(--laara-delay-stagger); }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-build-line,
  .glow-card:focus-within .icon-build-line {
    stroke-dashoffset: 0;
  }
}

/* 2. Local SEO & Google visibility — signal ping from the pin */
.icon-ping {
  transform-origin: 12px 10px;
  transform: scale(0.7);
  opacity: 0;
  transition: transform var(--laara-dur-draw) var(--laara-ease-out), opacity var(--laara-dur-draw) var(--laara-ease-out);
}
.icon-ping-2 { transition-delay: 140ms; }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-ping-1,
  .glow-card:focus-within .icon-ping-1 {
    transform: scale(1.9);
    opacity: 0;
    animation: none;
  }
  .glow-card:hover .icon-ping-1 { opacity: 0.55; }
  .glow-card:hover .icon-ping-2,
  .glow-card:focus-within .icon-ping-2 {
    transform: scale(2.4);
    opacity: 0;
  }
  .glow-card:hover .icon-pin-shape,
  .glow-card:focus-within .icon-pin-shape {
    transform: translateY(-1px);
    transition: transform 500ms var(--laara-ease-out);
  }
}
.icon-pin-shape { transition: transform 500ms var(--laara-ease-out); }

/* 3. Booking & enquiry systems — a booking gets ticked off */
.icon-check {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 620ms var(--laara-ease-out) 80ms;
}
.icon-ring { transition: transform var(--laara-dur-icon) var(--laara-ease-out); }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-check,
  .glow-card:focus-within .icon-check {
    stroke-dashoffset: 0;
  }
  .glow-card:hover .icon-ring,
  .glow-card:focus-within .icon-ring {
    transform: translateY(-1px);
  }
}

/* 4. Ongoing care & support — a quiet heartbeat, shield breathes */
.icon-pulse {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset var(--laara-dur-draw) var(--laara-ease-out) 60ms;
}
.icon-shield-shape {
  transform-origin: 12px 12px;
  transition: transform var(--laara-dur-icon-slow) var(--laara-ease-out);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-pulse,
  .glow-card:focus-within .icon-pulse {
    stroke-dashoffset: 0;
  }
  .glow-card:hover .icon-shield-shape,
  .glow-card:focus-within .icon-shield-shape {
    transform: scale(1.035);
  }
}

/* 5. Growth & paid ads — data points land on the trend line in sequence.
   The line itself is the icon's original shape, so (unlike the bonus
   elements on the other four icons) it stays fully visible at rest —
   only the marker dots are new and hidden until hover. */
.icon-trend-dot {
  opacity: 0;
  transform: scale(0.3);
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--laara-dur-icon) var(--laara-ease-out), opacity var(--laara-dur-icon) var(--laara-ease-out);
}
.icon-trend-dot-1 { transition-delay: 100ms; }
.icon-trend-dot-2 { transition-delay: 280ms; }
.icon-trend-dot-3 { transition-delay: 460ms; }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-trend-line,
  .glow-card:focus-within .icon-trend-line {
    transform: translateY(-1px);
    transition: transform 500ms var(--laara-ease-out);
  }
  .glow-card:hover .icon-trend-dot,
  .glow-card:focus-within .icon-trend-dot {
    opacity: 1;
    transform: scale(1);
  }
}
.icon-trend-line {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 500ms var(--laara-ease-out);
}

/* 6. Fast delivery / 7–10 days — bolt releases a spark on landing */
.icon-spark {
  opacity: 0;
  transform: scale(0.3);
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--laara-dur-icon-mid) var(--laara-ease-out), opacity var(--laara-dur-icon-mid) var(--laara-ease-out);
}
.icon-bolt-shape { transition: transform var(--laara-dur-icon) var(--laara-ease-out); }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-spark,
  .glow-card:focus-within .icon-spark {
    opacity: 1;
    transform: scale(1);
    transition-delay: 260ms;
  }
  .glow-card:hover .icon-bolt-shape,
  .glow-card:focus-within .icon-bolt-shape {
    transform: translateY(-1px);
  }
}

/* 7. Industry expertise / marketing-led design — clock hand ticks forward */
.icon-clock-hand {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  transition: transform var(--laara-dur-icon-slow) var(--laara-ease-out);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-clock-hand,
  .glow-card:focus-within .icon-clock-hand {
    transform: rotate(35deg);
  }
}

/* 8. Talk to the person building it — a presence dot confirms someone's there */
.icon-presence {
  opacity: 0;
  transform: scale(0.4);
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--laara-dur-icon-mid) var(--laara-ease-out), opacity var(--laara-dur-icon-mid) var(--laara-ease-out);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-presence,
  .glow-card:focus-within .icon-presence {
    opacity: 1;
    transform: scale(1);
  }
}

/* 9. Call us — the handset gives a small, restrained ring wiggle */
.icon-phone-shape { transform-box: fill-box; transform-origin: 60% 15%; }
@keyframes icon-phone-ring {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(-12deg); }
  50% { transform: rotate(9deg); }
  75% { transform: rotate(-5deg); }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-phone-shape,
  .glow-card:focus-within .icon-phone-shape {
    animation: icon-phone-ring var(--laara-dur-icon-slow) var(--laara-ease-out) 1;
  }
}

/* 10. WhatsApp us — a reply bubble lands, as if a message just came in */
.icon-chat-dot {
  opacity: 0;
  transform: scale(0.4);
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--laara-dur-icon) var(--laara-ease-out), opacity var(--laara-dur-icon) var(--laara-ease-out);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-chat-dot,
  .glow-card:focus-within .icon-chat-dot {
    opacity: 1;
    transform: scale(1);
    transition-delay: 120ms;
  }
}

/* 11. Email us — the envelope flap opens and a sent tick draws in */
.icon-mail-flap {
  transform-box: fill-box;
  transform-origin: 50% 0%;
  transition: transform var(--laara-dur-icon) var(--laara-ease-out);
}
.icon-mail-tick {
  opacity: 0;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset var(--laara-dur-icon-mid) var(--laara-ease-out) 200ms,
              opacity 1ms linear 200ms;
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-mail-tick,
  .glow-card:focus-within .icon-mail-tick {
    opacity: 1;
    stroke-dashoffset: 0;
  }
}

/* 12. Fixed pricing stat — the tag tilts as if just handed over, then locks in with a tick */
.icon-tag-shape {
  transform-box: fill-box;
  transform-origin: 30% 70%;
  transition: transform var(--laara-dur-icon) var(--laara-ease-out);
}
.icon-tag-check {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset var(--laara-dur-icon-slow) var(--laara-ease-out) 120ms;
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .glow-card:hover .icon-tag-shape,
  .glow-card:focus-within .icon-tag-shape {
    transform: rotate(-8deg);
  }
  .glow-card:hover .icon-tag-check,
  .glow-card:focus-within .icon-tag-check {
    stroke-dashoffset: 0;
  }
}

/* ---------- Generic/differentiator cards (no CTA) ---------- */
.laara-card--plain { gap: var(--laara-space-2); }
/* The site's icon tile — an ink square holding the accent-coloured icon.
   about.html's founder credential cards share it by selector rather than
   by copying the numbers, so the tile keeps one definition. The stacking
   margin stays on the plain cards only: .founder-cred spaces its own
   children with gap, because it flips between a row and a column. */
.laara-card--plain .laara-dot,
.founder-cred .laara-dot { width: 2.5rem; height: 2.5rem; border-radius: var(--laara-radius); display: flex; align-items: center; justify-content: center; background: var(--laara-ink); }
.laara-card--plain .laara-dot { margin-bottom: var(--laara-space-4); }
.laara-card--plain .laara-dot svg,
.founder-cred .laara-dot svg { width: 22px; height: 22px; color: var(--laara-green); }
.laara-card--plain h3 { margin: 0 0 var(--laara-space-2); }
.laara-card--plain p { margin: 0; color: var(--laara-text-secondary); }

/* ============================================================
   WHY LAARA — editorial layout (homepage only)
   Sits on top of .laara-card--plain/.glow-card entirely unchanged
   (still shared with about.html) — this only adds a numeral and a
   two-column split wrapper, so nothing here touches other pages.
   ============================================================ */
.why-laara-layout {
  display: grid;
  gap: var(--laara-space-10);
}
@media (min-width: 1080px) {
  .why-laara-layout {
    grid-template-columns: minmax(260px, 360px) 1fr;
    align-items: start;
    gap: var(--laara-space-16);
  }
}

/* Two nodes on purpose, mirroring .founder-media/.founder-portrait-frame on
   about.html: the OUTER .why-laara-visual is the sticky wrapper and
   carries no sizing or transform, the INNER -frame carries the box
   and the .reveal-zoom entrance transform. Keeping the transform off
   the sticky element is the whole reason for the split — it is the
   combination the founder pattern already proves works. */
@media (min-width: 1080px) {
  .why-laara-visual {
    position: sticky;
    top: calc(var(--laara-header-h-desktop) + var(--laara-space-10));
  }
}

/* The gradient is the image's load-in backdrop (same brand tones, so
   there is no flash of empty ink while the file arrives) rather than
   a placeholder to delete now that the real asset is wired in. */
.why-laara-visual-frame {
  position: relative;
  border-radius: var(--laara-radius-card);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(120% 140% at 12% 0%, rgb(var(--laara-green-rgb) / 0.38), transparent 55%),
    radial-gradient(90% 110% at 100% 100%, rgb(var(--laara-green-rgb) / 0.16), transparent 60%),
    var(--laara-ink);
  box-shadow: var(--laara-shadow-lg);
}
@media (min-width: 1080px) {
  .why-laara-visual-frame { aspect-ratio: 3 / 4; }
}
.why-laara-visual-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.why-laara-list {
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-6);
}

.why-laara-row { gap: var(--laara-space-6); }
@media (min-width: 768px) {
  .why-laara-row { flex-direction: row; align-items: flex-start; }
  /* Alternating composition: the numeral hops sides row to row —
     copy itself stays left-aligned throughout, so body text never
     reads right-to-left ragged. */
  .why-laara-list > .why-laara-row:nth-child(even) { flex-direction: row-reverse; }
}

.why-laara-num {
  display: block;
  flex-shrink: 0;
  margin-top: -0.09em;
  color: var(--laara-muted-grey);
  font-family: var(--laara-font);
  font-weight: var(--laara-fw-black);
  font-size: var(--laara-fs-display);
  line-height: 1;
  letter-spacing: -0.02em;
}
@media (min-width: 768px) {
  .why-laara-num { width: 5rem; }
}

/* flex-grow is load-bearing, not tidying. Without it the body shrinks
   to its own text width, and on the row-reverse rows (02, 04) that
   leaves it floating in the middle of the row instead of flush left —
   the exact opposite of what the comment above promises. Growing it
   makes every row's text column the same width in both directions. */
.why-laara-row-body { min-width: 0; flex-grow: 1; }

/* ============================================================
   VS TABLE — Laara vs traditional agency (homepage only)
   Deliberately not named .compare-table — that class already
   belongs to pricing.html's package comparison table, with its own
   light-theme styling and data-active states; reusing it here
   silently inherited that component's white background.
   A real <table> for built-in accessibility (row/column semantics
   screen readers understand natively, no ARIA role-juggling) inside
   a dark section for a distinct beat before Pricing. The Laara
   column gets a quiet green wash to read as the "win" without ever
   pairing green with white text.
   ============================================================ */
.vs-table-wrap {
  overflow-x: auto;
  border-radius: var(--laara-radius-card);
  border: 1px solid rgb(var(--laara-paper-rgb) / 0.14);
}
.vs-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  min-width: 480px;
}
.vs-table th,
.vs-table td {
  padding: var(--laara-space-5) var(--laara-space-6);
  text-align: left;
  vertical-align: top;
  font-size: var(--laara-fs-body);
}
/* 38% for four one-or-two-word labels was dead space that squeezed the two
   columns actually carrying the argument. 26/37/37 gives it back. */
.vs-table th:first-child,
.vs-table td:first-child { width: 26%; }
.vs-table th:not(:first-child),
.vs-table td:not(:first-child) { width: 37%; }
.vs-table thead th {
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--laara-on-dark-muted);
  border-bottom: 1px solid rgb(var(--laara-paper-rgb) / 0.14);
}
/* No white-space: nowrap. At 26% "Support after launch" has to be allowed to
   wrap, and it costs nothing: row height already tracks the tallest cell (the
   Laara column's two-line notes), and mobile overrides nowrap to normal anyway. */
.vs-table tbody th {
  font-weight: var(--laara-fw-bold);
  color: var(--laara-paper);
}
.vs-table tbody td { color: var(--laara-on-dark-muted); }
.vs-table tbody tr + tr th,
.vs-table tbody tr + tr td {
  border-top: 1px solid rgb(var(--laara-paper-rgb) / 0.10);
}
/* 0.12 read as an accident rather than an answer. 0.18 plus a solid green
   left edge makes the column a panel you notice before you read it. The wash
   is still almost entirely ink underneath, so paper text on it stays ~13.8:1. */
.vs-table td.vs-col--laara,
.vs-table th.vs-col--laara {
  background: rgb(var(--laara-green-rgb) / 0.18);
  box-shadow: inset 3px 0 0 var(--laara-green);
}
.vs-table tbody td.vs-col--laara {
  color: var(--laara-paper);
  font-weight: var(--laara-fw-semibold);
}
.vs-table thead th.vs-col--laara { color: var(--laara-green); }

/* The typical-agency column reads as superseded rather than merely different,
   borrowing .about-compare-them's exact recipe instead of inventing a second
   one. Strictly presentational: no cell text changes, and no number is added
   to this column — see the honesty comment under the table in index.html. */
.vs-table td.vs-col--them {
  color: var(--laara-on-dark-subtle);
  text-decoration: line-through;
  text-decoration-color: rgb(var(--laara-paper-rgb) / 0.3);
}
/* text-decoration inherits into descendants, and below 768px this cell also
   contains its column NAME. Striking through "Typical agency" reads as the
   column being cancelled rather than the value it holds. Desktop hides the
   label so this only ever showed on mobile. */
.vs-table td.vs-col--them .vs-cell-label { text-decoration: none; }

/* Decorative. aria-hidden in the markup, because the cell text already states
   the claim in full — this must not alter any accessible name. Green on ink
   is 7.20:1, the pairing this file uses everywhere on dark. */
.vs-mark {
  margin-right: var(--laara-space-2);
  color: var(--laara-green);
  font-weight: var(--laara-fw-bold);
}

/* Price is the strongest sourced claim on the page, so it's the one row that
   gets extra weight. Weight only, never size: the mobile note below measured
   "£797–£2,497" breaking onto two lines at 360px when the column narrows, and
   900 vs 700 adds almost no width where a size step would. */
.vs-row--price .vs-price { font-weight: var(--laara-fw-black); }

/* Desktop only: drop the Laara column's internal dividers so it reads as one
   continuous panel rather than four separately-tinted cells. Left alone below
   768px, where the restack moves the divider onto the row itself.

   NOT 'transparent' — that was tried and renders a dark line. Under
   border-collapse: collapse the collapsed border paints over the TABLE
   background, not the cell's own background, so a transparent border reveals
   the ink beneath rather than the green wash. Repeating the wash as the border
   colour composites to exactly the same value over that ink, so the seam
   disappears without hardcoding the composited hex. */
@media (min-width: 768px) {
  .vs-table tbody tr + tr td.vs-col--laara,
  .vs-table tbody tr + tr th.vs-col--laara {
    border-top-color: rgb(var(--laara-green-rgb) / 0.18);
  }
}
.vs-note {
  display: block;
  margin-top: var(--laara-space-1);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-regular);
  color: var(--laara-on-dark-muted);
}

/* The column name, carried inside each cell. Hidden on desktop, where <thead>
   already does this job; shown below 768px, where <thead> is hidden and the
   rows restack. It is real DOM text rather than a ::before with attr(), so
   screen readers announce it dependably — it folds into the cell's accessible
   name, giving "Laara Digital, 7-10 days, up to 3 weeks...". */
.vs-cell-label {
  display: none;
  margin-bottom: var(--laara-space-1);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--laara-on-dark-muted);
}
.vs-col--laara .vs-cell-label { color: var(--laara-green); }

/* ---------- Row-by-row reveal ----------
   Rides the shared IntersectionObserver in main.js, which already staggers
   siblings by parent at Math.min(idx,4)*70ms. The four rows share one <tbody>,
   so they get 0/70/140/210ms for free — the only JS change needed was adding
   this class to that observer's selector.

   Not just .reveal, because of the transform. Per the CSS Transforms spec a
   display:table-row box IS transformable — only table-column and
   table-column-group are excluded — but WebKit has shipped versions that drop
   transform (and position) on <tr> regardless, and this audience is on older
   phones. So above 768px, where rows are still display:table-row, this fades
   with opacity alone; below it the restack has already made each <tr> a grid
   container, where the slide is an ordinary box transform and safe. Opacity is
   unaffected by that bug at any display value, so rows always fade somewhere.

   .js-scoped like every other reveal primitive: no JS, no class, rows render
   fully visible. */
.js .vs-row-reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--laara-dur-reveal) var(--laara-ease-out),
              transform var(--laara-dur-reveal) var(--laara-ease-out);
}
.vs-row-reveal.is-visible { opacity: 1; transform: translateY(0); }
@media (min-width: 768px) {
  .js .vs-row-reveal,
  .vs-row-reveal.is-visible { transform: none; }
  /* Baked-in stagger, desktop only — which is why .vs-row-reveal is on the
     auto-stagger's skip list in main.js, alongside .reveal-heading and
     .reveal-support. The shared observer's inline delay is wrong here below
     768px: the section is taller than a phone viewport, so rows cross the
     threshold one at a time and a 210ms delay on the last row is not stagger,
     it is just lag before a row that is already on screen. Above 768px the
     whole table is in view at once and the delays do what they say. */
  .vs-row-reveal:nth-child(2) { transition-delay: 70ms; }
  .vs-row-reveal:nth-child(3) { transition-delay: 140ms; }
  .vs-row-reveal:nth-child(4) { transition-delay: 210ms; }
}
@media (prefers-reduced-motion: reduce) {
  .js .vs-row-reveal { opacity: 1; transform: none; transition: none; }
}

/* Below 768px three columns cannot fit. The row-label column alone took 182px
   of the 325px a 390px phone actually has (38% of a fixed 480px min-width,
   held wide by white-space: nowrap), which pushed the whole Laara Digital
   column off-screen — measured 0% visible at both 360px and 390px, behind a
   horizontal swipe. Worse, rows are sized by their tallest cell, so the
   visible column sat under unexplained gaps left by the invisible one.

   So each row restacks instead: the row label spans the full width and the
   two columns sit side by side beneath it. No horizontal scroll, both columns
   always visible, body text stays at 16px. Shrinking the columns to fit three
   across was tried first and rejected — it breaks "£797-£2,497" onto two
   lines at 360px, and only avoids that by dropping below the 16px floor.

   Overriding display drops <tbody>'s rowgroup role, so the table carries
   explicit ARIA roles in index.html. Chrome keeps table/row/rowheader/cell
   through this; the roles are insurance for the browsers that may not. */
@media (max-width: 767px) {
  .vs-table { display: block; min-width: 0; }
  .vs-table thead { display: none; }
  .vs-table tbody { display: block; }
  .vs-table tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
  /* These mirror the fixed-layout width rules above selector for selector so
     they actually win: `.vs-table th:not(:first-child)` is (0,2,1), and a
     shorter selector here would lose regardless of source order. */
  .vs-table th:first-child,
  .vs-table td:first-child { width: auto; }
  .vs-table th:not(:first-child),
  .vs-table td:not(:first-child) { width: auto; }
  .vs-table th,
  .vs-table td {
    display: block;
    padding: var(--laara-space-4);
  }
  .vs-table tbody th {
    grid-column: 1 / -1;
    padding-bottom: 0;
    white-space: normal;
  }
  /* The divider moves onto the row itself: with the label on its own line, a
     border on the cells would draw under the label rather than between rows. */
  .vs-table tbody tr + tr {
    border-top: 1px solid rgb(var(--laara-paper-rgb) / 0.10);
  }
  .vs-table tbody tr + tr th,
  .vs-table tbody tr + tr td { border-top: 0; }
  .vs-cell-label { display: block; }
  /* The tick is a desktop column-scanning aid and costs 20px of inline space
     (12px glyph + 8px margin). In the restacked cell that is exactly enough to
     break "£797–£2,497" onto two lines at 360px — measured — which is the case
     the block comment above was written to prevent. Each cell already names its
     column via .vs-cell-label here, so the tick has no job on mobile. */
  .vs-mark { display: none; }
}

/* ============================================================
   WORK
   ============================================================ */

.work-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
}
@media (min-width: 640px) {
  .work-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
}
@media (min-width: 1080px) {
  .work-grid { grid-template-columns: repeat(auto-fit, minmax(320px, 400px)); justify-content: center; }
  /* Masonry span only makes sense once a 4th card exists to fill row 2 under it — with 1-3 cards it just leaves a void. */
  .work-grid:has(a:nth-child(4)) a:first-child { grid-row: span 2; }
  .work-grid:has(a:nth-child(4)) a:first-child .work-media { aspect-ratio: 4 / 5.6; }
}

.work-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
  overflow: hidden;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out),
              box-shadow var(--laara-dur-micro) var(--laara-ease-out),
              border-color var(--laara-dur-micro) var(--laara-ease-out);
}
/* text-decoration: none needed here too, not just on the base rule — the
   global `a:hover { text-decoration: underline }` in brand tokens has
   higher specificity (0,1,1) than the class-only base rule (0,1,0) and
   would otherwise win the moment a card is hovered. */
.work-card:hover, .work-card:focus-visible { transform: translateY(-4px); box-shadow: var(--laara-shadow-lg); border-color: var(--laara-ink); text-decoration: none; }
.work-card:active { transform: translateY(-1px); }
@media (hover: none) {
  .work-card:active { transform: scale(0.99); }
}

/* Image sits on its own — no text overlaid on top of it, so nothing
   competes with the real site screenshot's own baked-in UI/text. */
.work-media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--laara-ink);
}
.work-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  transition: transform var(--laara-dur-section) var(--laara-ease-out);
}
.work-card:hover .work-media img,
.work-card:focus-visible .work-media img { transform: scale(1.05); }

.work-tags {
  position: absolute;
  top: var(--laara-space-4);
  left: var(--laara-space-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--laara-space-2);
}
.work-pill {
  display: inline-flex;
  align-items: center;
  padding: var(--laara-space-1) var(--laara-space-3);
  border-radius: 999px;
  background: var(--laara-accent);
  color: var(--laara-on-accent);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
}
.work-pill--ghost {
  background: rgb(var(--laara-ink-rgb) / 0.55);
  color: var(--laara-paper);
  border: 1.5px solid rgb(var(--laara-paper-rgb) / 0.4);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Homepage carousel only: tags sit in their own row above the image
   instead of overlaid on it, so the ghost pill needs a plain on-white
   treatment rather than the blur-over-photo one above. The direct-child
   selector is what scopes this — sector pages nest .work-tags inside
   .work-overlay, so their overlay-on-image cards are untouched. */
.work-card > .work-tags {
  position: static;
  padding: var(--laara-space-4) var(--laara-space-6) var(--laara-space-3);
}
.work-card > .work-tags .work-pill--ghost {
  background: none;
  color: var(--laara-text-secondary);
  border: 1.5px solid var(--laara-border);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Caption content lives in a solid card body below the image — same
   idea as the reference card Cuneyt shared: image up top, plain
   content block underneath, nothing fighting for legibility. */
.work-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--laara-space-6);
}
.work-index {
  display: block;
  color: var(--laara-muted-grey);
  font-family: var(--laara-font);
  font-weight: var(--laara-fw-black);
  font-size: var(--laara-fs-h3);
  letter-spacing: -0.02em;
  margin: 0 0 var(--laara-space-2);
}
.work-body h3 { color: var(--laara-ink); margin: 0 0 var(--laara-space-1); font-size: var(--laara-fs-h3); }
.work-body p { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-4); font-size: var(--laara-fs-small); }
/* ---------- Overlay-on-image work card (sector pages) ----------
   BUG FIX. .work-overlay and .work-caption were referenced in the markup on
   trades / food / fitness / hair-beauty but had no rules anywhere, so the
   caption laid out in normal flow after the image, inside .work-media —
   which is aspect-ratio 4/3 with overflow:hidden. The title, description and
   "Read case study" CTA were pushed past the bottom edge and clipped away
   entirely: those cards rendered as a bare screenshot with two pills. The
   card stayed clickable and screen readers still read the text, so nothing
   errored — it was simply invisible to sighted users.

   The scrim is what makes paper text legible over an arbitrary screenshot;
   .work-tags stays absolutely positioned by its own rule above. */
.work-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--laara-space-5);
  background: linear-gradient(to top,
              rgb(var(--laara-ink-rgb) / 0.92) 0%,
              rgb(var(--laara-ink-rgb) / 0.72) 38%,
              rgb(var(--laara-ink-rgb) / 0.15) 100%);
  color: var(--laara-paper);
}
.work-caption { display: flex; flex-direction: column; }
.work-caption h3 {
  color: var(--laara-paper);
  margin: 0 0 var(--laara-space-2);
  font-size: var(--laara-fs-h3);
}
.work-caption p {
  color: var(--laara-on-dark-muted);
  margin: 0 0 var(--laara-space-3);
  font-size: var(--laara-fs-body);
}
/* --laara-link is the on-light green (#0D844D) and only reaches 3.98:1 on
   ink. On this dark scrim the on-dark green is the correct pairing. */
.work-overlay .work-cta { color: var(--laara-link-on-dark); }

.work-cta {
  display: inline-flex;
  align-items: center;
  margin-top: auto;
  color: var(--laara-link);
  font-weight: var(--laara-fw-semibold);
  font-size: var(--laara-fs-small);
  transition: transform var(--laara-dur-micro) var(--laara-ease-out);
}
.work-card:hover .work-cta,
.work-card:focus-visible .work-cta { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) {
  .work-card:hover .work-cta,
  .work-card:focus-visible .work-cta { transform: none; }
}

/* ---------- Work carousel (homepage centrepiece) ----------
   work.html keeps its plain .work-grid untouched; this wraps the same
   .work-card/.work-media/.work-overlay markup in a horizontal, arrow +
   dot navigated carousel for the homepage — multiple projects visible at
   once with a peek of the next, no extra page-scroll consumed at all.
   Native scroll-snap means it's swipeable/draggable on touch even if JS
   never runs; main.js layers on a mouse click-and-hold drag, a pausable
   autoplay, and arrow/dot controls kept in sync with the scroll state. */
.work-track {
  display: flex;
  gap: var(--laara-space-6);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-block: var(--laara-space-2);
  cursor: grab;
}
.work-track::-webkit-scrollbar { display: none; }
.work-track:focus-visible { outline: 2px solid var(--laara-ink); outline-offset: 4px; }
@media (prefers-reduced-motion: no-preference) {
  .work-track { scroll-behavior: smooth; }
}
/* Dragging: kill smooth-scroll and snap so scrollLeft tracks the pointer
   1:1 instead of lagging/fighting the snap points, and stop card links
   and image/text selection from intercepting the gesture mid-drag. */
.work-track.is-dragging {
  cursor: grabbing;
  scroll-behavior: auto !important;
  scroll-snap-type: none;
  user-select: none;
}
.work-track.is-dragging .work-card { pointer-events: none; }
@media (hover: none) and (pointer: coarse) {
  .work-track { cursor: auto; }
}
.work-track .work-card {
  flex: 0 0 84%;
  scroll-snap-align: start;
}
@media (min-width: 640px) {
  .work-track .work-card { flex: 0 0 56%; }
}
@media (min-width: 1080px) {
  .work-track .work-card { flex: 0 0 31%; }
}

.work-carousel-controls {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--laara-space-6);
  margin-top: var(--laara-space-8);
}
.work-carousel-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--laara-control-h);
  height: var(--laara-control-h);
  border-radius: 999px;
  border: 1.5px solid var(--laara-border);
  background: var(--laara-white);
  color: var(--laara-ink);
  cursor: pointer;
  transition: background var(--laara-dur-micro) var(--laara-ease-out),
              border-color var(--laara-dur-micro) var(--laara-ease-out),
              color var(--laara-dur-micro) var(--laara-ease-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}
.work-carousel-arrow svg { width: 20px; height: 20px; }
.work-carousel-arrow:hover:not(:disabled) { border-color: var(--laara-ink); transform: translateY(-1px); }
.work-carousel-arrow:active:not(:disabled) { transform: translateY(0); }
.work-carousel-arrow:disabled { opacity: 0.35; cursor: default; }

.work-carousel-dots {
  display: flex;
  justify-content: center;
  gap: var(--laara-space-2);
  /* Grid items default to min-width:auto, so 4 dots at 44px touch
     targets (200px) forced this grid's 1fr track — and the whole
     page — wider than the viewport at 320px once a 4th project was
     added. min-width:0 lets the track actually shrink; overflow-x
     is the fallback if dots ever genuinely can't fit at any width. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.work-carousel-dots::-webkit-scrollbar { display: none; }
.work-carousel-dot {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
}
.work-carousel-dot::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--laara-border);
  transition: background var(--laara-dur-micro) var(--laara-ease-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}
.work-carousel-dot:hover::before { background: var(--laara-muted-grey); }
.work-carousel-dot[aria-current="true"]::before {
  background: var(--laara-accent);
  transform: scale(1.35);
}
@media (prefers-reduced-motion: reduce) {
  .work-track { scroll-behavior: auto; }
}

/* ============================================================
   CASE STUDIES (Work page)
   ============================================================ */

.case-study {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
  align-items: start;
  padding-bottom: var(--laara-space-16);
  margin-bottom: var(--laara-space-16);
  border-bottom: 1.5px solid var(--laara-border);
}
.case-study:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
@media (min-width: 900px) {
  .case-study { grid-template-columns: 1.1fr 1fr; gap: var(--laara-space-12); }
  .case-study--reverse .case-study-media { order: 2; }
  .case-study--reverse .case-study-body { order: 1; }
}

.case-study-media { overflow: hidden; border-radius: var(--laara-radius-card); }
.case-study-media img {
  width: 100%;
  height: auto;
  border-radius: var(--laara-radius-card);
  box-shadow: var(--laara-shadow-lg);
  display: block;
  transition: transform var(--laara-dur-section) var(--laara-ease-out);
}
.case-study:hover .case-study-media img { transform: scale(1.04); }

.case-study-tags {
  display: flex;
  gap: var(--laara-space-2);
  margin-bottom: var(--laara-space-3);
}

.tag-pill {
  display: inline-flex;
  align-items: center;
  padding: var(--laara-space-1) var(--laara-space-3);
  border-radius: 999px;
  border: 1.5px solid var(--laara-border);
  color: var(--laara-text-secondary);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
}
.case-study-body h3 { margin: 0 0 var(--laara-space-2); }
.case-study-body p { margin: 0 0 var(--laara-space-4); }
.case-study-body .laara-btn { margin-top: var(--laara-space-2); }

/* ============================================================
   CASE GRID (Work page — browser-frame preview cards)
   Scoped classes (case-frame, case-card, case-filters) so this
   redesign doesn't touch the shared .work-card / .case-study
   patterns still used on index.html, about.html and sector pages.
   ============================================================ */

.case-filters,
.service-pill-nav {
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
  gap: var(--laara-space-2);
  margin: var(--laara-space-8) 0 0;
  padding: var(--laara-space-2);
  background: var(--laara-surface);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-lg);
  box-shadow: var(--laara-shadow-sm);
}
.case-filters::-webkit-scrollbar,
.service-pill-nav::-webkit-scrollbar { display: none; }
.filter-pill {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--laara-space-5);
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--laara-text-secondary);
  font-family: var(--laara-font);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  cursor: pointer;
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.filter-pill:hover:not(.is-active) { background: rgb(var(--laara-ink-rgb) / 0.06); color: var(--laara-ink); }
.filter-pill.is-active { background: var(--laara-green); color: var(--laara-ink); font-weight: var(--laara-fw-bold); }
@media (hover: none) {
  .filter-pill:active { transform: scale(0.97); }
}
.filter-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  margin-left: var(--laara-space-2);
  border-radius: 999px;
  background: rgb(var(--laara-ink-rgb) / 0.08);
  color: inherit;
  font-size: var(--laara-fs-badge);
  font-weight: var(--laara-fw-bold);
}
.filter-pill.is-active .filter-count { background: rgb(var(--laara-ink-rgb) / 0.14); }

/* Sliding capsule behind the active pill — ink text (set on .filter-pill.is-active
   above) stays readable against both the plain track and this green capsule, so
   there's no contrast flip to time against the slide transition. The pill's own
   instant background flip (above) covers first paint/click before this settles. */
.filter-indicator {
  position: absolute;
  z-index: 0;
  top: var(--laara-space-2);
  left: 0;
  height: 44px;
  width: 0;
  background: var(--laara-green);
  border-radius: 999px;
  transition: transform var(--laara-dur-section) var(--laara-ease-out),
              width var(--laara-dur-section) var(--laara-ease-out);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .filter-indicator { transition: none; }
}

.case-card {
  display: flex;
  flex-direction: column;
}
/* .featured-project is a whole <section>, not a card, but it carries a
   data-category and joins the same filter — so it needs the hidden state too.
   .client-work / .concept-lab are hidden when a filter empties them, so a
   section heading never sits above nothing. */
.case-card.is-hidden,
.featured-project.is-hidden,
.client-work.is-hidden,
.concept-lab.is-hidden { display: none; }

/* Real vs. concept — a coloured top edge on the frame so the two are
   scannable at a glance, not just from the status badge in the body. */
.case-card--real .case-frame { border-top: 3px solid var(--laara-green); }
.case-card--concept .case-frame { border-top: 3px solid var(--laara-border); }

.case-frame {
  border-radius: var(--laara-radius-lg);
  overflow: hidden;
  background: var(--laara-ink);
  box-shadow: var(--laara-shadow-lg);
  transition: transform var(--laara-dur-section) var(--laara-ease-out),
              box-shadow var(--laara-dur-section) var(--laara-ease-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.case-card:hover .case-frame,
.case-card:focus-within .case-frame {
  transform: translateY(-4px);
  box-shadow: var(--laara-shadow-lg), 0 20px 44px -26px rgb(var(--laara-ink-rgb) / 0.35);
}
.case-card--real:hover .case-frame,
.case-card--real:focus-within .case-frame { border-top-color: var(--laara-green-deep); }
@media (hover: none) {
  .case-card:active .case-frame { transform: scale(0.99); }
}
@media (prefers-reduced-motion: reduce) {
  .case-card:hover .case-frame,
  .case-card:focus-within .case-frame { transform: none; }
}
.case-frame-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--laara-space-3) var(--laara-space-4);
  background: var(--laara-ink-raised);
}
.case-frame-bar span:not(.case-frame-url) {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgb(var(--laara-paper-rgb) / 0.22);
  flex-shrink: 0;
}
.case-frame-url {
  margin-left: var(--laara-space-3);
  padding: 3px var(--laara-space-3);
  border-radius: 999px;
  background: rgb(var(--laara-paper-rgb) / 0.08);
  color: var(--laara-on-dark-subtle);
  font-size: 0.75rem;
  font-family: var(--laara-font);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.case-frame-media {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}
.case-frame-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  transition: transform var(--laara-dur-section) var(--laara-ease-out);
}
.case-card:hover .case-frame-media img,
.case-card:focus-within .case-frame-media img { transform: scale(1.05); }

.case-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding-top: var(--laara-space-5);
}
.case-card-tags { display: flex; flex-wrap: wrap; gap: var(--laara-space-2); margin-bottom: var(--laara-space-3); }
.case-card-body h3 {
  margin: 0 0 var(--laara-space-1);
  font-size: var(--laara-fs-h3);
  transition: color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.case-card:hover .case-card-body h3,
.case-card:focus-within .case-card-body h3 { color: var(--laara-green-text); }
.case-card-location { color: var(--laara-text-secondary); font-size: var(--laara-fs-small); margin: 0 0 var(--laara-space-3); }
.case-card-excerpt {
  color: var(--laara-text-secondary);
  margin: 0 0 var(--laara-space-5);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.case-card-link {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-2);
  width: fit-content;
  min-height: var(--laara-touch-min);
  font-weight: var(--laara-fw-bold);
  color: var(--laara-ink);
  text-decoration: none;
  transition: color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.case-card-link:hover, .case-card-link:focus-visible { color: var(--laara-green-text); text-decoration: underline; }
.case-card-link span[aria-hidden] {
  display: inline-block;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out);
}
.case-card-link:hover span[aria-hidden],
.case-card-link:focus-visible span[aria-hidden] { transform: translate(2px, -2px); }

/* ---------- Project number + status badge ---------- */
.case-number {
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  letter-spacing: 0.04em;
  color: var(--laara-text-secondary);
  margin: 0 0 var(--laara-space-2);
}
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-2);
  padding: var(--laara-space-1) var(--laara-space-3);
  border-radius: 999px;
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  margin-bottom: var(--laara-space-3);
  width: fit-content;
}
.status-badge-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--laara-green); flex-shrink: 0; }
.status-badge--client {
  /* Fallback first for engines without color-mix() (older Android WebViews):
     they drop the line below and keep this. Value IS the mix, precomputed —
     14% #12B76A over #FFFFFF. Recompute if the green ever changes. */
  background: rgb(222 245 234);
  background: color-mix(in srgb, var(--laara-green) 14%, var(--laara-white));
  color: var(--laara-green-deep);
}
.status-badge--concept {
  background: var(--laara-surface);
  color: var(--laara-text-secondary);
  border: 1.5px solid var(--laara-border);
}

/* ---------- Concept-build tooltip (native <details>, keyboard + touch accessible) ---------- */
.status-badge-tooltip { position: relative; display: inline-flex; }
.status-badge-tooltip summary {
  list-style: none;
  position: relative;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  font-size: var(--laara-fs-badge);
  font-weight: var(--laara-fw-bold);
  color: inherit;
  cursor: pointer;
}
/* The visible dot is 20px, which is under both the WCAG 2.2 2.5.8 minimum
   (24px) and this project's own 44px rule. Growing the circle would unbalance
   the badge row it sits in, so the HIT AREA is expanded instead: a transparent
   ::before centred on the control, 44x44. The target is compliant; the
   ornament stays 20px. Do not remove this thinking it is decorative. */
.status-badge-tooltip summary::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--laara-touch-min);
  height: var(--laara-touch-min);
  transform: translate(-50%, -50%);
}
.status-badge-tooltip summary::-webkit-details-marker { display: none; }
.status-badge-tooltip summary:focus-visible { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.status-badge-tooltip[open] summary { background: var(--laara-ink); color: var(--laara-paper); border-color: var(--laara-ink); }
.status-badge-tooltip p {
  position: absolute;
  z-index: 5;
  top: calc(100% + var(--laara-space-2));
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 240px;
  margin: 0;
  padding: var(--laara-space-3) var(--laara-space-4);
  border-radius: var(--laara-radius);
  background: var(--laara-ink);
  color: var(--laara-paper);
  box-shadow: var(--laara-shadow-lg);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-regular);
  text-align: left;
}

/* ============================================================
   PRICING TEASER
   ============================================================ */

.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
}
@media (min-width: 780px) {
  .pricing-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- Tier grid (service pages — lighter package tiles) ----------
   Package counts vary per service (2-4 tiers), so this stays a plain
   auto-wrapping grid rather than pricing-grid's fixed 3-column layout;
   cards still reuse .laara-card.pricing-card unchanged. */
.tier-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
}
@media (min-width: 640px) { .tier-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .tier-grid { grid-template-columns: repeat(3, 1fr); } }

.pricing-card--featured { border-color: var(--laara-ink); border-width: 2px; position: relative; }

/* pricing.html-only depth fix — .pricing-card is shared with index.html's
   homepage teaser, so this is scoped by ID rather than editing the shared
   class or its base rule in brand/laara-brand-tokens.css. */
#packages .pricing-card,
#care-plans .pricing-card {
  box-shadow: var(--laara-shadow);
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              transform var(--laara-dur-micro) var(--laara-ease-out),
              box-shadow var(--laara-dur-micro) var(--laara-ease-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out);
}
#packages .pricing-card--featured { box-shadow: var(--laara-shadow-lg); }
.pricing-card-badge {
  align-self: flex-start;
  background: var(--laara-accent);
  color: var(--laara-on-accent);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  padding: var(--laara-space-1) var(--laara-space-3);
  border-radius: var(--laara-radius-sm);
  margin-bottom: var(--laara-space-4);
}
.pricing-card h3 { margin: 0 0 var(--laara-space-2); }
.pricing-price {
  font-size: var(--laara-fs-h3);
  font-weight: var(--laara-fw-black);
  color: var(--laara-ink);
  margin-bottom: var(--laara-space-4);
}
.pricing-price span { font-size: var(--laara-fs-body); font-weight: var(--laara-fw-regular); color: var(--laara-text-secondary); }
.pricing-card ul { flex-grow: 1; margin-bottom: var(--laara-space-4); }
.pricing-card .laara-btn { align-self: flex-start; }
.pricing-meta {
  font-size: var(--laara-fs-small);
  color: var(--laara-text-secondary);
  margin: 0 0 var(--laara-space-2);
}
.pricing-meta:last-of-type { margin-bottom: var(--laara-space-6); }
.pricing-meta strong { color: var(--laara-text); }

/* ---------- Pricing helper (homepage-only compact "help me choose") ----------
   pricing.html keeps its own full 3-question tool with a dedicated
   result panel (pricing.js/pricing-data.js) — this is a smaller,
   self-contained version scoped to the homepage teaser that answers
   by highlighting the matching card above instead of duplicating a
   result panel. Reuses .pill-toggle/.pill-toggle-btn verbatim (same
   component the contact wizard uses) rather than inventing a new
   input style. */
/* A card, not three label+pill rows floating under a hairline — it has to read
   as a tool rather than leftover form fields. The white background is also the
   contrast fix: --laara-text-secondary (#6E747D) is 4.24:1 on this section's
   --laara-surface (#F3F3F1) and FAILS AA, but 4.71:1 on --laara-white. The
   result copy is the most important text in the component and was the least
   readable. Fixed by moving the surface, not by editing a brand token —
   brand/laara-brand-tokens.css is a synced copy needing sign-off. */
.pricing-helper {
  margin-top: var(--laara-space-10);
  padding: var(--laara-space-8);
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
}
.pricing-helper-label {
  font-weight: var(--laara-fw-bold);
  font-size: var(--laara-fs-body-lg);
  margin: 0 0 var(--laara-space-6);
}
.pricing-helper-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-8);
}
@media (min-width: 900px) {
  .pricing-helper-grid { grid-template-columns: 1.1fr 1fr; align-items: start; }
}
.pricing-helper-q { margin-bottom: var(--laara-space-5); }
.pricing-helper-q:last-child { margin-bottom: 0; }
.pricing-helper-q .laara-field-label { margin: 0 0 var(--laara-space-2); }

/* 48px to match --laara-control-h, this project's own floor for controls.
   Scoped to the helper on purpose: the shared .pill-toggle-btn rule stays at
   the 44px --laara-touch-min because contact.html's wizard and pricing.html
   both use it, and raising those is a sitewide call, not an index.html one. */
[data-pricing-helper] .pill-toggle-btn { min-height: var(--laara-control-h); }

.pricing-helper-result {
  padding-top: var(--laara-space-8);
  border-top: 1.5px solid var(--laara-border);
}
@media (min-width: 900px) {
  .pricing-helper-result {
    padding-top: 0;
    padding-left: var(--laara-space-8);
    border-top: 0;
    border-left: 1.5px solid var(--laara-border);
  }
}

.pricing-helper-progress {
  display: flex;
  gap: var(--laara-space-2);
  margin-bottom: var(--laara-space-3);
}
.pricing-helper-progress-seg {
  flex: 1;
  height: 6px;
  border-radius: 999px;
  background: var(--laara-border);
  transition: background var(--laara-dur-section) var(--laara-ease-out);
}
/* green-deep (7.35:1), not green (2.62:1). This fill is the one place in the
   component where colour alone carries a UI state, so it owes WCAG 1.4.11's
   3:1 non-text minimum against the unfilled track — plain brand green misses. */
.pricing-helper-progress-seg.is-filled { background: var(--laara-green-deep); }

.pricing-helper-status {
  margin: 0 0 var(--laara-space-4);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  color: var(--laara-text-secondary);
}
.pricing-helper-placeholder { margin: 0; color: var(--laara-text-secondary); }
.pricing-helper-result.is-answered .pricing-helper-placeholder { display: none; }

/* Follows .choose-result's pattern (pricing.js), not the .js-scoped .reveal
   pattern: unconditional display:none, revealed by .is-visible then .is-shown
   on the next frame. A recommendation that was never computed has no honest
   "visible but unanimated" state — the <noscript> message is the no-JS answer,
   not an empty panel.

   Deliberately NOT .laara-card--wash, even though .choose-result is: that
   variant inverts the whole card to green on hover, and on a panel that
   repopulates under the cursor it reads as a false success animation. */
.pricing-helper-answer {
  display: none;
  opacity: 0;
  transform: translateY(10px);
  border-left: 3px solid var(--laara-green);
  padding-left: var(--laara-space-5);
  transition: opacity var(--laara-dur-reveal) var(--laara-ease-out),
              transform var(--laara-dur-reveal) var(--laara-ease-out);
}
.pricing-helper-answer.is-visible { display: block; }
.pricing-helper-answer.is-shown { opacity: 1; transform: translateY(0); }
.pricing-helper-answer-name { margin: 0 0 var(--laara-space-1); font-size: var(--laara-fs-h3); }
.pricing-helper-answer-price {
  margin: 0 0 var(--laara-space-2);
  font-weight: var(--laara-fw-bold);
  color: var(--laara-text);
}
.pricing-helper-answer-desc { margin: 0 0 var(--laara-space-5); color: var(--laara-text-secondary); }
.pricing-helper-answer-ctas { display: flex; flex-wrap: wrap; gap: var(--laara-space-3); }
@media (max-width: 480px) {
  .pricing-helper-answer-ctas { flex-direction: column; align-items: stretch; }
  .pricing-helper-answer-ctas .laara-btn { justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .pricing-helper-answer,
  .pricing-helper-progress-seg { transition: none; }
}

.pricing-card.is-recommended {
  border-color: var(--laara-green);
  box-shadow: 0 0 0 3px rgb(var(--laara-green-rgb) / 0.25), var(--laara-shadow-lg);
}

/* ============================================================
   COMPARISON TABLE
   Real <table> semantics — free screen-reader row/column
   association via scope="row"/"col", no hand-rolled ARIA.
   .table-scroll is an unconditional horizontal-scroll fallback
   at every viewport; the mobile tab-switcher (pricing.js) only
   ever hides columns when it sets data-active on .compare-table
   itself, so with JS disabled/failed no [data-active] attribute
   is ever present and every column stays visible + scrollable.
   ============================================================ */

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--laara-radius-card);
  border: 1.5px solid var(--laara-border);
}
.compare-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--laara-white);
}
/* Only the full 4-column desktop table needs a sizing floor — the mobile
   tab-switcher reduces this to 2 visible columns (feature + one package),
   and a leftover 640px floor would force it wide enough to push the
   package column off-screen, reachable only by scrolling past it. */
@media (min-width: 780px) {
  .compare-table { min-width: 640px; }
}
.compare-table th, .compare-table td {
  padding: var(--laara-space-4) var(--laara-space-5);
  text-align: left;
  border-bottom: 1.5px solid var(--laara-border);
  font-size: var(--laara-fs-small);
}
.compare-table thead th {
  font-size: var(--laara-fs-body);
  font-weight: var(--laara-fw-bold);
  color: var(--laara-text);
  background: var(--laara-surface);
  white-space: nowrap;
}
.compare-table th[scope="row"] {
  font-weight: var(--laara-fw-semibold);
  color: var(--laara-text);
}
.compare-table td { color: var(--laara-text-secondary); }
.compare-row--group th {
  background: var(--laara-ink);
  color: var(--laara-paper);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding-block: var(--laara-space-3);
  border-bottom: none;
}
.compare-check { color: var(--laara-green-deep); font-weight: var(--laara-fw-bold); }
.compare-dash { color: var(--laara-muted-grey); }
.compare-table tfoot td { border-bottom: none; padding-top: var(--laara-space-6); }
.compare-table tfoot .laara-btn { width: 100%; justify-content: center; }

/* ---------- Plan selector (reuses .option-card verbatim — same
   selected-state look as Help Me Choose below: green border, light
   green tint, checkmark badge. Only sizing is overridden here.) ---------- */
.compare-select-grid { margin-bottom: var(--laara-space-6); }
.compare-select-card { padding: var(--laara-space-5); gap: var(--laara-space-1); }
.compare-select-card .option-card-desc { font-size: var(--laara-fs-body); font-weight: var(--laara-fw-bold); color: var(--laara-text); }
.compare-select-badge { margin-bottom: 0; }

/* ---------- Highlight the selected package's column ----------
   Same driver as the mobile hide rule below (compareTable.dataset.active,
   set by pricing.js from the radio group's change event) — without JS
   the radios still work as inputs but have no effect on the table, which
   stays fully readable via .table-scroll's unconditional overflow-x. */
.compare-table[data-active="essential"] [data-package="essential"],
.compare-table[data-active="professional"] [data-package="professional"],
.compare-table[data-active="growth"] [data-package="growth"] {
  /* Fallback first — see the note on .status-badge--client. 8% #12B76A over #FFFFFF. */
  background: rgb(236 249 243);
  background: color-mix(in srgb, var(--laara-green) 8%, var(--laara-white));
}
.compare-table[data-active="essential"] td[data-package="essential"],
.compare-table[data-active="professional"] td[data-package="professional"],
.compare-table[data-active="growth"] td[data-package="growth"] {
  color: var(--laara-text);
  font-weight: var(--laara-fw-semibold);
}
.compare-table[data-active="essential"] thead th[data-package="essential"],
.compare-table[data-active="professional"] thead th[data-package="professional"],
.compare-table[data-active="growth"] thead th[data-package="growth"] {
  box-shadow: inset 0 -3px 0 var(--laara-green);
}

/* ---------- Mobile: hide the two non-selected columns (progressive
   enhancement) — a 4-column table needs this on a phone even with the
   highlight above; .table-scroll is the no-JS fallback either way. ---------- */
@media (max-width: 779px) {
  .compare-table[data-active] [data-package] { display: none; }
  .compare-table[data-active="essential"] [data-package="essential"],
  .compare-table[data-active="professional"] [data-package="professional"],
  .compare-table[data-active="growth"] [data-package="growth"] { display: table-cell; }
}

/* ============================================================
   HELP ME CHOOSE
   Question groups reuse .option-card-grid/.option-card verbatim
   (built for the contact-page wizard). Only the result panel
   below is new.
   ============================================================ */

.choose-questions { display: flex; flex-direction: column; gap: var(--laara-space-10); margin-bottom: var(--laara-space-8); }
.choose-question-label { display: block; margin-bottom: var(--laara-space-4); }
.choose-result {
  display: none;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--laara-dur-reveal) var(--laara-ease-out),
              transform var(--laara-dur-reveal) var(--laara-ease-out);
}
.choose-result.is-visible { display: flex; }
.choose-result.is-shown { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .choose-result { transition: none; }
}
/* Shown only inside <noscript>, where the picker above cannot function. */
.choose-noscript {
  margin: 0;
  padding: var(--laara-space-5);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
  background: var(--laara-white);
  color: var(--laara-text-secondary);
  font-size: var(--laara-fs-body);
}
.choose-result-eyebrow { margin-bottom: var(--laara-space-2); }
.choose-result h3 { margin: 0 0 var(--laara-space-2); }
.choose-result p { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-6); }

/* ============================================================
   PROCESS
   ============================================================ */

.process-wrap { position: relative; }

.process-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-8);
  list-style: none;
  padding: 0;
  counter-reset: step;
}
@media (min-width: 780px) {
  .process-list { grid-template-columns: repeat(3, 1fr); }
}
.process-step { position: relative; z-index: 1; text-align: left; }
.process-icon {
  width: 56px;
  height: 56px;
  border-radius: var(--laara-radius-lg);
  background: var(--laara-ink);
  color: var(--laara-green);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--laara-space-4);
  transition: background var(--laara-dur-section) var(--laara-ease-out),
              color var(--laara-dur-section) var(--laara-ease-out);
}
.process-icon svg { width: 26px; height: 26px; }
.process-step h3 { margin: 0 0 var(--laara-space-2); }
.process-step p { color: var(--laara-text-secondary); margin: 0; }

/* "Line reaches each step" activation — desktop only (see below for why
   this doesn't try to be a literal connecting line on mobile). No
   scroll-position tracking: the whole thing triggers once, the same
   moment every other .reveal on the page does, via the existing shared
   IntersectionObserver. The staggered feel comes entirely from each
   icon's own transition-delay, timed to land as the line "reaches" it. */
@media (min-width: 780px) {
  .process-step:nth-child(1) .process-icon { transition-delay: 150ms; }
  .process-step:nth-child(2) .process-icon { transition-delay: 450ms; }
  .process-step:nth-child(3) .process-icon { transition-delay: 750ms; }
  .process-step.is-visible .process-icon {
    background: var(--laara-green);
    color: var(--laara-ink);
  }
}

/* Connecting line through the icon centres. Desktop: horizontal, spans
   icon 1's centre to icon 3's centre — reliable because grid puts all
   three icons on the same row regardless of paragraph length. Mobile
   keeps the plain vertical stack without a drawn line: with items
   stacked in normal flow, paragraph length varies each step's height,
   so there's no way to calculate icon-centre-to-icon-centre in pure
   CSS without measuring live layout — not worth JS for a decorative
   line when the staggered fade-up already reads as a sequence. */
.process-connector { display: none; }
@media (min-width: 780px) {
  .process-connector {
    display: block;
    position: absolute;
    top: 28px;
    left: 16.66%;
    right: 16.66%;
    height: 1.5px;
    background: var(--laara-border);
    overflow: hidden;
    z-index: 0;
  }
}
/* A drawn line sliding up 20px like a card reads oddly — keep the base
   .reveal opacity fade but neutralise its translateY for this element. */
.process-connector.reveal { transform: none; }
.process-connector-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--laara-green);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 900ms var(--laara-ease-out);
}
.process-connector.is-visible .process-connector-fill { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .process-connector-fill { transition: none; }
  .process-step .process-icon { transition: none; }
}

/* ============================================================
   ADD-ONS GRID
   Uses .glow-card's quieter hover treatment deliberately — the
   bold green-wash on .laara-card is reserved for "pick me"
   package decisions, not browse-and-compare add-ons.
   ============================================================ */

.addon-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-6);
}
@media (min-width: 640px) {
  .addon-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  .addon-grid { grid-template-columns: repeat(3, 1fr); }
}
.addon-card { padding: var(--laara-space-6); }
.addon-card .laara-dot { margin-bottom: var(--laara-space-4); width: 2.5rem; height: 2.5rem; border-radius: var(--laara-radius); display: flex; align-items: center; justify-content: center; background: var(--laara-ink); }
.addon-card .laara-dot svg { width: 20px; height: 20px; color: var(--laara-green); }
.addon-card h3 { margin: 0 0 var(--laara-space-2); font-size: var(--laara-fs-h3); }
.addon-price { font-weight: var(--laara-fw-bold); color: var(--laara-text); margin: 0; }
.addon-note { display: block; font-size: var(--laara-fs-small); color: var(--laara-text-secondary); font-weight: var(--laara-fw-regular); }

/* ============================================================
   FAQ
   ============================================================ */

.faq-list {
  max-width: 46rem;
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-4);
}
.faq-item {
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
  padding: var(--laara-space-6);
  transition: border-color var(--laara-dur-section) var(--laara-ease-out),
              box-shadow var(--laara-dur-section) var(--laara-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .faq-item:not([open]):hover { border-color: var(--laara-ink); }
}
.faq-item[open] {
  border-color: var(--laara-green);
  box-shadow: var(--laara-shadow);
}
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--laara-space-4);
  min-height: var(--laara-touch-min);
  cursor: pointer;
  list-style: none;
  font-weight: var(--laara-fw-bold);
  font-size: var(--laara-fs-body-lg);
  transition: color var(--laara-dur-micro) var(--laara-ease-out);
}
.faq-item[open] summary { color: var(--laara-green-text); }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+';
  flex-shrink: 0;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--laara-green-text);
  transition: transform var(--laara-dur-reveal) var(--laara-ease-in-out);
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item summary:focus-visible {
  outline: 2px solid var(--laara-ink);
  outline-offset: 4px;
}
.faq-item p {
  margin: var(--laara-space-4) 0 0;
  color: var(--laara-text-secondary);
}
@media (prefers-reduced-motion: reduce) {
  .faq-item,
  .faq-item summary,
  .faq-item[open] summary::after { transition: none; }
}

/* ============================================================
   SECTOR CHIPS (service pages — "Who it's for")
   ============================================================ */

.sector-chip-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--laara-space-3);
}
.sector-chip {
  display: inline-flex;
  flex-direction: column;
  gap: 1px;
  min-height: var(--laara-touch-min);
  padding: var(--laara-space-2) var(--laara-space-5);
  border-radius: 999px;
  border: 1.5px solid var(--laara-border);
  background: var(--laara-white);
  text-decoration: none;
  justify-content: center;
  transition: border-color var(--laara-dur-micro) var(--laara-ease-in-out),
              background var(--laara-dur-micro) var(--laara-ease-in-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}
.sector-chip-name { font-weight: var(--laara-fw-bold); color: var(--laara-text); }
.sector-chip-note { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
@media (hover: hover) and (pointer: fine) {
  .sector-chip:hover,
  .sector-chip:focus-visible { border-color: var(--laara-ink); background: var(--laara-surface); transform: translateY(-1px); }
}
@media (hover: none) {
  .sector-chip:active { transform: scale(0.97); }
}
@media (prefers-reduced-motion: reduce) {
  .sector-chip:hover,
  .sector-chip:focus-visible { transform: none; }
}

/* ============================================================
   FINAL CTA
   ============================================================ */

/* More dramatic than the other .laara-inverse sections (stat band is an
   informational beat, this is the closing one) — bigger type, more
   padding, and the shared ambient glow (.laara-inverse::before) turned
   up a little. Still the same soft blurred/drifting blob, explicitly
   not the hero's WebGL fluid canvas — just a larger, slightly brighter
   version of it for this one moment. */
.final-cta {
  text-align: center;
  padding-block: calc(var(--laara-section-y) * 1.3);
}
.final-cta::before {
  width: 34rem;
  height: 34rem;
  top: -9rem;
  left: -8rem;
  opacity: 0.22;
}
.final-cta h2 {
  font-size: var(--laara-fs-display);
  margin: 0 0 var(--laara-space-6);
}
.final-cta p { margin: 0 auto var(--laara-space-10); color: var(--laara-on-dark-muted); font-size: var(--laara-fs-body-lg); max-width: 34rem; }
.final-cta-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--laara-space-4);
}

/* ============================================================
   FOOTER
   ============================================================ */

.site-footer {
  background: var(--laara-bg-inverse);
  color: var(--laara-text-inverse);
  padding-block: var(--laara-space-12) var(--laara-space-8);
}

/* The footer sets `background` directly rather than carrying `.laara-inverse`,
   so the on-dark safety overrides at the top of this file (`.laara-inverse a`,
   `.laara-inverse :focus-visible`) never reach it. Re-point focus here the same
   way `.consent-banner` does — otherwise the outline is ink-on-ink and invisible. */
.site-footer :focus-visible { outline-color: var(--laara-focus-ring-dark); }

/* ---- CTA strip: deliberately smaller and quieter than the page's .final-cta,
   which already sits directly above the footer on most routes. No display type,
   no reveal sweep, footer-tight spacing — a closing nudge, not a second hero. */
.footer-cta {
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-4);
  padding-bottom: var(--laara-space-10);
  margin-bottom: var(--laara-space-10);
  border-bottom: 1px solid rgb(var(--laara-paper-rgb) / 0.15);
}
.footer-cta h2 { font-size: var(--laara-fs-h3); margin: 0; }
.footer-cta p {
  color: var(--laara-on-dark-muted);
  font-size: var(--laara-fs-body);
  margin: var(--laara-space-2) 0 0;
  max-width: 40ch;
}
.footer-cta-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--laara-space-3) var(--laara-space-5);
}
.footer-cta-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
  color: var(--laara-text-inverse);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.footer-cta-link:hover,
.footer-cta-link:focus-visible { color: var(--laara-link-on-dark); }
/* On a narrow phone the primary button takes its own row so "Call us" and
   "WhatsApp us" sit together on the next line rather than one wrapping alone. */
@media (max-width: 479px) {
  .footer-cta-actions .laara-btn--primary { flex-basis: 100%; }
}
@media (min-width: 780px) {
  .footer-cta {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--laara-space-8);
  }
  .footer-cta p { margin-top: var(--laara-space-1); }
}

/* ---- grid ---- */
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-8);
  margin-bottom: var(--laara-space-10);
}
/* .footer-nav is a semantic <nav> landmark only; display:contents lets its three
   columns sit directly on .footer-grid's tracks. */
.footer-nav { display: contents; }
@media (min-width: 480px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}
@media (min-width: 780px) {
  .footer-grid { grid-template-columns: 2fr 1fr 1fr 1fr; }
  .footer-brand { grid-column: auto; }
}

/* ---- brand column ---- */
.footer-brand {
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-4);
  align-items: flex-start;
}
.footer-brand-logo {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
  color: var(--laara-text-inverse);  /* the logo link holds no text, but keep the
                                        on-light green off every footer element */
}
.footer-brand p { color: var(--laara-on-dark-muted); margin: 0; max-width: 28rem; }

.footer-status {
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-2);
  margin-top: var(--laara-space-1);
  padding: var(--laara-space-2) var(--laara-space-4);
  border-radius: 999px;
  border: 1.5px solid rgb(var(--laara-paper-rgb) / 0.16);
  color: var(--laara-on-dark-muted);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
}
/* The availability dot's pulse runs infinitely in the footer of every page.
   It used to animate box-shadow, which is a paint-and-composite property:
   the browser repainted the dot every frame, forever, on every route.
   Now the ring is a pseudo-element and only transform + opacity animate,
   both of which run on the compositor and cost no repaint. Visually the
   same expanding ring; the difference is only in what it costs to draw.
   The literal 2.4s / ease-in-out are a deliberate Phase 7 decision — a
   matching duration token would not name this timing truthfully. */
.footer-status-dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--laara-green);
  flex-shrink: 0;
}
.footer-status-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: rgb(var(--laara-green-rgb) / 0.5);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .footer-status-dot::after { animation: footer-status-pulse 2.4s ease-in-out infinite; }
}
@keyframes footer-status-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(2); opacity: 0; }
}

/* ---- social row: live in all 19 footers, Instagram only. The LinkedIn and
   Facebook <li>s sit commented out inside the same <ul> until those profiles
   exist, so these rules already carry more icons than are currently shown. ---- */
.footer-social {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--laara-space-2);
  padding: 0;
  margin: 0;
}
.footer-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--laara-touch-min);
  height: var(--laara-touch-min);
  border-radius: var(--laara-radius);
  color: var(--laara-on-dark-muted);
  transition: color var(--laara-dur-micro) var(--laara-ease-out),
              background var(--laara-dur-micro) var(--laara-ease-out);
}
/* Labelled variant, in use while Instagram is the only live profile. A single
   unlabelled 20px glyph read as a stray mark rather than a link, and the 44px
   box centres it so it also sat visually indented from the flush-left copy
   above. The pill mirrors .footer-status directly above it (999px literal and
   the same border — there is no pill-radius token), but rests in
   --laara-text-inverse like the other footer links, not the badge's muted grey.

   Two-class selector on purpose, per .laara-card--wash: the single-class form
   ties .footer-social a at (0,1,1) and would win on source order alone.

   This block sits ABOVE the :hover rule deliberately. .footer-social a:hover is
   also (0,2,1), so it only keeps winning the colour swap by being later. Moving
   this below it would freeze the link at its resting colour on hover. */
.footer-social.footer-social--labelled a {
  width: auto;
  gap: var(--laara-space-2);
  padding-inline: var(--laara-space-4);
  border: 1.5px solid rgb(var(--laara-paper-rgb) / 0.16);
  border-radius: 999px;
  color: var(--laara-text-inverse);
  font-weight: var(--laara-fw-semibold);
  text-decoration: none;
}

.footer-social a:hover,
.footer-social a:focus-visible {
  color: var(--laara-link-on-dark);
  background: rgb(var(--laara-paper-rgb) / 0.06);
}
/* (0,3,1), so it outranks the shared :hover rule above and only tints the
   border — colour and background still come from that rule. */
.footer-social.footer-social--labelled a:hover,
.footer-social.footer-social--labelled a:focus-visible {
  border-color: rgb(var(--laara-green-rgb) / 0.5);
}

/* Literal px matches the .sticky-mobile-cta-btn svg precedent — there is no
   icon-glyph size token. */
.footer-social svg { width: 20px; height: 20px; flex-shrink: 0; }

/* ---- link columns ---- */
.footer-col h3 {
  font-size: var(--laara-fs-small);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--laara-on-dark-subtle);
  margin: 0 0 var(--laara-space-3);
}
.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col li { margin-bottom: var(--laara-space-1); }
/* Nav links rest in paper — a whole column of brand green read as shouty and
   cheap. Green is the hover/focus accent now, with an underline as the
   non-colour cue (WCAG 1.4.1). */
.footer-col a {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
  padding-block: var(--laara-space-1);
  color: var(--laara-text-inverse);
  text-decoration: none;
  transition: color var(--laara-dur-micro) var(--laara-ease-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}
.footer-col a:hover,
.footer-col a:focus-visible {
  color: var(--laara-link-on-dark);
  transform: translateX(var(--laara-space-1));
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
@media (prefers-reduced-motion: reduce) {
  .footer-col a { transition: none; }
  .footer-col a:hover,
  .footer-col a:focus-visible { transform: none; }
}

/* ---- bottom bar ---- */
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--laara-space-3) var(--laara-space-5);
  border-top: 1px solid rgb(var(--laara-paper-rgb) / 0.15);
  padding-top: var(--laara-space-6);
  font-size: var(--laara-fs-small);
  color: var(--laara-on-dark-subtle);
}
.footer-company {
  flex-basis: 100%;
  font-style: normal;
  color: var(--laara-on-dark-subtle);
}
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--laara-space-3) var(--laara-space-5);
}
/* Rest in paper, not the on-light green token (`--laara-link` = #0D7F4A) that
   used to bleed in here and failed AA on ink at ~3.9:1. */
.footer-bottom a {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
  color: var(--laara-text-inverse);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.footer-bottom a:hover,
.footer-bottom a:focus-visible { color: var(--laara-link-on-dark); }
.footer-backtotop { margin-left: auto; }
@media (max-width: 479px) {
  .footer-backtotop { margin-left: 0; }
}

/* ============================================================
   CONTACT PAGE
   ============================================================ */

.contact-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-12);
}
@media (min-width: 900px) {
  .contact-layout { grid-template-columns: 3fr 2fr; }
}
.contact-form-card { padding: var(--laara-space-8); }
.form-row { margin-bottom: var(--laara-space-6); }
.form-row:last-of-type { margin-bottom: var(--laara-space-8); }
.form-hint { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); margin-top: var(--laara-space-2); }
.form-status { margin-top: var(--laara-space-4); font-weight: var(--laara-fw-semibold); }
.form-status[data-state="success"] { color: var(--laara-green-deep); }
.form-status[data-state="error"] { color: var(--laara-error); }

.contact-side { display: flex; flex-direction: column; gap: var(--laara-space-6); }
.contact-method {
  display: flex;
  align-items: flex-start;
  gap: var(--laara-space-4);
  text-decoration: none;
  color: inherit;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out), box-shadow var(--laara-dur-micro) var(--laara-ease-out);
}
.contact-method:hover, .contact-method:focus-visible { transform: translateY(-2px); box-shadow: var(--laara-shadow-lg); }
.contact-method-icon {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  border-radius: var(--laara-radius);
  background: var(--laara-accent);
  color: var(--laara-on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.contact-method-icon svg { width: 22px; height: 22px; }
.contact-method h3 { margin: 0 0 var(--laara-space-1); font-size: var(--laara-fs-body-lg); }
.contact-method p { margin: 0; color: var(--laara-text-secondary); font-size: var(--laara-fs-body); }
.contact-response {
  background: var(--laara-bg-inverse);
  color: var(--laara-text-inverse);
}
.contact-response p { margin: 0; color: var(--laara-on-dark-muted); }
/* Lifts the lead sentence out of the muted body colour on the dark card.
   Replaces an inline style="color:var(--laara-paper)". */
.contact-note-lead { color: var(--laara-paper); }

.page-hero {
  position: relative;
  overflow: hidden;
  background: var(--laara-bg-inverse);
  color: var(--laara-text-inverse);
  padding-block: var(--laara-space-16) var(--laara-space-12);
}
.page-hero h1 { color: var(--laara-paper); margin: 0 0 var(--laara-space-4); }
.page-hero p { color: var(--laara-on-dark-muted); max-width: 38rem; margin: 0; font-size: var(--laara-fs-body-lg); }
.page-hero > .laara-container { position: relative; z-index: 1; }

/* .page-hero is a dark surface but does NOT carry .laara-inverse, so the
   inverse button overrides in the tokens file never reached it: a secondary
   button here rendered ink-on-ink, i.e. invisible. That is why 404.html ships
   with a single CTA. Mirrors the .laara-inverse pairing rather than adding the
   class to the markup, which would also repaint links, labels and secondary
   text across all 17 heroes.

   Excludes --fluid: that modifier flips the hero to a LIGHT surface, where the
   default dark-on-light secondary button is already correct. */
.page-hero:not(.page-hero--fluid) .laara-btn--secondary {
  color: var(--laara-paper);
  border-color: var(--laara-paper);
}
.page-hero:not(.page-hero--fluid) .laara-btn--secondary:hover {
  background: var(--laara-paper);
  color: var(--laara-ink);
}
.page-hero:not(.page-hero--fluid) :focus-visible { outline-color: var(--laara-focus-ring-dark); }

/* Dark-band containers get relative/overflow so ambient blobs stay clipped and behind content */
.laara-inverse { position: relative; overflow: hidden; }
.laara-inverse > .laara-container { position: relative; z-index: 1; }

/* ============================================================
   SCROLL REVEAL

   NO-JS / SCRIPT-FAILURE GATE — read before editing.
   Every reveal primitive in this file starts hidden (opacity:0
   or a closed clip-path) and is only made visible by main.js
   adding .is-visible. If main.js never runs — a thrown error, a
   blocked script, a mangling mobile proxy — an ungated hidden
   state renders a permanently blank page. That is the worst
   possible failure for this audience (older users on mid-range
   phones and flaky connections) and it is silent.

   So every hidden state is scoped to `.js`, set by a one-line
   inline script that is the FIRST element in every <head>,
   before the stylesheets. No JS at all => no `.js` class =>
   the page renders fully visible and unanimated. Keep the
   prefers-reduced-motion overrides below `.js`-scoped too, or
   they lose the specificity race against these rules.
   ============================================================ */

.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--laara-dur-reveal) var(--laara-ease-out),
              transform var(--laara-dur-reveal) var(--laara-ease-out);
}
.reveal.is-visible { opacity: 1; transform: translateY(0); }

/* ---------- Zoom-reveal (portfolio/case-study imagery) ---------- */
.js .reveal-zoom {
  opacity: 0;
  transform: scale(1.08);
  transition: opacity var(--laara-dur-section) var(--laara-ease-out),
              transform var(--laara-dur-section) var(--laara-ease-out);
}
.reveal-zoom.is-visible { opacity: 1; transform: scale(1); }

@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .js .reveal-zoom { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   HEADING REVEAL — colour-sweep wipe
   A brand-coloured block travels left->right across a section
   heading. The block's trailing edge and the text's clip-path
   reveal boundary are both linear functions of the same eased
   transition progress, matched at t=0 and t=1 (see plan doc) —
   so they track each other exactly at every instant, not just
   at the endpoints. No keyframes needed, one transition drives
   both the block and the reveal.
   ============================================================ */

.reveal-heading {
  position: relative;
  overflow: hidden;
  padding-block: 0.3em;
  margin-block: -0.3em;
}
.reveal-heading__inner {
  position: relative;
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  padding-block: 0.3em;
  margin-block: -0.3em;
}
.js .reveal-heading__text {
  display: inline-block;
  clip-path: inset(-0.3em 100% -0.3em -0.3em);
  transform: translateX(-18px);
  transition: clip-path var(--laara-dur-heading) var(--laara-ease-out) var(--laara-delay-stagger),
              transform var(--laara-dur-heading) var(--laara-ease-out) var(--laara-delay-stagger);
}
/* No-JS: the sweep block never appears, so it can't park over the
   first quarter of a heading that will never be revealed. */
.reveal-heading__sweep { display: none; }
.js .reveal-heading__sweep {
  display: block;
  position: absolute;
  inset: -0.3em auto -0.3em 0;
  width: 24%;
  background: var(--heading-reveal-color, var(--laara-green));
  transform: translateX(0%);
  transition: transform var(--laara-dur-heading) var(--laara-ease-out) var(--laara-delay-stagger);
  pointer-events: none;
}
.reveal-heading.is-visible .reveal-heading__text {
  clip-path: inset(-0.3em 0 -0.3em -0.3em);
  transform: translateX(0);
}
.reveal-heading.is-visible .reveal-heading__sweep {
  transform: translateX(416.667%);
}

/* ---------- Supporting copy under a reveal-heading ---------- */
.js .reveal-support {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--laara-dur-reveal) var(--laara-ease-out) 300ms,
              transform var(--laara-dur-reveal) var(--laara-ease-out) 300ms;
}
.reveal-support.is-visible { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .js .reveal-heading__text {
    clip-path: none;
    transform: none;
    transition: none;
  }
  .js .reveal-heading__sweep { display: none; }
  .js .reveal-support { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   HERO ENTRANCE — CSS-only, plays from first paint
   The hero is above the fold on every page and its h1 is the
   page's LCP element. It used to wait for main.js to split the
   h1 into words and for the IntersectionObserver to fire, which
   measured as ~0.5s+ of LCP "element render delay" on mobile.
   Keyframes need no script, so the hero paints as soon as the
   CSS and HTML have arrived. main.js still adds .is-visible to
   these elements; that's harmless, the animation owns them.
   The h1 is readable from its first frame and only slides into
   place over a longer duration than its neighbours. Don't fade
   or clip it in: Chrome skips an element whose first paint is
   transparent or zero-area as an LCP candidate (measured — the
   metric silently moved to the logo), and on a slow phone the
   headline is the one thing that should never wait.
   ============================================================ */

@keyframes laara-hero-rise {
  from { opacity: 0; transform: translateY(20px); }
}
@keyframes laara-hero-rise-heading {
  from { transform: translateY(0.4em); }
}

.js :is(.hero, .page-hero) .reveal {
  animation: laara-hero-rise var(--laara-dur-section) var(--laara-ease-out) both;
}
.js :is(.hero, .page-hero) h1.laara-display.reveal {
  opacity: 1; /* beats .js .reveal's opacity: 0 — see note above */
  animation: laara-hero-rise-heading var(--laara-dur-heading) var(--laara-ease-out) both;
}
.js :is(.hero, .page-hero) .reveal:nth-child(2) { animation-delay: var(--laara-delay-stagger); }
.js :is(.hero, .page-hero) .reveal:nth-child(3) { animation-delay: calc(var(--laara-delay-stagger) * 2); }
.js :is(.hero, .page-hero) .reveal:nth-child(4) { animation-delay: calc(var(--laara-delay-stagger) * 3); }
.js :is(.hero, .page-hero) .reveal:nth-child(n+5) { animation-delay: calc(var(--laara-delay-stagger) * 4); }
/* The h1 rule above out-specifies these nth-child delays, so its shorthand
   resets the h1's delay to 0 on purpose: the LCP element starts at once. */

/* Must include the h1 selector at its own specificity, or the wipe survives. */
@media (prefers-reduced-motion: reduce) {
  .js :is(.hero, .page-hero) .reveal,
  .js :is(.hero, .page-hero) h1.laara-display.reveal { animation: none; }
}

/* ============================================================
   NAV INDICATOR
   ============================================================ */

.nav-indicator {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  width: 0;
  height: 0;
  background: var(--laara-ink);
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out),
              width var(--laara-dur-micro) var(--laara-ease-out),
              height var(--laara-dur-micro) var(--laara-ease-out),
              opacity var(--laara-dur-micro) var(--laara-ease-out);
}
@media (max-width: 899px) {
  .nav-indicator { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-indicator { transition: none; }
}

/* ============================================================
   CUSTOM CURSOR (decorative, desktop only, additive)
   ============================================================ */

.custom-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 22px;
  height: 22px;
  margin-left: -11px;
  margin-top: -11px;
  border-radius: 50%;
  background: var(--laara-green);
  opacity: 0.45;
  mix-blend-mode: normal;
  pointer-events: none;
  z-index: 200;
  transition: width var(--laara-dur-micro) var(--laara-ease-out),
              height var(--laara-dur-micro) var(--laara-ease-out),
              margin var(--laara-dur-micro) var(--laara-ease-out),
              opacity var(--laara-dur-micro) var(--laara-ease-out);
}
.custom-cursor--active {
  width: 44px;
  height: 44px;
  margin-left: -22px;
  margin-top: -22px;
  opacity: 0.22;
}
@media (max-width: 899px), (hover: none), (pointer: coarse) {
  .custom-cursor { display: none; }
}

/* ============================================================
   QUOTE WIZARD (Contact page)
   Fully namespaced (.mode-switch, .quote-wizard*, .option-card,
   .chip-select, .pill-toggle, .wizard-*) — only contact.html
   loads this markup. Values pull from brand tokens throughout.
   ============================================================ */

.quote-wizard-section { padding-top: var(--laara-space-12); }

/* ---------- Mode switch ---------- */
.mode-switch {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-2);
  background: var(--laara-surface);
  border-radius: var(--laara-radius-lg);
  padding: var(--laara-space-2);
  margin-bottom: var(--laara-space-10);
}
@media (min-width: 640px) {
  .mode-switch { flex-direction: row; }
}
.mode-switch-btn {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--laara-space-1);
  text-align: left;
  padding: var(--laara-space-4) var(--laara-space-6);
  min-height: var(--laara-control-h);
  border: none;
  background: transparent;
  border-radius: var(--laara-radius-card);
  cursor: pointer;
  font-family: var(--laara-font);
  color: var(--laara-text-secondary);
  transition: color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.mode-switch-title { font-weight: var(--laara-fw-bold); font-size: var(--laara-fs-body-lg); color: var(--laara-text); }
.mode-switch-hint { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
.mode-switch-btn.is-active .mode-switch-title { color: var(--laara-ink); }
.mode-switch-btn:focus-visible { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.mode-switch-indicator {
  position: absolute;
  z-index: 0;
  top: var(--laara-space-2);
  left: var(--laara-space-2);
  height: calc(100% - var(--laara-space-4));
  width: 0;
  background: var(--laara-white);
  border-radius: var(--laara-radius-card);
  box-shadow: var(--laara-shadow);
  transition: transform var(--laara-dur-section) var(--laara-ease-out),
              width var(--laara-dur-section) var(--laara-ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .mode-switch-indicator { transition: none; }
}

/* ---------- Layout: wizard column + sticky summary ---------- */
.quote-wizard-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-10);
  margin-bottom: var(--laara-space-12);
}
@media (min-width: 1080px) {
  .quote-wizard-layout { grid-template-columns: 7fr 3fr; align-items: start; }
}
.quote-wizard { min-width: 0; }

/* ---------- Progress tracker ---------- */
.wizard-progress { margin-bottom: var(--laara-space-8); }
.wizard-progress-track-wrap { position: relative; display: none; }
@media (min-width: 1080px) {
  .wizard-progress-track-wrap { display: block; }
  .wizard-progress-mobile { display: none; }
}
.wizard-progress-line {
  position: absolute;
  top: 16px;
  left: 16px;
  right: 16px;
  height: 2px;
  background: var(--laara-border);
}
.wizard-progress-line-fill {
  position: absolute;
  top: 16px;
  left: 16px;
  height: 2px;
  width: 0%;
  background: var(--laara-green);
  transition: width var(--laara-dur-section) var(--laara-ease-out);
}
.wizard-progress-track {
  position: relative;
  display: flex;
  justify-content: space-between;
  list-style: none;
  margin: 0;
  padding: 0;
}
.wizard-progress-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--laara-space-2);
  text-align: center;
  flex: 1;
}
.wizard-progress-num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  color: var(--laara-text-secondary);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  transition: background var(--laara-dur-section) var(--laara-ease-out),
              border-color var(--laara-dur-section) var(--laara-ease-out),
              color var(--laara-dur-section) var(--laara-ease-out);
}
.wizard-progress-label { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
.wizard-progress-step.is-current .wizard-progress-num { background: var(--laara-ink); border-color: var(--laara-ink); color: var(--laara-paper); }
.wizard-progress-step.is-current .wizard-progress-label { color: var(--laara-text); font-weight: var(--laara-fw-semibold); }
.wizard-progress-step.is-complete .wizard-progress-num { background: var(--laara-green); border-color: var(--laara-green); color: var(--laara-on-accent); }

.wizard-progress-mobile-label { margin: 0 0 var(--laara-space-3); font-size: var(--laara-fs-small); font-weight: var(--laara-fw-semibold); color: var(--laara-text); }
.wizard-progress-bar { height: 4px; border-radius: 999px; background: var(--laara-border); overflow: hidden; }
.wizard-progress-bar-fill { height: 100%; width: 20%; background: var(--laara-green); border-radius: 999px; transition: width var(--laara-dur-section) var(--laara-ease-out); }
@media (prefers-reduced-motion: reduce) {
  .wizard-progress-line-fill, .wizard-progress-bar-fill, .wizard-progress-num { transition: none; }
}

/* ---------- Form card + steps ---------- */
.quote-form-card, .enquiry-form-card { padding: var(--laara-space-8); }
.wizard-step { display: none; opacity: 1; transform: none; }
.wizard-step.is-active { display: block; }
.wizard-step h2 { margin: 0 0 var(--laara-space-2); }
.wizard-step-hint { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-6); }
.wizard-field-group { margin-bottom: var(--laara-space-8); }
.wizard-field-group:last-child { margin-bottom: var(--laara-space-8); }
.wizard-field-group > .laara-field-label,
.wizard-field-group > span.laara-field-label { display: block; margin-bottom: var(--laara-space-3); }
.wizard-field-row { display: grid; grid-template-columns: 1fr; gap: 0 var(--laara-space-6); }
@media (min-width: 560px) {
  .wizard-field-row { grid-template-columns: 1fr 1fr; }
}

.wizard-step.is-leaving-forward { opacity: 0; transform: translateX(-24px); }
.wizard-step.is-entering-forward { opacity: 0; transform: translateX(24px); }
.wizard-step.is-leaving-back { opacity: 0; transform: translateX(24px); }
.wizard-step.is-entering-back { opacity: 0; transform: translateX(-24px); }
.wizard-step.is-transitioning { transition: opacity var(--laara-dur-reveal) var(--laara-ease-out), transform var(--laara-dur-reveal) var(--laara-ease-out); }
@media (prefers-reduced-motion: reduce) {
  .wizard-step.is-transitioning { transition: opacity var(--laara-dur-micro) linear; }
  .wizard-step.is-leaving-forward, .wizard-step.is-entering-forward,
  .wizard-step.is-leaving-back, .wizard-step.is-entering-back { transform: none; }
}

/* ---------- Option cards (services, budget bands) ---------- */
.option-card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-4);
}
@media (min-width: 640px) {
  .option-card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  .option-card-grid:not(.option-card-grid--compact) { grid-template-columns: repeat(3, 1fr); }
}

.option-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--laara-space-2);
  padding: var(--laara-space-6);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
  background: var(--laara-white);
  cursor: pointer;
  transition: border-color var(--laara-dur-micro) var(--laara-ease-out),
              background var(--laara-dur-micro) var(--laara-ease-out),
              transform var(--laara-dur-micro) var(--laara-ease-out),
              box-shadow var(--laara-dur-micro) var(--laara-ease-out);
}
.option-card-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
}
.option-card:focus-within { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.option-card-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--laara-radius);
  background: var(--laara-ink);
  color: var(--laara-green);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--laara-space-2);
}
.option-card-icon svg { width: 22px; height: 22px; }
.option-card-title { font-weight: var(--laara-fw-bold); color: var(--laara-text); }
.option-card-desc { font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
.option-card-range { font-weight: var(--laara-fw-bold); color: var(--laara-text); font-size: var(--laara-fs-body-lg); }

@media (hover: hover) and (pointer: fine) {
  .option-card:hover { border-color: var(--laara-ink); transform: translateY(-2px); box-shadow: var(--laara-shadow); }
}
.option-card.is-selected,
.option-card:has(.option-card-input:checked) {
  border-color: var(--laara-green);
  /* Fallback first — see the note on .status-badge--client. 8% #12B76A over #FFFFFF. */
  background: rgb(236 249 243);
  background: color-mix(in srgb, var(--laara-green) 8%, var(--laara-white));
  box-shadow: var(--laara-shadow);
}
.option-card.is-selected::after,
.option-card:has(.option-card-input:checked)::after {
  content: '';
  position: absolute;
  top: var(--laara-space-4);
  right: var(--laara-space-4);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--laara-green) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23101114' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4L19 7'/%3E%3C/svg%3E") center / 13px no-repeat;
}
.option-card--muted { border-style: dashed; }
.option-card--muted .option-card-title,
.option-card--muted .option-card-desc { color: var(--laara-text-secondary); }
@media (prefers-reduced-motion: reduce) {
  .option-card:hover { transform: none; }
}

/* ---------- Chip select (multi-select feature chips) ---------- */
.chip-select { display: flex; flex-wrap: wrap; gap: var(--laara-space-2); }
.chip-select-item {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--laara-space-4);
  border-radius: 999px;
  border: 1.5px solid var(--laara-border);
  background: var(--laara-white);
  color: var(--laara-text-secondary);
  font-family: var(--laara-font);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  cursor: pointer;
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              color var(--laara-dur-micro) var(--laara-ease-in-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.chip-select-item:hover { border-color: var(--laara-ink); color: var(--laara-ink); }
.chip-select-item:focus-visible { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.chip-select-item[aria-pressed="true"] { background: var(--laara-green); border-color: transparent; color: var(--laara-ink); }
@media (hover: none) {
  .chip-select-item:active { transform: scale(0.97); }
}

/* ---------- Pill toggle (yes/no + single-select groups) ---------- */
.pill-toggle { display: flex; flex-wrap: wrap; gap: var(--laara-space-3); }
.pill-toggle-btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--laara-space-5);
  border-radius: 999px;
  border: 1.5px solid var(--laara-border);
  background: var(--laara-white);
  color: var(--laara-text-secondary);
  font-family: var(--laara-font);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  cursor: pointer;
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              color var(--laara-dur-micro) var(--laara-ease-in-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out);
}
.pill-toggle-btn:hover { border-color: var(--laara-ink); color: var(--laara-ink); }
.pill-toggle-btn:focus-visible { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.pill-toggle-btn[aria-pressed="true"],
.pill-toggle-btn[aria-checked="true"] { background: var(--laara-ink); border-color: var(--laara-ink); color: var(--laara-paper); }
@media (hover: none) {
  .pill-toggle-btn:active { transform: scale(0.97); }
}

/* ---------- Conditional reveal fields ---------- */
.wizard-reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  overflow: hidden;
  transition: grid-template-rows var(--laara-dur-section) var(--laara-ease-out),
              opacity var(--laara-dur-reveal) var(--laara-ease-out);
}
.wizard-reveal-inner { min-height: 0; }
.wizard-reveal.is-open { grid-template-rows: 1fr; opacity: 1; margin-top: var(--laara-space-4); }
@media (prefers-reduced-motion: reduce) {
  .wizard-reveal { transition: none; }
}

/* ---------- Field errors ---------- */
.wizard-field-error {
  color: var(--laara-error);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-semibold);
  margin-top: var(--laara-space-2);
}
.laara-input[aria-invalid="true"] { border-color: var(--laara-error); }

/* ---------- Wizard controls ---------- */
.wizard-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--laara-space-4);
  margin-top: var(--laara-space-8);
  padding-top: var(--laara-space-6);
  border-top: 1.5px solid var(--laara-border);
}
.wizard-controls .wizard-next,
.wizard-controls .wizard-submit { margin-left: auto; }
.laara-btn:disabled,
.laara-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

/* ---------- Review step ---------- */
.wizard-review { display: flex; flex-direction: column; gap: var(--laara-space-6); margin-bottom: var(--laara-space-6); }
.wizard-review-group { border: 1.5px solid var(--laara-border); border-radius: var(--laara-radius-card); padding: var(--laara-space-5) var(--laara-space-6); }
.wizard-review-group-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--laara-space-3); }
.wizard-review-group-head .laara-label { margin: 0; }
.wizard-review-edit {
  background: none;
  border: none;
  padding: var(--laara-space-1) var(--laara-space-2);
  color: var(--laara-link);
  font-weight: var(--laara-fw-semibold);
  font-size: var(--laara-fs-small);
  cursor: pointer;
  min-height: 32px;
}
.wizard-review-edit:hover, .wizard-review-edit:focus-visible { color: var(--laara-green-text); text-decoration: underline; }
.wizard-review-row { display: flex; justify-content: space-between; gap: var(--laara-space-4); padding: var(--laara-space-2) 0; font-size: var(--laara-fs-body); }
.wizard-review-row dt { color: var(--laara-text-secondary); margin: 0; }
.wizard-review-row dd { margin: 0; text-align: right; font-weight: var(--laara-fw-semibold); color: var(--laara-text); }
.wizard-reassurance { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-2); }
/* Was --laara-muted-grey (2.92:1 on surface) — fails AA at this size
   (--laara-fs-small = 14px, not large text), and this is real legal-notice
   text. Same fix as .laara-label, see docs/BUILD-PROMPT.md Decision 5. */
.wizard-privacy-note { color: var(--laara-text-grey); font-size: var(--laara-fs-small); margin: 0; }

/* ---------- Success state ---------- */
.wizard-success { text-align: center; padding: var(--laara-space-12) var(--laara-space-8); }
/* align-self: the card is a flex column (.laara-card), where text-align
   doesn't move an inline-flex child — without it the tick sat left. */
.wizard-success-check { display: inline-flex; align-self: center; margin-bottom: var(--laara-space-6); width: 72px; height: 72px; }
.wizard-success-check svg { width: 100%; height: 100%; }
.wizard-success-check-circle {
  stroke: var(--laara-green);
  stroke-width: 2.5;
  stroke-dasharray: 145;
  stroke-dashoffset: 145;
}
.wizard-success-check-mark {
  stroke: var(--laara-green);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
}
.wizard-success:not([hidden]) .wizard-success-check-circle { animation: wizard-draw-circle 700ms var(--laara-ease-out) forwards; }
.wizard-success:not([hidden]) .wizard-success-check-mark { animation: wizard-draw-mark 450ms var(--laara-ease-out) 550ms forwards; }
@keyframes wizard-draw-circle { to { stroke-dashoffset: 0; } }
@keyframes wizard-draw-mark { to { stroke-dashoffset: 0; } }
.wizard-success h2 { margin: 0 0 var(--laara-space-3); }
.wizard-success p { color: var(--laara-text-secondary); max-width: 32rem; margin: 0 auto var(--laara-space-6); }
.wizard-success-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--laara-space-4); }
@media (prefers-reduced-motion: reduce) {
  .wizard-success-check-circle,
  .wizard-success-check-mark { animation: none; stroke-dashoffset: 0; }
}

/* ---------- Sticky summary panel ---------- */
.wizard-summary { display: none; }
@media (min-width: 1080px) {
  .wizard-summary {
    display: block;
    position: sticky;
    top: calc(var(--laara-header-h-desktop) + var(--laara-space-6));
  }
}
.wizard-summary-card { padding: var(--laara-space-6); }
.wizard-summary-body { display: flex; flex-direction: column; }
.wizard-summary-row {
  display: flex;
  justify-content: space-between;
  gap: var(--laara-space-3);
  padding: var(--laara-space-3) 0;
  border-bottom: 1px solid var(--laara-border);
  font-size: var(--laara-fs-small);
}
.wizard-summary-row:last-child { border-bottom: none; }
.wizard-summary-row dt { color: var(--laara-text-secondary); margin: 0; }
.wizard-summary-row dd { margin: 0; text-align: right; font-weight: var(--laara-fw-semibold); color: var(--laara-text); max-width: 60%; }
.wizard-summary-row dd.is-empty { color: var(--laara-muted-grey); font-weight: var(--laara-fw-regular); }

/* [hidden] loses to any author rule of equal specificity that sets its own
   display (.laara-card, .quote-wizard-layout, .pill-toggle, etc. all do) —
   scope one override to the wizard so hidden panels/success states/reveals
   actually disappear regardless of what else targets that same element. */
.quote-wizard-section [hidden] { display: none !important; }

/* ---------- Contact methods strip ---------- */
.contact-methods-strip {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-4);
}
@media (min-width: 640px) {
  .contact-methods-strip { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1080px) {
  .contact-methods-strip { grid-template-columns: repeat(4, 1fr); }
}

/* ---------- Instagram follow line, under the methods strip ----------
   --laara-fs-body (16px), not --laara-fs-small: this is a real sentence of
   body copy, and 16px is the floor. --laara-link resolves to the AA-safe
   --laara-green-text, never raw #12B76A. The inline link is exempt from the
   44px target-size minimum under WCAG 2.2 SC 2.5.8's inline exception, which
   is the same basis as .form-privacy-note a. */
.contact-social-line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--laara-space-2);
  margin: var(--laara-space-6) 0 0;
  font-size: var(--laara-fs-body);
  color: var(--laara-text-grey);
  text-align: center;
}
.contact-social-line svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--laara-link);
}
.contact-social-line a {
  color: var(--laara-link);
  font-weight: var(--laara-fw-bold);
}

/* ============================================================
   WORK PAGE — REDESIGN
   Everything below is additive/scoped to work.html. Reuses the
   .case-frame / .case-card / .reveal* / .laara-inverse patterns
   defined above rather than introducing a second visual system.
   ============================================================ */

/* ---------- Hero: two-column layout override ----------
   .page-hero.page-hero--fluid > .laara-container (defined earlier)
   centers a single narrow column for the other fluid-hero pages.
   Same specificity (3 classes), so this wins on source order for
   the work-page hero only, without touching that shared rule. */
.page-hero--work.page-hero--fluid > .laara-container {
  text-align: left;
  max-width: var(--laara-content-max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-10);
  align-items: center;
}
@media (min-width: 1024px) {
  .page-hero--work.page-hero--fluid > .laara-container {
    grid-template-columns: 1.1fr 0.9fr;
    gap: var(--laara-space-12);
  }
}
.work-hero { padding-block: var(--laara-space-12) var(--laara-space-10); }
.work-hero-copy .laara-label { margin-bottom: var(--laara-space-4); }
.work-hero-copy h1 { margin: 0 0 var(--laara-space-5); }
.work-hero-sub {
  font-size: var(--laara-fs-body-lg);
  color: var(--laara-text-secondary);
  max-width: 34rem;
  margin: 0 0 var(--laara-space-6);
}
.work-hero-scroll {
  display: inline-flex;
  align-items: center;
  gap: var(--laara-space-2);
  min-height: var(--laara-touch-min);
  font-weight: var(--laara-fw-bold);
  color: var(--laara-ink);
  text-decoration: none;
}
.work-hero-scroll:hover, .work-hero-scroll:focus-visible { color: var(--laara-green-text); text-decoration: underline; }
.work-hero-scroll span {
  display: inline-block;
  transition: transform var(--laara-dur-micro) var(--laara-ease-out);
}
.work-hero-scroll:hover span { transform: translateY(3px); }

/* ---------- Hero project collage ---------- */
.work-hero-collage { position: relative; }
.work-hero-frame {
  position: relative;
  width: min(340px, 88%);
  margin-inline: auto;
  aspect-ratio: 16 / 10;
  transform: translate(calc(var(--px, 0px) * var(--depth, 1)), calc(var(--py, 0px) * var(--depth, 1)));
}
/* Parallax depth per frame. main.js writes --px/--py once on the collage
   container and each frame multiplies them by its own --depth, so one write
   per pointermove drives all three at different rates. These used to be
   inline style="--depth: …" attributes; they live here now because the
   frames already have modifier classes to hang them on. */
.work-hero-frame--1 { --depth: 0.6; }
.work-hero-frame--2 { --depth: 1.2; }
.work-hero-frame--3 { --depth: 1.6; }
.work-hero-frame--2, .work-hero-frame--3 { display: none; }
.work-hero-frame-inner {
  position: absolute;
  inset: 0;
  border-radius: var(--laara-radius-lg);
  overflow: hidden;
  background: var(--laara-ink);
  box-shadow: var(--laara-shadow-lg);
}
.work-hero-frame-inner img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.work-hero-frame--2 .work-hero-frame-inner { transition-delay: var(--laara-delay-stagger); }
.work-hero-frame--3 .work-hero-frame-inner { transition-delay: 160ms; }
@media (min-width: 1024px) {
  .work-hero-collage { height: 420px; }
  .work-hero-frame { width: auto; margin: 0; position: absolute; }
  .work-hero-frame--1 { width: 60%; top: 6%; left: 2%; z-index: 3; }
  .work-hero-frame--2 { display: block; width: 42%; top: 52%; left: 44%; z-index: 2; }
  .work-hero-frame--3 { display: block; width: 34%; top: -4%; left: 60%; z-index: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .work-hero-frame { transform: none; }
}

/* ---------- Featured project ----------
   Bottom padding trimmed from the default --laara-section-y (up to 120px)
   down to a fixed --laara-space-12 (48px): this section's own content is
   comparatively short (one image + a short text column), so the default
   padding stacked with the next section's own top padding read as one
   large dead gap rather than normal section rhythm. Same fix applied to
   .browse-work below for the same reason (short filter-bar content). */
.featured-project { padding-bottom: var(--laara-space-12); }
.featured-project-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-10);
}
@media (min-width: 1024px) {
  .featured-project-layout { grid-template-columns: 1.6fr 1fr; align-items: start; gap: var(--laara-space-12); }
}
.featured-project-layout > * { min-width: 0; }
.featured-project-media:hover .case-frame-media img,
.featured-project-media:focus-within .case-frame-media img { transform: scale(1.05); }
.featured-project-body h2 { margin: var(--laara-space-3) 0 var(--laara-space-2); font-size: clamp(2rem, 1.4rem + 2.4vw, 3.25rem); }
.featured-project-meta { color: var(--laara-text-secondary); font-weight: var(--laara-fw-semibold); margin: 0 0 var(--laara-space-4); }
.featured-project-body > p:not(.featured-project-meta) { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-6); }

/* ---------- Browse the work ---------- */
.browse-work { padding-top: var(--laara-space-12); padding-bottom: var(--laara-space-12); }
.browse-work .section-head { margin-bottom: 0; }

/* ---------- Client work: equal two-up ---------- */
.client-work { padding-top: var(--laara-space-12); }
.client-work-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-12);
  margin-top: var(--laara-space-8);
}
@media (min-width: 900px) {
  .client-work-grid { grid-template-columns: 1fr 1fr; align-items: start; gap: var(--laara-space-8); }
}
.client-work-grid > .case-card { min-width: 0; }

/* ---------- Concept lab: dark section, 2x2 grid ---------- */
.concept-lab .section-head p { color: var(--laara-on-dark-muted); }
.concept-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-10) var(--laara-space-8);
  margin-top: var(--laara-space-4);
}
@media (min-width: 640px) {
  .concept-grid { grid-template-columns: repeat(2, 1fr); }
}
.concept-grid > .case-card { min-width: 0; }
.concept-lab .case-card--concept .case-frame { border-top-color: rgb(var(--laara-paper-rgb) / 0.25); }
.concept-lab .case-number { color: var(--laara-on-dark-subtle); }
.concept-lab .case-card-body h3 { color: var(--laara-text-inverse); }
.concept-lab .case-card:hover .case-card-body h3,
.concept-lab .case-card:focus-within .case-card-body h3 { color: var(--laara-green); }
.concept-lab .case-card-location,
.concept-lab .case-card-excerpt { color: var(--laara-on-dark-muted); }
.concept-lab .tag-pill { border-color: rgb(var(--laara-paper-rgb) / 0.25); color: var(--laara-on-dark-muted); }
.concept-lab .case-card-link { color: var(--laara-paper); }
.concept-lab .case-card-link:hover, .concept-lab .case-card-link:focus-visible { color: var(--laara-green); }
.concept-lab .status-badge--concept {
  background: rgb(var(--laara-paper-rgb) / 0.08);
  color: var(--laara-on-dark-muted);
  border-color: rgb(var(--laara-paper-rgb) / 0.25);
}
.concept-lab .status-badge-tooltip[open] summary { background: var(--laara-paper); color: var(--laara-ink); border-color: var(--laara-paper); }

/* ---------- Approach ---------- */
.approach-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-10) var(--laara-space-8);
}
@media (min-width: 780px) {
  .approach-grid { grid-template-columns: repeat(2, 1fr); }
}
.approach-item {
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  border-radius: var(--laara-radius-card);
  padding: var(--laara-space-8);
  box-shadow: var(--laara-shadow);
}
.approach-number {
  font-family: var(--laara-font);
  font-weight: var(--laara-fw-black);
  font-size: clamp(2rem, 1.5rem + 2vw, 3rem);
  color: var(--laara-border);
  margin: 0 0 var(--laara-space-2);
  line-height: 1;
}
.approach-item h3 { margin: 0 0 var(--laara-space-2); }
.approach-item p { color: var(--laara-text-secondary); margin: 0; }

/* ---------- Final CTA: full-width stacked buttons on small phones ---------- */
@media (max-width: 480px) {
  .final-cta-actions { flex-direction: column; }
  .final-cta-actions .laara-btn { width: 100%; }
}

/* ============================================================
   ABOUT PAGE — REDESIGN
   Everything below is additive/scoped to about.html. Reuses
   .reveal / .reveal-heading / .laara-inverse / .tag-pill / .marquee
   patterns defined above rather than introducing a second
   visual system. Does not touch .case-study (shared with
   work.html/sector pages, see note near line 1512).
   ============================================================ */

/* ---------- Hero: asymmetric editorial layout override ----------
   .page-hero.page-hero--fluid > .laara-container (defined earlier)
   centers a single narrow column for the other fluid-hero pages.
   Same specificity (3 classes) as that rule, so this wins on
   source order for the about-page hero only — same technique
   already used for .page-hero--work above. */
.page-hero--about.page-hero--fluid > .laara-container {
  text-align: left;
  max-width: var(--laara-content-max);
  margin-inline: auto;
}
.about-hero-content { padding-block: var(--laara-space-6) var(--laara-space-2); }
.about-hero-kicker {
  display: block;
  font-weight: var(--laara-fw-bold);
  font-size: var(--laara-fs-small);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--laara-text-secondary);
  margin: 0 0 var(--laara-space-6);
}
.about-hero-heading { margin: 0 0 var(--laara-space-6); }
.about-hero-heading .hero-line { display: block; }
.about-hero-heading .hero-line--accent {
  color: var(--laara-green-text);
  margin-left: clamp(0rem, -2rem + 8vw, 3.5rem);
}
.about-hero-sub {
  font-size: var(--laara-fs-body-lg);
  color: var(--laara-text-secondary);
  max-width: 30rem;
  margin: 0;
}
@media (min-width: 560px) {
  .about-hero-sub { margin-left: auto; text-align: right; }
}

/* Sector ticker at the foot of the hero, above the fluid canvas/type layers */
.about-hero-ticker {
  position: relative;
  z-index: 1;
  margin-top: var(--laara-space-10);
  border-top: 1.5px solid var(--laara-border);
}

/* ---------- Meet the founder ----------
   Photo left, story right on tablet and up (story track wider), then a
   full-width credential band underneath, separated from the biography by
   its own hairline. Nothing here introduces a second visual system: the
   credential cards ARE .laara-card + .glow-card, so their surface,
   border, radius, -2px hover lift, focus-within state, touch tap state
   and reduced-motion path all come from the shared components. Entrance
   uses the existing .reveal / .reveal-zoom / .reveal-heading primitives,
   which means the section still renders fully with JS off. */
.founder-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-8);
}
@media (min-width: 768px) {
  .founder-grid {
    /* minmax(0, …) rather than a bare fr: an fr track's automatic minimum
       is min-content, so one long unbreakable word would push the track
       past its share and widen the whole grid. */
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    column-gap: var(--laara-space-10);
    align-items: start;
  }
}
/* No position: sticky on the media column, unlike the version of this
   section it replaces. Measured at 1440px the two columns now come out at
   693px and 557px, and at 1024px at 557px and 615px — there is no height
   difference left for a sticky photo to travel through, so it would be a
   scroll effect that never visibly does anything.

   Related, if it is ever reintroduced: the credential band is a SIBLING of
   .founder-grid, not a full-width row inside it. A sticky grid item is
   confined by the grid container's content box rather than by its own grid
   area, so with the band inside the grid a sticky photo travels down over
   the cards. And sticky would have to sit on .founder-media, never on
   .founder-portrait-frame — the frame carries .reveal-zoom's scale, and a
   transform on a sticky box makes it its own containing block, which
   silently kills the stickiness. */
@media (min-width: 1024px) {
  .founder-grid { column-gap: var(--laara-space-16); }
}

/* ---------- Portrait ---------- */
.founder-portrait { position: relative; }
.founder-portrait-frame {
  /* 4/5 is exactly the source file's 640x800, so object-fit: cover crops
     nothing — and the ratio reserves the full box before the image
     arrives, so neither the lazy load nor the reveal shifts the page. */
  aspect-ratio: 4 / 5;
  border-radius: var(--laara-radius-lg);
  border: 1.5px solid var(--laara-border);
  overflow: hidden;
  box-shadow: var(--laara-shadow);
  background: var(--laara-surface);
}
.founder-portrait-frame picture { display: block; height: 100%; }
.founder-portrait-frame img { width: 100%; height: 100%; object-fit: cover; }

.founder-portrait-badge {
  position: absolute;
  left: var(--laara-space-4);
  bottom: var(--laara-space-4);
  /* Never wider than the frame it sits in, down to 320px. */
  max-width: calc(100% - var(--laara-space-8));
  margin: 0;
  display: inline-flex;
  align-items: baseline;
  gap: var(--laara-space-2);
  padding: var(--laara-space-2) var(--laara-space-4);
  border-radius: var(--laara-radius);
  background: var(--laara-white);
  border: 1.5px solid var(--laara-border);
  box-shadow: var(--laara-shadow-sm);
  color: var(--laara-text);
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  line-height: 1.2;
}
.founder-portrait-badge-num {
  color: var(--laara-green-text);   /* 4.75:1 on the white chip — never --laara-green */
  font-size: var(--laara-fs-body-lg);
  font-weight: var(--laara-fw-black);
  line-height: 1;
}

/* ---------- Identity row ----------
   Three lines rather than one wrapping row: at 320px the role and the
   location cannot share a line, and a separator stranded at the end of a
   wrapped line reads as a mistake. */
.founder-identity { margin-top: var(--laara-space-5); }
.founder-identity-name {
  margin: 0;
  font-size: var(--laara-fs-h3);
  font-weight: var(--laara-fw-black);
  line-height: var(--laara-lh-tight);
  letter-spacing: -0.01em;
}
/* The brand's green full stop, set as the name's terminating mark. */
.founder-identity-name .laara-dot { margin-left: 0.14em; }
.founder-identity-role,
.founder-identity-place {
  margin: var(--laara-space-1) 0 0;
  color: var(--laara-text-secondary);
  font-size: var(--laara-fs-body);
  line-height: var(--laara-lh-tight);
}
.founder-identity-place {
  display: flex;
  align-items: center;
  gap: var(--laara-space-2);
}
.founder-identity-place svg { width: 16px; height: 16px; flex-shrink: 0; }

/* ---------- Story column ---------- */
.founder-body > h2 { margin: 0 0 var(--laara-space-4); }
.founder-lede {
  margin: 0 0 var(--laara-space-6);
  font-size: var(--laara-fs-body-lg);
  color: var(--laara-text-secondary);
}
.founder-copy { margin: 0 0 var(--laara-space-5); }
.founder-copy:last-of-type { margin-bottom: 0; }

.founder-quote {
  margin: var(--laara-space-8) 0 0;
  padding-left: var(--laara-space-6);
  border-left: 3px solid var(--laara-green);
}
.founder-quote p {
  margin: 0;
  max-width: 34rem;
  font-size: var(--laara-fs-h3);
  font-weight: var(--laara-fw-bold);
  line-height: var(--laara-lh-tight);
  color: var(--laara-text);
}

/* ---------- Credential band ---------- */
.founder-creds-band {
  margin-top: var(--laara-space-12);
  padding-top: var(--laara-space-10);
  border-top: 1.5px solid var(--laara-border);
}
.founder-creds-title { margin: 0 0 var(--laara-space-6); }
.founder-creds {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-4);
}
@media (min-width: 560px) {
  .founder-creds { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .founder-creds { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--laara-space-5); }
}

/* Overrides .laara-card's own padding/flex-direction — same specificity
   (0,1,0), later in the file, so source order decides. */
.founder-cred {
  padding: var(--laara-space-5);
  flex-direction: row;
  align-items: flex-start;
  gap: var(--laara-space-4);
}
.founder-cred .laara-dot { flex-shrink: 0; }

/* The -2px hover lift, its reduced-motion path and the touch tap state all
   come from .glow-card. That rule needs its doubled .glow-card.glow-card
   selector to beat .reveal.is-visible — see the note above the .glow-card
   block; these cards are .reveal too, so it applies here. */
.founder-cred-text { display: flex; flex-direction: column; gap: var(--laara-space-1); min-width: 0; }
.founder-cred-term {
  font-size: var(--laara-fs-body-lg);
  font-weight: var(--laara-fw-black);
  line-height: var(--laara-lh-tight);
  letter-spacing: -0.01em;
  color: var(--laara-text);
}
.founder-cred-desc {
  font-size: var(--laara-fs-body);
  line-height: var(--laara-lh-body);
  color: var(--laara-text-secondary);   /* 4.71:1 on the card's white */
}
@media (min-width: 1024px) {
  /* Icon above the text once four cards share the row — there is height
     to spend and no width to waste on a side-by-side icon. */
  .founder-cred { flex-direction: column; padding: var(--laara-space-6); }
  /* Two of the four terms wrap at this column width. Reserving two lines
     on all four keeps every description on the same baseline instead of
     stepping across the row. */
  .founder-cred-term { min-height: calc(2em * var(--laara-lh-tight)); }
}

.founder-cta { margin-top: var(--laara-space-10); }
/* Matches the .final-cta-actions treatment at the same width. */
@media (max-width: 480px) {
  .founder-cta .laara-btn { width: 100%; }
}

/* ---------- What we believe: alternating manifesto list ---------- */
.about-beliefs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--laara-space-8); }
.about-belief {
  display: flex;
  align-items: baseline;
  gap: var(--laara-space-5);
  border-top: 1.5px solid var(--laara-border);
  padding-top: var(--laara-space-8);
  max-width: 42rem;
}
.about-belief-number {
  font-weight: var(--laara-fw-black);
  font-size: clamp(2rem, 1.5rem + 2vw, 3rem);
  color: var(--laara-border);
  line-height: 1;
  flex-shrink: 0;
}
.about-belief p { margin: 0; font-size: var(--laara-fs-h3); font-weight: var(--laara-fw-bold); line-height: var(--laara-lh-heading); color: var(--laara-text); }
@media (min-width: 900px) {
  .about-belief--right { margin-left: auto; text-align: right; flex-direction: row-reverse; }
}

/* ---------- Small studio advantage: dark contrast pairs ---------- */
.about-compare-list { list-style: none; margin: 0; padding: 0; }
.about-compare-pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--laara-space-2);
  padding-block: var(--laara-space-6);
  border-top: 1.5px solid rgb(var(--laara-paper-rgb) / 0.14);
}
.about-compare-pair:last-child { border-bottom: 1.5px solid rgb(var(--laara-paper-rgb) / 0.14); }
.about-compare-them {
  color: var(--laara-on-dark-subtle);
  font-size: var(--laara-fs-body-lg);
  text-decoration: line-through;
  text-decoration-color: rgb(var(--laara-paper-rgb) / 0.3);
}
.about-compare-arrow { display: none; }
.about-compare-us { color: var(--laara-paper); font-size: var(--laara-fs-h3); font-weight: var(--laara-fw-bold); }
@media (min-width: 780px) {
  .about-compare-pair { grid-template-columns: 1fr auto 1.2fr; align-items: center; gap: var(--laara-space-6); }
  .about-compare-arrow { display: block; color: var(--laara-green); font-size: var(--laara-fs-h3); }
}

/* ============================================================
   Sticky mobile CTA — persistent call/WhatsApp bar
   Mobile-only, mirrors the header's own hamburger breakpoint
   (900px) rather than a separate cutoff. Opt out per page with
   body[data-no-sticky-cta] — used on Contact, where the form is
   already the page's CTA, and Thank-you, which is post-conversion.
   ============================================================ */
.sticky-mobile-cta {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 55;
  gap: var(--laara-space-3);
  padding: var(--laara-space-3) var(--laara-space-4);
  padding-bottom: calc(var(--laara-space-3) + env(safe-area-inset-bottom));
  background: var(--laara-bg-alt);
  border-top: 1.5px solid var(--laara-border);
  box-shadow: var(--laara-shadow-lg);
}
.sticky-mobile-cta-btn { flex: 1; gap: var(--laara-space-2); }
.sticky-mobile-cta-btn svg { width: 18px; height: 18px; flex-shrink: 0; }

@media (max-width: 899px) {
  .sticky-mobile-cta { display: flex; }
  body[data-no-sticky-cta] .sticky-mobile-cta { display: none; }
  /* Reserve room at the foot of the page so the fixed bar never ends up
     sitting over the last line of the footer once someone scrolls down.
     --laara-space-24, not -16: the redesigned footer adds the company line
     and "Back to top" below the legal row, so the last line sits lower and
     the old 64px reserve left it touching the sticky bar. */
  body:not([data-no-sticky-cta]) .site-footer {
    padding-bottom: calc(var(--laara-space-24) + env(safe-area-inset-bottom));
  }
}

/* ============================================================
   Legal / policy pages — plain sequential prose, not a card
   or grid component, so it gets its own narrow measure and
   heading rhythm rather than reusing a component class.
   ============================================================ */
.laara-container.laara-container--narrow { max-width: 40rem; }

.legal-content { padding-block: var(--laara-space-6) var(--laara-space-4); }
.legal-content h2 { margin-top: var(--laara-space-10); margin-bottom: var(--laara-space-3); }
.legal-content h2:first-child { margin-top: 0; }
.legal-content p, .legal-content ul { margin-bottom: var(--laara-space-4); max-width: none; }
.legal-content ul { padding-left: var(--laara-space-6); }
.legal-content li { margin-bottom: var(--laara-space-2); }
.legal-content a { text-decoration: underline; }

/* Sits under a form's submit button — reuses the wizard hint colour
   rather than --laara-text-grey, which fails AA on --laara-surface. */
.form-privacy-note {
  font-size: var(--laara-fs-small);
  color: var(--laara-text-secondary);
  margin-top: var(--laara-space-3);
}
.form-privacy-note a { color: inherit; }

/* ============================================================
   COOKIE CONSENT BANNER
   Injected by assets/js/analytics-consent.js — only when the
   visitor has made no choice yet, or when they reopen it from
   the footer "Cookie settings" button. GA4 (gtag.js) loads
   only after "Accept"; see that file's header comment.
   ============================================================ */
.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60; /* above the sticky mobile CTA (55), below the mobile nav (200) */
  background: var(--laara-bg-inverse);
  color: var(--laara-text-inverse);
  border-top: 1.5px solid rgb(var(--laara-paper-rgb) / 0.15);
  box-shadow: var(--laara-shadow-lg);
  padding: var(--laara-space-4);
  padding-bottom: calc(var(--laara-space-4) + env(safe-area-inset-bottom));
}
.consent-banner-inner {
  max-width: 60rem;
  margin: 0 auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--laara-space-4) var(--laara-space-6);
}
.consent-banner-text {
  flex: 1 1 20rem;
  margin: 0;
  font-size: var(--laara-fs-small);
  line-height: 1.6;
  color: var(--laara-text-inverse);
}
.consent-banner-text a {
  color: var(--laara-link-on-dark); /* green on ink — 7.20:1 */
  text-decoration: underline;
}
.consent-banner-actions {
  display: flex;
  gap: var(--laara-space-3);
  flex-shrink: 0;
}
.consent-banner-actions .laara-btn { flex: 1 1 auto; }
@media (max-width: 479px) {
  .consent-banner-actions { width: 100%; }
}

/* The banner isn't inside a .laara-inverse wrapper, so bring the outline
   (ink by default, invisible here) and the outline-style secondary button
   in line with the dark-surface treatment used elsewhere. */
.consent-banner :focus-visible { outline-color: var(--laara-focus-ring-dark); }
.consent-banner .laara-btn--secondary {
  color: var(--laara-text-inverse);
  border-color: var(--laara-text-inverse);
}
.consent-banner .laara-btn--secondary:hover {
  background: var(--laara-text-inverse);
  color: var(--laara-ink);
}

@media (prefers-reduced-motion: no-preference) {
  .consent-banner {
    transform: translateY(100%);
    transition: transform var(--laara-dur-section) var(--laara-ease-out);
  }
  .consent-banner[data-visible] { transform: translateY(0); }
}

/* Footer "Cookie settings" trigger — visually matches the adjacent
   "Privacy policy" link. Hidden with no JS, since it can do nothing then
   (same reveal-gate pattern as the rest of the site's JS-only affordances).
   Colour is the on-dark link, not `--laara-link` (#0D7F4A) — that on-light
   green fails AA at ~3.9:1 on the ink footer. This rule keeps sole ownership
   of the button's reset and visibility; the FOOTER section styles the shared
   `.footer-bottom a` siblings but deliberately does not touch `display` here. */
.footer-cookie-btn {
  display: none;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--laara-text-inverse);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}
.footer-cookie-btn:hover,
.footer-cookie-btn:focus-visible { color: var(--laara-link-on-dark); }
.js .footer-cookie-btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--laara-touch-min);
}


/* ============================================================
   BOOK A CALL — book-a-call.html + assets/js/booking.js
   Two steps: pick a day and time, then your details. Reuses the
   quote wizard's step, field, error and control classes; only
   the layout, calendar, time list and aside are new here.
   ============================================================ */
.booking-layout { display: grid; gap: var(--laara-space-8); align-items: start; }
@media (min-width: 1080px) {
  .booking-layout { grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--laara-space-10); }
}

.booking-card {
  padding: var(--laara-space-5);
  box-shadow: var(--laara-shadow);
  scroll-margin-top: calc(var(--laara-header-h-desktop) + var(--laara-space-4));
}
@media (min-width: 768px) {
  .booking-card { padding: var(--laara-space-8); }
}

/* No JS → the direct-contact block; JS → the widget. Same .js gate as
   the site's reveal primitives, so the page is never left empty. */
.booking-form { display: none; }
.js .booking-form { display: block; }
/* Same guard as .quote-wizard-section: booking.js toggles `hidden` on
   elements whose own display rule (grid/flex) would otherwise beat it. */
.booking-section [hidden] { display: none !important; }
.js .booking-nojs { display: none; }
.booking-nojs h2 { margin: 0 0 var(--laara-space-3); font-size: var(--laara-fs-h3); }
.booking-nojs p { color: var(--laara-text-secondary); margin: 0 0 var(--laara-space-6); }
.booking-botcheck { display: none; }

.booking-form fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.booking-form legend { padding: 0; margin-bottom: var(--laara-space-2); }
.booking-form .wizard-step h2 { margin: 0 0 var(--laara-space-6); font-size: var(--laara-fs-h3); }
.booking-form [data-step-heading]:focus:not(:focus-visible) { outline: none; }

.booking-contact-actions { display: flex; flex-wrap: wrap; gap: var(--laara-space-3); }
.laara-card .booking-contact-actions .laara-btn { margin-top: 0; }
@media (max-width: 559px) {
  .laara-card .booking-contact-actions .laara-btn { flex: 1 1 100%; }
}

.booking-unavailable {
  padding: var(--laara-space-6);
  background: var(--laara-surface);
  border-radius: var(--laara-radius-card);
}
.booking-unavailable > p:first-child { margin: 0 0 var(--laara-space-5); font-weight: var(--laara-fw-semibold); }
.booking-unavailable-alt { margin: var(--laara-space-5) 0 0; color: var(--laara-text-secondary); }

/* ---------- Step 1: calendar beside times on wide cards ---------- */
.booking-pick { display: grid; gap: var(--laara-space-8); }
@media (min-width: 900px) {
  .booking-pick { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: var(--laara-space-10); }
}

/* A rolling view (this week → last bookable week), not a month grid:
   the ~3-week window would otherwise hide next month behind an arrow. */
.booking-cal-month { margin: 0 0 var(--laara-space-4); font-size: var(--laara-fs-body-lg); font-weight: var(--laara-fw-bold); }

/* --booking-cols comes from booking.js: only weekdays that ever open get a
   column, so Mon–Fri hours give a 5-wide grid and ≥44px days at 360px. */
.booking-cal-weekdays,
.booking-cal-grid {
  display: grid;
  grid-template-columns: repeat(var(--booking-cols, 5), minmax(0, 1fr));
  gap: var(--laara-space-2);
}
.booking-cal-weekdays span {
  padding-bottom: var(--laara-space-2);
  text-align: center;
  font-size: var(--laara-fs-small);
  font-weight: var(--laara-fw-bold);
  color: var(--laara-text-secondary);
}

.booking-day {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.1;
  min-height: var(--laara-control-h);
  padding: 0;
  border: 1.5px solid transparent;
  border-radius: var(--laara-radius);
  /* Free days carry a green tint so they read as bookable at a glance;
     the number is --laara-green-deep for contrast on the tint. */
  background: rgb(var(--laara-green-rgb) / 0.12);
  color: var(--laara-green-deep);
  font-family: var(--laara-font);
  font-size: var(--laara-fs-body);
  font-weight: var(--laara-fw-bold);
  cursor: pointer;
  transition: background var(--laara-dur-micro) var(--laara-ease-in-out),
              color var(--laara-dur-micro) var(--laara-ease-in-out),
              border-color var(--laara-dur-micro) var(--laara-ease-in-out),
              transform var(--laara-dur-micro) var(--laara-ease-out);
}
.booking-day:hover:not(:disabled) { border-color: var(--laara-ink); }
.booking-day:active:not(:disabled) { transform: scale(0.95); }
.booking-day:focus-visible { outline: 3px solid var(--laara-focus-ring); outline-offset: 2px; }
.booking-day[aria-pressed="true"] { background: var(--laara-ink); border-color: var(--laara-ink); color: var(--laara-paper); }
.booking-day:disabled {
  background: transparent;
  color: var(--laara-muted-grey); /* inactive control: exempt from 1.4.3 */
  font-weight: var(--laara-fw-regular);
  cursor: default;
}
/* Today: the brand dot under the number. */
.booking-day.is-today::after {
  content: "";
  position: absolute;
  bottom: 6px;
  left: 50%;
  width: 5px;
  height: 5px;
  margin-left: -2.5px;
  border-radius: 50%;
  background: var(--laara-green);
}
/* "Oct" above the 1st of a new month. */
.booking-day-month { font-size: 0.6875rem; font-weight: var(--laara-fw-bold); letter-spacing: 0.04em; text-transform: uppercase; }
.booking-day--skeleton,
.booking-time--skeleton { background: var(--laara-surface); cursor: default; }
.booking-time--skeleton { display: block; width: 100%; min-height: var(--laara-control-h); border-radius: 999px; }

/* ---------- Step 1: times ---------- */
.booking-times h3 { margin: 0 0 var(--laara-space-1); font-size: var(--laara-fs-body-lg); }
.booking-tz-note { margin: 0 0 var(--laara-space-4); font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
/* 4.75rem still fits "12:30pm" and gives three columns at 360px wide
   (card inner ≈ 269px there: 3 × 76 + 2 gaps). */
.booking-time-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.75rem, 1fr));
  gap: var(--laara-space-3);
}
.booking-time-list.has-local { grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); }
.pill-toggle-btn.booking-time {
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-height: var(--laara-control-h);
  padding: var(--laara-space-2);
  font-size: var(--laara-fs-body);
  line-height: 1.2;
  white-space: nowrap;
  color: var(--laara-text);
}
.pill-toggle-btn.booking-time[aria-checked="true"] { color: var(--laara-paper); }
.booking-time-local { font-size: var(--laara-fs-small); font-weight: var(--laara-fw-regular); color: var(--laara-text-secondary); }
.booking-time[aria-checked="true"] .booking-time-local { color: var(--laara-on-dark-muted); }

/* Live region: announces the chosen day/time. Quiet unless it's an error. */
.booking-cal-status { min-height: 1.7em; }
.booking-cal-status:not([data-state]) { font-weight: var(--laara-fw-regular); color: var(--laara-text-secondary); }

@media (prefers-reduced-motion: no-preference) {
  .booking-day--skeleton,
  .booking-time--skeleton { animation: booking-pulse 1.4s var(--laara-ease-in-out) infinite alternate; }
}
@keyframes booking-pulse { from { opacity: 1; } to { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) {
  .booking-day { transition: none; }
  .booking-day:active:not(:disabled) { transform: none; }
}

/* ---------- Step 2: details ---------- */
.booking-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--laara-space-4);
  margin-bottom: var(--laara-space-8);
  padding: var(--laara-space-4) var(--laara-space-5);
  background: var(--laara-surface);
  border-left: 4px solid var(--laara-green);
  border-radius: var(--laara-radius-card);
}
.booking-summary > div { flex: 1 1 13rem; }
.booking-summary-label { margin: 0; font-size: var(--laara-fs-small); font-weight: var(--laara-fw-bold); color: var(--laara-text-secondary); }
.booking-summary-when { margin: 0; font-weight: var(--laara-fw-bold); }
/* The wizard's Edit link is 32px tall; here it is one of only two ways back
   to the calendar, so it gets a full touch target. */
.booking-summary .wizard-review-edit { flex-shrink: 0; min-height: var(--laara-touch-min); padding-inline: var(--laara-space-3); }

.booking-hint { margin: var(--laara-space-2) 0 0; font-size: var(--laara-fs-small); color: var(--laara-text-secondary); }
.booking-calltype .pill-toggle-btn,
.booking-source .pill-toggle-btn { font-size: var(--laara-fs-body); color: var(--laara-text); }
.booking-calltype .pill-toggle-btn[aria-checked="true"],
.booking-source .pill-toggle-btn[aria-checked="true"] { color: var(--laara-paper); }
.booking-turnstile { margin-bottom: var(--laara-space-4); }
.booking-turnstile:empty { display: none; }

/* ---------- Aside: what happens on the call (ink, floating plane) ---------- */
.booking-aside {
  padding: var(--laara-space-8);
  background: var(--laara-bg-inverse);
  color: var(--laara-text-inverse);
  border-radius: var(--laara-radius-lg);
  box-shadow: var(--laara-shadow-lg);
}
@media (min-width: 1080px) {
  .booking-aside { position: sticky; top: calc(var(--laara-header-h-desktop) + var(--laara-space-6)); }
}
.booking-aside h2 { margin: 0 0 var(--laara-space-6); font-size: var(--laara-fs-h3); color: inherit; }
.booking-expect { display: grid; gap: var(--laara-space-5); margin: 0; padding: 0; list-style: none; counter-reset: booking-expect; }
.booking-expect li {
  position: relative;
  padding-left: var(--laara-space-10);
  color: var(--laara-on-dark-muted);
  counter-increment: booking-expect;
}
.booking-expect li::before {
  content: counter(booking-expect, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0;
  font-weight: var(--laara-fw-black);
  color: var(--laara-link-on-dark); /* green on ink, 7.20:1 */
}
.booking-expect strong { display: block; color: var(--laara-text-inverse); }
.booking-aside-alt {
  margin: var(--laara-space-8) 0 0;
  padding-top: var(--laara-space-6);
  border-top: 1px solid rgb(var(--laara-paper-rgb) / 0.15);
  color: var(--laara-on-dark-muted);
}
.booking-aside a { color: var(--laara-link-on-dark); font-weight: var(--laara-fw-bold); white-space: nowrap; }
.booking-aside :focus-visible { outline-color: var(--laara-focus-ring-dark); }

/* ---------- Contact page: the prompt that links here ---------- */
.contact-book-call {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--laara-space-4) var(--laara-space-5);
  margin-bottom: var(--laara-space-8);
  padding: var(--laara-space-6);
  border-left: 4px solid var(--laara-green);
  box-shadow: var(--laara-shadow-sm);
}
.contact-book-call .laara-dot {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--laara-radius);
  background: var(--laara-ink);
}
.contact-book-call .laara-dot svg { width: 22px; height: 22px; color: var(--laara-green); }
.contact-book-call-text { flex: 1 1 16rem; }
.contact-book-call-text h2 { margin: 0 0 var(--laara-space-1); font-size: var(--laara-fs-body-lg); }
.contact-book-call-text p { margin: 0; color: var(--laara-text-secondary); }
.laara-card.contact-book-call .laara-btn { margin-top: 0; }
@media (max-width: 559px) {
  .laara-card.contact-book-call .laara-btn { flex: 1 1 100%; }
}
