/* ===== Design tokens ===== */
:root {
  --ink: #1A1C1E;
  --accent: #822149;
  --accent-hover: #641830;
  --accent-pressed: #50122c;
  --cream: #F5F3E7;
  --line: #D5D5D5;
  --field-bg: #FAFAF7;
  --placeholder: #737373;
  --disabled-text: #5F5F5F;
  --disabled-bg: #D9D9D9;
  --success: #247C46;
  --success-bg: #EDF7EF;
  --error: #BF2626;
  --error-bg: #FCF2F2;
  --img-bg: #E7E9EA;
  --sub-grey: #6C7371;
  --muted-on-dark: #A6A8AA;

  --radius-pill: 999px;
  --radius-card: 12px;
  --radius-field: 8px;

  --content-max: 1264px;
  --nav-max: 1728px;

  /* Helvetica/Arial sit ahead of the system UI fonts: if Roboto never loads
     (a self-contained build has no CDN), the fallback still reads as a
     neutral grotesque rather than jumping to a different-looking UI face. */
  font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
html { scroll-behavior: smooth; }
body {
  margin: 0;
  line-height: 1.5;
  color: var(--ink);
  background: #fff;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }


@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* ===== Focus ===== */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
/* Lightbox triggers are plain divs (tabindex + role=button, added by JS),
   so they aren't covered by the rule above and were showing the browser's
   raw default outline on click, which never cleared. :focus-visible here
   only shows the ring for real keyboard navigation, not mouse clicks. */
[data-gallery] { outline: none; }
[data-gallery]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.nav a:focus-visible,
.nav-link:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* ===== Header / Nav ===== */
.site-header {
  background: var(--ink);
  padding: 28px 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.logo {
  color: #fff;
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.01em;
}
.nav {
  display: flex;
  gap: 8px;
}
.nav-link {
  color: #fff;
  font-size: 14px;
  padding: 8px 18px;
  border-radius: var(--radius-pill);
  transition: background 0.15s ease;
}
.nav-link:hover { background: rgba(255,255,255,0.1); }
.nav-link.active {
  background: #fff;
  color: var(--ink);
  font-weight: 700;
}
.nav-link.active:hover { background: #fff; }

.menu-toggle {
  display: none;
  color: #fff;
  line-height: 1.5;
  padding: 8px;
  /* 40px square once padding is counted, so 50% is a true circle — keeps the
     pressed wash from rendering as a hard-cornered block. */
  border-radius: 50%;
}
/* The close X lives inside .mobile-menu, not on .menu-toggle. The overlay is
   position:fixed with z-index 100, so it covers the page header entirely —
   any icon left on the toggle would be painted over and never seen. */
.menu-close {
  color: #fff;
  line-height: 1.5;
  padding: 8px;
  margin-right: -8px;
  border-radius: 50%;
}
.menu-toggle svg,
.menu-close svg { display: block; fill: none; }

/* mobile nav overlay */
/* Stays display:flex at all times — `display` can't be transitioned, so the
   hidden state is carried by opacity + visibility instead. visibility (not
   just opacity:0) is what keeps the links out of the tab order and stops the
   invisible panel swallowing taps on the page behind it. */
.mobile-menu {
  display: flex;
  position: fixed;
  inset: 0;
  height: 100dvh;
  max-height: 100dvh;
  background: var(--ink);
  z-index: 100;
  flex-direction: column;
  padding: 20px;
  padding-bottom: max(20px, env(safe-area-inset-bottom));
  /* Expanded, the list is nine items — taller than a short phone viewport. */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  opacity: 0;
  visibility: hidden;
  /* Closing is a correction, so it's quicker than opening and the delay on
     visibility is what holds the panel around long enough to fade out. */
  transition: opacity 120ms ease-in, visibility 0s linear 120ms;
}
.mobile-menu.open {
  opacity: 1;
  visibility: visible;
  transition: opacity 180ms ease-out, visibility 0s linear 0s;
}
/* Top-level items arrive just behind the panel with a small rise. Targets
   direct children only — the case-study sublinks live one level down and
   must not animate individually, or they'd fade in while still collapsed.
   The logo and the close X deliberately aren't in here — the way out
   shouldn't be the last thing to appear. */
.mobile-menu-links > * {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 160ms ease-out, transform 160ms ease-out, background 120ms ease;
}
.mobile-menu.open .mobile-menu-links > * {
  opacity: 1;
  transform: translateY(0);
}
.mobile-menu.open .mobile-menu-links > *:nth-child(1) { transition-delay: 60ms; }
.mobile-menu.open .mobile-menu-links > *:nth-child(2) { transition-delay: 110ms; }
.mobile-menu.open .mobile-menu-links > *:nth-child(3) { transition-delay: 160ms; }
.mobile-menu .site-header { padding: 0 0 16px 0; }
/* The global reduced-motion rule only zeroes transition-duration, not
   transition-delay, so the panel would still linger for 120ms on close and
   the links would still be staggered. Zero both explicitly here. */
@media (prefers-reduced-motion: reduce) {
  .mobile-menu,
  .mobile-menu.open,
  .mobile-menu-links > *,
  .mobile-menu.open .mobile-menu-links > * { transition-delay: 0s !important; }
  .mobile-menu-links > * { opacity: 1; transform: none; }
  .menu-sublinks { transition: none !important; }
}
/* Stops the page behind the overlay scrolling under it. iOS Safari ignores
   overflow:hidden on body for touch, so pin it — main.js restores scrollY. */
body.menu-open {
  overflow: hidden;
  position: fixed;
  width: 100%;
  touch-action: none;
}
.mobile-menu-links {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 16px;
}
.mobile-menu-links a {
  color: #fff;
  font-size: 18px;
  padding: 16px;
  border-radius: 10px;
}
.mobile-menu-links a.active {
  background: #fff;
  color: var(--ink);
  font-weight: 700;
}

/* Expandable "Case-studies" group. It's a <button>, not a link, because it
   toggles rather than navigates — the index page is reachable via the logo
   in the overlay header. */
.menu-group-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 48px;
  color: #fff;
  font-size: 18px;
  padding: 0 16px;
  border-radius: 10px;
  text-align: left;
}
.menu-group-toggle .chev {
  flex-shrink: 0;
  transition: transform 200ms ease;
}
.menu-group-toggle[aria-expanded="true"] .chev { transform: rotate(180deg); }
/* Expanded reads as the selected section, so it takes the same white pill as
   an active link. The chevron uses stroke="currentColor", so it flips to ink
   along with the label. */
.menu-group-toggle[aria-expanded="true"] {
  background: #fff;
  color: var(--ink);
  font-weight: 700;
}

/* 0fr -> 1fr animates to the content's natural height, so there's no
   max-height guess to outgrow when a seventh case study gets added. */
.menu-sublinks {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 220ms ease;
}
.menu-group-toggle[aria-expanded="true"] + .menu-sublinks { grid-template-rows: 1fr; }
.menu-sublinks-inner {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 8px 0;
}
.menu-sublinks-inner a {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 16px 0 28px;
  font-size: 16px;
  color: #fff;
}
/* Divider is a pseudo-element parked in the middle of the 16px gap rather
   than a border on the row. A border would sit hard against the row's top
   edge, putting all the new space on one side of the line. Suppressed
   around the active pill — a hairline running into the white fill reads as
   a rendering artefact. */
.menu-sublinks-inner a + a::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -8px;
  height: 1px;
  background: rgba(255,255,255,0.15);
}
.menu-sublinks-inner a.active::before,
.menu-sublinks-inner a.active + a::before { display: none; }
.menu-sublinks-inner a.active {
  background: #fff;
  color: var(--ink);
  font-weight: 700;
}

@media (max-width: 860px) {
  .site-header { padding: 20px; }
  .nav { display: none; }
  .menu-toggle { display: block; }
}

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 28px;
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: 16px;
  transition: background 0.15s ease, transform 0.05s ease;
}
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { background: var(--accent-pressed); }
.btn-primary:disabled {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
}
.btn-secondary {
  background: transparent;
  color: var(--ink);
  border: 1.5px solid var(--ink);
}
.btn-secondary:hover { background: rgba(24,43,36,0.05); }
.btn-block { width: 100%; }

/* ===== Skill pills ===== */
.pills { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 24px; }
.pill {
  padding: 12px;
  border-radius: 20px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--cream);
}

/* ===== Sections / containers ===== */
.container {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 32px;
}
@media (max-width: 860px) { .container { padding: 0 20px; } }

/* ===== Case study page ===== */
.case-hero { padding: 64px 0 32px; }

.case-grid {
  display: grid;
  grid-template-columns: 727px 512px;
  gap: 25px;
  padding: 64px 0 88px;
  align-items: start;
  justify-content: center;
}
/* The 727/512 tracks are the real Figma widths and total 1264px — exactly the
   container's max-width, so above 1328px viewport the centred grid spans the
   full container and the 32px padding is absorbed. Below that the fixed tracks
   are wider than the content box and the page scrolls sideways, so switch to
   the same ratio in fr, which shrinks instead of overflowing. */
@media (max-width: 1327px) and (min-width: 901px) {
  .case-grid { grid-template-columns: minmax(0, 727fr) minmax(0, 512fr); }
}

.case-media { display: flex; flex-direction: column; gap: 40px; }
.case-content h1 { font-size: 40px; font-weight: 600; line-height: 1.3; margin: 0 0 8px; }
.case-content .subtitle { font-size: 20px; font-weight: 600; line-height: 1.5; color: var(--ink); margin: 0 0 24px; }

.case-body { padding-bottom: 80px; }
.case-body section,
.case-content section { margin-bottom: 16px; }
.case-content section:first-of-type { margin-top: 0; }
.case-body h2,
.case-content h2 { font-size: 18px; font-weight: 700; line-height: 1.5; margin: 0 0 16px; }
.case-body p,
.case-content p { font-size: 18px; line-height: 1.5; color: var(--ink); margin: 0 0 16px; }

/* ===== Mobile overrides (verified from the real mobile Figma frames,
   not scaled down from desktop — sizes, weights, and even pill shape
   genuinely differ at this breakpoint) ===== */

.key-results {
  background: #EDECE7;
  border-radius: 12px;
  padding: 23px 24px;
  max-width: 100%;
  margin-bottom: 32px;
}
/* "More case-studies" CTA. Lives inside .case-content so on desktop it sits
   in the text column and picks up its left edge automatically — no offset
   maths against the media column. 24px clear of the last paragraph; the
   page-bottom breathing room stays on .case-grid's padding. */
.case-more { padding-top: 24px; }
/* Desktop only. On desktop the button follows the last section inside the
   text column, so the trailing margin is zeroed to make the 24px exact.
   On mobile the images sit between the text and the button (order 4 vs 5),
   so zeroing here would collapse the gap before the first "Before" caption
   instead — the mobile block re-establishes it explicitly. */
@media (min-width: 901px) {
  .case-content section:last-of-type { margin-bottom: 0; }
  .case-content section:last-of-type > p:last-child { margin-bottom: 0; }
}
@media (max-width: 900px) {
  /* order 5 keeps it last, after the written sections (order 4). */
  .case-more { order: 5; padding-top: 24px; }
}

.key-results h3 { margin: 0 0 10px; font-size: 20px; font-weight: 700; line-height: 1.5; }
.key-results p { margin: 0 0 10px; font-size: 18px; font-weight: 400; line-height: 1.5; }
.key-results strong { font-weight: 500; }

/* image gallery / lightbox trigger */
.img-group { margin-bottom: 0; max-width: 100%; }
.img-caption { font-size: 20px; font-weight: 500; line-height: 1.5; color: var(--ink); margin-bottom: 16px; }

/* Desktop comparison: stacked Before/After, matching the real Figma layout
   pixel-for-pixel (that file's card is also 727px wide, so no scaling needed). */
.compare-stack {
  border-radius: 10px;
  overflow: hidden;
  padding: 20px 40px 24px;
}
.compare-stack img { display: block; width: 100%; }
.stack-label {
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}
.compare-stack .stack-label { margin-bottom: 4px; text-align: center; }
.compare-stack .stack-label:not(:first-child) { margin-top: 24px; }

/* Mobile comparison: two narrow columns side by side inside one shared box,
   label sits above each image, not below. */
.compare-side {
  border-radius: 10px;
  overflow: hidden;
  padding: 16px 0 16px;
  display: flex;
  justify-content: center;
  gap: 16px;
}
.compare-side-item { width: 212px; }
.compare-side-item img { display: block; width: 100%; }
.stack-label.center { text-align: center; margin-bottom: 8px; }

@media (max-width: 500px) {
  .compare-side { flex-direction: column; align-items: center; gap: 16px; }
  .compare-side-item { width: 80%; max-width: 260px; }
}
.thumb {
  position: relative;
  cursor: zoom-in;
  border-radius: 10px;
  overflow: hidden;
  background: #ffffff;
  aspect-ratio: 727 / 515;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.thumb img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; border-radius: 6px; }

/* Real per-project card backgrounds, verified against the homepage in Figma.
   Login and ZIP genuinely have no background there — plain white. */
.bg-travel { background: #e1ecee; }
.bg-credit-card { background: #dfe4ec; }
.bg-account-recovery { background: #ececcc; }
.bg-login { background: #dfe4ec; }
.bg-car-buying { background: #e1ecee; }
.bg-zip { background: #ececcc; }

/* Homepage cards span the full content width, so the 727:515 ratio above
   (measured for the narrower case-study media column) would blow the box
   out to ~895px tall here. Let it hug the image + padding instead. */
.card .thumb { aspect-ratio: auto; height: auto; min-height: 200px; }
.card .thumb img { max-height: 460px; }
.thumb .placeholder-label {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: #9aa; font-size: 13px;
}
.thumb-badge {
  position: absolute;
  bottom: 10px; right: 10px;
  background: rgba(24,43,36,0.75);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
}

/* ===== Lightbox ===== */
.lightbox {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  flex-direction: column;
  padding: 24px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}
.lightbox.open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.2s ease, visibility 0s linear 0s;
}
.lightbox-body { transform: scale(0.985); transition: transform 0.2s ease; }
.lightbox.open .lightbox-body { transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
  .lightbox { transition: opacity 0.01s linear, visibility 0s linear; }
  .lightbox-body,
  .lightbox.open .lightbox-body { transform: none; transition: none; }
}
.lightbox-close {
  position: absolute;
  bottom: 100%;
  right: 0;
  margin-bottom: 16px;
  color: #fff;
  background: rgba(255, 255, 255, 0.35);
  font-size: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.lightbox-body {
  display: flex; align-items: center; gap: 8px;
  max-width: min(1400px, 100%); width: 100%;
  justify-content: center;
}
.lightbox-arrow {
  color: #fff;
  background: rgba(255, 255, 255, 0.35);
  font-size: 24px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.lightbox-arrow:disabled { visibility: hidden; }
.lightbox-img-wrap {
  position: relative;
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border-radius: 12px;
  padding: 24px;
}
.lightbox-img-wrap img { border-radius: 4px; width: auto; height: auto; max-width: 100%; max-height: calc(100vh - 200px); object-fit: contain; }
.lightbox-meta { text-align: center; margin-top: 20px; color: #fff; }
.lightbox-title { font-weight: 600; font-size: 18px; margin-bottom: 10px; }
.lightbox-dots { display: flex; gap: 8px; justify-content: center; margin-bottom: 8px; }
.lightbox-dots span { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,0.35); }
.lightbox-dots span.active { background: #fff; }
.lightbox-counter { font-size: 13px; color: rgba(255,255,255,0.6); }

@media (max-width: 700px) {
  .lightbox { padding: 16px; }
  .lightbox-body { flex-direction: column; gap: 16px; }
  .lightbox-arrow { display: none; }
  .lightbox-img-wrap { width: 100%; }
  .lightbox-img-wrap img { max-height: calc(100vh - 240px); }
  .lightbox-meta { padding: 0 20px; }
}

/* ===== Form ===== */
.field { margin-bottom: 20px; }
.field label { display: block; font-size: 13px; color: #555; margin-bottom: 8px; }
.field input, .field textarea {
  width: 100%;
  font-size: 15px;
  font-family: inherit;
  padding: 14px 16px;
  border-radius: var(--radius-field);
  border: 1px solid var(--line);
  background: var(--field-bg);
  color: var(--ink);
}
.field input::placeholder, .field textarea::placeholder { color: var(--placeholder); }
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--accent);
}
.field.error input, .field.error textarea { border-color: var(--error); background: var(--error-bg); }
.field-error-text { color: var(--error); font-size: 12px; margin-top: 6px; }
.field input:disabled, .field textarea:disabled { background: #EDEDEB; color: var(--disabled-text); }

.form-status {
  border-radius: 10px;
  padding: 20px;
  max-width: 400px;
  display: none;
}
.form-status.show { display: block; }
.form-status.success { background: var(--success-bg); }
.form-status.success h4 { color: var(--success); margin: 0 0 6px; }
.form-status.error { background: var(--error-bg); }
.form-status.error h4 { color: var(--error); margin: 0 0 6px; }
.form-status p { margin: 0; color: #555; font-size: 14px; }

/* ===== Footer ===== */
.site-footer { background: var(--ink); color: #fff; padding: 88px 64px 40px; border-top: 1px solid rgba(255,255,255,0.08); }
.footer-cta { display: flex; align-items: center; justify-content: space-between; gap: 40px; flex-wrap: wrap; margin-bottom: 56px; }
.footer-cta h2 { font-size: 40px; margin: 0 0 12px; }
.footer-cta p { color: var(--muted-on-dark); margin: 0; font-size: 16px; max-width: 500px; }
.footer-email {
  background: #fff; color: var(--ink);
  padding: 18px 28px; border-radius: var(--radius-pill);
  font-weight: 700; white-space: nowrap;
}
.footer-email:hover { background: #f0f0f0; }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.18); padding-top: 24px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; }
.footer-bottom .copyright { font-size: 13px; color: var(--muted-on-dark); }
.footer-links { display: flex; gap: 32px; }
.footer-links a { font-size: 14px; }
.footer-links a:hover { text-decoration: underline; }

@media (max-width: 860px) {
  .site-footer { padding: 56px 20px 32px; }
  .footer-cta { flex-direction: column; align-items: flex-start; }
  .footer-email { width: 100%; text-align: center; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }
}

/* ===== Homepage ===== */
.hero { padding-top: 88px; padding-bottom: 56px; }
.hero h1 { font-size: 48px; line-height: 1.3; max-width: 100%; margin: 0; font-weight: 500; }

@media (max-width: 900px) {
  .hero { padding-top: 32px; padding-bottom: 40px; }
  .hero h1 { font-size: 28px; line-height: 1.3; padding-left: 0; }
}

.card-grid { display: flex; flex-direction: column; gap: 56px; padding-bottom: 88px; }
.card { display: block; }
.card .thumb { margin-bottom: 16px; }
.card-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 40px; }
.card-details { max-width: 746px; }
.card h3 { font-size: 28px; line-height: 1.3; margin: 0 0 6px; }
.card p { font-size: 18px; color: var(--sub-grey); margin: 0; white-space: nowrap; }
.card .btn { flex-shrink: 0; }

@media (max-width: 700px) {
  .card-row { flex-direction: column; align-items: stretch; gap: 16px; }
  .card .btn { width: 100%; }
}

/* ===== About page ===== */
.about-stack {
  max-width: 834px;
  margin: 0 auto;
  padding: 88px 0;
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.about-photo { border-radius: 12px; overflow: hidden; background: var(--img-bg); aspect-ratio: 834/600; }
.about-photo img { width: 100%; height: 100%; object-fit: cover; }
.about-intro h1 { font-size: 40px; margin: 0 0 16px; }
.about-subtitle { font-weight: 500; font-size: 22px; line-height: 1.5; margin: 0; }
.about-details p { font-size: 18px; line-height: 1.5; color: #333; margin: 0 0 20px; }
.about-details p:last-child { margin-bottom: 0; }
.about-contact h2 { font-size: 40px; margin: 0 0 8px; }
.about-contact .lede { color: var(--sub-grey); margin: 0 0 24px; font-size: 18px; }
.field-row { display: flex; gap: 24px; }
.field-row .field { flex: 1; }
@media (max-width: 700px) {
  .field-row { flex-direction: column; gap: 0; }
}

@media (max-width: 900px) {
  .about-stack { padding: 48px 0; gap: 32px; }
  .about-photo { aspect-ratio: 1/1; }
  .about-intro h1 { font-size: 30px; }
  .about-subtitle { font-size: 18px; }
  .about-details p { font-size: 16px; }
  .about-contact h2 { font-size: 28px; }
  .about-contact .lede { font-size: 16px; }
}

/* ===== Password gate ===== */
.gate-body { background: var(--ink); min-height: 100vh; display: flex; flex-direction: column; }
.gate-main { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 40px 24px; gap: 28px; }
.gate-lock { width: 48px; height: 48px; }
.gate-main h1 { color: #fff; font-size: 36px; margin: 0 0 12px; max-width: 560px; }
.gate-main .lede { color: var(--muted-on-dark); font-size: 17px; max-width: 440px; margin: 0; }
.gate-form { display: flex; gap: 12px; align-items: stretch; }
.gate-input-wrap { position: relative; width: 280px; }
.gate-form input {
  width: 100%; font-size: 15px; font-family: inherit;
  padding: 14px 44px 14px 16px; border-radius: var(--radius-field);
  border: 1px solid var(--line); background: var(--field-bg); color: var(--ink);
  transition: font-size 0.15s ease, letter-spacing 0.15s ease;
}
.gate-form input::placeholder { color: var(--placeholder); }
/* Masked password characters render as circular dots at a size tied to
   font-size. Doubling it once there's a value makes those dots read as
   twice as large while typing, without affecting the placeholder text. */
.gate-form input:not(:placeholder-shown)[type="password"] {
  font-size: 30px;
  letter-spacing: 2px;
}
.gate-eye {
  position: absolute;
  top: 50%; right: 14px; transform: translateY(-50%);
  color: var(--placeholder);
  display: flex; align-items: center; justify-content: center;
  padding: 4px;
}
.gate-eye:hover { color: var(--ink); }
.gate-error { color: #ffb4b4; font-size: 14px; display: none; }
.gate-error.show { display: block; }
.gate-fallback { color: #fff; font-size: 14px; text-decoration: underline; margin-top: -8px; }

@media (max-width: 560px) {
  .gate-form { flex-direction: column; width: 100%; max-width: 320px; }
  .gate-input-wrap { width: 100%; }
}

/* ===== Scroll reveal ===== */
.reveal {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.reveal-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none; }
}

/* ===== Mobile overrides (<=900px) =====
   MUST stay at the end of the file. These are all single-class selectors,
   so they tie on specificity with the base rules above and only win on
   source order. When this block sat higher up, every base rule declared
   after it silently overrode it (img-group margin, img-caption margin,
   compare-stack padding, key-results padding, card-grid gap all reverted). */
@media (max-width: 900px) {
  /* gap MUST stay 0 here. Every child below is a direct grid item (see
     display:contents), so any row-gap gets added on top of each element's
     own margin-bottom — that's what was doubling the spacing. Spacing at
     this breakpoint is owned entirely by the elements' margins. */
  .case-grid { grid-template-columns: 1fr; gap: 0; padding: 32px 0 56px; }
  /* Promote case-media's and case-content's own children to be direct grid
     items (display:contents keeps the DOM the same, no HTML edits needed),
     so images can be interleaved with title/results/body via order instead
     of moving as two whole blocks. */
  .case-media, .case-content { display: contents; }
  .case-content h1,
  .case-content .subtitle,
  .case-content .pills { order: 1; }
  .key-results { order: 2; }
  /* Images sit directly under the Key results box, ahead of the written
     sections. The label belongs to the image below it, so the gap between
     one Before/After block and the next must be clearly larger than the 8px
     between a caption and its own card — otherwise "After" reads as if it
     were captioning the image above. 40px vs 8px makes the pairing obvious,
     and the last block's 40px carries into "The Challenge". */
  .img-group { order: 3; margin-bottom: 40px; }
  .case-content section { order: 4; }
  /* Last section is followed by the CTA (order 5), so its trailing margins
     are zeroed to make .case-more's 24px the exact measured gap. */
  .case-content section:last-of-type { margin-bottom: 0; }
  .case-content section:last-of-type > p:last-child { margin-bottom: 0; }
  .case-content h1 { font-size: 26px; font-weight: 700; line-height: 1.3; margin: 0 0 16px; }
  .case-content .subtitle { font-size: 16px; font-weight: 600; line-height: 1.5; margin: 0 0 16px; }
  .case-body h2,
  .case-content h2 { font-size: 20px; font-weight: 700; line-height: 1.5; margin: 0 0 12px; }
  .case-body p,
  .case-content p { font-size: 16px; line-height: 1.5; margin: 0 0 14px; }
  .img-caption { font-size: 18px; line-height: 1.5; margin-bottom: 8px; }
  /* Same pairing logic for the travel page's shared Before/After card, and
     less horizontal inset so the screenshots aren't squeezed on a phone. */
  .compare-stack { padding: 16px 16px 20px; }
  .compare-stack .stack-label { margin-bottom: 8px; }
  .compare-stack .stack-label:not(:first-child) { margin-top: 32px; }
  .compare-side { padding: 16px 12px; }
  .pill { padding: 6px 14px; border-radius: 999px; font-size: 14px; line-height: 1.5; }
  .key-results { padding: 20px; margin-bottom: 40px; }
}

@media (max-width: 900px) {
  /* 40px between project cards on the homepage, and the same 56px tail as
     .case-grid uses — the base 88px was a desktop value left unscoped. */
  .card-grid { gap: 40px; padding-bottom: 56px; }
}

/* ===== Pressed states =====
   Kept at the end of the file: several of these tie on specificity with the
   base rules for the same elements, so source order decides. */
/* Mobile browsers paint a light-blue flash on tap by default, which doesn't
   belong to this palette. Suppress it and define the pressed states below
   in the site's own colours instead. */
a, button, [data-gallery], .card { -webkit-tap-highlight-color: transparent; }

/* On the dark green overlay, a translucent white wash reads as pressure
   without introducing a new hue. */
.menu-toggle:active,
.menu-close:active,
.menu-group-toggle:active,
.mobile-menu-links a:active { background: rgba(255,255,255,0.14); }
/* Items already filled white can't go lighter, so they press to the cream
   used by the Key results card. */
.mobile-menu-links a.active:active,
.menu-group-toggle[aria-expanded="true"]:active { background: #EDECE7; }
/* Sublinks only — the three top-level items get their background transition
   folded into the entrance rule instead, or this would outrank it and cost
   them their fade-and-rise. */
.menu-sublinks-inner a { transition: background 120ms ease; }

/* The thumbs carry gradient backgrounds, so a background-color press state
   would be painted over. Opacity works on top of any fill. */
.card .thumb, .card .thumb img { transition: opacity 120ms ease, filter 150ms ease; }
.card:active .thumb { opacity: 0.88; }
.nav-link:active { background: rgba(255,255,255,0.18); }
.btn-secondary:active { background: rgba(24,43,36,0.12); }


/* Case-study footer: next-case + all-case-studies pair */
.case-more { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
.case-more .btn { white-space: nowrap; }
@media (max-width: 900px) {
  .case-more { gap: 12px; flex-wrap: wrap; }
  .case-more .btn { padding: 14px 20px; font-size: 15px; }
}

/* Card data teaser */
.card .card-metric {
  margin: 8px 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
}

/* Card hover: colour shift only, no movement or scaling */
.card .btn { transition: background 150ms ease; }
.card:hover .btn-primary { background: var(--accent-hover); }
.card .thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  transition: opacity 150ms ease;
  pointer-events: none;
  z-index: 1;
}
.card:hover .thumb::after { opacity: 0.11; }
.card .thumb img { position: relative; z-index: 2; }
.card .thumb-badge { z-index: 3; }
@media (hover: none) {
  .card:hover .btn-primary { background: var(--accent); }
  .card:hover .thumb::after { opacity: 0; }
}

/* Count-up numbers keep a stable width so the line doesn't reflow mid-animation */
.count-up { font-variant-numeric: tabular-nums; }

/* Case-study media: let screenshots run the full column width, but never
   upscale past their natural pixel size (which would soften them). */
.case-media .thumb { padding: 40px; }
.case-media .thumb img {
  width: auto;
  max-width: 100%;
  max-height: none;
  image-rendering: auto;
}
.compare-stack { padding: 40px; }
.compare-stack img { width: auto; max-width: 100%; margin: 0 auto; }
.compare-side-item { width: auto; max-width: 100%; flex: 0 1 auto; }
.compare-side-item img { width: auto; max-width: 100%; }

/* Case-study screenshot cards: image is fully contained with an even 40px
   inset on every side, and never renders above its natural size. */
.case-media .thumb,
.img-group .thumb {
  box-sizing: border-box;
  padding: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  aspect-ratio: auto;
  height: auto;
}
.case-media .thumb img,
.img-group .thumb img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* Gallery removed: screenshots are static, not clickable. */
.thumb { cursor: default; }
.thumb-badge { display: none; }
#lightbox, .lightbox { display: none !important; }

/* Desktop: once the text column has been read through, its last element — the
   next/all buttons — pins below the header while the taller image column
   keeps scrolling. No background or shadow; they sit on the page as-is. */
@media (min-width: 901px) {
  .case-content { align-self: stretch; }
  .case-more { position: sticky; top: 0; }
}

/* Never upscale screenshots past their natural pixel size */
.case-media .thumb img,
.img-group .thumb img { width: auto; }
.case-media .thumb img { max-width: min(100%, 100%); }

/* Flat background behind case-study screenshots (no gradient) */
.case-media .thumb,
.img-group .thumb { background: #EDECE7 !important; }

/* Uniform homepage card thumbnail height */
.card .thumb {
  aspect-ratio: 1454 / 860;
  height: auto;
  min-height: 0;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
}
.card .thumb img { align-self: center; margin: auto; }
.card .thumb img { max-height: 100%; max-width: 100%; width: auto; height: auto; object-fit: contain; }

/* Homepage card previews sit 25% larger inside their thumb */
/* Credit Card preview: the source is very tall, so contain renders it tiny.
   Fill the padded box and crop from the top instead — no upscaling. */
.card .thumb.bg-credit-card,
.card .thumb.bg-car-buying { box-sizing: border-box; aspect-ratio: 1454 / 860; }

.card .thumb.bg-credit-card img {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  object-fit: cover;
  object-position: top center;
  align-self: center;
  margin: 0 auto;
}

/* Homepage card previews: even 24px inset on all four sides */
.card .thumb {
  padding: 24px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card .thumb img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  margin: 0 auto;
}
.card .thumb.bg-credit-card img,
.card .thumb.bg-car-buying img {
  width: 100%;
  height: auto;
  flex: 1 1 auto;
  min-height: 0;
  max-width: 100%;
  max-height: 100%;
  object-fit: cover;
  object-position: top center;
  margin: 0;
  align-self: stretch;
}

/* Result teaser sits on a light grey chip (12.4:1 against the burgundy) */
.card .card-metric {
  display: inline-block;
  background: #EDECE7;
  padding: 6px 12px;
  border-radius: 6px;
}

/* ===== Mobile: give previews real height and edge-to-edge width ===== */
@media (max-width: 700px) {
  /* Full-bleed cards so the screenshots aren't squeezed into a narrow column */
  .card .thumb {
    margin-left: -4px;
    margin-right: -4px;
    padding: 16px;
  }
  .card .thumb .card-metric { margin-bottom: 12px; }
  /* Landscape and square sources: show the whole frame, nothing cropped */
  .card .thumb img {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
  }
  /* Only the two full-page portrait shots are tall enough to crop from the top */
  .card .thumb.bg-credit-card,
  .card .thumb.bg-car-buying { aspect-ratio: 3 / 4; }
  .card .thumb.bg-credit-card img,
  .card .thumb.bg-car-buying img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
  }

  /* Case-study screenshots get the same full-bleed treatment */
}

/* ZIP preview reads small on phones — show it 25% larger there (native 659px) */
@media (max-width: 700px) {
  /* Match the card to the modal's own 659:532 ratio so the image fills the
     full width with only the 16px inset around it. */
  /* Larger modal without overflowing: match the card to the image's own
     ratio and tighten the inset, so the full modal still fits. */
  .card .thumb.bg-zip { aspect-ratio: 4 / 5; }
  .card .thumb.bg-zip img { width: 100%; max-width: 100%; height: auto; object-fit: contain; }
}

/* ===== Inner pages on phones =====
   The case screenshots are full-page exports (up to 1704×4096). At phone
   width a contained render is ~4000px tall, which buries the copy under an
   endless scroll. Cap the card height and crop from the top: the header and
   hero of each screenshot is the part that reads at this size, and the full
   version is one tap away on desktop. */
@media (max-width: 900px) {
  /* Full screenshots, never clipped — they're the evidence of the case study.
     Tall exports simply scroll with the page. */
  .case-media .thumb,
  .img-group .thumb {
    padding: 16px;
    box-sizing: border-box;
    max-height: none;
    overflow: visible;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .case-media .thumb img,
  .img-group .thumb img {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: none;
    object-fit: contain;
  }
  .compare-stack img { width: auto; max-width: 100%; height: auto; }

  .case-hero { padding: 32px 0 8px; }
  .case-more { width: 100%; }
  .case-more .btn { flex: 1 1 100%; width: 100%; justify-content: center; }
  .pills { gap: 8px; }
}

/* Caps at native width but still yields inside the padded card on phones */
.img-group .thumb img.img-native-375 { width: auto; max-width: min(100%, 375px); height: auto; }
.img-group .thumb img.img-native-659 { width: auto; max-width: min(100%, 659px); height: auto; }

/* Project sublinks read as a level below "Case-studies": cream fill instead
   of white, so the parent stays the brighter of the two. */
/* Active project: pill outline instead of a fill, so it reads as a different
   kind of item than the solid white "Case-studies" parent. */
.menu-sublinks-inner a.active {
  background: transparent;
  color: #fff;
  border: 1.5px solid #fff;
  border-radius: 10px;
}
.menu-sublinks-inner a.active:active { background: rgba(255,255,255,0.14); }
.mobile-menu-links a.active:active { background: #EDECE7; }

/* Footer supporting copy: the invitation carries real weight, the location
   line stays a quiet footnote under it. */
.footer-cta p {
  max-width: 620px;
  font-size: 20px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--muted-on-dark);
  opacity: 1;
  text-wrap: pretty;
}
.footer-cta .footer-meta {
  margin-top: 14px;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--muted-on-dark);
}
@media (max-width: 860px) {
  .footer-cta p { font-size: 18px; }
}

/* Result teaser as an overlay chip on the preview, 16px in from the corner */
.card .thumb .card-metric {
  position: static;
  order: -1;
  align-self: flex-start;
  flex: 0 0 auto;
  margin-bottom: 16px;
  box-shadow: none;
  margin: 0;
  display: inline-block;
  background: #fff;
  color: var(--accent);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.5;
  padding: 6px 12px;
  border-radius: 6px;
}

/* Clear the metric chip: the preview starts below it, never behind it.
   The taller card box (1454/860) absorbs this band without shrinking previews.
   Scoped to wide screens — the mobile block sets its own smaller band, and a
   two-class selector here would outrank it (media queries add no specificity). */


/* ===== Mobile title specs on case pages, normalised =====
   Every heading level had drifted: image captions, Before/After labels and the
   Key results heading were each set independently. One scale for all of them. */
@media (max-width: 900px) {
  .case-content h1 { font-size: 28px; font-weight: 700; line-height: 1.3; margin: 0 0 16px; }
  .case-content .subtitle { font-size: 18px; font-weight: 600; line-height: 1.5; margin: 0 0 16px; }
  .key-results h3,
  .case-body h2,
  .case-content h2 { font-size: 20px; font-weight: 700; line-height: 1.5; margin: 0 0 12px; }
  /* Caption-level labels: image captions and Before/After share one spec.
     The two-class selectors below are needed to outrank .compare-stack
     .stack-label / .compare-side .stack-label, which set their own margins. */
  .img-group .img-caption,
  .compare-stack .stack-label,
  .compare-side .stack-label,
  .compare-side .stack-label.center,
  .img-caption,
  .stack-label,
  .stack-label.center {
    font-size: 18px;
    font-weight: 600;
    line-height: 1.5;
    color: var(--ink);
    margin-bottom: 8px;
  }
  .key-results p { font-size: 16px; line-height: 1.5; margin: 0 0 10px; }
}

/* ===== iPhone-class screens =====
   At 414px the 4/3 card minus the 76px chip band left only ~210px of content
   height, which squeezed the portrait screenshots back down to thumbnails.
   Taller boxes here, and the chip band scales with the smaller type. */
@media (max-width: 700px) {
  .card .thumb,
  .card .thumb.bg-credit-card,
  .card .thumb.bg-car-buying,
  .card .thumb.bg-zip {
    padding: 16px;
    aspect-ratio: 3 / 4;
  }
  .card .thumb .card-metric {
    left: 16px;
    top: 16px;
    font-size: 14px;
    padding: 5px 10px;
  }
  /* Landscape sources don't need a portrait box — they'd letterbox badly. */
  .card .thumb.bg-login,
  .card .thumb.bg-travel { aspect-ratio: 1 / 1; }
  .card .thumb.bg-zip { aspect-ratio: 4 / 5; }

  /* Tap targets and rhythm */
  .card .btn { width: 100%; justify-content: center; }
  .card-row { flex-direction: column; align-items: stretch; gap: 16px; }

  /* Nothing should be able to push the page sideways on a narrow screen. */
  html, body { overflow-x: hidden; }
  .container { overflow-x: clip; }
}

/* ===== Very tall screenshot strips =====
   Two case images are full mobile-strip exports (375x4755 and 375x1720). Shown
   full-bleed they are several uninterrupted screens of scroll inside one card.
   Bound them and make the rest reachable deliberately — never a silent crop. */
.thumb.is-bounded {
  position: relative;
  max-height: 72vh;
  overflow: hidden;
  align-items: flex-start;
}
.thumb.is-bounded::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 96px;
  background: linear-gradient(to bottom, rgba(237,236,231,0), #EDECE7 88%);
  pointer-events: none;
  z-index: 3;
}
.thumb.is-bounded.is-expanded { max-height: none; }
.thumb.is-bounded.is-expanded::after { display: none; }
.thumb-expand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding: 12px 20px;
  min-height: 44px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1.5px solid var(--accent);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.thumb-expand:hover { background: var(--accent); color: #fff; }
.thumb-expand:active { background: var(--accent-hover); color: #fff; }
@media (max-width: 700px) {
  .thumb.is-bounded { max-height: 78vh; }
  .thumb-expand { width: 100%; justify-content: center; }
}

/* ===== Homepage card layout, final word =====
   Several earlier blocks set display/align/justify on .card .thumb at the same
   specificity, so this authoritative block sits last: chip on top in normal
   flow (never overlapping), image centred in the space that remains. */
.card .thumb {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  box-sizing: border-box;
}
.card .thumb .card-metric {
  order: -1;
  position: relative;
  z-index: 5;
  align-self: flex-start;
  flex: 0 0 auto;
  margin: 0 0 16px;
  box-shadow: none;
}
.card .thumb img {
  flex: 1 1 auto;
  min-height: 0;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  margin: 0 auto;
  align-self: center;
}
.card .thumb.bg-credit-card img,
.card .thumb.bg-car-buying img {
  width: 100%;
  align-self: stretch;
  object-fit: cover;
  object-position: top center;
}
@media (max-width: 700px) {
  .card .thumb .card-metric { margin-bottom: 12px; }
}

/* Required-field errors under name/email/message */
.field-error {
  display: block;
  margin-top: 6px;
  font-size: 13px;
  color: #C0392B;
}
