/* Toppi.io — standalone rebuild. Animation values are copied exactly from the
   live Elementor site (see plan doc): fadeIn-only entrance, 1.25s duration,
   0/200/400/600/800/1000ms stagger, shrink-to-90% hover, 2000ms count-up,
   5000ms/500ms carousel autoplay. Visual design is original, not an Elementor
   CSS port. */

:root{
  --wordmark:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTAiIGhlaWdodD0iNDQiIHZpZXdCb3g9IjAgMCAxMTAgNDQiIGZpbGw9Im5vbmUiPjxtYXNrIGlkPSJtYXNrMF8yMzU5XzIxMiIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSIxMTAiIGhlaWdodD0iNDQiPjxwYXRoIGQ9Ik0xMTAgMEgwVjQzLjQyMTJIMTEwVjBaIiBmaWxsPSJ3aGl0ZSI+PC9wYXRoPjwvbWFzaz48ZyBtYXNrPSJ1cmwoI21hc2swXzIzNTlfMjEyKSI+PHBhdGggZD0iTTAgNi42MTQ3MkMwIDQuOTA4ODkgMS4zNDk5MyA0LjA4MjAzIDIuNjk5ODcgNC4wODIwM0M0LjA0OTggNC4wODIwMyA1LjM5OTczIDQuOTA4ODkgNS4zOTk3MyA2LjYxNDcyVjEwLjYwOTNDNS4zOTk3MyAxMC43NzI5IDUuNTMyOTMgMTAuOTA1MyA1LjY5NzE0IDEwLjkwNTNIOC45MzA0NkMxMC43NDc2IDEwLjkwNTMgMTEuNjgyMiAxMi4yNDkzIDExLjY4MjIgMTMuNTQyMUMxMS42ODIyIDE0Ljg4NjEgMTAuNzk5NSAxNi4xNzgxIDguOTMwNDYgMTYuMTc4MUg1LjY5NzE0QzUuNTMyOTMgMTYuMTc4MSA1LjM5OTczIDE2LjMxMDggNS4zOTk3MyAxNi40NzQyVjIyLjIyNjVDNS4zOTk3MyAyNS44OTY3IDYuOTA1NTUgMjcuMzQ0IDEwLjA3MjYgMjcuMzQ0QzExLjc4NTkgMjcuMzQ0IDEyLjUxMzEgMjguNjM1OSAxMi41MTMxIDI5Ljk4MDdDMTIuNTEzMSAzMS4yNzI2IDExLjY4MjIgMzIuNjE2OCA5LjgxMjg5IDMyLjYxNjhDMi4yODQ2IDMyLjYxNyAwIDI5LjA1IDAgMjIuMjI2N1Y2LjYxNDcyWiIgZmlsbD0iIzAwMzM0MSI+PC9wYXRoPjxwYXRoIGQ9Ik0yNS42NTYgMzMuMTM0MUMxOC4xMjc3IDMzLjEzNDEgMTMuMjk4OCAyOC40Mjk2IDEzLjI5ODggMjEuNzYxNUMxMy4yOTg4IDE1LjA5MzQgMTguMTI3NyAxMC4zODgyIDI1LjYwNDEgMTAuMzg4MkMzMy4wODA1IDEwLjM4ODIgMzcuOTYxMyAxNS4wOTI3IDM3Ljk2MTMgMjEuNzYxNUMzNy45NjEzIDI4LjQ4MTYgMzMuMDgxIDMzLjEzNDEgMjUuNjU2IDMzLjEzNDFaTTI1LjYwNDEgMTUuMTQ0OEMyMS44MTQgMTUuMTQ0OCAxOS4yMTc4IDE3Ljg4NDEgMTkuMjE3OCAyMS43NjE2QzE5LjIxNzggMjUuNjkwNSAyMS44MTQgMjguMzc4NSAyNS42MDQxIDI4LjM3ODVDMjkuNDQ2NSAyOC4zNzg1IDMyLjA0MjcgMjUuNjkwNSAzMi4wNDI3IDIxLjc2MTZDMzIuMDQyNyAxNy44ODQxIDI5LjQ0NjUgMTUuMTQ0OCAyNS42MDQxIDE1LjE0NDhaIiBmaWxsPSIjMDAzMzQxIj48L3BhdGg+PHBhdGggZD0iTTQ2LjIyNTQgNDAuODM2NEM0Ni4yMjU0IDQyLjU0MjIgNDQuODc1NSA0My40MjEgNDMuNTI1NiA0My40MjFDNDIuMTc1NiA0My40MjEgNDAuODI1NyA0Mi41NDIyIDQwLjgyNTcgNDAuODM2NFYyMS44NjQ3QzQwLjgyNTcgMTUuNDU0NCA0NS40OTg1IDEwLjM4ODIgNTMuMDI3MyAxMC4zODgyQzYwLjUwMzkgMTAuMzg4MiA2NS4zODQ1IDE1LjQwMjQgNjUuMzg0NSAyMS43MDk1QzY1LjM4NDUgMjguODQzNSA2MC40NTIgMzMuMTM0MSA1My45NjE3IDMzLjEzNDFDNTAuOTU5OCAzMy4xMzQxIDQ4LjQ2NjQgMzEuOTk0OCA0Ni43NDk0IDI5Ljg2OTJDNDYuNTc0NCAyOS42NTI1IDQ2LjIyNTQgMjkuNzc0OSA0Ni4yMjU0IDMwLjA1M1Y0MC44MzY2VjQwLjgzNjRaTTUzLjAyNzUgMjguMzI2NEM1Ni41MDYxIDI4LjMyNjQgNTkuNDY1NyAyNS44OTY5IDU5LjQ2NTcgMjEuNzA5NUM1OS40NjU3IDE3LjUyMjIgNTYuNTU3OSAxNS4wNDA2IDUzLjA3OTQgMTUuMDQwNkM0OS42MDA5IDE1LjA0MDYgNDYuNjQwOCAxNy41NzQxIDQ2LjY0MDggMjEuNzA5NUM0Ni42NDA4IDI1Ljg0NSA0OS41NDg1IDI4LjMyNjQgNTMuMDI3NSAyOC4zMjY0WiIgZmlsbD0iIzAwMzM0MSI+PC9wYXRoPjxwYXRoIGQ9Ik03My42NDgzIDQwLjgzNjRDNzMuNjQ4MyA0Mi41NDIyIDcyLjI5ODMgNDMuNDIxIDcwLjk0ODYgNDMuNDIxQzY5LjU5ODggNDMuNDIxIDY4LjI0ODUgNDIuNTQyMiA2OC4yNDg1IDQwLjgzNjRWMjEuODY0N0M2OC4yNDg1IDE1LjQ1NDQgNzIuOTIxNiAxMC4zODgyIDgwLjQ1MDMgMTAuMzg4MkM4Ny45MjY3IDEwLjM4ODIgOTIuODA3NSAxNS40MDI0IDkyLjgwNzUgMjEuNzA5NUM5Mi44MDc1IDI4Ljg0MzUgODcuODc0OCAzMy4xMzQxIDgxLjM4NDUgMzMuMTM0MUM3OC4zODI3IDMzLjEzNDEgNzUuODg5MyAzMS45OTQ4IDc0LjE3MjQgMjkuODY5MkM3My45OTczIDI5LjY1MjUgNzMuNjQ4MyAyOS43NzQ5IDczLjY0ODMgMzAuMDUzVjQwLjgzNjZWNDAuODM2NFpNODAuNDUwMyAyOC4zMjY0QzgzLjkyODggMjguMzI2NCA4Ni44ODg0IDI1Ljg5NjkgODYuODg4NCAyMS43MDk1Qzg2Ljg4ODQgMTcuNTIyMiA4My45ODA3IDE1LjA0MDYgODAuNTAyMSAxNS4wNDA2Qzc3LjAyMzQgMTUuMDQwNiA3NC4wNjM3IDE3LjU3NDEgNzQuMDYzNyAyMS43MDk1Qzc0LjA2MzcgMjUuODQ1IDc2Ljk3MTQgMjguMzI2NCA4MC40NTAzIDI4LjMyNjRaIiBmaWxsPSIjMDAzMzQxIj48L3BhdGg+PHBhdGggZD0iTTk1LjIwNDYgMy4yMDMwMkM5NS4yMDQ2IDEuNDQ0NDQgOTYuNDU2NCAwLjE0NzY2NSA5OC4yMTQ2IDAuMDEwMDE3MkMxMDAuMzYxIC0wLjE1Nzk4NCAxMDIuMTg3IDEuODEzODggMTAxLjc4NSAzLjkyMDE1QzEwMS40ODIgNS41MDk0MyAxMDAuMTY0IDYuNDU5NDkgOTguNTI3NSA2LjQ1OTQ5Qzk2LjYwNjggNi40NTk0OSA5NS4yMDQ2IDUuMjE5NTMgOTUuMjA0NiAzLjIwMzAyWk05OC41Mzg1IDEwLjg1NDVDMTAwLjEzNCAxMC44NzMxIDEwMS4zODMgMTIuMjQ2NyAxMDEuMzgzIDEzLjgzNTVWMjUuNzQxN0MxMDEuMzgzIDI3LjAzNDQgMTAxLjY5NSAyNy40OTk1IDEwMi42ODEgMjcuNjAyN0MxMDMuNzIgMjcuNzU3OSAxMDQuMzQzIDI5LjA0OTkgMTA0LjE4NyAzMC4yOTA3QzEwNC4wODMgMzEuNDc5NCAxMDMuMiAzMi42MTY5IDEwMC44NjQgMzIuNjE2OUM5Ny44NTI1IDMyLjYxNjkgOTUuNjcyMSAzMC43MDQ1IDk1LjY3MjEgMjcuMDg1NVYxMy41NDJDOTUuNjcyMSAxMS43MTA3IDk2LjczNTEgMTAuODMzNSA5OC41Mzg1IDEwLjg1NDVaIiBmaWxsPSIjMDAzMzQxIj48L3BhdGg+PC9nPjwvc3ZnPgo=");
  /* Brand and neutral tokens mirror the dashboard's design system
     (dashboard/src/index.css), so --primary is the brand teal and --secondary the
     brand orange in both codebases. They used to mean the opposite here. */
  --ink:#171717;
  --ink-soft:#737373;
  --paper:#ffffff;
  /* The two mint grounds stay: the marketing pages lean on them for warmth the
     product UI has no use for. Everything else follows the dashboard. */
  --paper-tint:#ebf5f3;
  --paper-tint-2:#daf2e6;
  --primary:#5fa399;
  --primary-dark:#4a7871;
  --secondary:#f36618;
  --secondary-dark:#ba4d12;
  --accent:#e8957f;
  --success:#279b65;
  --border:#e6e6e6;
  /* The dashboard's two branded surfaces, copied verbatim from its tailwind config:
     the soft wash is the app's page ground (DashboardLayout), the saturated one fills
     primary AI/generate CTAs. Deliberately constant, they are brand surfaces. */
  --brand-wash:linear-gradient(135deg,#e8f2f0 0%,#f6ece8 60%,#fae7e0 100%);
  --brand-gradient:linear-gradient(135deg,#5fa399 0%,#d98c74 55%,#f0917a 100%);
  --radius:14px;
  --radius-lg:26px;
  --radius-pill:100px;
  --shadow-sm:0 1px 2px rgba(1,57,65,.05);
  --shadow:0 2px 4px rgba(1,57,65,.04),0 18px 44px rgba(1,57,65,.09);
  --shadow-lg:0 30px 70px rgba(1,57,65,.14);
  /* One rhythm for the whole page. Sections were each choosing 72px, which reads
     efficient rather than considered. */
  --section:clamp(96px,9vw,168px);
  --fl-duration:.85s;
  /* Habitline's easing signature, lifted from its computed styles. */
  --ease:cubic-bezier(.44,0,.56,1);
  /* Long-tail ease-out and a mild overshoot, for the hero's entrance only. */
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-burst:cubic-bezier(.3,1.25,.5,1);
  --max-width:1180px;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;
  font-family:"Geomanist regular","Inter",system-ui,-apple-system,Segoe UI,sans-serif;
  color:var(--ink);
  /* Fixed, so the ramp spans the viewport the way the app shell's h-screen does,
     rather than being stretched across the whole scroll height. */
  background:var(--brand-wash) fixed;
  line-height:1.55;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
h1,h2,h3,h4{line-height:1.08;margin:0 0 .5em;font-weight:600;letter-spacing:-.032em}
h1{font-size:clamp(2.6rem,6vw,4.9rem);line-height:1.02;letter-spacing:-.042em}
h2{font-size:clamp(1.9rem,3.4vw,3rem)}
h3{font-size:1.25rem;letter-spacing:-.02em}
p{margin:0 0 1em}
.wrap{max-width:var(--max-width);margin:0 auto;padding:0 24px}
.wrap.narrow{max-width:760px}
section,main > article{padding:calc(var(--section) / 2) 0}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}
.skip-link{position:absolute;left:-999px;top:0;background:var(--secondary);color:#fff;padding:10px 16px;z-index:200}
.skip-link:focus{left:8px;top:8px}

/* ---------- entrance: rise + fade on scroll ---------- */
.reveal{
  opacity:0;transform:translate3d(0,22px,0);
  transition:opacity var(--fl-duration) var(--ease),transform var(--fl-duration) var(--ease);
}
.reveal.is-visible{opacity:1;transform:none}
/* Children of a staggered group ride in one after another off a single observer,
   so a card grid arrives as a sequence rather than as a slab. */
[data-stagger] > *{
  opacity:0;transform:translate3d(0,22px,0);
  transition:opacity var(--fl-duration) var(--ease),transform var(--fl-duration) var(--ease);
  transition-delay:calc(var(--i,0) * 90ms);
}
[data-stagger].is-visible > *{opacity:1;transform:none}
[data-parallax],[data-parallax-x]{will-change:transform}
@media (prefers-reduced-motion: reduce){
  .reveal,[data-stagger] > *{opacity:1!important;transform:none!important}
  [data-parallax]{transform:none!important}
  *{transition-duration:0s!important;transition-delay:0s!important;animation:none!important}
}

/* ---------- buttons: shrink-to-90% on hover/focus/active, 0.3s ---------- */
.btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:15px 26px;border-radius:var(--radius-pill);font-weight:600;text-decoration:none;
  border:1px solid transparent;cursor:pointer;font-size:.97rem;line-height:1;
  letter-spacing:-.01em;
}
/* Lucide's arrow-up-right, the dashboard's icon set, at its 2px stroke. */
.btn::after{
  content:"";width:15px;height:15px;flex:0 0 auto;margin-right:-3px;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 7h10v10'/%3E%3Cpath d='M7 17 17 7'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 7h10v10'/%3E%3Cpath d='M7 17 17 7'/%3E%3C/svg%3E") center/contain no-repeat;
}
.btn-shrink{transition:transform .25s ease,box-shadow .25s ease}
.btn-shrink:hover,.btn-shrink:focus,.btn-shrink:active{transform:translateY(-2px);box-shadow:var(--shadow)}
.btn-primary{background:var(--secondary);color:#fff}
.btn-primary:hover{background:var(--secondary-dark)}
.btn-secondary{background:var(--ink);color:#fff}
/* Secondary actions and the newsletter carry no arrow: it marks the one thing to do next. */
.btn-ghost::after,.btn-ghost-invert::after,.btn-fill-secondary::after{display:none}
.btn-ghost{background:transparent;color:var(--primary-dark);border-color:var(--primary);border-radius:var(--radius-pill)}
.btn-ghost:hover{background:var(--primary);color:#fff}
.btn-fill-secondary{background:var(--primary);color:#fff;border-radius:var(--radius-pill)}
.btn-fill-secondary:hover{background:var(--primary-dark)}
.btn-ghost-invert{background:transparent;color:#fff;border-color:rgba(255,255,255,.6);border-radius:var(--radius-pill)}
.btn-ghost-invert:hover{background:#fff;color:var(--ink)}

/* ---------- header: light/mint by default, dark teal + white logo once scrolled
   (matches the live site's Elementor sticky-effects behavior exactly) ---------- */
.site-header{
  position:fixed;top:14px;left:0;right:0;z-index:100;background:transparent;border:0;
}
.site-header.is-scrolled{background:transparent;box-shadow:none}
.header-inner{
  position:relative;
  display:flex;align-items:center;justify-content:space-between;
  width:max-content;max-width:calc(100% - 32px);margin-inline:auto;
  padding:8px 8px 8px 22px;gap:10px;
  background:color-mix(in srgb,var(--paper) 84%,transparent);
  backdrop-filter:blur(16px);
  border:1px solid var(--border);
  border-radius:var(--radius-pill);
  box-shadow:var(--shadow);
  transition:background-color .4s ease-in-out,border-color .4s ease-in-out;
}
.site-header.is-scrolled .header-inner{background:color-mix(in srgb,var(--ink) 94%,transparent);border-color:transparent}
.brand{display:inline-flex}
.brand-mark{
  display:block;width:88px;height:34px;background-color:var(--ink);
  -webkit-mask:var(--wordmark) center/contain no-repeat;
  mask:var(--wordmark) center/contain no-repeat;
  transition:background-color .5s ease-in-out;
}
.site-header.is-scrolled .brand-mark{background-color:#fff}
.main-nav .nav-menu{display:flex;gap:2px;list-style:none;margin:0;padding:0}
.main-nav a{
  display:inline-block;padding:9px 15px;border-radius:var(--radius-pill);
  text-decoration:none;font-weight:500;color:var(--ink-soft);
  transition:color .3s ease-in-out,background-color .2s ease;
}
.site-header.is-scrolled .main-nav a{color:rgba(255,255,255,.82)}
.main-nav a:hover,.main-nav a:focus{color:var(--ink);background:var(--paper-tint)}
.site-header.is-scrolled .main-nav a:hover{color:#fff;background:rgba(255,255,255,.12)}
.header-actions{display:flex;align-items:center;gap:10px}
.header-actions .btn{padding:12px 20px;font-size:.92rem}
.lang-switch{display:flex;gap:4px;font-size:.85rem;font-weight:600}
.lang-switch a{text-decoration:none;color:var(--ink-soft);padding:2px 4px}
.lang-switch a.is-active{color:var(--secondary)}
.nav-menu-cta{display:none}
.nav-toggle{display:none;flex-direction:column;gap:4px;background:none;border:0;cursor:pointer;padding:6px}
.nav-toggle span{width:22px;height:2px;background:var(--ink);transition:background .5s ease-in-out}
.site-header.is-scrolled .nav-toggle span{background:#fff}
.nav-toggle:hover span,.nav-toggle:focus span{background:var(--secondary)}

@media (max-width:860px){
  /* `backdrop-filter` on the pill makes it the containing block for its positioned
     children, so a fixed full-screen panel sizes itself to the pill instead of the
     viewport and the links spill out of it. The menu hangs off the pill instead. */
  .main-nav .nav-menu{
    position:absolute;top:calc(100% + 10px);left:0;right:0;
    flex-direction:column;gap:2px;padding:10px;
    background:var(--paper);border:1px solid var(--border);
    border-radius:22px;box-shadow:var(--shadow-lg);
    transform:translateY(-8px);opacity:0;pointer-events:none;
    transition:opacity .25s,transform .25s;
  }
  .main-nav .nav-menu.is-open{opacity:1;transform:none;pointer-events:auto}
  .main-nav .nav-menu a{display:block;padding:13px 16px;border-radius:14px}
  .site-header.is-scrolled .main-nav .nav-menu a{color:var(--ink)}
  .nav-toggle{display:flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0}
  .nav-menu-cta{display:block;padding:8px 4px 2px}
  /* Outranks the menu's link rules above, which would flatten the button. */
  .site-header .main-nav .nav-menu .nav-menu-cta .btn{
    display:flex;justify-content:center;padding:15px 26px;
    border-radius:var(--radius-pill);color:#fff;background:var(--secondary);
  }
  .site-header .main-nav .nav-menu .nav-menu-cta .btn:hover{color:#fff;background:var(--secondary-dark)}
  /* No room for the CTA next to the wordmark on a phone, and the hero carries the same
     one two screens down. Dropping it lets the pill span the bar so the toggle sits
     hard right instead of floating mid-bar next to a shrink-wrapped pill. */
  .header-actions{display:none}
  .header-inner{width:auto;padding-right:12px}
  .brand{align-items:center;min-height:44px}
  .header-actions .btn{min-height:44px}
}

/* ---------- hero ---------- */
main > section:first-child,main > article:first-child{padding-top:clamp(146px,13vw,196px)}
.hero{
  padding-bottom:var(--section);text-align:center;
  /* A wash behind the headline rather than a flat panel: the depth is what stops a
     centred hero reading like a template. */
  background:
    radial-gradient(110% 70% at 50% -8%, color-mix(in srgb,var(--primary) 30%,transparent) 0%, transparent 60%),
    transparent;
}
.hero-inner{display:flex;flex-direction:column;align-items:center;gap:52px}
.hero-copy{max-width:900px;display:flex;flex-direction:column;align-items:center}
/* The longest variant of the rotating headline needs more room than the 900px copy
   column, and a headline that rewraps mid-flip would bounce the page. */
.hero-copy-flip{max-width:960px}
/* The rotating noun flips like a split-flap board. Both words share one grid cell so
   the box keeps the line box height, and JS animates the width so the rest of the
   headline reflows during the flip instead of jumping after it. */
.flip-word{display:inline-grid;perspective:800px;transition:width .26s cubic-bezier(.65,0,.35,1)}
.flip-word > span{
  grid-area:1/1;justify-self:center;white-space:nowrap;backface-visibility:hidden;
  background:var(--brand-gradient);-webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
  /* Clipped to text, the gradient only paints inside the box, and the tight tracking
     leaves the last glyph's ink hanging past it, as the 1.02 line-height does with
     descenders. The padding widens the paint area without moving the word. */
  padding:.14em .1em;margin:-.14em -.1em;
}
.flip-word > .is-out{animation:flip-out .26s cubic-bezier(.55,0,.9,.3) forwards}
.flip-word > .is-in{animation:flip-in .34s cubic-bezier(.22,1,.36,1) backwards}
@keyframes flip-out{to{transform:rotateX(-92deg) translateY(-.18em);opacity:0}}
@keyframes flip-in{from{transform:rotateX(92deg) translateY(.18em);opacity:0}}
/* The eyebrow is a badge everywhere it appears, not shouted small caps. */
.eyebrow{
  display:inline-flex;align-items:center;gap:8px;
  margin:0 0 24px;padding:7px 17px;
  background:var(--paper);border:1px solid var(--border);border-radius:var(--radius-pill);
  box-shadow:var(--shadow-sm);
  color:var(--primary-dark);font-weight:600;font-size:.85rem;
  text-transform:none;letter-spacing:-.005em;
}
.hero-subtitle{font-size:clamp(1.1rem,1.5vw,1.3rem);color:var(--ink-soft);max-width:46ch;line-height:1.5}
.hero-ctas{display:flex;gap:12px;flex-wrap:wrap;justify-content:center;margin-top:14px}
/* The product shot is a transparent cut-out, so it floats on the wash. Framing it
   in a card would draw a box around empty pixels. */
.hero-image{width:100%;max-width:1020px}
.hero-image img{width:100%}

/* ---------- two-column section header ---------- */
.section-head{
  display:grid;grid-template-columns:1.1fr .9fr;gap:44px;align-items:end;
  margin-bottom:60px;
}
.section-head h2{margin:0}
.section-head .section-intro{margin:0;font-size:1.06rem;max-width:42ch}
.section-head .eyebrow{align-self:start}
@media (max-width:900px){.section-head{grid-template-columns:1fr;gap:20px;align-items:start}}

/* ---------- feature bento ---------- */
.feature-bento{background:transparent}
.bento{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
.bento-card{
  position:relative;display:flex;flex-direction:column;overflow:hidden;
  min-height:540px;padding:40px;
  background:var(--paper-tint);border-radius:var(--radius-lg);
}
/* Mixed light and dark cards in one grid is the whole trick: a uniform grid reads
   as a spec sheet, an alternating one reads as a composition. */
.bento-card{transition:transform .45s var(--ease),box-shadow .45s var(--ease)}
.bento-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg)}
.bento-card.is-dark{background:var(--ink);color:#fff}
.bento-card.is-dark p{color:rgba(255,255,255,.74)}
.bento-card.is-dark .check-list li{color:rgba(255,255,255,.86)}
.bento-card h3{font-size:clamp(1.45rem,2.1vw,2rem);letter-spacing:-.03em;margin-bottom:.4em}
.bento-card > p{color:var(--ink-soft);max-width:40ch;margin-bottom:22px}
.bento-head{max-width:46ch}
/* The mockup bleeds past the card padding and is clipped by the card's own radius,
   so it reads as a window into the product rather than a pasted screenshot. */
.bento-visual{
  /* The mock sizes itself in cqi, but an element cannot query its own container, so with
     container-type only on .mock those units fell back to the viewport: on a phone the
     composition was scaled to 390px while its panel was 302px, and the pieces collided.
     Declaring the container on the parent makes them resolve against the panel. */
  container-type:inline-size;
  display:flex;margin:auto -40px -40px 0;border-radius:18px 0 0 0;overflow:hidden;
  box-shadow:0 -2px 0 rgba(1,57,65,.06),var(--shadow);
}
.bento-card.is-wide{grid-column:span 2;flex-direction:row;align-items:stretch;gap:48px;min-height:440px}
.bento-card.is-wide .bento-head{flex:1 1 42%;align-self:center}
.bento-card.is-wide .bento-visual{flex:1 1 58%;margin:-40px -40px -40px 0;border-radius:18px 0 0 18px}
.bento-card.is-wide.is-flip{flex-direction:row-reverse}
.bento-card.is-wide.is-flip .bento-visual{margin:-40px 0 -40px -40px;border-radius:0 18px 18px 0}
.bento-card .btn{align-self:flex-start}
@media (max-width:900px){
  .bento{grid-template-columns:1fr}
  .bento-card,.bento-card.is-wide,.bento-card.is-wide.is-flip{
    grid-column:span 1;flex-direction:column;min-height:0;gap:0;
  }
  /* Stacked, the panel sits under the copy instead of beside it, so the one-sided bleed
     that frames it on desktop reads as a misalignment: a gutter of card down the left and
     a clipped edge on the right. It bleeds to both edges here. */
  .bento-visual,
  .bento-card.is-wide .bento-visual,.bento-card.is-wide.is-flip .bento-visual{
    margin:28px -40px -40px;border-radius:18px 18px 0 0;
  }
}

/* ---------- animated hero stage ---------- */
/* One perspective, one preserve-3d world. Everything inside is a real plane at a real
   depth, so the browser sorts a chip on the far side of its ring behind the device
   rather than us faking it with z-index. Every level between the world and a chip's
   card keeps preserve-3d, or the leaf's depth collapses on the way up. */
.hero-stage{
  position:relative;width:100%;max-width:1180px;margin-inline:auto;
  aspect-ratio:1600/1130;
  perspective:1500px;perspective-origin:50% 46%;
}
/* A halo behind the device, so the chips read as floating in front of something
   rather than scattered on a flat panel. */
.hero-stage::before{
  content:"";position:absolute;left:50%;top:4%;width:62%;aspect-ratio:1;
  margin-left:-31%;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--primary) 40%,transparent) 0%,color-mix(in srgb,var(--primary) 14%,transparent) 38%,transparent 66%);
  opacity:0;transform:scale(.55);
  transition:opacity 1.6s var(--ease),transform 1.9s var(--ease-out);
}
.hero-stage:has(.is-visible)::before{opacity:1;transform:none}
/* The scene turns toward the pointer: main.js eases --rx/--ry on the world every
   frame (no transition here, it would fight that), the device turns a little further
   than the chips around it, so the two separate. */
.hero-world{
  position:absolute;inset:0;transform-style:preserve-3d;
  transform:rotateY(var(--ry,0deg)) rotateX(var(--rx,0deg));
  /* The world's own box is a plane at z=0 in the depth sort, so without this it
     catches the pointer whenever a chip swings behind it. The device opts back in
     so it still occludes the chips passing behind it. */
  pointer-events:none;
}
.hero-world > *{transform-style:preserve-3d}

/* ---------- the device, built rather than photographed ---------- */
.device{position:absolute;left:50%;top:2.5%;width:31%;margin-left:-15.5%;font-size:clamp(6px,.94vw,12.6px);pointer-events:auto}
.device-tilt{
  position:relative;
  transform:rotateY(calc(var(--ry,0deg) * 1.6)) rotateX(calc(var(--rx,0deg) * 1.6));
}
.device-body{
  position:relative;aspect-ratio:472/1000;padding:3.4%;
  background:#101215;
  border-radius:15.6%/7.36%;
  box-shadow:inset 0 0 0 1.4px #34393d,0 46px 78px rgba(1,57,65,.24),0 8px 20px rgba(1,57,65,.1);
}
/* Volume and power nubs. Small, but they are most of what separates a rounded rectangle
   from a phone. */
.device-body::before,.device-body::after{content:"";position:absolute;width:1.5%;background:#23282c}
.device-body::before{left:-1.2%;top:23%;height:11%;border-radius:2px 0 0 2px}
.device-body::after{right:-1.2%;top:28%;height:8.5%;border-radius:0 2px 2px 0}
.device-screen{
  position:relative;display:flex;flex-direction:column;height:100%;overflow:hidden;
  border-radius:13%/5.9%;background:#f6f5f2;
}
.device-head{flex:0 0 auto;background:#fff;padding:3.4% 5.6% 4.4%;color:var(--ink)}
/* The island sits in the status row itself, which is what lines the clock and the
   signal icons up with it the way they do on the device. */
.device-status{display:flex;align-items:center;justify-content:space-between;gap:.7em}
.device-time{font-size:1.12em;font-weight:600;letter-spacing:-.01em}
.device-island{
  position:relative;flex:0 0 auto;width:31%;height:2.05em;
  background:#07090a;border-radius:var(--radius-pill);
}
.device-island::after{
  content:"";position:absolute;right:.5em;top:50%;width:.72em;height:.72em;
  margin-top:-.36em;border-radius:50%;background:#161a1d;
}
.device-sigs{width:4em;height:.96em;flex:0 0 auto;fill:currentColor}
.device-search{
  display:flex;align-items:center;gap:.8em;margin-top:1.6em;font-size:1.08em;
  color:var(--ink-soft);white-space:nowrap;overflow:hidden;
}
.device-search .mock-glass{width:1.4em;height:1.4em}
.device-map{flex:1;min-height:0;width:100%;display:block}
.device-home{
  position:absolute;left:50%;bottom:1%;width:32%;height:.4em;margin-left:-16%;
  background:#0f1113;opacity:.8;border-radius:var(--radius-pill);z-index:2;
}
/* ---------- the place card, a separate item in front of the glass ---------- */
.place-anchor{left:50%;top:53%;width:38%;margin-left:-19%;transform:translateZ(76px)}
.place-card{
  background:#fff;border-radius:18px;padding:12px;text-align:left;
  box-shadow:0 26px 60px rgba(1,57,65,.26);
  font-size:clamp(7px,1.02vw,13.4px);
  transition:transform .4s var(--ease),box-shadow .4s var(--ease);
}
.place-card:hover{transform:scale(1.03);box-shadow:0 34px 72px rgba(1,57,65,.32)}
.place-card > img{width:100%;height:auto;border-radius:12px;display:block}
.place-title{display:flex;align-items:baseline;gap:10px;margin-top:.85em}
.place-title strong{flex:1;min-width:0;font-size:1.5em;font-weight:600;letter-spacing:-.03em;line-height:1.15}
.place-price{flex:0 0 auto;font-size:1.35em;font-weight:600;letter-spacing:-.02em;white-space:nowrap}
.place-price i{font-style:normal;color:var(--border)}
.place-meta{display:flex;flex-wrap:wrap;gap:.35em 1.1em;margin-top:.6em;font-size:1.02em;color:var(--ink-soft)}
.place-meta span{display:inline-flex;align-items:center;gap:.4em;white-space:nowrap}
.place-meta svg{width:1.15em;height:1.15em;flex:0 0 auto;fill:none;stroke:currentColor;stroke-width:1.3}
.place-meta .star{font-style:normal;color:var(--secondary);font-size:1.1em}
.place-meta span:nth-child(3){color:var(--secondary);font-weight:600}

/* ---------- the chips, on rings around the device ---------- */
/* A chip's orbit layer fills the whole stage, so turning it rotates the chip about the
   device's axis at whatever radius its own left/top already gave it. .chip-face turns
   back by the same amount on the same clock, so the card always faces the viewer. */
.chip{position:absolute;inset:0;pointer-events:none}
.chip-orbit{
  position:absolute;inset:0;transform-style:preserve-3d;
  transform:rotateY(0deg);
  animation:orbit var(--orb,40s) ease-in-out infinite;animation-delay:var(--od,0s);
}
.chip-anchor{position:absolute;transform-style:preserve-3d}
.chip-face{
  transform-style:preserve-3d;transform:rotateY(0deg);
  animation:billboard var(--orb,40s) ease-in-out infinite;animation-delay:var(--od,0s);
}
.chip-px{transform-style:preserve-3d}
.chip-fl{
  transform-style:preserve-3d;
  animation:floaty var(--dur,7s) ease-in-out infinite;animation-delay:var(--fd,0s);
}
.chip-fl > *{pointer-events:auto}
/* The entrance is a sequence, not a slab: the device lands first, rising out of the
   depth; the chips burst outward from behind it, one after another; the place card
   drops in front once the device is still. Overrides the generic stagger offset and clock. */
.hero-world > .device{
  transform:translate3d(0,9%,-320px) scale(.86);
  transition:opacity .9s var(--ease),transform 1.5s var(--ease-out);
  transition-delay:0s;
}
.hero-world > .chip{
  transform:translate3d(var(--bx,0),var(--by,0),-220px) scale(.6);
  transition:opacity .7s var(--ease),transform 1.3s var(--ease-burst);
  transition-delay:calc(.85s + (var(--i,2) - 2) * 110ms);
}
/* The place card and the icon clusters overlap the device, so they wait for it to
   settle and then arrive on their own plane: a short drop from the viewer's side, no
   depth travel. Two things would make them pop otherwise. A translateZ that crosses
   the device plane flips the depth sort mid-flight, and opacity below 1 flattens a
   preserve-3d layer into one group until the fade ends, at which point every icon
   snaps to its real depth. So the layer stays at z=0 and fully opaque, and the leaf
   fades instead. */
.hero-world > .place-item,
.hero-world > .chip-social,.hero-world > .chip-ai,.hero-world > .chip-maps-l,.hero-world > .chip-maps-r{
  opacity:1;
  transform:translate3d(0,2%,0) scale(1.06);
  transition:transform .9s var(--ease-out);
  transition-delay:calc(1.35s + (var(--i,2) - 2) * 60ms);
}
.hero-world > .place-item{transition-delay:1.3s}
.hero-world .place-card,.hero-world .icon-chip{opacity:0}
.hero-world .icon-chip{transition:opacity .35s var(--ease) calc(1.35s + (var(--i,2) - 2) * 60ms)}
/* Restates the card's own hover transitions: this rule outranks them, and a single
   opacity transition here would make the hover snap. */
.hero-world .place-card{transition:opacity .35s var(--ease) 1.3s,transform .4s var(--ease),box-shadow .4s var(--ease)}
.hero-world.is-visible .place-card,.hero-world.is-visible .icon-chip{opacity:1}
.hero-world.is-visible > .device,.hero-world.is-visible > .chip{transform:none}
/* The sparklines draw once their chip has landed, on the same clock. */
.hero-world [data-draw] .draw{transition-delay:calc(1.35s + (var(--i,2) - 2) * 110ms)}
.hero-world .spark-fill{transition-delay:calc(1.85s + (var(--i,2) - 2) * 110ms)}
@keyframes orbit{
  0%,100%{transform:rotateY(0deg)}
  25%{transform:rotateY(calc(var(--sw,0deg) * var(--swm,1)))}
  75%{transform:rotateY(calc(var(--sw,0deg) * var(--swm,1) * -1))}
}
@keyframes billboard{
  0%,100%{transform:rotateY(0deg)}
  25%{transform:rotateY(calc(var(--sw,0deg) * var(--swm,1) * -1))}
  75%{transform:rotateY(calc(var(--sw,0deg) * var(--swm,1)))}
}
@keyframes floaty{0%,100%{transform:translate3d(0,0,0)}50%{transform:translate3d(0,-10px,0)}}

/* A dashboard tile: the number leads, its label sits under it, the sparkline rides
   beside them. Grid placement, so the markup order can stay label-first for readers. */
.stat-chip{
  display:grid;grid-template-columns:auto auto 1fr;row-gap:1px;align-items:center;text-align:left;
  background:#fff;border-radius:18px;padding:13px 18px 12px;min-width:172px;
  box-shadow:0 12px 34px rgba(1,57,65,.12);
  transition:transform .4s var(--ease),box-shadow .4s var(--ease);
}
.stat-chip:hover{transform:scale(1.04);box-shadow:0 18px 44px rgba(1,57,65,.2)}
.stat-chip .lbl{grid-row:2;grid-column:1/3;font-size:.78rem;color:var(--ink-soft);line-height:1.2}
.stat-chip .val{
  grid-row:1;grid-column:1;display:flex;align-items:center;gap:6px;
  font-size:1.9rem;font-weight:600;letter-spacing:-.04em;line-height:1.1;
}
.stat-chip .num{white-space:nowrap}
.stat-chip .star{color:var(--secondary);font-size:1.25rem}
.stat-chip .delta{
  grid-row:1;grid-column:2;margin-left:8px;padding:2px 8px;
  background:var(--paper-tint-2);color:var(--primary-dark);
  border-radius:var(--radius-pill);font-size:.7rem;font-weight:600;
}
.spark{grid-row:1/3;grid-column:3;justify-self:end;align-self:center;width:64px;height:24px;margin-left:18px;color:var(--primary)}
.icon-chip{display:flex;gap:12px}
.icon-chip img{
  width:44px;height:44px;padding:9px;box-sizing:border-box;
  border-radius:50%;background:#fff;
  transition:transform .4s var(--ease);
}
.icon-chip img:hover{transform:translateY(-4px) scale(1.08)}

/* On a phone the stage is barely wider than the device, so it keeps only the four chips
   that still read at that size, shrinks them, and reins the orbits in to a suggestion. */
@media (max-width:860px){
  .hero-stage{aspect-ratio:1/1.2;perspective:900px}
  .device{width:46%;margin-left:-23%;font-size:clamp(5.5px,2.35vw,10px)}
  .device-search{font-size:.88em;margin-top:1.4em}
  .chip{--swm:.3}
  .chip-social,.chip-maps-l,.chip-maps-r,.chip-nav,.chip-book{display:none}
  .stat-chip{min-width:0;padding:7px 10px;border-radius:12px}
  /* The tiles are wider than the stacked cards were, so the two by the phone's
     shoulders move out to the stage edge. Inline positions, hence the override. */
  .chip-rating .chip-anchor{left:1%!important;top:9%!important}
  .chip-find .chip-anchor{right:1%!important;top:10%!important}
  .stat-chip .lbl{font-size:.68rem}
  .stat-chip .val{font-size:1rem;gap:4px}
  .stat-chip .star{font-size:.8rem}
  .stat-chip .delta{margin-left:5px;font-size:.6rem;padding:1px 6px}
  /* Beside the number the sparkline makes the tile wider than the phone allows. */
  .spark{display:none}
  .icon-chip{gap:8px}
  .icon-chip img{width:30px;height:30px;padding:6px}
  .place-anchor{width:66%;margin-left:-33%;top:52%}
  .place-card{padding:8px;border-radius:14px;font-size:clamp(6.5px,2.25vw,10.5px)}
  .place-card > img{border-radius:9px}
}

/* ---------- line art draws itself once it scrolls in ---------- */
/* main.js hands each path its own length as --len; everything else is CSS. --i comes
   from the staggered ancestor, so a sparkline waits for its own card to land. */
[data-draw] .draw{
  stroke-dasharray:var(--len,140);stroke-dashoffset:var(--len,140);
  transition:stroke-dashoffset 1.15s var(--ease);
  transition-delay:calc(var(--i,0) * 90ms + 240ms);
}
[data-draw].is-drawn .draw{stroke-dashoffset:0}
.spark-fill{opacity:0;transition:opacity .7s var(--ease);transition-delay:calc(var(--i,0) * 90ms + 760ms)}
[data-draw].is-drawn .spark-fill{opacity:.14}

/* ---------- bento mockups ---------- */
/* Elements, not exported pixels, for the reason the hero is: only a composition that
   comes apart can move on scroll. Sizes are em off a container-relative font-size, so
   one composition serves a half card, a wide card and a phone with one set of numbers.
   None of them counts anything: the hero and the stat strip already carry the numbers,
   and a third and fourth telling of "+40%" reads as filler. */
.mock{
  position:relative;flex:1;min-height:300px;overflow:hidden;
  background:var(--paper-tint);color:var(--ink);
  container-type:inline-size;
  font-size:clamp(.56rem,2.85cqi,.94rem);
}
.bento-card.is-wide .mock{min-height:0;font-size:clamp(.6rem,3.4cqi,1.14rem)}
/* Stacked, a wide card no longer stretches its stage, and every piece inside is
   absolutely positioned, so with no height of its own the panel collapses to nothing. */
@media (max-width:900px){.bento-card.is-wide .mock{min-height:330px}}
.mock-item{position:absolute;z-index:2}
/* Each piece arrives from the side it sits on, overriding the generic stagger offset. */
.mock > .mock-item{transform:translate3d(var(--fx,0),14px,0) scale(.95)}
.mock.is-visible > .mock-item{transform:none}
/* Each panel keeps one idea alive rather than every piece drifting at once: the cards and
   documents stay put, and only the element that carries that card's meaning moves. The
   entrance owns `transform` on .mock-item and the parallax owns it on .mock-px, so
   anything positional rides the independent `translate` property instead of either. */

/* Search: the pin hovers over the map it just landed on. */
@keyframes mock-bob{0%,100%{translate:0 0}50%{translate:0 -.55em}}
.mock.is-visible .mock-pin{animation:mock-bob 4.6s ease-in-out 1s infinite}

/* Reviews: the reply writes itself in a word at a time, holds, then starts over. Every
   word is already laid out, so only opacity moves and the card never reflows. */
@keyframes mock-write{0%{opacity:0}6%,80%{opacity:1}88%,100%{opacity:0}}
.mock.is-visible .mock-typed span{
  opacity:0;
  animation:mock-write 11s ease calc(var(--w,0) * 105ms + 1.2s) infinite;
}

/* Content: the channels the post fans out to drift on their own clocks, and the card
   flips from scheduled to published so the post is seen actually going out. */
.mock.is-visible .mock-plat{
  animation:mock-bob calc(5.4s + var(--i,0) * 620ms) ease-in-out infinite;
  animation-delay:calc(var(--i,0) * 260ms + 900ms);
}
.mock-swap{position:relative;display:block}
.mock-state{display:block}
/* Hidden in its own right, not only by the animation: the panel becomes visible before
   the swap's delay elapses, and without this both labels render stacked in that gap. */
.mock-state.is-next{position:absolute;left:0;top:0;right:0;opacity:0}
@keyframes mock-state-out{0%,40%{opacity:1;translate:0 0}48%,94%{opacity:0;translate:0 -.35em}100%{opacity:1;translate:0 0}}
@keyframes mock-state-in{0%,40%{opacity:0;translate:0 .35em}48%,94%{opacity:1;translate:0 0}100%{opacity:0;translate:0 .35em}}
.mock.is-visible .mock-state{animation:mock-state-out 9s ease-in-out 1.4s infinite}
.mock.is-visible .mock-state.is-next{animation-name:mock-state-in}
/* The dot rides the same clock: pending while queued, brand green once it is out. */
@keyframes mock-dot-state{
  0%,40%{background:var(--secondary);box-shadow:0 0 0 .3em color-mix(in srgb,var(--secondary) 24%,transparent)}
  48%,94%{background:var(--success);box-shadow:0 0 0 .3em color-mix(in srgb,var(--success) 26%,transparent)}
  100%{background:var(--secondary);box-shadow:0 0 0 .3em color-mix(in srgb,var(--secondary) 24%,transparent)}
}
.mock.is-visible .mock-dot{animation:mock-dot-state 9s ease-in-out 1.4s infinite}

/* Service: the upload bars keep working, without resetting their fill. */
@keyframes mock-sweep{from{background-position:180% 0}to{background-position:-80% 0}}
.mock.is-visible .mock-bar i{
  background-image:linear-gradient(90deg,transparent 20%,rgba(255,255,255,.55) 50%,transparent 80%);
  background-size:60% 100%;background-repeat:no-repeat;
  animation:mock-sweep 2.6s linear 1.6s infinite;
}
.mock-px{will-change:transform}
.mock-card{
  background:#fff;border-radius:1.15em;padding:1em 1.15em;margin:0;
  box-shadow:0 .85em 2.4em rgba(1,57,65,.14);
}
/* A mockup is its own light panel whatever card it sits in, and `.bento-card.is-dark p`
   is the more specific selector, so it has to be said again here. */
.bento-card.is-dark .mock p{color:var(--ink)}
.mock p{margin:0;font-size:.98em;line-height:1.4}
.mock strong{font-weight:600;letter-spacing:-.015em}
.mock .star{color:var(--secondary)}
.mock-thumb{width:100%;height:5.4em;object-fit:cover;border-radius:.7em}
.mock-who{display:flex;align-items:center;gap:.6em;margin-bottom:.7em}
.mock-who img{width:2.6em;height:2.6em;border-radius:50%;object-fit:cover;flex:0 0 auto}
.mock-who strong{display:block;font-size:1.02em;line-height:1.25}
.mock-stars{display:block;color:var(--secondary);font-size:.9em;letter-spacing:.08em;line-height:1.1}
.mock-role{display:block;font-size:.86em;color:var(--ink-soft);line-height:1.25}

/* card 1 — a search that lands on the venue */
.mock-map{position:absolute;inset:0;width:100%;height:100%;z-index:0;color:var(--primary);opacity:.32}
.mock-map .draw{stroke:currentColor;stroke-width:7;fill:none;stroke-linecap:round;transition-duration:1.5s}
.mock-map .draw:nth-child(2){transition-delay:.36s}
.mock-map .draw:nth-child(3){transition-delay:.5s}
.mock-map .draw:nth-child(4){transition-delay:.64s}
.mock-search .mock-search-bar{left:5%;top:8%;right:24%}
.mock-search-bar .mock-px{
  display:flex;align-items:center;gap:.65em;
  background:#fff;border-radius:var(--radius-pill);padding:.8em 1.15em;
  font-size:1.05em;color:var(--ink-soft);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  box-shadow:0 .7em 2em rgba(1,57,65,.14);
}
.mock-glass{
  width:1.05em;height:1.05em;flex:0 0 auto;background:var(--ink-soft);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7 1a6 6 0 1 0 3.66 10.75l3.3 3.3 1.06-1.06-3.3-3.3A6 6 0 0 0 7 1Zm0 1.5a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7 1a6 6 0 1 0 3.66 10.75l3.3 3.3 1.06-1.06-3.3-3.3A6 6 0 0 0 7 1Zm0 1.5a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Z'/%3E%3C/svg%3E") center/contain no-repeat;
}
.mock-search .mock-pin{right:15%;top:25%}
.mock-pin svg{width:2.2em;height:2.9em;filter:drop-shadow(0 .5em .8em rgba(1,57,65,.3))}
.mock-search .mock-result{left:6%;bottom:6%;width:66%}
.mock-result strong{display:block;margin-top:.6em;font-size:1.15em;letter-spacing:-.02em}
.mock-meta{display:block;margin-top:.1em;font-size:.86em;color:var(--ink-soft)}

/* card 2 — a review arriving, and the reply going back out */
.mock-reviews .mock-review{left:4%;top:8%;width:66%}
.mock-reviews .mock-reply{right:4%;bottom:7%;width:64%}
.mock-reply .mock-card{background:var(--ink)}
.bento-card.is-dark .mock-reply .mock-card p,.mock-reply .mock-card p{color:rgba(255,255,255,.84)}
.mock-by{
  display:flex;align-items:center;gap:.5em;margin-bottom:.55em;
  font-size:.84em;color:rgba(255,255,255,.55);
}
.mock-mark{
  width:2.7em;height:1.05em;flex:0 0 auto;background:var(--primary);
  -webkit-mask:var(--wordmark) center/contain no-repeat;
  mask:var(--wordmark) center/contain no-repeat;
}

/* card 3 — content going out everywhere, on a schedule */
.mock-content .mock-post{left:20%;top:8%;width:43%}
.mock-content .mock-post .mock-thumb{height:7.4em}
.mock-content .mock-post p{margin-top:.5em;font-size:.92em}
.mock-actions{display:flex;gap:.7em;margin-top:.6em}
.mock-actions svg{width:1.35em;height:1.35em;fill:none;stroke:var(--ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.mock-content .mock-sched{left:2%;top:26%;width:31%}
/* The panel's height is pinned at 330px below 900px but the post card's is not, so on a
   phone the action row rides up into the chip's 26% and the chip covers the like button.
   Sit it on the photo instead, with the left clearance it gets on a wider stage. */
@media (max-width:640px){.mock-content .mock-sched{left:4%;top:18%}}
.mock-sched .mock-card{display:flex;align-items:center;gap:.65em}
.mock-dot{
  width:.7em;height:.7em;flex:0 0 auto;border-radius:50%;background:var(--secondary);
  box-shadow:0 0 0 .3em color-mix(in srgb,var(--secondary) 24%,transparent);
}
.mock-sched strong{display:block;font-size:1.02em;line-height:1.25}
.mock-when{display:block;font-size:.86em;color:var(--ink-soft);line-height:1.25}
.mock-plat img{width:3.4em;height:3.4em;padding:.7em;box-sizing:border-box;border-radius:50%;background:#fff}
.mock-content .mock-plat-1{right:17%;top:41%}
.mock-content .mock-plat-2{right:5%;top:57%}
.mock-content .mock-plat-3{right:23%;top:71%}

/* card 4 — the work handed over, and the person who has it */
.mock-service .mock-upload{left:5%;top:15%;width:50%}
.mock-drop{
  display:flex;flex-direction:column;align-items:center;gap:.5em;text-align:center;
  border:.14em dashed var(--border);border-radius:.9em;padding:1.15em .8em;
  color:var(--ink-soft);font-size:.92em;
}
.mock-drop svg{width:1.6em;height:1.6em;fill:none;stroke:var(--ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.mock-file{display:flex;align-items:center;gap:.7em;margin-top:.8em;padding:.6em .75em;border:1px solid var(--border);border-radius:.8em}
.mock-ext{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:2.3em;height:2.6em;border-radius:.35em;background:var(--paper-tint);
  color:var(--primary-dark);font-size:.72em;font-weight:700;
}
.mock-file-body{flex:1;min-width:0}
.mock-file-body strong{display:block;font-size:.9em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mock-bar{display:block;height:.42em;margin-top:.4em;border-radius:var(--radius-pill);background:var(--paper-tint);overflow:hidden}
.mock-bar i{
  display:block;width:0;height:100%;border-radius:inherit;background:var(--primary);
  transition:width 1.1s var(--ease) calc(var(--i,0) * 90ms + 420ms);
}
.mock.is-visible .mock-bar i{width:var(--p,100%)}
.mock-service .mock-note{right:4%;top:36%;width:46%}

/* ---------- statement: the photos are set inside the sentence ---------- */
/* The reference's signature move, and the one thing this page had no equivalent of.
   Each photo opens from zero width as the line arrives, so the sentence sets its own
   type rather than leaving holes waiting to be filled, and the block lags the scroll
   (a negative parallax) so it rises into place instead of sliding past. */
.statement{background:transparent}
.statement .wrap{max-width:1060px}
.statement-px{will-change:transform}
.statement-line{
  margin:0;font-weight:600;letter-spacing:-.038em;text-wrap:balance;
  font-size:clamp(2rem,4.4vw,3.7rem);line-height:1.24;
}
.statement-photo{
  display:inline-block;vertical-align:middle;
  width:0;height:.92em;overflow:hidden;
  margin:0 .03em;border-radius:var(--radius-pill);
  transform:translateY(-.07em);
  transition:width .9s var(--ease);
  transition-delay:calc(var(--i,0) * 150ms + 160ms);
}
.statement-line.is-visible .statement-photo{width:1.95em}
.statement-photo img{width:1.95em;height:.92em;max-width:none;object-fit:cover;border-radius:var(--radius-pill)}
.statement-cta{margin-top:44px}
@media (max-width:600px){
  .statement-line{line-height:1.32}
  .statement-photo,.statement-photo img{height:.86em}
  .statement-line.is-visible .statement-photo,.statement-photo img{width:1.7em}
}

/* ---------- logo strip / carousel ---------- */
.logo-strip{padding:52px 0}
.ticker-px{will-change:transform}
/* Both states name the same filter functions, or the list mismatch makes the hover
   snap instead of easing. */
.logo-strip img{opacity:.55;filter:grayscale(1) brightness(1);transition:opacity .3s,filter .3s}
.logo-strip img:hover{opacity:1;filter:grayscale(1) brightness(0)}
.carousel{overflow:hidden}
.carousel-track{display:flex;gap:56px;align-items:center;will-change:transform}
/* The source PNGs carry generous padding around each mark, so the box runs tall. */
.logo-strip img{height:132px;width:auto;flex:0 0 auto}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (max-width:860px){.carousel-track{gap:36px}.logo-strip img{height:96px}}

/* ---------- stat strip ---------- */
.stat-strip{
  position:relative;overflow:hidden;
  background:var(--paper-tint);border-radius:var(--radius-lg);margin:0 24px;text-align:center;
}
.deco-blob{
  position:absolute;left:50%;top:-30%;width:min(760px,80%);aspect-ratio:1;
  margin-left:-40%;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--primary) 26%,transparent) 0%,transparent 62%);
}
.stat-strip .wrap{position:relative;z-index:1}
.stat-strip .wrap{max-width:var(--max-width)}
.stat-strip h2,.stat-strip>.wrap>p{max-width:60ch;margin-left:auto;margin-right:auto}
.stat-grid{display:flex;flex-wrap:wrap;justify-content:center;gap:36px 40px;margin:48px 0}
.stat-card{text-align:center;flex:0 0 168px}
.stat-number{font-size:clamp(2.8rem,4vw,3.6rem);font-weight:600;letter-spacing:-.045em;color:var(--ink);margin:0}
.stat-number .counter{color:var(--ink)}
.stat-number-suffix{color:var(--secondary)}
.stat-label{color:var(--ink-soft);font-size:.9rem;margin:0;max-width:22ch;margin-left:auto;margin-right:auto}
.stat-caption{text-align:center;color:var(--ink-soft);font-size:.9rem}
@media (max-width:860px){.stat-grid{grid-template-columns:repeat(2,1fr)}}

/* ---------- tabs ---------- */
.tabs-feature{background:transparent;text-align:center}
.tabs-feature .tab-list{justify-content:center}
.tabs-feature h2{max-width:46ch;margin-left:auto;margin-right:auto}
.tabs-feature .tab-panel-copy{text-align:left}
.tab-list{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:28px}
.tab-trigger{
  padding:11px 21px;border-radius:var(--radius-pill);border:1px solid var(--border);background:var(--paper);color:var(--ink-soft);
  cursor:pointer;font-weight:600;transition:background .3s,color .3s,border-color .3s;
}
.tab-trigger.is-active,.tab-trigger:hover{background:var(--ink);color:#fff;border-color:var(--ink)}
.tab-panel{display:none}
.tab-panel.is-active{display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:center}
.tab-panel-image img{border-radius:var(--radius-lg);box-shadow:var(--shadow)}
.tab-panel-copy{grid-column:1}
@media (max-width:860px){.tab-panel.is-active{grid-template-columns:1fr}.tab-panel-image{order:-1}}
.check-list{list-style:none;padding:0;margin:0 0 20px;display:flex;flex-direction:column;gap:10px}
.check-list.inline{flex-direction:row;flex-wrap:wrap;gap:20px}
.check-list li{padding-left:30px;position:relative}
.check-list li::before{
  content:"";position:absolute;left:0;top:2px;width:20px;height:20px;border-radius:50%;
  background:var(--primary) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M6.5 10.9 4 8.4l-1 1 3.5 3.5L13 6.4l-1-1-5.5 5.5Z' fill='white'/%3E%3C/svg%3E") center/12px no-repeat;
}
.check-list.invert li::before{
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M6.5 10.9 4 8.4l-1 1 3.5 3.5L13 6.4l-1-1-5.5 5.5Z' fill='%2351B5A6'/%3E%3C/svg%3E") center/12px no-repeat;
}

/* ---------- cards / problem-solution ---------- */
.card-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}
.card{
  background:#fff;border:1px solid var(--border);border-radius:var(--radius-lg);padding:30px;
  box-shadow:var(--shadow-sm);
  transition:transform .3s ease,box-shadow .3s ease;
}
.card:hover{transform:translateY(-3px);box-shadow:var(--shadow)}
.read-more{
  display:inline-flex;align-items:center;gap:6px;align-self:flex-start;
  background:var(--primary);color:#fff;font-weight:600;font-size:.85rem;
  padding:9px 16px;border-radius:var(--radius-pill);
}
.badge{display:inline-block;background:var(--accent);color:var(--ink);font-size:.75rem;font-weight:700;padding:3px 10px;border-radius:999px;margin-bottom:8px}
.section-intro{max-width:60ch;color:var(--ink-soft)}
@media (max-width:960px){.card-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:600px){.card-grid{grid-template-columns:1fr}}

/* ---------- success stories: an editorial spread ---------- */
/* Three portraits over two landscapes rather than five identical tiles, and every photo
   drifts inside its own frame at its own speed. The frame, the zoom and the drift are
   three nested elements because all three want to write transform. */
.case-spread{display:grid;grid-template-columns:repeat(6,1fr);gap:34px 22px}
.case-tile{grid-column:span 2;display:flex;flex-direction:column;text-decoration:none;color:inherit}
.case-tile.is-wide{grid-column:span 3}
.case-shot{
  position:relative;display:block;overflow:hidden;
  border-radius:var(--radius-lg);aspect-ratio:4/5;background:var(--paper-tint);
  transition:box-shadow .45s var(--ease);
}
.case-tile.is-wide .case-shot{aspect-ratio:16/10}
/* Taller than the frame on purpose: the slack is what the drift moves through. */
/* The tiles start 170px outside their column, which would drag the page that much wider on
   the way in. Clipping the section on x alone both kills that scroll and gives them an edge to
   emerge from; at rest they carry no transform, so nothing real is ever cut off. */
.case-teasers{overflow-x:hidden;overflow-x:clip}
/* The row converges: the outer tiles come in from the left and right edges and meet in
   the middle, landing with a short overshoot so they read as arriving rather than sliding
   to a stop. The centre tile lifts into the gap the other two close on. The delays stay
   tight, because the point is that they collide together rather than deal out in sequence.
   This overrides the generic [data-stagger] rise; `--i` is the index the observer writes. */
.case-spread > .case-tile{
  transition:opacity 1.1s var(--ease),transform 1.25s cubic-bezier(.19,1.12,.32,1);
  transition-delay:calc(var(--i,0) * 70ms);
}
.case-spread > .case-tile:nth-child(1),
.case-spread > .case-tile:nth-child(4){transform:translate3d(-170px,0,0)}
.case-spread > .case-tile:nth-child(3),
.case-spread > .case-tile:nth-child(5){transform:translate3d(170px,0,0)}
.case-spread > .case-tile:nth-child(2){transform:translate3d(0,42px,0)}
.case-spread.is-visible > .case-tile{transform:none}
.case-zoom{position:absolute;inset:-9% 0;display:block;transition:transform .6s var(--ease)}
.case-shot img{width:100%;height:100%;object-fit:cover}
.case-tile:hover .case-shot{box-shadow:var(--shadow-lg)}
.case-tile:hover .case-zoom{transform:scale(1.05)}
.case-name{
  display:block;margin-top:20px;font-weight:600;line-height:1.2;letter-spacing:-.03em;
  font-size:clamp(1.15rem,1.5vw,1.5rem);transition:color .3s var(--ease);
}
.case-tile:hover .case-name{color:var(--secondary)}
.case-line{display:block;margin-top:8px;color:var(--ink-soft);font-size:.95rem;line-height:1.45}
.case-tile .read-more{
  margin-top:auto;padding:16px 0 0;background:none;color:var(--primary-dark);font-size:.9rem;
  transition:transform .3s var(--ease);
}
.case-tile:hover .read-more{transform:translateX(5px)}
@media (max-width:980px){
  .case-spread{grid-template-columns:repeat(4,1fr)}
  .case-tile,.case-tile.is-wide{grid-column:span 2}
}
@media (max-width:620px){
  .case-spread{grid-template-columns:1fr;gap:30px}
  .case-tile,.case-tile.is-wide{grid-column:span 1}
  .case-tile.is-wide .case-shot{aspect-ratio:4/5}
}

/* ---------- testimonials ---------- */
.testimonials{text-align:center}
.testimonials h2,.testimonials .section-intro{margin-left:auto;margin-right:auto}
.testimonial{max-width:640px;margin:0 auto;text-align:center;padding:0 24px}
.testimonial p{font-size:clamp(1.3rem,2.2vw,1.9rem);font-style:normal;letter-spacing:-.025em;line-height:1.35}
.testimonial cite{color:var(--ink-soft);font-style:normal}
.testimonial-carousel .carousel-track{flex-direction:column}

/* ---------- cta band ---------- */
/* The closing band is the site's equivalent of the dashboard's auth marketing panel,
   which is the one place that surface is used at full-panel scale. */
.cta-band{background:var(--brand-gradient);color:#fff;border-radius:var(--radius-lg);margin:0 24px}
.cta-band h2{color:#fff}
/* The gradient is the closing band's signature. An in-page cta_band takes the quiet tint
   instead, so a page carrying both (about-us) doesn't show the same surface twice. */
.cta-band:not(.global-cta){background:var(--paper-tint);color:var(--ink)}
.cta-band:not(.global-cta) h2{color:var(--ink)}
.cta-band:not(.global-cta) .btn-primary{background:var(--ink);color:#fff}
.cta-band:not(.global-cta) .btn-primary:hover{background:#000}
.cta-inner{display:grid;grid-template-columns:1fr .6fr;gap:32px;align-items:center}
/* The band borrows .hero-ctas, which centres its row for the centred hero. Here the
   heading and bullets are flush left, so the buttons have to be too. */
.cta-band .hero-ctas{justify-content:flex-start;margin-top:26px}
/* Orange on the brand gradient fights the gradient's own warm end; white carries the
   primary action against both ends of it. */
.cta-band .btn-primary{background:#fff;color:var(--ink)}
.cta-band .btn-primary:hover{background:rgba(255,255,255,.86)}
/* Stacked, the column is wider than the file's intrinsic 500px, so the photo sat
   undersized with dead space beside it. It fills its column at every width now. */
.cta-image{border-radius:var(--radius);width:100%;height:auto}
@media (max-width:860px){.cta-inner{grid-template-columns:1fr}}

/* ---------- footer ---------- */
.site-footer{background:var(--ink);color:#fff;padding-top:64px;border-radius:var(--radius-lg) var(--radius-lg) 0 0;margin-top:24px}
.footer-top{padding-bottom:48px;border-bottom:1px solid rgba(255,255,255,.15)}
.footer-links{display:grid;grid-template-columns:1fr 1fr;gap:24px}
.footer-links h3{font-size:1rem;color:rgba(255,255,255,.7)}
.footer-links ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:10px}
.footer-links a{text-decoration:none;color:#fff;transition:color .3s}
.footer-links a:hover{color:var(--secondary)}
.footer-links .social-icons{flex-direction:row;gap:12px;margin-top:18px}
.social-icons a{
  display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.08);
  transition:background-color .3s,transform .3s;
}
.social-icons a:hover{background:var(--primary)}
.social-icons svg{width:16px;height:16px;fill:#fff}
.footer-bottom{display:flex;justify-content:space-between;align-items:center;padding-block:24px;flex-wrap:wrap;gap:12px;color:rgba(255,255,255,.6);font-size:.85rem}
.legal-links{list-style:none;display:flex;gap:18px;padding:0;margin:0}
.legal-links a{color:rgba(255,255,255,.8);text-decoration:none}
.hp-field{position:absolute!important;left:-9999px!important;width:1px;height:1px;overflow:hidden}
@media (max-width:760px){.footer-links{grid-template-columns:1fr}}
@media (max-width:860px){
  .footer-links ul:not(.social-icons){gap:0}
  .footer-links ul:not(.social-icons) a{display:inline-block;min-width:44px;padding:11px 0}
  .social-icons a{width:44px;height:44px}
  .legal-links{flex-wrap:wrap;gap:0 18px}
  .legal-links a{display:inline-block;padding:12px 0}
}
@media (max-width:480px){
}

/* ---------- lead form pages ---------- */
.lead-inner{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:start}
.lead-form-card{background:#fff;border:1px solid var(--border);border-radius:var(--radius-lg);padding:36px;box-shadow:var(--shadow-sm)}
.lead-form,.job-detail form{display:flex;flex-direction:column;gap:14px}
.lead-form input,.lead-form textarea,.job-detail form input,.job-detail form textarea{
  padding:12px 16px;border-radius:10px;border:1px solid var(--border);font:inherit;width:100%;
}
.form-status{font-weight:600;color:var(--secondary)}
@media (max-width:860px){.lead-inner{grid-template-columns:1fr}}

/* ---------- pricing ---------- */
/* Five shared rows (name, price, description, features, CTA) so every card's button sits
   on the same line however long its description or feature list runs. Row gap stays 0: the
   children carry their own bottom margins. */
.pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(5,auto);column-gap:20px;row-gap:0;margin-top:32px}
.pricing-card{
  display:grid;grid-template-rows:subgrid;grid-row:span 5;
  background:#fff;padding:32px;position:relative;
  border:1px solid transparent;border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm);
  transition:opacity var(--fl-duration) var(--ease),transform .45s var(--ease),box-shadow .45s var(--ease);
}
/* The card tilts toward the pointer, ported from the dashboard's event plan post cards
   (TiltCard). JS writes transform inline, so the .45s entrance transition has to step out
   of the way first: initTilt adds .is-tilting on the first hover, once the card is in. */
.pricing-card.is-tilting{transition:transform .15s ease-out,box-shadow .15s ease-out}
.pricing-card > .btn{justify-self:start;align-self:end;margin-top:28px}
.price{font-size:2rem;font-weight:700;color:var(--secondary)}
.price span{font-size:1rem;color:var(--ink-soft);font-weight:500}
.pricing-card.tone-light{color:var(--ink);border-color:var(--border)}
.pricing-card.tone-light .price{color:var(--primary-dark)}
.pricing-card.tone-teal{background:var(--primary);color:#fff}
.pricing-card.tone-teal h3,.pricing-card.tone-teal .price,.pricing-card.tone-teal .price span{color:#fff}
.pricing-card.tone-dark{background:var(--ink);color:#fff}
.pricing-card.tone-dark h3,.pricing-card.tone-dark .price,.pricing-card.tone-dark .price span{color:#fff}
.pricing-card .badge{position:absolute;top:32px;right:32px;background:var(--paper-tint-2);color:var(--primary-dark);font-weight:600}
/* Stacked, there is nothing to line up across, so the cards drop back to normal flow. */
@media (max-width:960px){
  .pricing-grid{grid-template-columns:1fr;grid-template-rows:none;row-gap:20px}
  .pricing-card{display:block;grid-row:auto}
}

/* ---------- jobs ---------- */
.job-cards{list-style:none;padding:0;display:flex;flex-direction:column;gap:14px}
.job-cards a{text-decoration:none;font-weight:600;font-size:1.1rem}
.job-meta{list-style:none;padding:0;display:flex;gap:24px;flex-wrap:wrap;color:var(--ink-soft);margin-bottom:32px}

/* ---------- legal / rich text ---------- */
.legal-body h2{margin-top:2em}
.legal-body ul{padding-left:1.3em}
.fact-list{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;list-style:none;padding:0}
.fact-list dt{color:var(--ink-soft);font-size:.85rem}
.fact-list dd{font-weight:700;margin:0}

/* ---------- floating language switcher ---------- */
/* ---------- language switch ---------- */
/* One circle carrying the flag of the language you are on. It opens upward to reveal the
   other flag, and that flag is the switch: with two languages the menu is a single item. */
.lang-fab{position:fixed;right:20px;bottom:20px;z-index:250;display:flex;flex-direction:column;align-items:center;gap:10px}
.lang-fab-toggle,.lang-fab-option{
  display:grid;place-items:center;width:48px;height:48px;padding:0;
  border-radius:50%;border:1px solid var(--border);background:#fff;
  box-shadow:0 8px 24px rgba(1,57,65,.18);
  font-size:1.3rem;line-height:1;text-decoration:none;cursor:pointer;
}
/* Collapsed, the other flag is tucked behind the toggle rather than merely transparent,
   so it never intercepts a click while hidden. */
.lang-fab-option{
  opacity:0;transform:translateY(14px) scale(.72);pointer-events:none;
  transition:opacity .28s var(--ease),transform .28s var(--ease);
}
.lang-fab.is-open .lang-fab-option{opacity:1;transform:none;pointer-events:auto}
.lang-fab-toggle{transition:transform .28s var(--ease),box-shadow .28s var(--ease)}
.lang-fab-toggle:hover{transform:scale(1.07)}
.lang-fab.is-open .lang-fab-toggle{box-shadow:0 0 0 2px var(--primary),0 8px 24px rgba(1,57,65,.18)}
.lang-fab-option:hover{transform:scale(1.07)}

/* ---------- misc page kinds ---------- */
/* ---------- case study detail ---------- */
.case-back{display:block;width:fit-content;margin-bottom:22px;color:var(--ink-soft);font-weight:600;font-size:.9rem;text-decoration:none}
.case-back:hover{color:var(--ink)}
/* These titles are full sentences rather than a few words, so the hero scale overruns
   the reading column. */
.case-detail h1{font-size:clamp(2rem,3.6vw,3.1rem);margin-bottom:0}
/* The location and title sit on the photo, over a scrim that starts well above them
   so the type never lands on an unlit patch. */
.case-hero{position:relative;margin:0;border-radius:var(--radius-lg);overflow:hidden;aspect-ratio:16/9;box-shadow:var(--shadow)}
.case-hero img{width:100%;height:100%;object-fit:cover;display:block}
.case-hero-copy{
  position:absolute;left:0;right:0;bottom:0;
  padding:140px clamp(24px,4vw,52px) clamp(24px,4vw,48px);
  background:linear-gradient(to top,rgba(8,20,22,.78) 0%,rgba(8,20,22,.45) 50%,transparent 100%);
}
.case-hero-copy .eyebrow{margin-bottom:16px}
.case-hero-copy h1{color:#fff;max-width:760px}
@media (max-width:760px){.case-hero{aspect-ratio:3/4}.case-hero-copy{padding-top:100px}}
.case-body{margin-top:48px}
.case-body h2{font-size:clamp(1.35rem,2.1vw,1.75rem)}
.case-body p{color:var(--ink-soft);line-height:1.65;margin:0}
.case-body .check-list{margin-bottom:0}
.case-quote{margin:52px 0 0;padding-left:26px;border-left:3px solid var(--primary)}
.case-quote p{font-size:clamp(1.2rem,1.9vw,1.55rem);line-height:1.35;letter-spacing:-.025em;margin:0 0 14px}
.case-quote cite{color:var(--ink-soft);font-style:normal;font-size:.95rem}
