/* ============================================================
   Lintel — shared design system
   oklch tokens + Fraunces/Sora. Midnight, concrete-silver, electric
   violet and warm coral, one gradient built from that pair used
   consistently for the hero and brand accents. Light mode is the
   default (a clean, cool neutral, not stark white). Dark mode is a
   toggle, stored in localStorage. Restraint over spectacle, per
   build-notes.md: short transitions, no bounce/spin/parallax.
   ============================================================ */

:root {
  --ink: oklch(21% 0.03 288);
  --paper: oklch(97% 0.004 260);
  --accent: oklch(63% 0.25 304);
  --accent-soft: oklch(63% 0.25 304 / 0.12);
  --accent-warm: oklch(78% 0.16 58);
  --accent-warm-soft: oklch(78% 0.16 58 / 0.14);
  --muted: oklch(55% 0.02 280);
  --mutedlight: oklch(75% 0.01 280);

  /* semantic tokens, light mode (default) */
  --bg: var(--paper);
  --bg-raised: oklch(100% 0 0);
  --fg: var(--ink);
  --fg-muted: var(--muted);
  --border: oklch(90% 0.006 280);
  --border-strong: oklch(80% 0.02 290 / 0.4);
  --card-bg: oklch(100% 0 0);
  --band-bg: oklch(95% 0.02 304);
  --overlay: oklch(21% 0.03 288 / 0.55);
  --shadow: oklch(21% 0.03 288 / 0.14);
  --hero-gradient: linear-gradient(160deg, oklch(95% 0.025 304) 0%, oklch(92% 0.05 318) 45%, oklch(89% 0.09 55) 100%);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --speed: 200ms;
  --speed-fast: 150ms;
  --speed-slow: 300ms;

  --radius: 10px;
  --radius-sm: 6px;
  --radius-pill: 999px;
  --max: 1120px;
}

:root[data-theme="dark"] {
  --bg: var(--ink);
  --bg-raised: oklch(27% 0.04 292);
  --fg: var(--paper);
  --fg-muted: var(--mutedlight);
  --border: oklch(80% 0.01 290 / 0.16);
  --border-strong: oklch(80% 0.02 290 / 0.32);
  --card-bg: oklch(26% 0.04 292);
  --band-bg: oklch(30% 0.06 300);
  --overlay: oklch(8% 0.02 288 / 0.65);
  --shadow: oklch(4% 0.02 288 / 0.5);
  /* Kept inside a narrow 19%-33% lightness band on purpose (unlike the
     light-mode version above, which can range wider) — this gradient now
     paints the full <body>, so on a long page it has to stay dark enough
     end-to-end for --paper (near-white) text to stay readable against it
     the whole way down, not just near the top. */
  --hero-gradient: linear-gradient(160deg, oklch(19% 0.04 300) 0%, oklch(27% 0.09 308) 35%, oklch(33% 0.13 322) 65%, oklch(29% 0.11 50) 100%);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: 'Sora', ui-sans-serif, system-ui, -apple-system, sans-serif;
  /* The violet-to-coral wash lives on <body> (not background-attachment:
     fixed, which iOS Safari doesn't handle reliably) so it naturally
     stretches over each page's full document height — every page, not
     just hero sections. var(--bg) stays as the flat fallback/base tone
     underneath, and small opaque UI surfaces (cards, popups, form fields,
     the browser/monitor mockup) keep using var(--bg)/var(--card-bg)
     directly so they still read as solid objects sitting on the wash. */
  background: var(--bg) var(--hero-gradient);
  color: var(--fg);
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
  -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; }

.container {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 24px;
}

section { padding: 88px 0; }
section.tight { padding: 56px 0; }

h1, h2, h3, h4 {
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin: 0 0 20px;
}

h1 { font-size: clamp(34px, 5vw, 56px); }
h2 { font-size: clamp(26px, 3.4vw, 38px); }
h3 { font-size: 22px; }

p { line-height: 1.65; color: var(--fg-muted); margin: 0 0 16px; font-size: 17px; }
p.lead { font-size: 20px; color: var(--fg); }

.eyebrow {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
  display: block;
}
/* homepage hero eyebrow: cycles through the six services, typewriter-style
   (see main.js .js-cycle-eyebrow). The cursor only exists once JS takes
   over; static markup has no cursor at all so a no-JS page doesn't show a
   stray blinking bar next to nothing. */
.cycle-cursor {
  display: inline-block;
  margin-left: 2px;
  animation: cycle-blink 1s step-end infinite;
}
@keyframes cycle-blink { 50% { opacity: 0; } }

.muted { color: var(--fg-muted); }

/* ---------- doorframe mark / logo ---------- */
.brand-mark { display: inline-flex; }
.brand-mark svg path { stroke: currentColor; }
/* settle-in animation: the two verticals draw in first, then the top bar
   draws and drops into place on top of them, like a lintel actually being
   set over a doorway. Runs once on load and again inside the theme-toggle's
   view-transition (main.js toggles .is-set off/on to restart it). */
/* Default state is fully drawn and visible (dashoffset 0) — a no-JS visitor,
   or the instant before main.js runs, just sees the finished mark, never a
   blank one. Only the .is-set state (JS-driven) resets the offset so the
   animation has somewhere to draw in from. */
.brand-mark .lm-l, .brand-mark .lm-r, .brand-mark .lm-bar { stroke-dasharray: 34; stroke-dashoffset: 0; }
.brand-mark .lm-bar { stroke-dasharray: 48; transform-origin: 32px 20px; }
.brand-mark.is-set .lm-l { stroke-dashoffset: 34; animation: lm-draw 380ms var(--ease) forwards; }
.brand-mark.is-set .lm-r { stroke-dashoffset: 34; animation: lm-draw 380ms var(--ease) 90ms forwards; }
.brand-mark.is-set .lm-bar { stroke-dashoffset: 48; animation: lm-draw 360ms var(--ease) 420ms forwards, lm-drop 380ms var(--ease) 420ms forwards; }
@keyframes lm-draw { to { stroke-dashoffset: 0; } }
@keyframes lm-drop {
  0% { transform: translateY(-9px); opacity: 0.4; }
  70% { transform: translateY(2px); }
  100% { transform: translateY(0); opacity: 1; }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  font-size: 18px;
  text-decoration: none;
  color: var(--fg);
}
.brand-word { display: inline-flex; font-family: 'Fraunces', ui-serif, Georgia, serif; }

@media (prefers-reduced-motion: reduce) {
  .brand-mark .lm-l, .brand-mark .lm-r, .brand-mark .lm-bar { stroke-dasharray: none; stroke-dashoffset: 0; }
  .brand-mark.is-set .lm-l, .brand-mark.is-set .lm-r, .brand-mark.is-set .lm-bar { animation: none !important; }
}

/* ---------- nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in oklch, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 0;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 30px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links > li > a:not(.btn) {
  position: relative;
  text-decoration: none;
  color: var(--fg-muted);
  font-size: 15px;
  font-weight: 500;
  padding: 6px 0;
  transition: color var(--speed) var(--ease);
}
.nav-links > li > a:not(.btn)::after {
  content: "";
  position: absolute;
  left: 0; right: 100%;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transition: right var(--speed) var(--ease);
}
.nav-links > li > a:not(.btn):hover, .nav-links > li > a:not(.btn).active { color: var(--fg); }
.nav-links > li > a:not(.btn):hover::after, .nav-links > li > a:not(.btn).active::after { right: 0; }
.nav-right { display: flex; align-items: center; gap: 14px; }
.nav-links li.mobile-nav-cta { display: none; }

/* ---------- nav services dropdown ---------- */
.nav-item { position: relative; list-style: none; }
.nav-dropdown-panel {
  position: absolute;
  top: 100%;
  left: 50%;
  width: max-content;
  /* This top padding is an invisible bridge: the panel's own hoverable box
     now starts right at the trigger's bottom edge, so the cursor never
     leaves .nav-item's hit area while moving down toward the visible
     dropdown (which is pushed down by this same padding). Without it, the
     gap between trigger and panel had no element in it, so :hover was lost
     mid-travel and the dropdown closed before you could reach it. */
  padding-top: 16px;
  transform: translateX(-50%) translateY(-8px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease), visibility var(--speed);
  z-index: 50;
}
.nav-item:hover .nav-dropdown-panel,
.nav-item.open .nav-dropdown-panel {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}
.nav-dropdown-inner {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 20px 44px -18px var(--shadow);
  padding: 10px;
  min-width: 250px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.nav-dropdown-panel a {
  display: block;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--fg-muted);
  text-decoration: none;
}
.nav-dropdown-panel a::after { display: none; }
.nav-dropdown-panel a:hover { background: var(--accent-soft); color: var(--fg); }
.nav-dropdown-panel a.nav-dropdown-all {
  margin-top: 6px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-weight: 600;
  color: var(--accent);
}

.theme-toggle {
  width: 38px; height: 38px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--fg);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.theme-toggle:hover { border-color: var(--border-strong); }
.theme-toggle svg { width: 17px; height: 17px; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }

/* Theme toggle circular reveal — a plain element main.js creates and
   removes on each click, not the View Transitions API. Two attempts at
   driving this off ::view-transition-new(root) (a JS Web-Animations-API
   clip-path, then a CSS @keyframes one reading a custom property) both
   measured correctly in every test here but still started from the wrong
   spot in real use — that pseudo-element's own box can apparently get
   scaled/repositioned by the browser in ways plain getBoundingClientRect()
   math doesn't account for, and there's no way to inspect that from
   outside the browser doing it. A real DOM node with an ordinary
   clip-path transition has no such translation step: its position is
   exactly the pixel values main.js gives it, in every browser, every
   time. Slower than before on purpose too (900ms grow + a short fade,
   versus 500ms total) — it was reported as too fast once already sped up
   by switching mechanisms, so this errs toward clearly-visible. */
.theme-reveal {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  background: var(--tr-bg, var(--bg));
  clip-path: circle(0px at var(--trx, 50%) var(--try, 50%));
  transition: clip-path 900ms cubic-bezier(0.76, 0, 0.24, 1);
}
.theme-reveal.is-covering {
  clip-path: circle(var(--tr-radius, 150%) at var(--trx, 50%) var(--try, 50%));
}
.theme-reveal.is-fading {
  transition: opacity 260ms ease-out;
  opacity: 0;
}

.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--fg);
  cursor: pointer;
}

@media (max-width: 860px) {
  /* a little extra breathing room off the screen edges, on top of the
     container's own 24px padding */
  .brand { margin-left: 6px; }
  .nav-right { margin-right: 6px; }

  .nav-links {
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: 14px 24px 22px;
    /* animated open/close instead of a hard display:none/flex snap: always
       laid out, just collapsed to nothing and non-interactive when closed */
    display: flex;
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    transform: translateY(-6px);
    pointer-events: none;
    visibility: hidden;
    border-bottom-color: transparent;
    box-shadow: none;
    transition: max-height var(--speed-slow) var(--ease),
                opacity var(--speed) var(--ease),
                transform var(--speed-slow) var(--ease),
                box-shadow var(--speed-slow) var(--ease),
                border-color var(--speed-slow) var(--ease),
                visibility var(--speed-slow);
  }
  .nav-links.open {
    max-height: 620px;
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
    visibility: visible;
    border-bottom-color: var(--border);
    /* separates the open menu from the page behind it, since a plain
       border reads as invisible against a near-white background */
    box-shadow: 0 24px 44px -20px var(--shadow),
                0 0 0 1px color-mix(in oklch, var(--accent) 14%, transparent),
                0 18px 36px -18px color-mix(in oklch, var(--accent) 16%, transparent);
  }
  .nav-links a { display: block; width: 100%; padding: 11px 0; }
  .nav-item { width: 100%; }
  .nav-dropdown-panel {
    position: static;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
    padding-top: 0;
    width: auto;
    min-width: 0;
  }
  .nav-dropdown-inner {
    box-shadow: none;
    border: none;
    background: transparent;
    padding: 2px 0 6px 14px;
    min-width: 0;
    gap: 2px;
  }
  .nav-dropdown-panel a { padding: 9px 0; }
  .nav-toggle { display: flex; align-items: center; justify-content: center; }
  .nav-toggle svg { width: 20px; height: 20px; }
  .nav-right .header-cta { display: none; }
  .nav-links li.mobile-nav-cta { display: block; margin-top: 10px; width: 100%; }
  /* ".nav-links a { display: block; }" above (higher specificity than the
     plain ".btn" rule) was overriding the button's own flex centering, so
     its label sat flush left instead of centered. Restated here with more
     specificity to win it back. */
  .nav-links li.mobile-nav-cta .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
  }
}

/* ---------- touch devices: no hover, so give the hover-only glow/lift
   effects a fixed, always-on version instead (pills, tier tags, cards) ---------- */
@media (hover: none) {
  .pill {
    border-color: color-mix(in oklch, var(--accent) 55%, var(--border-strong));
    box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 30%, transparent),
                0 0 14px 1px color-mix(in oklch, var(--accent) 24%, transparent);
  }
  .badge {
    border-color: color-mix(in oklch, var(--accent) 45%, var(--border-strong));
    box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 25%, transparent),
                0 0 10px 1px color-mix(in oklch, var(--accent) 18%, transparent);
  }
  /* .card's fixed-glow rules live further down, right after the base .card
     rules (around line ~510) instead of here: the base ".card { box-shadow:
     ... }" declared later in this file has equal (0,1,0) specificity, and
     equal specificity is decided by source order, so a ".card" override up
     here would just get silently overwritten by the later base rule. */
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  padding: 13px 24px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: transform var(--speed-fast) var(--ease), border-color var(--speed) var(--ease), background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn-primary {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, var(--fg), color-mix(in oklch, var(--fg) 78%, var(--accent) 22%));
  color: var(--bg);
  box-shadow: 0 10px 24px -14px var(--shadow);
}
.btn-primary:hover {
  background: linear-gradient(135deg, var(--accent), color-mix(in oklch, var(--accent) 75%, var(--fg) 25%));
  box-shadow: 0 14px 30px -14px var(--shadow);
  transform: translateY(-1px);
}
/* click shine: a light diagonal sweep on click (main.js toggles .shine).
   mix-blend-mode:overlay so the same white gradient reads correctly on
   both the dark-on-light button (light mode) and the pale button
   (dark mode) without needing separate per-theme colors. */
.btn-primary::after {
  content: "";
  position: absolute;
  top: 0; left: -75%;
  width: 50%; height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.55), transparent);
  transform: skewX(-20deg);
  mix-blend-mode: overlay;
  pointer-events: none;
}
.btn-primary.shine::after { animation: btn-shine-sweep 650ms ease; }
@keyframes btn-shine-sweep {
  from { left: -75%; }
  to { left: 125%; }
}
.btn-secondary { background: transparent; color: var(--fg); border-color: var(--border-strong); }
.btn-secondary:hover { border-color: var(--accent); color: var(--accent); }
.btn-ghost { background: transparent; color: var(--fg-muted); border: none; padding: 6px 0; font-weight: 500; text-decoration: underline; text-underline-offset: 4px; text-decoration-color: transparent; }
.btn-ghost:hover { text-decoration-color: var(--accent); color: var(--fg); }
.btn-sm { padding: 9px 16px; font-size: 13px; }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------- hero ---------- */
/* The gradient wash itself now lives on <body> (see above) and spans the
   whole site. .hero-group no longer paints its own copy of it — it just
   stays transparent here so the body wash shows straight through, and
   layers the two radial blobs + blueprint-grid texture below on top of it
   as hero-only extras, which is what actually earns this section a
   heavier look than the rest of the page. */
.hero-group {
  position: relative;
  overflow: hidden;
}
.hero-group::before {
  content: "";
  position: absolute;
  top: -14%; right: -10%;
  width: 640px; height: 640px;
  background: radial-gradient(circle, color-mix(in oklch, var(--accent) 22%, transparent), transparent 70%);
  pointer-events: none;
  z-index: 0;
  animation: hero-drift-a 19s ease-in-out infinite;
}
.hero-group::after {
  content: "";
  position: absolute;
  bottom: -18%; left: -8%;
  width: 460px; height: 460px;
  background: radial-gradient(circle, color-mix(in oklch, var(--accent-warm) 20%, transparent), transparent 70%);
  pointer-events: none;
  z-index: 0;
  animation: hero-drift-b 23s ease-in-out infinite;
}
/* faint blueprint-grid texture behind the hero, same device used on The
   Build page's mockup, so the architecture motif shows up before you ever
   scroll to it */
.hero-group .container::before {
  content: "";
  position: absolute;
  inset: -40px -24px auto -24px;
  height: 480px;
  z-index: 0;
  background-image:
    linear-gradient(color-mix(in oklch, var(--fg) 8%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklch, var(--fg) 8%, transparent) 1px, transparent 1px);
  background-size: 28px 28px;
  -webkit-mask-image: linear-gradient(to bottom, black, transparent);
  mask-image: linear-gradient(to bottom, black, transparent);
  pointer-events: none;
}
/* slow, subtle drift so the hero wash doesn't feel like a static flat
   gradient; kept small (a few percent, not full parallax) per the "restraint
   over spectacle" rule this file otherwise follows. */
@keyframes hero-drift-a {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-3%, 2.5%) scale(1.06); }
}
@keyframes hero-drift-b {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(2.5%, -2%) scale(1.04); }
}
.hero-group .container { position: relative; z-index: 1; }
.hero { padding: 100px 0 64px; }
.hero .nav-pills { display: flex; flex-wrap: wrap; gap: 10px; margin: 28px 0 14px; }
/* mobile: a little more breathing room between pills than desktop's 10px
   (Mario: "they are too glued to each other"). Matched to ".hero .nav-pills"
   above (same 0,2,0 specificity) and placed after it, since equal
   specificity is decided by source order, not by which is inside the media
   query — a plain ".nav-pills { gap: 14px }" up in the earlier mobile block
   was silently losing to the rule above for exactly this reason. */
@media (max-width: 860px) {
  .hero .nav-pills { gap: 14px; }
}
.hero-actions { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin-top: 22px; }
.pill {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  font-family: inherit;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  color: var(--fg);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease), transform var(--speed-fast) var(--ease), box-shadow var(--speed) var(--ease);
}
.pill:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateY(-2px);
  /* subtle in-theme glow on top of the existing lift, per Mario's LUMEN reference */
  box-shadow: 0 8px 20px -12px var(--shadow),
              0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent),
              0 0 18px 2px color-mix(in oklch, var(--accent) 30%, transparent);
}
.pill:active { transform: translateY(0); }

/* ---------- stat / counter ---------- */
.stat-row { display: flex; align-items: baseline; gap: 14px; padding: 8px 0 4px; }
.stat-num {
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-weight: 700;
  font-size: clamp(48px, 7vw, 76px);
  color: var(--fg);
  background: linear-gradient(135deg, var(--accent), var(--accent-warm));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  font-variant-numeric: tabular-nums;
}
.stat-label { font-size: 16px; color: var(--fg-muted); max-width: 320px; }

/* ---------- cards / grids ---------- */
.grid { display: grid; gap: 24px; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-5 { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 900px) { .grid-3, .grid-5 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid-3, .grid-2, .grid-5 { grid-template-columns: 1fr; } }

.card {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--card-bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 28px;
  /* always-on soft elevation so the card reads as a raised object by its
     own shadow, not just by contrast against whatever background sits
     behind it (needed once some band sections match the plain page bg) */
  box-shadow: 0 2px 6px -3px var(--shadow), 0 16px 32px -26px var(--shadow);
  transition: transform var(--speed) var(--ease), border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
  text-decoration: none;
  color: inherit;
  display: block;
}
/* cursor-following spotlight glow, clipped to the card by overflow:hidden above */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: radial-gradient(220px circle at var(--spot-x, 50%) var(--spot-y, 50%), color-mix(in oklch, var(--accent) 20%, transparent), transparent 72%);
  opacity: 0;
  transition: opacity var(--speed) var(--ease);
}
.card > * { position: relative; z-index: 1; }
/* NOTE: ".card:hover, .card.fade-in:hover" (not just ".card:hover") is
   deliberate: a plain ".card:hover" ties with ".fade-in.in-view" at equal
   specificity, and since .in-view is declared later in this file it was
   winning and silently cancelling the hover lift on any card that also
   carries the fade-in scroll-reveal class (this is exactly why the
   homepage Services cards only showed the color change, never the lift).
   The extra ".fade-in" class raises specificity so hover always wins. */
.card:hover,
.card.fade-in:hover {
  transform: translateY(-5px);
  border-color: var(--accent);
  box-shadow: 0 22px 40px -20px var(--shadow), 0 0 22px 1px color-mix(in oklch, var(--accent) 24%, transparent);
}
.card:hover::before { opacity: 1; }
.card h3 { margin-bottom: 10px; }
.card p { margin-bottom: 0; font-size: 15px; }
.card .badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }

/* touch devices: no hover, so give the cursor-spotlight glow a fixed,
   always-on version instead. Placed here (after the base .card rules just
   above, not up in the earlier "@media (hover: none)" block) so it wins:
   both are plain ".card" selectors at equal (0,1,0) specificity, and equal
   specificity is decided by source order, not by which was declared inside
   a media query. */
@media (hover: none) {
  .card {
    border-color: color-mix(in oklch, var(--accent) 30%, var(--border-strong));
    box-shadow: 0 2px 6px -3px var(--shadow), 0 16px 32px -26px var(--shadow),
                0 0 18px 1px color-mix(in oklch, var(--accent) 16%, transparent);
  }
  /* no cursor to track, so just show the spotlight softly centered */
  .card::before { opacity: 0.45; }
}

.badge {
  font-size: 12px;
  font-weight: 600;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  color: var(--fg-muted);
  white-space: nowrap;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), color var(--speed) var(--ease);
}
.badge:hover {
  border-color: var(--accent);
  color: var(--fg);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent),
              0 0 14px 1px color-mix(in oklch, var(--accent) 28%, transparent);
}

.list-plain { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.list-plain li { padding-left: 26px; position: relative; color: var(--fg); line-height: 1.55; }
.list-plain li::before {
  content: "";
  position: absolute; left: 0; top: 9px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

/* ---------- beam-list: a vertical beam with a dot that travels down it as
   you scroll, lighting each step's node as it passes — the "how it works"
   numbered steps reimagined as a load-bearing beam instead of a plain
   counter list. main.js (.beam-wrap) computes scroll progress and moves
   the real .beam-dot element + toggles .is-lit on each step. */
.beam-wrap { position: relative; }
.beam-list { list-style: none; margin: 0; padding: 0; counter-reset: step; display: flex; flex-direction: column; gap: 0; }
.beam-wrap::before {
  content: "";
  position: absolute;
  left: 15px; top: 14px; bottom: 14px;
  width: 2px;
  background: var(--border-strong);
}
.beam-dot {
  /* Sits BEHIND the numbered nodes (z-index below theirs) and carries a
     tighter glow than before — previously it painted on top and its halo
     was wide enough to wash out whichever number it passed, which is
     exactly what looked broken. Now the dot visibly travels the line
     between steps and slips behind each node as it passes, with only a
     soft edge of glow showing around the node rather than covering it. */
  position: absolute;
  left: 15px; top: 14px;
  width: 10px; height: 10px;
  margin-left: -5px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-warm));
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 18%, transparent),
              0 0 9px 1px color-mix(in oklch, var(--accent-warm) 35%, transparent);
  pointer-events: none;
  z-index: 1;
}
.beam-list li {
  position: relative;
  display: flex; gap: 18px; align-items: flex-start;
  counter-increment: step;
  padding: 12px 0 12px 42px;
}
.beam-list li::before {
  content: counter(step);
  position: absolute;
  left: 5px; top: 12px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--border-strong);
  color: var(--fg-muted);
  font-size: 11px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease), background var(--speed) var(--ease);
  z-index: 2;
}
.beam-list li.is-lit::before {
  border-color: transparent;
  color: var(--bg);
  background: linear-gradient(135deg, var(--accent), var(--accent-warm));
}

/* ---------- about teaser (home) ---------- */
.about-teaser {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 60px;
  align-items: start;
}
.about-teaser-list { padding-top: 4px; }
.about-teaser-list .list-plain { margin-top: 4px; }
@media (max-width: 900px) {
  .about-teaser { grid-template-columns: 1fr; gap: 32px; }
}

/* ---------- fade-in on scroll ---------- */
.fade-in {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--speed-slow) var(--ease), transform var(--speed-slow) var(--ease);
}
.fade-in.in-view { opacity: 1; transform: none; }

/* ---------- headline word-by-word reveal (main.js splits the H1 text into
   these spans; the H1 itself stays visible/static, only the words move) ---------- */
.split-reveal { opacity: 1; }
.word { display: inline-block; overflow: hidden; vertical-align: top; }
.word-inner {
  display: inline-block;
  transform: translateY(115%);
  transition: transform 560ms var(--ease);
}
.split-reveal.in-view .word-inner { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .fade-in { opacity: 1; transform: none; transition: none; }
  .card::before { display: none; }
  .word-inner { transform: none !important; transition: none !important; }
  .page-wipe { display: none !important; }
  .hero-group::before, .hero-group::after { animation: none !important; }
  .cycle-cursor { animation: none !important; }
  * { scroll-behavior: auto !important; }
  .beam-dot { display: none !important; }
  .beam-list li::before {
    border-color: transparent;
    color: var(--bg);
    background: linear-gradient(135deg, var(--accent), var(--accent-warm));
  }
  .level-bubble { transition: none !important; animation: none !important; left: calc(50% - 7px) !important; }
}

/* ---------- sections: alt background band ---------- */
/* Translucent, not solid, so the body-wide gradient wash keeps showing
   through band sections instead of getting blocked by an opaque tint —
   the whole point of moving the wash to <body> was for it to run under
   every section, band or not. */
.band { background: color-mix(in oklch, var(--band-bg) 55%, transparent); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }

/* ---------- calculator ---------- */
.calculator {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 32px;
  background: var(--card-bg);
}
.calc-step-label { font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); margin-bottom: 14px; }
.calc-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 28px; }
.calc-option {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 18px; cursor: pointer;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.calc-option:hover { border-color: var(--border-strong); }
.calc-option.selected { border-color: var(--accent); background: var(--accent-soft); }
.calc-option input { accent-color: var(--accent); width: 17px; height: 17px; flex: none; }
.calc-option .opt-text { flex: 1; font-size: 15px; }
.calc-option .opt-price { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.calc-total {
  display: flex; align-items: baseline; justify-content: space-between;
  border-top: 1px solid var(--border); padding-top: 22px; margin-top: 6px;
}
.calc-total-row + .calc-total-row { margin-top: 10px; }
.calc-total-label { font-size: 15px; color: var(--fg-muted); }
.calc-total-value {
  font-size: 30px; font-weight: 700; font-variant-numeric: tabular-nums;
  transition: color var(--speed) var(--ease);
}
.calc-discount-note { font-size: 13px; color: var(--accent); margin-top: 6px; }

/* ---------- popup ---------- */
.popup-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: var(--overlay);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  opacity: 0; visibility: hidden;
  transition: opacity var(--speed) var(--ease);
}
.popup-overlay.open { opacity: 1; visibility: visible; }
.popup {
  position: relative;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-width: 480px; width: 100%;
  max-height: 88vh;
  overflow: hidden;
  transform: scale(0.96) translateY(6px);
  transition: transform var(--speed) var(--ease);
}
.popup-overlay.open .popup { transform: scale(1) translateY(0); }
/* the scrolling part of the popup, separate from .popup itself so the close
   button (a sibling, not a child of this) never scrolls out of reach */
.popup-body { max-height: 88vh; overflow-y: auto; padding: 34px; }
.popup-head { margin-bottom: 6px; padding-right: 34px; }
.popup-close {
  position: absolute;
  top: 14px; right: 14px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--fg-muted);
  font-size: 20px; line-height: 1;
  z-index: 5;
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.popup-close:hover { color: var(--fg); border-color: var(--accent); }
.popup p.sub { margin-top: -6px; margin-bottom: 22px; font-size: 14px; }

/* ---------- forms ---------- */
.field { margin-bottom: 18px; }
.field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 7px; color: var(--fg); }
.field input[type="text"],
.field input[type="email"],
.field select,
.field textarea {
  width: 100%;
  font-family: inherit;
  font-size: 15px;
  padding: 12px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-raised);
  color: var(--fg);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  /* grows in smoothly (box-shadow is in the transition list just above)
     rather than snapping on, per Mario's "more click/focus animation" ask */
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 18%, transparent);
}
.field textarea { resize: vertical; min-height: 90px; }
.checkbox-grid { display: flex; flex-direction: column; gap: 10px; }
.checkbox-row { display: flex; align-items: center; gap: 10px; font-size: 15px; }
.checkbox-row input { accent-color: var(--accent); width: 17px; height: 17px; }
.form-note { font-size: 13px; color: var(--fg-muted); margin-top: 12px; text-align: center; }
.form-success { display: none; text-align: center; padding: 20px 0; }
.form-success.show { display: block; }
.form-success .brand-mark { color: var(--accent); margin-bottom: 14px; }

/* ---------- FAQ / honest answers ---------- */
.qa { border-top: 1px solid var(--border); padding: 22px 0; }
.qa:first-child { border-top: none; }
.qa h4 { margin: 0 0 8px; font-size: 17px; }
.qa p { margin: 0; }

/* ---------- callout ---------- */
.callout {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 22px 26px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-top: 12px;
}
.callout p { margin: 0; }

/* ---------- footer ---------- */
.site-footer { border-top: 1px solid var(--border); padding: 56px 0 40px; }
.footer-top { display: flex; justify-content: space-between; gap: 40px; flex-wrap: wrap; margin-bottom: 40px; }
.footer-cols { display: flex; gap: 60px; flex-wrap: wrap; }
.footer-col h4 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-muted); margin-bottom: 14px; }
.footer-col a { display: block; text-decoration: none; color: var(--fg-muted); font-size: 15px; margin-bottom: 10px; transition: color var(--speed) var(--ease); }
.footer-col a:hover { color: var(--fg); }
.footer-bottom {
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px;
  border-top: 1px solid var(--border); padding-top: 24px; font-size: 13px; color: var(--fg-muted);
  /* clears the fixed bottom-left back-to-top circle (44px wide, 24px from
     the edge) so it never sits on top of the copyright text once the page
     is scrolled all the way down; footer-bottom is the only row that ever
     shares that screen corner with it. */
  padding-left: 76px;
}
@media (max-width: 600px) {
  /* back-to-top shrinks to 40px/16px on this breakpoint (see .back-to-top
     below), so the same clearance needs less room here; flex-wrap above
     still lets it drop to two lines gracefully if a very long copy ever
     doesn't fit. */
  .footer-bottom { padding-left: 64px; }
}

/* ---------- closing CTA band ---------- */
.cta-band { text-align: center; padding: 90px 0; }
.cta-band h2 { margin-bottom: 30px; }

/* ---------- page hero (non-home) ---------- */
.page-hero { padding: 64px 0 20px; }
.page-hero .eyebrow { margin-bottom: 10px; }

/* ---------- page transition wipe ----------
   Reveals on every page load via a plain CSS animation (no JS needed for
   this half, so it still runs even if a script errors) and gets covered
   again by main.js right before an internal link navigates away. */
.page-wipe {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--accent);
  transform-origin: right;
  transform: scaleX(1);
  animation: page-wipe-reveal 480ms var(--ease) 60ms forwards;
  pointer-events: none;
}
@keyframes page-wipe-reveal {
  to { transform: scaleX(0); }
}
.page-wipe.covering {
  animation: none;
  transition: transform 360ms var(--ease);
  transform: scaleX(1);
}

/* ---------- scroll progress bar ---------- */
.scroll-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 200;
  pointer-events: none;
}
.scroll-progress-bar {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent), color-mix(in oklch, var(--accent) 55%, var(--fg) 45%));
  transition: width 80ms linear;
}

/* ---------- back to top ---------- */
.back-to-top {
  position: fixed;
  left: 24px;
  bottom: 24px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--fg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 10px 24px -12px var(--shadow);
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px) scale(0.9);
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease), visibility var(--speed), border-color var(--speed) var(--ease), color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
  z-index: 60;
}
.back-to-top.visible { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
.back-to-top:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-3px) scale(1);
  box-shadow: 0 16px 30px -14px var(--shadow), 0 0 16px 1px color-mix(in oklch, var(--accent) 32%, transparent);
}
.back-to-top svg { width: 18px; height: 18px; }
@media (max-width: 600px) {
  .back-to-top { left: 16px; bottom: 16px; width: 40px; height: 40px; }
}

/* ---------- "the-build" scroll demo page ----------
   A blank wireframe assembles into a finished mini site mockup as you
   scroll. Deliberately no JS-computed pin (that's what needs a library
   workaround for mobile Safari's address-bar jitter) — .build-sticky uses
   plain CSS `position: sticky`, which the browser already handles
   correctly on its own, on every device. main.js's counterpart
   (js/the-build.js) only computes a 0–1 scroll progress and per-element
   "reveal" fractions as CSS custom properties; every crossfade below is
   plain CSS reacting to those numbers, no animation library involved. */
.build-wrap {
  position: relative;
  height: 380vh;
}
.build-sticky {
  position: sticky;
  top: 96px; /* clears the sticky site header so the frame doesn't hide under it */
  height: calc(100vh - 140px);
  min-height: 420px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 20px 24px;
}
.browser-frame {
  width: min(920px, 100%);
  background: var(--card-bg);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: 0 40px 80px -40px var(--shadow);
  overflow: hidden;
}
.browser-chrome {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 12px 16px;
  background: var(--band-bg);
  border-bottom: 1px solid var(--border);
}
.browser-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border-strong); flex: none; }
.browser-url {
  margin-left: 10px;
  font-size: 12px;
  color: var(--fg-muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 14px;
}
.browser-body {
  position: relative;
  padding: clamp(18px, 4vw, 32px);
  isolation: isolate;
}
/* faint blueprint grid behind the unfinished mockup, fading out as the
   overall scroll progress (set on .build-wrap by js/the-build.js) advances */
.browser-body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(color-mix(in oklch, var(--accent) 16%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklch, var(--accent) 16%, transparent) 1px, transparent 1px);
  background-size: 22px 22px;
  opacity: calc(0.55 * (1 - var(--overall, 0)));
}

.reveal-stack { display: grid; }
.reveal-stack > * { grid-area: 1 / 1; min-width: 0; }
.reveal-skel { opacity: calc(1 - var(--reveal, 0)); }
.reveal-real {
  opacity: var(--reveal, 0);
  transform: translateY(calc((1 - var(--reveal, 0)) * 6px));
}
.skel-bar {
  display: inline-block;
  border-radius: 4px;
  background: color-mix(in oklch, var(--fg-muted) 32%, transparent);
  margin-bottom: 6px;
}
.skel-icon {
  display: inline-block;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: color-mix(in oklch, var(--fg-muted) 32%, transparent);
  margin-bottom: 8px;
}
.skel-block {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 120px;
  border-radius: 10px;
  background: color-mix(in oklch, var(--fg-muted) 22%, transparent);
}

.mock-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: clamp(20px, 4vw, 36px);
  font-size: 13px;
}
.mock-nav-brand { display: flex; align-items: center; gap: 8px; color: var(--fg); }
.mock-nav-brand .brand-mark { color: var(--fg); }
.mock-nav-links { display: flex; gap: 16px; color: var(--fg-muted); white-space: nowrap; }
.mock-hero {
  display: flex;
  align-items: center;
  gap: clamp(18px, 4vw, 40px);
  margin-bottom: clamp(20px, 4vw, 36px);
}
.mock-hero-text { flex: 1.1; min-width: 0; }
.mock-hero-text h2 { font-size: clamp(18px, 2.4vw, 24px); margin: 0 0 10px; line-height: 1.3; }
.mock-hero-text p { font-size: 13px; color: var(--fg-muted); margin: 0 0 14px; line-height: 1.5; }
.mock-hero-visual { flex: 1; min-width: 0; align-self: stretch; }
.mock-hero-visual .reveal-stack { height: 100%; min-height: 120px; }
.mock-visual {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 120px;
  border-radius: 10px;
  /* a different, warmer palette than Lintel's own accent on purpose —
     this mockup represents an example client's brand, not Lintel's */
  background: linear-gradient(135deg, #f2a65a, #e8734a 55%, #c8506a);
}
.mock-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 2vw, 18px);
}
.mock-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: clamp(12px, 2.4vw, 18px);
  background: var(--bg);
}
.mock-card h4 { font-size: 13px; margin: 0 0 4px; color: var(--fg); }
.mock-card p { font-size: 12px; color: var(--fg-muted); margin: 0; line-height: 1.45; }
.mock-icon { display: inline-block; color: #e8734a; margin-bottom: 8px; }
.mock-footer {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: clamp(18px, 3.5vw, 30px);
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--fg-muted);
}

@media (max-width: 700px) {
  .mock-hero { flex-direction: column; align-items: stretch; }
  .mock-hero-visual { min-height: 90px; }
  .mock-hero-visual .reveal-stack { min-height: 90px; }
  .mock-cards { grid-template-columns: 1fr; }
  .mock-nav-links { gap: 10px; font-size: 11px; }
}
@media (max-width: 600px) {
  .build-sticky { top: 76px; height: calc(100vh - 108px); padding: 14px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .build-wrap { height: auto; }
  .build-sticky { position: static; height: auto; }
  .reveal-skel { display: none; }
  .reveal-real { opacity: 1; transform: none; }
  .browser-body::before { display: none; }
}

/* ---------- desktop monitor bezel around the-build's browser-frame ----------
   A fixed graphite bezel (not theme tokens) on purpose — a real monitor's
   casing doesn't change color when the site behind it switches themes, and
   the fixed tone is what makes it read as a physical frame rather than just
   another themed card. */
.monitor-frame {
  position: relative;
  width: min(960px, 100%);
  padding: 16px 16px 42px;
  background: linear-gradient(160deg, #38394a, #202129);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 22px;
  box-shadow: 0 30px 60px -26px var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.monitor-frame .browser-frame {
  width: 100%;
  border: none;
  box-shadow: none;
  border-radius: 8px;
}
.monitor-frame::before {
  content: "";
  position: absolute;
  left: 50%; bottom: 18px;
  width: 46px; height: 14px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, #38394a, #202129);
  border-radius: 4px;
}
.monitor-frame::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 11px;
  width: 130px; height: 6px;
  transform: translateX(-50%);
  background: #202129;
  border-radius: 999px;
}
@media (max-width: 600px) {
  .monitor-frame { padding: 10px 10px 30px; border-radius: 16px; }
  .monitor-frame::before { bottom: 12px; width: 34px; height: 10px; }
  .monitor-frame::after { bottom: 7px; width: 90px; height: 5px; }
}

/* ---------- phone frames, doorframe-bezel instead of a generic notch ----------
   Each card shows a different small illustrative mini-layout (a booking
   chip, an inbox list, a portfolio grid) instead of the same flat
   gradient rectangle with the two colors reordered — that's what read as
   "3 same template boxes" before. The tint still varies card to card, but
   now it's dressing a genuinely different bit of UI each time. */
.phone-frame-row { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; }
.phone-frame {
  position: relative;
  width: 220px;
  padding: 14px 12px 20px;
  border-radius: 32px;
  background: linear-gradient(160deg, var(--bg-raised), var(--card-bg));
  border: 1px solid var(--border-strong);
  box-shadow: 0 30px 50px -28px var(--shadow);
}
.phone-notch { display: flex; justify-content: center; margin-bottom: 8px; color: var(--fg-muted); }
.phone-screen { border-radius: 20px; overflow: hidden; background: var(--band-bg); display: flex; flex-direction: column; }
.phone-screen-body { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.phone-screen-body h4 { font-size: 14px; margin: 0; color: var(--fg); font-family: 'Fraunces', ui-serif, Georgia, serif; }
.phone-screen-body p { font-size: 12px; margin: 0; color: var(--fg-muted); line-height: 1.4; }
.phone-caption { text-align: center; font-size: 13px; color: var(--fg-muted); margin-top: 18px; }

.phone-mock { height: 130px; padding: 16px 14px; display: flex; flex-direction: column; justify-content: center; gap: 9px; }

/* booking: a date chip + two text lines + a CTA pill */
.phone-screen-booking .phone-mock {
  background: linear-gradient(160deg, color-mix(in oklch, var(--accent) 26%, var(--band-bg)), color-mix(in oklch, var(--accent) 6%, var(--band-bg)));
}
.pm-chip {
  align-self: flex-start;
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-raised);
  color: var(--fg);
  box-shadow: 0 2px 6px -2px var(--shadow);
}
.pm-line { display: block; height: 7px; border-radius: 4px; background: color-mix(in oklch, var(--fg) 16%, transparent); width: 78%; }
.pm-line-lg { width: 88%; height: 9px; }
.pm-line-sm { width: 42%; }
.pm-btn {
  align-self: flex-start;
  margin-top: 3px;
  font-size: 10px; font-weight: 700;
  padding: 7px 15px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--accent), var(--accent-warm));
  color: oklch(99% 0 0);
}

/* list: an inbox/newsletter-style stack of rows */
.phone-screen-list .phone-mock {
  background: linear-gradient(160deg, color-mix(in oklch, var(--accent-warm) 22%, var(--band-bg)), color-mix(in oklch, var(--accent-warm) 5%, var(--band-bg)));
  gap: 11px;
}
.pm-row { display: flex; align-items: center; gap: 9px; }
.pm-row .pm-line { flex: 1; width: auto; }
.pm-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: color-mix(in oklch, var(--fg) 30%, transparent); }

/* grid: a 2x2 portfolio/thumbnail grid */
.phone-screen-grid .phone-mock {
  background: linear-gradient(160deg, color-mix(in oklch, var(--accent) 16%, var(--accent-warm) 16%), var(--band-bg));
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.pm-tile { border-radius: 8px; background: var(--bg-raised); box-shadow: inset 0 0 0 1px var(--border); }

/* ---------- spirit-level settling indicator, next to the homepage stat ---------- */
.level-widget { display: inline-flex; align-items: center; margin-left: 20px; }
.level-vial {
  position: relative;
  width: 64px; height: 20px;
  border-radius: 999px;
  border: 2px solid var(--border-strong);
  background: color-mix(in oklch, var(--band-bg) 55%, transparent);
  overflow: hidden;
}
.level-vial::before {
  content: "";
  position: absolute;
  left: 50%; top: 2px; bottom: 2px;
  width: 2px;
  background: var(--border-strong);
  transform: translateX(-50%);
}
.level-bubble {
  /* A real spirit level's bubble drifts to one side while it's still
     being set down, then settles at center and keeps a faint, continuous
     living wobble — it never just stops dead. Starting at 18px (visibly
     off-center, not jammed against the vial wall) reads as "not level
     yet"; settling adds a small spring overshoot; the idle sway afterward
     is what was missing before — without it, the one-shot move to center
     had nothing to explain it and just looked like a stuck animation. */
  position: absolute;
  top: 3px; left: 18px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-warm));
  box-shadow: 0 0 8px 1px color-mix(in oklch, var(--accent) 40%, transparent);
  transition: left 900ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.level-widget.is-settled .level-bubble {
  left: calc(50% - 7px);
  animation: level-idle 3200ms ease-in-out 900ms infinite;
}
@keyframes level-idle {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(2px); }
}
@media (max-width: 600px) {
  .level-widget { margin-left: 0; margin-top: 10px; }
}

/* screen-reader-only text: visually hidden but still announced */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* utility */
.center { text-align: center; }
.mt-0 { margin-top: 0; }
.flex-wrap-btns { display: flex; gap: 14px; flex-wrap: wrap; }
