/* Funnel Display — the 2026 identity typeface. Self-hosted: it is not on
   Google Fonts, and the licence (OFL, see assets/fonts/) permits it.
   One variable file covers 300-800, so there is no per-weight request. */
@font-face{
  font-family:'Funnel Display';
  src:url('/assets/fonts/FunnelDisplay-var.a16a9f9810.woff2') format('woff2-variations');
  font-weight:300 800;
  font-style:normal;
  font-display:swap;
}
/* Manrope — the secondary face (2026-09, client-supplied, OFL, see
   assets/fonts/Manrope-OFL.txt): body copy, ledes and subtitles, labels,
   buttons, navigation and forms. Funnel Display stays on every heading, name
   and figure. Static weights, so only the four the site uses are shipped. */
@font-face{
  font-family:'Manrope';
  src:url('/assets/fonts/Manrope-regular.5fef6bc7a1.woff2') format('woff2');
  font-weight:400;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:'Manrope';
  src:url('/assets/fonts/Manrope-medium.0ff5d10275.woff2') format('woff2');
  font-weight:500;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:'Manrope';
  src:url('/assets/fonts/Manrope-semibold.a74bcdad52.woff2') format('woff2');
  font-weight:600;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:'Manrope';
  src:url('/assets/fonts/Manrope-bold.97e10d3729.woff2') format('woff2');
  font-weight:700;
  font-style:normal;
  font-display:swap;
}
:root{
  --font-display:'Funnel Display',ui-sans-serif,system-ui,sans-serif;
  --font-body:'Manrope',ui-sans-serif,system-ui,sans-serif;
  /* code in CMS prose only — no label on the site is set in a monospace */
  --font-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* LABELS — the small type around the content: section kickers, category
     tags, dates, fact terms, form labels, "Read →" links. They were Geist Mono
     in tracked uppercase at 9-12px, across ~60 rules; the client read that as
     noise. They are now the body face, in their real case, at one of three
     sizes with one job each. Colour stays with the component. */
  --lbl-kicker:.9375rem; /* 15px — the label above a section heading */
  --lbl:.8125rem;        /* 13px — tags, dates, terms, form labels, links */
  --lbl-sm:.75rem;       /* 12px — inside a pill or chip only */
  --lbl-w:500;
  --maxw:1280px; --gutter:clamp(20px,5vw,72px);
  --r:14px;
}
/* ---------------------------------------------------------------------------
   TOKENS.
   `--line` / `--line-2` are DECORATIVE hairlines (section rules, card edges,
   textures). Anything that has to be *perceived* — the boundary of a control,
   a focus ring, an error, a connector in a schematic — gets its own token, so
   raising it can never thicken every divider on the site.

   Every token added below resolved in DARK to exactly the value that rule was
   already computing, so the accessibility pass moved the light theme only.
   The 2026 rebrand keeps that structure and changes only the hue: the accent
   role is TGS Purple #634ea0, which at 6.71:1 on white is legal as body text
   outright, so light no longer needs the darkened second accent the old orange
   did. Dark's accent / accent-text / on-card-accent remain one shared value.
   --------------------------------------------------------------------------- */
[data-theme="light"]{
  /* Surfaces step toward TGS Light Purple #DFD7EB rather than a neutral grey,
     so the light theme is built FROM the palette instead of merely avoiding
     orange. Two of the six brand colours were previously used nowhere at all. */
  --bg:#ffffff; --bg-2:#f7f5fb; --bg-3:#efebf6;
  /* the darkest thing on a light page is now TGS Navy, not the old near-black */
  --ink:#101224; --ink-2:#2b2f45; --mut:rgba(43,47,69,.72); --faint:rgba(43,47,69,.42);
  /* measured: the old .12/.07 hairlines composited to 1.16-1.31:1 on every band */
  /* --line-2 is TGS Gray #DCDCDC itself. It is the DECORATIVE hairline, not a
     UI boundary — 1.30:1 on white, which would be illegal for --ctrl-bd (that
     needs 3:1 under 1.4.11) and is exactly right for a rule between rows. */
  --line:rgba(16,18,36,.20); --line-2:#DCDCDC;
  /* the boundary of a UI COMPONENT (1.4.11 wants 3:1). Composites to
     rgb(137,138,141) on #ffffff = 3.44:1, 3.31:1 on the deepest band. */
  --ctrl-bd:rgba(16,18,36,.48);
  /* The focus ring is NOT --accent, and the reason survives the rebrand: any
     ring painted in the accent collapses into the .btn it is meant to be
     ringing (TGS Purple on the purple plate = 1.00:1; TGS Blue on it = 1.06:1)
     — the identical failure the old orange had at 1.00:1.
     Measured over the candidates: #0A0C12 clears the page surfaces easily
     (19.55:1 white / 18.08:1 --bg-2 / 17.42:1 --bg-3 / 16.80:1 --surf-3) but
     reaches only 2.91:1 against the purple .btn 3px away. #000000 clears BOTH
     readings — 21:1 on white and 3.13:1 against the button — and at 2px on
     focus only, it is perceptually identical to the ink. Take the one that
     needs no argument. */
  --focus:#000000;
  --danger:#C4161C;
  /* the prose link's rule is the only affordance it has, so it carries a real
     colour and a real weight instead of a 60%-alpha 1px hairline (1.73:1). */
  --uline:var(--accent-text); --uline-w:1.5px;
  --ok-bd:var(--accent-text);
  --primary:#3953a4; --secondary:#634ea0; --accent:#634ea0; --accent-text:#634ea0;
  /* .btn paints its label straight onto --accent. The old orange was a LIGHT
     plate, so the label was ink — 8.06:1 measured. TGS Purple is a
     dark mid-tone and the same ink measures 2.91:1 on it — a fail. The plate's
     foreground is therefore a token per theme: white on purple = 6.71:1. */
  --on-accent:#ffffff;
  /* the mid stop moves up: the eyebrow sits at ~38% of the hero and the picture
     under it is nearly black there, so even PURE --ink only reached 4.04:1 in
     Arabic on the old ramp. The photograph is still fully readable — this only
     brings the copy band's floor up to the one the display already had. */
  --hero-veil:linear-gradient(180deg,rgba(255,255,255,.20),rgba(255,255,255,.58) 46%,rgba(255,255,255,.94));
  --on-image:#0A0C12;
  /* 2026 homepage redesign — tokens the HOME block below (and only that block)
     draws on. Values are the mockup's dark ones, re-derived for a light surface
     per the plan's colour map (docs/superpowers/plans/2026-09-19-homepage-redesign.md). */
  --lilac:#634ea0; --lilac-soft:rgba(99,78,160,.08); --lilac-line:rgba(99,78,160,.45);
  --surf-card:linear-gradient(168deg,#ffffff,#f7f5fb 58%,#f3f0f9);
  --surf-card-2:linear-gradient(168deg,rgba(16,18,36,.05),rgba(16,18,36,.01));
  --wash:rgba(16,18,36,.035);
  --line-strong:rgba(16,18,36,.3);
  --lilac-sel:rgba(99,78,160,.12);
  --cta-bg:linear-gradient(104deg,rgba(99,78,160,.12),rgba(57,83,164,.07) 55%,rgba(99,78,160,.10));
  --cta-line:rgba(99,78,160,.3);
  --surf-solid:rgba(255,255,255,.96);
  --show-glow:radial-gradient(70% 60% at 60% 45%,rgba(99,78,160,.10),transparent 70%);
  /* client-logo art assumes a white background in both themes — not a themed
     surface token, so it's named for what it is rather than mapped to --surf. */
  --logo-tile-bg:#fff; --logo-tile-ink:#101224;
}
[data-theme="dark"]{
  /* Approach C: the neutrals are REBASED on TGS Navy #101224, not merely
     hue-shifted, so dark reads as the brand rather than as a near-black with
     purple on it. The ramp keeps the old one's step size, just in navy. */
  --bg:#101224; --bg-2:#161930; --bg-3:#1c203c;
  --ink:#F4F7FF; --ink-2:#DFD7EB; --mut:rgba(223,215,235,.70); --faint:rgba(223,215,235,.40);
  /* --mut carried .62 against the old #090B11. Measured off composited pixels
     it was ALREADY failing there — the section .eyebrow on a bloomed band read
     4.23:1 — and rebasing the surfaces on TGS Navy deepened it to 4.01:1
     (the CTA band's eyebrow in Arabic, where the scrim mirrors onto the
     photographic half). .70 is the lowest step that clears every measured
     backdrop: 4.70:1 at that worst case, 5.04:1 on the Success Stories band,
     5.85:1 on --surf-3, 6.24:1 on TGS Navy. The three-step hierarchy survives
     intact — ink 15.60:1, ink-2 10.55:1, mut 5.85:1 on --surf-3. The .78 the
     .panel/.rail-card override uses is unchanged. */
  --line:rgba(255,255,255,.12); --line-2:rgba(255,255,255,.07);
  /* dark republishes each new token at the value its rules already computed —
     this pass is measured against the light theme and must not move dark. */
  --ctrl-bd:rgba(255,255,255,.12);
  --focus:var(--accent);
  --danger:#e5484d;
  --uline:color-mix(in srgb,var(--accent) 60%,transparent); --uline-w:1px;
  --ok-bd:color-mix(in srgb,var(--accent) 45%,var(--line));
  --primary:#7B93E0; --secondary:#9A86D8; --accent:#a195c6; --accent-text:#a195c6;
  /* TGS Purple #634ea0 measures 2.76:1 on TGS Navy — it fails as text AND as a
     UI boundary — so dark carries a lighter tint, exactly as the retired theme
     lightened its orange. #a195c6 is TGS Purple mixed 40% toward white.
     The tint was NOT chosen off the token values. Arithmetic on the ramp alone
     says 35% (#9a8cc1) is enough — 4.68:1 at its worst surface — but the ramp
     is not what the accent actually lands on: the band carries --plane-a and
     --plane-b blooms and a grain layer over it, and rebasing --bg from the old
     #090B11 to TGS Navy lifts every one of those with it. Measured back off
     real composited pixels across ten pages in both locales, 35% lands at
     4.22:1 (.story .ktag), 4.40:1 (.idx .n, /ar/products/) and 4.43:1
     (.band .lead-num) — three AA failures that the token arithmetic could not
     see. 40% clears the same three at 4.67:1 / 4.87:1 / 4.90:1, which is the
     headroom the pre-rebase build had. That is why this is one step lighter
     than a paper derivation would pick. Ink on it is 7.10:1, so the dark .btn
     keeps its ink label.
     --secondary moves for the same reason: #786EEB measured 4.16:1 on
     --surf-3, a fail; #9A86D8 measures 5.39:1. --primary #7B93E0 is 5.64:1
     there. TGS Blue itself is unusable in dark at 2.35:1. */
  --on-accent:#0A0C12;
  --hero-veil:linear-gradient(180deg,rgba(16,18,36,.30),rgba(16,18,36,.55) 50%,rgba(16,18,36,.97));
  --on-image:#F4F7FF;
  /* 2026 homepage redesign — tokens the HOME block below (and only that block)
     draws on. Values are the mockup's own (docs/superpowers/specs/2026-09-19-
     homepage-redesign-design.md §5, plan's colour map). */
  --lilac:#C4B5E8; --lilac-soft:rgba(196,181,232,.12); --lilac-line:rgba(196,181,232,.55);
  --surf-card:linear-gradient(168deg,rgba(255,255,255,.058),rgba(255,255,255,.018) 58%,rgba(255,255,255,.006));
  --surf-card-2:linear-gradient(168deg,rgba(255,255,255,.05),rgba(255,255,255,.01));
  --wash:rgba(255,255,255,.04);
  --line-strong:rgba(255,255,255,.2);
  --lilac-sel:rgba(196,181,232,.16);
  --cta-bg:linear-gradient(104deg,rgba(110,82,180,.30),rgba(123,147,224,.16) 55%,rgba(110,82,180,.22));
  --cta-line:rgba(196,181,232,.28);
  --surf-solid:rgba(22,25,48,.92);
  --show-glow:radial-gradient(70% 60% at 60% 45%,rgba(121,101,183,.22),transparent 70%);
  /* same exception as light: white by design in both themes */
  --logo-tile-bg:#fff; --logo-tile-ink:#101224;
}
*{box-sizing:border-box}
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%}
/* the header is fixed at 74px, so every scroll-into-view the browser performs
   (fragment links, the skip link, sequential focus) has to clear it or the top
   of the target lands underneath. 94px = the bar + the 20px the sticky detail
   rail already uses (`.rail{top:94px}`). */
html{scroll-padding-top:94px; scroll-behavior:smooth}
body{
  margin:0; background:var(--bg); color:var(--ink-2);
  font-family:var(--font-body); font-weight:400; line-height:1.6;
  font-size:clamp(15px,1.05vw,17px); overflow-x:hidden;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  transition:background .5s ease,color .5s ease;
}
img{max-width:100%; display:block}
a{color:inherit; text-decoration:none}
::selection{background:var(--accent); color:#0A0C12}
.wrap{max-width:var(--maxw); margin:0 auto; padding-inline:var(--gutter)}
.eyebrow{font-size:var(--lbl-kicker); font-weight:var(--lbl-w); line-height:1.4; color:var(--on-card-accent)}
.accent{color:var(--accent)}
h1,h2,h3{font-family:var(--font-display); font-weight:420; line-height:1.02; letter-spacing:-.02em; color:var(--ink); margin:0}
.display{font-size:clamp(2.6rem,7.4vw,6.6rem); font-weight:380; line-height:.98}
.h-sec{font-size:clamp(2rem,4.6vw,3.9rem); font-weight:400}
p{margin:0}

/* ---------- header ---------- */
header.site{position:fixed; inset:0 0 auto 0; z-index:50; transition:background .4s ease,border-color .4s ease,backdrop-filter .4s}
header.site .bar{display:flex; align-items:center; gap:24px; height:74px; border-bottom:1px solid transparent}
header.site.scrolled{background:color-mix(in srgb,var(--bg) 78%,transparent); backdrop-filter:blur(14px)}
header.site.scrolled .bar{border-color:var(--line-2)}
.brand{display:flex; align-items:center; gap:12px}
/* The TGS mark is a transparent SVG that reads on both themes, so it needs no
   white backing plate. Client logos still use .wall-plate/.plate-fig, which DO
   need one — they are multi-colour marks that vanish on the dark surface. */
.logo-chip{display:flex; padding:0}
.logo-chip img{height:32px; width:auto}
nav.main{margin-inline-start:auto; display:flex; gap:26px}
nav.main a{position:relative; padding-block:7px; font-size:.86rem; color:var(--ink-2); opacity:.82; transition:opacity .2s,color .2s}
nav.main a:hover{opacity:1; color:var(--ink)}
/* The tabs scroll, so one of them is always "where you are". `currentColor` and
   not an accent token on purpose: over the hero the header commits to
   light-on-dark and stops following the theme, so any fixed colour here would
   be the one thing in the bar that ignores that. */
nav.main a.is-current{opacity:1; color:var(--ink)}
nav.main a.is-current::after{content:""; position:absolute; inset-inline:0; bottom:0;
  height:1.5px; border-radius:2px; background:currentColor}
.mnav-links a.is-current{color:var(--accent-text)}
.h-actions{display:flex; align-items:center; gap:14px}
.tt{appearance:none; border:1px solid var(--ctrl-bd); background:transparent; color:var(--ink); width:38px; height:38px; border-radius:50%; cursor:pointer; display:grid; place-items:center; transition:border-color .2s,transform .2s}
.tt:hover{border-color:var(--accent-text); transform:translateY(-1px)}
.tt svg{width:17px; height:17px}
.btn{display:inline-flex; align-items:center; gap:8px; font-size:.84rem; font-weight:500; padding:11px 20px; border-radius:999px; background:var(--accent); color:var(--on-accent); border:1px solid var(--accent); transition:transform .2s,box-shadow .25s}
.btn:hover{transform:translateY(-1px); box-shadow:0 8px 24px -8px var(--accent)}
.btn.ghost{background:transparent; color:var(--ink); border-color:var(--ctrl-bd)}
.btn.ghost:hover{border-color:var(--ink); box-shadow:none}
@media(max-width:1100px){ nav.main{display:none} }
@media(max-width:1100px){ .h-actions{display:none} }

/* ---- the bar over the hero photograph ------------------------------------
   ONE cause behind six measured failures (nav links, brand tag, language
   switch, theme toggle, hamburger glyph and every one of their rings): until
   `.scrolled` lands the header has no surface of its own, so in LIGHT theme it
   paints near-black ink straight onto a photograph — 1.29-1.92:1 across the
   whole bar, against 4.5:1 for the text and 3:1 for the controls. The same bar
   measures 7-8:1 the moment it is scrolled, and 8:1 on every inner page, so the
   ink ramp is not the defect: the transparent-over-picture state is.

   Over that band the chrome therefore stops following the theme and commits to
   light-on-dark, on a scrim that bounds the backdrop no matter which photograph
   the CMS serves (the hero image is CMS-supplied — the Arabic one already
   carries an rgb(102,107,144) patch under the nav, so a token-only fix would
   re-break on the next upload). It hands the tokens straight back when
   `.scrolled` paints var(--bg) behind the bar.

   Scoped to `> .bar` and not to header.site because `.mnav` is ALSO a child of
   the header and its panel is background:var(--bg) — white ink inherited in
   there would render the open mobile menu white-on-white. Scoped to
   [data-theme="light"] because dark's own veil already reaches .97. */
html[data-theme="light"] body:has(.hero) header.site::before{
  content:""; position:absolute; inset:0 0 auto 0; height:132px; z-index:-1; pointer-events:none;
  background:linear-gradient(180deg,rgba(9,11,17,.58) 0,rgba(9,11,17,.34) 56%,rgba(9,11,17,0) 100%);
  opacity:1; transition:opacity .4s ease;
}
html[data-theme="light"] body:has(.hero) header.site.scrolled::before{opacity:0}
html[data-theme="light"] body:has(.hero) header.site:not(.scrolled) > .bar{
  --ink:#FFFFFF; --ink-2:#F2F5FC; --mut:rgba(242,245,252,.84);
  --line:rgba(255,255,255,.46); --ctrl-bd:rgba(255,255,255,.46);
}

/* ---------- hero ---------- */
.hero{position:relative; min-height:92svh; display:flex; align-items:flex-end; padding-bottom:clamp(48px,8vh,96px); overflow:hidden}
.hero .bg{position:absolute; inset:0; z-index:0}
.hero .bg img{width:100%; height:120%; object-fit:cover; will-change:transform}
.hero .bg::after{content:""; position:absolute; inset:0; background:var(--hero-veil)}
/* Centred: heading, lede and buttons all sit on the page's centre line.
   `text-align` alone would not do it — the heading and lede are capped at a
   readable measure (16ch / 52ch), and a narrow block inside a wide one still
   sits at the start edge until its own margins are auto. */
.hero .inner{position:relative; z-index:1; width:100%; text-align:center}
.hero .display{color:var(--on-image); max-width:16ch; margin-inline:auto}
/* The accent word sits over the brightest part of the picture. This override
   long predates the rebrand and outlives it: the old orange measured 2.17:1
   avg / 1.41:1 worst there (needs 3:1 at 105px), and TGS Purple is DARKER
   still — 6.71:1 on white but only 1.9:1 on the bright hero pixels measured
   below — so no veil that leaves the photograph visible can lift it. The
   italic + weight delta carries the accent instead. Dark's accent sits on a
   .97 navy veil and clears it comfortably. */
/* Same root cause one line up: over a photograph the light theme's TRANSLUCENT
   inks have no headroom. --mut (70% alpha, 11.5px) measured 2.37-2.57:1 on the
   bare hero. Inside the hero light republishes it at full strength — same hue,
   no alpha. Dark is untouched. */
[data-theme="light"] .hero{--mut:var(--ink)}
/* H2: the sub-headline steps back from the display line. Was
   clamp(1rem,1.5vw,1.25rem) at the body weight of 400 — 20px/400 at 1440,
   16px/400 at 390. Every term is scaled by the same factor so the reduction
   holds across the ramp rather than only at one end: 16.6px at 1440 (-17%)
   and 13.6px at 390 (-15%), both inside the 15-20% the note asks for. The
   weight drops 400 -> 350 on Funnel Display's variable axis (300-800). RTL
   re-heavies it below: IBM Plex Sans Arabic ships static instances, so 350
   snaps to 300 and Arabic at 13.6px over the video goes too thin. */
.hero .lede{margin-top:26px; max-width:52ch; margin-inline:auto; font-size:clamp(.85rem,1.25vw,1.04rem); font-weight:350; color:var(--ink-2)}
[dir="rtl"] .hero .lede{font-weight:400}
[data-theme="dark"] .hero .lede{color:#cdd6e6}
.hero-cta{margin-top:34px; display:flex; flex-wrap:wrap; gap:14px; justify-content:center}
.hero .eyebrow{display:inline-flex; gap:12px; align-items:center; margin-bottom:22px}
.hero .eyebrow::before{content:""; width:34px; height:1px; background:var(--accent)}

/* ---------- generic section ---------- */
section{padding-block:clamp(72px,12vh,150px)}
.lead-num{font-size:var(--lbl-kicker); font-weight:var(--lbl-w); font-variant-numeric:tabular-nums; color:var(--accent-text)}
.sec-head{display:grid; grid-template-columns:1fr; gap:18px; margin-bottom:clamp(40px,6vw,72px)}
.sec-head .k{display:flex; align-items:center; gap:14px}
.sec-head .k .eyebrow{display:inline-flex; align-items:center; gap:12px}

/* about */
/* A5 — the picture and the copy are one even pair, 50/50. They used to sit at
   1.05fr/.95fr here and 1.02fr/.98fr in both scoped overrides below, which at
   1440 put the image 22px wider than the copy — enough to read as a mistake
   rather than a decision. `minmax(0,…)` keeps the long lede from pushing the
   copy column past its share. */
.about-grid{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(32px,5vw,80px); align-items:center}
.about-grid figure{margin:0; border-radius:var(--r); overflow:hidden; border:1px solid var(--line-2)}
.about-grid figure img{width:100%; aspect-ratio:4/3; object-fit:cover}
.about-copy p+p{margin-top:20px}
/* the lede is followed by a bare <p> on the homepage (handled by `p+p`) but by
   a .prose block on /about, which that rule cannot reach */
.about-copy .big + .prose{margin-block-start:22px}
.about-copy .big{font-family:var(--font-display); font-weight:380; font-size:clamp(1.5rem,2.4vw,2.1rem); line-height:1.18; color:var(--ink); letter-spacing:-.01em}
@media(max-width:860px){ .about-grid{grid-template-columns:1fr} }

/* vmv */
.vmv{display:grid; gap:0; border-top:1px solid var(--line)}
.vmv .row{display:grid; grid-template-columns:auto 1fr; gap:clamp(20px,5vw,64px); padding-block:clamp(28px,4vw,46px); border-bottom:1px solid var(--line); align-items:start}
.vmv .row .lab{font-size:var(--lbl-kicker); font-weight:var(--lbl-w); color:var(--mut); padding-top:.5em; min-width:96px}
.vmv .row q{font-family:var(--font-display); font-weight:360; font-style:normal; font-size:clamp(1.4rem,3vw,2.4rem); line-height:1.16; color:var(--ink); letter-spacing:-.015em; quotes:none}
.vmv .row q::before{content:"“"; color:var(--accent)}
.vmv .row q::after{content:"”"; color:var(--accent)}

/* index list (services / products) */
.idx{border-top:1px solid var(--line)}
.idx a, .idx .item{display:grid; grid-template-columns:auto 1fr auto; gap:clamp(16px,3vw,44px); align-items:baseline; padding-block:clamp(20px,2.6vw,30px); border-bottom:1px solid var(--line); transition:background .3s,padding-inline-start .3s}
.idx .item:hover{background:var(--bg-2); padding-inline-start:14px}
.idx .n{font-size:var(--lbl); font-weight:var(--lbl-w); font-variant-numeric:tabular-nums; color:var(--accent-text)}
.idx .nm{font-family:var(--font-display); font-weight:400; font-size:clamp(1.3rem,2.6vw,2rem); color:var(--ink); letter-spacing:-.01em}
.idx .ds{color:var(--mut); max-width:46ch; justify-self:start; font-size:.96rem}
/* the row-affordance arrow was --faint (2.35:1 light / 2.75:1 dark) — the one
   glyph that says "this row is clickable" was invisible until hover. --mut
   clears AA; the hover promotion to --on-card-accent still reads as a delta. */
.idx .ar{color:var(--mut); transition:color .25s,transform .25s}
.idx .item:hover .ar{color:var(--accent); transform:translateX(4px)}
/* (the responsive reflow of .idx lives in the MERGE LAYER below — it needs the
   explicit grid-template-areas track, which the base 3-column grid predates) */

/* industries */
.ind{display:grid; grid-template-columns:repeat(3,1fr); gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r); overflow:hidden}
.ind .cell{background:var(--bg); padding:clamp(26px,3vw,42px); min-height:300px; display:flex; flex-direction:column; gap:14px}
.ind .cell .ic{width:40px; height:40px; border-radius:10px; display:grid; place-items:center; background:color-mix(in srgb,var(--primary) 14%,transparent); color:var(--primary); margin-bottom:8px}
.ind .cell h3{font-size:clamp(1.3rem,2vw,1.7rem)}
.ind .cell p{color:var(--mut); font-size:.96rem; margin-top:auto}
@media(max-width:820px){ .ind{grid-template-columns:1fr} }

/* stories */
.stories{display:grid; grid-template-columns:1fr 1fr; gap:clamp(20px,2.4vw,30px)}
.story{border:1px solid var(--line); border-radius:var(--r); padding:clamp(24px,2.6vw,38px); background:var(--bg); display:flex; flex-direction:column; gap:18px; transition:border-color .3s,transform .3s}
.story:hover{border-color:color-mix(in srgb,var(--accent) 55%,var(--line)); transform:translateY(-3px)}
.story .top{display:flex; align-items:center; gap:16px}
.story .chip{background:#fff; border-radius:10px; padding:10px 12px; width:84px; height:54px; display:grid; place-items:center; flex:none}
.story .chip img{max-height:34px; width:auto; object-fit:contain}
.story .kname{font-family:var(--font-display); font-weight:420; font-size:1.5rem; color:var(--ink)}
.story .ktag{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--accent-text)}
.story p{color:var(--mut); font-size:.97rem}
@media(max-width:720px){ .stories{grid-template-columns:1fr} }

/* journey */
.journey{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(20px,3vw,48px)}
.journey .step{border-top:2px solid var(--line); padding-top:22px}
.journey .yr{font-family:var(--font-display); font-weight:400; font-size:clamp(2.4rem,5vw,4rem); color:var(--ink); line-height:1}
.journey .step:first-child .yr{color:var(--primary)}
.journey .step p{color:var(--mut); margin-top:14px; font-size:.96rem; max-width:34ch}
@media(max-width:760px){ .journey{grid-template-columns:1fr; gap:0} .journey .step{padding-block:26px; border-top:1px solid var(--line)} }

/* insights */
.insights{display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(22px,3vw,40px)}
.insight{display:grid; grid-template-columns:170px 1fr; gap:22px; align-items:center; padding-block:8px}
.insight figure{margin:0; border-radius:12px; overflow:hidden; border:1px solid var(--line-2)}
.insight figure img{aspect-ratio:4/3; object-fit:cover; width:100%; height:auto; transition:transform .8s cubic-bezier(.16,1,.3,1)}
.insight:hover figure img{transform:scale(1.05)}
.insight .kdate{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--accent-text)}
.insight h3{font-size:1.18rem; font-weight:440; line-height:1.18; margin-top:8px; letter-spacing:-.01em}

/* careers */
.careers .role{font-family:var(--font-display); font-size:clamp(1.3rem,2.6vw,2rem); color:var(--ink)}

/* contact */
.contact{background:var(--bg-2); border-top:1px solid var(--line)}
.contact .grid{display:grid; grid-template-columns:1.2fr .8fr; gap:clamp(36px,6vw,90px); align-items:end}
.contact .display{color:var(--ink); max-width:14ch}
.contact dl{display:grid; gap:18px; margin:0}
.contact dt{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut); margin-bottom:4px}
.contact dd{margin:0; font-size:1.05rem; color:var(--ink)}
.contact dd a:hover{color:var(--accent-text)}
@media(max-width:820px){ .contact .grid{grid-template-columns:1fr} }

footer.site{border-top:1px solid var(--line); padding-block:34px; background:var(--bg-2)}
footer.site .row{display:flex; flex-wrap:wrap; gap:20px; align-items:center; justify-content:space-between}
footer.site .soc{display:flex; gap:8px}
footer.site .soc a{width:38px; height:38px; border:1px solid var(--ctrl-bd); border-radius:50%; display:grid; place-items:center; color:var(--ink-2); transition:border-color .2s,color .2s,transform .2s}
footer.site .soc a:hover{border-color:var(--accent-text); color:var(--accent-text); transform:translateY(-2px)}
footer.site .cr{font-size:var(--lbl); color:var(--mut)}
footer.site .row{justify-content:center}
footer.site .foot-brand .soc{margin-top:20px; display:flex; gap:14px}

/* signature motion: quiet rise + clip reveal */
.reveal{opacity:0; transform:translateY(26px); transition:opacity .9s cubic-bezier(.16,1,.3,1),transform .9s cubic-bezier(.16,1,.3,1)}
.reveal.in{opacity:1; transform:none}
/* A `.reveal` nested inside another `.reveal` must never be left hidden. The
   observer (threshold .16, rootMargin -8%) can be satisfied by the ancestor
   while the descendant never crosses its own threshold — which is exactly how
   /about's stat rail rendered at opacity 0 while the copy above it was visible.
   The descendant keeps its own entrance whenever its observer fires first; this
   only guarantees it cannot end up invisible inside something already shown. */
.reveal.in .reveal{opacity:1; transform:none}
/* Sequential focus can move INTO a `.reveal` that the IntersectionObserver has
   not resolved: Tab scrolls the target only minimally into view, which does not
   satisfy `threshold:0.16` + the -8% bottom margin, so the observer never fires
   and the focused element stays at `opacity:0`. Reproduced at tab stops 20/29
   on `/` and stop 20 on `/ar/`. Focus reveals it immediately — no transition,
   because the element is already the thing the user is looking for. The
   `<noscript>` twin of this rule lives in base.njk. */
.reveal:focus-within{opacity:1; transform:none; transition:none}
.reveal[data-d="1"]{transition-delay:.08s}
.reveal[data-d="2"]{transition-delay:.16s}
.reveal[data-d="3"]{transition-delay:.24s}
/* Off-screen but RTL-safe (a −9999px left offset overflows in RTL documents). */
.skip{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0}
.skip:focus{width:auto; height:auto; margin:0; clip:auto; clip-path:none; left:16px; top:16px; z-index:99; background:var(--bg); color:var(--ink); padding:10px 16px; border:1px solid var(--focus); border-radius:8px}
/* The general-purpose visually-hidden utility. `.skip` above and `.hp` (the
   form honeypot) each carry the same geometry, but both are single-purpose —
   `.skip` un-hides itself on focus and `.hp` is a spam trap — so neither can be
   reused for text that must stay hidden. Same RTL-safe technique. */
.vh{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
    clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0}
a:focus-visible,button:focus-visible{outline:2px solid var(--focus); outline-offset:3px; border-radius:6px}
/* =========================================================================
   PAGE SYSTEM — inner pages, detail layout, prose, forms, mobile nav, footer
   (additive; reuses the tokens/primitives above — desktop landing unchanged)
   ========================================================================= */

/* (the inner-page top offset, the detail masthead, the covers and the related
   strip all live in the INNER PAGES section at the end of this file — they are
   band components now, not one-off page rules.) */

/* ---------- prose (rendered markdown) ---------- */
.prose{max-width:72ch; color:var(--ink-2); font-size:clamp(1rem,1.12vw,1.12rem); line-height:1.72}
.prose>*:first-child{margin-top:0}
.prose p{margin:0 0 1.15em}
.prose h2{font-family:var(--font-display); font-weight:420; font-size:clamp(1.5rem,2.6vw,2.1rem); line-height:1.12; letter-spacing:-.015em; color:var(--ink); margin:1.9em 0 .55em}
.prose h3{font-family:var(--font-display); font-weight:440; font-size:clamp(1.25rem,2vw,1.6rem); line-height:1.15; color:var(--ink); margin:1.6em 0 .5em}
.prose h4{font-family:var(--font-body); font-weight:600; font-size:1.05rem; color:var(--ink); margin:1.5em 0 .4em; letter-spacing:.01em}
.prose ul,.prose ol{margin:0 0 1.15em; padding-inline-start:1.3em}
.prose li{margin:0 0 .5em}
.prose li::marker{color:var(--accent-text)}
.prose a{color:var(--ink); text-decoration:underline; text-decoration-color:var(--uline); text-underline-offset:3px; text-decoration-thickness:var(--uline-w); transition:text-decoration-color .2s,color .2s}
.prose a:hover{color:var(--accent-text); text-decoration-color:var(--accent)}
.prose strong{color:var(--ink); font-weight:600}
.prose em{font-style:italic}
.prose blockquote{margin:1.6em 0; padding-block:6px; padding-inline-start:24px; border-inline-start:2px solid var(--accent); font-family:var(--font-display); font-weight:360; font-size:clamp(1.2rem,2vw,1.55rem); line-height:1.28; color:var(--ink)}
.prose hr{border:0; height:1px; background:var(--line); margin:2.4em 0}
.prose code{font-family:var(--font-mono); font-size:.9em; background:var(--bg-2); border:1px solid var(--line-2); border-radius:6px; padding:.1em .4em}
.prose pre{background:var(--bg-2); border:1px solid var(--line-2); border-radius:var(--r); padding:18px 20px; overflow:auto; margin:0 0 1.15em}
.prose pre code{background:none; border:0; padding:0}
.prose img{border-radius:12px; border:1px solid var(--line-2); margin:1.4em 0}
/* CMS article bodies can contain real markdown tables (ERP/tooling comparisons).
   Without these they render at browser defaults and a wide table pushes the whole
   page sideways on mobile — hence the scroll container rather than a min-width. */
.prose table{width:100%; border-collapse:collapse; margin:1.5em 0; font-size:.94rem;
  display:block; overflow-x:auto; -webkit-overflow-scrolling:touch}
.prose thead{background:var(--surf-2,var(--bg-2))}
.prose th,.prose td{border:1px solid var(--line); padding:10px 14px; text-align:start; vertical-align:top}
.prose th{font-size:.875rem; color:var(--ink); font-weight:600; white-space:nowrap}
.prose td{color:var(--ink-2)}
.prose tbody tr:nth-child(even){background:color-mix(in srgb,var(--ink) 3%,transparent)}
.prose-tight{font-size:clamp(1rem,1.05vw,1.08rem)}
.prose-tight h2{margin-top:1.3em}

/* ---------- about / careers: key factors ---------- */
.factor{display:flex; flex-direction:column; gap:14px}
.factor-n{font-size:var(--lbl); font-weight:var(--lbl-w); font-variant-numeric:tabular-nums; color:var(--accent-text)}
.factor p{color:var(--ink); font-family:var(--font-display); font-weight:400; font-size:clamp(1.1rem,1.6vw,1.35rem); line-height:1.24; letter-spacing:-.01em; margin-top:auto}

/* ---------- list card extras ---------- */
.ins-sum{color:var(--mut); font-size:.92rem; margin-top:8px; max-width:48ch}
.story-more{margin-top:auto; font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--accent-text)}
.empty-note{color:var(--mut); font-size:1.05rem}
.empty-note a{color:var(--accent-text)}

/* ---------- 404 ---------- */
.notfound .display{max-width:14ch; margin-top:14px}
.notfound .lede{margin-top:22px; max-width:44ch; font-size:clamp(1rem,1.5vw,1.2rem); color:var(--ink-2)}
.notfound .hero-cta{margin-top:32px}

/* ---------- contact + apply layouts ---------- */
.contact-grid,.apply-grid{display:grid; grid-template-columns:.9fr 1.1fr; gap:clamp(20px,2.4vw,34px); align-items:start}
.apply-grid{grid-template-columns:.8fr 1.2fr}
.contact-info dl{display:grid; gap:16px; margin:0 0 clamp(20px,2.4vw,26px)}
.contact-info dt{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut); margin-bottom:4px}
.contact-info dd{margin:0; font-size:1.05rem; color:var(--ink)}
.contact-info dd a:hover{color:var(--accent-text)}
.contact-info .soc{display:flex; gap:8px}
.contact-info .soc a{width:38px; height:38px; border:1px solid var(--ctrl-bd); border-radius:50%; display:grid; place-items:center; color:var(--ink-2); transition:border-color .2s,color .2s,transform .2s}
.contact-info .soc a:hover{border-color:var(--accent-text); color:var(--accent-text); transform:translateY(-2px)}
.apply-meta dl{display:grid; gap:14px; margin:0 0 22px; border-top:1px solid var(--line); padding-top:22px}
.apply-meta dt{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut); margin-bottom:3px}
.apply-meta dd{margin:0; color:var(--ink); font-size:1.02rem}
.apply-meta .apply-note{color:var(--mut); font-size:.95rem; line-height:1.6}
.apply-meta .apply-note a{color:var(--accent-text)}
@media(max-width:820px){ .contact-grid,.apply-grid{grid-template-columns:1fr} }

/* ---------- forms ---------- */
.lead-form{display:grid; gap:20px}
.field{display:grid; gap:8px}
.field-row{display:grid; grid-template-columns:1fr 1fr; gap:20px; align-items:start}
.lead-form label{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--ink-2)}
/* the asterisk is the only VISUAL required cue, so it has to be legible. Under
   the old orange, raw --accent measured 2.42:1 on the white form panel and this
   rule reached for the darkest text token. TGS Purple needs no such rescue —
   --accent, --accent-text and --on-card-accent are one value in each theme now
   — but the token stays, because the panel is white at 1440 and sits on the
   tinted band surface on a phone, and --on-card-accent is the one certified
   against both. */
.lead-form .req{color:var(--on-card-accent)}
/* "(optional)" is inside the <label> and is announced — content, not decoration.
   --faint measured 2.42:1 light / 2.83:1 dark, so it steps back to --mut. */
.lead-form .opt{font-weight:400; color:var(--mut)}
.lead-form input,.lead-form textarea{width:100%; font-family:var(--font-body); font-size:1rem; color:var(--ink); background:var(--bg); border:1px solid var(--ctrl-bd); border-radius:10px; padding:13px 15px; transition:border-color .2s,box-shadow .2s}
.lead-form textarea{resize:vertical; min-height:130px; line-height:1.55}
.lead-form input::placeholder,.lead-form textarea::placeholder{color:var(--faint)}
.lead-form input:focus,.lead-form textarea:focus{outline:none; border-color:var(--focus); box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 26%,transparent)}
.lead-form [aria-invalid="true"]{border-color:var(--danger)}
.lead-form [aria-invalid="true"]:focus{box-shadow:0 0 0 3px color-mix(in srgb,var(--danger) 26%,transparent)}
.field-err{color:var(--danger); font-size:.85rem; margin:0}
.form-actions{display:flex; align-items:center; gap:16px; flex-wrap:wrap}
.form-actions .btn{cursor:pointer}
.form-actions .btn[disabled]{opacity:.6; cursor:progress; box-shadow:none; transform:none}
.form-status{margin:0; font-size:.92rem; color:var(--mut)}
.form-status.error{color:var(--danger)}
.form-success{border:1px solid var(--ok-bd); border-radius:var(--r); padding:clamp(22px,3vw,32px); background:color-mix(in srgb,var(--accent) 7%,var(--bg))}
.form-success .fs-title{font-family:var(--font-display); font-weight:420; font-size:clamp(1.3rem,2vw,1.7rem); color:var(--ink); outline:none}
.form-success .fs-text{color:var(--mut); margin-top:10px}
.hp{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0}
@media(max-width:520px){ .field-row{grid-template-columns:1fr} }

/* ---------- header hamburger + mobile nav ---------- */
.nav-toggle{display:none; margin-inline-start:auto; appearance:none; border:1px solid var(--ctrl-bd); background:transparent; width:40px; height:40px; border-radius:10px; cursor:pointer; place-items:center; color:var(--ink); transition:border-color .2s}
.nav-toggle:hover{border-color:var(--accent-text)}
.nav-toggle-bars{display:grid; gap:4px; width:18px}
.nav-toggle-bars i{display:block; height:1.6px; background:currentColor; border-radius:2px}
@media(max-width:1100px){ .nav-toggle{display:grid; width:44px; height:44px} }
.mnav{position:fixed; inset:0; z-index:60}
.mnav[hidden]{display:none}
/* Only the open menu captures pointer events — a closing/closed overlay can never
   linger and eat page clicks (defends against a missed transitionend). */
.mnav:not(.open){pointer-events:none}
.mnav.open{pointer-events:auto}
.mnav-backdrop{position:absolute; inset:0; background:color-mix(in srgb,#000 55%,transparent); opacity:0; transition:opacity .3s ease}
.mnav.open .mnav-backdrop{opacity:1}
.mnav-panel{position:absolute; top:0; right:0; height:100%; width:min(400px,88vw); background:var(--bg); border-left:1px solid var(--line); padding:22px clamp(22px,6vw,34px) 34px; display:flex; flex-direction:column; overflow-y:auto; transform:translateX(100%); transition:transform .36s cubic-bezier(.16,1,.3,1)}
.mnav.open .mnav-panel{transform:none}
.mnav-top{display:flex; align-items:center; justify-content:space-between; margin-bottom:24px}
.mnav-x{width:38px; height:38px}
.mnav-links{list-style:none; margin:0; padding:0; display:grid; gap:2px; border-top:1px solid var(--line)}
.mnav-links a{display:block; padding:16px 2px; border-bottom:1px solid var(--line); font-family:var(--font-display); font-weight:400; font-size:1.5rem; color:var(--ink); letter-spacing:-.01em; transition:color .2s,padding-inline-start .2s}
.mnav-links a:hover,.mnav-links a:focus-visible{color:var(--accent-text); padding-inline-start:8px}
.mnav-foot{display:flex; align-items:center; gap:14px; margin-top:auto; padding-top:26px}
.mnav-foot .btn{flex:1; justify-content:center}
@media(max-width:880px){
  .mnav-x{width:44px; height:44px}
  /* below the nav breakpoint the pointer is a finger: every control the finger
     can reach gets a 44px box. The visual weight is unchanged — these are the
     hit boxes growing, not the type. */
  .mnav-foot .tt{width:44px; height:44px}
  .contact-info .soc a{width:44px; height:44px}
  .btn{min-height:44px}
  /* the related section's heading link is a 37px inline box; padding on an
     inline element grows the hit target without touching the line rhythm. */
  .related h2 a{padding-block:6px}
}

/* ---------- footer columns ---------- */
/* brand + Company/Explore/More — the Vistay column was removed per the 2026
   redesign (mockup footer has brand + 3 link columns, no product column). */
.foot-cols{display:grid; grid-template-columns:minmax(220px,1.8fr) repeat(3,minmax(120px,1fr)); gap:clamp(28px,4vw,56px); padding-bottom:34px; margin-bottom:28px; border-bottom:1px solid var(--line-2)}
.foot-brand .logo-chip{display:inline-flex; padding:0}
.foot-brand .logo-chip img{height:26px; width:auto}
.foot-tag{margin-top:16px; max-width:30ch; color:var(--mut); font-size:.95rem}
.foot-contact{display:grid; gap:10px; margin:20px 0 0}
.foot-contact dt{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--faint); margin-bottom:2px}
.foot-contact dd{margin:0; font-size:.94rem; color:var(--ink-2)}
.foot-contact dd a:hover{color:var(--accent-text)}
.foot-h{font-size:var(--lbl); font-weight:600; color:var(--mut)}
.foot-col ul{list-style:none; margin:16px 0 0; padding:0; display:grid; gap:11px}
.foot-col a{font-size:.94rem; color:var(--ink-2); opacity:.84; transition:opacity .2s,color .2s}
.foot-col a:hover{opacity:1; color:var(--accent-text)}
@media(max-width:820px){ .foot-cols{grid-template-columns:1fr 1fr; gap:32px} .foot-brand{grid-column:1/-1} }
@media(max-width:520px){ .foot-cols{grid-template-columns:1fr} }

/* (MOTION HYGIENE — the single `prefers-reduced-motion` block lives at the very
   end of this file, after every component and RTL rule, so nothing can outrank
   it on source order.) */

/* =========================================================================
   LANGUAGE SWITCHER (both directions)
   ========================================================================= */
.lang-switch{display:inline-flex; align-items:center; height:38px; padding:0 14px; border:1px solid var(--ctrl-bd); border-radius:999px; font-family:var(--font-body); font-size:.8rem; line-height:1; letter-spacing:normal; text-transform:none; color:var(--ink-2); opacity:.9; white-space:nowrap; transition:border-color .2s,color .2s,transform .2s}
.lang-switch:hover{border-color:var(--accent-text); color:var(--ink); opacity:1; transform:translateY(-1px)}
.mnav-langs{display:flex; gap:8px; margin-top:22px}
.mnav-lang{flex:1; text-align:center; padding:11px 12px; border:1px solid var(--ctrl-bd); border-radius:10px; font-family:var(--font-body); font-size:.92rem; letter-spacing:normal; color:var(--ink-2); transition:border-color .2s,color .2s,background .2s}
.mnav-lang:hover{border-color:var(--accent-text); color:var(--ink)}
.mnav-lang.is-current{border-color:var(--on-card-accent); color:var(--ink); background:color-mix(in srgb,var(--accent) 8%,transparent)}
/* Arabic label rendered inside the LTR chrome must never inherit Latin tracking */
[lang="ar"]{letter-spacing:normal}

/* =========================================================================
   DESIGN SYSTEM — "D-MERGED"
   ONE token system for every band below the credibility strip: one surface
   ramp, one elevation scale, one radius scale, one border token, one easing,
   ONE hover gesture and one band rhythm. The header, the hero and the
   credibility strip above are untouched.
   ========================================================================= */

/* ---- ONE radius / motion / rhythm scale --------------------------------- */
:root{
  --r-sm:10px; --r-lg:20px;
  --band-pad:clamp(48px,5vw,64px);
  --e-out:cubic-bezier(.16,1,.3,1);
  /* ONE hover gesture: one duration, one easing, one lift — used by every
     card, row, tile and schematic on the page. No second opinion anywhere. */
  --t-hover:.4s; --lift:-3px;
  /* per-image exposure trim. The duotone is only a system if every photograph
     enters it at the same luminance, so each picture carries its own
     multiplier (set inline from `_data/art.js`) and they all land on ~95
     (light) / ~55 (dark). */
  --ph-b:1; --ph-b-l:1; --ph-c:1; --ph-c-l:1;
  --dia-w:200px;
  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  /* the scrim is measured in PIXELS from the top of the text block, so the
     opaque stop always lands under the copy no matter how tall the card grows.
     Text over photography is therefore deterministically AA in both themes. */
  --scrim:linear-gradient(180deg,
    color-mix(in srgb,var(--card-base) 0%,transparent) 0,
    color-mix(in srgb,var(--card-base) 22%,transparent) 16px,
    color-mix(in srgb,var(--card-base) 68%,transparent) 38px,
    color-mix(in srgb,var(--card-base) 94%,transparent) 54px,
    var(--card-base) 68px);
  --scrim-pad:76px;
}
[data-theme="dark"]{
  /* the measurable surface ramp */
  --surf-1:#101224; --surf-2:#13162b; --surf-3:#181c35;
  --grain-a:.055;
  /* W3 — the .tex-plane bands are EXCLUDED from the G3 bloom below (a second
     wash over a certified one would over-light them), so their lighting has to
     come from these two planes instead. That matters because /work/ and
     /contact/ are entirely .tex-plane: under the exclusion alone they received
     no lighting at all, which is precisely the page the designer asked for it
     on.
     They now use --bloom (raw TGS Purple #634ea0) rather than --primary and
     --secondary. Those two are the dark theme's LIGHTENED tints, picked so they
     can be read as text; spent as a wash they go to luminance and arrive grey.
     The raw purple is dark and saturated, so the same lift buys far more
     violet — the same reasoning the G3 rule already records for itself.
     Kept at two different positions so a page of stacked planes is lit from
     alternating corners rather than reading as one flat sheet. */
  --plane-a:radial-gradient(70% 90% at 15% 0%,color-mix(in srgb,var(--bloom) 30%,transparent),transparent 68%);
  --plane-b:radial-gradient(60% 80% at 90% 100%,color-mix(in srgb,var(--bloom) 22%,transparent),transparent 70%);
  /* G3 — the colour of the lighting behind the flat bands. RAW TGS Purple, not
     --secondary: --secondary is the dark theme's LIGHTENED purple (#9A86D8),
     picked so it can be read as text; used as a wash it spends its budget on
     luminance and arrives grey. #634ea0 is dark and saturated, so the same
     luminance lift buys far more violet. Consumed by the .band rule below. */
  --bloom:#634ea0;
  /* one elevation scale */
  --sh-1:0 1px 2px rgba(0,0,0,.55);
  --sh-2:0 16px 36px -22px rgba(0,0,0,1),0 3px 10px rgba(0,0,0,.42);
  --sh-3:0 34px 70px -30px rgba(0,0,0,1),0 8px 22px rgba(0,0,0,.5);
  /* one card surface + one border token */
  --card-bg:linear-gradient(168deg,rgba(255,255,255,.058),rgba(255,255,255,.018) 58%,rgba(255,255,255,.006));
  --card-bd:rgba(255,255,255,.11);
  --card-bd-h:color-mix(in srgb,var(--accent) 48%,rgba(255,255,255,.12));
  /* the AA-safe image-card system */
  --card-base:#121428;
  --card-veil:linear-gradient(180deg,rgba(16,18,36,.04) 0%,rgba(16,18,36,.42) 44%,rgba(16,18,36,.74) 78%,rgba(16,18,36,.88) 100%);
  --card-tint:linear-gradient(206deg,color-mix(in srgb,var(--primary) 40%,transparent) 0%,transparent 60%),
              linear-gradient(18deg,color-mix(in srgb,var(--accent) 20%,transparent) 0%,transparent 44%);
  --tint-blend:normal; --tint-op:.88;
  --on-card:#F4F7FF; --on-card-mut:rgba(210,220,238,.80); --on-card-accent:#a195c6;
  --dia-mut:14%; --dia-sf:12%; --dia-mut-o:.9;
  --ghost-plate:88%;
  --svc-scrim-h:104px;
  --img-fx:saturate(.60) contrast(1.12) brightness(.78);
  --img-fx-soft:saturate(.70) contrast(1.06) brightness(.86);
  --glass:color-mix(in srgb,#121428 76%,transparent);
  --plate-bg:#ffffff;
}
[data-theme="light"]{
  --surf-1:#ffffff; --surf-2:#f3f6fc; --surf-3:#e9eef8;
  --grain-a:.032;
  --plane-a:radial-gradient(70% 90% at 15% 0%,color-mix(in srgb,var(--primary) 9%,transparent),transparent 68%);
  --plane-b:radial-gradient(60% 80% at 90% 100%,color-mix(in srgb,var(--secondary) 8%,transparent),transparent 70%);
  --sh-1:0 1px 1px rgba(16,24,40,.04),0 3px 6px -1px rgba(16,24,40,.08),0 12px 24px -12px rgba(16,24,40,.16);
  --sh-2:0 1px 2px rgba(16,24,40,.05),0 6px 12px -2px rgba(16,24,40,.10),0 22px 42px -16px rgba(16,24,40,.22);
  --sh-3:0 2px 4px rgba(16,24,40,.06),0 12px 24px -4px rgba(16,24,40,.12),0 40px 72px -22px rgba(16,24,40,.28);
  --card-bg:linear-gradient(168deg,#ffffff,#ffffff 62%,#fafcff);
  --card-bd:rgba(10,12,18,.16);
  /* the hover edge was 1.47:1 against the resting one — the orange half of the
     cue read as a grey smudge. Rebased on the darker accent (dark unchanged). */
  --card-bd-h:color-mix(in srgb,var(--accent-text) 62%,rgba(10,12,18,.11));
  --card-base:#ffffff;
  --card-veil:linear-gradient(180deg,rgba(255,255,255,0) 0%,rgba(255,255,255,.08) 50%,rgba(255,255,255,.24) 82%,rgba(255,255,255,.34) 100%);
  /* light is AUTHORED, not bleached: the photo is reduced to pure luminance and
     re-coloured with a duotone (mix-blend-mode:color). The ramp used to end in
     an amber to meet the orange accent; it now runs TGS Blue -> TGS
     Purple, which is the logo's own gradient. Blend mode `color` carries hue
     and saturation only, so this cannot move any text pair — the copy sits on
     --scrim, which is built from --card-base. */
  --card-tint:linear-gradient(206deg,var(--primary) 0%,var(--primary) 46%,var(--secondary) 78%,var(--accent) 100%);
  --tint-blend:color; --tint-op:.72;
  /* the old orange needed a darkened second value to clear AA on the white card.
     TGS Purple is 6.71:1 on --card-base #ffffff as it stands, so on-card-accent
     is simply the accent — one hue, one value, no second-guessing. */
  --on-card:#0A0C12; --on-card-mut:rgba(28,34,47,.78); --on-card-accent:#634ea0;
  /* the schematics' muted strokes/fills and surface tints were authored for a
     near-black canvas; on the white plate they measured 1.20-1.36:1. */
  --dia-mut:48%; --dia-sf:26%; --dia-mut-o:1;
  /* the closing CTA's ghost button sits on the PHOTOGRAPHIC half of the band's
     scrim, so its own plate is what its border has to read against. */
  --ghost-plate:92%;
  /* the caption sits 59px off the frame's floor and --scrim only reaches full
     --card-base at 68px, so in light the top line of "S/01" landed in the
     68-94% zone (4.12-4.49:1). Dark keeps the 104px it ships with. */
  --svc-scrim-h:132px;
  --img-fx:grayscale(1) contrast(1.26) brightness(.74);
  --img-fx-soft:grayscale(1) contrast(1.12) brightness(.88);
  --glass:color-mix(in srgb,#ffffff 80%,transparent);
  --plate-bg:#ffffff;
}

/* AA correction: the footer's contact labels were --faint, which measures
   2.39:1 (light) / 2.84:1 (dark). --mut clears 4.5:1 in both. */
.foot-contact dt{color:var(--mut)}

/* AA correction: --mut clears 4.5:1 against the page background in both themes,
   but a dark PANEL is a lighter surface than --bg, and the 10-11px micro-labels
   that sit on one (EMAIL / PHONE / STUDIO, the rail's AT A GLANCE, the form's
   "(optional)") measured 4.10-4.33:1 there. The panel republishes --mut at a
   higher alpha, so only the panel-borne cases move — everything on --bg keeps
   the ramp it was designed with. */
[data-theme="dark"] .panel,[data-theme="dark"] .rail-card{--mut:rgba(196,206,224,.78)}

/* every accent label uses the higher-contrast accent so it clears AA on the
   light theme's near-white surfaces (measured, not assumed) */
.band .lead-num,.idx .n,.story .ktag,.insight .kdate,.tl-tag{color:var(--on-card-accent)}

/* ---- BANDS: the measurable surface ramp --------------------------------- */
/* background-COLOR, not the `background` shorthand: the shorthand resets
   background-image, and G3 paints its bloom into exactly that slot. */
.band{position:relative; padding-block:var(--band-pad); background-color:var(--surf-1); isolation:isolate; overflow:clip;
  --glow-x:12%; --glow-y:-14%}
.band>.wrap{position:relative; z-index:3}
.band::after{content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background-image:var(--grain); background-size:140px 140px; opacity:var(--grain-a)}
.band.tint{background-color:var(--surf-2); --glow-x:88%}
.band.deep{background-color:var(--surf-3); --glow-x:50%; --glow-y:112%}
.band.tint,.band.deep{border-block:1px solid var(--line-2)}
/* G3 — LIGHTING. Dark theme only; the light theme was never flat and the
   selector, not a token, is what keeps it out.
   The centre sits OUTSIDE the band (--glow-y is -14% / 112%), so what lands
   inside is the shoulder of the bloom and never its hot core — that is the
   difference between lighting and a spotlight. Position comes from the
   --glow-x/--glow-y the band variants set above, so one gradient serves all
   three placements and no two consecutive bands are lit from the same corner.
   The gradient is written HERE rather than as a theme token on purpose: a
   var() nested inside a custom property is substituted where that property is
   DECLARED, so a --band-glow declared on [data-theme="dark"] would resolve
   --glow-x against <html>, find nothing, and compute to guaranteed-invalid —
   the whole layer silently disappears. It has to be authored on the band.
   One background-image on the band element itself: no extra element, no new
   stacking context, no filter, nothing to composite on scroll. Static, so
   prefers-reduced-motion has nothing to honour.
   Two exclusions, both deliberate. .tex-plane already carries
   --plane-a/--plane-b, so it is not one of the flat bands the note is about,
   and a second wash under a certified one would over-light it and move
   contrast pairs that were measured with the plane in place. .bp fills the
   band with a photograph at z-index 0 that covers the element background
   outright — the bloom there would be invisible paint.
   The hero is a <section class="hero">, not a .band, so it is untouched: the
   video is already the light there. */
[data-theme="dark"] .band:not(.tex-plane):not(.bp){
  background-image:radial-gradient(86% 118% at var(--glow-x) var(--glow-y),
    color-mix(in srgb,var(--bloom) 30%,transparent) 0%,
    color-mix(in srgb,var(--bloom) 15%,transparent) 44%,
    transparent 78%)}
/* the bloom exists to stop dark reading as a solid, not at the cost of legibility */
@media (prefers-contrast:more){
  [data-theme="dark"] .band:not(.tex-plane):not(.bp){background-image:none}}
/* masked blueprint grid */
.band.tex-grid::before{content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:linear-gradient(var(--line-2) 1px,transparent 1px),linear-gradient(90deg,var(--line-2) 1px,transparent 1px);
  background-size:78px 78px; background-position:center top;
  -webkit-mask-image:radial-gradient(120% 78% at 50% 0%,#000 8%,transparent 72%);
  mask-image:radial-gradient(120% 78% at 50% 0%,#000 8%,transparent 72%)}
/* layered translucent light planes */
.band.tex-plane::before{content:""; position:absolute; inset:-10% -5% auto -5%; height:120%; z-index:0; pointer-events:none;
  background-image:var(--plane-a),var(--plane-b); opacity:.9}

/* ---- section head: the hairline running out of the eyebrow -------------- */
.band .sec-head{margin-bottom:clamp(20px,2.2vw,28px); gap:12px}
.band .h-sec{font-size:clamp(1.9rem,4vw,3.4rem); text-wrap:balance}
.band .sec-head .k::after{content:""; flex:1 1 auto; height:1px; min-width:24px;
  background:linear-gradient(90deg,var(--line) 0%,transparent 88%)}
/* the section-level route out of a band. The homepage industries band is built
   from `partials/industries.njk`, which /industries itself also renders, so the
   cards cannot be anchors without that page's cards linking to themselves — the
   link belongs to the section head instead. Label type, in the accent every
   other label already uses. */
.sec-head .sec-more{flex:none; display:inline-flex; align-items:center; gap:8px;
  font-size:var(--lbl); font-weight:var(--lbl-w);
  color:var(--on-card-accent); white-space:nowrap; transition:opacity .25s}
.sec-head .sec-more>span{display:inline-block; transition:transform .25s var(--e-out)}
.sec-head .sec-more:hover,.sec-head .sec-more:focus-visible{opacity:.82}
.sec-head .sec-more:hover>span,.sec-head .sec-more:focus-visible>span{transform:translateX(4px)}

/* ---- scroll rule, parked on the bottom edge of the viewport -------------
   Never painted across the locked header's own bottom border. */
.sprog{position:fixed; inset:auto 0 0 0; height:1px; z-index:51; pointer-events:none;
  background:var(--line-2); opacity:0; transition:opacity .4s ease}
body.is-scrolled .sprog{opacity:1}
.sprog i{display:block; height:100%; width:100%; transform:scaleX(var(--p,0)); transform-origin:left center;
  background:linear-gradient(90deg,color-mix(in srgb,var(--primary) 88%,transparent),var(--accent))}

/* ---- shared photo treatment: duotone veil + brand tint + ken-burns ------- */
.ph{display:block; position:relative; overflow:hidden; isolation:isolate; background:var(--bg-3)}
/* the image is taken out of flow so a lazily-decoded photograph can NEVER
   resize its frame — every .ph carries its own aspect-ratio or height, so the
   page's layout is final before a single byte of imagery arrives */
.ph>img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  filter:var(--img-fx) brightness(var(--ph-b)) contrast(var(--ph-c));
  transform:scale(1.001); transition:transform 1.4s var(--e-out),filter .5s ease}
.ph.soft>img{filter:var(--img-fx-soft) brightness(var(--ph-b)) contrast(var(--ph-c))}
.ph::after{content:""; position:absolute; inset:0; background:var(--card-tint);
  mix-blend-mode:var(--tint-blend); opacity:var(--tint-op); pointer-events:none}
.ph-veil::before{content:""; position:absolute; inset:0; z-index:1; background:var(--card-veil); pointer-events:none}
.ph-hover:hover>img{transform:scale(1.06)}
.op-t>img{object-position:50% 18%}
.op-b>img{object-position:50% 82%}
.op-l>img{object-position:16% 50%}
.op-r>img{object-position:84% 50%}
/* the text plate that guarantees AA over any photograph */
.pbody{position:relative; background:var(--scrim);
  padding:var(--scrim-pad) clamp(18px,1.9vw,24px) clamp(17px,1.8vw,22px);
  display:flex; flex-direction:column}

/* ================= 01 ABOUT — layered plate + stat rail ================== */
/* the accreditation that has no slot in the 4-up rail: a credential footnote,
   set at the rail's own label size. */
.accred{margin-block-start:clamp(14px,1.6vw,20px); font-size:var(--lbl); color:var(--mut)}
/* A3 — the About heading sits over a two-column grid, not the full measure, so
   it does not need the full section scale the one-column bands use. Held ~15%
   under `.band .h-sec` (clamp(1.9rem,4vw,3.4rem)). Specificity (0,3,0) beats
   both `.band .h-sec` and the 640px `.h-sec,.band .h-sec` override, so the
   phone size comes from this clamp's floor, not from 1.85rem. */
.band.about-band .h-sec{font-size:clamp(1.62rem,3.4vw,2.9rem)}
.about-band .about-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(28px,4vw,64px); align-items:center}
/* the base rule is `.about-grid figure` (0,1,1) — match its specificity to un-clip the inset */
.about-grid figure.about-fig{position:relative; margin:0; border:0; border-radius:0; overflow:visible}
/* the inset overhangs THIS box, not the whole figure, so adding a caption
   below does not drag the overlap down with it */
.about-fig-media{position:relative}
.about-fig .plate{position:relative; border-radius:var(--r-lg); overflow:hidden; isolation:isolate;
  border:1px solid var(--card-bd); box-shadow:var(--sh-3)}
/* `height:auto` is load-bearing: an intrinsic `height` attribute on the <img>
   (kept for CLS) would otherwise be a used height and `aspect-ratio` would
   never apply — the inset grew to its full 600px source height. */
.about-fig .plate img{width:100%; height:auto; aspect-ratio:5/4; object-fit:cover; filter:var(--img-fx) brightness(var(--ph-b)) contrast(var(--ph-c))}
.about-fig .plate::after,.about-fig .inset::after{content:""; position:absolute; inset:0; pointer-events:none;
  background:var(--card-tint); mix-blend-mode:var(--tint-blend); opacity:var(--tint-op)}
.about-fig .inset{position:absolute; inset-inline-end:16px; bottom:-26px; width:44%; max-width:230px; isolation:isolate;
  border-radius:var(--r); overflow:hidden; border:1px solid var(--card-bd); box-shadow:var(--sh-3); background:var(--surf-1)}
.about-fig .inset img{width:100%; height:auto; aspect-ratio:4/3; object-fit:cover; filter:var(--img-fx) brightness(var(--ph-b)) contrast(var(--ph-c))}
/* The region, as a caption for the photograph. `padding-inline-end` keeps the
   line clear of the inset that overhangs the bottom trailing corner; the top
   margin clears that same 26px overhang. */
.about-where{margin-block-start:40px; padding-inline-end:48%;
  font-size:var(--lbl); color:var(--mut)}
.about-where::before{content:""; display:inline-block; width:16px; height:1px;
  background:var(--accent-text); vertical-align:middle; margin-inline-end:10px}
/* /about's opening CMS paragraph gets the homepage's display-serif lede, so the
   two instances of this band read as one component rather than two. */
.about-copy .prose-lede > p:first-child{font-family:var(--font-display); font-weight:380;
  font-size:clamp(1.5rem,2.4vw,2.1rem); line-height:1.18; color:var(--ink);
  letter-spacing:-.01em; margin-block-end:22px}
.stat-rail{display:grid; grid-template-columns:repeat(4,1fr); gap:0; margin:clamp(22px,3vw,30px) 0 0;
  border-top:1px solid var(--line)}
.stat-rail>div{display:flex; flex-direction:column; padding-block-start:16px; padding-inline-end:14px;
  border-inline-end:1px solid var(--line-2)}
.stat-rail>div:last-child{border-inline-end:0}
.stat-rail dt{font-size:var(--lbl); color:var(--mut); order:2; margin-top:4px; line-height:1.35}
.stat-rail dd{margin:0; order:1; font-family:var(--font-display); font-weight:400; line-height:1;
  font-size:clamp(1.5rem,2.5vw,2.1rem); color:var(--ink); font-variant-numeric:tabular-nums}
.stat-rail>div:nth-child(2) dd{color:var(--accent-text)}
@media(max-width:860px){ .about-band .about-grid{grid-template-columns:1fr} .about-fig .inset{width:38%} }
@media(max-width:520px){ .stat-rail{grid-template-columns:1fr 1fr; row-gap:14px}
  .stat-rail>div:nth-child(2){border-inline-end:0} }

/* ================= 02 VMV — photographic plate per row =================== */
.vmv .row{grid-template-columns:clamp(96px,9.4vw,132px) minmax(72px,auto) minmax(0,1fr);
  gap:clamp(18px,2.2vw,30px); padding-block:clamp(16px,1.8vw,22px); align-items:center}
.vmv-fig{width:100%; aspect-ratio:1; border-radius:var(--r); border:1px solid var(--card-bd); box-shadow:var(--sh-2)}
.vmv .row .lab{padding-top:0; min-width:0}
.vmv .row q{font-size:clamp(1.15rem,2.1vw,1.72rem); line-height:1.28}
.vmv .row:hover .vmv-fig>img{transform:scale(1.07)}
@media(max-width:760px){
  .vmv .row{grid-template-columns:76px minmax(0,1fr); gap:6px 16px; align-items:start}
  .vmv-fig{aspect-ratio:1; grid-column:1; grid-row:1/3; align-self:start}
  .vmv .row .lab{grid-column:2; grid-row:1; align-self:center}
  .vmv .row q{grid-column:2; grid-row:2; font-size:1.05rem; line-height:1.5}
}

/* ========== THE ROW FIX — explicit tracks, explicit named areas ===========
   3 columns / 4 children let the "→" wrap onto an implicit second row and
   doubled every row's height. grid-template-areas means a child can only ever
   land where it belongs. The 5-column `.cap` variant carries the schematic in
   its own named area, on the same rules.                                    */
.idx a,.idx .item{
  display:grid;
  grid-template-columns:auto minmax(0,1.02fr) minmax(0,1.18fr) auto;
  grid-template-areas:"n nm ds ar";
  column-gap:clamp(14px,2vw,30px); row-gap:0;
  align-items:center; min-height:84px;
  padding-block:clamp(12px,1.4vw,17px); padding-inline:12px;
  border-bottom:1px solid var(--line); position:relative;
  transition:background var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out);
}
.idx .n{grid-area:n; align-self:center}
.idx .nm,.idx .role{grid-area:nm; line-height:1.14; font-size:clamp(1.1rem,1.7vw,1.4rem); text-wrap:balance}
.idx .ds{grid-area:ds; max-width:46ch; font-size:.9rem; line-height:1.55; align-self:center; justify-self:start}
.idx .ar{grid-area:ar; align-self:center; justify-self:end}
.idx .thumb{grid-area:th; display:none; width:76px; aspect-ratio:1; border-radius:var(--r-sm); border:1px solid var(--card-bd)}
/* thumbnails are tiny — ease the grade so they read as pictures, not swatches */
.idx .thumb>img{filter:var(--img-fx-soft) brightness(var(--ph-b)) contrast(var(--ph-c))}
.idx .thumb::after{opacity:calc(var(--tint-op) * .55)}
.idx .item::before{content:""; position:absolute; inset-block:-1px auto; inset-inline-start:0; width:2px; height:100%;
  background:var(--on-card-accent); transform:scaleY(0); transform-origin:50% 100%; transition:transform var(--t-hover) var(--e-out)}
.idx .item:hover::before,.idx .item:focus-visible::before,.idx .item.is-active::before{transform:none}
/* the "row slides on hover" cue, done with transform instead of padding — no
   reflow of the description column, so it stays 60fps and never re-wraps */
.idx .n,.idx .nm,.idx .role{transition:transform var(--t-hover) var(--e-out)}
.idx .item:hover,.idx .item:focus-visible{background:color-mix(in srgb,var(--primary) 8%,transparent); padding-inline-start:12px}
.idx .item:focus-visible{outline:2px solid var(--focus); outline-offset:-2px; border-radius:var(--r-sm)}
.idx .item.is-active{background:color-mix(in srgb,var(--primary) 7%,transparent)}
.idx .item:hover .n,.idx .item:hover .nm,.idx .item:hover .role,
.idx .item:focus-visible .n,.idx .item:focus-visible .nm,.idx .item:focus-visible .role,
.idx .item.is-active .n,.idx .item.is-active .nm,.idx .item.is-active .role{transform:translateX(7px)}
.idx .ar{transition:color var(--t-hover) var(--e-out),transform var(--t-hover) var(--e-out)}
.idx .item:hover .ar,.idx .item.is-active .ar,.idx .item:focus-visible .ar{color:var(--on-card-accent); transform:translateX(4px)}

/* ================= 03 SERVICES — one panel, heading over the stage ======= */
/* S6 — the band is ONE rectangle: heading, photographic stage and the six rows
   all sit inside `.svc-panel`, on the same card treatment About's plate uses
   (`--card-bd` hairline, `--r-lg`, `--sh-3`).
   S5 — the heading moved out of the full-measure `.sec-head` above the band and
   into the left column, over the image.
   S4 — the stage is no longer a short sticky panel: it takes every pixel the
   heading leaves, so its base lands on the bottom of the last row ("Cloud
   Services"). `align-items:stretch` on the grid gives the column the list's
   height; `flex:1` hands the remainder to the stage. */
.svc{display:grid; grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr); gap:clamp(20px,2.4vw,38px); align-items:start}
.svc-panel{border:1px solid var(--card-bd); border-radius:var(--r-lg); background:var(--card-bg);
  box-shadow:var(--sh-3); padding:clamp(18px,2.2vw,30px)}
.svc-side{display:flex; flex-direction:column; min-width:0}
/* inside the panel the heading is a column header, not a band header — the
   `.sec-head` bottom margin is band-scale and would push the stage off the
   list's last row. */
.svc-side .sec-head,.band .svc-side .sec-head{margin-bottom:clamp(14px,1.6vw,22px)}
/* S3 — 8-10% under `.band .h-sec`, with the leading pulled in from 1.02 to .96.
   Specificity (0,3,0), the same trick `.band.about-band .h-sec` uses, so it
   also beats the 640px `.h-sec,.band .h-sec` override and the phone size comes
   from this clamp's floor. */
.band .svc-panel .h-sec{font-size:clamp(1.72rem,3.62vw,3.08rem); line-height:.96}
/* the floor is low on purpose: the LIST has to be the taller column, or the
   grid row takes its height from this side and the frame overshoots the last
   row. Arabic sets the heading three lines deep and was 46px over at 300px. */
/* NB: `.svc-side` is already `display:flex; flex-direction:column` above — the
   stage's own `flex:1 1 auto` is what makes it take the height the heading
   leaves over, which is what puts its bottom edge on the last row (S4). No
   second declaration needed here. */
/* The frame used to stretch to whatever height the list beside it ran to,
   which held while the list was six rows. The page lists TEN services: the slot
   became roughly 500x1000 and `object-fit:cover` cropped each photograph to a
   vertical slice of itself. The frame now keeps its own portrait proportion and
   STICKS while the list scrolls past it — the stage is showing the active
   service, so it has to stay in view to mean anything. */
.svc-stage{position:sticky; top:calc(var(--hdr-h,74px) + clamp(12px,1.6vw,24px));
  flex:0 0 auto; aspect-ratio:3/4; max-height:min(72vh,720px); min-height:320px}
/* the frame fills the space it is given, so it is as tall as the rows beside it */
.svc-frame{position:absolute; inset:0; border-radius:var(--r); overflow:hidden; isolation:isolate;
  border:1px solid var(--card-bd); box-shadow:var(--sh-2); background:var(--bg-3)}
.svc-img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; filter:var(--img-fx) brightness(var(--ph-b)) contrast(var(--ph-c));
  opacity:0; transform:scale(1.07); transition:opacity .75s ease,transform 1.6s var(--e-out)}
.svc-img.is-on{opacity:1; transform:none}
/* same duotone stack as .ph — tint blends, veil sits on top of it */
.svc-tint{position:absolute; inset:0; z-index:1; pointer-events:none;
  background:var(--card-tint); mix-blend-mode:var(--tint-blend); opacity:var(--tint-op)}
.svc-frame::after{content:""; position:absolute; inset:0; z-index:2; pointer-events:none; background:var(--card-veil)}
/* pixel-measured caption scrim → the caption is AA over any frame in either theme */
.svc-frame::before{content:""; position:absolute; z-index:3; inset:auto 0 0 0; height:var(--svc-scrim-h);
  background:var(--scrim); pointer-events:none}
.svc-cap{position:absolute; z-index:4; inset-inline:18px; bottom:15px; display:flex; flex-direction:column; gap:2px}
.svc-cap-n{font-size:var(--lbl); font-weight:var(--lbl-w); font-variant-numeric:tabular-nums; color:var(--on-card-accent)}
.svc-cap-t{font-family:var(--font-display); font-size:clamp(1.05rem,1.5vw,1.3rem); color:var(--ink); letter-spacing:-.01em; line-height:1.2}
.svc-meter{position:absolute; z-index:4; inset-inline:0; bottom:0; height:2px; background:var(--line); overflow:hidden}
.svc-meter i{display:block; height:100%; width:16.6667%; background:var(--on-card-accent);
  transition:transform .5s var(--e-out)}
/* S7 — every name on one line, at one size, at medium weight. The size is a
   fixed step (not the shared `.idx .nm` clamp) because "same font size" is the
   note: the longest string, "Digital Business Automation", sets it. Below the
   980px reflow the name column is far narrower, so the step drops again rather
   than letting one row overflow the panel.
   The weight is 450, not 500. The note reads "bold -> medium", but these rows
   were never bold: `.idx .nm` is 400, and CSS `medium` (500) rendered visibly
   HEAVIER than what is on the page today, which is the opposite of the note's
   intent. 450 is a medium instance of the variable face that still drops ink
   against the 22.4px/400 it replaces. */
.svc-list .nm{white-space:nowrap; font-size:1.12rem; font-weight:450; line-height:1.2}
/* the name never wraps now, so its track is sized to the longest name and the
   description takes whatever is left */
/* The four tracks are declared on the LIST and inherited by every row through
   subgrid, so all six rows share ONE name column and ONE description column.
   This previously read `.svc-list .item{grid-template-columns:auto max-content
   ...}` — per ROW — and `max-content` then sized each row's name track to that
   row's own text, so every card had a different label width and nothing lined
   up. Declaring `max-content` once on the container makes it the width of the
   WIDEST name across the whole list, which is what "same width" means, and it
   still adapts to Arabic (whose names are longer) without a magic number. */
.svc-list{display:grid; grid-template-columns:auto max-content minmax(0,1fr) auto;
  /* Equal rows. The descriptions wrap to two or three lines depending on their
     length, so the rows were different heights and the rhythm was irregular —
     S/01 ran three lines while S/02 ran two. Because every row is a grid item
     of this list, one `grid-auto-rows:1fr` distributes the height evenly
     instead of each row sizing to its own text. It also makes the list a
     predictable height, which is what lets the image beside it align. */
  grid-auto-rows:1fr; align-content:stretch}
.svc-list .item{grid-column:1/-1; grid-template-columns:subgrid}

/* ---- /services/ — the catalogue -----------------------------------------
   One photograph per service, grouped by the taxonomy. Each group's count is
   its own row (5 / 3 / 2), set from `--cols` in the markup, so a row never ends
   in a lone card — the thing that made a fixed 3- or 4-up grid look broken with
   ten items. Below 1100px the count stops mattering and the cards flow. */
.cap-set + .cap-set{margin-top:clamp(28px,3.4vw,48px)}
.cap-set-head{display:flex;align-items:baseline;gap:12px;margin-bottom:clamp(12px,1.4vw,18px)}
.cap-set-h{margin:0;font-size:var(--lbl-kicker);font-weight:var(--lbl-w);color:var(--ink-2)}
.cap-set-head::after{content:"";flex:1 1 auto;height:1px;background:var(--line-2)}
.cap-row{display:grid;grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));
  gap:clamp(12px,1.4vw,18px)}
.cap{display:flex;flex-direction:column;color:inherit;border:1px solid var(--card-bd);
  border-radius:var(--r);overflow:hidden;background:var(--card-bg);
  transition:border-color .2s,transform .2s var(--e-out)}
.cap:hover,.cap:focus-visible{border-color:var(--card-bd-h)}
.cap-ph{position:relative;display:block;aspect-ratio:4/3;overflow:hidden}
/* NOT `.ph`: that is the site's duotone treatment, which is grayscale(1) in
   the light theme. A capability card leads with its photograph in colour.
   No S/NN badge either: a number implies a sequence, and a catalogue is not
   one — the services do not happen in order. */
.cap-ph img{width:100%;height:100%;object-fit:cover;display:block;filter:saturate(1.05)}
.cap-ph::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(16,18,36,.05),rgba(16,18,36,.62))}
.cap-body{display:flex;flex-direction:column;gap:6px;padding:clamp(13px,1.4vw,17px)}
.cap-nm{font-family:var(--font-display);font-size:clamp(1rem,1.2vw,1.12rem);line-height:1.25;color:var(--ink)}
.cap-ds{font-size:.83rem;line-height:1.5;color:var(--mut)}
@media (max-width:1100px){.cap-row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}}
@media (max-width:520px){.cap-row{grid-template-columns:1fr}.cap{flex-direction:row}
  .cap-ph{flex:0 0 40%;aspect-ratio:auto}}
/* ---- services page: three named shelves, not one column of ten ----------
   The row's text used to sit in two tracks side by side; at this column width
   the description track fell under ~130px and every summary ran to four lines
   beside a half-empty name track. Name and summary are ONE block now, stacked,
   so the text sets to a readable measure and the rows share a rhythm. */
.svc-groups{display:flex; flex-direction:column; gap:clamp(26px,3vw,40px); min-width:0}
.svc-group-h{display:flex; align-items:center; gap:14px; margin:0 0 6px;
  font-size:var(--lbl-kicker); font-weight:var(--lbl-w); color:var(--mut)}
.svc-group-h::after{content:""; flex:1 1 auto; height:1px; background:var(--line-2)}
.svc-group-n{font-size:var(--lbl); color:var(--faint)}
.svc-groups .svc-list{display:block}
.svc-groups .svc-list .item{display:grid; grid-template-columns:auto minmax(0,1fr) auto;
  grid-template-areas:"n nm ar" "n ds ar"; column-gap:clamp(14px,1.6vw,20px); row-gap:4px;
  align-items:center; padding-block:clamp(13px,1.5vw,17px)}
.svc-groups .svc-list .item .thumb{display:none}
.svc-groups .svc-list .nm{grid-area:nm; white-space:normal; font-size:clamp(1.02rem,1.3vw,1.16rem)}
.svc-groups .svc-list .ds{grid-area:ds; max-width:56ch; font-size:.86rem; line-height:1.5}
.svc-groups .svc-list .n{grid-area:n}
.svc-groups .svc-list .ar{grid-area:ar}
.svc-groups .svc-list .item:last-child{border-bottom:0}
/* Subgrid is the only thing here that is not ancient. Where it is missing the
   rows fall back to a fixed name track — still uniform, just not self-sizing. */
@supports not (grid-template-columns:subgrid){
  .svc-list{display:block}
  .svc-list .item{grid-template-columns:auto clamp(190px,15vw,260px) minmax(0,1fr) auto}
}
@media(max-width:1180px){
  /* under this the description track falls below ~200px and every summary runs
     to four lines — stack it under the name instead of squeezing it */
  .svc-list .item{grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-areas:"n nm ar" "n ds ar"; row-gap:5px}
}
/* the closing hairline would sit 30px inside the panel's own edge and read as a
   stray rule */
.svc-panel .idx .item:last-child{border-bottom:0}
@media(max-width:980px){
  .svc{grid-template-columns:1fr} .svc-stage{display:none}
  .svc-side .sec-head,.band .svc-side .sec-head{margin-bottom:18px}
}
@media(max-width:900px){ .svc-list .nm{font-size:1.06rem} }
@media(max-width:460px){ .svc-list .nm{font-size:.92rem} }
/* one line stops being possible below ~380px: the longest name needs 196px and
   the row leaves 193px next to its thumbnail at 360. Wrapping beats clipping. */
@media(max-width:380px){ .svc-list .nm{white-space:normal} }

/* ================= 04 PRODUCTS — the interface schematics ================ */
/* Products is the SAME component as Services and must read at the same
   density. The ONLY difference from a Services row is a fourth column holding
   the schematic, at 200px / ~84px tall, so the row lands in the same 84–124px
   band. The name track is a fixed width (not max-content) so the description
   column starts on the same x on all six rows. */
.idx.cap .item{
  grid-template-columns:auto clamp(168px,12.4vw,180px) minmax(0,1fr) var(--dia-w) auto;
  grid-template-areas:"n nm ds dia ar";
  min-height:0; padding-block:8px;
}
.dia-wrap{grid-area:dia; display:block; border:1px solid var(--card-bd); border-radius:var(--r-sm);
  background:var(--surf-1); padding:5px 6px; box-shadow:var(--sh-1);
  transition:border-color var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),transform var(--t-hover) var(--e-out)}
.idx.cap .item:hover .dia-wrap,.idx.cap .item:focus-visible .dia-wrap{border-color:var(--card-bd-h); box-shadow:var(--sh-2); transform:translateY(var(--lift))}
.dia{display:block; width:100%; height:auto; color:var(--primary)}
.dia .st{fill:none; stroke:currentColor; stroke-width:1.3; stroke-linecap:round; stroke-linejoin:round}
.dia .sf{fill:color-mix(in srgb,var(--primary) var(--dia-sf),transparent)}
.dia .dash{fill:none; stroke:var(--line); stroke-width:1.3; stroke-dasharray:3 5}
.dia .ac{stroke:var(--on-card-accent)}
.dia .af{fill:color-mix(in srgb,var(--on-card-accent) 26%,transparent); stroke:var(--on-card-accent)}
.dia .mut{stroke:var(--ctrl-bd); opacity:var(--dia-mut-o)}
.dia .fillmut{fill:color-mix(in srgb,var(--ink) var(--dia-mut),transparent); stroke:none}
.dia .fillac{fill:var(--on-card-accent); stroke:none}
/* labels are sized in user units against a 220-unit box scaled to --dia-w;
   at 200px the multiplier is .909, so 13u/12u still render >= 10.9px CSS */
.dia .tx{font-family:var(--font-body); font-size:13px; font-weight:var(--lbl-w); fill:var(--ink-2); opacity:.85; text-anchor:middle; letter-spacing:.03em}
.dia .tx.s{text-anchor:start}
.dia .tx.e{text-anchor:end}
.dia .tiny{font-size:12px; opacity:.85}
/* draw-on-reveal: strokes trace themselves, labels and solids fade in behind.
   pathLength="1" normalises every shape to a unit length, so the dash pattern
   can never come up short on a shape whose real perimeter grows. */
.dia .st{stroke-dasharray:1; stroke-dashoffset:1}
.reveal.in .dia .st{animation:dia-draw 1.05s var(--e-out) forwards}
.reveal.in .dia .st.d2{animation-delay:.12s}
.reveal.in .dia .st.d3{animation-delay:.22s}
@keyframes dia-draw{to{stroke-dashoffset:0}}
.dia .tx,.dia .fillmut,.dia .fillac,.dia .dash{opacity:0}
.reveal.in .dia .tx{animation:dia-fade .6s ease .38s forwards}
.reveal.in .dia .fillmut,.reveal.in .dia .fillac,.reveal.in .dia .dash{animation:dia-fade .6s ease .3s forwards}
@keyframes dia-fade{to{opacity:1}}

/* ---- the featured product row ------------------------------------------
   Together is the only product with a live instance a prospect can be given
   access to, so the index has to single it out. A neon bloom on a dark card is
   exactly the tell this site avoids, so the distinction is assembled from the
   row's OWN parts instead: the hover rail latched permanently on, a warm wash
   at 5% (below the 7% the keyboard-active row already uses, so `.is-active`
   still reads as the stronger state), an accent hairline on the schematic
   frame, and one small tag. `--on-card-accent` — not `--accent` — because these
   sit ON the tinted band and that token is the one certified against it. */
.idx .item.is-feat{background:color-mix(in srgb,var(--accent) 5%,transparent)}
.idx .item.is-feat::before{transform:none; background:var(--on-card-accent)}
.idx .item.is-feat:hover,.idx .item.is-feat:focus-visible{background:color-mix(in srgb,var(--accent) 10%,transparent)}
.idx.cap .item.is-feat .dia-wrap{
  border-color:color-mix(in srgb,var(--on-card-accent) 40%,var(--card-bd));
  box-shadow:var(--sh-1),0 0 0 3px color-mix(in srgb,var(--on-card-accent) 10%,transparent)}
.idx.cap .item.is-feat:hover .dia-wrap,.idx.cap .item.is-feat:focus-visible .dia-wrap{
  border-color:var(--on-card-accent);
  box-shadow:var(--sh-2),0 0 0 4px color-mix(in srgb,var(--on-card-accent) 14%,transparent)}
/* rides inside `.nm`, so it inherits the row's start-edge shift on hover and
   mirrors with the name in RTL without a single physical property. */
.feat-tag{display:inline-block; margin-inline-start:.62em; vertical-align:.26em;
  font-size:var(--lbl-sm); font-weight:600; letter-spacing:.04em; line-height:1;
  padding:.44em .6em .34em; border-radius:3px; white-space:nowrap;
  color:var(--on-card-accent);
  border:1px solid color-mix(in srgb,var(--on-card-accent) 45%,transparent);
  background:color-mix(in srgb,var(--on-card-accent) 12%,transparent)}
/* The one moving part. It is NOT decoration: `.node-live` is the last link of
   the approval chain, the only step the drawing leaves unresolved, so a slow
   breath reads as a request still in flight. The `dia-draw` half is restated
   verbatim because `animation` is a shorthand — declaring the pulse alone at
   equal specificity would drop the draw-on-reveal entirely and the node would
   appear fully formed while its six neighbours traced themselves.
   prefers-reduced-motion kills both: `.dia .st{animation:none !important}`
   already matches this node and settles it at opacity 1. */
.reveal.in .dia .node-live{animation:dia-draw 1.05s var(--e-out) .22s forwards,
  node-live 3.4s var(--e-out) 1.5s infinite}
@keyframes node-live{0%,100%{opacity:1} 50%{opacity:.5}}

/* ================= 05 INDUSTRIES — photographic cards, deepest band ====== */
/* I2 — one structure for all three cards.
   The three verticals carry copy of very different lengths (7 / 5 / 6 lines at
   1440), and the card was a bottom-anchored flex column, so each plate started
   wherever its own paragraph happened to end: measured title tops of 136 / 181 /
   159px from the card's top edge, a 45px spread, and the longest card's plate
   covered 90% of its photograph while the shortest covered 77% — which is also
   why one icon sat on bare photo and another on the scrim ramp.

   The copy is NOT shortened (I3 is excluded), so the length has to be absorbed
   by the geometry instead. The card becomes a two-row grid — a photo band, then
   the text plate — and every card SUBGRIDS the parent's rows, so the plate row
   is sized once, by the longest paragraph, and shared by all three. The result:
   one crop band of identical height, one icon position that is always over bare
   photograph, and one title baseline. The slack from the shorter paragraphs
   falls at the FOOT of the plate, where it reads as plate depth, rather than at
   the head, where it read as three different cards.

   `grid-row:span 2` (not `1/-1`) so a fourth vertical from the CMS would start a
   fresh pair of rows instead of stacking on top of the first card; `grid-auto-
   rows` repeats the same two tracks for it. --ind-band is the guaranteed photo
   height: the plate can only grow downwards from it. */
.ind{--ind-band:clamp(140px,13vw,178px);
  grid-template-columns:repeat(3,1fr); gap:clamp(12px,1.4vw,18px); background:none; border:0; border-radius:0; overflow:visible}
.ind .cell{position:relative; isolation:isolate; overflow:hidden; background:var(--card-base);
  border:1px solid var(--card-bd); border-radius:var(--r-lg); box-shadow:var(--sh-2);
  padding:0; gap:0;
  display:grid; grid-template-rows:var(--ind-band) auto;
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
/* without subgrid each card still gets the band + plate structure; it just
   sizes its own plate, exactly as it did before this change. */
@supports (grid-template-rows:subgrid){
  .ind{grid-template-rows:var(--ind-band) auto; grid-auto-rows:var(--ind-band) auto}
  .ind .cell{grid-row:span 2; grid-template-rows:subgrid}
}
.ind .cell .ph{position:absolute; inset:0; z-index:-1}
.ind .cell:hover{transform:translateY(var(--lift)); box-shadow:var(--sh-3); border-color:var(--card-bd-h)}
.ind .cell:hover .ph>img{transform:scale(1.06)}
.ind .cell .ic{position:absolute; z-index:2; top:18px; inset-inline-start:18px; margin:0;
  background:var(--glass); backdrop-filter:blur(8px); border:1px solid var(--card-bd); color:var(--on-card)}
/* row 2. `justify-content:start` is what holds the title baseline: the plate is
   as tall as the longest card's, and the slack is left under the paragraph. */
.ind .cell .pbody{grid-row:2; justify-content:flex-start; gap:8px; padding-inline:clamp(20px,2.1vw,26px)}
.ind .cell h3{color:var(--ink); font-size:clamp(1.28rem,1.9vw,1.62rem)}
.ind .cell p{color:var(--mut); margin-top:0; font-size:.885rem; line-height:1.6}
/* stacked, the cards are never side by side, so there is no baseline to hold —
   drop the subgrid and let each card be exactly as tall as its own copy. */
@media(max-width:820px){ .ind{grid-template-columns:1fr; grid-template-rows:none; grid-auto-rows:auto;
    /* one column means one very wide card: the band is re-cut against the card's
       own width so the crop stays a picture and not a letterbox strip. */
    --ind-band:clamp(150px,24vw,230px)}
  .ind .cell{grid-row:auto; grid-template-rows:var(--ind-band) auto} }

/* ============ 06 SUCCESS STORIES — index, top-rule, logo wall ============ */
.stories{grid-template-columns:repeat(2,1fr); gap:clamp(12px,1.4vw,18px)}
.story{background:var(--card-bg); border:1px solid var(--card-bd); border-radius:var(--r-lg);
  padding:clamp(20px,2.2vw,28px); gap:14px; box-shadow:var(--sh-1); position:relative; overflow:hidden;
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
.story::before{content:""; position:absolute; inset:0 auto auto 0; width:100%; height:2px; background:var(--accent);
  transform:scaleX(0); transform-origin:0 50%; transition:transform var(--t-hover) var(--e-out)}
.story:hover::before,.story:focus-visible::before{transform:scaleX(1)}
.story:hover,.story:focus-visible{transform:translateY(var(--lift)); box-shadow:var(--sh-3); border-color:var(--card-bd-h)}
.story:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.story .chip{border:1px solid var(--card-bd); width:76px; height:50px; padding:8px 10px; border-radius:var(--r-sm)}
.story .chip img{height:32px; max-height:32px; object-fit:contain}
.story .kname{font-size:1.32rem}
.story p{font-size:.92rem; line-height:1.6; text-wrap:pretty; margin-bottom:auto}
/* --- the client + partner wall --- */
.wall{margin-top:clamp(18px,2.2vw,26px); border:1px solid var(--card-bd); border-radius:var(--r-lg);
  overflow:hidden; background:var(--card-bg); box-shadow:var(--sh-1)}
.wall-h{display:grid; grid-template-columns:4fr 3fr; gap:6px 18px; align-items:center; padding:11px 16px;
  border-bottom:1px solid var(--line-2); font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut)}
.wall-part{display:flex; align-items:center; justify-content:space-between; gap:10px 18px; flex-wrap:wrap}
.wall-cert{display:inline-flex; align-items:center; gap:8px; color:var(--ink-2); white-space:nowrap}
.wall-cert::before{content:""; width:6px; height:6px; border-radius:50%; background:var(--accent); flex:none}
.wall-g{display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); margin-inline-end:-1px; margin-block-end:-1px}
.wall-i{min-height:80px; display:grid; place-items:center; padding:14px 12px;
  border-inline-end:1px solid var(--line-2); border-block-end:1px solid var(--line-2)}
/* ONE cell treatment: every mark on the wall — client logo or partner
   wordmark — sits on the same white plate at the same height, so the row
   never reads as "four lit tiles then three empty ones" in dark. */
.wall-plate{background:var(--plate-bg); border:1px solid var(--line-2); border-radius:var(--r-sm); padding:8px 10px;
  display:grid; place-items:center; width:100%; min-height:42px}
[data-theme="dark"] .wall-plate{border-color:transparent}
.wall-plate img{height:30px; max-height:30px; width:auto; max-width:100%; object-fit:contain}
.wm{font-size:.875rem; font-weight:600; color:#1C222F; text-align:center; line-height:1.3}
@media(max-width:980px){ .wall-g{grid-template-columns:repeat(4,minmax(0,1fr))} }
@media(max-width:720px){ .stories{grid-template-columns:1fr} }
@media(max-width:560px){ .wall-g{grid-template-columns:repeat(2,minmax(0,1fr))} .wall-i{min-height:66px}
  /* 7 logos into 2 columns would leave the last cell beside an empty one with a
     divider hanging down the void — give the odd one out the whole final row */
  .wall-i:last-child{grid-column:1/-1}
  .wall-h{grid-template-columns:minmax(0,1fr); gap:5px 14px} }

/* ================= 07 JOURNEY — the horizontal timeline ================== */
.tl{position:relative; --tl-gap:clamp(16px,2.4vw,34px)}
/* the axis TERMINATES on the centre of the third dot instead of running into space */
.tl-axis{position:absolute; top:6px; z-index:0; overflow:hidden; height:1px; background:var(--line);
  inset-inline:6px calc(33.3333% - (var(--tl-gap) * 2 / 3) - 6px)}
.tl-fill{display:block; width:100%; height:100%; transform:scaleX(0); transform-origin:left center;
  background:linear-gradient(90deg,var(--primary),var(--accent-text)); transition:transform 1.5s var(--e-out) .1s}
.tl.is-on .tl-fill{transform:scaleX(1)}
.tl-steps{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(16px,2.4vw,34px)}
.tl-step{position:relative; padding-top:34px}
.tl-dot{position:absolute; top:0; inset-inline-start:0; width:13px; height:13px; border-radius:50%;
  background:var(--surf-1); border:1px solid var(--line); z-index:2;
  transition:transform .6s var(--e-out),border-color .6s,background .6s}
.tl-dot::after{content:""; position:absolute; inset:3px; border-radius:50%; background:var(--faint); transition:background .6s}
.tl.is-on .tl-step:nth-child(1) .tl-dot{transition-delay:.15s}
.tl.is-on .tl-step:nth-child(2) .tl-dot{transition-delay:.6s}
.tl.is-on .tl-step:nth-child(3) .tl-dot{transition-delay:1.05s}
.tl.is-on .tl-dot{border-color:color-mix(in srgb,var(--on-card-accent) 60%,transparent); transform:scale(1.15)}
.tl.is-on .tl-dot::after{background:var(--on-card-accent)}
/* terminal marker: the last dot reads as the end of the line, not a stop mid-air */
.tl-step:last-child .tl-dot::before{content:""; position:absolute; inset:-6px; border-radius:50%;
  border:1px solid color-mix(in srgb,var(--on-card-accent) 34%,transparent);
  transform:scale(.4); opacity:0; transition:transform .7s var(--e-out) 1.3s,opacity .7s ease 1.3s}
.tl.is-on .tl-step:last-child .tl-dot::before{transform:none; opacity:1}
.tl-tag{display:block; font-size:var(--lbl); font-weight:var(--lbl-w)}
.tl-step .yr{font-family:var(--font-display); font-weight:400; font-size:clamp(2.3rem,4.4vw,3.6rem); line-height:1;
  color:var(--ink); letter-spacing:-.02em; margin-top:8px; font-variant-numeric:tabular-nums}
.tl-card{margin-top:16px; padding:clamp(16px,1.8vw,22px); border:1px solid var(--card-bd); border-radius:var(--r);
  background:var(--card-bg); box-shadow:var(--sh-1); color:var(--mut); font-size:.92rem; line-height:1.58;
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out)}
.tl-step:hover .tl-card{transform:translateY(var(--lift)); box-shadow:var(--sh-2)}
@media(max-width:820px){
  .tl-steps{grid-template-columns:1fr; gap:0}
  .tl-step{padding-top:0; padding-inline-start:32px; padding-bottom:24px}
  .tl-step:last-child{padding-bottom:0}
  /* per-step connectors so the vertical rail ends exactly on the last dot */
  .tl-axis{display:none}
  .tl-step:not(:last-child)::before{content:""; position:absolute; top:10.5px; bottom:-10.5px;
    inset-inline-start:6px; width:1px; background:var(--line); z-index:0}
  .tl-step:not(:last-child)::after{content:""; position:absolute; top:10.5px; bottom:-10.5px;
    inset-inline-start:6px; width:1px; z-index:1;
    background:linear-gradient(180deg,var(--primary),var(--accent-text));
    transform:scaleY(0); transform-origin:center top; transition:transform .8s var(--e-out)}
  .tl.is-on .tl-step:nth-child(1)::after{transform:scaleY(1); transition-delay:.2s}
  .tl.is-on .tl-step:nth-child(2)::after{transform:scaleY(1); transition-delay:.65s}
  .tl-dot{top:4px; inset-inline-start:0}
  .tl-step:last-child .tl-dot::before{inset:-5px}
  .tl-step .yr{font-size:2.2rem; margin-top:2px}
}

/* ================= 08 INSIGHTS — 4-up photographic cards ================= */
.insights{grid-template-columns:repeat(4,1fr); gap:clamp(12px,1.4vw,18px)}
/* grid, not flex: a grid item resolves its height from `aspect-ratio` against a
   definite width, so the frame is sized before the photograph decodes.
   `align-items:stretch` un-does the `center` the base `.insight` rule sets for
   the old 2-column layout — without it the body box is only as tall as its
   text and `margin-top:auto` on "Read →" has no free space to absorb. */
.insight{display:grid; grid-template-columns:minmax(0,1fr); grid-template-rows:auto 1fr; gap:0; padding:0; overflow:hidden;
  align-items:stretch;
  border:1px solid var(--card-bd); border-radius:var(--r-lg); background:var(--card-bg); box-shadow:var(--sh-1);
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
.insight:hover,.insight:focus-visible{transform:translateY(var(--lift)); box-shadow:var(--sh-2); border-color:var(--card-bd-h)}
.insight:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.insight figure{border:0; border-radius:0; border-bottom:1px solid var(--card-bd)}
.insight figure.ph{aspect-ratio:16/10}
.insight:hover figure.ph>img{transform:scale(1.05)}
.insight>div{padding:14px clamp(14px,1.4vw,18px) clamp(16px,1.6vw,20px); display:flex; flex-direction:column; flex:1}
.insight h3{font-size:1rem; line-height:1.32; margin-top:7px; margin-bottom:14px; text-wrap:pretty}
/* the 14px minimum gap lives on the h3, NOT here: `margin-block-start` is the
   same property as `margin-top` and would have overwritten the `auto` that
   pins "Read →" to the bottom of every card. */
.insight .more{display:flex; align-items:center; gap:8px; margin-top:auto; padding-top:13px;
  border-top:1px solid var(--line-2); font-size:var(--lbl); font-weight:var(--lbl-w);
  color:var(--mut); transition:color .25s}
.insight .more span{display:inline-block; transition:transform .25s var(--e-out)}
.insight:hover .more,.insight:focus-visible .more{color:var(--on-card-accent)}
.insight:hover .more span,.insight:focus-visible .more span{transform:translateX(4px)}
/* two-up: the frame widens, so flatten the crop rather than let it grow tall */
@media(max-width:1100px){ .insights{grid-template-columns:repeat(2,1fr)}
  .insight figure.ph{aspect-ratio:21/9} .insight h3{font-size:1.06rem} }

/* ================= 09 CAREERS — workplace photography + roles ============ */
.careers-grid{display:grid; grid-template-columns:minmax(0,.72fr) minmax(0,1.28fr); gap:clamp(24px,3.2vw,52px); align-items:stretch}
.careers-fig{position:relative; margin:0; border-radius:var(--r-lg); min-height:280px;
  border:1px solid var(--card-bd); box-shadow:var(--sh-3)}
/* The chip look is split from the placement: the homepage careers band drops
   its figure, so its open-role `.pin` moves into the section head and must keep
   the chip without inheriting `position:absolute`. /careers is unchanged. */
.careers-fig figcaption,.pin{
  font-size:var(--lbl-sm); font-weight:var(--lbl-w);
  color:var(--on-card); background:var(--glass); backdrop-filter:blur(8px);
  border:1px solid var(--card-bd); border-radius:999px; padding:7px 13px; line-height:1}
.careers-fig figcaption,.careers-fig .pin{position:absolute; z-index:2}
.careers-fig figcaption{inset-inline-start:16px; bottom:16px}
.careers-fig .pin{top:16px; inset-inline-start:16px}
@media(max-width:900px){ .careers-grid{grid-template-columns:1fr} .careers-fig{aspect-ratio:16/9; min-height:0} }
@media(max-width:420px){ .careers-fig figcaption{inset-inline-end:16px} }


/* ---- light-theme half of the exposure trim -----------------------------
   Declared after every component rule so it wins on order, not on hacks.
   Each photograph carries --ph-b (dark) and --ph-b-l (light) inline; the two
   grades are non-linear, so one number could not serve both themes. */
[data-theme="light"] .ph>img{filter:var(--img-fx) brightness(var(--ph-b-l)) contrast(var(--ph-c-l))}
[data-theme="light"] .ph.soft>img,
[data-theme="light"] .idx .thumb>img{filter:var(--img-fx-soft) brightness(var(--ph-b-l)) contrast(var(--ph-c-l))}
[data-theme="light"] .about-fig .plate img,
[data-theme="light"] .about-fig .inset img,
[data-theme="light"] .svc-img{filter:var(--img-fx) brightness(var(--ph-b-l)) contrast(var(--ph-c-l))}

/* =========================================================================
   RESPONSIVE — declared last so it wins over the component rules above
   ========================================================================= */
/* the schematic never shrinks below legibility — the text column stacks instead */
@media(max-width:1180px){
  .idx.cap .item{
    grid-template-columns:auto minmax(0,1fr) var(--dia-w) auto;
    grid-template-areas:"n nm dia ar" "n ds dia ar";
    row-gap:6px;
  }
  .idx.cap .n{align-self:start; padding-top:3px}
  .idx.cap .ds{max-width:52ch}
}
@media(max-width:1040px){ :root{ --dia-w:184px } }
@media(max-width:980px){
  /* the stage is gone below 980px, so "active row" has nothing to point at —
     drop the highlight rather than leave a state with no consequence */
  .idx .item.is-active{background:none}
  .idx .item.is-active::before{transform:scaleY(0)}
  .idx .item.is-active .n,.idx .item.is-active .nm,.idx .item.is-active .role{transform:none}
}
@media(max-width:900px){
  /* default reflow: no thumbnail, so the copy takes the whole width. */
  .idx .item,.idx a{grid-template-columns:minmax(0,1fr);
    grid-template-areas:"n" "nm" "ds"; column-gap:16px; row-gap:5px;
    align-items:center; padding-block:16px; padding-inline:8px; min-height:0}
  .idx .item:hover,.idx .item:focus-visible{padding-inline-start:8px}
  .idx .ar{display:none}
  .idx .ds{max-width:none; font-size:.88rem}
  /* opt-in: the services list carries a 76px thumbnail into the reflow */
  .idx.thumbs .item{grid-template-columns:76px minmax(0,1fr);
    grid-template-areas:"th n" "th nm" "th ds"}
  .idx.thumbs .thumb{display:block; grid-row:1/4; align-self:center}
  .idx.cap .item{grid-template-columns:auto minmax(0,1fr);
    grid-template-areas:"n nm" "ds ds" "dia dia"; row-gap:8px; padding-inline:8px}
  .idx.cap .n{align-self:center; padding-top:0}
  .idx.cap .ds{max-width:none}
  .dia-wrap{max-width:300px; margin-top:2px}
}
@media(max-width:640px){
  :root{ --band-pad:40px; --dia-w:200px }
  .band .sec-head{margin-bottom:18px}
  .h-sec,.band .h-sec{font-size:1.85rem}
  /* hold at least two columns on a phone — full-width blocks of two-line copy
     were the single biggest source of mobile height */
  .insights{grid-template-columns:repeat(2,1fr); gap:10px}
  .insight>div{padding:11px 12px 13px}
  .insight h3{font-size:.92rem; line-height:1.3; margin-top:5px; margin-bottom:11px}
  .insight .more{padding-top:10px}
  .idx .ds{font-size:.84rem; line-height:1.5}
  .dia-wrap{max-width:240px; padding:5px 6px}
  .ind .cell .pbody{--scrim-pad:60px; padding-inline:16px}
  .ind .cell h3{font-size:1.22rem}
  .ind .cell p{font-size:.845rem; line-height:1.55}
  .stories{gap:10px}
  .story{padding:16px 15px; gap:11px}
  .story p{font-size:.86rem; line-height:1.55}
  /* I2 — the card is now band + plate, so its height is the sum of the two;
     a min-height would only add dead card under the paragraph. */
  .ind{gap:10px}
  .tl-card{padding:14px}
  /* real 44px thumb targets without moving the surrounding layout */
  .contact dd a,.foot-contact dd a{display:inline-flex; align-items:center; min-height:44px; margin-block:-11px}
  .foot-col ul{gap:0}
  .foot-col a{display:inline-flex; align-items:center; min-height:44px}
  footer.site .soc a{width:44px; height:44px}
  /* the base footer collapses to one column at 520px — the same "too much
     whitespace" failure this pass exists to fix */
  .foot-cols{grid-template-columns:1fr 1fr; gap:26px 24px; padding-bottom:26px; margin-bottom:22px}
  .foot-brand{grid-column:1/-1}
  footer.site{padding-block:28px}
}
/* two columns hold all the way to 320px — density over drama on a phone */
@media(max-width:460px){
  .idx.thumbs .item{grid-template-columns:60px minmax(0,1fr); column-gap:13px}
  .idx.thumbs .thumb{width:60px}
  .insight figure.ph,.insight figure.ph>img{aspect-ratio:3/2}
  .insight h3{font-size:.88rem}
}

/* ---- staggered reveals -------------------------------------------------- */
.reveal[data-d="4"]{transition-delay:.32s}
.reveal[data-d="5"]{transition-delay:.40s}

/* =========================================================================
   ARABIC TYPOGRAPHY + RTL LAYOUT
   Funnel Display is Latin-only; swap to Arabic faces and neutralise the
   Latin design's negative/tracked letter-spacing (which breaks AR shaping).
   Numerals stay Western for stats consistency.
   ========================================================================= */
[dir="rtl"]{
  --font-display:'Noto Kufi Arabic','Funnel Display',ui-sans-serif,system-ui,sans-serif;
  --font-body:'IBM Plex Sans Arabic','Funnel Display',ui-sans-serif,system-ui,sans-serif;
}
/* Reset all tracking for Arabic — negative/positive letter-spacing corrupts joins */
[dir="rtl"] *{letter-spacing:normal !important}
/* Arabic (Noto Kufi) tashkeel/diacritics need more vertical room than the tight
   Latin display leading, or the marks clip/collide between lines. */
[dir="rtl"] h1,[dir="rtl"] h2,[dir="rtl"] h3{line-height:1.42}
[dir="rtl"] .display{line-height:1.36}
[dir="rtl"] .h-sec{line-height:1.42}
/* S3's tightened leading is a Latin metric — Arabic at .96 collides. Tighten
   from the RTL base instead, and keep the specificity above `.band .svc-panel
   .h-sec` (0,3,0). */
[dir="rtl"] .band .svc-panel .h-sec{line-height:1.34}
[dir="rtl"] .vmv .row q,[dir="rtl"] .about-copy .big,[dir="rtl"] .story .kname,[dir="rtl"] .idx .nm,[dir="rtl"] .careers .role,[dir="rtl"] .factor p,[dir="rtl"] .prose h2,[dir="rtl"] .prose h3,[dir="rtl"] .insight h3,
[dir="rtl"] .statement,[dir="rtl"] .ft-title,[dir="rtl"] .wcard .kname,[dir="rtl"] .detail-title{line-height:1.5}

/* header: nav auto-margin already logical; hamburger already logical */
/* directional arrows: mirror the "→" glyph to "←" and reverse hover motion */
/* (no `display` here — the arrow is a grid item, so it is blockified already
   and `scaleX` applies. Setting `display:inline-block` would out-specify the
   `display:none` the mobile reflow relies on and drop the glyph into an
   implicit grid row.) */
[dir="rtl"] .idx .ar{transform:scaleX(-1)}
[dir="rtl"] .idx .item:hover .ar{transform:scaleX(-1) translateX(4px)}
[dir="rtl"] .story-more span{display:inline-block; transform:scaleX(-1)}
/* (.vistay-site's ↗ is no longer a character in the text stream — it is a
   masked ::after, so it takes no part in bidi at all. Its RTL mirror lives
   with the rest of the Vistay block.) */

/* mobile nav: slide in from the left (start edge) */
[dir="rtl"] .mnav-panel{right:auto; left:0; border-left:0; border-right:1px solid var(--line); transform:translateX(-100%)}
[dir="rtl"] .mnav.open .mnav-panel{transform:none}

/* skip link lands on the start (right) edge */
[dir="rtl"] .skip:focus{left:auto; right:16px}

/* Latin-content fields keep LTR flow but align to the reading (right) edge */
[dir="rtl"] input[type="email"],[dir="rtl"] input[type="tel"]{direction:ltr; text-align:right}
/* phone / email display strings stay LTR so digits and "+" don't reorder */
[dir="rtl"] a[href^="tel:"],[dir="rtl"] a[href^="mailto:"]{direction:ltr; unicode-bidi:embed; display:inline-block}

/* quote marks: mirror the decorative quote so it opens on the right */
[dir="rtl"] .vmv .row q::before{content:"”"}
[dir="rtl"] .vmv .row q::after{content:"“"}

/* -------------------------------------------------------------------------
   RTL — the D-merged components
   Everything positional in the merge layer is already logical (inset-inline,
   grid-template-areas, padding-inline). What is left is the handful of things
   CSS cannot mirror for us: gradient angles, transform origins, translate
   signs, glyph direction and the schematics.
   ------------------------------------------------------------------------- */

/* the hairline runs out of the eyebrow towards the reading edge */
[dir="rtl"] .band .sec-head .k::after{background:linear-gradient(270deg,var(--line) 0%,transparent 88%)}

/* progress meters fill from the start (right) edge */
[dir="rtl"] .sprog i{transform-origin:right center;
  background:linear-gradient(270deg,color-mix(in srgb,var(--primary) 88%,transparent),var(--accent))}
[dir="rtl"] .tl-fill{transform-origin:right center;
  background:linear-gradient(270deg,var(--primary),var(--accent-text))}

/* the row slide follows the reading direction */
[dir="rtl"] .idx .item:hover .n,[dir="rtl"] .idx .item:hover .nm,[dir="rtl"] .idx .item:hover .role,
[dir="rtl"] .idx .item:focus-visible .n,[dir="rtl"] .idx .item:focus-visible .nm,[dir="rtl"] .idx .item:focus-visible .role,
[dir="rtl"] .idx .item.is-active .n,[dir="rtl"] .idx .item.is-active .nm,[dir="rtl"] .idx .item.is-active .role{transform:translateX(-7px)}
[dir="rtl"] .idx .item:hover .ar,[dir="rtl"] .idx .item.is-active .ar,
[dir="rtl"] .idx .item:focus-visible .ar{transform:scaleX(-1) translateX(4px)}

/* the story's top rule draws out of the start (right) corner */
[dir="rtl"] .story::before{transform-origin:100% 50%}

/* "Read →" mirrors to "Read ←" and travels the other way */
[dir="rtl"] .sec-head .sec-more>span{transform:scaleX(-1)}
[dir="rtl"] .sec-head .sec-more:hover>span,[dir="rtl"] .sec-head .sec-more:focus-visible>span{transform:scaleX(-1) translateX(4px)}
[dir="rtl"] .insight .more span{transform:scaleX(-1)}
[dir="rtl"] .insight:hover .more span,[dir="rtl"] .insight:focus-visible .more span{transform:scaleX(-1) translateX(4px)}

/* 04 PRODUCTS — the schematics read right-to-left too. The whole drawing is
   mirrored, then every label is flipped back about its own box (fill-box) so
   the glyphs stay readable while sitting at the mirrored position. `direction`
   is pinned to ltr on the text so `text-anchor:start/end` keeps meaning
   "geometric left/right" and the double flip resolves exactly. */
[dir="rtl"] .dia{transform:scaleX(-1)}
/* A tick is a DIRECTIONAL glyph, like a letter: the board-level scaleX(-1) that
   correctly reverses the flow arrows turned the two approval checks into
   downward "V"s. Same un-flip the labels below get, about the shape's own box so
   it stays where it was drawn. Flow arrows deliberately do NOT carry this — in
   RTL they should point right-to-left. */
[dir="rtl"] .dia .nomirror{transform:scaleX(-1); transform-box:fill-box; transform-origin:center}
[dir="rtl"] .dia text{transform:scaleX(-1); transform-box:fill-box; transform-origin:center;
  direction:ltr; unicode-bidi:isolate}

/* =========================================================================
   INNER PAGES
   Phase 2: every page that is not the landing page, built from the SAME
   system — the surface ramp with its textures, one band rhythm, one card
   frame, one hover gesture, one photo treatment. Declared after the RTL layer
   so these components can carry their own mirroring where CSS cannot infer it.
   ========================================================================= */

/* ---- each band publishes its own surface, so a component sitting inside it
       (a photographic backdrop's scrim, a chip) can blend with it instead of
       guessing. ---------------------------------------------------------- */
.band{--band-surf:var(--surf-1)}
.band.tint{--band-surf:var(--surf-2)}
.band.deep{--band-surf:var(--surf-3)}

/* ---- the opening band of an inner page clears the fixed 74px header ----- */
.band.page-head{padding-top:calc(74px + clamp(30px,4.6vw,58px))}
/* a head band with nothing under the heading (the legal pages) doesn't carry
   the section-head's trailing margin as well as the band's own padding */
.page-head .sec-head:last-child{margin-bottom:0}
.page-lede{max-width:64ch; margin-top:clamp(14px,1.6vw,20px); color:var(--ink-2);
  font-size:clamp(1.02rem,1.35vw,1.16rem); line-height:1.62; text-wrap:pretty}

/* ---- BP — a band whose surface IS a photograph -------------------------
   Same stack as the landing page's contact band: exposure-trimmed picture,
   brand duotone, then a directional scrim that is opaque under the copy and
   lets the photograph breathe on the far side. Text over it is measured AA in
   both themes because the scrim resolves to the band's own surface colour. */
.bp{position:relative; isolation:isolate; overflow:clip}
.bp-bg{position:absolute; inset:0; z-index:0; isolation:isolate}
.bp-bg img{width:100%; height:100%; object-fit:cover; object-position:50% 42%;
  filter:var(--img-fx-soft) brightness(var(--ph-b)) contrast(var(--ph-c))}
.bp-bg::before{content:""; position:absolute; inset:0; z-index:1;
  background:var(--card-tint); mix-blend-mode:var(--tint-blend); opacity:var(--tint-op)}
.bp-bg::after{content:""; position:absolute; inset:0; z-index:2; background:linear-gradient(90deg,
  var(--band-surf) 0%,
  color-mix(in srgb,var(--band-surf) 92%,transparent) 34%,
  color-mix(in srgb,var(--band-surf) 42%,transparent) 68%,
  color-mix(in srgb,var(--band-surf) 14%,transparent) 100%)}
[data-theme="light"] .bp-bg img{filter:var(--img-fx-soft) brightness(var(--ph-b-l)) contrast(var(--ph-c-l))}
/* a photographic band sitting UNDER the fixed header needs a vertical veil as
   well: until you scroll, the header is transparent and its nav would sit on
   bare photography. (These bands never carry a texture class, so ::before is
   free — the grain still paints from ::after.) */
.page-head.bp::before{content:""; position:absolute; inset:0 0 auto 0; height:180px; z-index:1; pointer-events:none;
  background:linear-gradient(180deg,color-mix(in srgb,var(--band-surf) 88%,transparent) 0,
    color-mix(in srgb,var(--band-surf) 44%,transparent) 46%,transparent 100%)}
[dir="rtl"] .bp-bg::after{background:linear-gradient(270deg,
  var(--band-surf) 0%,
  color-mix(in srgb,var(--band-surf) 92%,transparent) 34%,
  color-mix(in srgb,var(--band-surf) 42%,transparent) 68%,
  color-mix(in srgb,var(--band-surf) 14%,transparent) 100%)}

/* the address chip, shared by the contact header and the closing CTA */
.c-meta{margin-top:22px; display:flex; flex-wrap:wrap; gap:8px}
.c-meta span{display:inline-flex; align-items:center; gap:9px;
  font-size:var(--lbl);
  color:var(--ink-2); border:1px solid var(--card-bd); border-radius:999px; padding:7px 14px; line-height:1;
  background:color-mix(in srgb,var(--band-surf) 88%,transparent)}
.c-meta svg{width:12px; height:12px; flex:none; color:var(--accent-text)}

/* ---- the closing CTA band — the landing page's contact band, reused ----- */
.cta-band{border-block-start:1px solid var(--line-2)}
.cta-inner{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:26px 44px}
.cta-copy{max-width:34rem}
.cta-inner .eyebrow{display:block; margin-bottom:14px}
.cta-inner h2{max-width:18ch; font-size:clamp(1.85rem,3.8vw,3rem); text-wrap:balance}
.cta-actions{display:flex; flex-wrap:wrap; gap:12px; align-items:center}
/* The band's scrim is directional: it is opaque under the copy and lets the
   photograph breathe on the far side — which is exactly where .cta-actions sits.
   A transparent ghost button therefore put --ink straight on the picture (3.44:1
   light, 1.86:1 at the worst pixel). It gets the same local backing the address
   chip already uses, so it carries its own contrast instead of borrowing the
   scrim's. */
.cta-band .btn.ghost{background:color-mix(in srgb,var(--band-surf) var(--ghost-plate),transparent);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px); border-color:var(--ctrl-bd)}
.cta-band .btn.ghost:hover{border-color:var(--ink);
  background:color-mix(in srgb,var(--band-surf) 94%,transparent)}

/* ---- BRAND PLATE — the treatment for artwork that is a MARK, not a
   photograph. The success stories' cover asset is the client's logo, and a
   logo cropped to 16/9 and pushed through the duotone reads as a mistake. It
   gets the same white plate the client wall uses instead: contained, full
   colour, never cropped. -------------------------------------------------- */
/* the image is taken out of flow for the same reason every `.ph` is: the frame
   owns the geometry (its aspect-ratio), so a percentage height has a definite
   box to resolve against and `object-fit:contain` can actually letterbox. */
.plate-fig{position:relative; overflow:hidden; background:var(--plate-bg)}
/* padding in PERCENT (of the frame's width), not px: the mark is held in a box
   proportional to the frame, so a square badge cannot fill a 16:9 plate while
   a wordmark beside it sits as a thin strip. `figure.` because the related
   strip's card rule `.insight figure img` (object-fit:cover) would otherwise
   win and crop every wide wordmark to its middle letters. */
figure.plate-fig img{position:absolute; inset:0; width:100%; height:100%; object-fit:contain;
  padding:12% 22%; filter:none}
.insight figure.plate-fig{aspect-ratio:16/10}
.mh-fig.plate-fig{aspect-ratio:3/2}
.wcard figure.plate-fig{aspect-ratio:16/9}

/* ---- one panel: the card frame for anything that is not a link tile ----- */
.panel{background:var(--card-bg); border:1px solid var(--card-bd); border-radius:var(--r-lg);
  box-shadow:var(--sh-1); padding:clamp(20px,2.4vw,32px)}

/* ================= LIST PAGES =========================================== */

/* the lead article, at feature size (insights) */
.feature{position:relative; display:grid; grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr);
  overflow:hidden; border:1px solid var(--card-bd); border-radius:var(--r-lg);
  background:var(--card-bg); box-shadow:var(--sh-2);
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
.feature:hover,.feature:focus-visible{transform:translateY(var(--lift)); box-shadow:var(--sh-3); border-color:var(--card-bd-h)}
.feature:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.feature figure.ph{margin:0; min-height:clamp(230px,26vw,340px); height:100%}
.feature:hover figure.ph>img,.feature:focus-visible figure.ph>img{transform:scale(1.05)}
.ft-body{padding:clamp(22px,2.8vw,42px); display:flex; flex-direction:column; justify-content:center}
.kdate{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--on-card-accent)}
.ft-title{font-family:var(--font-display); font-weight:400; font-size:clamp(1.45rem,2.7vw,2.15rem);
  line-height:1.12; letter-spacing:-.015em; color:var(--ink); margin-top:11px; text-wrap:balance}
.ft-sum{margin-top:15px; color:var(--mut); font-size:.97rem; line-height:1.62; max-width:52ch}
.feature .more{display:inline-flex; align-items:center; gap:8px; margin-top:clamp(20px,2.4vw,28px);
  font-size:var(--lbl); font-weight:var(--lbl-w);
  color:var(--mut); transition:color .25s}
.feature .more span{display:inline-block; transition:transform .25s var(--e-out)}
.feature:hover .more,.feature:focus-visible .more{color:var(--on-card-accent)}
.feature:hover .more span,.feature:focus-visible .more span{transform:translateX(4px)}
[dir="rtl"] .feature .more span{transform:scaleX(-1)}
[dir="rtl"] .feature:hover .more span,[dir="rtl"] .feature:focus-visible .more span{transform:scaleX(-1) translateX(4px)}

/* the archive grid: the landing page's card, three-up on a full page */
.insights.three{grid-template-columns:repeat(3,1fr)}
.insight .ins-sum{color:var(--mut); font-size:.87rem; line-height:1.55; margin:0 0 13px; max-width:none}
.insight h3+.ins-sum{margin-top:-4px}
.insights.three .insight h3{font-size:1.05rem; margin-bottom:10px}

/* success stories, now photographic */
.wcards{display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(12px,1.4vw,18px)}
.wcard{position:relative; display:grid; grid-template-rows:auto 1fr; overflow:hidden;
  border:1px solid var(--card-bd); border-radius:var(--r-lg); background:var(--card-bg); box-shadow:var(--sh-1);
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
.wcard::before{content:""; position:absolute; inset:0 auto auto 0; z-index:3; width:100%; height:2px;
  background:var(--accent); transform:scaleX(0); transform-origin:0 50%; transition:transform var(--t-hover) var(--e-out)}
[dir="rtl"] .wcard::before{transform-origin:100% 50%}
.wcard:hover::before,.wcard:focus-visible::before{transform:scaleX(1)}
.wcard:hover,.wcard:focus-visible{transform:translateY(var(--lift)); box-shadow:var(--sh-3); border-color:var(--card-bd-h)}
.wcard:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.wcard figure{margin:0; aspect-ratio:16/9; border-bottom:1px solid var(--card-bd)}
.wcard:hover figure.ph>img,.wcard:focus-visible figure.ph>img{transform:scale(1.05)}
.wbody{padding:clamp(17px,1.9vw,24px); display:flex; flex-direction:column; gap:13px}
.wcard .top{display:flex; align-items:center; gap:14px}
.wcard .chip{flex:none; background:var(--plate-bg); border:1px solid var(--card-bd); border-radius:var(--r-sm);
  width:74px; height:48px; padding:7px 9px; display:grid; place-items:center}
[data-theme="dark"] .wcard .chip{border-color:transparent}
.wcard .chip img{max-height:30px; width:auto; object-fit:contain}
.wcard .ktag{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--on-card-accent)}
.wcard .kname{font-family:var(--font-display); font-weight:420; font-size:1.28rem; line-height:1.16; color:var(--ink); margin-top:4px}
.wcard p{color:var(--mut); font-size:.9rem; line-height:1.6; margin-bottom:auto; text-wrap:pretty}
.wcard .story-more{display:inline-flex; align-items:center; gap:8px; padding-top:12px;
  border-top:1px solid var(--line-2); color:var(--on-card-accent); transition:color .25s}
.wcard .story-more span{display:inline-block; transition:transform .25s var(--e-out)}
.wcard:hover .story-more span,.wcard:focus-visible .story-more span{transform:translateX(4px)}
[dir="rtl"] .wcard:hover .story-more span,[dir="rtl"] .wcard:focus-visible .story-more span{transform:scaleX(-1) translateX(4px)}

/* key factors: the same tile as everything else on the page */
.factors{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(12px,1.4vw,18px)}
.factor{background:var(--card-bg); border:1px solid var(--card-bd); border-radius:var(--r-lg);
  box-shadow:var(--sh-1); padding:clamp(20px,2.2vw,30px); min-height:clamp(150px,15vw,190px);
  transition:transform var(--t-hover) var(--e-out),box-shadow var(--t-hover) var(--e-out),border-color var(--t-hover) var(--e-out)}
.factor:hover{transform:translateY(var(--lift)); box-shadow:var(--sh-2); border-color:var(--card-bd-h)}
.factor p{text-wrap:pretty}

/* /about/ — the landing page's composition, on the page's own head band */
.about-sec .about-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(28px,4vw,64px); align-items:center}
.about-sec .about-copy .prose{max-width:56ch}

/* ================= DETAIL PAGES ========================================= */

/* --- masthead: copy on the ramp, the cover inside the duotone system ---- */
.masthead{display:grid; grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:clamp(24px,3.6vw,56px); align-items:center}
.masthead.no-fig{grid-template-columns:minmax(0,1fr)}
.masthead.no-fig .mh-copy{max-width:64ch}
/* A SCREENSHOT NEEDS WIDTH, NOT A COLUMN.
   The two-column masthead gives the figure ~48% of the measure — about 520px —
   which is right for a photograph and useless for an interface: the Together
   capture is 2551px of dense product UI, and at 520px none of it can be read,
   which is the whole reason the capture is there. Stacked, the same frame is
   the full content width (~1136px at 1440), better than twice the area, and
   the copy above keeps its own reading measure rather than stretching to the
   band. Photographic mastheads are untouched. */
.masthead.is-ui{grid-template-columns:minmax(0,1fr)}
.masthead.is-ui .mh-copy{max-width:64ch}
.detail-head .eyebrow a{display:inline-flex; align-items:center; gap:12px; color:var(--mut); transition:color .25s}
.detail-head .eyebrow a::before{content:""; width:30px; height:1px; background:var(--accent); flex:none}
.detail-head .eyebrow a:hover{color:var(--on-card-accent)}
.detail-title{max-width:20ch; margin-top:15px; font-size:clamp(1.95rem,4vw,3.3rem); text-wrap:balance}
.detail-lede{margin-top:19px; max-width:52ch; font-size:clamp(1.02rem,1.4vw,1.2rem); color:var(--ink-2); line-height:1.6}
.detail-meta{display:flex; flex-wrap:wrap; gap:8px 18px; margin-top:20px;
  font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut)}
.detail-meta span{display:inline-flex; align-items:center}
.detail-meta span+span::before{content:"·"; margin-inline-end:18px; color:var(--faint)}
.mh-fig{margin:0; aspect-ratio:3/2; border-radius:var(--r-lg); border:1px solid var(--card-bd); box-shadow:var(--sh-3)}
.mh-fig:hover>img{transform:scale(1.04)}

/* ---- INTERFACE CAPTURES: a screenshot is not a photograph ---------------
   Measured on /products/together before this existed: `.mh-fig ph ph-veil soft`
   put the Together dashboard through `grayscale(1) contrast(1.12)
   brightness(.88)` in light — saturate(.70) in dark — blended an rgb(29,74,167)
   duotone over it via `.ph::after`,
   hazed it again with `.ph-veil::before`, then cropped 15.6% of its width —
   `aspect-ratio:3/2` (Rc 1.5) against a 1400x788 capture (Rs 1.7766) shows only
   Rc/Rs of the frame. That removed the navigation rail and the check-in control
   and left the product's own interface unreadable. Every one of those four
   treatments is correct for a photograph and wrong for a screen.

   So a capture gets its own object: native ratio, no crop, no filter, and a
   chrome bar carrying the address the product actually answers on — which makes
   the frame double as evidence it is live rather than a mockup. The dots stay
   neutral; coloured traffic lights would be an impression of someone else's
   operating system, and this is a web app. */
.mh-fig.ui-frame{aspect-ratio:auto; overflow:hidden; background:var(--surf-1); display:block}
.ui-chrome{display:flex; align-items:center; gap:clamp(8px,1vw,12px);
  padding:9px clamp(10px,1.1vw,14px);
  background:var(--bg-2); border-block-end:1px solid var(--card-bd)}
.ui-dots{display:inline-flex; gap:5px; flex:none}
.ui-dots i{display:block; width:8px; height:8px; border-radius:50%; background:var(--ctrl-bd)}
/* the address pill is flow-relative and ellipsised: at the narrowest column it
   truncates instead of pushing the dots out of the bar. */
.ui-addr{flex:1; min-width:0; text-align:center;
  font-size:var(--lbl-sm); line-height:1.5;
  color:var(--ink-2); background:var(--surf-1);
  border:1px solid var(--card-bd); border-radius:999px; padding:3px 12px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* height:auto, NOT the absolute-positioned `.ph>img` — that is what makes this
   uncropped. The capture sets the figure's height and nothing is thrown away. */
.ui-screen{display:block; background:var(--bg-3)}
.ui-screen img{display:block; width:100%; height:auto}
/* the related-card variant: still a `.ph` (same card rhythm as its neighbours)
   with the photographic layers switched off. `--img-fx` is saturate(.60) on the
   dark-first `:root` and grayscale(1) in the light override, and it reaches the
   image through TWO rules — `.ph>img` and `[data-theme="light"] .ph>img` — so
   both have to be reset or the capture stays grey in exactly one theme. */
.ph.ui-thumb>img,[data-theme="light"] .ph.ui-thumb>img{filter:none; object-position:50% 0}
.ph.ui-thumb::after{display:none}

/* --- body: one prose measure beside one facts rail ---------------------- */
.detail-cols{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,264px);
  gap:clamp(26px,3.6vw,60px); align-items:start}
.detail-copy .prose{max-width:70ch}
/* the summary, when the CMS body only repeated it: the page still lands on a
   statement instead of a void. */
.statement{position:relative; padding-inline-start:22px; max-width:26ch;
  font-family:var(--font-display); font-weight:380; font-size:clamp(1.4rem,2.5vw,2.05rem);
  line-height:1.2; letter-spacing:-.015em; color:var(--ink); text-wrap:pretty}
.statement::before{content:""; position:absolute; inset-block:6px auto; inset-inline-start:0;
  width:2px; height:calc(100% - 12px); background:var(--accent); border-radius:2px}
.rail{position:sticky; top:94px}
.rail-card{display:flex; flex-direction:column; gap:15px; padding:clamp(17px,1.8vw,22px);
  background:var(--card-bg); border:1px solid var(--card-bd); border-radius:var(--r-lg); box-shadow:var(--sh-1)}
.rail-h{font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut)}
.facts{display:grid; gap:12px; margin:0; padding-top:14px; border-top:1px solid var(--line-2)}
.facts dt{font-size:var(--lbl); color:var(--mut); margin-bottom:3px}
.facts dd{margin:0; color:var(--ink); font-size:.97rem; line-height:1.4; font-variant-numeric:tabular-nums}
.rail .chip{background:var(--plate-bg); border:1px solid var(--card-bd); border-radius:var(--r-sm);
  padding:9px 11px; display:grid; place-items:center; min-height:52px}
[data-theme="dark"] .rail .chip{border-color:transparent}
.rail .chip img{max-height:30px; width:auto; object-fit:contain}
.rail .btn{justify-content:center}

/* --- careers: the apply block ------------------------------------------ */
.apply-grid{align-items:stretch}
.apply-meta.panel{display:flex; flex-direction:column; justify-content:space-between; gap:22px}
.apply-meta.panel dl{margin:0; border-top:0; padding-top:0}
.apply-meta.panel .apply-note{margin:0}
.apply-form.panel{padding:clamp(20px,2.6vw,34px)}

/* --- related: the landing page's card, as many columns as there are cards */
.related h2 a{transition:color .25s}
.related h2 a:hover{color:var(--on-card-accent)}
.insights.three.n2{grid-template-columns:repeat(2,1fr)}
.insights.three.n1{grid-template-columns:minmax(0,440px)}

/* ================= CONTACT / LEGAL / 404 ================================ */
/* the two cards are one row: the info panel stretches to the form's height and
   distributes its own content, so the band never ends on half an empty card. */
.contact-grid{grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr); align-items:stretch}
.contact-info.panel{display:flex; flex-direction:column; justify-content:space-between; gap:24px}
.contact-info.panel dl{margin:0}
.contact-info .soc{margin-top:0}
.legal .panel{max-width:84ch; margin-inline:auto}
.legal .prose>*:first-child{margin-top:0}
.notfound{display:flex; align-items:center; min-height:clamp(420px,64svh,620px)}
.notfound>.wrap{width:100%}
.notfound .eyebrow{display:inline-flex; align-items:center; gap:12px}
.notfound .eyebrow::before{content:""; width:34px; height:1px; background:var(--accent)}

/* ================= RESPONSIVE — declared after the components =========== */
@media(max-width:1100px){
  .insights.three,.insights.three.n2{grid-template-columns:repeat(2,1fr)}
  .insights.three.n1{grid-template-columns:minmax(0,1fr)}
  .insights.three .insight figure.ph,.insights.three .insight figure.plate-fig{aspect-ratio:21/9}
}
@media(max-width:980px){
  .detail-cols{grid-template-columns:minmax(0,1fr); gap:clamp(24px,3vw,32px)}
  .rail{position:static}
  .rail-card{flex-direction:row; flex-wrap:wrap; align-items:center; gap:14px 22px}
  .rail-h{width:100%}
  .facts{flex:1 1 260px; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); border-top:0; padding-top:0}
  .rail .btn{margin-inline-start:auto}
  .statement{max-width:40ch}
  .factors{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:900px){
  .masthead,.masthead.no-fig{grid-template-columns:minmax(0,1fr); gap:clamp(20px,3vw,28px)}
  /* `.mh-fig.ui-frame` is 0,2,0 so it already outranks this, but the ORDER
     matters on its own: the capture belongs above the copy here too. */
  .mh-fig{aspect-ratio:16/9; order:-1}
  .mh-fig.ui-frame{aspect-ratio:auto}
  .contact-grid,.apply-grid{grid-template-columns:minmax(0,1fr)}
}
@media(max-width:860px){
  /* the landing page collapses this composition at 860px too — its rule is
     id-scoped to #about, so the page version needs its own. */
  .about-sec .about-grid{grid-template-columns:minmax(0,1fr)}
}
@media(max-width:820px){
  .feature{grid-template-columns:minmax(0,1fr)}
  .feature figure.ph{aspect-ratio:16/9; min-height:0; border-bottom:1px solid var(--card-bd)}
  .wcards{grid-template-columns:minmax(0,1fr)}
  /* The photographic bands' scrim is a 90deg gradient: it is opaque under the
     copy and lets the picture breathe on the far side. That resolves across
     1440px, but at 375px the copy column IS the far side — the gradient runs
     out inside it and drops 16px body copy onto live photography (measured
     1.90:1 at the worst pixel on /ar/contact/, 2.83:1 on the /ar/careers/ CTA
     heading). Mobile gets the vertical form the landing page's contact band
     already carries. Declared after the RTL rule at .bp-bg::after so it wins on
     order, and light-scoped because dark's numbers were clean and this pass
     must not move it. */
  [data-theme="light"] .bp-bg::after,
  [data-theme="light"] [dir="rtl"] .bp-bg::after{background:linear-gradient(180deg,
    color-mix(in srgb,var(--band-surf) 88%,transparent) 0%,
    color-mix(in srgb,var(--band-surf) 74%,transparent) 100%)}
}
@media(max-width:640px){
  .insights.three,.insights.three.n2,.insights.three.n1{grid-template-columns:repeat(2,1fr); gap:10px}
  .insights.three .insight h3{font-size:.92rem}
  .insight .ins-sum{display:none}
  .factors{grid-template-columns:minmax(0,1fr)}
  .cta-inner h2{font-size:1.85rem}
  .cta-actions{width:100%}
  .cta-actions .btn{flex:1 1 auto; justify-content:center}
  .rail-card{flex-direction:column; align-items:stretch}
  .rail .btn{margin-inline-start:0}
  .ft-body{padding:18px 16px 20px}
  .wbody{padding:16px 15px}
  .wcard .kname{font-size:1.18rem}
  .panel{padding:18px 16px}
}
@media(max-width:460px){
  .insights.three .insight figure.ph,.insights.three .insight figure.plate-fig{aspect-ratio:3/2}
  .insights.three .insight h3{font-size:.88rem}
}

/* =========================================================================
   MOTION HYGIENE — under reduced motion EVERYTHING resolves to its final
   state. Nothing hidden, nothing half-drawn, no parallax, no cursor effects.
   Declared last so no component or RTL rule can outrank it on source order.
   ========================================================================= */
@media (prefers-reduced-motion: reduce){
  .reveal{opacity:1; transform:none; transition:none}
  .hero .bg img{height:100%}
  .mnav-panel,.mnav-backdrop{transition:none}
  .sprog{display:none}
  /* schematics: drawn, labelled, complete */
  .dia .st{stroke-dasharray:none; stroke-dashoffset:0; animation:none !important}
  .dia .tx,.dia .fillmut,.dia .fillac,.dia .dash,.dia .sf{opacity:1; animation:none !important}
  .reveal.in .dia .tx,.reveal.in .dia .st{opacity:1; animation:none !important}
  /* timeline: axis filled, all three dots lit, terminal ring present */
  .tl-fill{transform:scaleX(1); transition:none}
  .tl-dot{transition:none}
  .tl-step:last-child .tl-dot::before{transform:none; opacity:1; transition:none}
  @media(max-width:820px){ .tl-fill{transform:scaleY(1)}
    .tl-step:not(:last-child)::after{transform:scaleY(1); transition:none} }
  /* stories rule, meters and photography settle at their end state */
  .story::before,.wcard::before{transform:scaleX(1); transition:none}
  .svc-meter i,.ph>img,.svc-img,.idx .item::before,.tl-card,.insight,.ind .cell,.story,.dia-wrap,
  .feature,.wcard,.factor,.mh-fig>img,.feature .more span,.wcard .story-more span{transition:none !important}
  .story:hover,.insight:hover,.ind .cell:hover,.feature:hover,.wcard:hover,.factor:hover{transform:none}
  /* the Vistay devices land instantly, at their composed offsets */
  /* the detail rail is a plain card, not a follower */
  .rail{position:static}
  *{scroll-behavior:auto !important}
}

.addr{display:inline-flex; align-items:center; gap:8px; color:inherit}
/* Self-sufficient sizing — the partial must not depend on a `.c-meta` ancestor
   existing, or it renders a 300x150px default-sized SVG wherever it doesn't. */
.addr svg{width:12px; height:12px; flex:none; color:var(--accent-text)}
a.addr{text-decoration:none; border-bottom:1px solid var(--uline); transition:border-color .2s ease, color .2s ease}
a.addr:hover{color:var(--ink); border-bottom-color:currentColor}
a.addr:focus-visible{outline:2px solid var(--focus); outline-offset:3px; border-radius:3px}
/* This link is the stakeholder's own request ("حاولت اضغط عليه ما فيش"). A
   single line of address text is ~22px tall — under the 44px touch minimum —
   so size it on coarse pointers rather than by viewport width. */
@media (pointer:coarse){ a.addr{min-height:44px; align-items:center} }

/* ================= COMPACTED HOMEPAGE BANDS ============================= */
/* The four trailing homepage bands keep their identity, their rows and their
   link to the full page — they just stop competing for vertical space with the
   five bands above them. Everything here is reduction: less padding, and the
   photography/schematics that already appear in full on the section's own page
   are dropped from the homepage teaser. No new components, no new copy. */
.band.compact{--band-pad:clamp(32px,3vw,44px)}
.band.compact .sec-head{margin-bottom:clamp(16px,1.6vw,22px)}
.band.compact .h-sec{font-size:clamp(1.6rem,2.8vw,2.3rem)}
/* the open-role count, moved off the deleted careers figure */
.band.compact .sec-head .k .pin{margin-inline-start:2px}

/* insights, without the card photograph: text rows on the band surface */
.band.compact .insights{grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:clamp(12px,1.4vw,20px)}
/* ...but (0,3,0) outranks the two responsive `.insights` rules below (0,1,0 in
   @media 1100 and 640), so `auto-fit` was the only thing deciding the count on
   the homepage: 3 columns for 4 items (an orphan row) from 800-1010px, and ONE
   column below ~470px, where 210px + the gap no longer fits twice. That last
   band is every phone, and it grew the section from 280px to 489px — the exact
   regression the 640px rule was written to prevent. Restate the two-up at the
   compact selector's own specificity. 1010px is where the four cards stop
   fitting three-up; below it, two is the answer at every width. */
@media(max-width:1010px){ .band.compact .insights{grid-template-columns:repeat(2,1fr)} }
.band.compact .insight{grid-template-rows:minmax(0,1fr); overflow:visible;
  border:0; border-radius:0; background:none; box-shadow:none;
  border-block-start:1px solid var(--line-2)}
.band.compact .insight:hover,.band.compact .insight:focus-visible{
  transform:none; box-shadow:none; border-block-start-color:var(--line)}
.band.compact .insight>div{padding:14px 0 0}
.band.compact .insight .more{padding-top:12px}

/* careers, without the workplace figure: the role rows take the full measure */
.band.compact .careers-grid{grid-template-columns:minmax(0,1fr)}

/* The homepage contact band now ends in the same lead form /contact uses, so
   its single copy column becomes two: copy on the reading side, form beside it.
   The form sits in a `.panel` — but in DARK `--card-bg` is a <=5.8%-white
   gradient over a transparent base, so the panel computed to rgba(0,0,0,0) and
   the form's labels/errors composited straight onto the backdrop PHOTOGRAPH:
   3.40:1 for labels and 2.15:1 for the error text, measured off screenshot
   pixels. Light already paints a real white card (#ffffff) and measured 5.99:1,
   so only dark needs a floor. `--surf-3` is the existing top of the dark
   surface ramp (#181c35 since the navy rebase) and is fully opaque, so every string in the form now
   composites against ONE known colour instead of whatever the crop happens to
   be under it. Scoped to this band: every other `.panel` on the site sits on a
   flat `--surf-*` band, not on photography. */
[data-theme="dark"] .contact .contact-form.panel{background:var(--surf-3)}
.contact .grid:has(.contact-form){grid-template-columns:minmax(0,1fr) minmax(0,.94fr);
  align-items:center; gap:clamp(28px,3.4vw,60px)}
.contact .grid:has(.contact-form)>div{max-width:none}
@media(max-width:900px){ .contact .grid:has(.contact-form){grid-template-columns:minmax(0,1fr)}
  .contact .grid:has(.contact-form)>div{max-width:34rem} }

/* Products dropped its schematics on the homepage, so the reserved `dia` column
   holds nothing and only squeezes the summary. Reclaim it at every breakpoint
   the schematic layout defines. /products keeps the schematics and this layout. */
.band.compact .idx.cap .item{
  grid-template-columns:auto clamp(168px,12.4vw,180px) minmax(0,1fr) auto;
  grid-template-areas:"n nm ds ar"}
@media(max-width:1180px){
  .band.compact .idx.cap .item{grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-areas:"n nm ar" "n ds ar"}
}
@media(max-width:980px){
  .band.compact .idx.cap .item{grid-template-columns:auto minmax(0,1fr);
    grid-template-areas:"n nm" "ds ds"}
}
/* the summary inherits a 46ch measure sized for the narrow column beside a
   schematic; with the schematic gone it wraps to three lines and leaves the row
   half empty. Let it use the width it now has. */
.band.compact .idx .ds{max-width:62ch}

/* =========================================================================
   VISTAY — TGS's own product, shown inside band 04 (#work).

   THE BAND IS A CARD IN THE SUCCESS-STORIES SYSTEM, not a bespoke panel: the
   same --card-bg / --card-bd / --r-lg / --sh-1 as `.story` and `.wall`, the
   same clamp(18px,2.2vw,26px) top rhythm as `.wall`, and a header rule shaped
   like `.wall-h` — label at the start edge, link at the end edge. It does NOT
   copy `.story`'s hover lift or accent rule: those are affordances for a
   clickable card, and this one is not a link.

   COLOUR — three loci, one job each:
     1. --on-card-accent  the header dot (the band's accent language).
     2. --vistay green    the bloom behind the hero device, DARK ONLY. Nothing
                          else. It used to tint the region label, both button
                          borders and the site link, where it collided with the
                          app's own green inside the screenshots.
     3. --plate-bg white  the store-badge tray, identical to `.wall-plate`.

   THE WORDMARK is the client's own artwork, one file per theme
   (`partials/vistay-wordmark.njk`): green lettering on light, white on dark.
   It replaced a code-drawn SVG copy of the mark (2026-09-24): client-supplied
   artwork always wins over ours.
   ========================================================================= */
.vistay{
  --vistay:#1BAD5C;
  /* Both store assets are the BLACK lockup with transparent corners (measured:
     full-bleed, ~10px radius at the 88px source). On this card they keep their
     white type but lose their edge, so each badge gets a hairline of its own.
     Apple ships a bordered black badge for exactly this case. A ring on the
     ANCHOR — the artwork is never touched. */
  --badge-edge:rgba(255,255,255,.24);
  --dir:1;                      /* flips the recessed device's offset in RTL */
  margin-block-start:clamp(18px,2.2vw,26px);
  /* the band is a <section>, and site.css:173 gives every section
     padding-block:clamp(72px,12vh,150px). The old build masked that with its
     own padding; this card sets its padding on `.vistay-body`, so the global
     must be zeroed explicitly or the card grows a 120px void at each end AND
     the hero device stops bleeding past the bottom edge. */
  padding-block:0;
  border:1px solid var(--card-bd); border-radius:var(--r-lg);
  background:var(--card-bg); box-shadow:var(--sh-1);
  /* the hero device bleeds past the bottom padding line; this is what crops
     it — and with it the capture's pure-white tab bar. */
  overflow:hidden;
}
[dir="rtl"] .vistay{--dir:-1}
/* #1BAD5C is 6.72:1 on #090B11 but 2.93:1 on #fff; #2D6A4F is the reverse
   (3.08:1 / 6.39:1). The green now only ever paints a decorative bloom, but
   keep the theme swap so it stays usable if anything else ever takes it. */
[data-theme="light"] .vistay{
  --vistay:#2D6A4F;
  --badge-edge:rgba(10,12,18,.16);   /* black artwork on a white card needs only definition */
}

/* --- the header rule: `.wall-h` geometry, anchored at both edges ---------- */
.vistay-h{display:flex; align-items:center; justify-content:space-between; gap:6px 18px;
  flex-wrap:wrap; padding:11px 16px; border-bottom:1px solid var(--line-2);
  font-size:var(--lbl); font-weight:var(--lbl-w); color:var(--mut)}
/* `h3` at site.css:81 forces the display serif, weight 420, line-height 1.02
   and --ink; `.eyebrow` loses to it on exactly those. Override the three that
   differ and inherit the row's own size and weight — do not restate `.eyebrow`. */
.vistay-h h3.eyebrow{font-size:inherit; font-weight:inherit;
  color:var(--mut); line-height:1.4; display:inline-flex; align-items:center; gap:8px}
.vistay-h h3.eyebrow::before{content:""; width:6px; height:6px; border-radius:50%;
  background:var(--on-card-accent); flex:none}
.vistay-site{display:inline-flex; align-items:center; color:var(--ink-2);
  text-decoration:none; transition:color .2s}
.vistay-site:hover{color:var(--ink); text-decoration:underline; text-underline-offset:3px}
.vistay-site:focus-visible{outline:2px solid var(--focus); outline-offset:3px; border-radius:3px}
/* The ↗ is a mask, not a character. As U+2197 it was a bidi-NEUTRAL glyph
   adjacent to the Latin run "vistay.ai", so the bidi algorithm absorbed it
   into the run and rendered it BEFORE the text in RTL. A pseudo-element is not
   in the text stream at all; `margin-inline-start` keeps it logically trailing
   and `scaleX(-1)` points it up-left, outward, at the RTL line end. */
.vistay-site::after{content:""; display:inline-block; width:.66em; height:.66em;
  margin-inline-start:6px; background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7'/%3E%3Cpath d='M8.6 7H17v8.4'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7'/%3E%3Cpath d='M8.6 7H17v8.4'/%3E%3C/svg%3E") center/contain no-repeat}
[dir="rtl"] .vistay-site::after{transform:scaleX(-1)}

/* --- the body: one composition, copy bottom-aligned to the device --------- */
/* --w-hero is the SCREEN width; every dimension of the frame derives from it.
   The ladder is monotone non-decreasing by construction — 292@1600, 288@1440,
   216@1080, 210@1079, 196@1000..640, 191@639, 164@390 — which the previous
   build was not (it rendered 210px at 880 and 133px at 1100). */
.vistay-body{--w-hero:clamp(210px,20vw,292px);
  display:grid; grid-template-columns:minmax(292px,.92fr) minmax(0,1fr);
  gap:clamp(32px,4vw,56px);
  /* NEVER center: `end` is what makes the copy sit on the same baseline the
     device stands on, instead of floating in the middle of its column. */
  align-items:end;
  padding:clamp(24px,3.2vw,40px); padding-block-end:0}
.vistay-copy{padding-block-end:clamp(24px,3.2vw,40px); min-width:0}

/* one wordmark file per theme; only the active theme's is displayed */
.vistay-wm{display:block; height:56px}
.vistay-wm img{display:block; height:100%; width:auto}
[data-theme="dark"] .vistay-wm .th-light,[data-theme="light"] .vistay-wm .th-dark{display:none}
/* the band's only display-serif voice, and its only type above 14px. This is
   PROVENANCE — what TGS did — never a description of what the app is for. */
.vistay-line{margin-block-start:26px; font-family:var(--font-display); font-weight:420;
  font-size:clamp(1.6rem,2.4vw,2.15rem); line-height:1.14; letter-spacing:-.02em;
  color:var(--ink); max-width:13ch; text-wrap:balance}
/* Kufi needs more leading than Fraunces at the same size. */
[dir="rtl"] .vistay-line{line-height:1.32; letter-spacing:normal}

/* three structural facts on one rule — and the thing that makes the copy
   column DECLARE its width instead of stopping at 47% of it. */
/* `auto` columns, not 1fr: at 900px three equal columns are ~103px each and
   "iOS · Android · Web" wrapped to two lines. space-between keeps the row
   spanning the full column — which is what stops the copy stopping at 47% of
   its column — without ever squeezing a value. */
.vistay-meta{display:grid; grid-template-columns:repeat(3,auto);
  justify-content:space-between; gap:0 20px;
  width:100%; margin:30px 0 0; padding-block-start:14px;
  border-block-start:1px solid var(--line-2)}
.vistay-meta dt{font-size:var(--lbl); color:var(--mut)}
.vistay-meta dd{margin:7px 0 0; font-family:var(--font-body); font-size:.82rem;
  font-weight:450; color:var(--ink-2)}

/* --- the store badges ----------------------------------------------------- */
/* Both files are pure rgb(0,0,0) with no gray border (measured at inset
   4/8/12/16px), which is 1.07:1 on the dark card — the silhouette dissolves
   and only the white glyphs float. Apple forbids adding the missing stroke
   ourselves and Google ships no white Play badge (its triangle is
   multi-colour, so inversion destroys the logo). The legal fix is the white
   plate `.wall-plate` already uses for the client logos a hundred pixels up
   this band: 19.7:1 against the dark card, black artwork 21:1 on the plate.
   16px padding and a 16px gutter both clear the ≥11px (¼ × 44) clear-space
   rule the two guidelines specify. */
/* NO TRAY. A white plate behind black artwork read as a slab pasted onto the
   card — the one thing the client called out by name. The badges sit directly
   on the card and carry their own edge (see --badge-edge). Dropping the plate
   also drops the --focus override it needed: on the card each theme's own
   --focus is already correct (the accent on dark, #000000 on light). */
.vistay-badges{display:inline-flex; align-items:center; flex-wrap:wrap; gap:16px; margin-block-start:26px;
  /* App Store first is Apple's stated rule, and these are Latin lockups —
     mirroring them in RTL would put Play at the reading-start edge. The tray
     itself still hugs the RTL start edge, because the column is flow-relative. */
  direction:ltr}
/* 44px satisfies Apple's ≥40, Google's ≥28, and "same size or larger" (equal),
   and is exactly 2× the 88px source so both stay sharp at DPR2. The rendered
   widths are 132.0 and 149.3 — DO NOT equalise them: rescaling one badge
   non-uniformly is forbidden by both guidelines, and the unequal pair is the
   thing that proves this is real store artwork and not a hand-made pill. */
/* `flex:none` is load-bearing, not tidying. As shrinkable flex items the two
   anchors were squeezed by the tray between ~866px and ~821px while the global
   `img{max-width:100%}` clamped the artwork's WIDTH and `height:44px` held its
   height: at 830px the App Store badge rendered 124.8x44 — 2.836:1 against a
   true 3.000:1, a 5.5% non-uniform rescale that both Apple's and Google's
   guidelines forbid, and the exact defect that justified replacing the
   hand-made pills. With `flex:none` the pair keeps its intrinsic size and the
   tray wraps (see `flex-wrap` above) instead of distorting. `object-fit` is
   the same belt `.wall-plate img` wears: if anything ever does clamp the box,
   the artwork letterboxes rather than stretches. */
.vistay-badge{display:block; flex:none; border-radius:6px;
  box-shadow:0 0 0 1px var(--badge-edge)}
.vistay-badge img{display:block; height:44px; width:auto; object-fit:contain}
/* a ring around the ANCHOR, never a transform or opacity change on the
   artwork — Apple: "don't modify, angle, or animate the App Store badge". */
.vistay-badge:hover{box-shadow:0 0 0 2px var(--on-card-accent)}
.vistay-badge:focus-visible{outline:2px solid var(--focus); outline-offset:3px}

/* --- the stage: two devices, the hero breaking the padding line ----------- */
/* THE DEAD-SPACE NUMBER, CORRECTED (2026-08-16).
   The build note for this band claimed "the tallest empty full-width band
   inside the card is 1px". That number reproduces exactly — 0-1px by pixel
   scan at tolerances 6/12/30/60, and 1px by box-union — but the METRIC is
   vacuous: a full-WIDTH band is empty only if every column is empty at that
   row, and the 648px hero spans the card top to bottom, so the figure cannot
   exceed ~1px no matter how much air the other column holds. It measures
   nothing.
   The honest measurement is per COLUMN, taken from the header rule and net of
   the card's own padding, 1440x900:
     copy column     117.2px EN / 144.0px AR   (worst 124.9 / 151.7 at 1600)
     picture column    0.0px both              (worst 75.7 EN / 55.8 AR at 860)
   That void is the arithmetic of two deliberate decisions — `align-items:end`
   below, and a 648px device beside a 433px EN / 406px AR copy stack. Three
   levers were built and measured; none closes it, each only moves it:
     * `--bleed` .34 -> .50 cuts the copy void 37% (124.9 -> 78.1 at 1600) but
       is a see-saw, not a close: below 1080px the COPY is already the taller
       column, so the same change grows the picture-column void from 45.8 to
       83.6px at 1000. Scoping it to >=1080px still only halves the worst case,
       costs 7 points of the product screenshot (85% -> 77.9% of the hero
       visible), and opens a fresh 15-20px void at 1080-1100.
     * `justify-content:space-between` on the copy column blows the 26/30/26
       internal rhythm out to 65/69/65 AND stretches two items that must not
       stretch — the badge tray 331px -> 480px and the wordmark to the full
       column — so it breaks the store-badge geometry to fix a gap.
     * shrinking --w-hero shrinks the subject.
   Closing it for real needs content this band is forbidden to carry. The
   composition stands; the claim is what was wrong. */
/* The recessed device peeks out on the START side only, so the PAIR's visual
   centre sits ~0.2 x --w left of the stage's geometric centre. The inline-start
   margin shrinks the stage's box — which also carries the 50% anchor the
   recessed device hangs off — and puts the mass back on the column's axis. */
/* No bleed and no inline-start correction any more: both existed to place a
   PAIR of devices whose visual centre sat left of the stage's geometric one,
   and to let the hero cross the card's bottom padding. The mockup carries its
   own shadow and its own margins, so the stage is just a centred box. */
.vistay-stage{position:relative; display:flex; justify-content:center;
  align-items:flex-start; align-self:end}
/* the ONLY remaining job for the Vistay green: a bloom that separates the
   device from a near-black card. Removed outright in light, where the card is
   already white and the shadow does the separating. */
/* sized off the DEVICE, not the stage: in the stacked layout the stage is the
   full card width and an inset-based ellipse became a 900px green cloud. */
.vistay-stage::before{content:""; position:absolute; z-index:0; pointer-events:none;
  inset-block:-4% 4%; inset-inline-start:50%;
  width:calc(var(--w-hero) * 2.1); margin-inline-start:calc(var(--w-hero) * -1.05);
  border-radius:50%;
  background:radial-gradient(closest-side, color-mix(in srgb, var(--vistay) 34%, transparent), transparent 74%);
  filter:blur(40px)}
[data-theme="light"] .vistay-stage::before{display:none}

/* THE FRAME IS THE ARTWORK NOW.
   What stood here was a pure-CSS iPhone: three nested boxes and a Dynamic
   Island pseudo-element, every dimension derived from --w-hero off the iPhone
   17's real point geometry, a safe-area strip pushing the capture's own status
   bar out of the island's way, a second recessed device anchored to the hero's
   centre line so it mirrored correctly in RTL, and a staged reveal gated on
   `@media (scripting: enabled)` so the phones stayed visible with JS off.

   The client supplied a rendered mockup instead, so all of it is gone — frame,
   island, safe-area strip, recede overlay, the staged reveal, and the four
   capture files. The device, its screen and its shadow are one transparent
   PNG and CSS only has to size it. The card's own `.reveal` still carries it
   in, which is why nothing here needs a scripting gate any more.

   The supplied file is 4500x3000 of mostly empty canvas with the phone at the
   far START edge and a ground shadow trailing 900px past it, so a centred box
   put the phone visibly left of its column. It is cropped to a window whose
   centre IS the phone's centre — measured, not eyeballed — which means the
   left side is transparent padding and the right cuts the shadow mid-falloff.
   That cut would have been a seam, so the crop fades the shadow's alpha to
   zero over its last 250px. The phone is now 60% of the file's width, hence
   the 1.5x rather than the 2x a left-anchored phone needed. */
.vistay-mockup{display:block; width:100%; max-width:calc(var(--w-hero) * 1.5);
  height:auto; margin-inline:auto}

/* --- responsive ---------------------------------------------------------- */
/* The two columns hold to 820px, not 1080: the copy stack is only ~330px of
   ink, so stacking it early leaves a full-card-width row with a 210px phone
   alone at one end — the exact dead-space failure this rewrite exists to fix. */
@media(max-width:1079px){ .vistay-body{--w-hero:clamp(196px,19.5vw,210px)} }
@media(max-width:820px){
  .vistay-body{grid-template-columns:minmax(0,1fr); gap:34px}
  .vistay-copy{padding-block-end:0}
  .vistay-stage{align-self:auto; margin-inline-start:0}
}
@media(max-width:639px){
  .vistay-body{--w-hero:clamp(164px,30vw,196px); gap:28px}
  /* On a phone let it use the whole column: at 60% of the file, a 300px image
     is a 180px device, which is the size the CSS frame it replaces drew. The
     padding bleed the left-anchored crop needed is gone with it. */
  .vistay-mockup{max-width:100%}
  .vistay-wm{height:46px}
}
@media(max-width:520px){
  .vistay-badges{flex-direction:column; align-items:flex-start; gap:12px}
  .vistay-badge img{height:40px}
  .vistay-meta{grid-template-columns:repeat(2,minmax(0,1fr)); justify-content:stretch; gap:18px}
}

/* PREVIEW: the reconstructed hero loop. Matches what .hero .bg img already did
   (cover the band, sit behind the copy), so the veil, scrim and parallax hook
   keep working unchanged. prefers-reduced-motion pauses it via JS is NOT wired
   yet — the plan's pause control and Save-Data fallback are still to build. */
.hero-video{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; display:block; z-index:0}
@media (prefers-reduced-motion:reduce){ .hero-video{display:none} }

/* ---- The hero is a DARK surface in both themes ---------------------------
   Light mode used to whiten the hero so its dark text could be read over a
   photograph: --hero-veil ramped white .20 -> .94. Over the motion plate that
   washes the artwork out completely, which is what it looked like.

   Measured on tools/hero/plate-master.png, in the band the headline and lede
   actually occupy: mean backdrop luminance 0.0198. For #0A0C12 to clear 4.5:1
   the backdrop must reach L >= 0.2135 — more than ten times brighter — so dark
   text over this plate is only legible if the plate is destroyed. Light ink
   #F4F7FF on the same raw pixels measures 13.43:1 with NO veil at all.

   So the hero keeps its dark treatment in light theme: light text, a gentle
   dark veil for the gradient into the band below, and the artwork intact.
   Everything downstream of the hero is unaffected. */
[data-theme="light"] .hero{
  /* .52 at the midpoint, not .42, and it is derived rather than eyeballed.
     Measured on the rendered page with the text hidden, the BRIGHTEST pixel
     under the lede was L=0.1737 — a particle streak passing behind it — giving
     #F4F7FF only 4.38:1, just under AA. Solving
     (1-a)*0.2947 + a*0.0067 <= 0.1584 for the veil alpha gives a >= 0.473;
     .52 leaves headroom for a brighter frame later in the loop. */
  --hero-veil:linear-gradient(180deg,rgba(16,18,36,.34),rgba(16,18,36,.52) 50%,rgba(16,18,36,.88));
  --on-image:#F4F7FF;
  --mut:rgba(244,247,255,.86);
}
[data-theme="light"] .hero .display,
[data-theme="light"] .hero .lede{color:var(--on-image)}
/* the emphasis word: --accent-text is #634ea0, which is 2.8:1 on the dark
   plate. On a dark hero it needs the dark theme's lighter tint. */
/* The controls sitting ON the hero follow it. `.btn.ghost` is
   color:var(--ink) with a --ctrl-bd border, both of which are tuned for a light
   page — on the now-dark hero they were dark-on-dark. The header's links are
   the same story wherever it overlaps the band. */
[data-theme="light"] .hero .btn.ghost{
  color:#F4F7FF;
  border-color:rgba(244,247,255,.42);
  background:rgba(16,18,36,.28);
}
[data-theme="light"] .hero .btn.ghost:hover{border-color:#F4F7FF}

/* ============================================================
   HOME (2026 redesign) — homepage sections below the hero.
   Ported from the approved mockup; tokens only, logical props only.
   ============================================================ */
/* ---- HOMEPAGE LIGHTING ---------------------------------------------------
   Aurora, built the way the technique actually works rather than as a ramp:
   a near-black base, colour blooms layered as radial gradients ON ONE element,
   a conic wash blurred and SATURATED over them, and a grain dither on top.
   Three things separate this from the flat gradient it replaces:
     1. saturate() on the blurred wash — blur alone desaturates as it averages,
        which is exactly how a bloom turns into grey mush;
     2. three hues, no more — the brand's lilac, violet and blue. Four or more
        and the overlaps go brown;
     3. grain. An 8-bit channel cannot express a 2000px ramp, so without a
        dither the blend bands into visible stripes on a dark ground.
   One element, static paint: no animation to honour reduced-motion, no
   backdrop-filter recompositing on scroll. Blooms are sized in px down the
   page, not in %, so they stay circular however long the page runs. */
.h-page{position:relative;overflow-x:clip}
[data-theme="dark"] .h-page{background-color:#07080f}
.h-aura{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  background-repeat:no-repeat;
  background-image:
    radial-gradient(120% 58% at 50% 34%, transparent 24%, rgba(3,4,9,.55) 72%, rgba(3,4,9,.85) 100%),
    radial-gradient(closest-side, rgba(146,124,214,.34), rgba(146,124,214,.07) 48%, transparent 70%),
    radial-gradient(closest-side, rgba(110,82,180,.32), rgba(110,82,180,.06) 48%, transparent 70%),
    radial-gradient(closest-side, rgba(123,147,224,.24), rgba(123,147,224,.05) 50%, transparent 72%),
    radial-gradient(closest-side, rgba(146,124,214,.27), rgba(146,124,214,.06) 48%, transparent 70%),
    radial-gradient(closest-side, rgba(110,82,180,.26), rgba(110,82,180,.05) 48%, transparent 70%),
    radial-gradient(closest-side, rgba(123,147,224,.20), rgba(123,147,224,.04) 50%, transparent 72%);
  background-size:100% 100%,1150px 820px,1050px 760px,1250px 900px,1100px 800px,1150px 820px,1050px 760px;
  background-position:0 0,-18% 2%,86% 14%,-22% 34%,78% 54%,-14% 72%,74% 90%}
/* the wash: blurred so no stop survives, saturated so the blur cannot grey it */
.h-aura::before{content:"";position:absolute;inset:-20%;
  background:conic-gradient(from 120deg at 50% 32%,#6E52B4,#7B93E0,#927CD6,#5B3FA0,#6E52B4);
  filter:blur(90px) saturate(1.5);opacity:.13}
/* the dither. --grain is an OPAQUE noise plate, so it only ever works at the
   single-digit opacity the bands use it at (--grain-a); anything more and it
   films the whole page grey. */
.h-aura::after{content:"";position:absolute;inset:0;
  background-image:var(--grain);background-size:140px 140px;opacity:var(--grain-a)}
/* The light theme is not the design's; it keeps the site's own pale ramp, which
   a wash built for a near-black ground would only muddy. */
[data-theme="light"] .h-aura{display:none}
/* Sections ride ON the light: transparent, content above it. */
.h-section{position:relative;z-index:1;padding:clamp(34px,4vw,64px) var(--gutter)}
.h-wrap{max-width:1380px;margin-inline:auto;display:flex;flex-direction:column;gap:clamp(22px,2.6vw,36px)}
.h-kicker{display:flex;align-items:center;gap:20px}
.h-rule{flex:1;height:1px;background:var(--line)}
.h-lbl{font-size:var(--lbl);font-weight:var(--lbl-w);line-height:1.4;color:var(--mut)}
/* the kicker above a section heading: one size up, in the section accent */
.h-kick{font-size:var(--lbl-kicker);color:var(--lilac)}
/* a category ("Venture Studio", "Managed Services"): a soft tinted pill */
.h-tag{align-self:flex-start;padding:4px 11px;border-radius:999px;background:var(--lilac-soft);color:var(--lilac);font-size:var(--lbl-sm)}
/* design correction (Task 4): a pill needs a boundary you can actually see,
   not the decorative hairline every other card edge uses. */
.h-pill{display:inline-flex;align-items:center;padding:.55em 1.2em;border:1px solid var(--line-strong);border-radius:999px}
.h-h2{margin:0;font-size:clamp(2rem,4.4vw,3.4rem);font-weight:420;line-height:1.04;letter-spacing:-.02em;color:var(--ink)}
.h-card{border:1px solid var(--line);border-radius:20px;background:var(--surf-card);overflow:hidden}
/* design correction (Task 4): every "view more" pill sits half the section's
   bottom gap closer to the content above it — applies once here instead of
   the mockup's #about-only rule, so later sections inherit it for free. */
.h-more{display:flex;justify-content:center;margin-top:calc(clamp(20px,2.6vw,36px) * -.5)}
.h-more-btn{display:inline-flex;align-items:center;gap:10px;height:46px;padding-inline:24px;border-radius:999px;border:1px solid var(--line);color:var(--ink);transition:border-color .2s}
.h-more-btn:hover,.h-more-btn:focus-visible{border-color:var(--lilac)}
.h-rowmore{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:20px clamp(18px,2vw,26px);color:inherit;transition:background .15s}
/* design correction (Task 4): a neutral wash on hover, not the lilac tint —
   the arrow alone carries the lilac accent. */
.h-rowmore:hover{background:var(--wash)}
.h-rowmore .h-ar{color:var(--lilac)}
[dir="rtl"] .h-ar{display:inline-block;transform:scaleX(-1)}

/* -- #about -------------------------------------------------------------- */
.h-about{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:clamp(26px,3.6vw,56px);align-items:start}
.h-about-fig{margin:0;display:flex;flex-direction:column;gap:16px}
.h-about-img{border-radius:20px;overflow:hidden;border:1px solid var(--line)}
.h-about-img img{display:block;width:100%;height:auto}
[data-theme="dark"] .h-about-img{box-shadow:0 1px 2px rgba(0,0,0,.55)}
[data-theme="dark"] .h-about-img img{filter:saturate(1.05) brightness(1.4)}
.h-about-copy{display:flex;flex-direction:column;gap:22px}
.h-about-copy p{margin:0;font-size:1rem;line-height:1.66;color:var(--mut);text-wrap:pretty}
.h-about-copy .h-about-lead{font-family:var(--font-display);font-size:clamp(1.4rem,2.3vw,2rem);font-weight:380;line-height:1.2;letter-spacing:-.01em;color:var(--ink)}
.h-stats{margin:0;position:relative;border-radius:18px;overflow:hidden;
  background:linear-gradient(104deg,#452C7F 0%,#5B3FA0 26%,#6E52B4 48%,#4E3690 72%,#7B5FC8 100%);
  box-shadow:0 26px 60px -30px rgba(50,32,96,.95)}
.h-stats-plate{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%;transform:scale(1.25)}
.h-stats-veil{position:absolute;inset:0;background:linear-gradient(90deg,rgba(40,24,80,.52),rgba(40,24,80,.18) 42%,rgba(40,24,80,.30))}
.h-stats-grid{position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(20px,3vw,44px);padding:clamp(26px,3.4vw,44px) clamp(24px,3.6vw,56px)}
.h-stat{display:flex;flex-direction:column-reverse;gap:8px;min-width:0}
/* Stats band sits on a fixed purple image in both themes, so #fff text there
   is intentional — same exception as the plate gradient. */
.h-stat dd{margin:0;font-family:var(--font-display);font-size:clamp(2.1rem,3.6vw,3.1rem);font-weight:600;line-height:1;color:#fff;letter-spacing:-.015em}
.h-stat dt{font-size:clamp(.85rem,1.1vw,.98rem);line-height:1.4;color:rgba(255,255,255,.84)}
@media (max-width:640px){.h-stats-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* -- #services ------------------------------------------------------------ */
.h-section-rule{border-top:1px solid var(--line-2)}
.h-lilac{color:var(--lilac)}
.h-svc{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}
.h-svc-lead{padding:clamp(24px,3vw,40px);display:flex;flex-direction:column;gap:clamp(20px,2.4vw,30px);border-inline-end:1px solid var(--line-2)}
.h-svc-h2{font-size:clamp(1.8rem,3.6vw,2.9rem);line-height:1.05}
.h-svc-media{position:relative;border-radius:16px;overflow:hidden;border:1px solid var(--line);min-height:300px;flex:1}
/* design correction (Task 6): the media crop carries its own exposure trim
   ON TOP of each photo's per-image exposure vars (art.services[slug].style) —
   the two stack rather than one replacing the other. */
.h-svc-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:saturate(1.1) brightness(.9);transition:opacity .15s}
.h-svc-veil{position:absolute;inset:0;background:linear-gradient(180deg,rgba(16,18,36,.1),rgba(16,18,36,.82))}
.h-svc-cap{position:absolute;inset-inline:20px;bottom:20px;display:flex;flex-direction:column;gap:6px;color:#F4F7FF}
.h-svc-cap-t{font-family:var(--font-display);font-size:clamp(1.05rem,1.5vw,1.3rem);letter-spacing:-.01em;line-height:1.2}
.h-svc-list{display:flex;flex-direction:column}
.h-svc-row{display:grid;grid-template-columns:64px minmax(0,1fr) minmax(0,1.1fr) 24px;align-items:center;gap:14px;
  padding:18px clamp(18px,2vw,26px);border-bottom:1px solid var(--line-2);border-inline-start:2px solid transparent;color:inherit;transition:background .15s}
.h-svc-row .h-lbl{font-variant-numeric:tabular-nums}
.h-svc-t{font-family:var(--font-display);font-size:clamp(1.05rem,1.5vw,1.22rem);color:var(--ink)}
.h-svc-d{font-size:.88rem;line-height:1.5;color:var(--mut)}
.h-svc-row .h-ar{color:var(--mut);text-align:end}
/* design correction (Task 6): neutral wash on a plain row hover; the lilac
   tint is reserved for the active row's own hover state. */
.h-svc-row:hover{background:var(--wash)}
.h-svc-row[aria-current="true"]{background:var(--lilac-soft);border-inline-start-color:var(--lilac)}
.h-svc-row[aria-current="true"]:hover{background:var(--lilac-sel)}
.h-svc-row[aria-current="true"] .h-ar{color:var(--lilac)}
.h-svc-row[aria-current="true"] .h-svc-d{color:color-mix(in srgb,var(--ink) 82%,transparent)}
@media (max-width:1023px){.h-svc-lead{border-inline-end:0;border-bottom:1px solid var(--line-2)}}
@media (max-width:640px){.h-svc-row{grid-template-columns:48px minmax(0,1fr) 20px}.h-svc-d{grid-column:2}}

/* -- #industries ----------------------------------------------------------- */
.h-ind{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(16px,2vw,24px)}
/* design correction (Task 7): a faint tint of its own, not the plain card
   surface — distinguishes the photo cards from the flat-panel cards above. */
.h-ind-card{display:flex;flex-direction:column;background:var(--surf-card-2);transition:border-color .2s}
.h-ind-card:hover{border-color:var(--lilac-line)}
.h-ind-ph{position:relative;aspect-ratio:4/3;overflow:hidden}
.h-ind-ph img{width:100%;height:100%;object-fit:cover;display:block}
/* design correction (Task 7): per-image saturate/brightness lift, dark theme
   only — the value is the `--ph-filter` var baked into art.industries[i]'s
   style attr (same convention as the other per-image exposure vars), read
   here instead of by the shared partials/industries.njk (untouched). */
[data-theme="dark"] .h-ind-ph img{filter:var(--ph-filter)}
.h-ind-veil{position:absolute;inset:0;background:linear-gradient(180deg,rgba(13,15,28,.2),rgba(13,15,28,.92))}
.h-ind-ic{position:absolute;inset-inline-start:18px;top:18px;display:grid;place-items:center;width:40px;height:40px;border-radius:12px;
  background:rgba(16,18,36,.72);border:1px solid rgba(255,255,255,.14);color:#F4F7FF}
.h-ind-body{padding:clamp(20px,2.4vw,28px);display:flex;flex-direction:column;gap:12px;margin-top:-10px;position:relative}
.h-ind-body h3{margin:0;font-size:clamp(1.4rem,2.2vw,1.8rem);font-weight:420;letter-spacing:-.01em;color:var(--ink)}
.h-ind-body p{margin:0;font-size:.95rem;line-height:1.6;color:var(--mut);text-wrap:pretty}
@media (max-width:1023px){.h-ind{grid-template-columns:1fr}.h-ind-card{flex-direction:row}.h-ind-ph{flex:0 0 40%;aspect-ratio:auto}}
@media (max-width:640px){.h-ind-card{flex-direction:column}.h-ind-ph{aspect-ratio:16/10}}

/* -- #work: the spotlight client, then one grid of stories -------------- */
/* ONE treatment for every client mark in the band: a white plate (white in
   both themes — the marks are drawn for white) with the mark held in a fixed
   box, so a wide wordmark and a square badge land at the same visual weight.
   The files arrive with their blank canvas already trimmed (cms.js
   `logoUrl`), so the box measures the logo itself, not its padding. */
.h-spot{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);color:inherit;transition:border-color .2s}
.h-spot:hover{border-color:var(--lilac-line)}
.h-spot-copy{padding:clamp(24px,3vw,44px);display:flex;flex-direction:column;gap:16px}
.h-spot-copy h3{margin:0;font-size:clamp(1.6rem,2.6vw,2.2rem);font-weight:420;color:var(--ink)}
.h-spot-copy p{margin:0;color:var(--mut);line-height:1.6;max-width:48ch}
.h-spot-copy .h-read{margin-top:6px}
.h-chips{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.h-chips .h-pill{font-size:var(--lbl-sm);padding:6px 13px}
.h-spot-logo{display:grid;place-items:center;background:var(--logo-tile-bg);min-height:240px;padding:clamp(28px,4vw,56px)}
.h-spot-logo img{width:auto;height:auto;max-width:min(68%,300px);max-height:104px}
/* the fallback when a client has no logo file: its name, set on the plate */
.h-logo-word{font-size:clamp(1.3rem,2.4vw,2rem);font-weight:600;letter-spacing:-.01em;line-height:1.1;text-align:center;color:var(--logo-tile-ink)}
.h-read{margin-top:auto;color:var(--lilac)}
.h-stories{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(14px,1.6vw,22px)}
.h-stories>li{display:flex}
.h-story{display:flex;flex-direction:column;width:100%;color:inherit;transition:border-color .2s}
.h-story:hover{border-color:var(--lilac-line)}
.h-story-logo{display:grid;place-items:center;height:clamp(112px,9vw,132px);padding:20px 28px;
  background:var(--logo-tile-bg);border-bottom:1px solid var(--line-2)}
.h-story-logo img{width:auto;height:auto;max-width:min(62%,200px);max-height:64px}
.h-story-logo .h-logo-word{font-size:1.3rem}
.h-story-body{flex:1;display:flex;flex-direction:column;gap:10px;padding:clamp(18px,2vw,24px)}
.h-story-body h3{margin:2px 0 0;font-size:clamp(1.25rem,1.7vw,1.45rem);font-weight:450;color:var(--ink)}
.h-story-body p{margin:0 0 6px;font-size:.92rem;line-height:1.6;color:var(--mut);text-wrap:pretty}
@media (max-width:1023px){.h-spot{grid-template-columns:1fr}.h-spot-logo{order:-1;min-height:180px}
  .h-stories{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:640px){.h-stories{grid-template-columns:1fr}}

/* ---- #stack: partners & stacks (Task 9) --------------------------------- */
.h-stack-head{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,3vw,48px);align-items:start}
.h-stack-head p{margin:0;color:var(--mut);line-height:1.66;max-width:52ch}
.h-stack{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:clamp(12px,1.4vw,18px)}
.h-stack-card{display:flex;flex-direction:column;gap:16px;padding:clamp(22px,2.4vw,30px);min-height:220px;transition:border-color .2s}
.h-stack-card:hover{border-color:var(--lilac-line)}
/* the icon and the name are one header row: a soft tile, no outline, so the
   mark reads as part of the card and not as a separate button */
.h-stack-top{display:flex;align-items:center;gap:14px}
.h-stack-ic{flex:none;display:grid;place-items:center;width:48px;height:48px;border-radius:14px;background:var(--lilac-soft);color:var(--lilac)}
.h-stack-ic svg{width:26px;height:26px}
/* the client's own partner icons are complete badges, so no tile behind them */
.h-stack-mark{flex:none;display:block;width:48px;height:48px}
.h-stack-card h3{margin:0;font-size:1.15rem;font-weight:500;color:var(--ink)}
.h-stack-card p{margin:0;font-size:.88rem;line-height:1.55;color:var(--mut);flex:1}
.h-stack-card>.h-lbl{padding-top:12px;border-top:1px solid var(--line-2)}
@media (max-width:1023px){.h-stack-head{grid-template-columns:1fr}.h-stack{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:640px){.h-stack{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ---- #products: showcase tabs + full suite (Task 10) -------------------- */
.h-tabs{display:flex;gap:10px;flex-wrap:wrap}
/* design correction (Task 10): a tab needs a boundary you can actually see. */
.h-tab{display:inline-flex;align-items:center;height:42px;padding-inline:20px;border-radius:999px;border:1px solid var(--line-strong);color:var(--mut);font-size:.95rem;transition:border-color .2s,background .2s,color .2s}
.h-tab[aria-selected="true"]{background:var(--lilac-sel);border-color:var(--lilac);color:var(--ink)}
.h-prod{display:flex;flex-direction:column;gap:18px}
.h-show-bar{display:flex;justify-content:space-between;gap:12px;padding:14px clamp(18px,2vw,24px);border-bottom:1px solid var(--line-2)}
.h-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--lilac);vertical-align:middle}
/* Every tab panel is the same height, whatever its copy runs to, so switching
   tabs never resizes the section under the reader. */
.h-show-body{display:grid;grid-template-columns:1fr 1fr;align-items:stretch;min-height:clamp(360px,28vw,430px)}
/* design correction (Task 10): the copy pane's boundary moves to the visual
   pane (below) instead — a plain hairline there would fight the glow. */
/* centred on the column's height: the panel keeps one height for every tab,
   and the copy sits level with the product visual beside it. */
.h-show-copy{padding:clamp(24px,3vw,44px);display:flex;flex-direction:column;justify-content:center;gap:22px}
.h-show-logo{align-self:flex-start;color:var(--ink)}
.h-show-logo .vistay-wm{height:48px}
.h-show-name{margin:0;font-size:clamp(2rem,3.6vw,3rem);font-weight:420;color:var(--ink)}
.h-show-line{margin:0;font-size:clamp(1.3rem,2vw,1.7rem);line-height:1.25;color:var(--ink)}
.h-facts{margin:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;border-top:1px solid var(--line-2);padding-top:18px}
.h-facts dt{margin-bottom:6px}.h-facts dd{margin:0;font-size:.92rem;color:var(--ink)}
.h-badges{display:flex;gap:10px}.h-badges img{height:40px;width:auto;display:block}
/* design correction (Task 10): the visual pane carries the boundary and the
   lilac show-glow the mockup gives it; the copy pane no longer has one. */
.h-show-vis{--media-h:clamp(230px,19vw,290px);--stage-pad:clamp(20px,2.6vw,34px);
  margin:0;position:relative;display:grid;place-items:center;overflow:hidden;padding:var(--stage-pad);border-inline-start:1px solid var(--line-2);background:var(--show-glow)}
.h-show-vis > img{height:auto;max-height:var(--media-h);width:auto;max-width:100%;object-fit:contain}
/* ONE product-media system. Every surface frames the same asset the same way:
   a 16:10 box at a fixed size, a drawn logo centred on its plate, a capture or
   photograph filling the box. Fixed sizes, because these images arrive at wildly
   different intrinsic sizes and anything `auto` reflows as each one loads. */
.h-media{display:grid;place-items:center;overflow:hidden;border-radius:12px;background:var(--logo-tile-bg);border:1px solid transparent}
.h-media img{display:block;max-width:76%;max-height:64%;width:auto;height:auto;object-fit:contain}
.h-media.is-shot{background:var(--bg-3);border-color:var(--line-2)}
.h-media.is-shot img{max-width:none;max-height:none;width:100%;height:100%;object-fit:cover;object-position:50% 0}
.prod-mark{padding:0;aspect-ratio:16/10;height:auto}
.prod-mark img{max-width:72%;max-height:60%}
/* showcase pane: one fixed-height stage, so switching tabs never jumps and a
   late-loading image cannot resize the panel under the reader. */
.h-show-placeholder{width:min(420px,100%);height:var(--media-h);display:grid;place-items:center;border:1px solid var(--line);border-radius:16px;background:var(--surf-card)}
.h-show-frame{height:var(--media-h);aspect-ratio:16/10;max-width:100%;display:flex;flex-direction:column;
  border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--bg-2);box-shadow:0 30px 60px -34px rgba(0,0,0,.8)}
.h-show-chrome{display:flex;align-items:center;gap:6px;padding:9px 12px;border-bottom:1px solid var(--line-2);background:var(--bg-3);flex:none}
.h-show-chrome i{width:8px;height:8px;flex:none;border-radius:50%;background:var(--faint)}
.h-show-chrome b{margin-inline-start:10px;font-size:var(--lbl-sm);color:var(--mut);font-weight:400}
.h-show-frame img{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 0}
.h-show-plate{display:grid;place-items:center;height:var(--media-h);aspect-ratio:16/10;max-width:100%;border-radius:16px;
  background:var(--logo-tile-bg);border:1px solid var(--line);padding:clamp(20px,3vw,36px)}
.h-show-plate img{max-width:78%;max-height:56%;width:auto;object-fit:contain}
/* /products/ index rows reuse the schematic slot for the same mark. */
.prod-mark{display:grid;place-items:center;padding:10px 14px;background:var(--logo-tile-bg)}
.prod-mark img{display:block;max-height:46px;max-width:150px;width:auto;object-fit:contain}
.prod-mark.is-shot{padding:0;overflow:hidden}
.prod-mark.is-shot img{max-height:none;max-width:none;width:100%;height:100%;object-fit:cover;object-position:0 0}
/* Detail masthead: a shipped app's own render, no plate behind it. */
.mh-fig.app-fig img{display:block;width:100%;max-width:420px;height:auto;margin-inline:auto;
  filter:drop-shadow(0 34px 60px rgba(0,0,0,.55))}
.h-show[role="tabpanel"]:not([hidden]){animation:h-fade .2s ease}
@keyframes h-fade{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){.h-show[role="tabpanel"]{animation:none}}
@media (max-width:1023px){.h-show-body{grid-template-columns:1fr}.h-show-vis{border-inline-start:0;border-top:1px solid var(--line-2)}}
@media (max-width:640px){.h-tabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}.h-facts{grid-template-columns:1fr 1fr}}

/* ---- CTA band, #insights, #careers (Task 11) ----------------------------- */
/* design correction (Task 11): the band gets its own larger block padding. */
.h-cta-wrap{padding-block:clamp(34px,4.4vw,64px)}
/* The one band on the page that is allowed to raise its voice: the purple wash
   plus a lilac bloom behind the heading, a hairline top edge catching light,
   and the whole plate lifted off the page. */
.h-cta{position:relative;isolation:isolate;overflow:hidden;
  display:flex;justify-content:space-between;align-items:center;gap:clamp(24px,4vw,56px);flex-wrap:wrap;
  padding:clamp(32px,4vw,56px) clamp(28px,3.8vw,56px);
  border:1px solid var(--cta-line);border-radius:24px;background:var(--cta-bg);
  box-shadow:0 34px 70px -40px rgba(60,40,120,.85),inset 0 1px 0 var(--cta-line)}
.h-cta::before{content:"";position:absolute;inset-inline-start:-6%;top:-90%;width:70%;aspect-ratio:1;z-index:-1;
  background:radial-gradient(circle,color-mix(in srgb,var(--lilac) 28%,transparent),transparent 66%);pointer-events:none}
.h-cta::after{content:"";position:absolute;inset-inline:8%;top:0;height:1px;z-index:-1;
  background:linear-gradient(90deg,transparent,var(--lilac),transparent);opacity:.65}
.h-cta-h{margin:12px 0 0;font-size:clamp(1.9rem,3.2vw,2.9rem);font-weight:420;line-height:1.06;letter-spacing:-.02em;max-width:20ch;color:var(--ink)}
.h-cta .btn{height:50px;padding-inline:26px}
.h-cta-btns{display:flex;gap:10px;flex-wrap:wrap}
.h-ins{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(12px,1.4vw,18px)}
.h-ins-card{display:flex;flex-direction:column;color:inherit;transition:border-color .2s}
.h-ins-card:hover{border-color:var(--lilac-line)}
.h-ins-ph{position:relative;aspect-ratio:16/10;overflow:hidden}.h-ins-ph img{width:100%;height:100%;object-fit:cover;display:block}
/* design correction (Task 11): a veil over the image, plus dark-only filter. */
.h-ins-veil{position:absolute;inset:0;background:linear-gradient(180deg,rgba(16,18,36,.15),rgba(16,18,36,.7))}
[data-theme="dark"] .h-ins-ph img{filter:saturate(1.05) brightness(1.2)}
.h-ins-body{display:flex;flex-direction:column;gap:10px;padding:16px 18px 18px;flex:1}
.h-ins-body h3{margin:0;font-size:1rem;font-weight:500;line-height:1.35;color:var(--ink)}
.h-jobs{list-style:none;margin:0;padding:0;border-top:1px solid var(--line-2)}
.h-job{display:grid;grid-template-columns:56px minmax(0,1fr) minmax(0,1.1fr) 24px;gap:18px;align-items:center;padding:22px 0;border-bottom:1px solid var(--line-2);color:inherit;transition:background .15s}
/* design correction (Task 11): neutral wash on hover, not a title-colour change. */
.h-job:hover{background:var(--wash)}
.h-job .h-lbl{font-variant-numeric:tabular-nums}
.h-job-t{font-family:var(--font-display);font-size:clamp(1.2rem,1.9vw,1.6rem);color:var(--ink)}
.h-job-d{font-size:.85rem;line-height:1.5;color:var(--mut)}
.h-job .h-ar{color:var(--mut);text-align:end}
@media (max-width:1023px){.h-ins{grid-template-columns:repeat(2,minmax(0,1fr))}.h-job{grid-template-columns:40px minmax(0,1fr) 20px}.h-job-d{grid-column:2}}
@media (max-width:640px){.h-ins{grid-template-columns:1fr}}

/* ---- #contact (Task 12) --------------------------------------------------- */
.h-contact{position:relative;overflow:hidden;padding-block:clamp(40px,5vw,72px)}
/* design correction (Task 12): grayscale+darken the photo itself, no opacity fade. */
.h-contact-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:grayscale(.3) brightness(.55)}
.h-contact-veil{position:absolute;inset:0;background:linear-gradient(90deg,rgba(16,18,36,.94),rgba(16,18,36,.62))}
[data-theme="light"] .h-contact-veil{background:linear-gradient(90deg,rgba(255,255,255,.95),rgba(255,255,255,.7))}
.h-contact-grid{position:relative;display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,4vw,64px);align-items:center}
.h-contact-copy{display:flex;flex-direction:column;gap:18px}
.h-contact-h{margin:0;font-size:clamp(2.4rem,5.4vw,4rem);font-weight:420;line-height:1.02;letter-spacing:-.02em;color:var(--ink)}
/* scope the shared .addr partial to the mockup's underlined icon+text row, without touching its own rule. */
.h-contact-copy .addr{display:inline-flex;align-items:center;gap:10px;border-bottom:1px solid var(--line-strong);padding-bottom:8px;align-self:flex-start;color:var(--ink)}
.h-contact-copy .addr svg{width:17px;height:17px;color:var(--mut)}
.h-contact-pills{display:flex;gap:12px;flex-wrap:wrap}
/* a distinct fill pill, not .btn — keeps the site's one named primary CTA ("Talk to Us") unambiguous. */
.h-pill-fill{display:inline-flex;align-items:center;height:48px;padding-inline:26px;border-radius:999px;background:var(--lilac);color:var(--on-accent);font-weight:500;transition:background-color .2s}
.h-pill-fill:hover{background:color-mix(in srgb,var(--lilac) 85%,white 15%)}
.h-contact-form{background:var(--surf-solid);padding:clamp(22px,2.8vw,34px)}
@media (max-width:1023px){.h-contact-grid{grid-template-columns:1fr}}

@media (prefers-reduced-motion:reduce){.h-section *{transition:none!important}}
