/* =========================================================
   Lannex — styles
   Light theme. Type and spacing both run off a single scale;
   change the tokens below and the page re-proportions with them.
   ========================================================= */

:root{
  --bg:#F7F8FC;
  --bg-2:#F1F3F9;
  --panel:#FFFFFF;

  /* Card surfaces are translucent so the section washes below show through them —
     that, rather than heavy blur, is what makes the glass read. */
  --surface:rgba(255,255,255,.62);
  --surface-2:#FFFFFF;
  --line:rgba(16,19,43,.07);
  --line-2:rgba(16,19,43,.13);
  /* A light top edge is what sells a glass panel. Used for card borders only —
     --line is still the right colour for section rules and dividers. */
  --glass-line:rgba(255,255,255,.75);

  --text:#0D1020;
  --muted:#59607A;

  --violet:#5B45E0;
  --violet-lt:#7A63F5;
  --blue:#3E6DF0;
  --grad:linear-gradient(120deg,var(--violet),var(--blue));

  --shadow-sm:0 1px 2px rgba(13,16,32,.04),0 2px 8px -4px rgba(13,16,32,.06);
  --shadow-md:0 1px 3px rgba(13,16,32,.04),0 10px 28px -14px rgba(13,16,32,.14);
  --shadow-lg:0 2px 6px rgba(13,16,32,.04),0 26px 56px -26px rgba(13,16,32,.18);

  --radius:16px;
  --radius-lg:22px;
  --wrap:1280px;
  --ease:cubic-bezier(.22,1,.36,1);

  /* Rhythm. Tightened: section padding was 154px top AND bottom at desktop, so every
     section boundary opened a 308px gap and the page ran to nearly 10,000px. */
  --section-y:clamp(3.75rem,7vw,6rem);
  --head-gap:clamp(2rem,3.6vw,3rem);
  --gap:clamp(1.1rem,1.7vw,1.6rem);
  --card-p:clamp(1.5rem,2vw,2rem);
}

*,*::before,*::after{box-sizing:border-box}

html{scroll-behavior:smooth;background:var(--bg)}

/* Inter, self-hosted.
   Previously this came from fonts.googleapis.com, which meant a render-blocking
   stylesheet plus DNS + TLS to a second origin (fonts.gstatic.com) before any text
   could paint in the real face. Same file, same latin subset Google was serving —
   now same-origin, preloaded, and cached for a year. One variable file covers every
   weight from 400 to 800, so the five separate weights are a single download. */
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:400 800;
  font-display:swap;
  src:url('fonts/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                U+2212, U+2215, U+FEFF, U+FFFD;
}

body{
  margin:0;
  color:var(--text);
  font-family:'Inter','Segoe UI Variable Display','Segoe UI',system-ui,-apple-system,sans-serif;
  font-size:18px;
  line-height:1.7;
  -webkit-font-smoothing:antialiased;
  /* text-rendering:optimizeLegibility was here. It forces kerning and ligature work on
     every text run and measurably slows first layout on long pages, for a difference
     you cannot see in Inter. */
  overflow-x:hidden;
  /* A single tint that fades out below the hero — a fixed wash haunts every section. */
  background:
    linear-gradient(180deg,rgba(91,69,224,.07),rgba(91,69,224,0) 760px),
    var(--bg);
  background-repeat:no-repeat;
}

img,svg{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4{margin:0;line-height:1.1;letter-spacing:-.03em;font-weight:700}
p{margin:0}
ul,ol{margin:0;padding:0;list-style:none}

:focus-visible{outline:2px solid var(--violet);outline-offset:3px;border-radius:6px}

.wrap{width:min(var(--wrap),90vw);margin-inline:auto}
/* The nav is fixed, so anchor targets need clearance or their heading lands underneath it. */
section[id]{scroll-margin-top:5.5rem}

/* ---------- Ambient ---------- */
/* A fixed, full-viewport layer. mix-blend-mode:multiply used to sit here, which forced
   the compositor to blend the whole screen every frame — the most expensive thing on the
   page during a scroll, for a texture at 2.8% opacity nobody can consciously see. The
   texture stays; the per-frame blend does not. */
.grain{
  position:fixed;inset:0;z-index:100;pointer-events:none;opacity:.028;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
/* ---------- Buttons ---------- */
.btn{
  --pad:1.05rem 1.9rem;
  display:inline-flex;align-items:center;gap:.6rem;
  padding:var(--pad);border-radius:999px;border:1px solid transparent;
  font-weight:600;font-size:1rem;line-height:1.2;letter-spacing:-.01em;cursor:pointer;
  transition:transform .35s var(--ease),background .3s,border-color .3s,box-shadow .35s,color .3s;
  ;
}
.btn span{transition:transform .35s var(--ease)}
.btn:hover span{transform:translateX(4px)}
.btn-primary{background:var(--grad);color:#fff;box-shadow:0 6px 18px -8px rgba(91,69,224,.5)}
.btn-primary:hover{box-shadow:0 12px 28px -10px rgba(91,69,224,.45);transform:translateY(-1px)}
.btn-ghost{border-color:var(--line-2);color:var(--text);background:rgba(255,255,255,.7)}
.btn-ghost:hover{border-color:var(--violet);color:var(--violet);background:#fff;box-shadow:var(--shadow-sm)}
.btn-sm{--pad:.7rem 1.3rem;font-size:.95rem}
.btn-block{width:100%;justify-content:center}

.pill{
  display:inline-block;padding:.45rem 1rem;border-radius:999px;
  border:1px solid rgba(91,69,224,.16);font-size:.75rem;font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;color:var(--violet);
  background:rgba(91,69,224,.06);line-height:1.4;
}

/* ---------- Nav ---------- */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:110;
  display:flex;align-items:center;justify-content:space-between;
  gap:2rem;padding:.85rem clamp(1.25rem,5vw,3rem);
  /* The one place a real blur is always worth it: a single fixed element, and the
     content sliding under it is what makes the effect visible. */
  background:rgba(247,248,252,.72);
  -webkit-backdrop-filter:blur(18px) saturate(180%);
          backdrop-filter:blur(18px) saturate(180%);
  border-bottom:1px solid var(--line);
}
/* .nav.scrolled lived here. Nothing has added that class since the scroll listener was
   removed, and its opaque background would have cancelled the nav's blur if it ever
   came back. Deleted rather than left as a trap. */
.brand{display:inline-flex;align-items:center;color:var(--text);flex:none}
/* Wordmark is set in the heading typeface, not drawn. */
.wordmark{font-size:1.65rem;font-weight:700;letter-spacing:-.02em;line-height:1;transition:color .35s}
.wordmark-e{color:var(--blue)}
.brand:hover .wordmark{color:var(--violet)}
.brand-lg .wordmark{font-size:2.05rem}
.nav-links{display:flex;align-items:center;gap:2.4rem}
.nav-links a:not(.btn):not(.nav-ig){font-size:1rem;color:var(--muted);position:relative;transition:color .3s;white-space:nowrap}
.nav-links a:not(.btn):not(.nav-ig)::after{
  content:"";position:absolute;left:0;bottom:-7px;height:2px;width:0;background:var(--violet);border-radius:2px;transition:width .35s var(--ease);
}
.nav-links a:not(.btn):not(.nav-ig):hover{color:var(--text)}
.nav-links a:not(.btn):not(.nav-ig):hover::after{width:100%}

/* CTA + Instagram sit closer together than the nav's own gap. */
.nav-actions{display:inline-flex;align-items:center;gap:.7rem}
.nav-ig{
  display:inline-grid;place-items:center;width:38px;height:38px;flex:none;
  border-radius:50%;border:1px solid transparent;color:var(--muted);
  transition:color .3s,background .3s,border-color .3s;
}
.nav-ig svg{width:19px;height:19px}
.nav-ig:hover{color:var(--violet);background:rgba(91,69,224,.07);border-color:rgba(91,69,224,.16)}

/* ---------- Theme toggle ---------- */
.theme-toggle{
  display:inline-grid;place-items:center;width:38px;height:38px;flex:none;
  border-radius:50%;border:1px solid transparent;background:none;cursor:pointer;
  color:var(--muted);padding:0;
  transition:color .3s,background .3s,border-color .3s;
}
.theme-toggle:hover{color:var(--violet);background:rgba(91,69,224,.07);border-color:rgba(91,69,224,.16)}
:root[data-theme="dark"] .theme-toggle:hover{background:rgba(146,128,255,.12);border-color:rgba(146,128,255,.28)}
.theme-toggle svg{width:19px;height:19px}
/* One icon shows at a time: sun while dark (tap for light), moon while light. */
.theme-toggle .icon-sun{display:none}
:root[data-theme="dark"] .theme-toggle .icon-sun{display:block}
:root[data-theme="dark"] .theme-toggle .icon-moon{display:none}


.menu-ig{display:inline-flex;align-items:center;gap:.6rem}
.menu-ig svg{width:.85em;height:.85em}

.burger{display:none;flex-direction:column;gap:6px;background:none;border:0;padding:12px;cursor:pointer}
.burger span{width:26px;height:2px;background:var(--text);border-radius:2px;transition:transform .35s var(--ease),opacity .25s}
.burger[aria-expanded="true"] span:first-child{transform:translateY(4px) rotate(45deg)}
.burger[aria-expanded="true"] span:last-child{transform:translateY(-4px) rotate(-45deg)}

.mobile-menu{
  position:fixed;inset:0;z-index:105;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:1.8rem;
  background:#F6F7FB;
  font-size:clamp(1.8rem,7vw,2.4rem);font-weight:700;letter-spacing:-.03em;
}
.mobile-menu[hidden]{display:none}
.mobile-menu a{opacity:0;transform:translateY(14px);animation:menuIn .5s var(--ease) forwards}
.mobile-menu a:nth-child(2){animation-delay:.06s}
.mobile-menu a:nth-child(3){animation-delay:.12s}
.mobile-menu a:nth-child(4){animation-delay:.18s}
.mobile-menu a:nth-child(5){animation-delay:.24s}
.mobile-menu a:nth-child(6){animation-delay:.3s}
@keyframes menuIn{to{opacity:1;transform:none}}

/* ---------- Reveal ----------
   Deliberately a no-op. This used to fade elements in on scroll via IntersectionObserver,
   with .reveal starting at opacity:0. When the observer failed to fire — which it did,
   repeatedly, on mobile — whole sections stayed permanently invisible. No entrance
   animation is worth a blank page, so content is now always visible. The .reveal classes
   are left in the markup harmlessly; the page's motion comes from the pinned services
   track, the parallax orbs and the marquee instead. */

/* ---------- Section furniture ---------- */
.section-head{max-width:70ch;margin-bottom:var(--head-gap)}
.section-head .pill{margin-bottom:1.35rem}
.section-title{font-size:clamp(1.85rem,2.9vw,2.5rem);line-height:1.14;letter-spacing:-.03em}
.section-sub{margin-top:1.1rem;color:var(--muted);font-size:clamp(1rem,1.1vw,1.08rem);line-height:1.65;max-width:58ch}

/* Large blurred orbs used to sit here. They were the most expensive thing on the page:
   44vw boxes with blur(120px), moved by scroll, forcing a full repaint every frame. The
   body's background gradient gives the same colour wash for free. */

/* ---------- Hero ---------- */
.hero{
  position:relative;min-height:100svh;
  display:grid;grid-template-columns:1.05fr .95fr;align-items:center;
  gap:clamp(3rem,6vw,5rem);
  padding:clamp(7.5rem,14vh,11rem) clamp(1.25rem,5vw,3rem) clamp(5rem,9vw,7.5rem);
  overflow:hidden;
}
/* 44ch was too tight for the headline: ch is relative to the ROOT font size, so this
   measure stayed ~500px while .hero-title scales to 61.6px on wide screens. The heading
   outgrew its column and "We build" ended up alone on a line. The grid column has the
   room — it was the measure holding it back. .hero-sub keeps the old 44ch explicitly so
   the body copy's line length is unchanged. */
.hero-inner{position:relative;z-index:2;max-width:min(52ch,100%)}
.hero-title{font-size:clamp(2.45rem,4.2vw,3.85rem);line-height:1.12;letter-spacing:-.035em}
.hero-title em{
  font-style:normal;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}

/* Rotating hero word.
   --rot-lh must equal .hero-title's line-height: the mask is one line tall and each
   word occupies exactly one line, so any mismatch clips words. Both read the same
   variable rather than repeating the number, which is what drifted last time. */
.rot{
  --rot-lh:1.12;
  display:inline-block;vertical-align:top;overflow:hidden;
  height:calc(1em * var(--rot-lh));
}
.rot-in{
  display:flex;flex-direction:column;
  animation:rot 13s cubic-bezier(.76,0,.24,1) infinite;
  will-change:transform;
}
.rot em{
  height:calc(1em * var(--rot-lh));line-height:var(--rot-lh);
  white-space:nowrap;
}
/* Five rows, so each step is 20%. The fifth repeats the first: the jump from
   -80% back to 0 lands on the same word and is invisible. */
@keyframes rot{
  0%,17%   {transform:translateY(0)}
  20%,37%  {transform:translateY(-20%)}
  40%,57%  {transform:translateY(-40%)}
  60%,77%  {transform:translateY(-60%)}
  80%,100% {transform:translateY(-80%)}
}
.hero-sub{margin-top:1.5rem;color:var(--muted);font-size:clamp(1.05rem,1.25vw,1.18rem);line-height:1.7;max-width:44ch}
.hero-cta{display:flex;flex-wrap:wrap;gap:1rem;margin-top:2.25rem}

/* ---------- Hero: laptop showing a dashboard screenshot ----------
   Static. A tiny 3D rotation and a glare gradient are what stop this reading as a flat
   vector drawing, and both are one-off composites with no per-frame cost. */
.hero-stage{position:relative;z-index:2;width:100%;display:flex;align-items:center;justify-content:center}

.laptop{
  width:100%;max-width:620px;
  transform:perspective(1600px) rotateY(-3deg) rotateX(2.2deg);
  filter:drop-shadow(0 34px 46px rgba(13,16,32,.20));
}
.laptop-lid{
  position:relative;border-radius:14px 14px 4px 4px;
  padding:11px 11px 15px;
  background:linear-gradient(165deg,#2A2D38,#171922 60%,#12141B);
}
.laptop-cam{
  position:absolute;top:5px;left:50%;transform:translateX(-50%);
  width:4px;height:4px;border-radius:50%;background:#3D414F;
}
.laptop-screen{
  position:relative;overflow:hidden;border-radius:5px;
  aspect-ratio:16/10;background:#F5F6FA;
  box-shadow:0 0 0 1px rgba(0,0,0,.5) inset;
}
/* Diagonal sheen across the panel — the main "this is a photo" cue. */
.screen-glare{
  position:absolute;inset:0;pointer-events:none;border-radius:5px;
  background:
    linear-gradient(108deg,rgba(255,255,255,.50) 0%,rgba(255,255,255,.16) 22%,rgba(255,255,255,0) 44%),
    linear-gradient(200deg,rgba(255,255,255,.22) 0%,rgba(255,255,255,0) 30%);
}
.laptop-base{
  position:relative;height:11px;margin:0 auto;width:107%;left:-3.5%;
  border-radius:0 0 13px 13px;
  background:linear-gradient(180deg,#C9CDD8 0%,#AEB3C1 55%,#8E93A2 100%);
}
.laptop-notch{
  position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:86px;height:5px;border-radius:0 0 7px 7px;background:rgba(13,16,32,.22);
}

/* --- browser chrome --- */
.shot-ui{position:absolute;inset:0;display:flex;flex-direction:column;font-size:10px;background:#fff}
.shot-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top left}
.br{flex:none;background:#E6E8EE;border-bottom:1px solid #D2D5DE}
.br-tabs{display:flex;align-items:flex-end;gap:6px;padding:5px 7px 0}
.br-dots{display:flex;gap:4px;padding:0 4px 5px 0}
.br-dots i{width:6px;height:6px;border-radius:50%;background:#C6C9D2}
.br-dots i:first-child{background:#FF5F57}
.br-dots i:nth-child(2){background:#FEBC2E}
.br-dots i:nth-child(3){background:#28C840}
.br-tab{
  display:inline-flex;align-items:center;gap:5px;max-width:60%;
  padding:4px 8px;border-radius:6px 6px 0 0;background:#fff;color:#3A3F52;
  font-size:8.5px;white-space:nowrap;overflow:hidden;
}
.br-fav{width:7px;height:7px;border-radius:2px;background:var(--grad);flex:none}
.br-tab b{margin-left:2px;color:#9AA0B2;font-weight:400}
.br-plus{color:#8A90A2;font-size:10px;padding:0 3px 4px}
.br-bar{display:flex;align-items:center;gap:5px;padding:5px 8px;background:#fff;border-top:1px solid #EDEFF4}
.br-nav{color:#A3A8B8;font-size:10px;line-height:1;width:11px;text-align:center}
.br-url{
  flex:1;display:flex;align-items:center;gap:5px;min-width:0;
  background:#F1F2F6;border-radius:20px;padding:3px 9px;
  font-size:8.5px;color:#6B7186;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.br-lock{
  width:5px;height:7px;border-radius:1.5px;background:#8B90A2;position:relative;flex:none;
}
.br-lock::after{
  content:"";position:absolute;left:1px;top:-3px;width:3px;height:4px;
  border:1.2px solid #8B90A2;border-bottom:0;border-radius:2px 2px 0 0;
}
.br-avatar{
  flex:none;width:15px;height:15px;border-radius:50%;background:var(--grad);color:#fff;
  font-size:6.5px;font-weight:700;display:grid;place-items:center;letter-spacing:.02em;
}

/* --- the app --- */
.ap{flex:1;min-height:0;display:grid;grid-template-columns:78px 1fr;background:#F6F7FB}
.ap-side{
  padding:8px 6px;border-right:1px solid #E7E9F0;background:#fff;
  display:flex;flex-direction:column;gap:1px;
}
.ap-brand{font-size:8.5px;font-weight:700;letter-spacing:-.01em;color:var(--text);padding:2px 5px 7px}
.ap-nav{font-size:7.5px;line-height:1.5;color:#6E7488;padding:3.5px 5px;border-radius:4px;white-space:nowrap}
.ap-nav.is-on{background:rgba(91,69,224,.10);color:var(--violet);font-weight:600}
.ap-main{min-width:0;min-height:0;overflow:hidden;padding:9px 10px;display:flex;flex-direction:column;gap:6px}
.ap-top{display:flex;align-items:center;justify-content:space-between;gap:6px}
.ap-h{font-size:11px;font-weight:700;letter-spacing:-.02em}
.ap-range{font-size:7px;color:#6E7488;background:#fff;border:1px solid #E4E7EF;border-radius:20px;padding:2.5px 7px;white-space:nowrap}

.ap-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}
.ap-kpi{background:#fff;border:1px solid #E7E9F0;border-radius:6px;padding:5px 6px;display:grid;gap:0}
.ap-kpi span{font-size:6px;letter-spacing:.07em;text-transform:uppercase;color:#7C8298;line-height:1.6}
.ap-kpi b{font-size:11px;letter-spacing:-.025em;line-height:1.3}
.ap-kpi i{font-style:normal;font-size:6px;line-height:1.6;color:#7C8298;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ap-kpi i.up{color:#1B7A4B}
/* The conversion figure lives in the dashboard as a highlighted tile. */
.ap-kpi-hi{border-color:rgba(91,69,224,.30);background:linear-gradient(160deg,rgba(91,69,224,.09),rgba(62,109,240,.04)),#fff}
.ap-kpi-hi b{color:var(--violet)}
.ap-kpi-hi i.up{color:var(--violet)}

.ap-chart{flex:1;min-height:0;background:#fff;border:1px solid #E7E9F0;border-radius:6px;padding:6px 7px 4px;display:flex;flex-direction:column;gap:4px}
.chart-head{display:flex;align-items:center;justify-content:space-between;gap:5px}
.chart-title{font-size:6.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#7C8298;line-height:1.5}
.chart-legend{display:inline-flex;align-items:center;gap:4px;font-size:6.5px;color:#7C8298;line-height:1.5;white-space:nowrap}
.chart-legend i{width:9px;height:2px;border-radius:2px;background:var(--violet)}
.chart-plot{flex:1;min-height:0;display:flex;gap:4px}
.chart-y{width:13px;flex:none;display:flex;flex-direction:column;justify-content:space-between;font-size:5.5px;line-height:1;color:#8A90A4;text-align:right}
.chart-canvas{position:relative;flex:1;min-width:0}
.chart-canvas svg{width:100%;height:100%;display:block;overflow:visible}
/* Stroke widths would smear under preserveAspectRatio="none" — this keeps them even. */
.chart-grid line{stroke:#EDEFF5;stroke-width:1;vector-effect:non-scaling-stroke}
.chart-dot{position:absolute;right:0;top:19.7%;width:5px;height:5px;margin:-2.5px -2.5px 0 0;border-radius:50%;background:var(--violet);box-shadow:0 0 0 1.5px #fff}
.chart-x{display:flex;justify-content:space-between;padding-left:17px;font-size:5.5px;line-height:1;color:#8A90A4}
.chart-line,.chart-area{opacity:1}

.ap-feed{flex:none;background:#fff;border:1px solid #E7E9F0;border-radius:6px;overflow:hidden}
.ap-feed-row{display:flex;align-items:center;gap:6px;padding:4.5px 7px;border-top:1px solid #F1F2F7}
.ap-feed-row:first-child{border-top:0}
.ap-tag{flex:none;font-size:5.5px;font-weight:700;letter-spacing:.05em;color:#7C8298;background:#F1F2F7;border-radius:3px;padding:1.5px 4px}
.ap-tag-ai{color:#fff;background:var(--grad)}
.ap-feed-t{flex:1;min-width:0;font-size:7px;color:#414761;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ap-feed-m{flex:none;font-size:6px;color:#8A90A4}

.scroll-hint{
  position:absolute;bottom:2.5rem;left:50%;transform:translateX(-50%);
  display:flex;align-items:center;gap:.7rem;font-size:.75rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--muted);z-index:3;
}
.scroll-hint span{width:22px;height:34px;border:1px solid var(--line-2);border-radius:12px;position:relative;background:rgba(255,255,255,.6)}
.scroll-hint span::after{content:"";position:absolute;top:9px;left:50%;width:3px;height:7px;border-radius:2px;background:var(--violet);transform:translateX(-50%)}

/* ---------- Marquee ---------- */
.marquee{
  position:relative;z-index:2;border-block:1px solid var(--line);
  background:rgba(255,255,255,.55);padding:1.15rem 0;
  overflow:hidden;
  /* Soften both ends so words enter and leave instead of being chopped off. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
}
/* Scrolls again — but as a CSS animation, not the rAF loop this used to run. A single
   compositor-driven transform, so it costs nothing per scroll frame. It moves -50%, which
   is exactly one of the two identical sets in the markup, so the wrap is invisible. */
.marquee-track{
  display:flex;width:max-content;
  animation:marqueeScroll 40s linear infinite;
}
.marquee-set{
  display:flex;align-items:center;gap:1.8rem;padding-right:1.8rem;
  font-size:.78rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  white-space:nowrap;
}
.marquee-set b{color:var(--violet);font-size:.4rem;opacity:.7}
@keyframes marqueeScroll{
  from{transform:translateX(0)}
  to{transform:translateX(-50%)}
}
/* Let people stop it to read a word. */
.marquee:hover .marquee-track{animation-play-state:paused}

/* ---------- Services ----------
   This was a 560vh section whose cards were dragged sideways by scroll position. It looked
   good on desktop but meant a transform on every scroll frame, and it collapsed to a plain
   stack on mobile anyway. It is now simply a grid at every size. */
.services{position:relative;z-index:2;padding:var(--section-y) 0}
.svc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:var(--gap)}
.svc{
  padding:var(--card-p);border-radius:var(--radius-lg);
  border:1px solid var(--line);background:var(--surface);box-shadow:var(--shadow-sm);
  position:relative;overflow:hidden;transition:border-color .4s,transform .5s var(--ease),box-shadow .4s;
}
.svc:hover{border-color:rgba(91,69,224,.22);box-shadow:var(--shadow-md)}
.svc-num{font-size:.78rem;letter-spacing:.22em;color:var(--violet);font-weight:700}
.svc-icon{width:52px;height:52px;margin:1.5rem 0 1.8rem;color:var(--violet);border:1px solid rgba(91,69,224,.14);border-radius:14px;display:grid;place-items:center;background:rgba(91,69,224,.05)}
.svc-icon svg{width:27px;height:27px}
.svc h3{font-size:1.4rem;line-height:1.25;margin-bottom:.85rem}
.svc p{color:var(--muted);font-size:1.02rem;line-height:1.65}
.svc ul{margin-top:1.9rem;display:grid;gap:.85rem;position:relative}
.svc li{padding-left:1.7rem;position:relative;font-size:.98rem;line-height:1.5;color:var(--text)}
.svc li::before{content:"";position:absolute;left:.2rem;top:.66em;width:5px;height:5px;border-radius:50%;background:var(--violet)}
.svc-cta{display:flex;flex-direction:column;justify-content:center;gap:1.1rem;align-items:flex-start;
  background:linear-gradient(165deg,rgba(91,69,224,.1),rgba(62,109,240,.05)),#fff;
  border-color:rgba(91,69,224,.2);
}
.svc-cta .btn{margin-top:.8rem}

/* ---------- Reusable device frames ----------
   The hero laptop's shell and browser chrome are reused for the section screenshots.
   A standalone browser window (.win) and a phone (.phone) give some variety so the page
   doesn't read as the same laptop four times. All static. */

/* Phone */
.phone{
  width:100%;max-width:250px;margin-inline:auto;
  border-radius:30px;padding:8px;
  background:linear-gradient(165deg,#2A2D38,#171922 60%,#12141B);
  box-shadow:var(--shadow-lg);
}
.phone-screen{
  position:relative;overflow:hidden;border-radius:23px;
  aspect-ratio:9/17.5;background:#fff;
}
.phone-notch{
  position:absolute;top:6px;left:50%;transform:translateX(-50%);z-index:3;
  width:56px;height:15px;border-radius:20px;background:#14161D;
}
.phone-screen .screen-glare{border-radius:23px}

/* --- Section figure wrapper --- */
.shot-fig{margin:0 0 var(--head-gap)}
.shot-fig figcaption{
  margin-top:1rem;text-align:center;font-size:.85rem;color:var(--muted);line-height:1.6;
}

/* --- AI: intake agent conversation on a phone --- */
.chat-ui{position:absolute;inset:0;display:flex;flex-direction:column;background:#F6F7FB}
.chat-top{
  flex:none;padding:26px 12px 9px;background:#fff;border-bottom:1px solid #EDEFF4;
  display:flex;align-items:center;gap:7px;
}
.chat-av{width:20px;height:20px;border-radius:50%;background:var(--grad);flex:none}
.chat-who b{display:block;font-size:9px;line-height:1.3;color:#16182A}
.chat-who span{font-size:7px;color:#1B7A4B}
.chat-body{flex:1;min-height:0;overflow:hidden;padding:9px;display:flex;flex-direction:column;justify-content:flex-end;gap:7px}
.msg{max-width:84%;font-size:8px;line-height:1.5;padding:6px 8px;border-radius:11px}
.msg-in{align-self:flex-start;background:#fff;color:#2C3145;border:1px solid #EBEDF3;border-bottom-left-radius:3px}
.msg-out{align-self:flex-end;background:var(--grad);color:#fff;border-bottom-right-radius:3px}
.msg-note{
  align-self:center;font-size:6.5px;color:#6E7488;background:#EEF0F7;
  border-radius:20px;padding:3px 8px;text-align:center;
}
.chat-in{
  flex:none;margin:0 9px 10px;display:flex;align-items:center;gap:6px;
  background:#fff;border:1px solid #E4E7EF;border-radius:20px;padding:6px 9px;
}
.chat-in span{font-size:7.5px;color:#9AA0B2}
.chat-send{margin-left:auto;width:15px;height:15px;border-radius:50%;background:var(--grad);flex:none}

/* --- IT: the service desk queue, now inside a laptop --- */
.desk-ui{position:absolute;inset:0;display:flex;flex-direction:column;background:#F6F7FB}
.desk-head{
  flex:none;display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:9px 11px;background:#fff;border-bottom:1px solid #E7E9F0;
}
.desk-h{font-size:11px;font-weight:700;letter-spacing:-.02em}
.desk-count{font-size:7px;color:#6E7488;background:#F1F2F7;border-radius:20px;padding:2.5px 7px}
.desk-list{flex:1;min-height:0;overflow:hidden;padding:9px 10px;display:flex;flex-direction:column;gap:6px}
.desk-item{background:#fff;border:1px solid #E7E9F0;border-radius:6px;padding:6px 8px}
.desk-row{display:flex;align-items:center;justify-content:space-between;gap:8px}
.desk-ref{font-size:6px;font-weight:700;letter-spacing:.08em;color:#8A90A4}
.desk-chip{font-size:6px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:1.5px 5px;border-radius:20px;background:#F1F2F7;color:#7C8298}
.desk-chip-high{background:rgba(224,67,91,.13);color:#C9243C}
.desk-chip-done{background:rgba(30,150,90,.13);color:#1B7A4B}
.desk-title{margin:3px 0 2px;font-size:8.5px;font-weight:600;line-height:1.35;color:#20243A}
.desk-meta{display:flex;justify-content:space-between;gap:8px;font-size:6.5px;color:#8A90A4}

/* ---------- AI ---------- */
.ai{position:relative;z-index:2;padding:var(--section-y) 0;border-top:1px solid var(--line)}
/* Four across, matching the card grids elsewhere rather than a single tall column.
   .ai-grid and .ai-sticky lived here for the old two-column layout. */
.ai-steps{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap)}
.ai-step{
  padding:clamp(1.75rem,2.4vw,2.4rem);border-radius:var(--radius-lg);border:1px solid var(--line);
  background:var(--surface);box-shadow:var(--shadow-sm);
  transition:transform .5s var(--ease),border-color .4s,box-shadow .4s;
}
.ai-step:hover{transform:translateX(6px);border-color:rgba(91,69,224,.22);box-shadow:var(--shadow-md)}
.ai-step-num{font-size:.78rem;letter-spacing:.22em;color:var(--violet);font-weight:700}
.ai-step h3{font-size:1.3rem;margin:.8rem 0 .65rem}
.ai-step p{color:var(--muted);font-size:1rem;line-height:1.65}

/* ---------- IT support ---------- */
.itsupport{position:relative;z-index:2;overflow:hidden;padding:var(--section-y) 0;border-top:1px solid var(--line)}
.it-head{max-width:64ch}
.itsupport .section-title em{
  font-style:normal;background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}

/* Lead feature — the service desk */
.it-desk{
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(2.5rem,5vw,4.5rem);align-items:center;
  padding:clamp(2rem,4vw,3.25rem);border-radius:var(--radius-lg);
  border:1px solid rgba(91,69,224,.2);box-shadow:var(--shadow-md);
  background:linear-gradient(155deg,rgba(91,69,224,.09),rgba(62,109,240,.04)),#fff;
}
.it-desk-tag{
  display:inline-block;font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--violet);margin-bottom:1rem;
}
.it-desk-copy h3{font-size:clamp(1.5rem,2.3vw,1.95rem);line-height:1.2;margin-bottom:1rem}
.it-desk-copy > p{color:var(--muted);line-height:1.7;max-width:46ch}
.it-desk-list{margin-top:1.9rem;display:grid;gap:1rem}
.it-desk-list li{position:relative;padding-left:1.9rem;color:var(--muted);font-size:1rem;line-height:1.6}
.it-desk-list strong{color:var(--text);font-weight:600}
.it-desk-list li::before{
  content:"";position:absolute;left:.2rem;top:.6em;width:5px;height:5px;border-radius:50%;background:var(--violet);
}

/* The service desk now renders inside a laptop — see the device frames above. */
.it-desk-visual{display:flex;align-items:center}

/* Section-closing line. Was three near-identical classes (.it-foot, .budget-foot,
   .promo-foot), one of which was centred while the others were left-aligned. */
.section-foot{
  margin-top:2.75rem;color:var(--muted);font-size:1.05rem;line-height:1.75;max-width:70ch;
}
.section-foot a{color:var(--violet);font-weight:600;border-bottom:1px solid rgba(91,69,224,.35)}
.section-foot a:hover{border-bottom-color:var(--violet)}

/* ---------- Budget ---------- */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

.budget{position:relative;z-index:2;overflow:hidden;padding:var(--section-y) 0;border-top:1px solid var(--line)}
.budget-head{max-width:64ch}
.budget .section-title em{
  font-style:normal;background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.budget-notes{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap)}
.note{
  padding:clamp(1.75rem,2.4vw,2.25rem);border-radius:var(--radius-lg);border:1px solid var(--line);
  background:var(--surface);box-shadow:var(--shadow-sm);
  transition:border-color .4s,box-shadow .4s,transform .5s var(--ease);
}
.note:hover{border-color:rgba(91,69,224,.22);box-shadow:var(--shadow-md);transform:translateY(-4px)}
.note h4{font-size:1.15rem;margin-bottom:.8rem}
.note p{color:var(--muted);font-size:.98rem;line-height:1.65}

/* ---------- Contact ---------- */
.contact{position:relative;z-index:2;overflow:hidden;padding:var(--section-y) 0;border-top:1px solid var(--line)}
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(3rem,6vw,5.5rem);align-items:start;position:relative;z-index:2}

.form{
  padding:clamp(1.9rem,3vw,2.75rem);border-radius:var(--radius-lg);border:1px solid var(--line);
  background:var(--surface);box-shadow:var(--shadow-md);display:grid;gap:1.35rem;
}
.field{position:relative}
/* Honeypot. Off-canvas rather than display:none — some bots skip hidden inputs. */
.field-trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.field input,.field textarea{
  width:100%;padding:1.6rem 1.15rem .7rem;border-radius:14px;
  border:1px solid var(--line-2);background:#FAFBFE;color:var(--text);
  font:inherit;font-size:1rem;line-height:1.5;outline:none;transition:border-color .3s,box-shadow .3s,background .3s;resize:vertical;
}
.field input:focus,.field textarea:focus{border-color:var(--violet);background:#fff;box-shadow:0 0 0 3px rgba(91,69,224,.14);outline:none}
.field label{
  position:absolute;left:1.15rem;top:1.15rem;color:var(--muted);font-size:1rem;
  pointer-events:none;transition:transform .25s var(--ease),font-size .25s var(--ease),color .25s;
  transform-origin:left top;
}
.field input:focus + label,.field input:not(:placeholder-shown) + label,
.field textarea:focus + label,.field textarea:not(:placeholder-shown) + label{
  transform:translateY(-.7rem) scale(.75);color:var(--violet);
}
.field.invalid input,.field.invalid textarea{border-color:#E0435B}
.form-note{min-height:1.5rem;font-size:.95rem;line-height:1.5;color:var(--violet);text-align:center;font-weight:500}
.form-note.err{color:#C9243C}

/* ---------- Footer ---------- */
.footer{position:relative;z-index:2;border-top:1px solid var(--line-2);padding:clamp(4rem,7vw,5.5rem) 0 2.5rem;background:#fff}
.footer-grid{display:grid;grid-template-columns:1.7fr 1fr 1fr 1fr;gap:clamp(2.5rem,4vw,3.5rem)}
.footer-tag{margin-top:1.4rem;color:var(--muted);font-size:.98rem;line-height:1.7;max-width:36ch}
.footer h4{font-size:.76rem;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:1.4rem;font-weight:600}
.footer nav,.footer-grid > div:last-child{display:flex;flex-direction:column;align-items:flex-start;gap:.9rem;font-size:1rem}
.footer nav a:hover,.footer-grid a:hover{color:var(--violet)}
.footer-base{display:flex;justify-content:space-between;align-items:center;margin-top:4rem;padding-top:2rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem;flex-wrap:wrap;gap:1rem}
.footer-base a{transition:color .3s}
.footer-base a:hover{color:var(--violet)}
.footer-email{font-weight:600;color:var(--text)}
.footer-email:hover{color:var(--violet)}
.to-top:hover{color:var(--violet)}

/* ============ AI promotional video ============ */
.promo{padding-block:var(--section-y)}
/* Match the IT and Budget heads — both use a pill plus a gradient em. */
.promo .section-head{max-width:64ch}
.promo .section-title em{
  font-style:normal;background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.promo-grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap);margin-top:var(--head-gap);
}
.promo-card{
  padding:var(--card-p);border-radius:var(--radius-lg);background:var(--surface);
  border:1px solid var(--line);box-shadow:var(--shadow-sm);
}
.promo-card h3{font-size:1.24rem;letter-spacing:-.02em;margin-bottom:.7rem}
.promo-card p{color:var(--muted);line-height:1.75}


@media (max-width:900px){
  .promo-grid{grid-template-columns:1fr}
}
/* Section screenshot figures */
.ai-shot{margin-top:var(--head-gap);margin-bottom:0}
.itsupport .it-desk-visual .laptop{max-width:none}
@media (max-width:560px){
  /* Fine detail in these shots stops being legible on a phone — keep the essentials. */
  .site-shot{display:none}
  .site-strip{grid-template-columns:1fr 1fr}
  .site-card:last-child{display:none}
  .desk-list .desk-item:last-child{display:none}
}

/* ---------- Responsive ---------- */
@media (max-width:1150px){
  .nav-links{gap:1.6rem}
  .nav-links a:not(.btn){font-size:.95rem}
}
@media (max-width:1080px){
  .hero{grid-template-columns:1fr;gap:4.5rem;min-height:auto}
  .hero-inner{max-width:min(52ch,100%)}
  .laptop{max-width:560px}
  .ai-steps{grid-template-columns:repeat(2,1fr)}
  .footer-grid{grid-template-columns:1fr 1fr}
  .budget-notes{grid-template-columns:1fr}
  .it-desk{grid-template-columns:1fr}
  .contact-grid{grid-template-columns:1fr}
}
@media (max-width:820px){
  /* Hide the text links and the CTA, but NOT the whole of .nav-links — the
     Instagram icon lives inside it, and hiding the lot left nothing to tap on a
     phone. Both hidden items are already in the burger menu; the icon wasn't. */
  .nav-links > a{display:none}
  .nav-actions .btn{display:none}
  .nav-links{margin-left:auto;gap:0}
  .nav{gap:.35rem}
  .nav-ig,.theme-toggle{width:44px;height:44px}  /* full-size touch targets */
  .nav-ig svg,.theme-toggle svg{width:21px;height:21px}
  .burger{display:flex}
  .scroll-hint{display:none}
}
/* Small phones only. At 375px "We build <word>" clears the line by ~11px, but below
   about 360px it doesn't, and the rotator drops to its own line leaving "We build"
   stranded. 375px and up are untouched. */
@media (max-width:365px){
  .hero-title{font-size:2.05rem}
}

@media (max-width:560px){
  .ai-steps{grid-template-columns:1fr}
  body{font-size:17px}
  .wrap{width:90vw}
  .footer-grid{grid-template-columns:1fr}
  /* A phone-width screen can't carry the sidebar, the fourth tile or the activity feed. */
  .laptop{transform:none}
  .ap{grid-template-columns:1fr}
  .ap-side{display:none}
  .ap-kpis{grid-template-columns:1fr 1fr}
  .ap-kpi:nth-child(3){display:none}
  /* Hiding a tile leaves an odd count, so let the conversion figure take the full row. */
  .ap-kpi-hi{grid-column:1 / -1}
  .ap-feed{display:none}
  .ap-main{padding:8px}
}

/* ================= Glass =================
   Two ingredients, and the cheap one does most of the work:

   1. Colour behind the panels. These are plain static radial gradients painted once —
      not the blurred orbs that used to live here, which were 44vw boxes running
      blur(120px) and repainting every scroll frame. A gradient costs nothing to scroll.
   2. Translucent panels (--surface is 62% white) with a light top edge.

   backdrop-filter is the expensive part, so it is used sparingly: always on the nav,
   which is one fixed element, and on cards only from 900px up. Phones get the
   translucency and the light edge without paying for a blur per card. */
/* Sections alternate between the page ground and a slightly recessed band, each with a
   hairline above it. Every section used to carry a wash on the same ground, so the page
   read as one continuous gradient and you couldn't tell where one section ended.
   background-image (not the shorthand) so the band colour below still applies. */
.services{background-image:radial-gradient(900px 520px at 88% -5%,rgba(62,109,240,.10),transparent 62%)}
.ai{background-image:radial-gradient(880px 540px at 8% 6%,rgba(91,69,224,.11),transparent 60%)}
.promo{background-image:radial-gradient(900px 540px at 92% 4%,rgba(124,99,255,.10),transparent 60%)}
.itsupport{background-image:radial-gradient(860px 520px at 6% 8%,rgba(62,109,240,.09),transparent 60%)}
.contact{background-image:radial-gradient(820px 540px at 12% 104%,rgba(91,69,224,.10),transparent 60%)}

/* The alternation itself. */
.ai,.itsupport,.contact{background-color:var(--bg-2)}
.services,.promo,.budget{border-top:1px solid var(--line)}

.svc,.note,.ai-step,.form,.promo-card,.faq-item{border-color:var(--glass-line)}

@media (min-width:900px){
  .svc,.note,.ai-step,.form,.promo-card,.faq-item{
    -webkit-backdrop-filter:blur(14px) saturate(150%);
            backdrop-filter:blur(14px) saturate(150%);
  }
}

/* Service card headings link through to their own page. Kept visually identical to the
   plain headings they replaced — the whole card already reads as clickable. */
.svc-link{color:inherit;transition:color .3s}
.svc-link:hover{color:var(--violet)}

/* ================= Service pages ================= */
/* Clears the fixed nav — these pages have no tall hero to sit under it. */
.crumbs{padding:7.25rem 0 0;font-size:.88rem;color:var(--muted)}
.crumbs a{color:var(--muted);transition:color .3s}
.crumbs a:hover{color:var(--violet)}
.crumbs span[aria-hidden]{margin:0 .45rem;opacity:.5}

/* Service pages need their own washes — the homepage ones are keyed to section classes
   that don't exist here, so without these the glass panels sit on flat white and read
   as ordinary cards. */
.svc-hero{
  padding:2.6rem 0 var(--section-y);
  background:radial-gradient(900px 540px at 88% -12%,rgba(91,69,224,.11),transparent 62%);
}
.page-section{background:radial-gradient(820px 500px at 6% 0%,rgba(62,109,240,.07),transparent 60%)}
.page-section.alt{
  background:
    radial-gradient(860px 520px at 92% 4%,rgba(124,99,255,.11),transparent 60%),
    var(--bg-2);
}
/* Long measures are hard to read. The steps run full width on these pages, unlike the
   homepage where they sit in a narrow column. */
/* Two across on the service pages — their step copy is longer than the homepage's, so
   four columns would squeeze it into narrow ribbons. */
.page-section .ai-steps{grid-template-columns:repeat(2,1fr)}
@media (max-width:760px){
  .page-section .ai-steps{grid-template-columns:1fr}
}
.svc-hero .section-head{max-width:62ch}
.svc-hero .pill{margin-bottom:1.6rem}
.svc-hero h1{font-size:clamp(2.3rem,4.4vw,3.5rem);line-height:1.1;letter-spacing:-.035em}
.svc-hero h1 em{
  font-style:normal;background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.svc-hero .hero-cta{margin-top:2.6rem;display:flex;gap:1rem;flex-wrap:wrap}

.page-section{padding-block:var(--section-y);position:relative;z-index:2}
.page-section.alt{background:var(--bg-2);border-block:1px solid var(--line)}
.page-section h2{
  font-size:clamp(1.75rem,2.7vw,2.4rem);line-height:1.15;letter-spacing:-.03em;
  margin-bottom:var(--head-gap);max-width:24ch;
}

/* Reuses .promo-card for the "what you get" tiles. */
.faq-list{display:grid;gap:1rem;max-width:74ch}
.faq-item{
  padding:clamp(1.5rem,2.2vw,2rem);border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow-sm);
}
.faq-item h3{font-size:1.12rem;letter-spacing:-.015em;margin-bottom:.6rem}
.faq-item p{color:var(--muted);line-height:1.75}
.faq-item p + p{margin-top:.8rem}

/* Long-form text pages (privacy, terms). Measure kept near 70 characters — the reason
   these pages are usually unreadable is a 1200px line length. */
.prose{max-width:70ch}
.prose h2{
  font-size:clamp(1.25rem,1.8vw,1.5rem);line-height:1.25;letter-spacing:-.02em;
  margin:2.75rem 0 .9rem;
}
.prose h2:first-child{margin-top:0}
.prose p{color:var(--muted);line-height:1.8;margin-bottom:1.1rem}
.prose strong{color:var(--text);font-weight:600}
.prose a{color:var(--violet);font-weight:600;border-bottom:1px solid rgba(91,69,224,.35)}
.prose a:hover{border-bottom-color:var(--violet)}

/* Keyboard users shouldn't have to tab through the whole nav to reach the content. */
.skip-link{
  position:absolute;left:1rem;top:-4rem;z-index:200;
  padding:.7rem 1.15rem;border-radius:0 0 10px 10px;
  background:var(--violet);color:#fff;font-weight:600;font-size:.95rem;
  transition:top .2s var(--ease);
}
.skip-link:focus{top:0}

.cta-band{padding-block:var(--section-y);text-align:center}
.cta-band h2{margin:0 auto 1.2rem;max-width:20ch}
.cta-band p{color:var(--muted);max-width:52ch;margin:0 auto 2.4rem;line-height:1.75}
.cta-band .hero-cta{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap}

/* Other services, linked from the foot of each page. */
.also{display:flex;flex-wrap:wrap;gap:.7rem;justify-content:center;margin-top:3rem}
.also a{
  font-size:.92rem;font-weight:600;color:var(--muted);
  border:1px solid var(--line-2);border-radius:100px;padding:.6rem 1.1rem;background:var(--surface);
  transition:color .3s,border-color .3s;
}
.also a:hover{color:var(--violet);border-color:rgba(91,69,224,.35)}

/* ================= Scroll-driven parallax =================
   Driven by scroll-driven CSS animations, not JavaScript. There is still no scroll
   listener and no rAF loop on this site: the browser runs these on the compositor,
   off the main thread, which is why the parallax can come back without the jank that
   got the old version removed.

   Two deliberate constraints:

   1. Everything is gated behind @supports. A browser without scroll-driven animations
      simply gets the static page — no half-applied effects.
   2. Every effect is a transform. Nothing here animates opacity, so no element can
      ever be left invisible. That is what made the old JS reveal system dangerous:
      one missing script tag and the whole page was blank.

   Parallax is the *difference* in speed between layers, so the mockups travel further
   than the headings they sit beside. Magnitudes are kept under ~3.5rem so nothing
   drifts into the section above or below. */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){

    /* Hero: the laptop sinks and shrinks slightly as the hero scrolls away. */
    .hero-stage{
      animation:heroDrift linear both;
      animation-timeline:scroll(root);
      animation-range:0 90vh;
    }
    @keyframes heroDrift{
      to{transform:translateY(-4rem) scale(.96)}
    }

    /* Section mockups: the fastest-moving layer. */
    .shot-fig,.it-desk-visual{
      animation:shotDrift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes shotDrift{
      from{transform:translateY(3.4rem)}
      to{transform:translateY(-3.4rem)}
    }

    /* Headings move at roughly a third of that. The gap is the effect. */
    .section-head{
      animation:headDrift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes headDrift{
      from{transform:translateY(1.2rem)}
      to{transform:translateY(-1.2rem)}
    }

    /* Card grids drift least, so they read as the layer nearest the page. */
    .svc,.promo-card,.note,.ai-step{
      animation:cardDrift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes cardDrift{
      from{transform:translateY(.85rem)}
      to{transform:translateY(-.85rem)}
    }
  }
}

/* ================= Dark theme =================
   Driven by data-theme on <html>. Most of the work is token overrides — the palette is
   already centralised, so components follow automatically. The rules after the token
   block are the handful of places that hardcode a light value for a good reason.

   Deliberately NOT inverted: the device mockups (hero laptop, AI phone, IT desk). Those
   are screenshots of light-themed software, and a screenshot doesn't repaint itself
   because the page around it went dark. */
:root[data-theme="dark"]{
  --bg:#0B0D16;
  --bg-2:#0F1220;
  --panel:#141827;
  --surface:rgba(255,255,255,.055);
  --surface-2:#141827;
  --line:rgba(255,255,255,.09);
  --line-2:rgba(255,255,255,.17);
  --glass-line:rgba(255,255,255,.10);

  --text:#EDEFF7;
  --muted:#A6ADC4;

  /* Lifted off the light-theme values: #5B45E0 on near-black only reaches about 3:1. */
  --violet:#9280FF;
  --violet-lt:#AE9EFF;
  --blue:#7AA2FF;

  --shadow-sm:0 1px 2px rgba(0,0,0,.4),0 2px 10px -4px rgba(0,0,0,.5);
  --shadow-md:0 2px 6px rgba(0,0,0,.45),0 14px 34px -16px rgba(0,0,0,.6);
  --shadow-lg:0 4px 10px rgba(0,0,0,.5),0 30px 64px -28px rgba(0,0,0,.7);
}

/* The device mockups are screenshots of light-themed software, so they keep their light
   palette in both themes. They set a white background but never set a text colour, which
   meant they inherited the page's — turning their text white-on-white in dark mode.
   Re-declaring the tokens inside the subtree fixes every descendant at once, including
   anything using var(--violet) or var(--grad). */
.shot-ui,.chat-ui,.desk-ui{
  --text:#16182A;
  --muted:#6E7488;
  --violet:#5B45E0;
  --violet-lt:#7A63F5;
  --blue:#3E6DF0;
  color:#16182A;
}
.ap-brand{color:#16182A}

:root[data-theme="dark"] body{
  background:
    linear-gradient(180deg,rgba(146,128,255,.14),rgba(146,128,255,0) 760px),
    var(--bg);
}
:root[data-theme="dark"] .nav{background:rgba(11,13,22,.72)}
:root[data-theme="dark"] .marquee{background:rgba(255,255,255,.03)}
:root[data-theme="dark"] .footer{background:#0E111C}
:root[data-theme="dark"] .btn-ghost{background:rgba(255,255,255,.06)}
:root[data-theme="dark"] .btn-ghost:hover{background:rgba(255,255,255,.11);border-color:var(--violet);color:var(--violet-lt)}
:root[data-theme="dark"] .pill{background:rgba(146,128,255,.14);border-color:rgba(146,128,255,.30)}
:root[data-theme="dark"] .scroll-hint span{background:rgba(255,255,255,.06)}
:root[data-theme="dark"] .field input,
:root[data-theme="dark"] .field textarea{background:rgba(255,255,255,.05)}
:root[data-theme="dark"] .field input:focus,
:root[data-theme="dark"] .field textarea:focus{background:rgba(255,255,255,.09)}
:root[data-theme="dark"] .it-desk,
:root[data-theme="dark"] .svc-cta{
  background:linear-gradient(155deg,rgba(146,128,255,.16),rgba(122,162,255,.07)),var(--panel);
  border-color:rgba(146,128,255,.26);
}
/* White noise over a dark page reads as dust, so dial it back. */
:root[data-theme="dark"] .grain{opacity:.016}

/* The section washes are near-invisible at light-theme alpha on a dark ground. */
:root[data-theme="dark"] .services{background:radial-gradient(900px 520px at 88% -5%,rgba(122,162,255,.16),transparent 62%)}
:root[data-theme="dark"] .ai{background:radial-gradient(880px 540px at 8% 6%,rgba(146,128,255,.17),transparent 60%)}
:root[data-theme="dark"] .promo{background:radial-gradient(900px 540px at 92% 4%,rgba(146,128,255,.16),transparent 60%)}
:root[data-theme="dark"] .itsupport{background:radial-gradient(860px 520px at 6% 8%,rgba(122,162,255,.15),transparent 60%)}
:root[data-theme="dark"] .contact{background:radial-gradient(820px 540px at 12% 104%,rgba(146,128,255,.16),transparent 60%)}
:root[data-theme="dark"] .svc-hero{background:radial-gradient(900px 540px at 88% -12%,rgba(146,128,255,.17),transparent 62%)}
:root[data-theme="dark"] .page-section{background:radial-gradient(820px 500px at 6% 0%,rgba(122,162,255,.12),transparent 60%)}
:root[data-theme="dark"] .page-section.alt{
  background:radial-gradient(860px 520px at 92% 4%,rgba(146,128,255,.16),transparent 60%),var(--bg-2);
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* Stop the marquee outright rather than letting the blanket rule below race it to
     the end of its keyframes. */
  .marquee-track{animation:none}
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important}
  .laptop{transform:none}
}

/* Sits under the submit button — the point where someone actually hands over their data. */
.form-privacy{
  margin-top:.35rem;font-size:.82rem;line-height:1.65;color:var(--muted);text-align:center;
}
.form-privacy a{color:var(--violet);font-weight:600;border-bottom:1px solid rgba(91,69,224,.3)}
.form-privacy a:hover{border-bottom-color:var(--violet)}
