:root {
  /* Light theme — the site's real surface once the hero clip is behind you. */
  --bg: transparent;
  --surface: transparent;
  --ink: #0b0f1a;
  --ink-soft: #3d4351;

  /* Dark tokens — used only by the nav and the hero, which sit over the clip. */
  --white: #ffffff;
  --text-soft: #eef0f4;
  --text-muted: #9aa3b8;
  --line: rgba(255, 255, 255, 0.1);
  --glass: rgba(10, 13, 22, 0.5);

  /* Motion vocabulary. Three curves, each with one job, so the whole site
     decelerates the Franklyne way instead of mixing five ad-hoc easings.
     --ease-entrance: things arriving from off-screen or from nothing. Long
       tail, no overshoot — settles rather than bounces.
     --ease-swap: a state trading places with another (open/closed, on/off).
       Symmetric, so it reads the Franklyne played backwards.
     --ease-ui: short hover and focus feedback. Quick out, gentle in. */
  --ease-entrance: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-swap: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-ui: cubic-bezier(0.4, 0, 0.2, 1);

  /* One family for the whole site. Hierarchy comes from weight and size,
     not from switching typeface. Repoint this single token to swap in
     licensed Aeonik. */
  --font-sans: 'General Sans', 'Aeonik', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* The wash used on display type. Bright at the head of the line, cooling
     toward the tail — the reference reads left-to-right, so the gradient
     does too. Two variants: one for dark grounds, one for light. */
  --text-wash-dark: linear-gradient(100deg, #ffffff 0%, #e8eef5 30%, #93a6bd 78%, #6f8299 100%);
  --text-wash-light: linear-gradient(100deg, #0b0f1a 0%, #26313f 32%, #51657c 76%, #6d8299 100%);

  /* The near-black used by the darkened bands and the closing wordmark. */
  --ink-deep: #0b0e13;
}

/* Everything after the hero re-points the shared tokens at their light values,
   so the section CSS below needs no per-property dark/light branching. */
.site-light {
  --white: #0b0f1a;
  --text-muted: #656c7a;
  --line: rgba(0, 0, 0, 0.09);
  /* Opaque from here down. These were transparent so the fixed canvas showed
     through the whole page; now the clip belongs to the hero alone and
     everything past it stands on its own white ground.
     --surface stays a shade off-white so the service cards still read as cards
     before they light up. */
  --bg: #ffffff;
  --surface: #fafbfc;
  position: relative;
  z-index: 2;
  background: var(--bg);
  color: var(--ink);
}

* {
  box-sizing: border-box;
}

/* The page is served with viewport-fit=cover so content reaches into the
   status-bar and home-indicator strips instead of the browser letterboxing
   them into flat bands.
   Two things have to hold for that to work on iOS:
   1. The root must not be a scroll container. `overflow-x: hidden` on <html>
      makes it one, and Safari then refuses to extend the page into the safe
      areas — which is what produced the white caps top and bottom. The
      clipping moves to <body>, as `clip` so it doesn't create a scroll
      container of its own either.
   2. Whatever the strips do fall back to must not be white, so the root
      carries a colour. script.js keeps it and <meta name="theme-color"> in
      step with the section actually against the edge, so the browser's own
      chrome tints to match instead of cutting a visible line. */
html {
  background: #0b0e13;
  /* Belt and braces: iOS uses this for the rubber-band area past either end. */
  overscroll-behavior-y: none;
}

html,
body {
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  color: var(--ink);
}

body {
  background: transparent;
  overflow-x: hidden;
  overflow-x: clip;
}

a {
  cursor: pointer;
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid #6ea8fe;
  outline-offset: 3px;
}

/* ================= SCROLL CANVAS — fullscreen fixed background ================= */
#scroll-canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  z-index: 0;
  display: block;
  transform-origin: center center;
  /* Opacity is driven per-frame from the scroll position, so it must not be
     transitioned here or the two fight each other. */
  transition:
    transform 1s var(--ease-entrance),
    filter 0.75s var(--ease-ui);
}

/* The clip pulls back and defocuses as the white page takes over. The fade
   itself is applied inline from the scroll handler; this is the finishing
   flourish, toggled at the same boundary that flips the nav theme so the two
   can never disagree about where the hero ends. */
#scroll-canvas.canvas-out {
  transform: scale(1.07);
  filter: blur(10px);
}

/* ================= SCROLL SPACER — gives room for the animation to play =================
   Must stay transparent: any background here paints over the fixed canvas
   and hides the clip entirely. */
.hero-scroll-spacer {
  position: relative;
}

/* ================= NAV — frosted glass / blurred background ================= */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 56px;
  background: var(--glass);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--line);
  transition: background 0.35s var(--ease-ui), border-color 0.35s var(--ease-ui);
}

/* Past the hero the nav sits on white, so it inverts to match. */
.nav.on-light {
  background: rgba(255, 255, 255, 0.72);
  border-bottom-color: rgba(0, 0, 0, 0.09);
}

.nav.on-light .logo {
  color: var(--ink);
}

.nav.on-light .nav-links a {
  color: rgba(11, 15, 26, 0.72);
}

.nav.on-light .nav-links a:hover {
  color: var(--ink);
}

.nav.on-light .nav-links a::after {
  background: rgba(11, 15, 26, 0.06);
  border-color: rgba(11, 15, 26, 0.09);
}

.nav.on-light .nav-links .has-dot::before {
  background: rgba(11, 15, 26, 0.55);
}

.nav.on-light .btn-nav-schedule {
  background: var(--ink);
  color: #fff;
}

.nav.on-light .nav-toggle {
  color: var(--ink);
}

.logo {
  font-family: var(--font-sans);
  font-size: 23px;
  font-weight: 500;
  color: var(--white);
}

.nav-links {
  display: flex;
  align-items: center;
  /* The hover pill carries its own 16px of horizontal padding, so this gap
     is only the breathing room *between* two pills. */
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
  font-size: 14.5px;
  font-weight: 500;
  transition: color 0.35s var(--ease-swap);
}

/* The hover pill. It is clipped to an ellipse pinned to the link's right
   edge, so growing that ellipse's horizontal radius sweeps the fill
   leftwards behind a curved leading edge — the middle of the pill fills
   before its top and bottom corners do.
   ry tunes the bow: larger flattens it toward a straight wipe, smaller
   rounds it into a circle. Uses ::after because ::before is the .has-dot
   status dot. */
.nav-links a::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.16);
  clip-path: ellipse(0% 88% at 100% 50%);
  transition: clip-path 0.55s var(--ease-swap);
}

.nav-links a:hover::after,
.nav-links a:focus-visible::after {
  clip-path: ellipse(150% 88% at 100% 50%);
}

.nav-links a:hover {
  color: #fff;
}

.nav-links svg {
  width: 10px;
  height: 10px;
  opacity: 0.7;
}

.nav-links .has-dot::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.6);
}

.btn-nav-schedule {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--white);
  color: #0b0f1a;
  font-size: 14px;
  font-weight: 600;
  padding: 13px 20px;
  border-radius: 999px;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.2s var(--ease-ui), transform 0.2s var(--ease-ui);
}

.btn-nav-schedule:hover {
  opacity: 0.85;
}

.btn-nav-schedule svg {
  width: 13px;
  height: 13px;
}

/* The toggle reads as a word rather than an icon: "Menu" / "Close", with a
   live green dot to its right. */
.nav-toggle {
  display: none;
  align-items: center;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 2px;
  font-family: var(--font-sans);
  font-size: 14.5px;
  font-weight: 500;
  color: #fff;
  letter-spacing: 0.1px;
  /* Depth for the barrel roll below — without it the rotateX reads as a
     vertical squash rather than something turning over. */
  perspective: 420px;
}

/* The roll is applied to this inner span rather than the button so the
   button's own hit area never rotates out from under the tap. */
.nav-toggle-face {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  transform-style: preserve-3d;
}

.nav-toggle.rolling .nav-toggle-face {
  animation: nav-barrel-roll 0.5s var(--ease-swap);
}

/* The same arc the wordmark travels, not a spin: tipped back and settling
   level. -95deg is exactly what .logo rolls in from, so when the two fire
   together they read as one movement. */
@keyframes nav-barrel-roll {
  from {
    transform: rotateX(-95deg);
  }

  to {
    transform: rotateX(0deg);
  }
}

/* Three-line mark. Hidden by default — the bar at the hero uses the dot and
   the word, and only the stripped-back state past the hero swaps to this. */
.nav-toggle-icon {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 26px;
  height: 18px;
}

.nav-toggle-icon i {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.35s var(--ease-swap), opacity 0.25s linear;
}

.nav-toggle-icon i:nth-child(1) {
  width: 100%;
}

.nav-toggle-icon i:nth-child(2) {
  width: 100%;
}

.nav-toggle-icon i:nth-child(3) {
  width: 62%;
}

.nav-toggle-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #22c55e;
  box-shadow: 0 0 8px rgba(34, 197, 94, 0.9);
  animation: dot-blink 1.4s ease-in-out infinite;
}

@keyframes dot-blink {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.15;
  }
}

/* ---- mobile menu ----
   The panel is transparent while the wipe canvas underneath paints the
   black. Once the wipe lands, the panel takes a solid background of its own
   and the canvas stops rendering — Franklyne handoff Hello Monday does in
   makeMenuBackgroundSolid(). */
.mobile-menu {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 190;
  background: transparent;
  padding: 104px 28px 40px;
  flex-direction: column;
  justify-content: center;
  /* Centred rather than stretched, so each link shrinks to its own text and
     the hover pill hugs the word instead of spanning the viewport. */
  align-items: center;
  text-align: center;
  gap: 2px;
  overflow-y: auto;
}

.mobile-menu.open {
  display: flex;
}

.mobile-menu.solid {
  background: #08090f;
}

.mobile-menu a {
  position: relative;
  isolation: isolate;
  font-family: var(--font-sans);
  /* Matches .logo, which holds at 23px everywhere the hamburger exists — the
     19px override is desktop-only, where this menu never opens. Flush with the
     wordmark still showing at the top of the open menu. */
  font-size: 23px;
  font-weight: 400;
  line-height: 1.35;
  padding: 8px 24px;
  border-radius: 999px;
  color: #fff;
  text-decoration: none;
  /* The open animation writes transform/opacity per item; 0 keeps each one
     hidden until its tween starts. */
  opacity: 0;
  will-change: transform, opacity;
}

/* Franklyne curved wipe as the desktop links: an ellipse pinned to the right edge
   whose horizontal radius grows across the pill. :active and :focus-visible
   are included because a touch screen has no hover to give. The CTA is
   excluded — it already has a solid fill, which would hide the pill. */
.mobile-menu a:not(.mobile-cta)::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.16);
  clip-path: ellipse(0% 88% at 100% 50%);
  transition: clip-path 0.55s var(--ease-swap);
}

.mobile-menu a:not(.mobile-cta):hover::after,
.mobile-menu a:not(.mobile-cta):focus-visible::after,
.mobile-menu a:not(.mobile-cta):active::after {
  clip-path: ellipse(150% 88% at 100% 50%);
}

.mobile-menu .mobile-cta {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  line-height: normal;
  align-self: center;
  margin-top: 30px;
  background: #fff;
  color: #0b0f1a;
}

/* The wipe canvas: under the panel, over the page. */
#menu-wipe {
  position: fixed;
  inset: 0;
  z-index: 180;
  display: none;
  pointer-events: none;
}

/* With the menu open the bar is just the logo and the close button sitting
   on the black — no glass, no rule. */
.nav.menu-open,
.nav.menu-open.on-light {
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.nav.menu-open .logo,
.nav.menu-open.on-light .logo {
  color: #fff;
}

.nav.menu-open .nav-toggle,
.nav.menu-open.on-light .nav-toggle {
  color: #fff;
}

/* Open, the three lines fold into a cross. */
.nav.menu-open .nav-toggle-icon i:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav.menu-open .nav-toggle-icon i:nth-child(2) {
  transform: rotate(-45deg);
}

.nav.menu-open .nav-toggle-icon i:nth-child(3) {
  opacity: 0;
  transform: scaleX(0);
}

/* ================= MOBILE / TABLET NAV =================
   The bar belongs to the hero. Past it the whole chrome — glass, rule and
   wordmark — drops away and the mark is all that is left, so nothing sits on
   top of the white page. The wordmark returns only while the menu is open,
   rolling in on the same axis the toggle turns on. */
@media (max-width: 1024px) {

  /* viewport-fit=cover puts y=0 behind the status bar, so the bar has to
     carry the inset itself or the logo tucks under the clock. */
  .nav {
    padding-top: calc(16px + env(safe-area-inset-top));
  }

  .nav.on-light:not(.menu-open) {
    background: transparent;
    border-bottom-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .logo {
    transform-origin: 50% 0;
    transition: opacity 0.4s var(--ease-ui), transform 0.55s var(--ease-swap);
  }

  /* Rolled up and away: this is the resting state past the hero. */
  .nav.on-light:not(.menu-open) .logo {
    opacity: 0;
    transform: rotateX(-95deg) translateY(-8px);
    pointer-events: none;
  }

  .nav.menu-open .logo {
    opacity: 1;
    transform: rotateX(0deg) translateY(0);
  }

  /* Past the hero the word and the status dot give way to the mark. It stays
     while the menu is open so the cross has something to animate. */
  .nav.on-light .nav-toggle-icon {
    display: flex;
  }

  .nav.on-light .nav-toggle-dot,
  .nav.on-light #navToggleLabel {
    display: none;
  }

  /* Nothing behind the mark, so it needs its own contrast against the white
     page. Removed again once the menu paints the screen black. */
  .nav.on-light:not(.menu-open) .nav-toggle {
    color: var(--ink);
    filter: drop-shadow(0 1px 2px rgba(255, 255, 255, 0.9));
  }

  .floating-cta {
    bottom: calc(20px + env(safe-area-inset-bottom));
  }
}

@media (prefers-reduced-motion: reduce) {

  .nav-toggle.rolling .nav-toggle-face {
    animation: none;
  }

  .logo {
    transition: opacity 0.2s linear;
  }

  .nav.on-light:not(.menu-open) .logo {
    transform: none;
  }
}

/* ================= FLOATING CTA =================
   Ported from byFranklyn.com's own .floating-btn: the "beep" is a box-shadow
   ring that expands to 10px and fades, on a 1.8s ease loop. Sits below the
   menu layers (180/190) so an open mobile menu covers it — that menu carries
   its own call-to-action. */
.floating-cta {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 170;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-radius: 300px;
  background: #e8ecf0;
  color: #181a1d;
  text-decoration: none;
  animation: pulseShadow 1.8s ease infinite;
}

@keyframes pulseShadow {
  0% {
    box-shadow: 0 0 0 0 rgba(154, 175, 181, 0.7);
  }

  70% {
    box-shadow: 0 0 0 10px rgba(154, 175, 181, 0);
  }

  100% {
    box-shadow: 0 0 0 0 rgba(154, 175, 181, 0);
  }
}

.floating-cta-avatar {
  width: 44px;
  height: 44px;
  border-radius: 300px;
  object-fit: cover;
  flex-shrink: 0;
}

.floating-cta-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-right: 4px;
}

.floating-cta-text strong {
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
}

.floating-cta-text span {
  font-size: 14px;
  line-height: 1.3;
  color: rgba(24, 26, 29, 0.6);
}

.floating-cta-rule {
  width: 1px;
  height: 40px;
  background: rgba(24, 26, 29, 0.2);
  flex-shrink: 0;
}

.floating-cta-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  flex-shrink: 0;
  color: rgba(24, 26, 29, 0.75);
}

.floating-cta-icon svg {
  width: 17px;
  height: 17px;
}

@media (max-width: 420px) {
  .floating-cta {
    right: 14px;
    bottom: 14px;
    padding: 8px;
  }

  .floating-cta-avatar {
    width: 38px;
    height: 38px;
  }

  .floating-cta-text strong {
    font-size: 14.5px;
  }

  .floating-cta-text span {
    font-size: 12.5px;
  }
}

/* ================= HERO ================= */
/* The hero is pinned to exactly one viewport while the canvas animation scrubs.
   Nothing here may exceed that box, so every vertical value below is fluid:
   it collapses toward the low end of its clamp() on short screens instead of
   being cut off by the overflow:hidden above. */
.hero {
  position: sticky;
  top: 0;
  z-index: 2;
  overflow: hidden;
  height: 100vh;
  height: 100dvh;
  padding: clamp(92px, 13vh, 168px) 56px clamp(28px, 6vh, 100px);
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  /* The two columns were reading as one crowded block. Widening the gutter
     pushes the copy and the form apart toward their own edges. */
  gap: clamp(40px, 6.5vw, 104px);
  align-items: center;
}

.hero-left,
.hero-right {
  position: relative;
  z-index: 2;
  /* --fit is set by the fit-to-viewport script only when a column still
     overflows after the clamps have bottomed out. */
  transform: scale(var(--fit, 1));
  transform-origin: center center;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  padding: 6px 6px 6px 16px;
  border-radius: 999px;
  font-size: 13px;
  margin-bottom: clamp(16px, 3.4vh, 34px);
  color: #ffffff;
  opacity: 0;
  animation: fadeUp 0.8s var(--ease-entrance) forwards;
}

.status-badge .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #22c55e;
  box-shadow: 0 0 10px rgba(34, 197, 94, 0.8);
}

.status-badge .cta {
  background: rgba(255, 255, 255, 0.07);
  padding: 7px 14px;
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.65);
}

.hero-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(34px, 4vw, 54px);
  line-height: 1.18;
  letter-spacing: -0.037em;
  margin: 0 0 26px;
  color: var(--text-muted);
  opacity: 0;
  animation: fadeUp 0.8s var(--ease-entrance) 0.09s forwards;
}

.hero-title .line-soft {
  color: inherit;
}

.highlight-word {
  position: relative;
  display: inline-block;
  border: 1.5px solid rgba(255, 255, 255, 0.4);
  border-radius: 4px;
  padding: 0 12px;
  margin: 0 2px;
}

.highlight-word i {
  position: absolute;
  font-style: normal;
  font-size: 9px;
  color: rgba(255, 255, 255, 0.55);
  line-height: 1;
}

.highlight-word .tl {
  top: -6px;
  left: -6px;
}

.highlight-word .tr {
  top: -6px;
  right: -6px;
}

.highlight-word .bl {
  bottom: -6px;
  left: -6px;
}

.highlight-word .br {
  bottom: -6px;
  right: -6px;
}

.hero-desc {
  color: #ffffff;
  font-size: 16.5px;
  line-height: 1.75;
  max-width: 540px;
  margin: 0 0 36px;
  opacity: 0;
  animation: fadeUp 0.8s var(--ease-entrance) 0.18s forwards;
}

/* Backdrop plate behind the hero subtext. Desktop only — below 1025px the
   column is already narrow and the plate would crowd the copy against the
   viewport edge. Same fill and blur as the rotating START A PROJECT badge,
   so the two read as one material. */
@media (min-width: 1025px) {
  .hero-desc {
    max-width: 580px;
    padding: 22px 28px;
    border-radius: 22px;
    background: rgba(40, 60, 80, 0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
  }
}

.hero-buttons {
  display: flex;
  gap: 14px;
  margin-bottom: 56px;
  opacity: 0;
  animation: fadeUp 0.8s var(--ease-entrance) 0.27s forwards;
}

.btn-primary,
.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 16px 26px;
  border-radius: 999px;
  font-size: 14.5px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  text-decoration: none;
  transition: opacity 0.2s var(--ease-ui), background 0.2s var(--ease-ui), transform 0.2s var(--ease-ui);
}

.btn-primary {
  background: #fff;
  color: #0b0f1a;
}

.btn-primary:hover {
  opacity: 0.85;
}

.btn-primary svg {
  width: 12px;
  height: 12px;
}

.btn-secondary {
  background: rgba(255, 255, 255, 0.04);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.btn-secondary:hover {
  background: rgba(255, 255, 255, 0.1);
}

.badges-row {
  display: flex;
  gap: 34px;
  opacity: 0;
  animation: fadeUp 0.8s var(--ease-entrance) 0.36s forwards;
}

.badge-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: rgba(255, 255, 255, 0.55);
}

.badge-item svg {
  width: 24px;
  height: 36px;
  color: rgba(255, 255, 255, 0.35);
  margin-bottom: 2px;
}

.badge-label {
  font-size: 9.5px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
}

.badge-sub {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.8);
  font-weight: 500;
}

@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(14px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---- right column: rotating badge + form card ---- */
.hero-right {
  position: relative;
  padding-top: 10px;
}

.start-badge {
  position: absolute;
  top: 18px;
  left: -52px;
  width: 104px;
  height: 104px;
  z-index: 10;
  border-radius: 50%;
  background: rgba(40, 60, 80, 0.45);
  border: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);
  transform: rotate(-25deg);
}

/* The ring text orbits the badge. The keyframes and the reduced-motion
   opt-out for this already existed; the animation itself was never applied,
   so both badges sat frozen. Slow enough to read a word at a time. */
.start-badge svg.ring {
  width: 100%;
  height: 100%;
  animation: spin 26s linear infinite;
}

.start-badge .center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(25deg);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 17px;
  font-weight: 300;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.contact-card {
  position: relative;
  z-index: 1;
  margin-top: 60px;
  background: #f6f7f9;
  border-radius: 26px;
  padding: 70px 36px 36px;
  box-shadow: none;
}

/* ---- hero form: compact rhythm ----
   The hero is pinned to exactly one viewport with overflow:hidden, so this
   column has a hard height budget the Start a project form does not. At the
   shared spacing the column ran 40px past the hero's content box and the
   submit button fell off the bottom edge. Everything below buys that back
   from padding and gaps — no field is smaller, only the air between them.
   Scoped to .hero-right so the second form keeps the roomier rhythm. */
/* The top inset exists only to clear the badge. The badge is a circle, so it
   reaches lowest at its own centre — which lands on the card's left edge,
   left of where the labels begin. Measured at the label's x there is 23px of
   real headroom, so the fields sit 14px higher than the naive box would
   allow and still clear it. */
.hero-right .contact-card {
  padding: 50px 32px 24px;
}

.hero-right .form-row,
.hero-right .form-group {
  margin-bottom: 16px;
}

.hero-right .form-group textarea {
  min-height: 84px;
}

.hero-right .btn-submit {
  margin-top: 18px;
  padding: 16px 24px;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-bottom: 22px;
}

.form-group {
  margin-bottom: 22px;
}

.form-group:last-child {
  margin-bottom: 0;
}

.form-group label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: #101318;
  margin-bottom: 8px;
}

.form-group label .req {
  color: #ef4444;
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  background: #e2e4e8;
  border: 1px solid transparent;
  border-radius: 12px;
  padding: 13px 15px;
  font-size: 14px;
  font-family: var(--font-sans);
  color: #101318;
  outline: none;
  transition: border-color 0.18s var(--ease-ui), background 0.18s var(--ease-ui);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: #101318;
  background: #fff;
}

.form-group textarea {
  min-height: 110px;
  resize: vertical;
  font-family: inherit;
}

.form-group select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%23444' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  background-size: 8px;
}

/* ================= responsive ================= */
@media (max-width: 980px) {

  /* On narrow screens the hero is taller than the viewport, so pinning it
     would clip the bottom. It scrolls normally instead and the spacer
     collapses to the hero's own height, keeping the page flowing. */
  .hero-scroll-spacer {
    height: auto;
  }

  .hero {
    position: relative;
    grid-template-columns: 1fr;
    padding-top: 150px;
    height: auto;
    min-height: 100vh;
  }

  .hero-right {
    margin-top: 90px;
  }
}

/* Tablet and below run on the menu, not the inline links. Seven pills plus a
   CTA do not fit an iPad bar without crowding, and the toggle was previously
   dead weight between 761px and 1024px — links hidden nowhere, menu
   unreachable. 1024px is iPad landscape. */
@media (max-width: 1024px) {
  .nav-links {
    display: none;
  }

  .nav-toggle {
    display: inline-flex;
  }
}

/* Desktop bar, 40% shorter: 85px down to 51px.
   Its height is the tallest child plus the bar's own padding, so trimming the
   padding alone would not have done it — the CTA button was the 45px floor.
   Padding and every child come down together, and the type sizes hold so the
   bar reads tighter rather than smaller. Tablet and mobile keep their own
   proportions. */
@media (min-width: 1025px) {

  /* 13px block padding puts the bar at 59px against the 33px CTA button — 15%
     up from the 51px it sat at. */
  .nav {
    padding: 13px 56px;
  }

  .logo {
    font-size: 19px;
  }

  .nav-links a {
    padding: 6px 14px;
  }

  .btn-nav-schedule {
    padding: 7px 18px;
  }
}

@media (max-width: 760px) {
  .nav {
    padding: 16px 20px;
  }

  /* The logo, the CTA and the menu toggle cannot share a 375px bar without
     colliding. The CTA lives inside the menu at this width. */
  .nav .btn-nav-schedule {
    display: none;
  }

  .hero {
    padding: 130px 20px 70px;
  }

  .badges-row {
    flex-wrap: wrap;
    row-gap: 20px;
  }
}

@media (prefers-reduced-motion: reduce) {

  .status-badge,
  .hero-title,
  .hero-desc,
  .hero-buttons,
  .badges-row {
    animation: none;
    opacity: 1;
  }

  .start-badge svg.ring {
    animation: none;
  }

  /* The pill still appears on hover, it just stops sweeping. */
  .nav-links a::after,
  .mobile-menu a::after,
  .btn-primary::after,
  .btn-secondary::after,
  .btn-nav-schedule::after,
  .btn-start::after,
  .btn-submit::after,
  .btn-pricing::after,
  .reviews-cta-btn::after,
  .floating-cta::after,
  .status-badge .cta::after {
    transition: none;
  }
}

/* ================= STATS STRIP ================= */
.stats-strip {
  position: relative;
  z-index: 2;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  padding: 52px 56px;
}

.stats-grid {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.stat-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 0 24px;
  border-right: 1px solid var(--line);
}

.stat-item:last-child {
  border-right: none;
}

.stat-num {
  font-family: var(--font-sans);
  font-size: clamp(40px, 5vw, 62px);
  font-weight: 500;
  color: var(--white);
  line-height: 1;
  letter-spacing: -0.02em;
}

.stat-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ================= TEXT WASH =================
   Display type is painted with a gradient rather than a flat colour: the
   glyphs clip a background image, so the line brightens at its head and
   cools toward its tail. Reserved for large type — at body sizes the cool
   end loses too much contrast to be safe to read.
   The section sets which wash is in play; the utility never has to know
   whether it sits on a dark or a light ground. */
.text-wash {
  background-image: var(--text-wash, var(--text-wash-light));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  /* The gradient is painted across the element's box and then clipped to the
     glyphs, so anything falling outside that box is never painted at all.
     At line-height 1.12 a descender (g, y, p) hangs below the content box
     and gets sheared off. This reserves the extra strip so the whole glyph
     has paint under it. 0.18em rather than the ~0.14em the tightest case
     strictly needs, so subpixel rounding at other zoom levels and pixel
     ratios cannot shave it back into clipping. */
  padding-bottom: 0.18em;
}

.nav,
.hero,
.about-section,
.testimonials-section,
.cta-band,
.site-footer,
.wordmark-band,
/* The one dark card inside an otherwise light section — it needs the dark
   wash even though its section does not. */
.pricing-card.featured {
  --text-wash: var(--text-wash-dark);
}

/* Past the hero the bar sits on white, so the logo's wash inverts with it. */
.nav.on-light {
  --text-wash: var(--text-wash-light);
}

/* The open menu is always dark, whatever the bar was sitting on when it was
   tapped. Without this the logo keeps the light-ground wash it had over a white
   section — dark ink on a near-black overlay, so the tail of the word goes
   invisible. The .on-light pairing is needed to outweigh the rule above. */
.nav.menu-open,
.nav.menu-open.on-light {
  --text-wash: var(--text-wash-dark);
}

/* The brand name carries the closing wordmark's wash everywhere it appears —
   nav logo, footer lockup, and mid-sentence in body copy. display:inline
   would break the gradient across line wraps, so it stays inline-block. */
.brand-wash {
  display: inline-block;
  background-image: var(--text-wash, var(--text-wash-light));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

/* Children that must stay solid inside a washed heading. Without this they
   inherit the transparent fill and vanish. */
.text-wash .highlight-word,
.text-wash .highlight-word i {
  -webkit-text-fill-color: #fff;
  color: #fff;
}

/* ================= SHARED SECTION STYLES ================= */
.section-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(34px, 4vw, 54px);
  line-height: 1.12;
  /* Measured off the reference: its h1 tracks -1.28px at 35px and its h2
     -0.96px, i.e. roughly -0.037em and -0.027em. Grotesques at display size
     need the negative tracking or they read looser than the original. */
  letter-spacing: -0.028em;
  margin: 0;
  color: var(--white);
}

.section-body {
  color: var(--text-muted);
  font-size: 15.5px;
  line-height: 1.75;
  margin: 0;
}

/* ================= SERVICES ================= */
.services-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 120px 56px;
}

.services-header {
  max-width: 1200px;
  margin: 0 auto 64px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
}

.services-header .section-title {
  max-width: 480px;
}

.services-header .section-body {
  max-width: 360px;
}

.services-bento {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

.svc-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 36px;
  transition: background 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui), translate 0.3s var(--ease-entrance), box-shadow 0.3s var(--ease-ui);
}

/* Lifts via the individual `translate` property, not `transform`, so the
   hover and the scroll reveal animate different things and never contend
   for one shorthand.
   .in-view is the Franklyne state driven by scroll position instead of the
   pointer: a card lights up as it passes through the middle of the viewport
   and settles back once it leaves, so the grid reads as a sequence on the
   way down. Touch devices never fire :hover, so this is the only way that
   treatment is ever seen on a phone. */
.svc-card:hover,
.svc-card.in-view {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.16);
  translate: 0 -3px;
  box-shadow: 0 18px 40px rgba(11, 15, 26, 0.07);
}

/* The pointer still wins over the scroll state while it is on the card. */
.svc-card.in-view:hover {
  box-shadow: 0 22px 48px rgba(11, 15, 26, 0.1);
}

.svc-card.wide {
  grid-column: span 2;
}

.svc-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--ink);
  border: 1px solid var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 24px;
  color: #fff;
}

.svc-icon svg {
  width: 20px;
  height: 20px;
}

.svc-name {
  font-size: 17px;
  font-weight: 600;
  color: var(--white);
  margin: 0 0 10px;
}

.svc-desc {
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-muted);
  margin: 0;
}

/* ================= ABOUT =================
   The themed band that breaks up the white run after the services grid.
   Re-points the shared tokens the Franklyne way the testimonials section does,
   so the children below need no light/dark branching. Slate rather than the
   near-black ink, so it reads as a separate beat from the testimonials
   band further down instead of a repeat of it. */
.about-section {
  --white: #ffffff;
  --text-muted: rgba(255, 255, 255, 0.68);
  --ink-soft: rgba(255, 255, 255, 0.82);
  --line: rgba(255, 255, 255, 0.16);
  position: relative;
  z-index: 2;
  background: #3f5165;
  color: #fff;
  border-top: none;
  padding: 120px 56px;
}

.about-inner {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 96px;
  align-items: center;
}

.about-body {
  color: var(--text-muted);
  font-size: 15.5px;
  line-height: 1.8;
  margin: 20px 0 0;
}

.about-creds {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 36px;
}

.cred-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid var(--line);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 500;
  color: #fff;
}

.cred-pill .gdot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #22c55e;
  flex-shrink: 0;
}

.about-image-wrap {
  position: relative;
  border-radius: 24px;
  overflow: hidden;
  aspect-ratio: 4/5;
  background: #e9ebef;
  border: 1px solid var(--line);
}

.about-image-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ================= PROCESS ================= */
.process-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 120px 56px;
}

.process-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.process-header {
  margin-bottom: 72px;
}

.process-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.process-step {
  padding: 0 48px 0 0;
  border-right: 1px solid var(--line);
}

.process-step:nth-child(2) {
  padding: 0 48px 0 48px;
}

.process-step:last-child {
  border-right: none;
  padding: 0 0 0 48px;
}

.step-num {
  font-family: var(--font-sans);
  font-size: 80px;
  font-weight: 500;
  color: rgba(11, 15, 26, 0.12);
  line-height: 1;
  margin-bottom: 24px;
  display: block;
}

.step-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--white);
  margin: 0 0 14px;
}

.step-desc {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--text-muted);
  margin: 0;
}

/* ================= TESTIMONIALS ================= */
/* The one dark band in the middle of the white page. Re-pointing the shared
   tokens here means .section-title and the card internals below need no
   dark/light branching of their own. */
.testimonials-section {
  --white: #ffffff;
  --text-muted: #8d97ad;
  --line: rgba(255, 255, 255, 0.1);
  position: relative;
  z-index: 2;
  background: var(--ink);
  color: #fff;
  padding: 120px 0;
  overflow: hidden;
}

.testimonials-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 56px;
}

.testimonials-header {
  margin-bottom: 56px;
}

.section-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--line);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  margin-bottom: 26px;
}

.section-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
}

/* ---- marquee ----
   The track holds the card list twice over. Sliding it left by exactly 50%
   lands copy two where copy one started, so the loop point is invisible.
   That only holds if every card contributes the Franklyne width, which is why the
   spacing is margin-right on the card rather than a gap on the track — a gap
   would add one extra space between the copies and break the alignment. */
.testi-marquee {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}

.testi-track {
  display: flex;
  width: max-content;
  animation: testi-scroll 150s linear infinite;
}

.testi-marquee:hover .testi-track {
  animation-play-state: paused;
}

/* Second row travels the Franklyne way as the first, just slower, so the two
   never drift into lockstep. */
.testi-track.alt {
  animation-duration: 185s;
}

.testi-marquee+.testi-marquee {
  margin-top: 14px;
}

/* ---- social-proof bar under the two rows ---- */
.reviews-cta-wrap {
  display: flex;
  justify-content: center;
  padding: 0 28px;
  margin-top: 46px;
}

.reviews-cta {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  /* Tighter on the right: the button carries its own padding. */
  padding: 9px 9px 9px 16px;
  border-radius: 999px;
  background: #171b23;
  border: 1px solid var(--line);
}

.reviews-avatars {
  display: flex;
  flex-shrink: 0;
}

.reviews-avatars img {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  /* The ring matches the pill behind it, so the stack reads as cut-outs. */
  border: 2px solid #171b23;
  margin-left: -11px;
}

.reviews-avatars img:first-child {
  margin-left: 0;
}

.reviews-cta-text {
  font-size: 14.5px;
  color: rgba(255, 255, 255, 0.74);
  white-space: nowrap;
}

.reviews-cta-btn {
  display: inline-flex;
  align-items: center;
  padding: 12px 22px;
  border-radius: 999px;
  background: #56708c;
  color: #fff;
  font-size: 14.5px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.25s var(--ease-ui);
}

.reviews-cta-btn:hover {
  background: #63809f;
}

/* The bar keeps its single-line pill shape all the way down; it scales its
   own parts rather than stacking them. */
@media (max-width: 560px) {
  .reviews-cta-wrap {
    padding: 0 14px;
  }

  .reviews-cta {
    gap: 10px;
    padding: 6px 6px 6px 12px;
  }

  .reviews-avatars img {
    width: 27px;
    height: 27px;
    margin-left: -9px;
  }

  .reviews-cta-text {
    font-size: 12.5px;
  }

  .reviews-cta-btn {
    font-size: 12.5px;
    padding: 9px 15px;
  }
}

@media (max-width: 360px) {
  .reviews-cta {
    gap: 8px;
    padding: 5px 5px 5px 10px;
  }

  .reviews-avatars img {
    width: 24px;
    height: 24px;
    margin-left: -8px;
  }

  .reviews-cta-text {
    font-size: 11.5px;
  }

  .reviews-cta-btn {
    font-size: 11.5px;
    padding: 8px 12px;
  }
}

@keyframes testi-scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

.testi-card {
  flex: 0 0 auto;
  width: 372px;
  margin-right: 14px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 26px 28px 30px;
  display: flex;
  flex-direction: column;
  transition: background 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui);
}

/* The row pauses under the cursor, so the card being pointed at lifts out of
   the strip. */
.testi-card:hover {
  background: rgba(255, 255, 255, 0.09);
  border-color: rgba(255, 255, 255, 0.24);
}


.testi-top {
  display: flex;
  align-items: flex-start;
  margin-bottom: 20px;
}

/* Sits where the role line used to, so it reads as the second line of the
   author block rather than a right-hand column. */
.testi-stars {
  display: flex;
  gap: 3px;
  flex-shrink: 0;
  margin-top: 5px;
}

.testi-stars svg {
  width: 14px;
  height: 14px;
  color: #7d94b8;
}

.testi-quote {
  font-size: 14.5px;
  line-height: 1.72;
  color: rgba(255, 255, 255, 0.72);
  margin: 0;
  flex: 1;
}

.testi-author {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
}

.testi-author>div {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.testi-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--line);
}

.testi-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.testi-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14.5px;
  font-weight: 600;
  color: #fff;
  margin: 0;
}

.testi-name svg {
  width: 15px;
  height: 15px;
  color: #7d94b8;
  flex-shrink: 0;
}

/* ================= START A PROJECT =================
   Reuses the hero's .start-badge and .contact-card wholesale; only the
   colours are re-pointed, since here they sit on a light panel rather than
   over the dark clip. */
.start-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  /* The faint pin-dot grid the panel floats on. */
  background-image: radial-gradient(rgba(11, 15, 26, 0.14) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: 96px 56px;
}

.start-panel {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
  background: #e8e6ec;
  padding: 68px 64px;
}

/* Corner ticks, Franklyne idea as the hero's highlighted word. */
.start-panel>i {
  position: absolute;
  font-style: normal;
  font-size: 15px;
  line-height: 1;
  color: rgba(11, 15, 26, 0.45);
}

.start-panel>i.tl {
  top: -7px;
  left: -7px;
}

.start-panel>i.tr {
  top: -7px;
  right: -7px;
}

.start-panel>i.bl {
  bottom: -7px;
  left: -7px;
}

.start-panel>i.br {
  bottom: -7px;
  right: -7px;
}

.start-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: start;
}

.start-badge-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: rgba(11, 15, 26, 0.07);
  padding: 7px 15px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 22px;
}

.start-badge-pill::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(11, 15, 26, 0.55);
}

.start-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(34px, 4vw, 52px);
  line-height: 1.1;
  letter-spacing: -0.032em;
  color: var(--ink);
  margin: 0 0 20px;
}

.start-desc {
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--ink-soft);
  max-width: 420px;
  margin: 0 0 40px;
}

.start-actions {
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
}

.btn-start {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: #4a6076;
  color: #fff;
  font-size: 14.5px;
  font-weight: 600;
  padding: 17px 28px;
  border-radius: 999px;
  text-decoration: none;
  transition: background 0.25s var(--ease-ui);
}

.btn-start:hover {
  background: #587189;
}

.btn-start svg {
  width: 12px;
  height: 12px;
}

.start-proof {
  display: flex;
  align-items: center;
  gap: 12px;
}

.start-proof .reviews-avatars img {
  border-color: #e8e6ec;
}

.start-proof-num {
  font-size: 14.5px;
  font-weight: 600;
  color: #40536a;
  margin: 0;
}

.start-proof-sub {
  font-size: 12.5px;
  color: rgba(11, 15, 26, 0.5);
  margin: 2px 0 0;
}

.start-right {
  position: relative;
  padding-top: 10px;
}

/* The badge is left exactly as the hero styles it — Franklyne glass fill, Franklyne
   offset, Franklyne white ring text. No overrides here on purpose. */

.start-section .contact-card {
  margin-top: 60px;
  background: #fff;
  box-shadow: 14px 14px 0 rgba(11, 15, 26, 0.09);
}

.start-section .form-group input,
.start-section .form-group select,
.start-section .form-group textarea {
  background: #f4f5f7;
  border-color: rgba(11, 15, 26, 0.12);
}

.start-section .form-group input:focus,
.start-section .form-group select:focus,
.start-section .form-group textarea:focus {
  background: #fff;
  border-color: var(--ink);
}

.btn-submit {
  width: 100%;
  margin-top: 30px;
  background: #4a6076;
  color: #fff;
  font-family: var(--font-sans);
  font-size: 14.5px;
  font-weight: 600;
  padding: 18px 24px;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.25s var(--ease-ui);
}

.btn-submit:hover {
  background: #587189;
}

@media (max-width: 980px) {
  .start-section {
    padding: 72px 28px;
  }

  .start-panel {
    padding: 44px 28px;
  }

  .start-inner {
    grid-template-columns: 1fr;
    gap: 64px;
  }
}

@media (max-width: 560px) {
  .start-actions {
    gap: 18px;
  }

  .start-section .contact-card {
    padding: 60px 22px 28px;
    box-shadow: 8px 8px 0 rgba(11, 15, 26, 0.09);
  }
}

/* ================= PRICING ================= */
.pricing-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 120px 56px;
}

.pricing-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.pricing-header {
  text-align: center;
  margin-bottom: 72px;
}

.pricing-header .section-title {
  max-width: 100%;
}

.pricing-subtitle {
  color: var(--text-muted);
  font-size: 16px;
  margin: 16px 0 0;
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  align-items: stretch;
}

.pricing-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 24px;
  padding: 40px 36px;
  display: flex;
  flex-direction: column;
}

/* The recommended tier is the one dark object on the white page. */
.pricing-card.featured {
  background: var(--ink);
  border-color: transparent;
  box-shadow: 0 30px 70px rgba(11, 15, 26, 0.18);
}

.pricing-card.featured .price-tier {
  color: rgba(255, 255, 255, 0.5);
}

.pricing-card.featured .price-from {
  color: rgba(255, 255, 255, 0.5);
}

.pricing-card.featured .price-num {
  color: #fff;
}

.pricing-card.featured .price-note {
  color: rgba(255, 255, 255, 0.55);
}

.pricing-card.featured .features-list .feature-item {
  color: rgba(255, 255, 255, 0.82);
}

.pricing-card.featured .feature-divider {
  border-color: rgba(255, 255, 255, 0.14);
}

.pricing-card.featured .btn-pricing {
  background: #fff;
  color: var(--ink);
  border-color: transparent;
}

.pricing-card.featured .btn-pricing:hover {
  opacity: 0.88;
  background: #fff;
}

.pricing-card.featured .feature-item svg {
  color: #4ade80;
}

.price-tier {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 28px;
}

.price-from {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.price-num {
  font-family: var(--font-sans);
  font-size: clamp(40px, 4vw, 56px);
  font-weight: 500;
  color: var(--white);
  line-height: 1;
  letter-spacing: -0.02em;
  margin-bottom: 8px;
}

.price-note {
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 36px;
}

.feature-divider {
  border: none;
  border-top: 1px solid var(--line);
  margin-bottom: 28px;
}

.features-list {
  list-style: none;
  padding: 0;
  margin: 0 0 36px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 1;
}

.feature-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.5;
}

.feature-item svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 1px;
  color: #16a34a;
}

.btn-pricing {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 15px 24px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid rgba(11, 15, 26, 0.18);
  color: var(--ink);
  background: #fff;
  transition: background 0.2s var(--ease-ui), border-color 0.2s var(--ease-ui);
  cursor: pointer;
}

.btn-pricing:hover {
  background: var(--surface);
  border-color: rgba(11, 15, 26, 0.36);
}

/* ================= FAQ =================
   Franklyne construction as the Start a project section: a light plate floating
   on the pin-dot grid, registered with tick marks at its corners. */
.faq-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  background-image: radial-gradient(rgba(11, 15, 26, 0.14) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: 96px 56px;
}

.faq-panel {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
  background: #e8e6ec;
  padding: 68px 64px;
}

.faq-panel>i {
  position: absolute;
  font-style: normal;
  font-size: 15px;
  line-height: 1;
  color: rgba(11, 15, 26, 0.45);
}

.faq-panel>i.tl {
  top: -7px;
  left: -7px;
}

.faq-panel>i.tr {
  top: -7px;
  right: -7px;
}

.faq-panel>i.bl {
  bottom: -7px;
  left: -7px;
}

.faq-panel>i.br {
  bottom: -7px;
  right: -7px;
}

.faq-inner {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 72px;
  align-items: start;
}

/* The plate is lighter than white, so the hairlines and the icon tiles need
   to sit a shade darker to keep their edges. */
.faq-list {
  border-top-color: rgba(11, 15, 26, 0.14);
}

.faq-item {
  border-bottom-color: rgba(11, 15, 26, 0.14);
}

/* The intro holds its place while the answers move beside it. */
.faq-intro {
  position: sticky;
  top: 120px;
}

.faq-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: rgba(11, 15, 26, 0.06);
  padding: 7px 15px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 22px;
}

.faq-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #4a6076;
}

.faq-intro .section-title {
  color: var(--ink);
  margin-bottom: 18px;
}

.faq-intro-body {
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--ink-soft);
  max-width: 340px;
  margin: 0 0 32px;
}

.faq-list {
  border-top: 1px solid rgba(11, 15, 26, 0.1);
}

.faq-item {
  border-bottom: 1px solid rgba(11, 15, 26, 0.1);
}

.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 26px 4px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-sans);
  color: var(--ink);
  transition: color 0.25s var(--ease-ui);
}

.faq-q:hover {
  color: #4a6076;
}

.faq-icon {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  border: 1px solid rgba(11, 15, 26, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #4a6076;
  background: #fff;
  transition: background 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui), color 0.3s var(--ease-ui);
}

.faq-icon svg {
  width: 20px;
  height: 20px;
}

.faq-item.open .faq-icon {
  background: #4a6076;
  border-color: #4a6076;
  color: #fff;
}

.faq-label {
  flex: 1;
  font-size: 17px;
  font-weight: 500;
  line-height: 1.4;
}

/* Plus that loses its vertical stroke to become a minus. */
.faq-toggle {
  position: relative;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: rgba(11, 15, 26, 0.55);
}

.faq-toggle::before,
.faq-toggle::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  background: currentColor;
  border-radius: 1px;
  transition: transform 0.4s var(--ease-swap), opacity 0.3s var(--ease-ui);
}

.faq-toggle::before {
  width: 15px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}

.faq-toggle::after {
  width: 1.5px;
  height: 15px;
  transform: translate(-50%, -50%);
}

.faq-item.open .faq-toggle::after {
  transform: translate(-50%, -50%) rotate(90deg);
  opacity: 0;
}

/* 0fr to 1fr animates the answer open without measuring it or capping it
   with a max-height that breaks the moment the copy rewraps. */
.faq-a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s var(--ease-entrance);
}

.faq-item.open .faq-a {
  grid-template-rows: 1fr;
}

.faq-a-inner {
  overflow: hidden;
}

.faq-a-inner p {
  font-size: 15px;
  line-height: 1.75;
  color: var(--ink-soft);
  margin: 0 0 26px;
  padding-left: 66px;
  max-width: 560px;
}

@media (max-width: 980px) {
  .faq-section {
    padding: 72px 28px;
  }

  .faq-panel {
    padding: 44px 28px;
  }

  .faq-inner {
    grid-template-columns: 1fr;
    gap: 48px;
  }

  .faq-intro {
    position: static;
  }

  .faq-intro-body {
    max-width: 100%;
  }
}

@media (max-width: 560px) {
  .faq-q {
    gap: 14px;
    padding: 22px 2px;
  }

  .faq-icon {
    width: 38px;
    height: 38px;
    border-radius: 11px;
  }

  .faq-icon svg {
    width: 17px;
    height: 17px;
  }

  .faq-label {
    font-size: 15.5px;
  }

  .faq-a-inner p {
    padding-left: 52px;
    font-size: 14.5px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .faq-a {
    transition: none;
  }
}

/* ================= CTA BAND ================= */
/* The closing stretch — CTA, footer and wordmark — runs dark as one block,
   so the page resolves into the Franklyne ground the hero opened on. */
.cta-band {
  --white: #ffffff;
  --text-muted: rgba(255, 255, 255, 0.62);
  --line: rgba(255, 255, 255, 0.12);
  position: relative;
  z-index: 2;
  background: var(--ink-deep);
  border-top: none;
  padding: 112px 56px;
  text-align: center;
  color: #fff;
}

.cta-band .section-title {
  max-width: 680px;
  margin: 0 auto 16px;
}

.cta-band .section-body {
  max-width: 460px;
  margin: 0 auto 44px;
  color: var(--text-muted);
}

.cta-band .btn-primary {
  background: #fff;
  color: var(--ink-deep);
  font-size: 15px;
  padding: 18px 32px;
}

.cta-band .btn-primary:hover {
  opacity: 0.86;
}

/* ================= FOOTER ================= */
.site-footer {
  --white: #ffffff;
  --ink: #ffffff;
  --text-muted: rgba(255, 255, 255, 0.6);
  --line: rgba(255, 255, 255, 0.12);
  position: relative;
  z-index: 2;
  background: var(--ink-deep);
  color: #fff;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
  padding: 72px 56px 40px;
}

.footer-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.footer-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 60px;
  padding-bottom: 56px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 40px;
}

.footer-brand .logo {
  display: block;
  margin-bottom: 14px;
}

.footer-tagline {
  font-size: 13.5px;
  color: var(--text-muted);
  line-height: 1.65;
  max-width: 240px;
}

.footer-cols {
  display: flex;
  gap: 80px;
}

.footer-nav-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.footer-nav-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42);
  margin-bottom: 4px;
}

.footer-link {
  font-size: 13.5px;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.2s var(--ease-ui);
}

.footer-link:hover {
  color: var(--ink);
}

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.footer-copy {
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.42);
}

.social-links {
  display: flex;
  gap: 12px;
}

.social-link {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.62);
  text-decoration: none;
  transition: border-color 0.2s var(--ease-ui), color 0.2s var(--ease-ui);
}

.social-link:hover {
  border-color: rgba(255, 255, 255, 0.45);
  color: #fff;
}

.social-link svg {
  width: 15px;
  height: 15px;
}

/* ================= CLOSING WORDMARK =================
   The sign-off. One word at display scale inside a hairline frame with tick
   marks at its corners — a drafting register, echoing the ticks around the
   hero's highlighted word and the Start a project panel. */
.wordmark-band {
  position: relative;
  background: black !important;
  z-index: 2;
  /* A step lighter than the footer above it, so the sign-off reads as its
     own plate rather than more footer. */
  background: #171b22;
  /* Top padding is load-bearing: the corner ticks sit at -8px and the band
     clips its overflow, so with no top inset the upper pair was sheared off
     at the seam with the footer. */
  padding: clamp(26px, 4vw, 52px) clamp(20px, 4vw, 56px) clamp(28px, 5vw, 64px);
  /* viewport-fit=cover means this band now runs under the home indicator, so
     it grows by that inset to keep the wordmark clear of it. */
  padding-bottom: calc(clamp(28px, 5vw, 64px) + env(safe-area-inset-bottom));
  overflow: hidden;
}

.wordmark-frame {
  position: relative;
  max-width: 1400px;
  margin: 0 auto;
  border: 1px solid rgba(255, 255, 255, 0.14);
  padding: clamp(18px, 3vw, 44px) clamp(16px, 3vw, 40px);
  /* Brightest along the top edge, fading down into the band. */
  background: linear-gradient(to bottom, #2e3542 0%, #262c37 32%, #21242c 68%, #1c1f26 100%);
}

.wordmark-frame>i {
  position: absolute;
  font-style: normal;
  font-size: 16px;
  line-height: 1;
  color: rgba(154, 190, 230, 0.75);
}

.wordmark-frame>i.tl {
  top: -8px;
  left: -8px;
}

.wordmark-frame>i.tr {
  top: -8px;
  right: -8px;
}

.wordmark-frame>i.bl {
  bottom: -8px;
  left: -8px;
}

.wordmark-frame>i.br {
  bottom: -8px;
  right: -8px;
}

/* Sized off the viewport so the word always spans the frame edge to edge,
   at any width, without a media query per breakpoint. */
.wordmark {
  display: block;
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(52px, 15.2vw, 260px);
  line-height: 1;
  letter-spacing: -0.035em;
  white-space: nowrap;
  text-align: left;
}

@media (prefers-reduced-motion: no-preference) {
  .wordmark-band .wordmark-frame {
    transition: border-color 0.4s var(--ease-ui);
  }
}

/* ================= SCROLL REVEAL =================
   Travels on the individual `translate` property rather than `transform`.
   Many revealed elements are also hoverable, and a shared `transform` meant
   this rule — later in the cascade at equal specificity — silently replaced
   their hover transitions. Separate properties let both coexist. */
/* The rise travels further than it used to and, more importantly, the fade
   finishes well before the movement does. Matching both to one duration is what
   made this read as a flat block sliding in — resolving the opacity early lets
   the element settle into place while it is already legible, which is what makes
   it feel like it arrived rather than appeared. script.js cascades neighbouring
   items on top of this so a row of cards comes in one after another. */
.reveal {
  opacity: 0;
  translate: 0 28px;
  transition:
    opacity 0.55s var(--ease-entrance),
    translate 0.95s var(--ease-entrance);
}

.reveal.visible {
  opacity: 1;
  translate: none;
}

/* These reveal AND respond to pointer. One declaration has to carry both
   sets, or whichever rule loses the cascade contributes nothing. */
.svc-card.reveal,
.cta-band .btn-primary.reveal {
  transition:
    opacity 0.8s var(--ease-entrance),
    translate 0.3s var(--ease-entrance),
    background 0.3s var(--ease-ui),
    border-color 0.3s var(--ease-ui),
    box-shadow 0.3s var(--ease-ui),
    scale 0.2s var(--ease-ui);
}

/* The reveal's own travel is the slow one; it runs before any hover can. */
.svc-card.reveal:not(.visible),
.cta-band .btn-primary.reveal:not(.visible) {
  transition-duration: 0.8s;
}

.reveal-d1 {
  transition-delay: 0.08s;
}

.reveal-d2 {
  transition-delay: 0.16s;
}

.reveal-d3 {
  transition-delay: 0.24s;
}

.reveal-d4 {
  transition-delay: 0.32s;
}

.reveal-d5 {
  transition-delay: 0.40s;
}

/* ================= RESPONSIVE (new sections) ================= */
@media (max-width: 980px) {
  .stats-strip {
    padding: 40px 28px;
  }

  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .stat-item {
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 28px 0;
  }

  .stat-item:nth-child(2n) {
    padding-left: 32px;
    border-left: 1px solid var(--line);
  }

  .stat-item:nth-last-child(-n+2) {
    border-bottom: none;
  }

  .services-section,
  .about-section,
  .process-section,
  .pricing-section,
  .cta-band,
  .site-footer {
    padding-left: 28px;
    padding-right: 28px;
  }

  /* The testimonials section stays full-bleed so the marquee can run edge to
     edge; only its header inset narrows. */
  .testimonials-inner {
    padding: 0 28px;
  }

  .services-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .services-bento {
    grid-template-columns: 1fr 1fr;
  }

  .svc-card.wide {
    grid-column: span 2;
  }

  .about-inner {
    grid-template-columns: 1fr;
    gap: 52px;
  }

  /* The portrait is a 4:5 file. Cropping it to 16:9 here sliced the head
     off, so the frame keeps the photo's own ratio and shows it whole.
     width:100% is load-bearing — auto margins on a grid item with an auto
     width make it shrink to its content instead of filling the column. */
  .about-image-wrap {
    aspect-ratio: 4/5;
    width: 100%;
    max-width: 420px;
    margin-left: auto;
    margin-right: auto;
  }

  .process-steps {
    grid-template-columns: 1fr;
  }

  .process-step,
  .process-step:nth-child(2),
  .process-step:last-child {
    padding: 40px 0;
    border-right: none;
    border-bottom: 1px solid var(--line);
  }

  .process-step:last-child {
    border-bottom: none;
  }

  .pricing-grid {
    grid-template-columns: 1fr;
    max-width: 440px;
    margin-left: auto;
    margin-right: auto;
  }

  .footer-top {
    flex-direction: column;
    gap: 44px;
  }

  .footer-cols {
    gap: 48px;
  }
}

@media (max-width: 760px) {
  .services-bento {
    grid-template-columns: 1fr;
  }

  .svc-card.wide {
    grid-column: span 1;
  }

  .testi-card {
    width: 300px;
    padding: 22px 22px 26px;
  }

  .testi-stars svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
  }

  .footer-cols {
    flex-direction: column;
    gap: 32px;
  }

  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
}

/* ================= THE WIPE =================
   The nav links' curved sweep, applied to every button on the site. Same
   ellipse pinned to the right edge, same duration, same curve — growing its
   horizontal radius draws the fill leftwards behind a bowed edge.

   The sheen is currentColor rather than a fixed value. Every button already
   sets a text colour that contrasts with its own fill, so a dark-on-light
   button gets a dark wipe and a light-on-dark button a light one, with no
   per-button colour list to maintain and nothing to update when a button is
   restyled.

   Unlike the nav links this sits *above* the fill rather than behind it: these
   buttons are already painted, so a layer underneath would never be seen. It
   is translucent, so the label reads straight through it.

   :active and :focus-visible are listed alongside :hover because a touch
   screen has no hover to give, and the ask was for this to be felt on mobile
   too. */
/* .floating-cta is deliberately absent: it is already position:fixed, and
   listing it here overrode that to relative, dropping it out of the corner and
   behind the nav. Any positioned value works for the wipe — it only needs a
   containing block, not specifically `relative`. */
.btn-primary,
.btn-secondary,
.btn-nav-schedule,
.btn-start,
.btn-submit,
.btn-pricing,
.reviews-cta-btn,
.status-badge .cta {
  position: relative;
}

.btn-primary::after,
.btn-secondary::after,
.btn-nav-schedule::after,
.btn-start::after,
.btn-submit::after,
.btn-pricing::after,
.reviews-cta-btn::after,
.floating-cta::after,
.status-badge .cta::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Follows whatever shape the button already is — pill, rounded, or square. */
  border-radius: inherit;
  background: currentColor;
  opacity: 0.14;
  pointer-events: none;
  clip-path: ellipse(0% 88% at 100% 50%);
  transition: clip-path 0.55s var(--ease-swap);
}

.btn-primary:hover::after,
.btn-primary:focus-visible::after,
.btn-primary:active::after,
.btn-secondary:hover::after,
.btn-secondary:focus-visible::after,
.btn-secondary:active::after,
.btn-nav-schedule:hover::after,
.btn-nav-schedule:focus-visible::after,
.btn-nav-schedule:active::after,
.btn-start:hover::after,
.btn-start:focus-visible::after,
.btn-start:active::after,
.btn-submit:hover::after,
.btn-submit:focus-visible::after,
.btn-submit:active::after,
.btn-pricing:hover::after,
.btn-pricing:focus-visible::after,
.btn-pricing:active::after,
.reviews-cta-btn:hover::after,
.reviews-cta-btn:focus-visible::after,
.reviews-cta-btn:active::after,
.floating-cta:hover::after,
.floating-cta:focus-visible::after,
.floating-cta:active::after,
.status-badge .cta:hover::after,
.status-badge .cta:focus-visible::after,
.status-badge .cta:active::after {
  clip-path: ellipse(150% 88% at 100% 50%);
}

/* ================= INTERACTION FEEDBACK =================
   Every CTA on the page was a dead click target: colour changed on hover and
   nothing acknowledged the press. These two rules give the whole site one
   consistent touch response. */
.btn-start,
.btn-submit,
.btn-pricing,
.reviews-cta-btn,
.floating-cta {
  transition: background 0.25s var(--ease-ui), border-color 0.25s var(--ease-ui), transform 0.2s var(--ease-ui);
}

.btn-primary:active,
.btn-secondary:active,
.btn-nav-schedule:active,
.btn-start:active,
.btn-submit:active,
.btn-pricing:active,
.reviews-cta-btn:active,
.floating-cta:active {
  scale: 0.975;
}

/* The outbound arrow leans the way it will take you. */
.btn-primary svg,
.btn-nav-schedule svg,
.btn-start svg,
.floating-cta-icon svg {
  transition: transform 0.28s var(--ease-entrance);
}

.btn-primary:hover svg,
.btn-nav-schedule:hover svg,
.btn-start:hover svg,
.floating-cta:hover .floating-cta-icon svg {
  transform: translate(2px, -2px);
}

/* Counting digits must not reflow the row as they widen. */
.stat-num {
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    translate: none;
    transition: none;
  }

  /* Nothing below travels, lifts, or leans. */
  .svc-card:hover {
    translate: none;
  }

  .btn-primary:hover svg,
  .btn-nav-schedule:hover svg,
  .btn-start:hover svg,
  .floating-cta:hover .floating-cta-icon svg {
    transform: none;
  }

  .btn-primary:active,
  .btn-secondary:active,
  .btn-nav-schedule:active,
  .btn-start:active,
  .btn-submit:active,
  .btn-pricing:active,
  .reviews-cta-btn:active,
  .floating-cta:active {
    scale: none;
  }

  /* No auto-scroll; the strip becomes a normal swipeable rail instead. */
  .testi-track {
    animation: none;
  }

  .testi-marquee {
    overflow-x: auto;
  }

  .floating-cta,
  .nav-toggle-dot {
    animation: none;
  }

  /* The clip still leaves, it just fades instead of pulling back. */
  #scroll-canvas {
    transition: opacity 0.3s linear;
  }

  #scroll-canvas.canvas-out {
    transform: none;
    filter: none;
  }
}

/* ================= INNER PAGE HERO =================
   The home page opens on the scroll clip. Inner pages have no clip to scrub, so
   they open on a dark plate instead: same ground, same wash, a quarter of the
   height. .site-light starts immediately after, so the nav flips to its light
   theme at exactly the same boundary it does on the home page. */
.page-hero {
  position: relative;
  z-index: 2;
  background: var(--ink-deep);
  background-image: radial-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: clamp(150px, 20vh, 210px) 56px clamp(72px, 10vh, 118px);
  --text-wash: var(--text-wash-dark);
  --white: #ffffff;
  --text-muted: #8d97ad;
  --line: rgba(255, 255, 255, 0.1);
  color: #fff;
  overflow: hidden;
}

.page-hero-inner {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
}

.page-hero-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(38px, 6vw, 76px);
  line-height: 1.06;
  letter-spacing: -0.037em;
  margin: 0 0 20px;
  max-width: 15ch;
}

.page-hero-lead {
  font-size: 16.5px;
  line-height: 1.75;
  color: rgba(255, 255, 255, 0.72);
  max-width: 560px;
  margin: 0;
}

.page-hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 34px;
}

.page-hero-meta span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 500;
}

.page-hero-meta span::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #22c55e;
  flex-shrink: 0;
}

/* ================= VALUES ================= */
.values-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 110px 56px;
}

.values-grid {
  max-width: 1200px;
  margin: 56px auto 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

.value-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 34px 32px 36px;
  transition: background 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui), translate 0.3s var(--ease-entrance), box-shadow 0.3s var(--ease-ui);
}

.value-card:hover {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.16);
  translate: 0 -3px;
  box-shadow: 0 18px 40px rgba(11, 15, 26, 0.07);
}

.value-num {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.6px;
  color: rgba(11, 15, 26, 0.32);
  display: block;
  margin-bottom: 22px;
}

.value-name {
  font-size: 19px;
  font-weight: 600;
  color: var(--white);
  margin: 0 0 12px;
  letter-spacing: -0.02em;
}

.value-desc {
  font-size: 14.5px;
  line-height: 1.72;
  color: var(--text-muted);
  margin: 0;
}

/* ================= PORTFOLIO GRID ================= */
.work-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 96px 56px 120px;
}

.work-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.work-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 44px;
}

.work-chip {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 18px;
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.25s var(--ease-ui), border-color 0.25s var(--ease-ui), color 0.25s var(--ease-ui);
}

.work-chip:hover {
  border-color: rgba(11, 15, 26, 0.3);
  color: var(--ink);
}

.work-chip[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}

.work-card {
  display: block;
  text-decoration: none;
  color: inherit;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: #fff;
  transition: translate 0.3s var(--ease-entrance), box-shadow 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui);
}

.work-card:hover {
  translate: 0 -4px;
  border-color: rgba(11, 15, 26, 0.16);
  box-shadow: 0 20px 44px rgba(11, 15, 26, 0.09);
}

/* Hidden by the filter. */
.work-card[hidden] {
  display: none;
}

.work-thumb {
  aspect-ratio: 4/3;
  overflow: hidden;
  background: #eceef2;
}

.work-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Top-anchored so the crop keeps each site's masthead and hero rather than
     slicing through the middle of the page. */
  object-position: center top;
  display: block;
  transition: scale 0.5s var(--ease-entrance);
}

.work-card:hover .work-thumb img {
  scale: 1.04;
}

.work-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 20px 22px 22px;
}

.work-name {
  font-size: 16.5px;
  font-weight: 600;
  color: var(--white);
  margin: 0;
  letter-spacing: -0.02em;
}

.work-cat {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
}

.work-empty {
  padding: 40px 0;
  color: var(--text-muted);
  font-size: 15px;
}

/* ================= CONTACT PAGE ================= */
.contact-split {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(40px, 6vw, 92px);
  align-items: start;
}

.contact-aside .section-title {
  margin-bottom: 18px;
}

.contact-aside-body {
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--ink-soft);
  margin: 0 0 40px;
  max-width: 44ch;
}

.contact-detail {
  padding: 22px 0;
  border-top: 1px solid var(--line);
}

.contact-detail:last-of-type {
  border-bottom: 1px solid var(--line);
}

.contact-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: rgba(11, 15, 26, 0.4);
  margin: 0 0 8px;
}

.contact-value {
  font-size: 16px;
  color: var(--ink);
  margin: 0;
  text-decoration: none;
}

a.contact-value:hover {
  text-decoration: underline;
}

/* The contact page form sits on white, not over the clip. */
.contact-page-form {
  position: relative;
}

.contact-page-form .contact-card {
  margin-top: 60px;
  box-shadow: 14px 14px 0 rgba(11, 15, 26, 0.07);
}

@media (max-width: 980px) {
  .page-hero {
    padding-left: 28px;
    padding-right: 28px;
  }

  .values-section,
  .work-section {
    padding-left: 28px;
    padding-right: 28px;
  }

  .values-grid {
    grid-template-columns: 1fr 1fr;
  }

  .work-grid {
    grid-template-columns: 1fr 1fr;
  }

  .contact-split {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 680px) {

  .values-grid,
  .work-grid {
    grid-template-columns: 1fr;
  }
}

/* ================= PAGE HERO ARTWORK =================
   The inner-page heroes carry a bespoke SVG rather than a photograph: it is
   drawn from the same vocabulary as the rest of the site (pin dots, tick
   marks, slate strokes), weighs a couple of KB, costs no extra request, and
   stays sharp at any density. */
.page-hero-inner {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(36px, 5vw, 80px);
  align-items: center;
}

.page-hero-art {
  position: relative;
}

.page-hero-art svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* Structure lines, node fills and accents, so a single SVG can be restyled
   from CSS instead of carrying hardcoded colours. */
.art-line {
  stroke: rgba(255, 255, 255, 0.16);
  fill: none;
  stroke-width: 1;
}

.art-line-soft {
  stroke: rgba(255, 255, 255, 0.08);
  fill: none;
  stroke-width: 1;
}

.art-plate {
  fill: rgba(255, 255, 255, 0.035);
  stroke: rgba(255, 255, 255, 0.13);
  stroke-width: 1;
}

.art-plate-lit {
  fill: rgba(125, 148, 184, 0.16);
  stroke: rgba(154, 180, 214, 0.44);
  stroke-width: 1;
}

.art-bar {
  fill: rgba(255, 255, 255, 0.14);
}

.art-bar-lit {
  fill: rgba(154, 180, 214, 0.62);
}

.art-node {
  fill: #9ab4d6;
}

.art-node-live {
  fill: #22c55e;
}

.art-tick {
  fill: rgba(154, 190, 230, 0.6);
  font-size: 13px;
  font-family: var(--font-sans);
}

/* ---- motion ----
   Slow, small and out of phase, so the panel breathes instead of performing. */
@keyframes art-float {

  0%,
  100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(-7px);
  }
}

@keyframes art-pulse {

  0%,
  100% {
    opacity: 0.35;
    r: 3;
  }

  50% {
    opacity: 1;
    r: 4.4;
  }
}

@keyframes art-ripple {
  from {
    transform: scale(0.35);
    opacity: 0.75;
  }

  to {
    transform: scale(1.35);
    opacity: 0;
  }
}

@keyframes art-trace {
  to {
    stroke-dashoffset: 0;
  }
}

.art-float {
  animation: art-float 7s ease-in-out infinite;
}

.art-float-b {
  animation: art-float 9s ease-in-out infinite;
  animation-delay: -2.5s;
}

.art-float-c {
  animation: art-float 11s ease-in-out infinite;
  animation-delay: -5s;
}

.art-pulse {
  animation: art-pulse 3.2s ease-in-out infinite;
  transform-box: fill-box;
}

.art-pulse-b {
  animation: art-pulse 3.2s ease-in-out infinite;
  animation-delay: -1.6s;
  transform-box: fill-box;
}

.art-ripple {
  transform-origin: center;
  transform-box: fill-box;
  animation: art-ripple 3.6s var(--ease-entrance) infinite;
}

.art-ripple-b {
  animation-delay: -1.2s;
}

.art-ripple-c {
  animation-delay: -2.4s;
}

.art-trace {
  stroke-dasharray: 340;
  stroke-dashoffset: 340;
  animation: art-trace 2.4s var(--ease-entrance) forwards;
}

@media (max-width: 900px) {
  .page-hero-inner {
    grid-template-columns: 1fr;
    gap: 44px;
  }
}

/* Below this the hero has to stay short enough to read at a glance, and the
   artwork is decoration rather than information. */
@media (max-width: 720px) {
  .page-hero-art {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {

  .art-float,
  .art-float-b,
  .art-float-c,
  .art-pulse,
  .art-pulse-b,
  .art-ripple,
  .art-trace {
    animation: none;
  }

  .art-trace {
    stroke-dashoffset: 0;
  }
}

/* ================= CASE STUDY: PHONE MODULE =================
   A hardware frame around a looping screen recording. The frame is drawn, not
   an image: it stays sharp at any density, recolours with the theme, and costs
   nothing to download. */
.case-stage {
  position: relative;
  z-index: 2;
  background: var(--ink-deep);
  background-image: radial-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: clamp(64px, 9vh, 108px) 56px clamp(72px, 10vh, 120px);
  --white: #ffffff;
  --text-muted: #8d97ad;
  --line: rgba(255, 255, 255, 0.1);
  --text-wash: var(--text-wash-dark);
  color: #fff;
  overflow: hidden;
}

.case-stage-inner {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
}

/* A soft pool of light behind the device so it sits in the plate rather than
   on top of it. */
.phone-wrap {
  position: relative;
  display: flex;
  justify-content: center;
}

.phone-wrap::before {
  content: '';
  position: absolute;
  inset: -12% -22%;
  background: radial-gradient(ellipse at center, rgba(125, 148, 184, 0.28), rgba(125, 148, 184, 0) 68%);
  pointer-events: none;
}

.phone {
  position: relative;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 9 / 19.5;
  background: linear-gradient(160deg, #2c3340, #14181f 42%, #1e232c);
  border-radius: 46px;
  padding: 11px;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.12),
    0 40px 90px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 36px;
  overflow: hidden;
  background: #0b0e13;
}

.phone-screen video,
.phone-screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The pill cut out of the top of the screen. */
.phone-island {
  position: absolute;
  top: 12px;
  left: 50%;
  translate: -50% 0;
  width: 88px;
  height: 26px;
  border-radius: 999px;
  background: #05070a;
  z-index: 3;
}

.phone-btn {
  position: absolute;
  width: 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.16);
}

.phone-btn.vol-up {
  left: -3px;
  top: 22%;
  height: 8%;
}

.phone-btn.vol-down {
  left: -3px;
  top: 33%;
  height: 8%;
}

.phone-btn.power {
  right: -3px;
  top: 26%;
  height: 12%;
}

/* Says plainly that this is a capture, not a live embed. */
.rec-badge {
  position: absolute;
  top: 52px;
  left: 50%;
  translate: -50% 0;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 13px 6px 11px;
  border-radius: 999px;
  background: rgba(8, 9, 15, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.4px;
  color: rgba(255, 255, 255, 0.9);
}

.rec-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ef4444;
  box-shadow: 0 0 9px rgba(239, 68, 68, 0.9);
  animation: rec-blink 1.6s ease-in-out infinite;
}

@keyframes rec-blink {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.2;
  }
}

.case-specs {
  list-style: none;
  margin: 34px 0 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.spec-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px;
}

.spec-item:first-child {
  border-top: 1px solid var(--line);
}

.spec-key {
  color: rgba(255, 255, 255, 0.55);
}

.spec-val {
  color: #fff;
  font-weight: 500;
  text-align: right;
}

/* ---- build notes ---- */
.case-notes {
  position: relative;
  z-index: 2;
  background: var(--bg);
  padding: 110px 56px;
}

.notes-grid {
  max-width: 1200px;
  margin: 52px auto 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

.note-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 32px 34px 34px;
  transition: background 0.3s var(--ease-ui), border-color 0.3s var(--ease-ui), translate 0.3s var(--ease-entrance), box-shadow 0.3s var(--ease-ui);
}

.note-card:hover {
  background: #fff;
  border-color: rgba(0, 0, 0, 0.16);
  translate: 0 -3px;
  box-shadow: 0 18px 40px rgba(11, 15, 26, 0.07);
}

.note-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: #4a6076;
  margin-bottom: 16px;
}

.note-name {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--white);
  margin: 0 0 12px;
}

.note-desc {
  font-size: 14.5px;
  line-height: 1.72;
  color: var(--text-muted);
  margin: 0;
}

@media (max-width: 980px) {
  .case-stage {
    padding-left: 28px;
    padding-right: 28px;
  }

  .case-stage-inner {
    grid-template-columns: 1fr;
    gap: 52px;
  }

  /* Device first on narrow screens: it is the point of the page. */
  .phone-wrap {
    order: -1;
  }

  .case-notes {
    padding: 84px 28px;
  }

  .notes-grid {
    grid-template-columns: 1fr;
  }
}

/* ================= PLATE SECTIONS =================
   The FAQ's construction, generalised: a light plate floating on the pin-dot
   grid with registration ticks at its corners. Any section can adopt it. */
.plate-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  background-image: radial-gradient(rgba(11, 15, 26, 0.14) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: 96px 56px;
}

.plate {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
  background: #e8e6ec;
  padding: 68px 64px;
}

.plate>i {
  position: absolute;
  font-style: normal;
  font-size: 15px;
  line-height: 1;
  color: rgba(11, 15, 26, 0.45);
}

.plate>i.tl {
  top: -7px;
  left: -7px;
}

.plate>i.tr {
  top: -7px;
  right: -7px;
}

.plate>i.bl {
  bottom: -7px;
  left: -7px;
}

.plate>i.br {
  bottom: -7px;
  right: -7px;
}

.plate-head {
  max-width: 620px;
  margin-bottom: 48px;
}

.plate-head .section-title {
  margin-bottom: 16px;
}

.plate-lead {
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--ink-soft);
  margin: 0;
}

/* ---- a numbered rail of steps or points ---- */
.plate-rail {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 56px;
}

.rail-item {
  display: flex;
  gap: 20px;
  padding: 24px 0;
  border-top: 1px solid rgba(11, 15, 26, 0.14);
}

.rail-num {
  flex-shrink: 0;
  width: 30px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 1.4px;
  color: #4a6076;
  padding-top: 3px;
}

.rail-name {
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 7px;
}

.rail-desc {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0;
}

/* ---- three columns of copy on the plate ---- */
.plate-cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
}

.plate-col-name {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 10px;
  padding-top: 22px;
  border-top: 1px solid rgba(11, 15, 26, 0.2);
}

.plate-col-desc {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 0;
}

.plate-col-meta {
  display: block;
  margin-top: 14px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: #4a6076;
}

/* ---- metric tiles ---- */
.plate-metrics {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}

.metric {
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(11, 15, 26, 0.1);
  border-radius: 16px;
  padding: 26px 24px 28px;
}

.metric-val {
  font-family: var(--font-sans);
  font-size: clamp(30px, 3.4vw, 42px);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--ink);
  margin: 0 0 10px;
}

.metric-key {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
}

@media (max-width: 980px) {
  .plate-section {
    padding: 72px 28px;
  }

  .plate {
    padding: 44px 28px;
  }

  .plate-rail,
  .plate-cols {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .plate-cols {
    gap: 34px;
  }

  .plate-metrics {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 560px) {
  .plate-metrics {
    grid-template-columns: 1fr;
  }
}

/* ================= BOOKING PAGE =================
   The branded wrapper the call link lands on: the mark, what to expect, and the
   scheduler itself sitting on the same plate as everything else. */
.book-section {
  position: relative;
  z-index: 2;
  background: var(--bg);
  background-image: radial-gradient(rgba(11, 15, 26, 0.14) 1px, transparent 1px);
  background-size: 26px 26px;
  padding: 84px 56px 110px;
}

.book-plate {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
  background: #e8e6ec;
  padding: 0;
  display: grid;
  grid-template-columns: 380px 1fr;
}

.book-plate>i {
  position: absolute;
  font-style: normal;
  font-size: 15px;
  line-height: 1;
  color: rgba(11, 15, 26, 0.45);
}

.book-plate>i.tl {
  top: -7px;
  left: -7px;
}

.book-plate>i.tr {
  top: -7px;
  right: -7px;
}

.book-plate>i.bl {
  bottom: -7px;
  left: -7px;
}

.book-plate>i.br {
  bottom: -7px;
  right: -7px;
}

/* The identity rail. Dark, so the mark sits on brand ground. */
.book-aside {
  background: var(--ink-deep);
  color: #fff;
  padding: 44px 40px 48px;
  --line: rgba(255, 255, 255, 0.12);
  --text-wash: var(--text-wash-dark);
}

.book-mark {
  width: 62px;
  height: 62px;
  border-radius: 14px;
  background: #fff;
  padding: 7px;
  margin-bottom: 26px;
}

.book-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.book-who {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 6px;
}

.book-what {
  font-size: 14.5px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.62);
  margin: 0 0 30px;
}

.book-facts {
  list-style: none;
  margin: 0;
  padding: 0;
}

.book-fact {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 0;
  border-top: 1px solid var(--line);
  font-size: 14px;
  color: rgba(255, 255, 255, 0.86);
}

.book-fact svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-top: 3px;
  color: #7d94b8;
}

.book-note {
  margin: 30px 0 0;
  font-size: 12.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.42);
}

/* The scheduler well. Min-height reserves the space so the plate does not
   jump when the embed finishes loading. */
.book-embed {
  padding: 26px;
  min-height: 660px;
}

.book-embed-frame {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 608px;
  border: 1px solid rgba(11, 15, 26, 0.12);
  border-radius: 16px;
  background: #fff;
  overflow: hidden;
}

/* Sits over the iframe only — fills the frame it's embedded in, not the
   section around it — and fades out once Cal.com reports loaded. */
.cal-loader {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: #fff;
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: #656c7a;
  transition: opacity 0.4s ease;
}

.cal-loader.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.cal-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid rgba(11, 15, 26, 0.12);
  border-top-color: #4a6076;
  animation: cal-spin 0.8s linear infinite;
}

@keyframes cal-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cal-spinner {
    animation: none;
  }
}

.book-embed-frame iframe {
  width: 100%;
  height: 100%;
  min-height: 606px;
  border: 0;
  display: block;
}

.cal-loader {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #fff;
  z-index: 10;
  transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
}

.cal-loader.hidden {
  opacity: 0;
  visibility: hidden;
}

.cal-loader p {
  margin-top: 16px;
  color: #656c7a;
  font-size: 14px;
  font-weight: 500;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 3px solid rgba(11, 15, 26, 0.1);
  border-top: 3px solid #4a6076;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

@media (max-width: 900px) {
  .book-section {
    padding: 64px 20px 84px;
  }

  .book-plate {
    grid-template-columns: 1fr;
  }

  .book-embed {
    padding: 16px;
    min-height: 620px;
  }
}

.phone-screen {
  position: relative;
  overflow: hidden;
}

.phone-scroll {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;

  scrollbar-width: none;
  -ms-overflow-style: none;
}

.phone-scroll::-webkit-scrollbar {
  display: none;
}

.phone-scroll img {
  display: block;
  width: 100%;
  height: auto;
}

/* ================= SELECTED BUILDS =================
   A browser frame per project. Two files sit in the same viewport: the 3:2 hero
   crop on top, and the full-page capture underneath. Hovering fades the crop out
   and walks the tall capture upward, so the whole page plays inside the frame.
   Both are static files in /video — nothing here needs a server. */
.shots-section {
  padding: clamp(72px, 10vh, 118px) 56px;
  border-top: 1px solid var(--line);
}

.shots-inner {
  max-width: 1200px;
  margin: 0 auto;
}

.shots-head {
  max-width: 620px;
  margin-bottom: clamp(40px, 6vw, 64px);
}

/* .section-badge is built for the dark bands and goes invisible on white, so
   the light-ground version carries its own colours. */
.shots-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #fafbfc;
  font-size: 13px;
  font-weight: 500;
  color: var(--white);
  margin-bottom: 22px;
}

.shots-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #4a6076;
}

.shots-lead {
  font-size: 16px;
  line-height: 1.72;
  color: var(--text-muted);
  margin: 18px 0 0;
}

.shots-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 3.4vw, 46px);
}

.shot {
  margin: 0;
}

.shot-frame {
  border-radius: 14px;
  overflow: hidden;
  background: linear-gradient(160deg, #2c3340, #14181f 45%, #1e232c);
  padding: 0 0 7px;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.07),
    0 2px 5px rgba(11, 15, 26, 0.05),
    0 22px 46px -18px rgba(11, 15, 26, 0.34);
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}

.shot-chrome {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
}

.shot-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  flex-shrink: 0;
}

.shot-dot:first-child {
  background: rgba(255, 255, 255, 0.34);
}

.shot-url {
  flex: 1;
  margin-left: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  font-family: var(--font-sans);
  font-size: 11.5px;
  letter-spacing: 0.2px;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shot-url em {
  font-style: normal;
  color: rgba(255, 255, 255, 0.34);
}

.shot-view {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: #0b0e13;
  margin: 0 7px;
  border-radius: 8px;
}

.shot-hero,
.shot-full {
  display: block;
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

.shot-hero {
  height: 100%;
  object-fit: cover;
  object-position: top center;
  z-index: 2;
  transition: opacity 0.5s ease;
}

/* Tall capture, its own height many times the viewport's. Translating by a
   percentage resolves against that height, so one figure works for every
   capture regardless of how long the page ran. */
.shot-full {
  height: auto;
  z-index: 1;
  transition: transform 0.6s ease;
}

.shot-hint {
  position: absolute;
  z-index: 3;
  right: 10px;
  bottom: 10px;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(8, 9, 15, 0.68);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
  opacity: 0;
  translate: 0 6px;
  transition: opacity 0.4s ease, translate 0.4s ease;
}

.shot-cap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: 16px;
}

.shot-cap-right {
  display: flex;
  align-items: center;
  gap: 14px;
}

.shot-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--white);
}

.shot-tag {
  font-size: 12px;
  color: var(--text-muted);
}

.shot-open {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  color: #4a6076;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 14px;
  cursor: pointer;
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}

.shot-open:hover {
  background: #4a6076;
  border-color: #4a6076;
  color: #fff;
}

/* The frame currently loaded into the phone above. */
.shot.is-current .shot-frame {
  outline: 2px solid #4a6076;
  outline-offset: 3px;
}

.shot.is-current .shot-open {
  background: #4a6076;
  border-color: #4a6076;
  color: #fff;
}

/* ---- case stage copy ---- */
.case-eyebrow {
  display: inline-block;
  padding: 6px 14px;
  margin-bottom: 16px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
}

.case-back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: 26px;
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.72);
  text-decoration: none;
  transition: color 0.25s ease;
}

.case-back svg {
  width: 13px;
  height: 13px;
  transition: translate 0.25s ease;
}

.case-back:hover {
  color: #fff;
}

.case-back:hover svg {
  translate: -3px 0;
}

/* Hover drives it on pointer devices; touch gets the same thing from a tap,
   which the script below toggles as .is-playing. */
@media (hover: hover) {
  .shot:hover .shot-frame {
    transform: translateY(-4px);
    box-shadow:
      0 0 0 1px rgba(0, 0, 0, 0.07),
      0 2px 5px rgba(11, 15, 26, 0.05),
      0 34px 62px -20px rgba(11, 15, 26, 0.42);
  }

  .shot:hover .shot-hero {
    opacity: 0;
  }

  .shot:hover .shot-hint {
    opacity: 1;
    translate: 0 0;
  }

  /* Stops short of the true bottom so the shortest capture in the set still has
     travel left and none of them bottom out early. */
  .shot:hover .shot-full {
    transform: translateY(-93%);
    transition: transform 9s linear;
  }
}

.shot.is-playing .shot-hero {
  opacity: 0;
}

.shot.is-playing .shot-full {
  transform: translateY(-93%);
  transition: transform 9s linear;
}

/* Without a hover state to hint at, touch needs the badge visible up front so
   the frame reads as something you can tap. */
@media (hover: none) {
  .shot-hint {
    opacity: 1;
    translate: 0 0;
  }

  .shot.is-playing .shot-hint {
    background: rgba(74, 96, 118, 0.9);
  }
}

@media (prefers-reduced-motion: reduce) {

  .shot-frame,
  .shot-hero,
  .shot-full,
  .shot-hint {
    transition: none;
  }

  .shot:hover .shot-full,
  .shot.is-playing .shot-full {
    transform: none;
    transition: none;
  }

  .shot:hover .shot-hero,
  .shot.is-playing .shot-hero {
    opacity: 1;
  }
}

@media (max-width: 900px) {
  .shots-section {
    padding: 64px 20px;
  }

  .shots-grid {
    grid-template-columns: 1fr;
    gap: 30px;
  }
}

/* ================= MOBILE / TABLET TYPE SCALE =================
   The type was tuned at desktop reading distance, where 13–15px body copy is
   comfortable. A phone is held closer but the text sits in a much narrower
   column, and at those sizes it reads cramped. This block lifts body copy,
   labels and controls a step or two; headings already scale with clamp() and
   are left alone. Last in the file so it wins on equal specificity.

   Form fields go to 16px deliberately: below that, iOS Safari zooms the page
   on focus and the user has to pinch back out. */
@media (max-width: 1024px) {

  /* ---- body copy ---- */
  .section-body {
    font-size: 17px;
  }

  .page-hero-lead,
  .shots-lead {
    font-size: 17.5px;
  }

  .faq-a-inner p,
  .testi-quote,
  .svc-desc,
  .value-desc,
  .rail-desc,
  .plate-col-desc,
  .step-desc,
  .note-desc,
  .book-what,
  .spec-item {
    font-size: 16px;
  }

  .feature-item,
  .book-fact {
    font-size: 15.5px;
  }

  /* ---- names and headings that don't use clamp() ---- */
  .faq-label,
  .svc-name {
    font-size: 18px;
  }

  .work-name {
    font-size: 17.5px;
  }

  .testi-name {
    font-size: 16px;
  }

  /* ---- controls ---- */
  .btn-primary,
  .btn-secondary,
  .btn-start,
  .btn-submit,
  .cta-band .btn-primary {
    font-size: 16px;
  }

  .btn-pricing,
  .mobile-menu .mobile-cta {
    font-size: 15.5px;
  }

  .work-chip {
    font-size: 14.5px;
  }

  .form-group label {
    font-size: 15px;
  }

  .form-group input,
  .form-group select,
  .form-group textarea {
    font-size: 16px;
  }

  /* ---- badges, meta and micro-labels ---- */
  .section-badge,
  .shots-badge,
  .faq-badge,
  .status-badge,
  .start-badge-pill {
    font-size: 14.5px;
  }

  .page-hero-meta span,
  .book-note,
  .badge-sub,
  .price-note,
  .value-num {
    font-size: 14px;
  }

  .price-from {
    font-size: 14.5px;
  }

  .work-cat,
  .shot-tag,
  .footer-copy,
  .metric-key,
  .cred-pill,
  .rail-num,
  .plate-col-meta {
    font-size: 13.5px;
  }

  .footer-link,
  .footer-tagline {
    font-size: 15px;
  }

  .contact-label,
  .stat-label,
  .price-tier {
    font-size: 12.5px;
  }

  .badge-label {
    font-size: 11px;
  }
}