/* Self-hosted Plus Jakarta Sans (SIL OFL). One variable file per subset
   covers weights 400–600. latin-ext carries Romanian ă, ș and ț. Served from
   our own origin, so the first paint no longer waits on a third-party
   stylesheet. */
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/plus-jakarta-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/plus-jakarta-sans-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;
}

/* Metric-matched stand-in for Plus Jakarta Sans. The swap still happens, but
   the two faces occupy the same space, so nothing shifts or visibly re-flows. */
@font-face {
  font-family: 'Jakarta Fallback';
  src: local('Helvetica Neue'), local('Arial');
  size-adjust: 105.5%;
  ascent-override: 95%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* ---------- Tokens ---------- */
:root {
  --base: #0A0A0F;
  --raise: #101018;
  --ink: #F2F1F6;
  --ink-mute: #85858F;
  --rule: #1E1E28;
  --rule-lit: #32323F;

  /* Used as signal — action, progress, active state. Never as wallpaper. */
  --signal: #6B5CFF;
  --signal-warm: #A957B1;

  --glow: color-mix(in srgb, var(--signal) 55%, transparent);
  --ring: color-mix(in srgb, var(--signal) 22%, transparent);

  --font: 'Plus Jakarta Sans', 'Jakarta Fallback', system-ui, sans-serif;

  --shell: 1140px;
  --gutter: clamp(20px, 5vw, 56px);
  --header-h: 68px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: dark;
  accent-color: var(--signal);
}

/* ---------- Reset ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

/* Lenis (smooth wheel scrolling) animates scroll itself, so the browser's own
   smooth behaviour has to step aside while it runs. Rules from lenis.css. */
html.lenis { scroll-behavior: auto; }
html.lenis, html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }

body {
  background: var(--base);
  color: var(--ink);
  font-family: var(--font);
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  scrollbar-color: var(--rule-lit) transparent;
}

body.menu-open { overflow: hidden; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: none; }
img, svg { display: block; max-width: 100%; }
ul, ol { list-style: none; }

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

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------- Type scale ---------- */
h1, h2, h3, h4 {
  font-weight: 600;
  line-height: 1.1;
  text-wrap: balance;
}

p { text-wrap: pretty; }

.section-title {
  font-size: clamp(1.875rem, 1.1rem + 3.4vw, 3.25rem);
  letter-spacing: -0.035em;
}

.section-lede {
  margin-top: 14px;
  max-width: 48ch;
  color: var(--ink-mute);
}

/* ---------- Ambient backdrop ----------
   One fixed layer. Softness comes from the gradient stops rather than a blur
   filter, so nothing is re-rasterized while it parallaxes. */
.backdrop {
  position: fixed;
  inset: -15% -8%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(circle at 16% 14%, color-mix(in srgb, var(--signal) 17%, transparent), transparent 30%),
    radial-gradient(circle at 84% 22%, color-mix(in srgb, var(--signal-warm) 13%, transparent), transparent 28%),
    radial-gradient(circle at 30% 72%, color-mix(in srgb, var(--signal) 12%, transparent), transparent 30%),
    radial-gradient(circle at 88% 84%, color-mix(in srgb, var(--signal-warm) 11%, transparent), transparent 28%);
  will-change: transform;
}

/* ---------- Header ---------- */
.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 70;
  border-bottom: 1px solid transparent;
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease);
}

.site-header.scrolled {
  background: color-mix(in srgb, var(--base) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--rule);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  block-size: var(--header-h);
}

.logo-img {
  height: 24px;
  width: auto;
  transition: height 0.4s var(--ease);
}

.site-header.scrolled .logo-img { height: 21px; }

.nav {
  display: flex;
  gap: 34px;
  font-size: 0.9375rem;
}

.nav a {
  position: relative;
  color: var(--ink-mute);
  transition: color 0.25s var(--ease);
}

.nav a::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block-end: -6px;
  block-size: 1px;
  background: var(--signal);
  scale: 0 1;
  transform-origin: left;
  transition: scale 0.35s var(--ease);
}

.nav a:hover { color: var(--ink); }
.nav a:hover::after { scale: 1 1; }

.header-actions { display: flex; align-items: center; gap: 16px; }

/* ---------- Language switcher ---------- */
.lang {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 0.8125rem;
  font-weight: 500;
}

.lang-opt {
  position: relative;
  padding: 4px 8px;
  border-radius: 4px;
  color: var(--ink-mute);
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
}

.lang-opt:hover { color: var(--ink); }

/* Invisible 48px tap area around a small visible pill. */
.lang-opt::before,
.footer-nav a::before,
.footer-brand::before,
.footer-legal a::before,
.form-note a::before,
.contact-email::before,
.menu-mail::before {
  content: '';
  position: absolute;
  inset: -13px -7px;
}

.lang-opt[aria-current="true"] {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.lang-menu { font-size: 1rem; gap: 4px; }
.lang-menu .lang-opt { padding: 12px 14px; }
.lang-menu .lang-opt::before { inset: 0; }

/* Two bars, not three — closer to the X it becomes. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  inline-size: 48px;
  block-size: 48px;
  margin-inline-end: -12px;
  z-index: 80;
}

.nav-toggle span {
  display: block;
  inline-size: 22px;
  block-size: 1.5px;
  margin-inline: auto;
  background: var(--ink);
  border-radius: 2px;
  transition: translate 0.3s var(--ease), rotate 0.3s var(--ease);
}

.nav-toggle[aria-expanded="true"] span:first-child {
  translate: 0 3.75px;
  rotate: 45deg;
}

.nav-toggle[aria-expanded="true"] span:last-child {
  translate: 0 -3.75px;
  rotate: -45deg;
}

/* ---------- Mobile menu: full-screen panel ---------- */
.menu {
  position: fixed;
  inset: 0;
  z-index: 65;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(32px, 7vh, 56px);
  padding: calc(var(--header-h) + 32px) var(--gutter) max(40px, env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--base) 94%, transparent);
  backdrop-filter: blur(24px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0.4s var(--ease);
}

.menu.open { opacity: 1; visibility: visible; }

.menu-nav { display: flex; flex-direction: column; }

.menu-nav a {
  padding-block: clamp(14px, 2.4vh, 22px);
  border-bottom: 1px solid var(--rule);
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  font-weight: 600;
  letter-spacing: -0.035em;
}

.menu-foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 20px;
}

.menu-mail { position: relative; color: var(--ink-mute); }

/* Items settle in after the panel itself, so the panel reads as the container
   and the links as its contents. */
.menu-nav a,
.menu-foot {
  opacity: 0;
  translate: 0 16px;
  transition: opacity 0.45s var(--ease), translate 0.45s var(--ease);
}

.menu.open .menu-nav a,
.menu.open .menu-foot {
  opacity: 1;
  translate: 0 0;
  transition-delay: calc(var(--i, 0) * 0.055s + 0.1s);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.75em 1.6em;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  border-radius: 999px;
  white-space: nowrap;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease),
              box-shadow 0.25s var(--ease), translate 0.2s var(--ease),
              scale 0.12s var(--ease);
}

.btn-sm { padding: 0.6em 1.25em; font-size: 0.875rem; }
.btn-lg { padding: 0.95em 2em; font-size: 1.0625rem; }

/* Solid ink on a dark page: the highest-contrast thing on screen, which is
   what the one action on the page deserves. The brand gradient stays on the
   logo and the drawn path rather than being spent here. */
.btn-primary {
  color: var(--base);
  background: var(--ink);
}

/* A halo rather than a coloured glow: reads clearly against the dark page
   without tinting the button. */
.btn-primary:hover {
  background: #fff;
  translate: 0 -1px;
  box-shadow:
    0 0 0 5px color-mix(in srgb, var(--ink) 12%, transparent),
    0 10px 24px -12px rgb(0 0 0 / 0.65);
}

/* Disabled has to look disabled — otherwise the hover still invites a click. */
.btn:disabled { cursor: progress; opacity: 0.55; }
.btn-primary:disabled:hover { background: var(--ink); translate: none; box-shadow: none; }

.btn-primary:active {
  translate: 0 0;
  scale: 0.975;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 16%, transparent);
}

/* ---------- Hero ----------
   The section is taller than the viewport; the inner panel sticks while that
   extra height scrolls past, which is what drives the scale-out. */
.hero {
  position: relative;
  /* The extra 70svh is the pinned stretch the fade plays across. */
  height: 170svh;
  view-timeline: --heroview block;
}

.hero-pin {
  position: sticky;
  inset-block-start: 0;
  block-size: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

.hero-inner { will-change: transform, opacity; }

.hero-inner { text-align: center; }

/* One line of thought, hard-broken at the comma in both languages, so the
   break never lands mid-clause. */
.hero-title {
  font-size: clamp(2.375rem, 1rem + 7vw, 5.5rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.045em;
  max-width: 22ch;
  margin-inline: auto;
}

/* What we do, in plain search terms. Part of the H1 so it carries weight,
   but set small so the slogan still leads. */
.hero-kicker {
  display: block;
  margin-bottom: clamp(18px, 3vh, 28px);
  font-size: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.hero-lede {
  margin-top: clamp(20px, 3vh, 30px);
  margin-inline: auto;
  max-width: 44ch;
  font-size: clamp(1.0625rem, 1rem + 0.4vw, 1.25rem);
  color: var(--ink-mute);
}

.hero-actions {
  margin-top: clamp(28px, 4.5vh, 44px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  flex-wrap: wrap;
}

.hero-note { font-size: 0.875rem; color: var(--ink-mute); }

/* ---------- Hero art ----------
   Only on wide screens, where the hero splits into copy on the left and the
   drawing on the right. It builds once on load, then one small cube floats. */
.hero-art { display: none; }

@media (width > 960px) {
  .hero-inner {
    display: grid;
    /* The copy needs less than half: the title hard-breaks at the comma, so its
       widest line is far narrower than the column. The drawing takes the
       difference. */
    grid-template-columns: minmax(0, 0.84fr) minmax(0, 1.16fr);
    align-items: center;
    gap: clamp(88px, 10vw, 156px);
    text-align: start;
  }

  .hero-title {
    font-size: clamp(2.75rem, 1rem + 3.4vw, 4.25rem);
    max-width: none;
    margin-inline: 0;
  }

  .hero-lede { margin-inline: 0; }
  .hero-actions { justify-content: flex-start; }

  /* The circles run past the drawing's own box on both sides; measured, they
     clear the copy on the left and the viewport on the right, so the box itself
     needs no inset and the drawing gets the whole column. */
  .hero-art { display: block; }
  .hero-art svg { display: block; inline-size: 100%; block-size: auto; overflow: visible; }
}

.hero-ring { stroke-width: 1.25; stroke-opacity: 0.45; stroke-dasharray: 1; stroke-dashoffset: 0; }

.hero-orbit {
  stroke: var(--rule-lit);
  stroke-width: 1.5;
  stroke-dasharray: 2 9;
  stroke-linecap: round;
}

/* The surface is solid now, so the underground layer only shows below its edge. */
.hero-base {
  fill: #101017;
  stroke: color-mix(in srgb, var(--ink) 16%, transparent);
  stroke-width: 1.25;
  stroke-linejoin: round;
}

.hero-base-side { fill: #0C0C12; stroke: color-mix(in srgb, var(--ink) 12%, transparent); stroke-width: 1; stroke-linejoin: round; }

/* Backend: a darker slab underneath, with server slots and status lights. */
.hero-backend path { stroke-linejoin: round; stroke-width: 1; }
.hero-backend .hb-top { fill: #0B0B11; stroke: color-mix(in srgb, var(--signal) 40%, transparent); stroke-dasharray: 4 5; }
.hero-backend .hb-left { fill: #0E0E16; stroke: var(--rule-lit); }
.hero-backend .hb-right { fill: #121219; stroke: var(--rule-lit); }
.hero-backend .hb-slots { stroke: color-mix(in srgb, var(--ink) 14%, transparent); stroke-linecap: round; }
.hero-led { fill: var(--signal); }

.hero-conduit { stroke: color-mix(in srgb, var(--signal) 45%, transparent); stroke-width: 1.25; stroke-dasharray: 2 4; }
.hero-conduit-pulse { stroke: #C9A4F0; stroke-width: 2; stroke-linecap: round; stroke-dasharray: 10 30; stroke-dashoffset: 0; opacity: 0; }

/* The links between the shapes, and a pulse of light travelling round them. */
.hero-link { stroke: color-mix(in srgb, var(--signal) 45%, transparent); stroke-width: 1.25; stroke-dasharray: 3 5; }
.hero-link--spoke { stroke: color-mix(in srgb, var(--ink) 16%, transparent); }
.hero-pulse { stroke: var(--signal); stroke-width: 2.5; stroke-linecap: round; stroke-dasharray: 8 92; stroke-dashoffset: 0; opacity: 0; }
/* One short dash per spoke, all travelling in towards the hub together. */
.hero-pulse--spoke { stroke: #C9A4F0; stroke-width: 2; stroke-dasharray: 4 21; }

.hero-node { fill: var(--signal); }

.hero-shape path, .hero-shape ellipse { stroke-linejoin: round; stroke-width: 1; }

/* Dark shapes: solid faces with a faint edge, so they read as volume. */
.hero-shape--tower .hs-left { fill: #101017; stroke: var(--rule-lit); }
.hero-shape--tower .hs-right { fill: #15151E; stroke: var(--rule-lit); }
.hero-shape--tower .hs-top { fill: #1D1D29; stroke: var(--rule-lit); }

/* A sphere, so the four shapes are four different solids, and low enough at the
   front node to leave the hub's spark clear. */
.hs-shadow { fill: #08080D; }
.hs-orb { stroke: color-mix(in srgb, #fff 14%, transparent); }

/* Brand shapes carry the logo gradient. */
.hero-shape--pyramid .hs-left { fill: #3A338F; }
.hero-shape--pyramid .hs-right { fill: #633673; }
.hs-band { stroke: color-mix(in srgb, #fff 18%, transparent); }

/* The shared hub every shape talks to; it answers each pulse with a ripple. */
.hero-hub-halo { stroke: var(--signal); stroke-width: 1.5; }

/* The drawing assembles in the order the thing itself would be built: the
   circles, then the surface, then each shape by its own logic, then the hub
   and the links across the surface, and only then the backend slab underneath
   and the conduits that tie it to the surface. */
@media (width > 960px) and (prefers-reduced-motion: no-preference) {
  /* 1. The circles. */
  .hero-ring { animation: heroRing 1.2s var(--ease) 0.05s both; }
  .hero-orbit { animation: heroFade 0.7s var(--ease) 0.15s both; }

  /* 2. The surface they sit on. */
  .hero-base, .hero-base-side { animation: heroFade 0.5s var(--ease) 0.3s both; }

  /* 3. Each shape arrives the way that shape would be made. */
  .hero-shape { transform-box: fill-box; transform-origin: 50% 100%; }

  /* Extruded straight up out of the plane. */
  .hero-shape--tower { animation: heroGrow 0.55s var(--ease) 0.55s both; }

  /* Lifted into place and set down. */
  .hero-shape--pyramid { animation: heroRaise 0.5s var(--ease) 0.72s both; }

  /* Stacked, a disc at a time from the bottom up — a database being built. */
  .hs-disc {
    transform-box: fill-box;
    transform-origin: 50% 100%;
    animation: heroStack 0.34s var(--ease) calc(0.88s + var(--i) * 0.11s) both;
  }

  /* Dropped in last, and it lands with a little weight. */
  .hero-shape--orb { animation: heroDrop 0.42s var(--ease) 1.25s both; }

  /* 4. The hub, then the links reaching out to each shape. */
  .hero-hub, .hero-hub-halo { transform-box: fill-box; transform-origin: center; }
  .hero-hub { animation: heroPop 0.34s var(--ease) 1.5s both; }
  .hero-link { animation: heroDraw 0.65s var(--ease) 1.62s both; }

  /* 5. The slab underneath rises into place, and the conduits connect it. */
  .hero-backend { animation: heroSink 0.55s var(--ease) 1.85s both; }
  .hero-conduit { animation: heroFade 0.45s var(--ease) 2.1s both; }

  /* 6. Built. From here it just runs. */
  .hero-hub-halo { animation: heroRipple 1.8s ease-out 2.3s infinite; }
  .hero-pulse { animation: heroFadeIn 0.4s 2.3s both, heroPulse 4s linear 2.3s infinite; }
  .hero-pulse--spoke { animation: heroFadeIn 0.4s 2.3s both, heroSpoke 1.8s linear 2.3s infinite; }
  .hero-conduit-pulse { animation: heroFadeIn 0.4s 2.3s both, heroConduit 1.6s linear 2.3s infinite; }
  .hero-led { animation: heroLed 2.4s steps(1) calc(2.3s + var(--d)) infinite; }
}

@keyframes heroRing { from { stroke-dashoffset: 1; } }
@keyframes heroFade { from { opacity: 0; } }
@keyframes heroFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes heroDraw { from { stroke-dashoffset: 100; opacity: 0; } }
@keyframes heroGrow { from { scale: 1 0; opacity: 0; } 30% { opacity: 1; } }
@keyframes heroRaise { from { scale: 0.45; translate: 0 34px; opacity: 0; } 40% { opacity: 1; } }
@keyframes heroStack { from { translate: 0 -30px; opacity: 0; } 55% { opacity: 1; } }
@keyframes heroDrop {
  from { translate: 0 -58px; opacity: 0; }
  45%  { opacity: 1; }
  72%  { translate: 0 5px; }
  100% { translate: 0 0; }
}
@keyframes heroPop { from { scale: 0; opacity: 0; } }
@keyframes heroPulse { to { stroke-dashoffset: -100; } }
@keyframes heroSpoke { to { stroke-dashoffset: -25; } }
@keyframes heroSink { from { opacity: 0; translate: 0 36px; } }
@keyframes heroConduit { to { stroke-dashoffset: -40; } }
@keyframes heroLed { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
@keyframes heroRipple {
  from { scale: 1; opacity: 0.7; }
  to   { scale: 2.6; opacity: 0; }
}


.scroll-cue {
  position: absolute;
  inset-block-end: clamp(24px, 5vh, 44px);
  inset-inline-start: 50%;
  translate: -50% 0;
  inline-size: 48px;
  block-size: clamp(48px, 6vh, 54px);
}

.scroll-cue-track {
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(50% - 0.5px);
  inline-size: 1px;
  overflow: hidden;
  background: var(--rule-lit);
}

.scroll-cue-track::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  block-size: 40%;
  background: var(--ink);
  animation: cueRun 2.4s var(--ease) infinite;
}

@keyframes cueRun {
  0%   { inset-block-start: -40%; }
  100% { inset-block-start: 100%; }
}

/* ---------- Sections ---------- */
.section {
  position: relative;
  padding-block: clamp(88px, 13vw, 168px);
  view-timeline: --sec block;
}

/* The divider is the chapter break: it sweeps across as the section arrives,
   so moving from one section to the next is an event rather than a seam. */
.section::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 1px;
  background: linear-gradient(90deg, var(--signal), var(--signal-warm) 55%, var(--rule));
  transform-origin: left;
}

/* ---------- Services: cards in rows of two ----------
   Asymmetric widths (7 + 5, then 5 + 7, repeating) so the eye zigzags through them.
   The two core services carry a warmer fill and a badge. Each card has a
   small isometric drawing in the brand's language that moves on hover. */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(14px, 1.6vw, 20px);
  margin-top: clamp(40px, 6vw, 72px);
}

.svc {
  grid-column: span 5;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: clamp(24px, 2.6vw, 36px);
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--base);
  transition: border-color 0.4s var(--ease), background-color 0.4s var(--ease);
}

.svc:nth-child(4n + 1), .svc:nth-child(4n) { grid-column: span 7; }

.svc--core {
  background: linear-gradient(160deg, color-mix(in srgb, var(--signal) 9%, var(--raise)), var(--raise) 60%);
}

.svc:hover { border-color: var(--rule-lit); }

.svc-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

.svc-num {
  font-size: 0.8125rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.svc-badge {
  padding: 4px 10px;
  border: 1px solid color-mix(in srgb, var(--signal) 45%, transparent);
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 500;
  color: color-mix(in srgb, var(--signal) 55%, var(--ink));
}

.svc-art {
  display: block;
  align-self: flex-start;
  block-size: clamp(120px, 12vw, 160px);
  inline-size: auto;
  margin-block: 4px 12px;
  overflow: visible;
}

.svc h3 { font-size: 1.25rem; letter-spacing: -0.02em; }
.svc--core h3 { font-size: clamp(1.375rem, 1.1rem + 0.9vw, 1.875rem); letter-spacing: -0.03em; }

.svc p { color: var(--ink-mute); max-width: 46ch; }

.svc-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 10px; }

.svc-tags li {
  padding: 4px 10px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-size: 0.8125rem;
  color: var(--ink-mute);
}

.tech-link {
  border-bottom: 1px solid var(--rule-lit);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}

.tech-link:hover,
.tech-link:focus-visible { border-bottom-color: var(--signal); color: var(--ink); }

/* What comes with every project, under the cards: a tight grid of small cards. */
.svc-extras { margin-top: clamp(40px, 5vw, 64px); }

/* Hub and spokes. Two columns of three flank a core; every item is wired to it
   by a dashed stub that meets a continuous vertical spine, so the wiring lines
   up whatever height the descriptions happen to be. */
.extras-hub {
  --wire: color-mix(in srgb, var(--signal) 42%, transparent);
  --spoke: clamp(40px, 5vw, 76px);
  --rows-gap: clamp(34px, 4.2vw, 58px);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  column-gap: var(--spoke);
  /* Stretch, not centre: the columns are different heights because the copy
     wraps differently, and centring each one independently leaves their rows
     out of step with each other. */
  align-items: stretch;
  margin-top: clamp(40px, 5vw, 68px);
}

/* Three equal rows, so the icons land at the same heights on both sides and at
   even intervals — which also puts the middle one at the true centre. */
.extras-col {
  display: grid;
  grid-template-rows: repeat(3, 1fr);
  gap: var(--rows-gap);
}

.svc-extra {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

/* Left-hand items read inwards: icon nearest the core, text ranged right. */
.extras-col--left .svc-extra { flex-direction: row-reverse; text-align: end; }
.extras-col--left .svc-extra p { margin-inline-start: auto; }

/* One line per item, core to icon. The items' heights depend on how their copy
   wraps, so script.js measures the endpoints rather than guessing them; every
   line is the same stroke, so the diagram has a single kind of dotted line. */
.extras-wires {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  pointer-events: none;
}

.extras-wires path {
  fill: none;
  stroke: var(--wire);
  stroke-width: 1;
  stroke-dasharray: 3 5;
  stroke-linecap: round;
}

/* The core carries the same three rows and the same gap as the columns, so the
   orb can be placed in row two by layout rather than measured into position.
   Nothing about it depends on when the measuring runs, so a web font swapping
   in on a cold load cannot leave it off centre. */
.extras-core {
  position: relative;
  align-self: stretch;
  display: grid;
  grid-template-rows: repeat(3, 1fr);
  gap: var(--rows-gap);
  justify-items: center;
  inline-size: clamp(140px, 15vw, 210px);
}

/* An icon is 38px tall and sits at the top of its row, so its centre line is
   19px down; the orb is 13px, so its top goes (38 - 13) / 2 down to meet it. */
.extras-core-node {
  grid-row: 2;
  align-self: start;
  margin-block-start: calc((38px - 13px) / 2);
  position: relative;
  z-index: 1;
  inline-size: 13px;
  block-size: 13px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow:
    0 0 0 7px var(--base),
    0 0 0 8px color-mix(in srgb, var(--signal) 22%, transparent),
    0 0 22px color-mix(in srgb, var(--signal) 55%, transparent);
}

.extras-core-node::after {
  content: '';
  position: absolute;
  inset: -5px;
  border: 1px solid var(--signal);
  border-radius: 50%;
}


@media (prefers-reduced-motion: no-preference) {
  .extras-core-node::after {
    transform-origin: center;
    animation: heroRipple 2.6s ease-out infinite;
  }
}

/* Under the breakpoint the two columns cannot flank anything, so the wiring
   comes off and it is simply the list it always was. */
@media (width <= 900px) {
  .extras-hub { grid-template-columns: minmax(0, 1fr); row-gap: 14px; }
  .extras-core, .extras-wires { display: none; }
  .extras-col { grid-template-rows: none; gap: 14px; }
  .extras-col--left .svc-extra { flex-direction: row; text-align: start; }
  .extras-col--left .svc-extra p { margin-inline-start: 0; }
  .extras-hub .svc-extra {
    gap: 16px;
    padding: clamp(20px, 4.5vw, 28px);
    border: 1px solid var(--rule);
    border-radius: 14px;
    background: var(--base);
  }

  .extras-hub .svc-extra p { max-width: none; }
}

.svc-extra-mark {
  display: grid;
  place-items: center;
  inline-size: 38px;
  block-size: 38px;
  margin-bottom: 6px;
  border-radius: 10px;
  color: var(--signal);
  background: color-mix(in srgb, var(--signal) 12%, transparent);
}

.svc-extra-mark svg { inline-size: 20px; block-size: 20px; }
.svc-extra-mark { flex: none; margin-bottom: 0; }
.svc-extra-text { display: grid; gap: 5px; }
.svc-extra h4 { font-size: 1.0625rem; letter-spacing: -0.02em; }
.svc-extra p { color: var(--ink-mute); font-size: 0.9375rem; max-width: 34ch; }

.extras-title {
  font-size: clamp(1.625rem, 1.25rem + 1.3vw, 2.125rem);
  letter-spacing: -0.025em;
}

.extras-lede {
  margin-top: 12px;
  max-width: 46ch;
  color: var(--ink-mute);
}

/* Heading above the technology marquee. */
.build-label {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--ink-mute);
  margin-bottom: 14px;
}

/* Isometric parts shared by the service drawings. */
.svc-art path { stroke-linejoin: round; stroke-width: 1; }
.ia-left { fill: #101017; stroke: var(--rule-lit); }
.ia-right { fill: #15151E; stroke: var(--rule-lit); }
.ia-top { fill: #1D1D29; stroke: var(--rule-lit); }
.ia-grad { fill: url(#svcGradient); }
.ia-plane { fill: color-mix(in srgb, var(--ink) 3%, transparent); stroke: color-mix(in srgb, var(--ink) 16%, transparent); stroke-dasharray: 3 4; }
.ia-pad { fill: color-mix(in srgb, var(--signal) 14%, transparent); stroke: var(--signal); }
.ia-led { fill: var(--signal); }
.ia-led--dim { fill: color-mix(in srgb, var(--signal) 40%, transparent); }
.ia-link, .ia-loop {
  stroke: color-mix(in srgb, var(--signal) 60%, transparent);
  stroke-width: 1.5;
  stroke-dasharray: 3 4;
  stroke-linecap: round;
}
.svc-hub { fill: var(--signal); }
.ia-bar { fill: color-mix(in srgb, var(--signal) 55%, transparent); }
.ia-tape { stroke: color-mix(in srgb, #fff 35%, transparent); stroke-width: 3; stroke-linecap: butt; }
.ia-coin { fill: url(#svcGradient); }
.svc-hub-halo { stroke: var(--signal); stroke-width: 1.25; opacity: 0.5; transform-box: fill-box; transform-origin: center; }

/* Gradient-topped blocks get coloured sides too. */
.svc-art g:has(> .ia-grad) .ia-left { fill: #3A338F; stroke: none; }
.svc-art g:has(> .ia-grad) .ia-right { fill: #633673; stroke: none; }

.svc-slab, .svc-bob, .svc-rider, .svc-rise, .svc-lid, .svc-coin { transition: translate 0.6s var(--ease); }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* Backend: the server slabs lift apart. */
  .svc:hover .svc-slab--2 { translate: 0 -8px; }
  .svc:hover .svc-slab--3 { translate: 0 -18px; }

  /* Cloud: blocks bob one after another, like capacity coming up. */
  .svc:hover .svc-bob { animation: svcBob 1.6s var(--ease) infinite; }
  .svc:hover .svc-bob--2 { animation-delay: 0.15s; }
  .svc:hover .svc-bob--3 { animation-delay: 0.3s; }
  .svc:hover .svc-bob--4 { animation-delay: 0.45s; }

  /* Integrations and automations: data flows along the links. */
  .svc:hover .ia-link, .svc:hover .ia-loop { animation: svcFlow 1.2s linear infinite; }
  .svc:hover .svc-hub-halo { animation: svcRipple 1.4s ease-out infinite; }
  .svc:hover .svc-rider { translate: 126px -26px; }

  /* Websites: the page's sections stand up. E-commerce: the parcel opens. */
  .svc:hover .svc-rise--1 { translate: 0 -10px; }
  .svc:hover .svc-rise--2 { translate: 0 -16px; }
  .svc:hover .svc-rise--3 { translate: 0 -6px; }
  .svc:hover .svc-lid { translate: 0 -16px; }
  .svc:hover .svc-coin--2 { translate: 0 -10px; }
}

@keyframes svcBob { 0%, 100% { translate: 0 0; } 40% { translate: 0 -8px; } }
@keyframes svcFlow { to { stroke-dashoffset: -14; } }
@keyframes svcRipple { from { scale: 1; opacity: 0.6; } to { scale: 2.2; opacity: 0; } }

/* ---------- Stack marquee ----------
   Two identical tracks side by side, each sliding its own full width, so the
   seam never shows. Transform-only, so it runs on the compositor. */
.stack { margin-top: clamp(48px, 7vw, 88px); }

.marquee {
  --gap: clamp(32px, 4.5vw, 64px);
  display: flex;
  overflow: hidden;
  padding-block: 10px;
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}

.marquee-track {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--gap);
  padding-inline-end: var(--gap);
  animation: marquee 50s linear infinite;
}

.marquee:hover .marquee-track { animation-play-state: paused; }

@keyframes marquee {
  to { translate: -100% 0; }
}

.marquee-item {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--ink-mute);
  white-space: nowrap;
  transition: color 0.3s var(--ease);
}

.marquee-item svg { inline-size: 26px; block-size: 26px; flex: none; }
.marquee-item:hover { color: var(--ink); }

/* ---------- Process: a path that draws as you move through it ---------- */
.path {
  position: relative;
  margin-top: clamp(44px, 6vw, 76px);
  padding-inline-start: 80px;
  display: flex;
  flex-direction: column;
  gap: clamp(36px, 5vw, 64px);
}

/* Pinned, like the hero: the section is taller than the viewport and the panel
   sticks while that extra height scrolls past. The travel is split evenly
   between the four steps, so each one costs the same scroll.

   All of this is desktop-only. A phone has no room for a panel that holds
   still, so below the breakpoint the section stays an ordinary list of four
   steps with the line drawing itself as they scroll past, exactly as it did
   before the pin existed. */
@media (width > 760px) {
  .process {
    block-size: 320svh;
    padding-block: 0;
  }

  .process-pin {
    position: sticky;
    inset-block-start: 0;
    block-size: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* Three steps at a time: the one in focus sits in the middle, with the step
     before and the step after it above and below, dimmed. The list slides by
     one step at a time and the mask fades whatever is on its way in or out. */
  .path-view {
    block-size: clamp(300px, 44svh, 400px);
    overflow: hidden;
    mask-image: linear-gradient(transparent, #000 22%, #000 78%, transparent);
  }

  .process-grid .path {
    translate: 0 var(--path-shift, 0px);
    transition: translate 0.6s var(--ease);
  }

  /* Only the text dims. Fading the whole step would fade the number's own
     background with it, and the track behind would show straight through the
     circle. */
  .process-grid .path-text {
    /* Dim enough to be clearly secondary, light enough to still be read. */
    opacity: 0.35;
    transition: opacity 0.5s var(--ease);
  }

  .process-grid .path-step.current .path-text { opacity: 1; }

  /* Discrete now, so the track fills to the step reached rather than to a
     continuous scroll position. Overrides both the scroll-timeline version and
     the --path-progress fallback below. */
  .process-grid .path::after {
    animation: none;
    scale: 1 var(--step-progress, 0);
    transition: scale 0.6s var(--ease);
  }

  /* The ring reads the same progress: a quarter of it per step, from empty. */
  .process-art .art-ring {
    animation: none;
    stroke-dashoffset: calc(1 - var(--step-progress, 0));
    transition: stroke-dashoffset 0.6s var(--ease);
  }
}

/* Illustration on the left, steps on the right. */
.process-grid {
  display: grid;
  grid-template-columns: 0.78fr 1.22fr;
  gap: clamp(40px, 5vw, 80px);
  align-items: center;
  margin-top: clamp(44px, 6vw, 76px);
}

.process-grid .path { margin-top: 0; }

.process-grid .path { gap: clamp(26px, 3.2vw, 44px); }

/* The track stops at the centre of the last number instead of running on under
   its text. --path-end is the distance from there to the foot of the list, which
   only JS can know, so it is measured alongside the steps. */
.process-grid .path::before,
.process-grid .path::after { inset-block-end: var(--path-end, 12px); }


/* Brighter than the shared default, which reads too faint at this size. */
.process-art .art-orbit { stroke: color-mix(in srgb, var(--ink) 15%, var(--rule-lit)); }

.process-art .art-ring { stroke-opacity: 0.9; stroke-width: 2; }

.process-art {
  align-self: center;
  max-inline-size: 440px;
}

.process-art svg { display: block; inline-size: 100%; block-size: auto; overflow: visible; }

.step-plate {
  fill: color-mix(in srgb, var(--ink) 5%, transparent);
  stroke: color-mix(in srgb, var(--ink) 22%, transparent);
  stroke-width: 1.5;
  stroke-linejoin: round;
  transition: fill 0.4s var(--ease), stroke 0.4s var(--ease);
}

/* Lights as the cube lands on it, not as it takes off. */
.step-plate.lit {
  fill: color-mix(in srgb, var(--signal) 22%, transparent);
  stroke: var(--signal);
  transition-delay: 0.3s;
}

/* The cube is drawn at y = 0 and moved onto each layer's centre. The outer
   group travels down; the inner one hops, so together they arc. */
.step-cube {
  opacity: 0;
  translate: 0 33px;
  transition: translate 0.5s var(--ease), opacity 0.3s var(--ease);
}

.process-art[data-level="1"] .step-cube { translate: 0 93px; }
.process-art[data-level="2"] .step-cube { translate: 0 191px; }
.process-art[data-level="3"] .step-cube { translate: 0 289px; }
.process-art[data-level="4"] .step-cube { translate: 0 387px; }
.process-art:not([data-level="0"]) .step-cube { opacity: 1; }

.step-cube-body { transform-box: fill-box; transform-origin: 50% 100%; }
.step-cube-body.hop { animation: cubeHop 0.55s var(--ease); }

@keyframes cubeHop {
  0%   { translate: 0 0; scale: 1; }
  35%  { translate: 0 -46px; scale: 1; }
  80%  { translate: 0 0; scale: 1.08 0.9; }
  100% { translate: 0 0; scale: 1; }
}

.step-face--left { fill: #3A338F; }
.step-face--right { fill: #633673; }

/* Unlit track */
.path::before {
  content: '';
  position: absolute;
  inset-inline-start: 23px;
  inset-block: 12px;
  inline-size: 2px;
  background: var(--rule);
}

/* Lit fill — scaleY is driven by scroll progress through the section. */
.path::after {
  content: '';
  position: absolute;
  inset-inline-start: 23px;
  inset-block: 12px;
  inline-size: 2px;
  background: linear-gradient(var(--signal), var(--signal-warm));
  transform-origin: top;
  scale: 1 var(--path-progress, 0);
  will-change: transform;
}

.path-step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.path-num {
  position: absolute;
  inset-inline-start: -80px;
  inset-block-start: -2px;
  display: grid;
  place-items: center;
  inline-size: 48px;
  block-size: 48px;
  flex: none;               /* never let the text beside it squash the circle */
  z-index: 1;               /* sit above the track and its scroll-drawn fill */
  border-radius: 50%;
  border: 1px solid var(--rule-lit);
  background: var(--base);
  font-size: 0.875rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.path-step.lit .path-num {
  color: var(--ink);
  border-color: var(--signal);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--signal) 12%, transparent);
}

.path-text h3 {
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.4375rem);
  letter-spacing: -0.02em;
}

.path-text p {
  color: var(--ink-mute);
  max-width: 46ch;
}

/* The focused step carries the panel, so it reads a size up from a plain list
   — with four of them still fitting the pin at a laptop height. */
.process-grid .path-text { display: grid; gap: 8px; }
.process-grid .path-text h3 { font-size: clamp(1.3rem, 1.05rem + 0.9vw, 1.75rem); }

.process-grid .path-text p {
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.1875rem);
  line-height: 1.5;
  max-width: 46ch;
}

/* ---------- About ---------- */
.about {
  min-block-size: 100svh;
  display: grid;
  align-content: center;
}

.about-inner {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
}

.about-lead {
  margin-top: 18px;
  max-width: 24ch;
  font-size: clamp(1.25rem, 1rem + 1vw, 1.625rem);
  line-height: 1.35;
  letter-spacing: -0.02em;
}

.about-body {
  display: grid;
  gap: 18px;
  margin-top: clamp(28px, 4vw, 40px);
  color: var(--ink-mute);
  max-width: 54ch;
}

.about-art svg { inline-size: 100%; block-size: auto; overflow: visible; }

/* pathLength="1" lets the ring draw itself with a single 0 to 1 offset. */
.art-ring { stroke-width: 1.25; stroke-opacity: 0.45; stroke-dasharray: 1; stroke-dashoffset: 0; }

/* The about frames are diamonds, not rings; mitres at their points would read
   as spikes. */
.about-art .art-ring,
.about-art .art-orbit { stroke-linejoin: round; }

/* The dotted diamond turns one way as it arrives; this one turns the other, so
   the pair counter-rotate into place. SVG elements transform about 0 0 unless
   told otherwise. */
.about-art .art-ring { transform-box: fill-box; transform-origin: center; }

.art-orbit {
  stroke: var(--rule-lit);
  stroke-width: 1.5;
  stroke-dasharray: 2 9;
  stroke-linecap: round;
  transform-box: fill-box;
  transform-origin: center;
}

.art-plate { stroke-linejoin: round; stroke-width: 1.5; transform-box: fill-box; transform-origin: center; }
.art-plate--mid { stroke: var(--signal); fill: color-mix(in srgb, var(--signal) 6%, transparent); }

.art-face--left { fill: #3A338F; }
.art-face--right { fill: #633673; }

/* ---------- Contact ---------- */
.contact-inner {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: clamp(40px, 6vw, 88px);
  align-items: start;
}

.contact-email {
  position: relative;
  display: inline-block;
  margin-top: 26px;
  font-weight: 500;
  border-bottom: 1px solid var(--rule-lit);
  padding-bottom: 3px;
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}

.contact-email:hover { border-bottom-color: var(--signal); }

.contact-form { display: flex; flex-direction: column; gap: 18px; }

.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 0.875rem;
  color: var(--ink-mute);
}

.field input,
.field textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 3%, transparent);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 13px 15px;
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}

.field textarea {
  field-sizing: content;
  min-block-size: 3lh;
  max-block-size: 40svh;
  resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--rule-lit); }

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--ring);
}

/* Only after the visitor has actually interacted. */
.field :user-invalid { border-color: color-mix(in srgb, #FF6B6B 65%, var(--rule)); }
.field :user-invalid:focus { box-shadow: 0 0 0 3px color-mix(in srgb, #FF6B6B 18%, transparent); }

.contact-form .btn { align-self: flex-start; margin-top: 4px; }

.form-status { font-size: 0.875rem; color: var(--ink-mute); min-height: 1.4em; }
.form-status.is-error { color: #FF8585; }

.form-note { font-size: 0.8125rem; color: var(--ink-mute); }

.form-note a,
.footer-legal a,
.legal a:not(.btn) {
  position: relative;
  color: var(--ink);
  border-bottom: 1px solid var(--rule-lit);
  transition: border-color 0.25s var(--ease);
}

.form-note a:hover,
.footer-legal a:hover,
.legal a:not(.btn):hover { border-bottom-color: var(--signal); }

/* ---------- Footer ---------- */
.site-footer { border-top: 1px solid var(--rule); }

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-block: 30px;
}

.footer-brand { position: relative; display: flex; align-items: center; gap: 10px; font-weight: 600; }
.footer-mark { height: 20px; width: 20px; }

.footer-nav { display: flex; gap: 26px; font-size: 0.875rem; color: var(--ink-mute); }
.footer-nav a { position: relative; transition: color 0.25s var(--ease); }
.footer-nav a:hover { color: var(--ink); }

.footer-copy { font-size: 0.8125rem; color: var(--ink-mute); }

/* Company details required by Law 365/2002. Quiet, but always there. */
.footer-legal {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 24px;
  padding-block: 0 30px;
  font-size: 0.8125rem;
  color: var(--ink-mute);
}

.footer-company { display: flex; flex-wrap: wrap; gap: 4px 0; }
.footer-company > span + span::before { content: '·'; margin-inline: 10px; }

/* ---------- Legal pages ---------- */
.legal {
  padding-block: calc(var(--header-h) + clamp(40px, 7vw, 88px)) clamp(64px, 10vw, 120px);
}

.legal-inner { max-width: 720px; }

.legal .btn { margin-top: 32px; }

.legal h1 {
  font-size: clamp(2rem, 1.3rem + 3vw, 3rem);
  letter-spacing: -0.035em;
}

.legal-updated { margin-top: 12px; font-size: 0.875rem; color: var(--ink-mute); }

.legal h2 {
  margin-top: 48px;
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  letter-spacing: -0.02em;
}

.legal p,
.legal ul { margin-top: 14px; color: color-mix(in srgb, var(--ink) 80%, var(--base)); }

.legal ul { list-style: disc; padding-inline-start: 1.25em; display: grid; gap: 6px; }
.legal li::marker { color: var(--ink-mute); }

.legal code {
  font-size: 0.875em;
  padding: 1px 6px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

/* ============================================================
   MOTION — scroll-linked and continuous.
   Native scroll timelines where available; script.js writes the same
   custom properties as a fallback for browsers without them.
   ============================================================ */

/* Backdrop drifts slower than the page: parallax depth. */
.backdrop { translate: 0 calc(var(--scroll-y, 0) * -0.05px); }

/* Hero scales and fades out as its extra height scrolls past. */
.hero-inner {
  opacity: calc(1 - var(--hero-progress, 0) * 1.15);
  scale: calc(1 - var(--hero-progress, 0) * 0.08);
  translate: 0 calc(var(--hero-progress, 0) * -4svh);
}

.scroll-cue { opacity: calc(1 - var(--hero-progress, 0) * 2.2); }

/* The path owns a named timeline so its fill — a pseudo-element only two
   pixels wide — is driven by the list's travel through the viewport rather
   than its own. */
.path { view-timeline: --path block; }

@supports (animation-timeline: view()) {
  .backdrop {
    translate: none;
    animation: backdropDrift linear both;
    animation-timeline: scroll(root block);
  }

  /* The pinned panel never crosses the viewport itself, so the timeline belongs
     to the section around it. `contain` is the phase where a section taller
     than the viewport fully covers it — which is exactly the stretch the sticky
     child stays pinned for. (`exit` only begins once the pin has let go.) */
  .hero-inner,
  .scroll-cue {
    animation: heroOut linear both;
    animation-timeline: --heroview;
    /* Hold fully visible for the first fifth, then fade until the pin lets go. */
    animation-range: contain 20% contain 100%;
  }

  .hero-inner { opacity: 1; scale: 1; translate: none; }
  .scroll-cue { opacity: 1; }

  .path::after {
    scale: 1 0;
    animation: pathDraw linear both;
    animation-timeline: --path;
    animation-range: entry 55% exit 15%;
  }

  /* Section arrival. Ranges are given as lengths, not percentages, so a tall
     section and a short one take the same distance to resolve. */
  .section::before {
    scale: 0 1;
    animation: ruleSweep linear both;
    animation-timeline: --sec;
    animation-range: entry 0 entry 420px;
  }

  .section > .shell {
    animation: sectionRise linear both;
    animation-timeline: --sec;
    animation-range: entry 0 entry 400px;
  }
}

@keyframes backdropDrift {
  to { translate: 0 -12%; }
}

@keyframes heroOut {
  to { opacity: 0; scale: 0.92; translate: 0 -5svh; }
}

@keyframes pathDraw {
  to { scale: 1 1; }
}

@keyframes ruleSweep {
  to { scale: 1 1; }
}

/* Motion only, no fade: content is readable even where the timeline never
   advances (full-page screenshots, crawlers, print). */
@keyframes sectionRise {
  from { translate: 0 72px; scale: 0.97; }
  to   { translate: 0 0; scale: 1; }
}

/* Fallback: same arrival, driven by a class instead of a timeline. */
.anim-js .section::before { scale: 0 1; transition: scale 1s var(--ease); }
.anim-js .section.seen::before { scale: 1 1; }

.anim-js .section > .shell {
  translate: 0 72px;
  scale: 0.97;
  transition: translate 0.9s var(--ease), scale 0.9s var(--ease);
}

.anim-js .section.seen > .shell { translate: 0 0; scale: 1; }

/* The about forms build themselves in order as the section arrives: the ring
   draws, the plane opens out, then the cube's faces slide in along their own
   isometric axes and lock together. */
@supports (animation-timeline: view()) {
  .art-ring, .art-orbit, .art-plate, .art-face {
    animation: linear both;
    animation-timeline: --sec;
  }
  .art-ring { animation-name: artRing; animation-range: cover 2% cover 38%; }
  .about-art .art-ring { animation-name: artRingSpin; animation-range: cover 2% cover 48%; }
  .art-orbit { animation-name: artOrbit; animation-range: cover 2% cover 48%; }
  /* The process section is 320svh, so its `cover` range is 420vh and the pin
     holds from 100/420 to 320/420 of it. Mapping the turn onto exactly that
     keeps the dotted ring moving through the fourth step instead of finishing
     early — the ratio is in vh on both sides, so it holds at any height. */
  .process-art .art-orbit { animation-range: cover 24% cover 76%; }
  .art-plate--mid { animation-name: artPlateMid; animation-range: cover 12% cover 40%; }
  .art-face--left { animation-name: artFaceLeft; animation-range: cover 18% cover 44%; }
  .art-face--right { animation-name: artFaceRight; animation-range: cover 18% cover 44%; }
  .art-face--top { animation-name: artFaceTop; animation-range: cover 24% cover 48%; }
}

@keyframes artRing { from { stroke-dashoffset: 1; } }
@keyframes artOrbit { from { rotate: -140deg; opacity: 0; } }
@keyframes artRingSpin { from { stroke-dashoffset: 1; rotate: 140deg; opacity: 0; } }
@keyframes artPlateMid { from { translate: 0 -60px; scale: 0.45; opacity: 0; } }
@keyframes artFaceLeft { from { translate: -110px 55px; opacity: 0; } }
@keyframes artFaceRight { from { translate: 110px 55px; opacity: 0; } }
@keyframes artFaceTop { from { translate: 0 -150px; opacity: 0; } }

/* Fallback: the same build, run once by time when the section is seen. */
.anim-js .art-ring { transition: stroke-dashoffset 1.2s var(--ease); }
.anim-js .about-art .art-ring { transition: stroke-dashoffset 1.2s var(--ease), rotate 1.6s var(--ease), opacity 1s var(--ease); }
.anim-js .art-orbit { transition: rotate 1.6s var(--ease), opacity 1s var(--ease); }
.anim-js .art-plate,
.anim-js .art-face { transition: translate 1s var(--ease), scale 1s var(--ease), opacity 0.8s var(--ease); }
.anim-js .art-face--left,
.anim-js .art-face--right { transition-delay: 0.3s; }
.anim-js .art-face--top { transition-delay: 0.45s; }

.anim-js .section:not(.seen) .art-ring { stroke-dashoffset: 1; }
.anim-js .section:not(.seen) .art-orbit { rotate: -140deg; opacity: 0; }
.anim-js .about:not(.seen) .art-ring { rotate: 140deg; opacity: 0; }
.anim-js .about:not(.seen) .art-plate--mid { translate: 0 -60px; scale: 0.45; opacity: 0; }
.anim-js .about:not(.seen) .art-face--left { translate: -110px 55px; opacity: 0; }
.anim-js .about:not(.seen) .art-face--right { translate: 110px 55px; opacity: 0; }
.anim-js .about:not(.seen) .art-face--top { translate: 0 -150px; opacity: 0; }

/* ---------- Responsive ---------- */
@media (width <= 900px) {
  .svc, .svc:nth-child(4n + 1), .svc:nth-child(4n) { grid-column: span 6; }
}

@media (width <= 860px) {
  .nav, .header-cta, .header-actions > .lang { display: none; }
  .nav-toggle { display: flex; }

  /* No menu to hold the switcher on legal pages, so it stays in the header. */
  .site-header--plain .header-actions > .lang { display: flex; }
}

@media (width <= 760px) {
  .contact-inner, .about-inner { grid-template-columns: 1fr; }
  .about-art { max-inline-size: 280px; margin-inline: auto; }

  /* On a phone the art would scroll away before its steps light up. */
  .process-grid { grid-template-columns: 1fr; }
  .process-art { display: none; }
  .footer-inner { flex-direction: column; align-items: flex-start; gap: 18px; }
  .footer-legal { flex-direction: column; gap: 14px; }
  .footer-company { flex-direction: column; }
  .footer-company > span + span::before { content: none; }

  /* Hero reads as a centered statement on a phone. */
  .hero { height: 155svh; }

  .hero-title { max-width: none; }
  .hero-actions { flex-direction: column; gap: 16px; }
  .hero-actions .btn { width: 100%; max-width: 320px; }

  /* Numbers sit inline above the step instead of being crushed beside it. */
  .path { padding-inline-start: 0; }
  .path::before, .path::after { inset-inline-start: 23px; }
  .path-step { padding-inline-start: 68px; }
  .path-num { inset-inline-start: 0; }
}

@media (width <= 560px) {
  .svc, .svc:nth-child(4n + 1), .svc:nth-child(4n) { grid-column: span 12; }
}

@media (width <= 420px) {
  .path-step { padding-inline-start: 56px; }
  .path-num { inline-size: 40px; block-size: 40px; }
  .path::before, .path::after { inset-inline-start: 19px; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .step-cube-body.hop { animation: none; }

  /* No drift: show the logos once, wrapped. */
  .marquee { mask-image: none; }
  .marquee-track { animation: none; flex-wrap: wrap; flex-shrink: 1; row-gap: 20px; }
  .marquee-track[aria-hidden="true"] { display: none; }

  .hero { height: auto; min-height: 100svh; }
  .hero-pin { position: static; block-size: auto; min-block-size: 100svh; }

  /* No pin, no sliding window: the four steps are simply a list again. */
  .process { block-size: auto; padding-block: clamp(88px, 13vw, 168px); }
  .process-pin { position: static; block-size: auto; }
  .path-view { block-size: auto; overflow: visible; mask-image: none; }
  .process-grid .path { translate: none; }
  .process-grid .path-text { opacity: 1; }
  .process-grid .path::after { scale: 1 1; }
  .process-art .art-ring { stroke-dashoffset: 0; }

  .hero-inner, .scroll-cue, .backdrop,
  .path::after, .section::before, .section > .shell,
  .art-ring, .art-orbit, .art-plate, .art-face {
    animation: none !important;
    opacity: 1;
    scale: 1;
    translate: none;
  }

  .section::before { scale: 1 1; }

  .path::after { scale: 1 1; }
  .scroll-cue-track::after { animation: none; inset-block-start: 30%; }

  *, *::before, *::after {
    transition-duration: 0.01ms !important;
  }
}
