/* ============================================================
   LEAP interactive, LLC — Production Design System
   Direction: "Interactive by nature" (concept 4, approved)
   Fonts: Schibsted Grotesk (headings) + Onest (body)
   Themes: light (default) + dark. Tokens map cleanly to Divi
   global colors later. No black hovers. No magnetic buttons.
   ============================================================ */

/* ---------- Tokens ---------- */
:root{
  --bg:#EDEFF2; --surface:#FFFFFF; --surface-2:#F5F7F9;
  --fg:#0B0F14; --soft:#586372; --faint:#8A94A1;
  --line:rgba(11,15,20,.09); --line-2:rgba(11,15,20,.16);
  --blue:#1E63C8; --blue-bright:#2F81F7; --teal:#0FB89E;
  --shadow:0 26px 60px -32px rgba(15,30,60,.35);
  --shadow-sm:0 12px 30px -18px rgba(15,30,60,.28);
  --spot:rgba(47,129,247,.10);
  --grad:linear-gradient(120deg,var(--blue),var(--blue-bright) 60%,var(--teal));
  /* Loop-safe gradients. An animated gradient tiles by shifting one image width,
     so the wrap is only invisible when the first stop equals the last stop.
     --grad-loop (horizontal) for tall elements: edges line up at any height.
     --grad-loop-d (120deg) for thin bars: keeps the brand angle, no height for
     an edge mismatch to show. Never animate --grad itself. */
  --grad-loop:linear-gradient(90deg,var(--blue),var(--blue-bright) 25%,var(--teal) 50%,var(--blue-bright) 75%,var(--blue));
  --grad-loop-d:linear-gradient(120deg,var(--blue),var(--blue-bright) 25%,var(--teal) 50%,var(--blue-bright) 75%,var(--blue));
  --wash-teal:13%; --wash-blue:9%;
  --radius:20px; --radius-lg:28px; --radius-sm:13px;
  --ease:cubic-bezier(.2,.9,.3,1.1);
  --spring:cubic-bezier(.2,.9,.3,1.4);
  --container:1200px;
  --gutter:clamp(20px,5vw,44px);
  --font-head:"Schibsted Grotesk",system-ui,sans-serif;
  --font-body:"Onest",system-ui,-apple-system,sans-serif;
}
/* White: same light palette, pure-white page. The team read the default light
   bg (#EDEFF2) as "cloudy", so here --bg goes to #FFF. That collapses the
   bg/surface contrast cards relied on, so the shadow tokens carry a 1px inset
   ring: every card keeps a crisp edge with zero extra rules and no layout
   shift (inset, not border). Surface-2 stays tinted so nested blocks still
   separate, and the corner washes tick up a point since they read lighter on
   pure white. Light and dark are untouched. */
:root[data-theme="white"]{
  --bg:#FFFFFF; --surface:#FFFFFF; --surface-2:#F4F6FA;
  --line:rgba(11,15,20,.10); --line-2:rgba(11,15,20,.17);
  --shadow:0 26px 60px -32px rgba(15,30,60,.30),inset 0 0 0 1px rgba(11,15,20,.07);
  --shadow-sm:0 14px 32px -20px rgba(15,30,60,.26),inset 0 0 0 1px rgba(11,15,20,.07);
  /* Same shadows minus the ring, for components that already draw a real border. */
  --shadow-bare:0 26px 60px -32px rgba(15,30,60,.30);
  --shadow-sm-bare:0 14px 32px -20px rgba(15,30,60,.26);
  --wash-teal:15%; --wash-blue:10%;
}
/* The ring stacks on top of an existing 1px border and reads as one thick edge,
   so anything already bordered takes the bare shadow instead. */
:root[data-theme="white"] .drop{box-shadow:var(--shadow-bare)}
:root[data-theme="white"] .card-surface{box-shadow:var(--shadow-sm-bare)}
:root[data-theme="dark"]{
  --bg:#090C11; --surface:#141B24; --surface-2:#0F151D;
  --fg:#EEF2F6; --soft:#9AA6B4; --faint:#5F6B79;
  --line:rgba(238,242,246,.10); --line-2:rgba(238,242,246,.18);
  --blue:#2F81F7; --blue-bright:#5B9BFF; --teal:#2DD4B7;
  --shadow:0 30px 70px -30px rgba(0,0,0,.75);
  --shadow-sm:0 16px 40px -22px rgba(0,0,0,.6);
  --spot:rgba(45,212,183,.14);
  --wash-teal:17%; --wash-blue:11%;
}

/* ---------- Reset / base ---------- */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{background:var(--bg);color:var(--fg);font-family:var(--font-body);line-height:1.6;-webkit-font-smoothing:antialiased;transition:background .5s,color .5s;overflow-x:hidden;position:relative}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer}
ul{list-style:none}
img,svg{max-width:100%;display:block}
h1,h2,h3,h4{font-family:var(--font-head);font-weight:600;letter-spacing:-.02em;line-height:1.06}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{animation-duration:.01ms!important;transition-duration:.01ms!important}}

/* ---------- Layout helpers ---------- */
.wrap{max-width:var(--container);margin:0 auto;padding:0 var(--gutter)}
.sec{position:relative;z-index:2;padding:clamp(56px,9vw,120px) 0}
.sec.tight{padding:clamp(40px,6vw,72px) 0}
.sec.flush-top{padding-top:0}
.kick{display:inline-flex;align-items:center;gap:12px;font-size:.78rem;font-weight:500;color:var(--soft);font-family:var(--font-body)}
.kd{position:relative;top:-1px;width:8px;height:8px;border-radius:50%;background:var(--teal);flex:none}
.kd::before,.kd::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--teal);pointer-events:none}
.kd::before{animation:ping 2.6s cubic-bezier(.2,.7,.3,1) infinite}
.kd::after{animation:ping 2.6s cubic-bezier(.2,.7,.3,1) infinite;animation-delay:.6s}
.sec-head{max-width:640px;margin-bottom:clamp(30px,4vw,50px)}
.sec-head h2{font-size:clamp(1.9rem,4vw,2.9rem)}
.sec-head p{color:var(--soft);margin-top:14px;font-size:1.06rem}
.sec-head.row{max-width:none;display:flex;justify-content:space-between;align-items:end;flex-wrap:wrap;gap:18px}
.grad-txt{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}

/* ---------- Cursor spotlight ---------- */
#spot{position:fixed;top:0;left:0;width:520px;height:520px;border-radius:50%;pointer-events:none;z-index:1;transform:translate(-50%,-50%);background:radial-gradient(circle,var(--spot),transparent 65%);opacity:0;transition:opacity .5s}
body:hover #spot{opacity:1}
@media(hover:none){#spot{display:none}}

/* ============================================================
   Buttons + links  (approved styles)
   ============================================================ */
.btn{position:relative;display:inline-flex;align-items:center;gap:9px;padding:12px 22px;border-radius:99px;font-family:var(--font-head);font-weight:600;font-size:.92rem;border:1px solid transparent;overflow:hidden;transition:background-position .5s,box-shadow .4s,border-color .35s,color .35s,background .35s}
.btn svg{width:16px;height:16px;transition:transform .35s var(--ease)}
.btn:hover svg{transform:translateX(4px)}
/* primary: gradient shift */
.btn.primary{background:var(--grad);background-size:160% 100%;background-position:0 0;color:#fff}
.btn.primary:hover{background-position:100% 0;box-shadow:var(--shadow-sm)}
/* secondary: teal border + tint */
.btn.secondary{background:transparent;border-color:var(--line-2);color:var(--fg)}
.btn.secondary:hover{border-color:var(--teal);color:var(--teal);background:color-mix(in srgb,var(--teal) 9%,transparent)}
/* on dark surfaces (e.g. inside a dark hero image) */
.btn.on-dark{border-color:rgba(255,255,255,.32);color:#fff}
.btn.on-dark:hover{border-color:var(--teal);color:var(--teal);background:color-mix(in srgb,var(--teal) 14%,transparent)}
.btn.sm{padding:10px 16px;font-size:.85rem}

/* arrow link: underline grow */
.link{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-head);font-weight:600;color:var(--blue);position:relative;padding-bottom:4px}
.link svg{width:15px;height:15px;transition:transform .3s var(--ease)}
.link::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:var(--teal);transition:width .35s cubic-bezier(.4,0,.2,1)}
.link:hover{color:var(--teal)}
.link:hover::after{width:100%}
.link:hover svg{transform:translateX(4px)}

/* ============================================================
   Card hover toolkit — apply per card as needed
   Each expects an inner ".go" arrow circle (optional)
   ============================================================ */
.go{width:44px;height:44px;border-radius:50%;border:1px solid var(--line-2);display:grid;place-items:center;color:var(--fg);transition:.35s var(--spring)}
.go svg{width:17px;height:17px}

.hover-ring{transition:transform .4s var(--ease),box-shadow .4s}
.hover-ring:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.hover-ring:hover .go{border-color:var(--teal);color:var(--teal);background:color-mix(in srgb,var(--teal) 10%,transparent)}

.hover-fill{transition:transform .4s var(--ease),box-shadow .4s}
.hover-fill:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.hover-fill:hover .go{background:linear-gradient(135deg,var(--blue),var(--teal));border-color:transparent;color:#fff;transform:rotate(-45deg)}

.hover-bar{position:relative;overflow:hidden;transition:transform .4s var(--ease),box-shadow .4s}
.hover-bar::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:var(--grad);transform:scaleX(0);transform-origin:left;transition:transform .4s cubic-bezier(.4,0,.2,1)}
.hover-bar:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.hover-bar:hover::before{transform:scaleX(1)}
.hover-bar:hover .go{border-color:var(--blue);color:var(--blue)}

.hover-tint{transition:transform .4s var(--ease),box-shadow .4s,background .4s}
.hover-tint:hover{transform:translateY(-4px);box-shadow:var(--shadow);background:color-mix(in srgb,var(--blue) 6%,var(--surface))}
.hover-tint:hover .go{background:var(--blue);border-color:transparent;color:#fff}

/* ============================================================
   Header / nav
   ============================================================ */
header.site-header{position:sticky;top:0;z-index:40;background:var(--bg);border-bottom:1px solid transparent;transition:border-color .4s}
header.site-header.scrolled{border-bottom-color:var(--line)}
.nav{display:flex;align-items:center;justify-content:space-between;height:72px;gap:22px}
.brand{display:flex;align-items:center;gap:10px;font-family:var(--font-head);font-weight:700;font-size:1.16rem;letter-spacing:-.02em}
/* The mark is the real logo PNG on a transparent ground, so it carries its own
   blues and needs no chip behind it. Sized in CSS, not just the width/height
   attributes, so it scales with the header at every breakpoint. */
.brand .mk{width:32px;height:32px;flex:none;object-fit:contain;transition:transform .4s var(--ease)}
.brand:hover .mk{transform:scale(1.07)}
/* .brand is a flex row, so every text node beside the mark chip used to become
   its own flex item and pick up the 10px gap: "LEAP i nteractive". The wordmark
   now lives in one inline span, so the gap only ever sits between chip and word. */
/* "LEAP" carries the weight in the page's own ink, "interactive" carries the
   brand colour. .wm holds the blue so the bare "nteractive" text node picks it
   up without needing a wrapper of its own, and .lp overrides back to --fg. */
.brand .wm{white-space:nowrap;color:var(--blue)}
.brand .lp{color:var(--fg)}
.brand .i{color:inherit}
.menu{display:flex;gap:4px;align-items:center}
.menu>li>a{display:inline-flex;align-items:center;gap:6px;padding:9px 14px;border-radius:99px;font-size:.92rem;color:var(--soft);transition:color .25s,background .25s}
/* The pill background is the hover affordance, so it belongs to the links you can
   still travel to. The page you are already on drops the pill and states itself in
   brand blue instead, which reads as "here" rather than as another target. */
.menu>li>a:hover{color:var(--fg);background:color-mix(in srgb,var(--fg) 6%,transparent)}
.menu>li.current>a,.menu>li.current>a:hover{color:var(--blue);background:none}
.has-drop{position:relative}
.has-drop>a svg{width:12px;height:12px;transition:transform .3s}
.has-drop:hover>a svg{transform:rotate(180deg)}
.drop{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%) translateY(8px);min-width:270px;padding:10px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);opacity:0;visibility:hidden;transition:opacity .28s var(--ease),transform .28s var(--ease)}
.has-drop:hover .drop,.has-drop:focus-within .drop{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0)}
/* The 8px gap under the trigger was dead space: the pointer left `.has-drop` before
   it reached the panel and the menu closed. This bridges the gap with an invisible
   strip that belongs to the panel, so the hover never breaks. */
.drop::before{content:"";position:absolute;left:0;right:0;top:-10px;height:10px}
.drop a{display:block;padding:11px 13px;border-radius:var(--radius-sm);transition:background .2s}
.drop a:hover{background:var(--surface-2)}
.drop strong{display:block;font-family:var(--font-head);font-size:.95rem;font-weight:600}
.drop span{font-size:.82rem;color:var(--faint)}
.tools{display:flex;align-items:center;gap:12px}
.tg{width:40px;height:40px;border-radius:50%;border:1px solid var(--line-2);background:var(--surface);color:var(--fg);display:grid;place-items:center;transition:transform .4s,border-color .3s}
.tg:hover{border-color:var(--teal);color:var(--teal)}
.tg svg{width:17px;height:17px}
.navtoggle{display:none;width:42px;height:42px;border-radius:11px;border:1px solid var(--line-2);background:var(--surface);position:relative}
.navtoggle span{position:absolute;left:11px;right:11px;height:2px;background:var(--fg);border-radius:2px;transition:.3s}
.navtoggle span:nth-child(1){top:15px}.navtoggle span:nth-child(2){top:20px}.navtoggle span:nth-child(3){top:25px}
@media(max-width:900px){
  .menu{position:fixed;inset:72px 0 auto 0;flex-direction:column;align-items:stretch;gap:2px;padding:16px 20px 26px;background:var(--bg);border-bottom:1px solid var(--line);transform:translateY(-120%);transition:transform .4s var(--ease);z-index:39;max-height:calc(100vh - 72px);overflow-y:auto}
  body.nav-open .menu{transform:translateY(0)}
  .menu>li>a{padding:14px 12px;font-size:1.05rem;justify-content:space-between}
  .drop{position:static;opacity:1;visibility:visible;transform:none;box-shadow:none;border:none;background:transparent;padding:0 0 8px 12px;min-width:0}
  .drop::before{display:none}
  .has-drop:hover>a svg{transform:none}
  .navtoggle{display:block}
  body.nav-open .navtoggle span:nth-child(1){top:20px;transform:rotate(45deg)}
  body.nav-open .navtoggle span:nth-child(2){opacity:0}
  body.nav-open .navtoggle span:nth-child(3){top:20px;transform:rotate(-45deg)}
  .nav .btn.primary{display:none}
}

/* ============================================================
   Hero
   ============================================================ */
.hero{position:relative;z-index:2;padding:clamp(46px,7vw,86px) 0 clamp(30px,5vw,56px)}
.hero-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(28px,5vw,60px);align-items:center}
.hero h1{font-size:clamp(2.5rem,5.4vw,4.1rem);font-weight:700}
.cycle{display:inline-flex;position:relative;color:var(--blue);min-width:4ch}
.cycle .w{display:inline-block}
.hero .lead{margin:22px 0 30px;color:var(--soft);font-size:clamp(1.02rem,1.7vw,1.18rem)}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap}
.hero-proof{display:flex;gap:26px;margin-top:34px;flex-wrap:wrap}
.hero-proof .p{display:flex;flex-direction:column}
.hero-proof .p b{font-family:var(--font-head);font-size:1.5rem;font-weight:700}
.hero-proof .p span{font-size:.82rem;color:var(--faint)}
@media(max-width:820px){.hero-grid{grid-template-columns:1fr}.hero-visual{order:-1}}

/* hero interactive visual card */
.viz{position:relative;background:var(--surface);border-radius:var(--radius-lg);padding:26px;box-shadow:var(--shadow);transform-style:preserve-3d;transition:transform .3s ease-out;will-change:transform}
.viz-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}
.viz-head .dots{display:flex;gap:6px}
.viz-head .dots i{width:9px;height:9px;border-radius:50%;background:var(--line-2)}
.viz-head .dots i:first-child{background:var(--teal)}
.viz-head span{font-size:.72rem;color:var(--faint);font-family:var(--font-head);letter-spacing:.04em}
.track{display:flex;flex-direction:column;gap:14px}
.phase{display:flex;align-items:center;gap:14px;padding:15px 16px;border-radius:var(--radius-sm);background:var(--surface-2);transition:background .4s,transform .4s var(--spring)}
.phase .num{font-family:var(--font-head);font-weight:700;font-size:.82rem;color:var(--faint);width:22px}
.phase .nm{font-family:var(--font-head);font-weight:600;font-size:1.02rem}
.phase .bar{flex:1;height:6px;border-radius:6px;background:var(--line);overflow:hidden}
.phase .bar i{display:block;height:100%;width:0;border-radius:6px;background:linear-gradient(90deg,var(--blue),var(--teal));transition:width .9s cubic-bezier(.4,0,.2,1)}
.phase.on{background:color-mix(in srgb,var(--teal) 12%,var(--surface-2));transform:translateY(-3px) scale(1.015)}
.phase.on .num{color:var(--teal)}
.phase.on .bar i{width:100%}
.viz-foot{margin-top:20px;display:flex;align-items:center;justify-content:space-between;font-size:.8rem;color:var(--soft)}
.pulse{display:inline-flex;align-items:center;gap:8px}
.pulse b{position:relative;width:8px;height:8px;border-radius:50%;background:var(--teal);flex:none}
.pulse b::before,.pulse b::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--teal);pointer-events:none}
.pulse b::before{animation:ping 2.6s cubic-bezier(.2,.7,.3,1) infinite}
.pulse b::after{animation:ping 2.6s cubic-bezier(.2,.7,.3,1) infinite;animation-delay:.6s}
/* P1 double ping: two rings leave the dot half a beat apart. Shared by the hero
   status dot and every section eyebrow dot (.kd) so they read as one idea. */
@keyframes ping{0%{transform:scale(1);opacity:.55}70%{opacity:0}100%{transform:scale(3.4);opacity:0}}

/* ============================================================
   Marquee
   ============================================================ */
.marquee{position:relative;z-index:2;overflow:hidden;padding:20px 0;margin-top:clamp(24px,4vw,44px);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.marquee-track{display:flex;gap:0;white-space:nowrap;width:max-content;will-change:transform}
.marquee-track span{font-family:var(--font-head);font-weight:600;font-size:clamp(1.1rem,2.2vw,1.7rem);padding:0 30px;display:inline-flex;align-items:center;gap:30px;color:var(--fg)}
.marquee-track span::after{content:"";width:8px;height:8px;border-radius:50%;background:var(--teal)}
.marquee-track span:nth-child(even){color:var(--soft)}

/* ============================================================
   Value
   ============================================================ */
.value{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,64px);align-items:stretch}
.value .big{font-family:var(--font-head);font-weight:600;font-size:clamp(1.5rem,3.2vw,2.3rem);line-height:1.25;letter-spacing:-.01em;color:var(--fg)}
.value .big em{font-style:normal;color:var(--blue)}
.value p{color:var(--soft);margin-top:20px;font-size:1.04rem}

/* Reusable media frame — drop in <img>/<video> to replace the placeholder */
.media-frame{position:relative;overflow:hidden;border-radius:var(--radius-lg);background:radial-gradient(70% 85% at 20% 22%,color-mix(in srgb,var(--blue) 22%,transparent),transparent 72%),radial-gradient(75% 90% at 82% 80%,color-mix(in srgb,var(--teal) 18%,transparent),transparent 72%),var(--surface);display:grid;place-items:center;min-height:380px;box-shadow:var(--shadow-sm)}
.media-frame img,.media-frame video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.media-frame .play{width:64px;height:64px;border-radius:50%;background:rgba(255,255,255,.9);display:grid;place-items:center;box-shadow:var(--shadow-sm);position:relative;z-index:1}
.media-frame .play svg{width:22px;height:22px;color:#0B0F14;margin-left:3px}
.media-frame .lbl{position:absolute;left:16px;bottom:12px;font-family:var(--font-head);font-weight:600;font-size:.72rem;color:var(--fg);opacity:.6;z-index:1}
@media(max-width:760px){.value{grid-template-columns:1fr}.media-frame{min-height:240px}}

/* ============================================================
   Services (soft rows w/ icon → hover-ring)
   ============================================================ */
.services{display:flex;flex-direction:column;gap:12px}
.svc{--tx:100%;--ty:0%;--bx:0%;--by:115%;--tsize:64% 128%;--bsize:56% 120%;
  position:relative;isolation:isolate;background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow-sm);padding:clamp(22px,3vw,32px);display:grid;grid-template-columns:auto 1fr auto;gap:24px;align-items:center}
/* Corner wash. Static, no motion. Absolutely positioned so it stays out of the
   grid; z-index -1 sits it on the card surface but under the content without
   touching any child. Position, pool size and strength are variables so each
   card can differ without repeating the gradient itself. */
.svc::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;opacity:.88;transition:opacity .4s;
  background:
    radial-gradient(var(--tsize) at var(--tx) var(--ty),color-mix(in srgb,var(--teal) var(--wash-teal),transparent),transparent 68%),
    radial-gradient(var(--bsize) at var(--bx) var(--by),color-mix(in srgb,var(--blue) var(--wash-blue),transparent),transparent 68%)}
.svc:hover::before{opacity:1}
/* Per-card variation (W2): same two pools and strength on every card, but the
   teal/blue corners rotate around the card so the three rows never read as one
   repeated card. Origins stay at the corners, keeping each pool off the icon chip. */
.services .svc:nth-child(2){--tx:0%;--ty:-12%;--bx:100%;--by:112%}
.services .svc:nth-child(3){--tx:100%;--ty:112%;--bx:-4%;--by:-16%}
.svc .ic{width:52px;height:52px;border-radius:14px;background:color-mix(in srgb,var(--blue) 12%,var(--surface));display:grid;place-items:center;color:var(--blue);transition:background .4s,color .4s}
/* White (option B, approved): a white card carrying the wash on a white page has
   no edge to sit inside, so the gradient read as a smudge. Wash off; the card is
   defined by the ring in --shadow-sm plus the lift. Light theme keeps the wash. */
:root[data-theme="white"] .svc::before{content:none}
/* The tint is the hover state, not the resting one: the card sits pure white until
   you touch it, then takes a flat blue 6%. Same value as the .hover-tint toolkit,
   and a flat fill rather than a soft pool, which is what read as dirt on white.
   The transition list is restated because setting it here would otherwise drop the
   transform and shadow easing .hover-ring supplies. */
:root[data-theme="white"] .svc{transition:transform .4s var(--ease),box-shadow .4s,background .4s}
:root[data-theme="white"] .svc:hover{background:color-mix(in srgb,var(--blue) 6%,var(--surface))}
/* Dark: the two-pool corner wash muddied against the near-black surface, so the
   cards take one flat teal tint instead. Wash off entirely; the icon chip mixes
   against that tint so it still reads a step off the card. */
:root[data-theme="dark"] .svc{--card:color-mix(in srgb,var(--teal) 12%,var(--surface));background:var(--card)}
:root[data-theme="dark"] .svc::before{content:none}
/* Pointer spotlight (approved), shared by dark and white. Both drop the idle chip
   glow idea in favour of lighting the card where the cursor actually is: a loop
   that runs with nobody there reads as decoration, a pool that tracks the pointer
   reads as alive. z-index -1 against the card's own isolation puts the pool on the
   surface but under the content, the same trick the corner wash used. --mx/--my
   are written by js/main.js; with no pointer the card simply never lights.
   Dark only. On white the pool read as a smudge for the same reason the corner
   wash did: soft color with no edge to sit inside looks like dirt on the page. */
:root[data-theme="dark"] .svc{--pool:var(--teal);--pool-str:16%}
:root[data-theme="dark"] .svc::after{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;
  background:radial-gradient(220px circle at var(--mx,50%) var(--my,50%),color-mix(in srgb,var(--pool) var(--pool-str),transparent),transparent 68%);
  opacity:0;transition:opacity .35s}
:root[data-theme="dark"] .svc:hover::after{opacity:1}
/* Chip is tonal teal at rest: a brighter step of the card's own tint, icon in full
   teal, so no second hue sits on the card. Hover is restated rather than inherited
   because a :root[data-theme] rule outranks plain `.svc:hover .ic`, so without this
   line the gradient fill would never fire in dark. */
:root[data-theme="dark"] .svc .ic{background:color-mix(in srgb,var(--teal) 24%,var(--surface));color:var(--teal);animation:none}
:root[data-theme="dark"] .svc:hover .ic{background:linear-gradient(135deg,var(--blue),var(--teal));color:#fff}
.svc:hover .ic{background:linear-gradient(135deg,var(--blue),var(--teal));color:#fff}
.svc .ic svg{width:24px;height:24px}
.svc .body h3{font-size:1.3rem}
.svc .body p{color:var(--soft);margin-top:6px;font-size:.98rem;max-width:54ch}
@media(max-width:640px){.svc{grid-template-columns:auto 1fr;gap:16px}.svc .go{display:none}}

/* ============================================================
   Featured work — Soft cards (layout A, approved)
   ============================================================ */
.work{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.wk{background:var(--surface);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm)}
.wk .thumb{height:190px;position:relative;overflow:hidden}
.wk .thumb .mono{position:absolute;top:14px;right:16px;font-family:var(--font-head);font-weight:700;font-size:2.4rem;color:var(--fg);opacity:.08}
.wk .thumb .tag{position:absolute;left:16px;bottom:12px;font-size:.68rem;color:var(--soft);font-family:var(--font-head);letter-spacing:.05em;opacity:.7}
.wk .thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.wk .body{padding:22px}
.wk .cat{font-size:.74rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--teal)}
.wk .body h3{font-size:1.25rem;margin:8px 0}
.wk .body p{color:var(--soft);font-size:.92rem}
.wk .foot{display:flex;justify-content:space-between;align-items:center;margin-top:16px}
.wk .yr{font-size:.82rem;color:var(--faint)}
.wk .sector{font-size:.82rem;font-weight:600;letter-spacing:.03em;color:var(--soft)}
@media(max-width:820px){.work{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.work{grid-template-columns:1fr}}

/* Brand-tinted placeholders (swap for photos later) */
.t1{background:radial-gradient(70% 90% at 18% 20%,color-mix(in srgb,var(--blue) 22%,transparent),transparent 72%),radial-gradient(75% 95% at 85% 82%,color-mix(in srgb,var(--teal) 18%,transparent),transparent 72%),var(--surface)}
.t2{background:radial-gradient(70% 90% at 82% 18%,color-mix(in srgb,var(--blue-bright) 24%,transparent),transparent 72%),radial-gradient(75% 95% at 15% 85%,color-mix(in srgb,var(--blue) 16%,transparent),transparent 72%),var(--surface)}
.t3{background:radial-gradient(72% 92% at 20% 82%,color-mix(in srgb,var(--teal) 26%,transparent),transparent 72%),radial-gradient(72% 92% at 82% 18%,color-mix(in srgb,var(--blue) 14%,transparent),transparent 72%),var(--surface)}
.t4{background:radial-gradient(70% 90% at 50% 12%,color-mix(in srgb,var(--blue-bright) 22%,transparent),transparent 72%),radial-gradient(75% 95% at 20% 90%,color-mix(in srgb,var(--blue) 16%,transparent),transparent 72%),var(--surface)}
.t5{background:radial-gradient(72% 92% at 85% 85%,color-mix(in srgb,var(--teal) 20%,transparent),transparent 72%),radial-gradient(72% 92% at 15% 20%,color-mix(in srgb,var(--blue-bright) 18%,transparent),transparent 72%),var(--surface)}

/* ============================================================
   Stats
   ============================================================ */
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;text-align:center}
.stat b{font-family:var(--font-head);font-weight:700;font-size:clamp(2.4rem,6vw,4rem);background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;display:block;line-height:1}
.stat span{color:var(--soft);font-size:.98rem;display:block;margin-top:8px}
@media(max-width:640px){.stats{grid-template-columns:1fr;gap:34px}}

/* ============================================================
   Clients
   ============================================================ */
.clients{text-align:center}
.clients .lbl{font-size:.82rem;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:26px}
.clients .row{display:flex;flex-wrap:wrap;justify-content:center;gap:clamp(24px,5vw,58px);align-items:center}
.clients .row span{font-family:var(--font-head);font-weight:700;font-size:clamp(1.1rem,2.4vw,1.6rem);color:var(--soft);opacity:.62;transition:opacity .3s,color .3s}
.clients .row span:hover{opacity:1;color:var(--fg)}

/* ============================================================
   Why choose — comparison (without vs with LEAP)
   ============================================================ */
.why-head{text-align:center;max-width:660px;margin:0 auto clamp(30px,4vw,46px)}
.why-head h2{margin-top:10px;font-size:clamp(2.2rem,5.4vw,3.7rem)}
.vs{display:grid;grid-template-columns:1fr 1fr;gap:clamp(16px,3vw,28px);max-width:920px;margin:0 auto}
.vs .col{padding:clamp(24px,3vw,36px);border-radius:var(--radius-lg)}
.vs .them{background:var(--surface-2)}
.vs .us{background:var(--surface);box-shadow:var(--shadow)}
.vs h4{font-family:var(--font-head);font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;margin-bottom:16px}
.vs .them h4{color:var(--faint)}
.vs .us h4{color:var(--teal)}
.vs li{display:flex;gap:11px;padding:9px 0;font-size:1rem;color:var(--soft);align-items:flex-start}
.vs .us li{color:var(--fg)}
.vs .ico{flex-shrink:0;font-weight:700;font-family:var(--font-head)}
.vs .them .ico{color:var(--faint)}
.vs .us .ico{color:var(--teal);display:inline-flex;margin-top:2px}
/* K1 checkmark: each tick draws itself once, staggered down the list, when the
   "Working with LEAP" column reveals (keyed to .rv->.in from js/main.js). Fires
   once by design - a checklist that keeps animating reads as a spinner. */
.vs .us .chk{width:20px;height:20px;color:var(--teal);overflow:visible}
.vs .us .chk path{fill:none;stroke:currentColor;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:24;stroke-dashoffset:24;transition:stroke-dashoffset .5s cubic-bezier(.5,0,.2,1)}
.vs .us .chklist.in .chk path{stroke-dashoffset:0}
.vs .us li:nth-child(1) .chk path{transition-delay:.42s}
.vs .us li:nth-child(2) .chk path{transition-delay:.54s}
.vs .us li:nth-child(3) .chk path{transition-delay:.66s}
.vs .us li:nth-child(4) .chk path{transition-delay:.78s}
@media(prefers-reduced-motion:reduce){.vs .us .chk path{stroke-dashoffset:0;transition:none}}
@media(max-width:640px){.vs{grid-template-columns:1fr}}

/* ============================================================
   Trusted — woven statement (few brands, no boxes)
   ============================================================ */
.trusted{text-align:center}
.trusted .lbl{font-size:.82rem;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:24px}
.trusted .statement{max-width:720px;margin:0 auto;font-family:var(--font-head);font-weight:600;font-size:clamp(1.4rem,3.4vw,2.3rem);line-height:1.42;letter-spacing:-.01em;text-wrap:balance}
.trusted .statement b{color:var(--blue)}
.trusted .statement .t{color:var(--teal)}

/* ============================================================
   CTA — plain full-bleed, big type + glow (swap to media bg later)
   ============================================================ */
.cta-plain{--dot:8%;--mesh:8%;--mesh2:9%;--calm:55%;--mark:.05;text-align:center;position:relative;overflow:hidden;border-top:1px solid var(--line);background-image:radial-gradient(color-mix(in srgb,var(--fg) var(--dot),transparent) 1px,transparent 1px);background-size:22px 22px}
.cta-plain::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;background:
  radial-gradient(42% 62% at 0% 0%,color-mix(in srgb,var(--blue) var(--mesh),transparent),transparent 70%),
  radial-gradient(42% 62% at 100% 100%,color-mix(in srgb,var(--teal) var(--mesh2),transparent),transparent 70%),
  radial-gradient(58% 66% at 50% 44%,var(--bg),color-mix(in srgb,var(--bg) var(--calm),transparent) 70%,transparent)}
.cta-plain .mark{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;font-family:var(--font-head);font-weight:700;font-size:clamp(7rem,24vw,20rem);letter-spacing:-.04em;line-height:1;white-space:nowrap;background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;opacity:var(--mark);pointer-events:none}
.cta-plain .c{position:relative;z-index:3;max-width:820px;margin:0 auto}
.cta-plain h2{font-size:clamp(2.4rem,6.5vw,4.6rem)}
.cta-plain p{color:var(--soft);margin:18px auto 32px;max-width:46ch}
.cta-plain .hero-cta{justify-content:center}

/* ============================================================
   Footer
   ============================================================ */
.site-footer{position:relative;z-index:2;border-top:1px solid var(--line);padding:clamp(36px,4vw,56px) 0 30px}
.foot-top{display:flex;justify-content:space-between;gap:36px;flex-wrap:wrap;align-items:flex-start;padding-bottom:24px}
.foot-links{display:flex;gap:clamp(30px,6vw,68px);flex-wrap:wrap}
.foot-top .brand-blk p{color:var(--soft);max-width:30ch;margin:14px 0 16px;font-size:.94rem}
.foot-col h4{font-family:var(--font-head);font-size:.8rem;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:14px;font-weight:600}
.foot-col a{display:block;padding:5px 0;color:var(--soft);font-size:.96rem;transition:color .25s}
.foot-col a:hover{color:var(--teal)}
.soc{display:flex;gap:10px}
.soc a{width:38px;height:38px;border-radius:11px;border:1px solid var(--line-2);display:grid;place-items:center;color:var(--soft);transition:.3s}
.soc a:hover{color:var(--teal);border-color:var(--teal);transform:translateY(-2px)}
.soc svg{width:17px;height:17px}
.foot-bot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px;padding-top:20px;border-top:1px solid var(--line);color:var(--faint);font-size:.85rem}
.foot-bot a:hover{color:var(--fg)}
@media(max-width:560px){.foot-top{flex-direction:column}}

/* ============================================================
   Inner page hero + prose + forms (for other pages)
   ============================================================ */
.page-hero{position:relative;z-index:2;padding:clamp(40px,6vw,80px) 0 clamp(24px,4vw,48px)}
/* Display type needs a measure. On the split heroes the column already supplies
   one, but on a full-width hero the h1 runs the whole 1200px wrap and breaks
   wherever it happens to land. 22ch is wide enough for a two-line statement and
   narrow enough that the break is deliberate. Never narrower than the column it
   already sits in, so the split heroes are unaffected. */
.page-hero h1{font-size:clamp(2.2rem,5.5vw,3.8rem);font-weight:700;max-width:22ch}
/* One phrase of the page-hero heading carries brand color. Same device as the
   blue em inside .argue .big, so it reads as the site's emphasis mark rather
   than as a one-off. Used where the hero has no image to carry color. */
.page-hero h1 em{font-style:normal;color:var(--blue)}
/* 78ch, not 60. On the split heroes the column caps the measure anyway, but on
   a full-width hero 60ch left the paragraph stranded in the left half of the
   wrap with dead space beside it. Still short of the ~85ch where a line gets
   genuinely hard to track back from. */
.page-hero .lead{max-width:78ch;margin-top:18px;color:var(--soft);font-size:clamp(1.02rem,1.7vw,1.2rem)}

/* ============================================================
   Pro Tips — blog index and single post
   Standard blog architecture: hero, one featured post across the full width,
   then a card grid that loads more on demand. No thumbnails anywhere, because
   no artwork exists for any of the nineteen posts and a grey placeholder box
   looks worse than none. The category label carries the colour instead.
   ============================================================ */
.eyebrow{font-family:var(--font-head);font-weight:600;font-size:.64rem;letter-spacing:.15em;
  text-transform:uppercase;color:var(--teal)}

/* Featured post: full width of the wrap, image on one side and the text on the
   other, so the block reads as a lead story rather than as a wide empty panel. */
.pfeat{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  border-radius:var(--radius-lg);overflow:hidden;background:var(--surface);
  box-shadow:var(--shadow-sm);transition:transform .4s var(--ease),box-shadow .4s}
.pfeat:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.pfeat .shot{position:relative;overflow:hidden;min-height:clamp(240px,30vw,420px);
  background:var(--surface-2)}
.pfeat .shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .7s var(--ease)}
.pfeat:hover .shot img{transform:scale(1.04)}
.pfeat .body{align-self:center;padding:clamp(26px,3.6vw,54px)}
.pfeat h2{font-family:var(--font-head);font-weight:700;font-size:clamp(1.5rem,3vw,2.3rem);
  line-height:1.12;letter-spacing:-.018em;margin-top:14px;transition:color .35s}
.pfeat .dek{display:block;color:var(--soft);font-size:clamp(1rem,1.5vw,1.1rem);margin-top:15px}
.pfeat .meta{display:flex;gap:clamp(14px,2.4vw,26px);flex-wrap:wrap;margin-top:19px;
  font-family:var(--font-head);font-weight:600;font-size:.68rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}
.pfeat .btn{margin-top:24px;pointer-events:none}
@media(max-width:860px){
  .pfeat{grid-template-columns:minmax(0,1fr)}
  .pfeat .shot{min-height:0;aspect-ratio:16/9}}

/* Card grid. Thumbnail on top, then the body. */
.pgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(14px,2vw,20px);
  margin-top:clamp(24px,3.2vw,36px)}
.pcard{display:flex;flex-direction:column;overflow:hidden;
  border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow-sm);
  transition:transform .4s var(--ease),box-shadow .4s}
.pcard:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.pcard .thumb{position:relative;aspect-ratio:16/10;overflow:hidden;background:var(--surface-2)}
.pcard .thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .6s var(--ease)}
.pcard:hover .thumb img{transform:scale(1.05)}
.pcard .body{display:flex;flex-direction:column;flex:1;padding:clamp(20px,2.4vw,26px)}
.pcard h3{font-family:var(--font-head);font-weight:700;font-size:1.08rem;line-height:1.28;
  margin-top:11px;transition:color .3s}
.pcard p{color:var(--soft);font-size:.91rem;margin-top:9px;flex:1}
/* align-self is doing real work: .body is a flex column, so a default stretch
   would blow the inline-flex link out to the full card width and drag its
   underline across with it. */
.pcard .link{align-self:flex-start;margin-top:18px;font-size:.86rem}
.pcard.is-hidden{display:none}
/* Post hero on the single article */
.post-shot{position:relative;overflow:hidden;border-radius:var(--radius-lg);
  aspect-ratio:21/9;background:var(--surface-2);margin-top:clamp(22px,3vw,32px)}
.post-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
@media(max-width:900px){.pgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:600px){.pgrid{grid-template-columns:minmax(0,1fr)}}

.loadmore{display:flex;flex-direction:column;align-items:center;gap:15px;
  margin-top:clamp(28px,3.6vw,44px)}
.loadmore .count{font-family:var(--font-head);font-weight:600;font-size:.7rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}

/* ---------- Single post ---------- */
.post-head{padding-bottom:2px}
.post-head h1{font-family:var(--font-head);font-weight:700;font-size:clamp(1.95rem,5vw,3.4rem);
  line-height:1.05;letter-spacing:-.022em;margin-top:13px;max-width:21ch}
.post-head .standfirst{color:var(--fg);font-size:clamp(1.08rem,1.9vw,1.32rem);line-height:1.45;
  margin-top:18px;max-width:54ch}
.post-meta{display:flex;gap:clamp(16px,3vw,34px);flex-wrap:wrap;margin-top:clamp(20px,2.6vw,28px);
  padding:13px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  font-family:var(--font-head);font-weight:600;font-size:.67rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}
.post-meta b{color:var(--fg);font-weight:600}
.post-body{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,250px);
  gap:clamp(28px,5vw,70px);align-items:start;margin-top:clamp(26px,3.6vw,44px)}
.post-copy{max-width:66ch}
.post-copy p{color:var(--soft);font-size:1.05rem;margin-top:17px}
.post-copy h2{font-family:var(--font-head);font-weight:700;font-size:clamp(1.2rem,2.2vw,1.55rem);
  line-height:1.24;margin-top:clamp(30px,3.8vw,42px)}
.post-copy ol{counter-reset:pt;list-style:none;padding:0;margin-top:18px}
.post-copy ol li{position:relative;padding-left:46px;margin-top:17px;color:var(--soft)}
.post-copy ol li::before{counter-increment:pt;content:counter(pt,decimal-leading-zero);
  position:absolute;left:0;top:2px;font-family:var(--font-head);font-weight:700;font-size:.76rem;
  letter-spacing:.04em;color:var(--blue)}
.post-copy ol li b{color:var(--fg);font-family:var(--font-head);font-weight:700}
/* Pull quote on a heavy top rule rather than a left border, so it does not
   duplicate the leadership quote treatment on About. */
.pull{margin:clamp(30px,4vw,44px) 0;padding:22px 0 22px clamp(20px,2.6vw,28px);
  border-left:3px solid var(--blue);
  font-family:var(--font-head);font-weight:700;font-size:clamp(1.18rem,2.2vw,1.58rem);
  line-height:1.24;letter-spacing:-.01em;color:var(--fg);max-width:24ch}
/* Sidebar. Related posts carry their own thumbnail rather than sitting as a
   list of bare titles, and the conversion block gets a glyph and a real link,
   so the column has something to look at instead of three lines of grey text.
   Titles never change colour on hover; the image and the row do the answering. */
.post-aside{position:sticky;top:96px}
.post-aside .lbl{font-family:var(--font-head);font-weight:600;font-size:.64rem;letter-spacing:.15em;
  text-transform:uppercase;color:var(--faint);padding-bottom:12px;border-bottom:1px solid var(--line)}
/* Icon and title only. A thumbnail was tried and dropped: at 78px in a 250px
   column the images read as clutter rather than as pictures. No rules between
   rows and no background on hover; the glyph is the only thing that answers. */
.post-aside .rel{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:center;
  padding:11px 0}
.post-aside .rel .ic{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;
  color:var(--blue);background:color-mix(in srgb,var(--blue) 11%,transparent);
  transition:background .35s,color .35s,transform .35s var(--spring)}
.post-aside .rel:hover .ic{background:var(--blue);color:#fff;transform:translateY(-2px)}
.post-aside .rel .ic svg{width:17px;height:17px}
.post-aside .rel b{font-family:var(--font-head);font-weight:600;font-size:.87rem;line-height:1.32;
  color:var(--fg)}
.post-aside .note{margin-top:20px;padding:clamp(18px,2vw,22px);border-radius:var(--radius);
  background:color-mix(in srgb,var(--blue) 8%,var(--surface))}
.post-aside .note h3{font-family:var(--font-head);font-weight:700;font-size:1.02rem;
  line-height:1.3}
.post-aside .note p{color:var(--soft);font-size:.89rem;margin-top:8px}
.post-aside .note .link{margin-top:15px;font-size:.86rem}
@media(max-width:900px){
  .post-body{grid-template-columns:minmax(0,1fr)}
  .post-aside{position:static}}

/* ============================================================
   Legal pages — privacy policy, terms of use
   Long-form reading, so it gets a measure and nothing else. No cards, no
   accents, no reveal staggering down a wall of text.
   ============================================================ */
/* Sticky section index on the left, one measure of text on the right. The width
   change between the two columns is structural, which is the point: the earlier
   version had a 22ch heading over a 78ch intro over a 76ch body, three widths
   down one edge with nothing explaining why.
   No .rv anywhere above .legal-toc. The reveal uses a transform, and a
   transformed ancestor becomes the containing block for position:sticky, which
   silently kills it. */
/* Full width, matching the body below it. Now that .legal-body has no cap
   either, the legal pages run one edge-to-edge measure top to bottom. */
.legal-hero .lead{max-width:none}
.legal-grid{display:grid;grid-template-columns:minmax(0,230px) minmax(0,1fr);
  gap:clamp(28px,5vw,72px);align-items:start}
.legal-toc{position:sticky;top:96px}
/* The index label and the updated line are one pair across two columns, so they
   are set by one rule. They had different sizes and different padding before,
   which put the two hairlines 5px apart and made the top of the page look like
   a mistake. margin:0 on both so each starts at the exact top of its column. */
.legal-toc .lbl,.legal .updated{margin:0;font-family:var(--font-head);font-weight:600;
  font-size:.66rem;line-height:1.5;letter-spacing:.15em;text-transform:uppercase;
  color:var(--faint);padding-bottom:14px;border-bottom:1px solid var(--line)}
.legal-toc a{display:block;padding:8px 0;font-size:.89rem;color:var(--soft);transition:color .25s}
.legal-toc a:hover{color:var(--blue)}
.legal-toc a.on{color:var(--blue);font-weight:600}
/* No measure cap: the body fills its column so the text block lands on the same
   right edge as every other section on the site. */
.legal-body{max-width:none}
/* Anchor targets clear the sticky header rather than landing under it. */
.legal-body h2{scroll-margin-top:96px}
@media(max-width:820px){
  .legal-grid{grid-template-columns:minmax(0,1fr)}
  .legal-toc{position:static;margin-bottom:8px}}
.legal h2{font-family:var(--font-head);font-weight:700;font-size:clamp(1.2rem,2.1vw,1.5rem);
  line-height:1.25;margin-top:clamp(32px,4.2vw,48px)}
.legal h3{font-family:var(--font-head);font-weight:700;font-size:1.02rem;margin-top:26px}
.legal p{color:var(--soft);margin-top:13px}
.legal p strong{color:var(--fg);font-weight:600}
.legal ul{list-style:none;margin-top:13px;padding:0}
.legal li{position:relative;padding-left:22px;color:var(--soft);margin-top:9px}
.legal li::before{content:"";position:absolute;left:2px;top:.62em;width:6px;height:6px;border-radius:50%;
  background:color-mix(in srgb,var(--blue) 55%,transparent)}
.legal li strong{color:var(--fg);font-weight:600}
.legal a{color:var(--blue);transition:color .25s}
.legal a:hover{color:var(--teal)}
/* Closing block: where to write if any of the above matters to you. */
.legal-foot{margin-top:clamp(34px,4.4vw,52px);padding:clamp(20px,2.6vw,28px);
  border-radius:var(--radius);background:color-mix(in srgb,var(--blue) 7%,var(--surface))}
.legal-foot h2{margin-top:0}
.legal-foot p:first-of-type{margin-top:10px}

.prose{max-width:760px}
.prose>*+*{margin-top:1.1em}
.prose p,.prose li{color:var(--soft);font-size:1.06rem}
.prose h2{font-size:1.75rem;margin-top:1.8em}
.prose h3{font-size:1.3rem;margin-top:1.5em}
.prose ul{padding-left:0}
.prose ul li{position:relative;padding-left:26px}
.prose ul li::before{content:"";position:absolute;left:2px;top:12px;width:8px;height:8px;border-radius:50%;background:var(--teal)}
.prose a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
.prose strong{color:var(--fg)}

.field{display:flex;flex-direction:column;gap:8px;margin-bottom:18px}
.field label{font-family:var(--font-head);font-weight:600;font-size:.86rem}
.field input,.field textarea,.field select{font:inherit;padding:13px 15px;border-radius:var(--radius-sm);border:1px solid var(--line-2);background:var(--surface);color:var(--fg);transition:border-color .25s,box-shadow .25s}
.field textarea{min-height:140px;resize:vertical}
.field input:focus,.field textarea:focus,.field select:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 4px color-mix(in srgb,var(--teal) 16%,transparent)}
.card-surface{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-lg);box-shadow:var(--shadow-sm);padding:clamp(24px,4vw,40px)}


/* ============================================================
   Work index
   Each project is a full-bleed row, edge to edge, wider than any other content
   on the site. That break out of the 1200px wrap is the emphasis: the projects
   are the page, and a card sitting inside the same measure as body copy cannot
   say that. Scene fills half the row at full height, text sits centred in the
   other half, and the sides alternate so the eye zigzags down instead of
   scanning a column. Every row sits on the same surface: the alternating scene
   side is what separates them, so no banding and no borders are needed.
   Nothing dims on hover.
   ============================================================ */
.works{display:flex;flex-direction:column}
.wproj{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:stretch;
  background:var(--surface)}
/* White theme takes a hair off pure white here. These rows run edge to edge with
   no border, so on a #FFFFFF page they had no edge at all and read as one
   undifferentiated field. */
:root[data-theme="white"] .wproj{background:#FCFCFC}
.wproj:nth-child(even) .shot{order:2}
.wproj .shot{position:relative;overflow:hidden;min-height:clamp(340px,40vw,560px);
  background:color-mix(in srgb,var(--blue) 6%,var(--surface-2))}
.wproj .shot img,.wproj .shot .scene{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;transition:transform .7s var(--ease)}
.wproj:hover .shot img,.wproj:hover .shot .scene{transform:scale(1.04)}
.wproj .meta{align-self:center;justify-self:center;width:100%;max-width:600px;
  padding:clamp(34px,5vw,72px) clamp(24px,4.5vw,68px)}
.wproj .sect{font-family:var(--font-head);font-weight:600;font-size:.7rem;letter-spacing:.15em;
  text-transform:uppercase;color:var(--teal)}
.wproj h3{font-family:var(--font-head);font-size:clamp(1.7rem,3.4vw,2.6rem);font-weight:700;
  line-height:1.16;letter-spacing:-.01em;margin-top:14px}
/* Two paragraphs per project: the need, then what was made. The second is
   full-strength so the answer outweighs the problem, same typographic move as
   the why-here sections. No labels, the order does the work. */
.wproj p{color:var(--soft);font-size:1rem;margin-top:16px;max-width:50ch}
.wproj p+p{color:var(--fg);font-size:clamp(1.02rem,1.4vw,1.12rem);margin-top:14px}
/* Services are tagged with the site's own .pill component, glyph chip and all,
   rather than a second flavour of pill invented for this page. */
@media(max-width:900px){
  .wproj{grid-template-columns:minmax(0,1fr)}
  .wproj:nth-child(even) .shot{order:0}
  .wproj .shot{min-height:0;aspect-ratio:16/10}
  .wproj .meta{max-width:none}}

/* ============================================================
   Contact
   ============================================================ */
.contact-split{display:grid;grid-template-columns:minmax(0,1.14fr) minmax(0,.86fr);
  gap:clamp(24px,3.4vw,48px);align-items:start}
@media(max-width:900px){.contact-split{grid-template-columns:1fr}}
/* minmax(0,1fr), not 1fr. A plain 1fr track has an auto minimum, so the inputs'
   intrinsic width (browsers give them a default size) pushed the columns wider
   than the card and the whole grid overflowed its right padding. */
.form-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0 clamp(14px,2vw,20px)}
.form-grid .full{grid-column:1 / -1}
.form-grid .field{min-width:0}
.form-grid .field input,.form-grid .field textarea,.form-grid .field select{width:100%;max-width:100%}
@media(max-width:560px){.form-grid{grid-template-columns:minmax(0,1fr)}}
.field label .opt{color:var(--faint);font-weight:400}
/* No rule above the footer: the form is already a boxed object, so a divider
   inside it is a second edge doing nothing. The note runs the full width of the
   form and the button sits under it, right-aligned. align-self:stretch is doing
   real work: in a column flex with align-items:flex-end the paragraph would
   otherwise shrink to its text. */
/* No margin-top: the last .field already carries 18px below it, which is the
   same rhythm as the gap between fields. Anything more stacks on top of it. */
.form-foot{display:flex;flex-direction:column;align-items:flex-end;gap:16px}
.form-foot p{color:var(--faint);font-size:.82rem;align-self:stretch}
.form-foot p a{color:var(--teal)}

/* Details rail: one blue-tinted panel holding all five rows, rather than five
   separate blocks. Beside the form it reads as a single deliberate object, and
   the tint means the column carries colour at rest instead of waiting for a
   hover nobody on a phone will ever trigger. No borders and no dividing rules
   anywhere. Glyphs are solid blue from the start, one accent for all five.
   Hover does two things and no more: the row tints further and the glyph lifts.
   Nothing dims. */
.cdetails{display:flex;flex-direction:column;margin:0;
  background:color-mix(in srgb,var(--blue) 7%,var(--surface));
  border-radius:var(--radius-lg);padding:clamp(14px,1.8vw,20px)}
.cdetail{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:clamp(12px,1.6vw,15px);align-items:start;
  padding:clamp(12px,1.5vw,14px) clamp(10px,1.3vw,13px);
  border-radius:var(--radius-sm);transition:background .35s}
.cdetail:hover{background:color-mix(in srgb,var(--blue) 12%,var(--surface))}
.cdetail .ic{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;
  color:#fff;background:var(--blue);transition:transform .38s var(--spring)}
.cdetail:hover .ic{transform:translateY(-3px)}
.cdetail .ic svg{width:19px;height:19px}
.cdetail dt{font-family:var(--font-head);font-weight:600;font-size:.66rem;letter-spacing:.15em;
  text-transform:uppercase;color:var(--faint)}
.cdetail dd{margin:7px 0 0;font-family:var(--font-head);font-weight:600;font-size:1.02rem;color:var(--fg)}
.cdetail dd a{color:inherit}
.cdetail .note{display:block;font-family:var(--font-body);font-weight:400;font-size:.88rem;
  color:var(--soft);margin-top:6px}
/* Provisional value. Dashed and unmistakable so it cannot ship by accident,
   and theme-safe because it borrows --faint and --line-2 rather than a colour
   of its own. Same intent as the "stock placeholder" caption on About. */
.todo{display:inline-block;font-family:var(--font-head);font-weight:600;font-size:.76rem;
  letter-spacing:.03em;color:var(--faint);border:1px dashed var(--line-2);
  border-radius:99px;padding:4px 12px}


/* ============================================================
   Section background — blueprint grid, masked
   One drawn treatment for the one section that has to stand out. The radial mask
   is what keeps it from reading as graph paper: the grid only exists under the
   content and dissolves before it reaches the section edges. Line colour comes
   from --fg so it inverts with the theme instead of being hardcoded.
   Rule of use: only ever one section per page carries this. The CTA already has
   its own treatment, so this is the page's second and last.
   ============================================================ */
/* --bpy is the vertical centre of the mask, 50% so the fade is even top and
   bottom. Override it inline per section only if that section's weight really
   does sit off centre.
   The fade is soft by construction: the radii are smaller than the section
   (58% of its height, so the ramp is finished before the edge rather than
   being cut off by it) and the ramp runs across three stops instead of two,
   which is what stops the grid ending on a visible ring. Solid core to 20%,
   half strength at 60%, gone by 100%. */
.bg-blueprint{--bpy:50%;position:relative;overflow:hidden}
/* background-size gives each gradient a real 74px tile, which is what makes
   background-position mean anything: a repeating gradient with no size fills the
   whole box, so it always tiles from the top-left corner and the pattern only
   lands symmetrically when the section happens to be an exact multiple of 74px.
   Sized and centred, the centre of the section sits in the middle of a cell at
   every width and height, so the grid is mirrored about the masked circle. */
.bg-blueprint::before{content:"";position:absolute;inset:0;pointer-events:none;
  background-image:
    linear-gradient(90deg,color-mix(in srgb,var(--fg) 6%,transparent) 0 1px,transparent 1px),
    linear-gradient(0deg,color-mix(in srgb,var(--fg) 6%,transparent) 0 1px,transparent 1px);
  background-size:74px 74px;background-position:center center;
  mask-image:radial-gradient(68% 58% at 50% var(--bpy),#000 0 20%,rgba(0,0,0,.5) 60%,transparent 100%);
  -webkit-mask-image:radial-gradient(68% 58% at 50% var(--bpy),#000 0 20%,rgba(0,0,0,.5) 60%,transparent 100%)}
.bg-blueprint::after{content:"";position:absolute;left:50%;top:50%;width:min(620px,86%);aspect-ratio:1;
  transform:translate(-50%,-50%);border:1px solid color-mix(in srgb,var(--teal) 26%,transparent);
  border-radius:50%;pointer-events:none}
/* Absolutely positioned pseudo-elements paint above static content, so the
   section's own children need a layer of their own or the circle crosses the type. */
.bg-blueprint>.wrap{position:relative;z-index:1}

/* Dotted field band — the CTA's background borrowed for one mid-page section.
   Same dot grid, same blue and teal corner meshes, same centre wash that keeps
   the type off the texture. Two differences on purpose: it is ruled top and
   bottom so it reads as a band rather than as the end of the page, and it does
   not carry the ghost wordmark or the drift animation. Those two stay the
   CTA's, or the CTA stops being the page's last word. */
.bg-field{--dot:8%;--mesh:8%;--mesh2:9%;--calm:55%;position:relative;overflow:hidden;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background-image:radial-gradient(color-mix(in srgb,var(--fg) var(--dot),transparent) 1px,transparent 1px);
  background-size:22px 22px}
.bg-field::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;background:
  radial-gradient(42% 62% at 0% 0%,color-mix(in srgb,var(--blue) var(--mesh),transparent),transparent 70%),
  radial-gradient(42% 62% at 100% 100%,color-mix(in srgb,var(--teal) var(--mesh2),transparent),transparent 70%),
  radial-gradient(58% 66% at 50% 44%,var(--bg),color-mix(in srgb,var(--bg) var(--calm),transparent) 70%,transparent)}
.bg-field>.wrap{position:relative;z-index:2}

/* ============================================================
   Timeline — numbered steps on a rail (About, service pages)
   The rail is drawn per step rather than once behind the list, so a step can be
   added or removed without touching a height anywhere. Static gradient, no
   animation: the rail is tall and thin, which is exactly where a moving gradient
   shows its seam.
   ============================================================ */
.tl{display:flex;flex-direction:column}
.tl-step{position:relative;display:grid;grid-template-columns:auto 1fr;gap:clamp(18px,3vw,28px);padding-bottom:clamp(26px,4vw,40px)}
.tl-step:last-child{padding-bottom:0}
.tl-step::before{content:"";position:absolute;left:23px;top:52px;bottom:0;width:2px;transform:translateX(-1px);
  background:linear-gradient(180deg,color-mix(in srgb,var(--teal) 55%,transparent),color-mix(in srgb,var(--teal) 8%,transparent))}
.tl-step:last-child::before{display:none}
.tl-num{position:relative;z-index:1;width:46px;height:46px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--font-head);font-weight:700;font-size:.92rem;color:var(--blue);
  background:color-mix(in srgb,var(--blue) 10%,var(--surface));box-shadow:inset 0 0 0 1px var(--line)}
.tl-body h3{font-size:1.24rem}
.tl-body p{color:var(--soft);margin-top:8px;max-width:62ch;font-size:1rem}
:root[data-theme="dark"] .tl-num{color:var(--teal);background:color-mix(in srgb,var(--teal) 14%,var(--surface))}

/* ============================================================
   Credibility blocks — About page, reusable on service pages
   Every block here is a surface card on a tinted ground, so all three themes
   inherit the card decisions already made: ring on white, flat teal on dark.
   ============================================================ */
/* split page hero: copy beside a tall image, with a fact card breaking the image
   edge. The overlap is the whole point, it puts two planes on the page so the
   intro stops reading as a column of text. */
.ph-split{display:grid;grid-template-columns:1.02fr .98fr;gap:clamp(26px,4vw,54px);align-items:center}
.ph-shot{position:relative}
.ph-shot .frame{position:relative;border-radius:var(--radius-lg);overflow:hidden;aspect-ratio:4/4.4;box-shadow:var(--shadow)}
.ph-shot .frame img{width:100%;height:100%;object-fit:cover;display:block}
.ph-chip{position:absolute;left:-18px;bottom:26px;padding:16px 21px;border-radius:var(--radius);
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow)}
.ph-chip b{display:block;font-family:var(--font-head);font-weight:700;font-size:1.5rem;line-height:1}
.ph-chip span{display:block;color:var(--faint);font-size:.76rem;margin-top:5px}
@media(max-width:860px){.ph-split{grid-template-columns:1fr}.ph-chip{left:14px}}

/* stat tiles: one accent per tile, plus the same glyph repeated huge and faint in
   the corner. The watermark is what keeps the tile from reading as empty space. */
.stiles{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(12px,1.6vw,16px);margin-top:clamp(30px,4vw,44px)}
.stile{position:relative;overflow:hidden;padding:clamp(20px,2.6vw,26px);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow-sm);transition:transform .4s var(--ease),box-shadow .4s}
.stile:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.stile .wm{position:absolute;right:-16px;bottom:-20px;width:100px;height:100px;color:var(--ac);opacity:.09;pointer-events:none}
.stile .chip{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;color:var(--ac);
  background:color-mix(in srgb,var(--ac) 13%,var(--surface))}
.stile .chip svg{width:19px;height:19px}
.stile b{display:block;font-family:var(--font-head);font-weight:700;font-size:clamp(1.9rem,3.4vw,2.6rem);
  line-height:1;letter-spacing:-.03em;margin-top:16px;color:var(--ac)}
.stile span{position:relative;display:block;color:var(--soft);font-size:.86rem;margin-top:9px}
@media(max-width:820px){.stiles{grid-template-columns:1fr 1fr}}

/* mission and values as one section: the mission is the claim, the values are the
   evidence, so they share a row instead of two thin sections. */
.mv{display:grid;grid-template-columns:.9fr 1.1fr;gap:clamp(26px,4vw,58px);align-items:center}
.mv .stmt{font-family:var(--font-head);font-weight:700;font-size:clamp(1.45rem,3vw,2.2rem);
  line-height:1.26;letter-spacing:-.02em;margin-top:16px}
.mv .sub{color:var(--soft);margin-top:20px;font-size:1rem;max-width:46ch}
/* One cell construction, two containers. .vgrid sits in the .mv split at two
   across; .cells runs the full wrap at three across. Hairline gaps are the grid
   background showing through a 1px gap, so the rules between cells never
   double up at the joins. */
.mv .vgrid,.cells{display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.mv .vgrid{grid-template-columns:1fr 1fr}
.cells{grid-template-columns:repeat(3,1fr);margin-top:clamp(28px,3.6vw,44px)}
.mv .v,.cells .v{background:var(--surface);padding:clamp(20px,2.6vw,30px);transition:background .35s}
/* ---------- hover, shared by both cell grids ----------
   Every response brightens the cell you are on. Nothing dims, fades, or greys:
   an earlier version dropped the unhovered cells to half opacity and that is
   exactly the cloudiness the white theme exists to avoid.
   A rule draws across the top edge, the icon or numeral fills solid and lifts,
   the heading takes the accent, and the body copy comes up from --soft to full
   strength. Same behaviour whether the chip holds an icon (.mv, About values)
   or an index (.cells, deliverables). */
.mv .v,.cells .v{position:relative}
.mv .v::before,.cells .v::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--ac);transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.mv .v:hover,.cells .v:hover{background:color-mix(in srgb,var(--ac) 7%,var(--surface))}
.mv .v:hover::before,.cells .v:hover::before{transform:scaleX(1)}
.mv .v .ic,.cells .v .num{transition:background .38s,color .38s,
  transform .38s var(--spring),opacity .5s var(--ease)}
.mv .v:hover .ic,.cells .v:hover .num{background:var(--ac);color:#fff;transform:translateY(-3px)}
.mv .v:hover h4,.cells .v:hover h4{color:var(--ac)}
.mv .v:hover p,.cells .v:hover p{color:var(--fg)}
.mv .v h4,.mv .v p,.cells .v h4,.cells .v p{transition:color .38s}

/* Reveal stagger is .cells only: the deliverables grid steps its contents in
   one cell after another. The stagger is on the contents rather than the cell,
   because .cells clips to its own radius and moving the cells would shear the
   bottom row. About's values grid keeps the reveal it already had. */
.cells .v>*{opacity:0;transform:translateY(8px);
  transition:opacity .5s var(--ease),transform .5s var(--ease),background .38s,color .38s}
.cells.in .v>*{opacity:1;transform:none}
.cells.in .v:nth-child(2)>*{transition-delay:.05s}
.cells.in .v:nth-child(3)>*{transition-delay:.1s}
.cells.in .v:nth-child(4)>*{transition-delay:.15s}
.cells.in .v:nth-child(5)>*{transition-delay:.2s}
.cells.in .v:nth-child(6)>*{transition-delay:.25s}
/* Once the grid is live the reveal delays must not apply to hover, or every
   cell answers a quarter second late. */
.cells.in:hover .v>*{transition-delay:0s}
@media(prefers-reduced-motion:reduce){
  .cells .v>*{opacity:1;transform:none;transition:background .38s,color .38s}
  .mv .v::before,.cells .v::before{transition:none}}
.mv .v .ic,.cells .v .ic{width:36px;height:36px;border-radius:10px;display:grid;place-items:center;
  color:var(--ac);background:color-mix(in srgb,var(--ac) 13%,transparent)}
.mv .v .ic svg,.cells .v .ic svg{width:18px;height:18px}
/* Numbered variant of the cell, for a list where the order is the point and an
   icon per item would be six drawings nobody asked for. Same box as .ic so the
   two variants sit identically. */
.mv .v .num,.cells .v .num{width:36px;height:36px;border-radius:10px;display:grid;place-items:center;
  font-family:var(--font-head);font-weight:700;font-size:.79rem;letter-spacing:.04em;
  color:var(--ac);background:color-mix(in srgb,var(--ac) 13%,transparent)}
/* Bigger in the full-width grid: at three across the cell has the room, and a
   36px chip read as a footnote rather than as the index it is. */
.cells .v .num{width:44px;height:44px;border-radius:13px;font-size:.9rem}
.mv .stmt em{font-style:normal;color:var(--blue)}
.mv .v h4,.cells .v h4{font-family:var(--font-head);font-size:1.04rem;font-weight:700;margin-top:14px}
.mv .v p,.cells .v p{color:var(--soft);font-size:.88rem;margin-top:6px}
@media(max-width:860px){.mv{grid-template-columns:1fr}.cells{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.cells{grid-template-columns:1fr}}

/* fact bar: four hard numbers directly under the hero */
.factbar{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;margin-top:clamp(30px,4vw,44px)}
.factbar .f{background:var(--surface);padding:clamp(17px,2.4vw,24px)}
.factbar .f b{display:block;font-family:var(--font-head);font-weight:700;font-size:clamp(1.3rem,2.4vw,1.7rem);line-height:1}
.factbar .f span{display:block;color:var(--faint);font-size:.79rem;margin-top:7px}
@media(max-width:760px){.factbar{grid-template-columns:1fr 1fr}}

/* awards / recognition */
.awards{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(12px,2vw,18px)}
.award{text-align:center;padding:clamp(22px,3vw,30px) 18px;border-radius:var(--radius);background:var(--surface);
  box-shadow:var(--shadow-sm);transition:transform .4s var(--ease),box-shadow .4s}
.award:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.award .medal{width:54px;height:54px;margin:0 auto 14px;border-radius:50%;display:grid;place-items:center;
  color:var(--teal);background:color-mix(in srgb,var(--teal) 12%,var(--surface))}
.award .medal svg{width:25px;height:25px}
/* Badge art, no plate and no size cap: the image runs the full content width of
   the card and sets its own height. */
.award .badge{display:block;width:100%;margin:0 0 16px}
.award .badge img{display:block;width:100%;height:auto}
/* The Expertise badge ships two files. Its black plate went invisible on the dark
   theme, so dark swaps to the white-mark version. Only one is ever rendered. */
.award .badge img.dk{display:none}
[data-theme="dark"] .award .badge img.lt{display:none}
[data-theme="dark"] .award .badge img.dk{display:block}
/* Category-led card: the big line is the short category, the full award title sits
   under it. Four headline words of near-equal length, and nothing abbreviated,
   which is what killed the ragged row of centered titles. */
.award b{display:block;font-family:var(--font-head);font-size:1.14rem;font-weight:700;line-height:1.3}
.award .full{display:block;color:var(--soft);font-size:.8rem;line-height:1.4;margin-top:7px}
.award .org{display:block;color:var(--faint);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;margin-top:11px}
.award span{display:block;color:var(--soft);font-size:.85rem;margin-top:6px}
.award .yr{color:var(--faint);font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;margin-top:12px}
@media(max-width:820px){.awards{grid-template-columns:1fr 1fr}}

/* mission statement */
.mission{text-align:center;max-width:920px;margin:0 auto}
.mission .stmt{font-family:var(--font-head);font-weight:700;font-size:clamp(1.7rem,4.4vw,3rem);
  line-height:1.24;letter-spacing:-.02em;text-wrap:balance;margin-top:18px}
.mission .sub{color:var(--soft);margin-top:20px;font-size:1.06rem;max-width:60ch;margin-inline:auto}

/* values */
.vals{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(12px,2vw,18px)}
.val{position:relative;overflow:hidden;padding:clamp(24px,3vw,32px);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow-sm);transition:transform .4s var(--ease),box-shadow .4s}
.val::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:linear-gradient(180deg,var(--blue),var(--teal));transform:scaleY(.3);transform-origin:top;
  transition:transform .45s var(--ease)}
.val:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.val:hover::before{transform:scaleY(1)}
.val .vn{font-family:var(--font-head);font-weight:700;font-size:1.14rem}
.val p{color:var(--soft);font-size:.95rem;margin-top:9px}
@media(max-width:860px){.vals{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.vals{grid-template-columns:1fr}}

/* leadership feature */
.leader{display:grid;grid-template-columns:.82fr 1.18fr;gap:clamp(26px,4vw,52px);align-items:center}
.leader .shot{position:relative;border-radius:var(--radius-lg);overflow:hidden;min-height:440px;box-shadow:var(--shadow)}
.leader .shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.leader .role{color:var(--teal);font-family:var(--font-head);font-weight:600;font-size:.78rem;letter-spacing:.12em;text-transform:uppercase}
.leader h3{font-size:clamp(1.6rem,3.2vw,2.3rem);margin:12px 0 16px}
.leader p{color:var(--soft);font-size:1.04rem}
.leader p+p{margin-top:14px}
.leader blockquote{margin-top:26px;padding-left:22px;border-left:3px solid var(--teal);
  font-family:var(--font-head);font-weight:600;font-size:clamp(1.05rem,1.8vw,1.24rem);line-height:1.5;color:var(--fg)}
@media(max-width:860px){.leader{grid-template-columns:1fr}.leader .shot{min-height:340px}}

/* global team: country chips */
.flags{display:flex;flex-wrap:wrap;gap:12px;margin-top:26px}
.flag{display:inline-flex;align-items:center;gap:12px;padding:11px 18px 11px 13px;border-radius:99px;
  background:var(--surface);box-shadow:var(--shadow-sm);font-family:var(--font-head);font-weight:600;font-size:.94rem}
/* Fixed 3:2 box with cover. Source flags differ in ratio (Honduras and the
   Philippines are 2:1, Mexico is 7:4), so height:auto made every chip a slightly
   different size. The box is uniform now and the crop is imperceptible. */
.flag img{width:28px;aspect-ratio:3/2;height:auto;object-fit:cover;border-radius:3px;display:block;box-shadow:0 0 0 1px var(--line)}
.flag.tbd{background:transparent;box-shadow:none;border:1px dashed var(--line-2);color:var(--faint);font-weight:500}
.flag.tbd i{width:28px;height:19px;border-radius:3px;border:1px dashed var(--line-2);display:block}

/* industries */
.inds{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,2vw,18px)}
.ind{display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:start;padding:clamp(21px,2.6vw,28px);
  border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow-sm);
  transition:transform .4s var(--ease),box-shadow .4s}
.ind:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.ind .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;color:var(--blue);
  background:color-mix(in srgb,var(--blue) 11%,var(--surface));transition:background .4s,color .4s}
.ind:hover .ic{background:linear-gradient(135deg,var(--blue),var(--teal));color:#fff}
.ind .ic svg{width:21px;height:21px}
.ind h3{font-size:1.06rem;line-height:1.28}
.ind p{color:var(--soft);font-size:.9rem;margin-top:7px}
:root[data-theme="dark"] .ind .ic{color:var(--teal);background:color-mix(in srgb,var(--teal) 16%,var(--surface))}
@media(max-width:900px){.inds{grid-template-columns:1fr 1fr}}
@media(max-width:560px){.inds{grid-template-columns:1fr}}

/* FAQ — plain Q&A blocks, deliberately crawlable rather than an accordion */
.faq{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,2vw,18px)}
.faq .q{padding:clamp(22px,2.6vw,30px);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow-sm)}
.faq .q h3{font-size:1.06rem;line-height:1.3;margin-bottom:9px}
.faq .q p{color:var(--soft);font-size:.96rem}
@media(max-width:760px){.faq{grid-template-columns:1fr}}

/* ============================================================
   Service pages — additions only, everything else is reused
   Three small parts: a two-up variant of the capability grid, sub-offering pills
   that sit under a split, and a deliverables checklist. Nothing here introduces a
   new surface, hover, or accent; they all inherit the card decisions already made.
   ============================================================ */
/* Branding has three capabilities and lands on the 3-up grid cleanly. Web and
   print have four, which left a hole in the second row, so they take a 2-up. */
.inds.two{grid-template-columns:repeat(2,1fr)}
@media(max-width:900px){.inds.two{grid-template-columns:1fr}}

/* Sub-offering pills (A1, approved). The country pills on About work because each
   one carries a real object and a flag brings its own colour with it. A text label
   cannot do that, so every pill gets its own glyph in a tinted chip and the hue
   rotates through the brand three. The colour belongs to the item, not to the row. */
.pills{display:flex;flex-wrap:wrap;gap:10px;margin-top:26px}
.pill{display:inline-flex;align-items:center;gap:11px;padding:8px 17px 8px 8px;border-radius:99px;
  background:var(--surface);box-shadow:var(--shadow-sm);font-family:var(--font-head);
  font-weight:600;font-size:.86rem;color:var(--fg);transition:transform .35s var(--ease),box-shadow .35s}
.pill:hover{transform:translateY(-2px);box-shadow:var(--shadow)}
.pill .ch{width:30px;height:30px;border-radius:50%;display:grid;place-items:center;flex:none;
  color:var(--ac);background:color-mix(in srgb,var(--ac) 16%,var(--surface))}
.pill .ch svg{width:16px;height:16px}

/* Service router (C3, approved) — the block under the services hub hero.
   It replaced a four-number stat bar whose every number already appeared on the
   About page. Someone landing here usually knows which of the three they need, so
   the block routes rather than boasts. Teal ring numeral, jumps to the section. */
.srouter{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-top:clamp(30px,4vw,44px)}
.srouter a{display:grid;grid-template-columns:auto 1fr auto;gap:14px;align-items:center;padding:21px 22px;
  border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow-sm);
  transition:transform .35s var(--ease),box-shadow .35s,background .35s}
.srouter a:hover{transform:translateY(-3px);box-shadow:var(--shadow)}
.srouter .rg{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;
  border:2px solid color-mix(in srgb,var(--teal) 55%,transparent);color:var(--teal);
  font-family:var(--font-head);font-weight:700;font-size:.76rem;transition:background .35s,color .35s}
.srouter a:hover .rg{background:var(--teal);color:var(--surface)}
.srouter b{font-family:var(--font-head);font-weight:600;font-size:1rem}
.srouter .go2{color:var(--faint);display:grid;place-items:center}
.srouter .go2 svg{width:17px;height:17px}
@media(max-width:820px){.srouter{grid-template-columns:1fr}}
/* the router jumps to a section, and the header is sticky, so the target needs to
   stop clear of it rather than under it */
.sec[id]{scroll-margin-top:96px}

/* Process feed (B1, approved). Steps as a feed on a node rail: something happened,
   then the next thing did. The card reserves room on the right for the step number
   set huge and faint, which is the same watermark idea as the About stat tiles and
   is what stops the card reading as empty on the right.

   On the accent: --ac is stepped evenly from blue to teal across the five steps in
   the markup, so the rail warms as the project progresses. It is a ramp, not three
   arbitrary colours. Anything less than an even ramp reads as a mistake. */
/* The timeline runs the full wrap on every page that carries it. No max-width
   here: .fcard sets its own measure and the rail wants the room. */
.fit{position:relative;display:grid;grid-template-columns:auto 1fr;gap:clamp(16px,2.4vw,22px)}
.fit::before{content:"";position:absolute;left:20px;top:62px;bottom:0;width:2px;
  background:color-mix(in srgb,var(--ac) 30%,transparent)}
.fit:last-child::before{display:none}
/* The node drops by the card's top padding plus half a line, so its centre lands on
   the centre of the step heading rather than on the top edge of the card. */
.fnode{position:relative;z-index:1;margin-top:10px;width:42px;height:42px;border-radius:50%;
  display:grid;place-items:center;color:var(--ac);
  background:color-mix(in srgb,var(--ac) 14%,var(--surface));box-shadow:0 0 0 4px var(--bg)}
.fnode svg{width:19px;height:19px}
.fcard{position:relative;overflow:hidden;background:var(--surface);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);padding:20px 22px 22px;margin-bottom:14px;
  transition:transform .4s var(--ease),box-shadow .4s}
.fit:last-child .fcard{margin-bottom:0}
.fcard:hover{transform:translateY(-3px);box-shadow:var(--shadow)}
.fcard .mt{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:7px}
.fcard .mt h3{font-family:var(--font-head);font-weight:700;font-size:1.08rem}
.fcard .mt em{font-style:normal;font-family:var(--font-head);font-weight:600;font-size:.7rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ac)}
.fcard p{position:relative;color:var(--soft);font-size:.93rem}
/* Timeline life: a signal runs the rail on a loop and each node lights as it
   passes, so the section keeps moving instead of playing once and dying.
   The rail highlight is a single repeating tile translated by exactly its own
   height, which is what makes the loop seamless, and the tile starts and ends
   transparent so there is no seam where it wraps.
   The five steps are phase-offset down the list. That is the whole trick: five
   independent loops read as one signal travelling the length of the timeline.
   Pause is gated on .feed, never on the individual step. Pausing steps one at a
   time as they scroll in resumes each from a different point in the cycle and
   the 1-2-3-4-5 order falls apart, which is exactly what it looked like. */
.fit::after{content:"";position:absolute;left:20px;top:62px;bottom:0;width:2px;pointer-events:none;
  background-image:linear-gradient(180deg,transparent 0 22%,
    color-mix(in srgb,var(--ac) 55%,transparent) 50%,transparent 78% 100%);
  background-size:100% 220px;background-repeat:repeat-y;
  animation:railrun 4.8s linear infinite;animation-play-state:paused}
.fit:last-child::after{display:none}
@keyframes railrun{from{background-position:0 0}to{background-position:0 220px}}
.fnode{animation:nodelight 4.8s ease-in-out infinite;animation-play-state:paused}
/* deliberately small: a tint lift and a soft ring, no fill, no icon colour
   flip, 3% of scale. The icon keeps its colour the whole way through. This
   sits next to five paragraphs people are meant to read, so it has to register
   in peripheral vision without ever pulling the eye off the line. */
@keyframes nodelight{
  0%,52%,100%{background:color-mix(in srgb,var(--ac) 14%,var(--surface));transform:scale(1);
    box-shadow:0 0 0 4px var(--bg),0 0 0 4px color-mix(in srgb,var(--ac) 0%,transparent)}
  16%,28%{background:color-mix(in srgb,var(--ac) 34%,var(--surface));transform:scale(1.03);
    box-shadow:0 0 0 4px var(--bg),0 0 0 7px color-mix(in srgb,var(--ac) 12%,transparent)}}
/* each step lags the one above it by .6s of the 4.8s cycle: 2.4s to run all
   five, then 2.4s of stillness before it starts again. The rest beat is doing
   as much work as the pulse. Negative delays so every step is already
   mid-cycle rather than waiting its turn on first paint. */
.fit:nth-child(2)::after,.fit:nth-child(2) .fnode{animation-delay:-4.2s}
.fit:nth-child(3)::after,.fit:nth-child(3) .fnode{animation-delay:-3.6s}
.fit:nth-child(4)::after,.fit:nth-child(4) .fnode{animation-delay:-3s}
.fit:nth-child(5)::after,.fit:nth-child(5) .fnode{animation-delay:-2.4s}
.feed.lit .fit::after,.feed.lit .fnode{animation-play-state:running}
@media(prefers-reduced-motion:reduce){
  .fit::after{display:none}
  .fnode{animation:none}}
/* No corner mark on these cards. A large numeral was tried and dropped (digits
   vary in width, so no two cards aligned), then the step glyph, also dropped. The
   step number lives in the "Step 01" label beside the heading and that is enough. */

/* Three-up argument cards (C1, approved). Headings are written to one grammar and
   one length so the three resolve as a set, and the icons are a matched triptych:
   in each one, three things become one. */
.trio{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,2vw,18px)}
.tcard{padding:clamp(24px,3vw,30px);border-radius:var(--radius);background:var(--surface);
  box-shadow:var(--shadow-sm);transition:transform .4s var(--ease),box-shadow .4s}
.tcard:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.tcard .ic{width:52px;height:52px;border-radius:15px;display:grid;place-items:center;color:var(--ac);
  background:color-mix(in srgb,var(--ac) 13%,var(--surface));margin-bottom:20px;
  transition:background .4s,color .4s}
.tcard:hover .ic{background:linear-gradient(135deg,var(--blue),var(--teal));color:#fff}
.tcard .ic svg{width:26px;height:26px}
.tcard h3{font-family:var(--font-head);font-size:1.12rem;font-weight:700;line-height:1.25}
.tcard p{color:var(--soft);font-size:.91rem;margin-top:9px}
/* Two-up variant, for the service that covers four capabilities rather than
   three. Same card, different column count. */
.trio.two{grid-template-columns:repeat(2,1fr)}
@media(max-width:880px){.trio,.trio.two{grid-template-columns:1fr}}

/* Drawn scenes — our own artwork instead of stock photography.
   Every fill and stroke is a class rather than a hardcoded color, so one scene
   works in light, white, and dark without a second copy. They fill a .media-frame
   or a .ph-shot .frame exactly the way an <img> would. */
.scene{position:absolute;inset:0;width:100%;height:100%;display:block}
.scene .pane{fill:var(--surface);stroke:color-mix(in srgb,var(--fg) 15%,transparent);stroke-width:2}
.scene .pane-2{fill:color-mix(in srgb,var(--fg) 4%,var(--surface));stroke:color-mix(in srgb,var(--fg) 13%,transparent);stroke-width:2}
.scene .ln{fill:none;stroke:color-mix(in srgb,var(--fg) 15%,transparent);stroke-width:3;stroke-linecap:round}
.scene .ln-soft{fill:none;stroke:color-mix(in srgb,var(--fg) 9%,transparent);stroke-width:2;stroke-linecap:round}
.scene .dash{fill:none;stroke:color-mix(in srgb,var(--teal) 55%,transparent);stroke-width:2;stroke-dasharray:9 8}
.scene .blue{fill:var(--blue)}
.scene .teal{fill:var(--teal)}
.scene .soft{fill:color-mix(in srgb,var(--fg) 8%,transparent)}
.scene .reg{fill:none;stroke:color-mix(in srgb,var(--fg) 22%,transparent);stroke-width:2}

/* the argument block: a claim at full width with the reasoning in two columns
   under it. Exists so a service page can make its case without another
   image-beside-text split, which is the pattern this site already leans on most. */
.argue{max-width:880px}
.argue .big{font-family:var(--font-head);font-weight:600;font-size:clamp(1.5rem,3.2vw,2.35rem);
  line-height:1.24;letter-spacing:-.01em;color:var(--fg);margin-top:16px}
.argue .big em{font-style:normal;color:var(--blue)}
.argue-cols{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,4vw,54px);margin-top:clamp(26px,3.4vw,40px)}
.argue-cols p{color:var(--soft);font-size:1.02rem}
.argue-cols p+p{margin-top:18px}
.argue-foot{margin-top:clamp(24px,3vw,34px)}
@media(max-width:760px){.argue-cols{grid-template-columns:1fr}}


/* ============================================================
   Why-here argument (service detail pages)
   ============================================================ */
/* Media left, argument right. The argument is prose and nothing else: no
   panels, no rules, no labels, no chips. Every framed variant tried here
   competed with the scene beside it and turned one thought into two boxes.
   Emphasis is carried by type alone, which is the only device in this section
   that does not add a container. */
.why-split{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(24px,4vw,58px);align-items:stretch}
.why-split .media-frame{height:100%}
@media(max-width:900px){.why-split{grid-template-columns:1fr}}
.why-body{margin-top:clamp(20px,2.6vw,28px)}
.why-body p{color:var(--soft);font-size:1rem}
.why-body p+p{margin-top:clamp(15px,1.8vw,19px)}
/* The turn in the argument. Larger, full-strength, looser leading. That step in
   scale is the whole emphasis, so it has to be a clear step and not a nudge. */
.why-body p.lead-p{color:var(--fg);font-size:clamp(1.08rem,1.7vw,1.24rem);line-height:1.56}
.why-foot{margin-top:clamp(20px,2.6vw,30px)}


/* ============================================================
   Motion system — keep the page alive top to bottom
   ============================================================ */
/* slim scroll-progress bar */
.scroll-progress{position:fixed;top:0;left:0;height:3px;width:0;z-index:100;background:var(--grad);pointer-events:none}
/* reveal direction variants (base .rv defined in Reveal section below) */
.rv.from-left{transform:translateX(-42px)}
.rv.from-right{transform:translateX(42px)}
.rv.zoom{transform:scale(.94)}
/* heading mask reveal */
.mask{display:inline-block;overflow:hidden;padding-bottom:.06em;vertical-align:top}
/* Headings stay block. inline-block is what lets the clip box hug the text, but
   it also lets a short heading flow up alongside the .kick span that precedes
   it. That only stayed hidden while every heading was wide enough to force its
   own line. Block keeps the row to itself; overflow:hidden still clips the
   reveal because the inner span is what moves. */
h1.mask,h2.mask{display:block}
.mask>span{display:inline-block;transform:translateY(116%);transition:transform 1s var(--ease)}
.mask.in>span{transform:none}
/* ambient: slow gradient shimmer on gradient text — seamless loop palette */
@keyframes gradmove{to{background-position:200% center}}
.stat b,.cta-plain .mark,.grad-txt{background-image:var(--grad-loop);background-size:200% auto;animation:gradmove 7s linear infinite}
/* ambient: gentle float */
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
.floaty{animation:floaty 5.5s ease-in-out infinite}
/* ambient: living gradient surfaces — placeholders never sit still */
@keyframes livingGrad{0%{background-position:28% 30%}50%{background-position:72% 70%}100%{background-position:28% 30%}}
.t1,.t2,.t3,.t4,.t5,.media-frame{background-size:200% 200%;animation:livingGrad 18s ease-in-out infinite}
.t2{animation-delay:-2.5s}.t3{animation-delay:-5s}.t4{animation-delay:-7.5s}.t5{animation-delay:-10s}
.media-frame{animation-duration:16s}
/* ambient: service icons breathe (staggered) */
/* Service icons: a soft blue halo blooms in and out, no size change. Kept gentle
   (blue 16%, wide blur, no hard spread) since it runs ambiently on every page. */
/* Glow hue is a variable, not a hardcoded color, so a theme or a state can point
   it somewhere else without a second copy of the keyframes. Default blue.
   The pulse only runs while the card is hovered: idling on every chip made the
   section restless with nobody there. Dark opts out of it in both states, via the
   `:root[data-theme="dark"] .svc .ic` animation:none rule, which outranks this. */
@keyframes iconGlow{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--glow) 0%,transparent)}50%{box-shadow:0 0 18px 0 color-mix(in srgb,var(--glow) 16%,transparent)}}
.svc .ic{--glow:var(--blue)}
.svc:hover .ic{animation:iconGlow 3.2s ease-in-out infinite}
.services .svc:nth-child(2) .ic{animation-delay:-1.05s}
.services .svc:nth-child(3) .ic{animation-delay:-2.1s}
/* ambient: media play button floats + emits a pulse ring */
@keyframes playring{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--teal) 40%,transparent)}70%,100%{box-shadow:0 0 0 18px color-mix(in srgb,var(--teal) 0%,transparent)}}
.media-frame .play{animation:floaty 5.5s ease-in-out infinite, playring 2.6s ease-out infinite}
/* ambient: CTA dot grid drifts, ghosted wordmark breathes */
@keyframes gridDrift{to{background-position:22px 22px}}
.cta-plain{animation:gridDrift 7s linear infinite}
@keyframes breathe{0%,100%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.035)}}
.cta-plain .mark{animation:gradmove 7s linear infinite, breathe 9s ease-in-out infinite}
/* ambient: the "LEAP" column carries a living gradient bar */
.vs .us{position:relative;overflow:hidden}
.vs .us::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background-image:var(--grad-loop-d);background-size:200% auto;animation:gradmove 4.5s linear infinite}

@media(prefers-reduced-motion:reduce){.kd::before,.kd::after,.pulse b::before,.pulse b::after,.stat b,.cta-plain,.cta-plain .mark,.grad-txt,.floaty,.t1,.t2,.t3,.t4,.t5,.media-frame,.svc .ic,.media-frame .play,.vs .us::before{animation:none}.mask>span{transform:none}.cta-plain .mark{transform:translate(-50%,-50%)}}

/* ============================================================
   Reveal
   ============================================================ */
.rv{opacity:0;transform:translateY(26px);transition:opacity .8s var(--ease),transform .8s var(--ease)}
.rv.in{opacity:1;transform:none}
.rv.d1{transition-delay:.09s}.rv.d2{transition-delay:.18s}.rv.d3{transition-delay:.27s}
@media(prefers-reduced-motion:reduce){.rv{opacity:1;transform:none}}

/* Once the reveal has finished, js/main.js stamps .rvdone and the element goes
   back to its own interaction timing. Without this the reveal's .8s duration and
   its d1/d2/d3 stagger delay stay attached to the element forever, so hovering
   the third card waited .27s and then lifted over .8s. Cannot just shorten .rv,
   the same declarations are what animate the reveal itself. */
.rv.rvdone{transition:transform .4s var(--ease),box-shadow .4s,background .4s;transition-delay:0s}

/* Hover lift, restored on revealed cards.
   `.rv.in{transform:none}` and `.award:hover{transform:translateY(-4px)}` are both
   two classes' worth of specificity, and .rv.in is later in the file, so it won
   every tie and silently killed the lift on every card that also carries .rv.
   That is why hovering an award, a stat tile, a value, an industry or a work card
   moved the shadow but not the card. One rule with enough reach fixes all of them
   at once, rather than bumping each hover selector on its own. */
.rv.in:is(.hover-ring,.hover-fill,.hover-bar,.hover-tint,.award,.stile,.val,.ind,.tcard):hover{transform:translateY(-4px)}
