/* ===========================================================================
   Corva ERP — design system (LIGHT).
   One light scale so every surface shares a single light source; the jade accent
   is the only chromatic voice (blue appears in decorative gradients only) to
   keep the pages from reading as a template. Loaded by every page.

   Inverting the dark original was not a token swap. Three things had to change
   in kind, not just in value:
     - Surfaces were white-alpha over a dark ground (rgba(255,255,255,.024)).
       On a light ground that is invisible, so cards are now SOLID white lifted
       off an off-white page — the separation comes from the ground being darker
       than the card, which is the reverse of the dark build.
     - Depth came from large near-black shadows. At light weights those read as
       dirt, so elevation is now carried mostly by hairline borders with only a
       soft, wide shadow underneath.
     - The jade had to darken. #5ee9b5 is ~1.5:1 on white and fails as text at
       any size; --accent is now #0a7d57, which clears AA (4.9:1) on --bg.
   =========================================================================== */
:root{
  --bg:#f7f8fa;
  --bg-raise:#ffffff;
  --surface:#ffffff;
  --surface-2:#eef1f5;
  --line:rgba(13,17,23,.09);
  --line-2:rgba(13,17,23,.17);
  --ink:#0d1117;
  --ink-2:#47525f;      /* AA on --bg at body sizes (7.4:1) */
  --ink-3:#626c78;      /* AA on --bg down to 13px (4.8:1) */
  --accent:#0a7d57;     /* AA on --bg (4.9:1) — the renders' green, darkened to pass */
  /* Jade text on a JADE TINT, not on --bg. The tint lifts the background to about
     #e6efef, which drops --accent to 4.39:1 — under AA. Anywhere accent-coloured
     text sits on an accent-tinted chip (pill, active nav, active ghost button,
     the contact mailto) it must use this darker step, which clears 4.5:1 on the
     tint. Checking the accent against the page background is the wrong test. */
  --accent-ink:#097350;
  --accent-soft:#0b8f63;/* fills/dots only, never text */
  --accent-2:#3b5bd9;
  --shadow-sm:0 2px 4px -2px rgba(13,17,23,.06),0 6px 16px -10px rgba(13,17,23,.14);
  --shadow-md:0 4px 10px -6px rgba(13,17,23,.08),0 18px 40px -24px rgba(13,17,23,.20);
  --shadow-lg:0 8px 18px -10px rgba(13,17,23,.10),0 34px 70px -40px rgba(13,17,23,.28);
  --radius:16px;
  --maxw:1120px;
  --head-h:68px;
  --ease:cubic-bezier(.22,.68,0,1);
  --font:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
}
*,*::before,*::after{box-sizing:border-box}
/* Declared here, ahead of the reduced-motion block at the end of the file,
   because an equal-specificity `html` rule placed after it would win over the
   override inside it. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);
  font-size:16px;line-height:1.6;letter-spacing:-.005em;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
ul,ol{margin:0;padding:0;list-style:none}
h1,h2,h3{margin:0;font-weight:600;letter-spacing:-.032em;line-height:1.08}
p{margin:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
::selection{background:rgba(10,125,87,.16)}
/* Anchor targets clear the sticky header instead of hiding under it. */
[id]{scroll-margin-top:calc(var(--head-h) + 22px)}

.wrap{width:100%;max-width:var(--maxw);margin:0 auto;padding:0 24px}
@media (max-width:520px){.wrap{padding:0 18px}}

.skip{position:absolute;left:-9999px;top:0;padding:12px 18px;background:var(--accent);color:#fff;font-weight:600;z-index:100}
.skip:focus{left:12px;top:12px}

/* ---------- type scale ---------- */
/* Capped at 3.5rem: the home copy column is ~570px, and anything larger pushes
   the headline to four lines, which reads as a wall rather than a statement. */
.h1{font-size:clamp(2.3rem,5.4vw,3.5rem)}
.h1-sm{font-size:clamp(2.05rem,4.4vw,2.95rem)}
.h2{font-size:clamp(1.75rem,3.5vw,2.6rem)}
.h3{font-size:1.0625rem;letter-spacing:-.017em}
.lead{font-size:clamp(1.03rem,1.35vw,1.19rem);color:var(--ink-2);line-height:1.62;max-width:63ch}
.prose{color:var(--ink-2);font-size:1rem;line-height:1.68;max-width:68ch}
.prose+.prose{margin-top:16px}
/* A module section puts a .lead tagline directly above its body copy; without
   this the two paragraphs touch and read as one block. */
.lead+.prose{margin-top:14px}
.prose strong{color:var(--ink);font-weight:600}
.eyebrow{
  display:inline-flex;align-items:center;gap:9px;margin:0 0 20px;
  font-size:.75rem;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--accent);
}
.eyebrow::before{content:"";width:20px;height:1px;background:linear-gradient(90deg,transparent,var(--accent))}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  height:46px;padding:0 20px;border-radius:10px;border:1px solid transparent;
  font-size:.9375rem;font-weight:600;letter-spacing:-.011em;white-space:nowrap;
  transition:transform .25s var(--ease),background-color .25s,border-color .25s,box-shadow .25s,color .25s;
}
.btn svg{flex:none}
/* White on jade, not the dark ink the dark build used: at this weight dark-on-jade
   drops under 4.5:1, white clears it at 5.1:1. */
.btn-primary{background:var(--accent);color:#fff;box-shadow:0 1px 0 rgba(255,255,255,.18) inset,0 10px 24px -12px rgba(10,125,87,.55)}
.btn-primary:hover{background:#096b4a;transform:translateY(-1px);box-shadow:0 1px 0 rgba(255,255,255,.22) inset,0 16px 32px -14px rgba(10,125,87,.6)}
.btn-ghost{border-color:var(--line-2);color:var(--ink);background:var(--surface);box-shadow:var(--shadow-sm)}
.btn-ghost:hover{border-color:rgba(13,17,23,.28);background:#fff;transform:translateY(-1px);box-shadow:var(--shadow-md)}
.btn-ghost[aria-current="page"]{border-color:rgba(10,125,87,.4);background:rgba(10,125,87,.07);color:var(--accent-ink)}
.btn-sm{height:38px;padding:0 15px;font-size:.875rem;border-radius:9px}

/* ---------- pill ---------- */
.pill{
  display:inline-flex;align-items:center;gap:8px;height:28px;padding:0 11px 0 9px;
  border:1px solid rgba(10,125,87,.28);border-radius:99px;background:rgba(10,125,87,.07);
  color:var(--accent-ink);font-size:.75rem;font-weight:600;letter-spacing:.055em;text-transform:uppercase;
}
/* The ring is a pseudo-element scaled with transform, not an animated box-shadow.
   box-shadow repaints the element every frame and never reaches the compositor;
   this runs forever in both the header and the footer of all seven pages, so it
   was the page's only permanent paint cost. transform + opacity composite. */
.pill .dot{position:relative;width:6px;height:6px;border-radius:50%;background:var(--accent-soft)}
.pill .dot::after{
  content:"";position:absolute;inset:-3px;border-radius:50%;
  background:rgba(10,125,87,.14);animation:breathe 2.8s ease-in-out infinite;
}
@keyframes breathe{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(1.5)}}

/* ---------- header ---------- */
.site-head{position:sticky;top:0;z-index:50;transition:background-color .3s,border-color .3s,backdrop-filter .3s}
.site-head::after{content:"";position:absolute;inset:auto 0 -1px;height:1px;background:var(--line);opacity:0;transition:opacity .3s}
/* .92, not the usual .7: blur alone does not stop 56px near-black headline
   glyphs from reading through the bar as a hero scrolls under it. */
.site-head.is-stuck{background:rgba(247,248,250,.92);backdrop-filter:blur(20px) saturate(140%);-webkit-backdrop-filter:blur(20px) saturate(140%)}
.site-head.is-stuck::after{opacity:1}
/* The open mobile panel needs the bar opaque even at scroll position 0. */
.site-head.nav-open{background:rgba(247,248,250,.97);backdrop-filter:blur(20px) saturate(140%);-webkit-backdrop-filter:blur(20px) saturate(140%)}
.head-in{display:flex;align-items:center;gap:16px;height:var(--head-h)}
.logo{height:26px;width:auto;color:var(--ink);flex:none}
/* The logo is a 26px mark; the padding takes the link itself to a 44px target. */
.brand{display:inline-flex;align-items:center;flex:none;padding:9px 0;border-radius:8px}
.nav{display:flex;gap:2px;margin-left:14px}
.nav a{padding:8px 12px;border-radius:8px;font-size:.9rem;color:var(--ink-2);transition:color .2s,background-color .2s}
.nav a:hover{color:var(--ink);background:var(--surface-2)}
/* Active page: jade text plus a tinted chip — colour alone is never the only cue,
   the link also carries aria-current for assistive tech. */
.nav a[aria-current="page"]{color:var(--accent-ink);background:rgba(10,125,87,.08)}
.head-right{display:flex;align-items:center;gap:12px;margin-left:auto}
.nav-toggle{
  display:none;place-items:center;width:44px;height:44px;margin-left:2px;padding:0;
  border:1px solid var(--line-2);border-radius:10px;background:var(--surface);color:var(--ink);cursor:pointer;
}
.nav-toggle:hover{background:var(--surface-2)}
.nav-toggle .i-close{display:none}
.nav-toggle[aria-expanded="true"] .i-menu{display:none}
.nav-toggle[aria-expanded="true"] .i-close{display:block}

@media (max-width:900px){
  /* Fully opaque, and NO backdrop-filter: the panel's parent (.site-head.nav-open)
     already establishes the backdrop root, so a filter here has nothing of the page
     left to sample and the near-opaque tint alone let the hero headline read
     through. A menu must never be transparent to the content it covers. */
  .nav{
    position:absolute;top:100%;left:0;right:0;display:none;flex-direction:column;gap:2px;
    margin:0;padding:8px 24px 18px;background:var(--bg);
    border-bottom:1px solid var(--line);box-shadow:var(--shadow-lg);
  }
  .nav.is-open{display:flex;animation:navIn .24s var(--ease)}
  .nav a{padding:13px 12px;font-size:1rem}
  .nav-toggle{display:grid}
  /* No JS: the toggle can't work, so the links live in the header and the bar is
     allowed to grow instead of hiding navigation behind a dead button. */
  html:not(.js) .nav-toggle{display:none}
  html:not(.js) .nav{position:static;display:flex;flex-direction:row;flex-wrap:wrap;padding:0;background:none;border:0;box-shadow:none}
  html:not(.js) .head-in{height:auto;flex-wrap:wrap;padding:12px 0;row-gap:6px}
}
@keyframes navIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
@media (max-width:520px){.nav{padding-left:18px;padding-right:18px}}
@media (max-width:430px){.head-right .pill{display:none}}

/* ---------- shared decorative backdrop ---------- */
.bg-wrap{position:absolute;inset:-1px;z-index:0;pointer-events:none;overflow:hidden}
/* The tiled hairline grid that used to sit here (.bg-grid, and .cta-grid in the
   CTA band) is gone, markup included. It was tuned against a near-black ground
   where the lines barely registered; on an off-white page the 72px cells read as
   graph paper behind copy that has nothing to do with measurement. The radial
   tint below does the same job of lifting the hero off flat white, without
   drawing a surface the content does not sit on. */
/* Tints, not glows. A radial "glow" on a light ground has nothing to glow against
   and just muddies the white, so these are held very low and warm-neutral. */
.bg-glow{position:absolute;inset:0;background:
  radial-gradient(58% 42% at 18% 4%,rgba(10,125,87,.07),transparent 62%),
  radial-gradient(48% 40% at 88% 12%,rgba(59,91,217,.06),transparent 62%),
  radial-gradient(70% 50% at 50% 100%,rgba(10,125,87,.035),transparent 70%);
}
.bg-arc{position:absolute;top:-14%;left:50%;width:min(1180px,142%);transform:translateX(-50%);opacity:.28}

/* ---------- home hero ---------- */
.hero{position:relative;padding:clamp(64px,10vw,116px) 0 clamp(72px,9vw,112px);overflow:hidden}
.hero-in{position:relative;z-index:1;display:grid;grid-template-columns:minmax(0,1.13fr) minmax(0,.87fr);gap:clamp(32px,5vw,64px);align-items:center}
@media (max-width:960px){.hero-in{grid-template-columns:1fr;gap:44px}}
.hero-copy{max-width:620px}
.hero h1{margin:20px 0 22px;text-wrap:balance}
/* Solid, not a gradient. The emphasis this span carries is semantic — it is the
   half of the sentence that states the product's actual claim — so it earns a
   colour change, but a three-stop gradient made it decorative and cost it a
   reliable contrast figure. One flat jade at display size reads stronger and
   measures 4.9:1 against the page. */
.grad{color:var(--accent)}
.hero .lead{max-width:56ch}
.cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:32px}
/* The label is ONE span: a bare text node beside the span would become a second
   anonymous flex item and wrap independently of the address. */
.hero-note{display:flex;align-items:flex-start;gap:8px;margin-top:20px;font-size:.8125rem;color:var(--ink-3)}
.hero-note svg{flex:none;margin-top:3px}

/* ---------- subpage hero ---------- */
.subhero{position:relative;overflow:hidden;padding:clamp(52px,7vw,86px) 0 clamp(40px,5vw,62px)}
.subhero-in{position:relative;z-index:1;max-width:760px}
.subhero h1{margin:16px 0 20px;text-wrap:balance}
.subhero .lead{max-width:60ch}
.subhero .cta-row{margin-top:28px}
/* -4px margin absorbs the padding below so the row's optical position is unchanged. */
.crumb{display:flex;align-items:center;gap:8px;margin:-4px 0 14px;font-size:.8125rem;color:var(--ink-3)}
/* 21px tall bare, which fails WCAG 2.2 SC 2.5.8 (24px minimum, AA). Padding takes
   the target to 29px without moving the text. */
.crumb a{display:inline-block;padding:4px 2px;color:var(--ink-3);transition:color .2s}
.crumb a:hover{color:var(--accent)}
.crumb svg{flex:none;opacity:.6}

/* ---------- imagery ----------
   The renders all sit on the page background by construction (their ground is
   shifted to --bg at build time), so they are NOT boxed in a bordered panel —
   a border would draw a rectangle around something already flush with the page.
   Only the photographs, which have real edges, get a radius and a shadow. */
/* The trailing margin is not optional. Every band is followed by a `.sec-tight`
   section, which zeroes its own top padding on the assumption that whatever
   precedes it supplies the separation — so without this the image ends 0px above
   the next section and 8-24px above its first line of text. The bottom is larger
   than the top because the space below belongs to the heading underneath it. */
.band{position:relative;margin:clamp(28px,4vw,44px) 0 clamp(52px,6.5vw,80px)}
.band img{width:100%;height:auto;border-radius:var(--radius)}
/* Photographs carry their own frame; renders bleed into the page. */
.band-photo img{box-shadow:var(--shadow-lg)}
.band-render img{border-radius:0}
.band-cap{margin-top:14px;font-size:.8125rem;color:var(--ink-3)}

/* A square render cannot run full-bleed — at the 1120px measure it would stand
   over a thousand pixels tall and push the page's first real sentence off screen.
   It is centred and capped instead, which also reads as the "single object on a
   lot of white" idiom the renders were art-directed for. */
.band-object{margin-top:clamp(24px,3vw,36px)}
.band-object img{max-width:min(420px,72vw);margin:0 auto;border-radius:0}
/* The module thumb rides beside the section heading at large widths and drops
   above the copy on small ones — never shrinking below a legible 88px. */
.mod-thumb{flex:none;width:clamp(88px,9vw,116px);height:auto;border-radius:12px}

/* ---------- transaction card (home hero graphic) ---------- */
.tcard{
  position:relative;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);
  box-shadow:var(--shadow-lg);
  padding:18px;
}
.tcard::before{
  content:"";position:absolute;inset:-1px;border-radius:inherit;padding:1px;pointer-events:none;
  background:linear-gradient(150deg,rgba(10,125,87,.55),transparent 34%,transparent 68%,rgba(59,91,217,.35));
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
}
.tcard-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:2px 4px 16px;border-bottom:1px solid var(--line);margin-bottom:6px}
.tcard-badge{display:inline-flex;align-items:center;gap:7px;font-size:.6875rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--accent)}
.tcard-op{font-family:var(--mono);font-size:.75rem;color:var(--ink-3);letter-spacing:-.01em;white-space:nowrap}
.rails{display:flex;flex-direction:column}
.rail{display:flex;align-items:center;gap:12px;padding:11px 4px}
.rail+.rail{border-top:1px solid var(--line)}
.rail-ico{
  flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:9px;
  border:1px solid var(--line);background:var(--surface-2);color:var(--accent);
}
.rail-txt{min-width:0}
.rail-txt b{display:block;font-size:.875rem;font-weight:600;letter-spacing:-.012em;color:var(--ink)}
.rail-txt span{display:block;font-family:var(--mono);font-size:.6875rem;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rail-line{flex:1 1 24px;min-width:18px;height:8px;margin:0 2px}
.rl-base{stroke:var(--line-2);stroke-width:1;fill:none}
/* Dash 4 + gap 7 = period 11; the offset must be a whole number of periods
   (-33) or the loop visibly jumps. non-scaling-stroke keeps it in screen px. */
.rl-flow{stroke:var(--accent);stroke-width:1.6;fill:none;stroke-linecap:round;stroke-dasharray:4 7;animation:flow 1.35s linear infinite;animation-delay:var(--d,0s)}
@keyframes flow{from{stroke-dashoffset:0}to{stroke-dashoffset:-33}}
/* No outer glow: on a light ground the halo reads as a smudge, so the endpoint
   is a solid dot with a soft tint ring instead. */
.rail-end{flex:none;width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px rgba(10,125,87,.13);animation:endpulse 1.5s ease-in-out infinite;animation-delay:var(--d,0s)}
@keyframes endpulse{0%,100%{opacity:.45;transform:scale(.82)}55%{opacity:1;transform:scale(1)}}
.tcard-foot{display:flex;align-items:center;gap:11px;margin-top:8px;padding:14px 4px 2px;border-top:1px solid var(--line)}
.tcard-foot .tick{flex:none;color:var(--accent)}
/* The tick starts undrawn and is drawn by the reveal. The keyframes carry BOTH
   endpoints because the animation must not inherit its own end state as `from`. */
.tcard-foot .tick path{stroke-dasharray:26;stroke-dashoffset:26}
.js .tcard.in .tick path{animation:draw .9s var(--ease) .35s forwards}
html:not(.js) .tcard-foot .tick path{stroke-dashoffset:0}
@keyframes draw{from{stroke-dashoffset:26}to{stroke-dashoffset:0}}
.tcard-foot b{font-size:.875rem;font-weight:600;letter-spacing:-.012em}
.tcard-foot small{margin-left:auto;font-size:.75rem;color:var(--ink-3);text-align:right}
@media (max-width:560px){.tcard-op{display:none}}
/* Below ~420px the rail steals the width the mono sublabels need and they start
   ellipsising; the endpoint dot alone still terminates the row. */
@media (max-width:420px){.rail-line{display:none}.rail{gap:11px}.rail-txt{flex:1 1 auto}}

/* ---------- sections ---------- */
section{position:relative}
.sec{padding:clamp(72px,9vw,124px) 0}
.sec-tight{padding-top:0}
.sec-head{max-width:720px;margin-bottom:clamp(40px,5vw,60px)}
.sec-head h2{margin-bottom:18px;text-wrap:balance}
.rule{height:1px;background:linear-gradient(90deg,transparent,var(--line),transparent)}

/* ---------- the 7-stage spine ---------- */
.spine-wrap{position:relative}
/* An abspos SVG is a REPLACED element: left+right+auto width is over-constrained,
   so the browser ignores `right` and sizes it from the viewBox ratio instead
   (2px tall x 250:1 = 500px). The width therefore has to be stated outright. */
.spine-line{position:absolute;top:23px;left:0;height:2px;width:100%;display:none}
.spine{display:grid;gap:22px 18px;position:relative;grid-template-columns:repeat(auto-fill,minmax(232px,1fr))}
.stage{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start}
.stage-node{
  position:relative;z-index:1;display:grid;place-items:center;width:48px;height:48px;border-radius:13px;
  border:1px solid var(--line);background:var(--surface);box-shadow:var(--shadow-sm);
  color:var(--accent);transition:border-color .3s,transform .3s var(--ease),box-shadow .3s;
}
.stage:hover .stage-node{border-color:rgba(10,125,87,.45);transform:translateY(-2px);box-shadow:var(--shadow-md)}
.stage-num{display:block;margin-bottom:6px;font-family:var(--mono);font-size:.6875rem;letter-spacing:.08em;color:var(--accent);opacity:.85}
.stage h3{margin-bottom:6px}
.stage p{font-size:.875rem;color:var(--ink-3);line-height:1.55}
/* The heading holds the real link; this pseudo-element stretches its hit area
   over the whole stage without nesting the grid children inside an anchor. */
.stage-a::after{content:"";position:absolute;inset:-8px;border-radius:12px}
.stage-link{display:inline-flex;align-items:center;gap:5px;margin-top:9px;font-size:.8125rem;font-weight:600;color:var(--accent);opacity:0;transform:translateX(-4px);transition:opacity .25s,transform .25s var(--ease)}
.stage:hover .stage-link,.stage:focus-within .stage-link{opacity:1;transform:none}
.stage:focus-within .stage-node{border-color:rgba(10,125,87,.45)}
@media (hover:none){.stage-link{opacity:1;transform:none}}
@media (min-width:1060px){
  .spine{grid-template-columns:repeat(7,minmax(0,1fr));gap:0 12px}
  /* align-content:start because the stages are stretched to equal height by the
     outer grid; without it the free space is split between the icon row and the
     text row, so the tallest column's text sits ~11px above its neighbours'. */
  .stage{grid-template-columns:1fr;gap:0;align-content:start}
  .stage-node{width:46px;height:46px;margin-bottom:16px;background:var(--bg-raise)}
  /* The connector must terminate at the FIRST and LAST node centres, not at the
     container edges, or it visibly overshoots stage 07. A node is 46px wide at
     the start of its column, so its centre is 23px in; the last column is
     (100% - 6 gaps) / 7 wide, putting node 07's centre that far from the right. */
  .spine-line{display:block;left:23px;width:calc(100% - (100% - 72px) / 7)}
}
.sl-base{stroke:var(--line-2);stroke-width:1;fill:none;stroke-dasharray:1 5;stroke-linecap:round}
/* non-scaling-stroke puts the dash pattern in screen px, so the gap is sized to
   exceed the widest possible connector — never two pulses in flight at once. */
.sl-flow{stroke:var(--accent);stroke-width:2;fill:none;stroke-linecap:round;stroke-dasharray:44 1200;animation:sweep 7s linear infinite;opacity:.9}
@keyframes sweep{from{stroke-dashoffset:44}to{stroke-dashoffset:-1200}}
.note-row{display:flex;align-items:flex-start;gap:10px;margin-top:clamp(36px,4vw,52px);padding-top:26px;border-top:1px solid var(--line);font-size:.9375rem;color:var(--ink-2);max-width:74ch}
.note-row svg{flex:none;margin-top:3px;color:var(--accent)}

/* ---------- card grid ---------- */
.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(min(100%,304px),1fr))}
.card{
  position:relative;display:flex;flex-direction:column;padding:26px 24px 28px;
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow-sm);
  transition:border-color .35s,transform .35s var(--ease),box-shadow .35s;
}
.card:hover{border-color:var(--line-2);transform:translateY(-3px);box-shadow:var(--shadow-md)}
a.card:hover{border-color:rgba(10,125,87,.35)}
.card-ico{
  display:grid;place-items:center;width:42px;height:42px;margin-bottom:20px;border-radius:11px;
  border:1px solid rgba(10,125,87,.22);background:rgba(10,125,87,.08);
  color:var(--accent);
}
.card h3{margin-bottom:10px}
.card p{font-size:.9375rem;color:var(--ink-2);line-height:1.62}
.card-more{display:inline-flex;align-items:center;gap:6px;margin-top:auto;padding-top:18px;font-size:.875rem;font-weight:600;color:var(--accent)}
.card-more svg{transition:transform .25s var(--ease)}
a.card:hover .card-more svg{transform:translateX(3px)}

/* ---------- three cores ---------- */
.cores{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(min(100%,268px),1fr));margin-bottom:24px}
.core{position:relative;padding:26px 22px;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow-sm);overflow:hidden}
.core::before{content:"";position:absolute;top:0;left:22px;right:22px;height:1px;background:linear-gradient(90deg,var(--accent),rgba(59,91,217,.5),transparent)}
.core-k{display:block;margin-bottom:14px;font-family:var(--mono);font-size:.6875rem;letter-spacing:.1em;color:var(--ink-3);text-transform:uppercase}
.core h3{margin-bottom:9px}
.core p{font-size:.9rem;color:var(--ink-2);line-height:1.6}

/* ---------- invariants panel ---------- */
.inv{
  position:relative;padding:clamp(24px,3vw,34px);border:1px solid var(--line);border-radius:var(--radius);
  background:linear-gradient(135deg,rgba(10,125,87,.05),rgba(255,255,255,0) 46%),var(--surface);
  box-shadow:var(--shadow-sm);
}
.inv-t{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:20px}
.inv-t h3{font-size:1.0625rem}
.inv-t span{font-size:.8125rem;color:var(--ink-3)}
.inv-list{display:grid;gap:13px 30px;grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr))}
.inv-list li{display:flex;gap:11px;align-items:flex-start;font-size:.9375rem;color:var(--ink-2);line-height:1.5}
.inv-list svg{flex:none;margin-top:2px;color:var(--accent)}
/* Single column with a capped measure. CSS columns broke the sentence across a
   column boundary mid-phrase, which reads far worse than a short measure. The
   divider moves to the list above (.inv-ruled), which still spans the panel, so
   capping the note's measure no longer shortens the rule with it. */
.inv-ruled{padding-bottom:19px;border-bottom:1px solid var(--line)}
.inv-note{margin-top:19px;font-size:.8125rem;color:var(--ink-3);max-width:68ch}

/* ---------- split: prose beside a figure ----------
   Top-aligned, not centred: the figure and the prose are rarely the same height,
   and centring the shorter column leaves it floating in the middle of a gap. */
.split{display:grid;gap:clamp(28px,4vw,54px);align-items:start;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.split-wide{grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr)}
@media (max-width:900px){.split,.split-wide{grid-template-columns:1fr;gap:32px}}
.split-alt .split-fig{order:-1}
@media (max-width:900px){.split-alt .split-fig{order:0}}

/* ---------- figure panel (diagrams) ---------- */
.fig{
  position:relative;padding:20px;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);
  box-shadow:var(--shadow-md);
}
.fig-t{display:flex;align-items:center;gap:8px;padding:0 2px 14px;margin-bottom:14px;border-bottom:1px solid var(--line);
  font-size:.6875rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--accent)}
.fig-cap{margin-top:14px;padding-top:13px;border-top:1px solid var(--line);font-size:.8125rem;color:var(--ink-3)}

/* steps: numbered vertical flow with a rail */
.steps{position:relative;display:grid;gap:2px}
.step{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;padding:12px 2px}
.step-n{
  position:relative;z-index:1;display:grid;place-items:center;width:28px;height:28px;border-radius:50%;
  border:1px solid rgba(10,125,87,.38);background:var(--bg-raise);
  font-family:var(--mono);font-size:.75rem;color:var(--accent);
}
/* The rail is drawn on each step but the last, so it never dangles past the end. */
.step:not(:last-child)::before{content:"";position:absolute;left:14px;top:40px;bottom:0;width:1px;background:linear-gradient(var(--line-2),var(--line))}
.step b{display:block;font-size:.9375rem;font-weight:600;letter-spacing:-.012em}
.step span{display:block;margin-top:3px;font-size:.875rem;color:var(--ink-3);line-height:1.55}

/* balanced posting figure */
.tacct{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;align-items:stretch}
.tacct-side{display:grid;gap:8px}
.tacct-k{font-family:var(--mono);font-size:.6875rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-bottom:2px}
.tacct-row{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:9px 11px;border:1px solid var(--line);border-radius:9px;background:var(--surface-2)}
.tacct-row b{font-size:.8125rem;font-weight:600}
.tacct-row span{font-family:var(--mono);font-size:.8125rem;color:var(--accent)}
.tacct-eq{display:grid;place-items:center;width:26px;color:var(--ink-3)}
.tacct-sum{display:flex;justify-content:space-between;gap:10px;margin-top:4px;padding:8px 11px;font-size:.75rem;color:var(--ink-3);border-top:1px dashed var(--line-2)}
.tacct-sum span{font-family:var(--mono);color:var(--ink-2)}
@media (max-width:600px){
  .tacct{grid-template-columns:1fr}
  .tacct-eq{width:auto;height:24px;transform:rotate(90deg)}
}

/* isolation figure */
.iso{display:grid;gap:10px}
.iso-row{display:flex;align-items:center;gap:12px;padding:12px;border:1px solid var(--line);border-radius:11px;background:var(--surface-2)}
.iso-row.deny{border-color:var(--line);background:#f4f5f7}
.iso-tag{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:9px;border:1px solid var(--line-2);color:var(--accent);background:var(--surface)}
.iso-row.deny .iso-tag{color:var(--ink-3)}
.iso-txt{min-width:0}
.iso-txt b{display:block;font-family:var(--mono);font-size:.75rem;font-weight:400;color:var(--ink-2);word-break:break-word}
.iso-txt span{display:block;margin-top:2px;font-size:.8125rem;color:var(--ink-3)}
.iso-out{flex:none;margin-left:auto;font-family:var(--mono);font-size:.75rem;color:var(--accent);white-space:nowrap}
.iso-row.deny .iso-out{color:var(--ink-3)}
@media (max-width:520px){.iso-row{flex-wrap:wrap}.iso-out{margin-left:44px}}

/* ---------- module sections ---------- */
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}
.chip{
  display:inline-flex;align-items:center;gap:7px;padding:7px 13px 7px 10px;border:1px solid var(--line);
  border-radius:99px;background:var(--surface);font-size:.8125rem;color:var(--ink-2);
  transition:border-color .25s,color .25s,background-color .25s,transform .25s var(--ease);
}
.chip:hover{border-color:rgba(10,125,87,.38);color:var(--ink);transform:translateY(-1px)}
.chip i{font-family:var(--mono);font-style:normal;font-size:.6875rem;color:var(--accent);opacity:.9}
.mod{padding:clamp(40px,5vw,64px) 0;border-top:1px solid var(--line)}
.mod-in{display:grid;gap:clamp(24px,4vw,48px);grid-template-columns:minmax(0,1fr) minmax(0,.85fr);align-items:start}
@media (max-width:900px){.mod-in{grid-template-columns:1fr;gap:26px}}
.mod-head{display:flex;align-items:center;gap:14px;margin-bottom:16px}
.mod-ico{
  flex:none;display:grid;place-items:center;width:52px;height:52px;border-radius:14px;
  border:1px solid rgba(10,125,87,.22);background:rgba(10,125,87,.08);
  color:var(--accent);
}
.mod-num{display:block;font-family:var(--mono);font-size:.6875rem;letter-spacing:.1em;color:var(--accent);opacity:.85;margin-bottom:5px}
.mod-caps{display:grid;gap:11px;margin-top:20px}
.mod-caps li{display:flex;gap:11px;align-items:flex-start;font-size:.9375rem;color:var(--ink-2);line-height:1.55}
.mod-caps svg{flex:none;margin-top:3px;color:var(--accent)}
.mod-writes{padding:18px;border:1px solid var(--line);border-radius:13px;background:var(--surface);box-shadow:var(--shadow-sm)}
.mod-writes .core-k{margin-bottom:11px}
.mod-writes ul{display:grid;gap:8px}
.mod-writes li{display:flex;gap:9px;align-items:flex-start;font-size:.8125rem;color:var(--ink-3);line-height:1.5}
.mod-writes li b{color:var(--ink-2);font-weight:600}
.mod-writes svg{flex:none;margin-top:2px;color:var(--accent);opacity:.85}

/* ---------- key/value facts strip ---------- */
.facts{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(min(100%,232px),1fr))}
.fact{padding:22px 20px;border:1px solid var(--line);border-radius:13px;background:var(--surface);box-shadow:var(--shadow-sm)}
.fact b{display:block;margin-bottom:7px;font-size:.9375rem;font-weight:600;letter-spacing:-.014em}
.fact span{font-size:.875rem;color:var(--ink-3);line-height:1.55}

/* ---------- cta band ---------- */
.cta{position:relative;overflow:hidden;padding:clamp(66px,8vw,104px) 0}
.cta-bg{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(64% 100% at 50% 108%,rgba(10,125,87,.10),transparent 66%),radial-gradient(40% 70% at 84% 0%,rgba(59,91,217,.06),transparent 70%)}
.cta-in{position:relative;z-index:1;text-align:center;max-width:660px;margin:0 auto}
.cta-in h2{margin:18px 0;text-wrap:balance}
.cta-in .lead{margin:0 auto;max-width:52ch}
.cta-in .cta-row{justify-content:center;margin-top:30px}
.cta-mail{display:inline-block;margin-top:12px;padding:6px 8px;font-family:var(--mono);font-size:.8125rem;color:var(--ink-3);transition:color .2s}
.cta-mail:hover{color:var(--accent)}

/* ---------- contact card ---------- */
/* Top-aligned: stretching the card to match the taller right column only moved
   the emptiness inside it, leaving a ~200px void between the address and the
   footnote. A card that ends where its content ends reads as finished. */
.contact-grid{display:grid;gap:16px;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);align-items:start}
@media (max-width:860px){.contact-grid{grid-template-columns:1fr}}
.ccard{display:flex;flex-direction:column;padding:clamp(24px,3vw,32px);border:1px solid var(--line);border-radius:var(--radius);
  background:linear-gradient(135deg,rgba(10,125,87,.06),rgba(255,255,255,0) 52%),var(--surface);box-shadow:var(--shadow-md)}
.ccard h2{margin-bottom:14px}
.ccard-fine{margin-top:22px;font-size:.875rem;color:var(--ink-3)}
/* Intentionally stretched by .ccard's column flex: on the contact page this reads
   as the page's primary affordance and gives a full-width tap target. */
.mailto-big{display:inline-flex;align-items:center;gap:10px;margin-top:22px;padding:14px 18px;border:1px solid rgba(10,125,87,.32);
  border-radius:12px;background:rgba(10,125,87,.07);font-family:var(--mono);font-size:.9375rem;color:var(--accent-ink);
  transition:background-color .25s,border-color .25s,transform .25s var(--ease);word-break:break-all}
.mailto-big:hover{background:rgba(10,125,87,.12);border-color:rgba(10,125,87,.5);transform:translateY(-1px)}
.mailto-big svg{flex:none}

/* ---------- footer ---------- */
.site-foot{border-top:1px solid var(--line);padding:clamp(44px,5vw,64px) 0 40px;margin-top:0}
.foot-cols{display:grid;gap:34px 28px;grid-template-columns:minmax(0,1.4fr) repeat(3,minmax(0,1fr))}
@media (max-width:900px){.foot-cols{grid-template-columns:1fr 1fr}}
@media (max-width:520px){.foot-cols{grid-template-columns:1fr;gap:28px}}
.foot-brand .logo{height:24px;margin-bottom:14px}
.foot-brand p{font-size:.875rem;color:var(--ink-3);line-height:1.6;max-width:34ch}
.foot-brand .pill{margin-top:16px}
.foot-h{display:block;margin-bottom:14px;font-size:.75rem;font-weight:600;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-2)}
/* gap drops 10px -> 4px because each link now carries 6px of its own vertical
   padding; the rhythm between rows is unchanged, the hit target is not. */
.foot-list{display:grid;gap:4px}
/* 16px tall bare — the worst target on the site. 28px with padding (SC 2.5.8, AA). */
.foot-list a{display:inline-block;padding:6px 0;font-size:.875rem;color:var(--ink-3);transition:color .2s}
.foot-list a:hover{color:var(--accent)}
.foot-bar{display:flex;flex-wrap:wrap;align-items:center;gap:10px 20px;margin-top:clamp(36px,4vw,52px);padding-top:24px;border-top:1px solid var(--line);font-size:.8125rem;color:var(--ink-3)}
.foot-sep{color:var(--line-2)}
.foot-bar .to-top{margin-left:auto;display:inline-flex;align-items:center;gap:7px;padding:5px 2px;transition:color .2s}
/* Inline links inside running prose (e.g. "the platform page") inherit the
   paragraph's 17px line box. Vertical padding on an inline element does not
   grow the line box, so the target is enlarged without disturbing the leading. */
.prose a:not(.btn):not(.card-more),
.note-row a:not(.btn),
.inv-note a,
.fig-cap a{display:inline-block;padding:4px 0;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;text-decoration-color:rgba(10,125,87,.4);color:var(--accent);transition:text-decoration-color .2s}
.prose a:not(.btn):not(.card-more):hover,
.note-row a:not(.btn):hover,
.inv-note a:hover,
.fig-cap a:hover{text-decoration-color:var(--accent)}
.foot-bar .to-top:hover{color:var(--accent)}
@media (max-width:600px){.foot-bar .to-top{margin-left:0}}

/* ---------- scroll reveal (JS-gated so content is never hidden without JS) --- */
.js .reveal{opacity:0;transform:translateY(16px)}
.js .reveal.in{opacity:1;transform:none;transition:opacity .7s var(--ease) var(--d,0ms),transform .7s var(--ease) var(--d,0ms)}

/* Reduced motion is a request for less movement, not for no feedback. The old
   rule set EVERY duration to .001ms globally, which also flattened hover, focus
   and colour transitions into instant jumps — the interface stopped confirming
   that anything had been pressed or focused.
   What actually causes trouble is the ambient, unprompted, looping motion, so:
     - the four decorative infinite loops are switched off outright,
     - anything that only reveals content resolves to its final state,
     - user-initiated feedback (hover, focus, colour, the nav panel) is KEPT and
       merely shortened, so the interface still answers the pointer. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .rl-flow,.sl-flow,.pill .dot::after,.rail-end{animation:none}
  .rail-end{opacity:1;transform:none}
  .js .reveal{opacity:1;transform:none;transition:none}
  .tcard-foot .tick path{stroke-dashoffset:0;animation:none}
  .stage-link{opacity:1;transform:none}
  .nav.is-open{animation:none}
  /* Kept, not killed: a 90ms change still reads as a response. Transform-based
     lifts are dropped so nothing slides under the pointer. */
  .btn,.card,.chip,.stage-node,.nav a,.foot-list a,.mailto-big{transition-duration:90ms}
  .btn:hover,.card:hover,.chip:hover,.stage:hover .stage-node,.mailto-big:hover{transform:none}
}
