/* =========================================================================
   Tacke Infotech — Modernist redesign
   One stylesheet, no build step. Tokens first, then components, then pages.
   Type is the Helvetica system stack per client direction — real Helvetica
   Neue on macOS/iOS, metrically-identical Arial on Windows/Android, CJK
   system fonts (PingFang SC / Microsoft YaHei) carry the Chinese locale.
   No webfont is downloaded; the supplied Apple TTFs may not be
   redistributed as webfonts (see source_site/README.md).
   ========================================================================= */

:root {
  --color-bg: #f3f2f2;
  --color-surface: #eae9e9;
  --color-text: #201e1d;
  --color-accent: #ec3013;
  --color-divider: color-mix(in srgb, #201e1d 40%, transparent);

  --color-neutral-100: #f8f4f4;
  --color-neutral-200: #eae7e7;
  --color-neutral-300: #d7d3d3;
  --color-neutral-400: #bab6b6;
  --color-neutral-500: #9b9797;
  --color-neutral-600: #7d7979;
  --color-neutral-700: #605d5d;
  --color-neutral-800: #444141;
  --color-neutral-900: #2d2b2b;

  --color-accent-100: #fff2ef;
  --color-accent-200: #ffe0d9;
  --color-accent-300: #ffc4b8;
  --color-accent-400: #ff9783;
  --color-accent-500: #ff563c;
  --color-accent-600: #dd2b0f;
  --color-accent-700: #ae1800;
  --color-accent-800: #7c1405;
  --color-accent-900: #4d170e;

  --font-body: "Helvetica Neue", Helvetica, Arial, "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", system-ui, sans-serif;
  --font-heading: var(--font-body);
  --font-heading-weight: 700;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;

  --radius-sm: 0px;
  --radius-md: 0px;
  --radius-lg: 0px;

  --shadow-sm: 0 1px 2px color-mix(in srgb, #2d2b2b 14%, transparent);
  --shadow-md: 0 3px 10px color-mix(in srgb, #2d2b2b 16%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #2d2b2b 22%, transparent);

  /* body-copy ink mixes used throughout */
  --ink-82: color-mix(in srgb, var(--color-text) 82%, transparent);
  --ink-78: color-mix(in srgb, var(--color-text) 78%, transparent);
  --ink-70: color-mix(in srgb, var(--color-text) 70%, transparent);
  --ink-65: color-mix(in srgb, var(--color-text) 65%, transparent);
  --ink-62: color-mix(in srgb, var(--color-text) 62%, transparent);
  --ink-60: color-mix(in srgb, var(--color-text) 60%, transparent);
  --ink-58: color-mix(in srgb, var(--color-text) 58%, transparent);
  --ink-55: color-mix(in srgb, var(--color-text) 55%, transparent);
  --ink-45: color-mix(in srgb, var(--color-text) 45%, transparent);

  /* The reading measure. Floor 1240px is reached at exactly a 2000px viewport
     (62vw x 20 = 1240), so every viewport at or below 2000 renders byte-identical
     to before; the ceiling arrives at 2742px. 62vw is 1240/20, the same ratio the
     display type below uses, which is what keeps type and measure locked together
     instead of drifting apart on a wide screen. */
  --measure: clamp(1240px, 62vw, 1700px);
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  /* 100%, never `none`: `none` would also disable the text scaling a reader gets
     from pinch-zoom, which is an accessibility regression. This only tells iOS
     to render the size the stylesheet asked for. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 28px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  margin: 0;
}
p { margin: 0; }
ul, ol, dl, dd, figure { margin: 0; padding: 0; }
li { list-style: none; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }
.tnum { font-feature-settings: 'tnum' 1; font-variant-numeric: tabular-nums; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--color-accent); color: var(--color-bg);
  padding: 10px 16px; font-weight: 700; font-size: 14px;
}
.skip-link:focus { left: 0; }

.wrap { width: min(var(--measure), 100% - 2 * clamp(20px, 6.94vw, 100px)); margin-inline: auto; }

/* ---------- shared type ---------- */
.kicker {
  font-size: 13px; line-height: 20px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--color-accent-700);
  font-feature-settings: 'tnum' 1;
}
.h2 {
  font-size: clamp(52px, 2.6vw, 71px); line-height: 1.06; letter-spacing: -0.02em;
  margin-left: -0.058em; margin-top: 14px;
}
.h2 .soft { color: var(--ink-45); display: block; }
.microlabel {
  font-size: 12px; line-height: 18px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-55);
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: flex-start; gap: 9px;
  font-family: var(--font-heading); font-weight: 700;
  font-size: 14px; line-height: 1.2; text-align: left;
  padding: 9px 16px; border: 1px solid transparent; border-radius: 0;
  cursor: pointer; text-decoration: none; color: var(--color-text);
  background: transparent; transition: background .22s ease, color .22s ease;
}
.btn svg { display: block; flex: none; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--color-accent); color: var(--color-bg); }
.btn-primary:hover { background: var(--color-accent-600); }
.btn-primary:active { background: var(--color-accent-700); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }

/* ---------- tags ---------- */
.tag {
  display: inline-flex; align-items: center;
  font-size: 11px; line-height: 18px; letter-spacing: 0.02em;
  padding: 3px 10px; border-radius: 0;
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent-700); background: transparent; }

/* ---------- chevron ---------- */
.chev { width: 16px; height: 16px; stroke: currentColor; stroke-width: 2; fill: none; transition: transform .28s cubic-bezier(0.33, 1, 0.68, 1); }
[aria-expanded="true"] .chev { transform: rotate(180deg); }

/* =========================================================================
   HEADER
   ========================================================================= */
.header {
  position: sticky; top: 0; z-index: 100;
  background: var(--color-bg);
  border-bottom: 2px solid var(--color-divider);
  transition: box-shadow .2s ease;
}
.header__inner {
  display: flex; align-items: center; gap: 28px;
  height: 76px; transition: height .2s ease;
}
.header.is-scrolled { box-shadow: var(--shadow-sm); }
.header.is-scrolled .header__inner { height: 60px; }

.wordmark {
  display: inline-flex; align-items: center; gap: 10px;
  font-weight: 700; font-size: 19px; letter-spacing: -0.02em;
  margin-right: auto; transition: font-size .2s ease;
}
/* The chameleon mark from the brand lockup, flattened onto paper with a
   multiply blend so the JPEG's white field vanishes against --color-bg.
   Height-driven, width:auto keeps the 227:120 ratio from the attributes. */
.wordmark__mark { height: 38px; width: auto; flex: none; transition: height .2s ease; }
.header.is-scrolled .wordmark__mark { height: 30px; }
.footer .wordmark__mark { height: 26px; }
@media (max-width: 880px) {
  .wordmark__mark { height: 30px; }
}
.header.is-scrolled .wordmark { font-size: 17px; }

.nav-list { display: flex; gap: 28px; }
.nav-list a {
  font-size: 14px; line-height: 20px; padding: 4px 0;
  border-bottom: 2px solid transparent;
  transition: color .2s ease;
}
.nav-list a:hover { color: var(--color-accent); }
.nav-list a.is-active, .nav-list a[aria-current="page"] {
  color: var(--color-accent); border-bottom-color: var(--color-accent);
}

.lang {
  display: inline-flex;
  border: 1px solid var(--color-divider);
}
.lang a, .lang .lang__current {
  display: inline-flex; align-items: center;
  padding: 5px 10px; font-size: 12px; letter-spacing: 0.06em; line-height: 16px;
}
.lang a + a, .lang * + * { border-left: 1px solid var(--color-divider); }
.lang .lang__current { background: var(--color-accent); color: var(--color-bg); }
.lang a:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }

.header .btn-cta { font-size: 14px; padding: 9px 16px; transition: font-size .2s ease, padding .2s ease; }
.header.is-scrolled .btn-cta { font-size: 13px; padding: 7px 14px; }

/* progress rail */
.progress-rail { height: 2px; background: var(--color-neutral-300); }
.progress {
  height: 2px; background: var(--color-accent);
  transform: scaleX(0); transform-origin: 0 0; will-change: transform;
}

/* burger */
.burger {
  display: none; flex-direction: column; justify-content: center; gap: 5px;
  width: 32px; height: 32px; position: relative;
}
.burger span { display: block; width: 22px; height: 2px; background: var(--color-text); transition: transform .2s ease, opacity .2s ease, background .2s ease; }
.burger[aria-expanded="true"] span { background: var(--color-accent); }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* drawer */
.scrim {
  position: fixed; inset: 0; z-index: 90;
  background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
  opacity: 0; pointer-events: none; transition: opacity .2s ease;
}
.scrim.is-open { opacity: 1; pointer-events: auto; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 95;
  width: min(360px, 88vw);
  background: var(--color-bg); border-left: 2px solid var(--color-divider);
  transform: translateX(100%); transition: transform .25s ease;
  overflow-y: auto; display: none; flex-direction: column;
}
.drawer.is-open { transform: translateX(0); }
.drawer__list { padding-top: 76px; }
.drawer__list a {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px; font-weight: 700; font-size: 22px;
  border-bottom: 1px solid var(--color-divider);
}
.drawer__list a:last-child { border-bottom-width: 2px; }
.drawer__list .chev { color: var(--color-accent); width: 18px; height: 18px; }
.drawer__foot { display: flex; flex-direction: column; gap: 12px; padding: 24px 20px; }
.drawer__foot .btn { width: 100%; }
.drawer__foot .lang { align-self: flex-start; }

/* =========================================================================
   HERO
   ========================================================================= */
/* The hero is the one dark panel above the fold: a muted loop of the factory
   floor runs behind the type. The clip is a montage, not one shot — it cuts
   between a near-black machine close-up and a near-white desk overhead — so a
   single flat scrim cannot hold the type legible across both. .hero__veil
   stacks a vertical and a horizontal pass instead, weighted to where the copy
   actually sits: heaviest bottom-left under the stats, lifting to nothing at
   the right edge, where only the tail of the headline reaches.

   The stops are fitted, not guessed: sampled across the loop against the real
   glyph boxes, with brightness(1.15) below applied. The tightest page is the
   Vietnamese one, whose two-line headline leaves the shortest column and so
   pushes the centred stats highest into the lighter part of this gradient —
   worst frame there gives headline 4.7:1 and stats 3.3:1 (floor 3:1 at that
   size), kicker 5.0:1, stat labels 5.0:1, sub 6.7:1 (floor 4.5:1).

   Three things move these numbers, so re-measure rather than eyeball if you
   change any of them: the footage, the brightness filter, and the height of
   the hero copy — a shorter column raises the stats into a lighter band.

   Every ink token below flips to a paper mix, and the accent steps up the ramp:
   #ec3013 manages only 1.8:1 on this ground. */
/* The panel is held to 80% of the viewport so the clip reads as a full-bleed
   opening rather than a strip, and the copy is centred in whatever height that
   leaves — on a tall screen the content is shorter than the box. svh, not vh:
   on mobile `vh` is the tallest viewport, so 80vh overflows the screen while
   the browser chrome is still showing. The padding stays as the floor for
   short windows, where min-height never binds. */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  min-height: 80vh;
  min-height: 80svh;
  padding: 96px 0 84px;
  background: var(--color-text);
  color: var(--color-bg);
}
/* -2 / -1 sit below the .wrap content, which stays in the default z-order.
   `isolation: isolate` on .hero keeps both out of the header's stacking. */
.hero__video {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
  pointer-events: none;
  /* Lifts the clip 15%. It costs contrast, because a multiply hits the bright
     desk frames hardest and those are the ones the veil is fighting: it takes
     the tightest margin on the page, the stats, from 3.6:1 to 3.4:1 against a
     3:1 floor. That still passes, but it is most of the remaining headroom —
     going much past 1.15 needs the veil re-fitted, not just this number
     raised. */
  filter: brightness(1.15);
}
.hero__veil {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(32, 30, 29, 0.46) 0%, rgba(32, 30, 29, 0.66) 62%, rgba(32, 30, 29, 0.9) 100%),
    linear-gradient(90deg, rgba(32, 30, 29, 0.76) 0%, rgba(32, 30, 29, 0.66) 45%, rgba(32, 30, 29, 0) 100%);
}
.hero .kicker { color: var(--color-accent-400); }
.hero .sweep__stroke path { stroke: var(--color-accent-500); }
.hero :focus-visible { outline-color: var(--color-bg); }
.display {
  font-size: clamp(86px, 4.3vw, 118px); line-height: 1.06; letter-spacing: -0.025em;
  margin-left: -0.058em; margin-top: 18px;
}
.display span { display: block; }
/* Vietnamese runs 32–35 characters per sentence where English runs 22, so both
   were wrapping: four lines where the design wants two. Size it off the measure
   rather than the shared ramp — 5.1vw is the widest that keeps the longer
   sentence on one line, and the cap stops it outgrowing the column once the
   measure stops widening. Phones are deliberately excluded: one line at 375px
   would need ~19px type, which is not a headline any more. */
@media (min-width: 721px) {
  html[lang="vi"] .display { font-size: min(5.1vw, 74px); }
}
/* Opaque paper mixes, not the transparent --ink ramp: a translucent fill would
   let the clip move through the letterforms themselves. */
.hero__sub {
  font-size: 19px; line-height: 32px;
  color: color-mix(in srgb, var(--color-bg) 82%, var(--color-text));
  max-width: 54ch; margin-top: 24px;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }
.hero__actions .btn { font-size: 15px; padding: 13px 22px; }
.hero__stats {
  display: grid; grid-template-columns: repeat(3, minmax(0, clamp(240px, 12vw, 329px))); gap: 24px;
  border-top: 2px solid color-mix(in srgb, var(--color-bg) 40%, transparent);
  margin-top: 56px; padding-top: 32px;
}
.hero__stats dt {
  font-weight: 700; font-size: clamp(52px, 2.6vw, 71px); line-height: 1; letter-spacing: -0.03em;
  margin-left: -0.045em; color: var(--color-accent-500);
  font-feature-settings: 'tnum' 1;
}
.hero__stats dd {
  margin: 10px 0 0; font-size: 13px; line-height: 18px;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-bg) 70%, var(--color-text));
}

/* =========================================================================
   FIG. 01 — NETWORK
   ========================================================================= */
.figpanel__cap {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  padding-bottom: 14px; border-bottom: 2px solid var(--color-divider);
}
.figpanel__cap .kicker { color: var(--color-accent-700); }
.figpanel__cap .meta { font-size: 13px; color: var(--ink-60); font-feature-settings: 'tnum' 1; }

/* The animated globe panel (assets/js/fig01-globe.js). The canvas draws the
   desktop scene (1920×1080) above 720px and the mobile scene (1080×1240)
   below it; the aspect-ratio here must match the scene the script picks. */
.globe-fig { margin-bottom: 48px; background: #0b1230; aspect-ratio: 1920 / 1080; overflow: hidden; }
.globe-fig canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: pan-y; }
.globe-fig canvas:active { cursor: grabbing; }
@media (max-width: 720px) {
  .globe-fig { aspect-ratio: 1080 / 1240; }
}

.hairgrid { display: grid; gap: 1px; background: var(--color-divider); }
.hairgrid > * { background: var(--color-bg); }

/* stacked list — the text equivalent of the figure */
.net-stack { padding: 28px 0 8px; }
.net-stack__origins { display: flex; gap: 40px; margin-bottom: 10px; }
.net-stack__lane { margin-bottom: 28px; font-size: 13px; letter-spacing: 0.04em; color: var(--ink-60); }
.net-stack__origin { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 22px; }
.net-stack__origin .sq { width: 10px; height: 10px; background: var(--color-accent); flex: none; }
.net-stack__origin small { font-size: 12px; font-weight: 400; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-60); }
.net-stack__cluster { margin-bottom: 28px; }
.net-stack__cluster:last-child { margin-bottom: 0; }
.net-stack__head {
  display: flex; justify-content: space-between; align-items: baseline;
  border-top: 2px solid var(--color-text); padding-top: 12px; margin-bottom: 4px;
}
.net-stack__head h3 { font-size: 15px; letter-spacing: 0.02em; text-transform: uppercase; }
.net-stack__head .total { color: var(--color-accent); font-weight: 700; font-feature-settings: 'tnum' 1; }
.net-stack__cluster li {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 9px 0; border-bottom: 1px solid var(--color-divider);
  font-size: 14px;
}
.net-stack__cluster li span:last-child { color: var(--color-accent-700); font-feature-settings: 'tnum' 1; }

/* With JS running the globe carries the figure at every width; the caption
   row and the text list stay in the DOM for screen readers. Without JS the
   text figure is the visible fallback. */
.js .figpanel__cap,
.js .net-stack { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.no-js .globe-fig { display: none; }

/* =========================================================================
   SECTIONS
   ========================================================================= */
.section { border-top: 2px solid var(--color-divider); padding: 72px 0 88px; }
.sec-head { margin-bottom: 40px; }

/* 01 problem */
.problem__cols {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 64px;
  max-width: clamp(1040px, 52vw, 1426px);
}
.problem__cols p { font-size: 18px; line-height: 30px; color: var(--ink-82); }

/* 02/03 markets */
.market { margin-bottom: 72px; }
.market:last-child { margin-bottom: 0; }
.market__fig { display: flex; align-items: baseline; gap: 14px; margin-top: 12px; }
.market__fig .n {
  font-weight: 700; font-size: clamp(64px, 3.2vw, 88px); line-height: 1; letter-spacing: -0.03em;
  color: var(--color-accent); font-feature-settings: 'tnum' 1;
}
.market__fig .u { font-weight: 700; font-size: clamp(32px, 1.6vw, 44px); letter-spacing: -0.02em; }
.market__sub { margin-top: 10px; font-size: 17px; color: var(--ink-70); }

.mrows { border-top: 2px solid var(--color-text); margin-top: 28px; }
.mrow h3 { margin: 0; }
.mrow__btn {
  display: grid; width: 100%;
  grid-template-columns: 72px minmax(0, 1fr) 96px 200px minmax(0, 1fr) 28px;
  gap: 20px; align-items: baseline;
  padding: 18px 0; border-bottom: 1px solid var(--color-divider);
  text-align: left; transition: background .25s ease;
}
.mrow__btn:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.mrow__code { font-weight: 700; font-size: 14px; letter-spacing: 0.06em; color: var(--color-accent); }
.mrow__name { font-size: 17px; }
.mrow__pop { font-size: 15px; color: var(--ink-70); font-feature-settings: 'tnum' 1; }
.mrow__site { font-size: 15px; }
.mrow__reg { font-size: 14px; letter-spacing: 0.04em; color: var(--ink-62); }
.mrow__btn .chev { align-self: center; justify-self: end; }

.mrow__panel[inert] { display: none; }
.mrow__panel {
  display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 20px;
  padding: 8px 0 26px;
  background: color-mix(in srgb, var(--color-text) 3%, transparent);
  border-bottom: 1px solid var(--color-divider);
}
.mrow__tags { display: flex; flex-wrap: wrap; gap: 8px; grid-column: 2; padding-top: 10px; }
.mrow__panel .hairgrid { grid-column: 2; margin-top: 10px; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.mrow__panel .hairgrid > div { padding: 12px 14px; background: var(--color-bg); }
.mrow__panel .hairgrid .domain { display: block; font-size: 14px; }
.mrow__panel .hairgrid .pop { display: block; font-size: 13px; color: var(--color-accent-700); font-feature-settings: 'tnum' 1; }

.market__note { margin-top: 18px; font-size: 14px; color: var(--ink-58); }

/* 04 risk atlas */
.risk__sub { margin-top: 14px; font-size: 15px; color: var(--ink-62); }
.risk__grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--color-divider);
  border-top: 2px solid var(--color-text); border-bottom: 2px solid var(--color-text);
  margin-top: 36px;
}
.risk-card { background: var(--color-bg); }
.risk-card h3 { margin: 0; }
.risk-card__btn { display: block; width: 100%; text-align: left; padding: 24px; transition: background .25s ease; }
.risk-card__btn:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.risk-card__top { display: flex; align-items: baseline; gap: 12px; }
.risk-card__num { font-weight: 700; font-size: 14px; color: var(--color-accent); font-feature-settings: 'tnum' 1; }
.risk-card__title { font-weight: 700; font-size: 24px; letter-spacing: -0.015em; margin-right: auto; }
.risk-card__top .chev { align-self: center; flex: none; }
.risk-card__preview { display: block; margin: 8px 0 0 28px; font-size: 14px; line-height: 22px; color: var(--ink-65); }
.risk-card__tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0 28px; }

.risk-card__panel[inert] { display: none; }
.risk-card__panel { padding: 4px 24px 26px 52px; }
.block-label {
  font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-55); margin: 22px 0 10px;
}
.block-label:first-child { margin-top: 0; }
.block-label--accent { color: var(--color-accent-700); }
.risk-items li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  padding: 9px 0; border-bottom: 1px solid var(--color-divider); font-size: 15px;
}
.risk-items li:last-child { border-bottom: 0; }
.risk-cost { font-size: 17px; line-height: 26px; }
.risk-does { display: flex; flex-wrap: wrap; gap: 8px; }

/* 05 services */
.services { border-top: 2px solid var(--color-divider); margin-top: 36px; }
.service {
  /* the third track is capped rather than 1fr: at a 1700px measure a 1fr
     description reaches 125ch, well past readable. The row simply ends short. */
  display: grid; grid-template-columns: 96px minmax(0, 300px) minmax(0, 71ch);
  gap: 48px; align-items: baseline;
  padding: 26px 0; border-bottom: 2px solid var(--color-divider);
}
.service__num { font-weight: 700; font-size: 14px; color: var(--color-accent); font-feature-settings: 'tnum' 1; }
.service__name { font-size: 26px; letter-spacing: -0.015em; }
.service__desc { font-size: 16px; line-height: 28px; color: var(--ink-78); }

/* 06 how we work */
.steps { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 28px; margin-top: 36px; }
.step { border-top: 2px solid var(--color-text); padding-top: 18px; }
.step .sq { display: block; width: 12px; height: 12px; background: var(--color-accent); margin-bottom: 14px; }
.step__num { font-weight: 700; font-size: 14px; font-feature-settings: 'tnum' 1; }
.step__name { font-size: 22px; letter-spacing: -0.015em; margin-top: 4px; }
.step__desc { font-size: 14px; line-height: 22px; color: var(--ink-70); margin-top: 10px; }
.step__dur {
  margin-top: 12px; font-size: 13px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--color-accent-700);
}

/* 07 pricing */
.pricing__lede { font-size: 18px; line-height: 30px; color: var(--ink-82); max-width: 60ch; margin-top: 20px; }
.manifest {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px;
  background: var(--color-divider);
  border-top: 2px solid var(--color-divider); border-bottom: 2px solid var(--color-divider);
  margin-top: 36px;
}
.manifest .cell { background: var(--color-bg); padding: 18px; }
.manifest .k { display: block; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); margin-bottom: 8px; }
.manifest b { font-weight: 700; font-size: 17px; letter-spacing: -0.01em; }

.stages { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; align-items: stretch; margin-top: 36px; }
.stage { display: flex; flex-direction: column; padding: 28px; border: 1px solid var(--color-divider); }
.stage--entry { border: 2px solid var(--color-accent); }
.stage__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.stage__no { font-weight: 700; font-size: 14px; color: var(--color-accent); font-feature-settings: 'tnum' 1; }
.start-here {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  background: var(--color-accent); color: var(--color-bg); padding: 4px 10px;
}
.stage__name { font-size: 34px; line-height: 1.1; letter-spacing: -0.02em; }
.stage__sub { font-size: 16px; color: var(--ink-65); margin-top: 4px; }
.stage__price { margin-top: 18px; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; line-height: 1; }
.stage__amt { font-weight: 700; font-size: clamp(44px, 2.2vw, 60px); letter-spacing: -0.03em; line-height: 1; font-feature-settings: 'tnum' 1; }
/* The Vietnamese site quotes in dong, so the amount is ~15 characters against
   the dollar sites' 7, and the flex row was breaking it into three lines —
   "từ" / "15.000.000" / "₫". Hold it on one line everywhere, and step the size
   down only in the three-column band, where the card is barely 204px wide.
   Below 1081px the stages stack into a 640px column with room to spare, so
   that keeps the shared sizing and the price stays as loud as on the other
   two sites. */
html[lang="vi"] .stage__amt { white-space: nowrap; }
@media (min-width: 1081px) {
  html[lang="vi"] .stage__amt { font-size: clamp(29px, 1.5vw, 60px); }
}
/* nowrap removes the phone's escape route too: the shared 40px overran the
   card by 21px once the amount could no longer break. */
@media (max-width: 720px) {
  html[lang="vi"] .stage__amt { font-size: 34px; }
}
.stage__unit { font-size: 16px; color: var(--ink-60); }
.stage__note { margin-top: 10px; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); }
.stage .rule { height: 2px; background: var(--color-divider); margin: 20px 0; }
.list-label { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); margin-bottom: 12px; }
.rows li { position: relative; padding-left: 20px; font-size: 15px; line-height: 23px; margin-bottom: 12px; }
.rows li::before { content: ""; position: absolute; left: 0; top: 8px; width: 8px; height: 8px; background: var(--color-accent); }
.infobox { background: var(--color-surface); padding: 16px; margin: auto 0 20px; }
.rows + .infobox { margin-top: 24px; }
.infobox__label { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-60); margin-bottom: 10px; }
.infobox ul li { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; padding: 4px 0; }
.infobox .k { color: var(--ink-65); }
.infobox .v { font-weight: 700; }
.stage .btn { width: 100%; margin-bottom: 16px; font-size: 15px; padding: 12px 18px; }
.stage__footnote { font-size: 14px; line-height: 22px; color: var(--ink-65); }
.stage__footnote .em { font-weight: 700; color: var(--color-text); }

/* launch score */
.score__top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.score__top .lbl { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-60); }
.score__val { font-size: 14px; font-feature-settings: 'tnum' 1; }
.score__val .n { font-weight: 700; font-size: 17px; }
.score__val .verdict { color: var(--color-accent); font-weight: 700; }
.score__bar { display: flex; gap: 3px; height: 12px; margin: 10px 0; }
.score__bar span { flex: 1; background: var(--color-neutral-300); }
.score__bar span.on { background: var(--color-accent); }
.score__scale { display: flex; justify-content: space-between; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); }

/* Hover pop — mouse devices only; touch and keyboard are unaffected. */
@media (hover: hover) and (pointer: fine) {
  .stage { transition: transform .28s cubic-bezier(0.33, 1, 0.68, 1), box-shadow .28s cubic-bezier(0.33, 1, 0.68, 1), border-color .28s ease; }
  .stage:hover {
    border-color: var(--color-accent);
    transform: translateY(-4px);
    box-shadow: var(--shadow-md);
  }
}

/* Side-by-side stage cards share row tracks so every zone — head, name,
   price, note, rule, list, info panel, button, footnote — sits on one
   line across all three cards. Older browsers keep the flex fallback. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 1081px) {
    .stages { grid-template-rows: repeat(11, auto); gap: 0 24px; }
    .stage {
      display: grid; grid-template-rows: subgrid; grid-row: span 11;
      align-content: start;
    }
    .stage .infobox { margin: 24px 0 20px; }
    .stage__footnote { align-self: start; }
  }
}

/* mobile stage switcher */
.stageswitch { display: none; }

/* consignment — the second "way in", highlighted like stage card 01 */
.collab { background: var(--color-surface); padding: 40px; margin-top: 36px; border: 2px solid var(--color-accent); }
.collab .start-here { display: inline-block; margin-bottom: 14px; font-feature-settings: 'tnum' 1; }
.start-here { font-feature-settings: 'tnum' 1; }
.collab__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
.collab__title { font-size: 36px; line-height: 1.15; letter-spacing: -0.02em; margin-top: 10px; max-width: 24ch; }
.collab__price { text-align: right; }
.collab__price .amt { display: block; font-weight: 700; font-size: clamp(48px, 2.4vw, 66px); line-height: 1; letter-spacing: -0.03em; color: var(--color-accent); font-feature-settings: 'tnum' 1; }
.collab__price .unit { display: block; margin-top: 8px; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); }
.collab__lede { font-size: 17px; line-height: 28px; color: var(--ink-82); max-width: 76ch; margin-top: 20px; }
.collab__split {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 48px;
  border-top: 2px solid var(--color-divider); margin-top: 28px; padding-top: 24px;
}
.collab__foot { border-top: 2px solid var(--color-divider); margin-top: 28px; padding-top: 24px; }
.foot-lead { font-size: 16px; }
.foot-lead .em { font-weight: 700; }
.structures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 16px; }
.struct { display: flex; gap: 14px; background: var(--color-bg); padding: 16px; font-size: 14px; line-height: 22px; }
.struct__k { font-weight: 700; color: var(--color-accent); font-size: 17px; }

.principles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; margin-top: 36px; }
.principle { display: flex; gap: 14px; align-items: flex-start; }
.principle .sq { flex: none; width: 12px; height: 12px; background: var(--color-accent); margin-top: 7px; }
.principle p { font-size: 16px; line-height: 26px; }

/* 08 FAQ */
.faq { max-width: 900px; border-top: 2px solid var(--color-text); }
.faq__item h3 { margin: 0; }
.faq__btn {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px 0; border-bottom: 1px solid var(--color-divider);
  text-align: left; font-weight: 700; font-size: 20px; transition: color .2s ease;
}
.faq__btn:hover { color: var(--color-accent); }
.faq__btn .chev { width: 18px; height: 18px; flex: none; }
.faq__panel[inert] { display: none; }
.faq__panel { padding: 16px 60px 24px 0; border-bottom: 1px solid var(--color-divider); }
.faq__panel p { font-size: 16px; line-height: 28px; color: var(--ink-78); }

/* poster */
.poster { background: var(--color-accent); color: var(--color-bg); padding: 88px 0; }
.poster h2 { font-size: clamp(60px, 3vw, 82px); line-height: 1.06; letter-spacing: -0.025em; margin-left: -0.058em; max-width: 22ch; }
.poster__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 40px; }
.poster .btn { font-size: 15px; padding: 14px 22px; }
.btn-paper { background: var(--color-bg); color: var(--color-accent); }
.btn-paper:hover { background: var(--color-neutral-200); }
.btn-ghost-paper { border: 1px solid var(--color-bg); color: var(--color-bg); }
.btn-ghost-paper:hover { background: color-mix(in srgb, #ffffff 14%, transparent); }
.poster :focus-visible { outline-color: var(--color-bg); }

/* footer */
.footer { padding: 64px 0 32px; }
.footer__grid {
  display: grid; grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
  gap: 48px; padding-bottom: 40px; border-bottom: 2px solid var(--color-divider);
}
.footer__desc { margin-top: 12px; font-size: 15px; line-height: 24px; color: var(--ink-65); max-width: 34ch; }
.footer__col h3 { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); font-weight: 400; margin-bottom: 14px; }
.footer__col li { margin-bottom: 9px; font-size: 15px; }
.footer__col a:hover { color: var(--color-accent); }
.footer__col span { color: var(--ink-65); }
.footer__bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px 32px;
  padding-top: 24px; font-size: 13px; color: var(--ink-60);
}
.footer__bar .lang { margin-left: auto; }

/* homepage sticky mobile bar */
.mobilebar {
  display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  background: var(--color-bg); border-top: 2px solid var(--color-divider);
  padding: 14px 20px; gap: 12px;
}
.mobilebar .btn-primary { flex: 1; font-size: 15px; padding: 13px 16px; }
.mobilebar .btn-wa-sq { width: 48px; height: 48px; padding: 0; justify-content: center; border: 1px solid var(--color-divider); flex: none; }

/* =========================================================================
   AUDIT PAGE
   ========================================================================= */
.audit-header { position: sticky; top: 0; z-index: 100; background: var(--color-bg); border-bottom: 2px solid var(--color-divider); }
.audit-header__inner { display: flex; align-items: center; justify-content: space-between; height: 76px; }
.audit-header .meta { font-size: 14px; color: var(--ink-60); }
.audit-header .meta--short { display: none; }

.audit { padding: 72px 0 88px; }
.audit__grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 80px; }

.audit__lead h1 { font-size: clamp(54px, 2.7vw, 74px); line-height: 1.06; letter-spacing: -0.02em; margin-left: -0.058em; margin-top: 14px; }
.audit__lede { font-size: 18px; line-height: 30px; color: var(--ink-82); margin-top: 24px; }
.audit__steps { border-top: 2px solid var(--color-text); margin-top: 32px; }
.audit__steps li { display: flex; gap: 18px; padding: 16px 0; border-bottom: 1px solid var(--color-divider); }
.audit__steps .n { font-weight: 700; font-size: 14px; color: var(--color-accent); font-feature-settings: 'tnum' 1; }
.audit__steps .t { font-weight: 700; font-size: 16px; }
.audit__steps .d { font-size: 14px; line-height: 22px; color: var(--ink-70); margin-top: 2px; }
.audit__talk { background: var(--color-surface); padding: 24px; margin-top: 32px; }
.audit__talk p { font-weight: 700; margin-bottom: 14px; }

.form-panel { border: 2px solid var(--color-text); padding: 36px; }
.field { margin-bottom: 18px; }
.field > label { display: block; font-size: 12px; letter-spacing: 0.04em; color: var(--ink-70); margin-bottom: 6px; }
.field .req { color: var(--color-accent-700); }
.input {
  width: 100%; min-height: 44px; padding: 8px 12px; font: inherit;
  /* 16px is the BASE, not a touch override: anything smaller makes iOS Safari
     zoom the layout viewport in on focus and never zoom back out. The 15px
     desktop size is gated on a real mouse further down, so this fails safe on
     any finger-driven device that reports a fine pointer (iPad + trackpad,
     Windows touch laptop). */
  font-size: 16px;
  color: var(--color-text); caret-color: var(--color-accent);
  background: var(--color-surface); border: 1px solid var(--color-divider); border-radius: 0;
}
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { min-height: 96px; resize: vertical; }
.input.is-error { border-color: var(--color-accent); }
.field__err { display: none; font-size: 13px; line-height: 18px; color: var(--color-accent-700); margin-top: 6px; }
.field.has-error .field__err { display: block; }

.form-rule { height: 2px; background: var(--color-divider); margin: 26px 0; border: 0; }

.seg { display: flex; border: 1px solid var(--color-divider); }
.seg-opt {
  display: inline-flex; align-items: center; justify-content: flex-start;
  padding: 10px 14px; font-size: 14px; cursor: pointer; flex: 1;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt input, .tile input, .radio input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.seg-opt:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; background: var(--color-divider); border: 1px solid var(--color-divider); }
.tile { display: flex; align-items: center; background: var(--color-bg); padding: 12px 14px; font-size: 14px; cursor: pointer; }
.tile:hover { background: color-mix(in srgb, var(--color-text) 6%, transparent); }
.tile:has(input:checked) { background: var(--color-accent); color: var(--color-bg); }
.tile:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

.radio { display: flex; align-items: center; gap: 10px; cursor: pointer; font-size: 15px; padding: 6px 0; }
.radio .dot { width: 16px; height: 16px; flex: none; border-radius: 50%; border: 1.5px solid var(--color-divider); }
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot { border-color: var(--color-accent); background: var(--color-accent); box-shadow: inset 0 0 0 4px var(--color-bg); }
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.form-foot { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.form-foot .note { font-size: 13px; line-height: 20px; color: var(--ink-60); max-width: 40ch; }
.form-foot .btn { font-size: 15px; padding: 13px 22px; flex: none; }

.form-success { text-align: left; }
.form-success h2 { font-size: 34px; line-height: 1.15; letter-spacing: -0.02em; margin-bottom: 16px; }
.form-success p { font-size: 16px; line-height: 28px; color: var(--ink-78); margin-bottom: 12px; }

/* mobile form steps */
.form-stepmeta { display: none; }
.form-stickybar { display: none; }

/* =========================================================================
   PORTFOLIO
   ========================================================================= */
.pf-hero { padding: 72px 0 48px; }
.pf-hero h1 { font-size: clamp(52px, 2.6vw, 71px); line-height: 1.06; letter-spacing: -0.02em; margin-left: -0.058em; margin-top: 14px; }
.pf-hero h1 span { display: block; }
.pf-hero h1 .soft { color: var(--ink-45); }
.pf-hero__sub { font-size: 17px; line-height: 28px; color: var(--ink-70); margin-top: 18px; max-width: 76ch; }
.pf-hero .hero__stats { margin-top: 44px; }

.cases { border-top: 2px solid var(--color-text); margin-top: 14px; margin-bottom: 72px; }
.case { border-bottom: 1px solid var(--color-divider); }
.case__row {
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr) auto 28px;
  gap: 20px; align-items: center; width: 100%;
  padding: 20px 0; text-align: left; transition: background .25s ease;
}
.case__row:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.case__name { font-weight: 700; font-size: 22px; letter-spacing: -0.015em; }
.case__cat, .case__market { font-size: 14px; color: var(--ink-65); }
.case__status { justify-self: start; }
.case__row .chev { align-self: center; justify-self: end; }
.case__panel[inert] { display: none; }
.case__panel {
  padding: 4px 0 36px;
  background: color-mix(in srgb, var(--color-text) 3%, transparent);
}
.case__body { max-width: 900px; padding: 16px 20px 0; }
.case__tagline { font-weight: 700; font-size: 19px; letter-spacing: -0.01em; line-height: 28px; margin-bottom: 6px; }
.case__body > p:not(.block-label):not(.case__tagline) { font-size: 15px; line-height: 25px; color: var(--ink-78); max-width: 80ch; }
.case__body .block-label { margin-top: 24px; }
.case__body .rows li { font-size: 15px; }

/* =========================================================================
   REVEAL / MOTION
   ========================================================================= */
.js .reveal {
  opacity: 0; transform: translateY(18px);
  transition: opacity .7s cubic-bezier(0.22, 1, 0.36, 1), transform .7s cubic-bezier(0.22, 1, 0.36, 1);
}
.js .reveal.in { opacity: 1; transform: none; }
.js .reveal.d1 { transition-delay: .1s; }
.js .reveal.d2 { transition-delay: .2s; }
.js .reveal.d3 { transition-delay: .3s; }
.js .reveal.d4 { transition-delay: .4s; }

/* Disclosure panels (markets, risk atlas, FAQ, cases) slide + fade open.
   site.js drives height/padding inline; this class carries the curve. */
.js .is-anim {
  overflow: hidden;
  transition: height .4s cubic-bezier(0.33, 1, 0.68, 1),
              padding-top .4s cubic-bezier(0.33, 1, 0.68, 1),
              padding-bottom .4s cubic-bezier(0.33, 1, 0.68, 1),
              border-bottom-width .4s cubic-bezier(0.33, 1, 0.68, 1),
              opacity .34s ease;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
  .js .reveal { opacity: 1; transform: none; }
  /* A looping clip is unstoppable motion in the reader's peripheral vision,
     which is the case this query exists for. The panel keeps its dark ground,
     so nothing else in the hero has to change. site.js also skips play(). */
  .hero__video { display: none; }
}

/* =========================================================================
   RESPONSIVE
   ========================================================================= */
@media (max-width: 1080px) {
  /* The left-weighted pass above assumes the copy sits in the left half and the
     right edge is free. Once the wrap narrows, the headline and sub-copy run
     the full width and that near-transparent right end lands straight behind
     them — measured, it drops the sub-copy to 3.3:1. Flatten the horizontal
     pass to a single value and let the vertical one do the shaping; the same
     sampling then reads 9.2:1 headline, 5.3:1 kicker, 6.8:1 sub, 4.1:1 stats
     and 6.2:1 stat labels. */
  .hero__veil {
    background:
      linear-gradient(180deg, rgba(32, 30, 29, 0.46) 0%, rgba(32, 30, 29, 0.62) 62%, rgba(32, 30, 29, 0.84) 100%),
      rgba(32, 30, 29, 0.68);
  }

  .display { font-size: 64px; }
  .h2 { font-size: 44px; }
  .poster h2 { font-size: 48px; }
  .risk__grid { grid-template-columns: minmax(0, 1fr); }
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
  .steps .step { margin-bottom: 12px; }
  .stages { grid-template-columns: minmax(0, 1fr); max-width: 640px; }
  .manifest { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .audit__grid { grid-template-columns: minmax(0, 1fr); gap: 48px; }
  .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 880px) {
  .header__nav, .header .lang, .header .btn-cta { display: none; }
  .burger { display: flex; }
  .drawer { display: flex; }
  .header__inner { height: 60px; }
  .wordmark { font-size: 18px; }

  .mrow__btn { grid-template-columns: 52px minmax(0, 1fr) 28px; gap: 8px 14px; padding: 16px 0; }
  .mrow__name { font-size: 16px; font-weight: 700; }
  .mrow__pop { grid-column: 2; font-size: 13px; }
  .mrow__site { display: none; }
  .mrow__reg { display: none; }
  .mrow__btn .chev { grid-row: 1 / span 2; grid-column: 3; }
  .mrow__code { grid-row: 1 / span 2; align-self: start; }
  .mrow__panel { grid-template-columns: minmax(0, 1fr); padding-left: 0; }
  .mrow__tags, .mrow__panel .hairgrid { grid-column: 1; }
  .mrow__panel .hairgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .service { grid-template-columns: 48px minmax(0, 1fr); gap: 8px 20px; }
  .service__desc { grid-column: 2; }
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .problem__cols { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .collab__split, .structures, .principles { grid-template-columns: minmax(0, 1fr); }
  .collab { padding: 28px 20px; }
  .case__row { grid-template-columns: minmax(0, 1fr) 28px; gap: 6px 14px; }
  .case__cat, .case__market, .case__status { grid-column: 1; }
  .case__row .chev { grid-row: 1; grid-column: 2; }
  .case__body { padding-inline: 14px; }
}

@media (max-width: 720px) {
  body { font-size: 15px; line-height: 26px; }

  .hero { padding: 32px 0 36px; }
  .display { font-size: 42px; letter-spacing: -0.02em; }
  .hero__sub { font-size: 17px; line-height: 28px; margin-top: 18px; }
  .hero__actions { flex-direction: column; align-items: stretch; margin-top: 24px; }
  .hero__actions .btn { width: 100%; padding: 15px 16px; }
  /* Rules move from the 1px-gap trick onto the rows themselves: that wanted an
     opaque fill per row, which would have blanked the clip behind the stats. */
  .hero__stats {
    grid-template-columns: minmax(0, 1fr); gap: 0;
    border-top: 2px solid var(--color-bg); padding-top: 0; margin-top: 36px;
  }
  .hero__stats > div {
    display: flex; align-items: baseline; gap: 20px; padding: 14px 0;
    border-top: 1px solid color-mix(in srgb, var(--color-bg) 40%, transparent);
  }
  .hero__stats > div:first-child { border-top: 0; }
  .hero__stats dt { font-size: 36px; min-width: 100px; }
  .hero__stats dd { margin: 0; font-size: 12px; }

  .section { padding: 48px 0 56px; }
  .sec-head { margin-bottom: 28px; }
  .h2 { font-size: 34px; line-height: 1.08; }
  .kicker { font-size: 12px; }
  .problem__cols p { font-size: 16px; line-height: 26px; }
  .market__fig .n { font-size: 44px; }
  .market__fig .u { font-size: 22px; }
  .market__sub { font-size: 15px; }

  .risk-card__btn { padding: 20px; }
  .risk-card__title { font-size: 20px; }
  .risk-card__preview { font-size: 13px; margin-left: 25px; }
  .risk-card__tags { margin-left: 25px; }
  .risk-card__panel { padding: 4px 20px 22px 25px; }

  .service__name { font-size: 22px; }
  .steps { grid-template-columns: minmax(0, 1fr); }

  .manifest { grid-template-columns: minmax(0, 1fr); }
  .stageswitch { display: flex; border: 1px solid var(--color-divider); margin-top: 28px; }
  .stageswitch button { flex: 1; text-align: left; padding: 10px 12px; font-size: 13px; font-weight: 700; }
  .stageswitch button + button { border-left: 1px solid var(--color-divider); }
  .stageswitch button[aria-pressed="true"] { background: var(--color-accent); color: var(--color-bg); }
  .stages { margin-top: 20px; gap: 20px; }
  .js .stages.is-switching .stage { display: none; }
  .js .stages.is-switching .stage.is-current { display: flex; }
  .stage { padding: 22px; }
  .stage__name { font-size: 30px; }
  .stage__amt { font-size: 40px; }
  .stage .btn { width: 100%; }

  .collab__title { font-size: 28px; }
  .collab__price .amt { font-size: 40px; }
  .faq__btn { font-size: 17px; }
  .faq__panel { padding-right: 24px; }

  .poster { padding: 56px 0; }
  .poster h2 { font-size: 36px; }
  .poster__actions { flex-direction: column; align-items: stretch; }

  .footer { padding: 48px 0 96px; }
  .footer__grid { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .footer__bar { padding-top: 20px; }
  .footer__bar .lang { margin-left: 0; }

  .mobilebar { display: flex; }
  body.has-mobilebar { padding-bottom: 80px; }

  /* audit page */
  .audit-header__inner { height: 60px; }
  .audit-header .meta { display: none; }
  .audit-header .meta--short { display: block; font-size: 13px; }
  .audit { padding: 36px 0 120px; }
  .audit__lead h1 { font-size: 36px; line-height: 1.07; }
  .audit__lede { font-size: 16px; line-height: 26px; }
  .form-panel { border: 0; padding: 0; margin-top: 8px; }
  .input { min-height: 48px; font-size: 16px; }
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tile { padding: 14px; font-size: 15px; }
  .form-foot { flex-direction: column; align-items: stretch; }
  .form-foot .btn { width: 100%; }

  .js .form-step { display: none; }
  .js .form-step.is-current { display: block; }
  .js .form-foot { display: none; }
  .form-stepmeta { display: block; font-size: 13px; color: var(--ink-60); margin-bottom: 20px; }
  .form-stickybar {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
    background: var(--color-bg); border-top: 2px solid var(--color-divider);
    padding: 14px 20px;
  }
  .form-stickybar .btn { width: 100%; font-size: 16px; padding: 15px 18px; }

  .pf-hero { padding: 36px 0 28px; }
  .pf-hero h1 { font-size: 34px; }
}

@media (max-width: 380px) {
  .display { font-size: 36px; }
  .h2 { font-size: 30px; }
  .lang a, .lang .lang__current { padding: 5px 8px; }
}

/* no-JS: disclosures render open, reveals visible */
.no-js .mrow__panel[inert],
.no-js .risk-card__panel[inert],
.no-js .faq__panel[inert],
.no-js .case__panel[inert] { display: block; }
.no-js .reveal { opacity: 1; transform: none; }

@media print {
  .header, .mobilebar, .drawer, .scrim, .form-stickybar { display: none !important; }
}

/* =========================================================================
   V2 upgrades layer — kinetic hero, section patterns + sheets, brushes,
   proof strip, ledgers, marquee, green CTAs, flags. All motion dies under
   the reduced-motion kill-switch above.
   ========================================================================= */

/* ---------- section background patterns — global / fulfillment / logistics.
   v2: each section is a textured ground with its own paper shade; the
   section's .wrap becomes an opaque SHEET that carries every piece of
   text, so the pattern underlaps content and never runs through it.
   Line art in ink/red only, opacity baked into each SVG (6–12%). ---------- */

#problem > .wrap, #markets > .wrap, #risk > .wrap, #services > .wrap,
#proof > .wrap, #how > .wrap, #pricing > .wrap, #faq > .wrap {
  background: var(--color-bg);
  padding: 48px clamp(20px, 4vw, 56px) 56px;
  border: 1px solid color-mix(in srgb, var(--color-text) 10%, transparent);
}
#problem, #markets, #risk, #services, #proof, #how, #pricing, #faq {
  padding: 56px 0 64px;
}
@media (max-width: 720px) {
  #problem > .wrap, #markets > .wrap, #risk > .wrap, #services > .wrap,
  #proof > .wrap, #how > .wrap, #pricing > .wrap, #faq > .wrap {
    padding: 28px 16px 36px;
  }
  #problem, #markets, #risk, #services, #proof, #how, #pricing, #faq {
    padding: 36px 0 44px;
  }
}

/* 01 · The problem — globe graticule fan */
#problem {
  background-color: #f5f4f2;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='340' height='340'%3E%3Cg fill='none' stroke='%23201e1d' stroke-opacity='.08'%3E%3Ccircle cx='0' cy='340' r='55'/%3E%3Ccircle cx='0' cy='340' r='110'/%3E%3Ccircle cx='0' cy='340' r='165'/%3E%3Ccircle cx='0' cy='340' r='275'/%3E%3Ccircle cx='0' cy='340' r='330'/%3E%3Cpath d='M0 340 L340 250M0 340 L300 120M0 340 L170 30M0 340 L60 0'/%3E%3C/g%3E%3Ccircle cx='0' cy='340' r='220' fill='none' stroke='%23ec3013' stroke-opacity='.06'/%3E%3C/svg%3E");
}

/* 02/03 · Markets — route hub: dashed lanes out to square ports */
#markets {
  background-color: #eff0f2;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300'%3E%3Cg fill='none'%3E%3Cpath d='M35 152 Q140 40 250 70' stroke='%23201e1d' stroke-opacity='.085' stroke-dasharray='7 6'/%3E%3Cpath d='M35 155 Q170 120 330 150' stroke='%23ec3013' stroke-opacity='.07' stroke-dasharray='3 6'/%3E%3Cpath d='M35 158 Q160 240 300 230' stroke='%23201e1d' stroke-opacity='.085' stroke-dasharray='7 6'/%3E%3C/g%3E%3Crect x='30' y='150' width='10' height='10' fill='%23201e1d' fill-opacity='.12'/%3E%3Crect x='246' y='64' width='8' height='8' fill='%23201e1d' fill-opacity='.10'/%3E%3Crect x='326' y='145' width='8' height='8' fill='%23ec3013' fill-opacity='.09'/%3E%3Crect x='296' y='226' width='8' height='8' fill='%23201e1d' fill-opacity='.10'/%3E%3Cpolygon points='188,131 172,139 175,127' fill='%23201e1d' fill-opacity='.10'/%3E%3C/svg%3E");
}

/* 04 · Risk atlas — customs stamp, hazard hatch, alert mark */
#risk {
  background-color: #f6f2f0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='320'%3E%3Cg fill='none' stroke='%23201e1d'%3E%3Ccircle cx='90' cy='95' r='44' stroke-opacity='.09' stroke-dasharray='4 5'/%3E%3Ccircle cx='90' cy='95' r='32' stroke-opacity='.07'/%3E%3C/g%3E%3Crect x='85' y='90' width='10' height='10' fill='%23201e1d' fill-opacity='.10'/%3E%3Cg fill='none' stroke='%23201e1d' stroke-opacity='.06'%3E%3Cpath d='M240 300 L330 210M258 300 L348 210M276 300 L360 216M240 282 L312 210'/%3E%3C/g%3E%3Ccircle cx='302' cy='72' r='13' fill='none' stroke='%23ec3013' stroke-opacity='.07'/%3E%3Crect x='298' y='68' width='8' height='8' fill='%23ec3013' fill-opacity='.09'/%3E%3C/svg%3E");
}

/* 05 · Services — ISO containers with corrugation and door braces */
#services {
  background-color: #f0f1ef;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='380' height='300'%3E%3Cg fill='none' stroke='%23201e1d' stroke-opacity='.09'%3E%3Crect x='24' y='50' width='130' height='58'/%3E%3Cpath d='M38 50v58M52 50v58M66 50v58M80 50v58M94 50v58M108 50v58M122 50v58'/%3E%3Cpath d='M130 50 L154 108M154 50 L130 108'/%3E%3C/g%3E%3Cg fill='%23201e1d' fill-opacity='.12'%3E%3Crect x='22' y='48' width='5' height='5'/%3E%3Crect x='151' y='48' width='5' height='5'/%3E%3Crect x='22' y='105' width='5' height='5'/%3E%3Crect x='151' y='105' width='5' height='5'/%3E%3C/g%3E%3Cg fill='none' stroke='%23ec3013' stroke-opacity='.06'%3E%3Crect x='210' y='180' width='110' height='50'/%3E%3Cpath d='M224 180v50M238 180v50M252 180v50M266 180v50M280 180v50M294 180v50'/%3E%3C/g%3E%3C/svg%3E");
}

/* Proof — product lineup on a shelf rule */
#proof {
  background-color: #f4f3f1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='260'%3E%3Cpath d='M30 150 H290' stroke='%23201e1d' stroke-opacity='.08' fill='none' stroke-width='2'/%3E%3Crect x='60' y='132' width='18' height='18' fill='%23201e1d' fill-opacity='.09'/%3E%3Crect x='130' y='124' width='26' height='26' fill='none' stroke='%23ec3013' stroke-opacity='.08'/%3E%3Crect x='210' y='136' width='14' height='14' fill='%23201e1d' fill-opacity='.10'/%3E%3C/svg%3E");
}

/* 06 · How we work — dashed route through waypoint squares */
#how {
  background-color: #eff0ef;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='380' height='320'%3E%3Cpath d='M44 74 L164 54 L234 184 L330 144' stroke='%23201e1d' stroke-opacity='.08' stroke-dasharray='3 7' fill='none'/%3E%3Crect x='40' y='70' width='9' height='9' fill='%23201e1d' fill-opacity='.11'/%3E%3Crect x='160' y='50' width='9' height='9' fill='%23201e1d' fill-opacity='.11'/%3E%3Crect x='230' y='180' width='9' height='9' fill='%23ec3013' fill-opacity='.09'/%3E%3Cpolygon points='344,141 330,136 332,150' fill='%23201e1d' fill-opacity='.10'/%3E%3C/svg%3E");
}

/* 07 · Pricing — barcode block over ledger rules */
#pricing {
  background-color: #f5f4f3;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200'%3E%3Cg stroke='%23201e1d' stroke-opacity='.05' fill='none'%3E%3Cpath d='M170 0v200M230 0v200M290 0v200'/%3E%3C/g%3E%3Cg fill='%23201e1d' fill-opacity='.08'%3E%3Crect x='40' y='42' width='4' height='44'/%3E%3Crect x='47' y='42' width='2' height='44'/%3E%3Crect x='52' y='42' width='6' height='44'/%3E%3Crect x='61' y='42' width='2' height='44'/%3E%3Crect x='66' y='42' width='4' height='44'/%3E%3Crect x='74' y='42' width='2' height='44'/%3E%3Crect x='79' y='42' width='8' height='44'/%3E%3Crect x='90' y='42' width='3' height='44'/%3E%3Crect x='96' y='42' width='5' height='44'/%3E%3C/g%3E%3Cg stroke='%23201e1d' stroke-opacity='.07' fill='none'%3E%3Cpath d='M226 60h8M286 150h8'/%3E%3C/g%3E%3Cpath d='M166 120h8' stroke='%23ec3013' stroke-opacity='.09' fill='none'/%3E%3C/svg%3E");
}

/* 08 · FAQ — dot grid with an occasional open ring */
#faq {
  background-color: #f1f1f0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cg fill='%23201e1d' fill-opacity='.09'%3E%3Ccircle cx='20' cy='20' r='1.4'/%3E%3Ccircle cx='80' cy='20' r='1.4'/%3E%3Ccircle cx='20' cy='80' r='1.4'/%3E%3C/g%3E%3Ccircle cx='80' cy='80' r='5' fill='none' stroke='%23201e1d' stroke-opacity='.08'/%3E%3C/svg%3E");
}

/* ---------- 1 · count-up numerals (behaviour in draft.js) ---------- */
[data-countup] { display: inline-block; }

/* ---------- WhatsApp-green collaborative CTAs ---------- */
.btn-wa { background: #1c9e50; color: #fff; }
.btn-wa:hover { background: #178443; color: #fff; }
.btn-wa:active { background: #126937; }
.js .btn-wa { position: relative; overflow: hidden; z-index: 0; }
.js .btn-wa::before {
  content: ""; position: absolute; inset: 0; background: #178443;
  transform: scaleX(0); transform-origin: 0 50%; z-index: -1;
  transition: transform .32s cubic-bezier(0.33, 1, 0.68, 1);
}
.js .btn-wa:hover::before, .js .btn-wa:focus-visible::before { transform: scaleX(1); }
.js .btn-wa:hover { background: #1c9e50; }
.poster .btn-wa:focus-visible { outline-color: var(--color-bg); }

/* mobile bar WhatsApp square goes green too */
.mobilebar .btn-wa-sq { background: #1c9e50; border-color: transparent; color: #fff; }
.mobilebar .btn-wa-sq:hover { background: #178443; }

/* consignment CTA — the standout button in the collab panel */
.btn-collab { font-size: 16px; font-weight: 700; padding: 15px 26px; margin-top: 20px; }

/* ---------- shared marquee keyframes (poster marquee) ---------- */
@keyframes marq { to { transform: translateX(-50%); } }

/* ---------- 4 · kinetic hero ---------- */
.display__mask { display: block; overflow: hidden; }
.js .display--kinetic .display__line {
  display: block; transform: translateY(112%);
  transition: transform .85s cubic-bezier(0.22, 1, 0.36, 1);
}
.js .display--kinetic .display__mask:nth-child(2) .display__line { transition-delay: .12s; }
.js .display--kinetic.in .display__line { transform: none; }

/* brushstroke underline — a hand-drawn SVG stroke that paints itself in.
   z-index:0 makes the em a stacking context, so the negative-z stroke sits
   behind the glyphs but still above the opaque section sheet. */
.sweep { font-style: normal; position: relative; display: inline-block; z-index: 0; }
.sweep__stroke {
  position: absolute; left: -0.06em; bottom: -0.16em;
  width: calc(100% + 0.12em); height: 0.38em;
  overflow: visible; pointer-events: none; z-index: -1;
}
.sweep__stroke path { fill: none; stroke: var(--color-accent); stroke-linecap: round; }
.sweep__stroke .sweep__p1 { stroke-width: 8; opacity: 0.9; }
.sweep__stroke .sweep__p2 { stroke-width: 3.5; opacity: 0.55; }
.js .sweep__stroke path { stroke-dasharray: 1; stroke-dashoffset: 1; }
.js .sweep__stroke .sweep__p1 { transition: stroke-dashoffset .7s cubic-bezier(0.6, 0.05, 0.4, 1) .95s; }
.js .sweep__stroke .sweep__p2 { transition: stroke-dashoffset .55s cubic-bezier(0.6, 0.05, 0.4, 1) 1.2s; }
/* draws when the carrying .reveal block comes in (hero headline and section heads) */
.js .reveal.in .sweep__stroke path { stroke-dashoffset: 0; }

/* section-heading sweeps: slimmer stroke for 52px type, snappier timing */
.h2 .sweep__stroke .sweep__p1 { stroke-width: 9; }
.h2 .sweep__stroke .sweep__p2 { stroke-width: 4; }
.js .h2 .sweep__stroke .sweep__p1 { transition-delay: .45s; }
.js .h2 .sweep__stroke .sweep__p2 { transition-delay: .68s; }

/* green brush variant — "crossed" */
.sweep--green .sweep__stroke path { stroke: #1c9e50; }

/* staggered cascades for the three big grids */
.js .services.reveal .service, .js .steps.reveal .step, .js .risk__grid.reveal .risk-card {
  opacity: 0; transform: translateY(16px);
  transition: opacity .6s cubic-bezier(0.22, 1, 0.36, 1), transform .6s cubic-bezier(0.22, 1, 0.36, 1);
}
.js .services.reveal.in .service, .js .steps.reveal.in .step, .js .risk__grid.reveal.in .risk-card {
  opacity: 1; transform: none;
}
.js .services.reveal.in .service:nth-child(1), .js .steps.reveal.in .step:nth-child(1), .js .risk__grid.reveal.in .risk-card:nth-child(1) { transition-delay: .05s; }
.js .services.reveal.in .service:nth-child(2), .js .steps.reveal.in .step:nth-child(2), .js .risk__grid.reveal.in .risk-card:nth-child(2) { transition-delay: .12s; }
.js .services.reveal.in .service:nth-child(3), .js .steps.reveal.in .step:nth-child(3), .js .risk__grid.reveal.in .risk-card:nth-child(3) { transition-delay: .19s; }
.js .services.reveal.in .service:nth-child(4), .js .steps.reveal.in .step:nth-child(4), .js .risk__grid.reveal.in .risk-card:nth-child(4) { transition-delay: .26s; }
.js .services.reveal.in .service:nth-child(5), .js .steps.reveal.in .step:nth-child(5), .js .risk__grid.reveal.in .risk-card:nth-child(5) { transition-delay: .33s; }
.js .services.reveal.in .service:nth-child(6), .js .risk__grid.reveal.in .risk-card:nth-child(6) { transition-delay: .40s; }
.js .risk__grid.reveal.in .risk-card:nth-child(7) { transition-delay: .47s; }
.js .risk__grid.reveal.in .risk-card:nth-child(8) { transition-delay: .54s; }

/* ---------- 5 · timeline draw (how we work) ---------- */
.js .steps.reveal .step { border-top-color: transparent; position: relative; }
.js .steps.reveal .step::before {
  content: ""; position: absolute; top: -2px; left: 0; width: 100%; height: 2px;
  background: var(--color-accent);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .55s cubic-bezier(0.33, 1, 0.68, 1);
}
.js .steps.reveal.in .step::before { transform: scaleX(1); }
.js .steps.reveal .step .sq { transform: scale(0); transition: transform .3s cubic-bezier(0.33, 1, 0.68, 1); }
.js .steps.reveal.in .step .sq { transform: scale(1); }
.js .steps.reveal.in .step:nth-child(1)::before { transition-delay: .05s; }
.js .steps.reveal.in .step:nth-child(2)::before { transition-delay: .22s; }
.js .steps.reveal.in .step:nth-child(3)::before { transition-delay: .39s; }
.js .steps.reveal.in .step:nth-child(4)::before { transition-delay: .56s; }
.js .steps.reveal.in .step:nth-child(5)::before { transition-delay: .73s; }
.js .steps.reveal.in .step:nth-child(1) .sq { transition-delay: .3s; }
.js .steps.reveal.in .step:nth-child(2) .sq { transition-delay: .47s; }
.js .steps.reveal.in .step:nth-child(3) .sq { transition-delay: .64s; }
.js .steps.reveal.in .step:nth-child(4) .sq { transition-delay: .81s; }
.js .steps.reveal.in .step:nth-child(5) .sq { transition-delay: .98s; }

/* ---------- pricing cards: hard offset-shadow hover (poster-style) ---------- */
@media (hover: hover) and (pointer: fine) {
  .stage {
    transition: transform .3s cubic-bezier(0.33, 1, 0.68, 1),
                box-shadow .3s cubic-bezier(0.33, 1, 0.68, 1),
                border-color .3s ease;
  }
  .stage:hover {
    border-color: var(--color-accent);
    transform: translate(-4px, -4px);
    box-shadow: 8px 8px 0 var(--color-accent-600);
  }
}

/* ---------- 6 · micro-interactions ---------- */
/* primary button wipe */
.js .btn-primary { position: relative; overflow: hidden; z-index: 0; }
.js .btn-primary::before {
  content: ""; position: absolute; inset: 0; background: var(--color-accent-600);
  transform: scaleX(0); transform-origin: 0 50%; z-index: -1;
  transition: transform .32s cubic-bezier(0.33, 1, 0.68, 1);
}
.js .btn-primary:hover::before, .js .btn-primary:focus-visible::before { transform: scaleX(1); }
.js .btn-primary:hover { background: var(--color-accent); }

/* tags stamp in when a panel opens */
@keyframes tag-in { from { opacity: 0; transform: translateY(5px) scale(0.96); } }
.js .mrow__panel:not([inert]) .tag, .js .risk-card__panel:not([inert]) .tag {
  animation: tag-in .3s cubic-bezier(0.33, 1, 0.68, 1) backwards;
}
.js .mrow__panel:not([inert]) .tag:nth-child(2), .js .risk-card__panel:not([inert]) .tag:nth-child(2) { animation-delay: .04s; }
.js .mrow__panel:not([inert]) .tag:nth-child(3), .js .risk-card__panel:not([inert]) .tag:nth-child(3) { animation-delay: .08s; }
.js .mrow__panel:not([inert]) .tag:nth-child(4), .js .risk-card__panel:not([inert]) .tag:nth-child(4) { animation-delay: .12s; }
.js .mrow__panel:not([inert]) .tag:nth-child(5), .js .risk-card__panel:not([inert]) .tag:nth-child(5) { animation-delay: .16s; }
.js .mrow__panel:not([inert]) .tag:nth-child(6), .js .risk-card__panel:not([inert]) .tag:nth-child(6) { animation-delay: .20s; }
.js .mrow__panel:not([inert]) .tag:nth-child(7), .js .risk-card__panel:not([inert]) .tag:nth-child(7) { animation-delay: .24s; }

/* launch-score bar lights sequentially on reveal */
.js .stages.reveal .score__bar span.on { background: var(--color-neutral-300); transition: background .3s ease; }
.js .stages.reveal.in .score__bar span.on { background: var(--color-accent); }
.js .stages.reveal.in .score__bar span.on:nth-child(1) { transition-delay: .45s; }
.js .stages.reveal.in .score__bar span.on:nth-child(2) { transition-delay: .52s; }
.js .stages.reveal.in .score__bar span.on:nth-child(3) { transition-delay: .59s; }
.js .stages.reveal.in .score__bar span.on:nth-child(4) { transition-delay: .66s; }
.js .stages.reveal.in .score__bar span.on:nth-child(5) { transition-delay: .73s; }
.js .stages.reveal.in .score__bar span.on:nth-child(6) { transition-delay: .80s; }
.js .stages.reveal.in .score__bar span.on:nth-child(7) { transition-delay: .87s; }

/* ---------- 9 · failure-mode cycler ---------- */
.cycler {
  display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  max-width: clamp(1040px, 52vw, 1426px); margin-bottom: 32px; padding: 12px 0;
  border-top: 2px solid var(--color-text); border-bottom: 1px solid var(--color-divider);
}
.cycler__idx {
  flex: none; font-weight: 700; font-size: 13px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--color-accent);
}
.cycler__text { font-size: 15px; color: var(--ink-78); }
.js .cycler__idx, .js .cycler__text { transition: opacity .28s ease, transform .28s ease; }
.js .cycler.is-swap .cycler__idx, .js .cycler.is-swap .cycler__text { opacity: 0; transform: translateY(6px); }

/* ---------- 8 · Fig. 02 risk ledger ---------- */
.riskledger { margin-top: 26px; }
.riskledger__grid { display: grid; grid-template-columns: repeat(10, 12px); gap: 4px; width: max-content; }
.riskledger__grid i {
  width: 12px; height: 12px; background: var(--color-neutral-300);
  transition: background .25s ease, transform .25s ease;
}
.riskledger__grid i.on { background: var(--color-accent); }
.riskledger__grid i.hot { background: var(--color-accent-700); transform: scale(1.3); }
.riskledger__cap {
  margin-top: 10px; font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-55);
}
.riskledger__cap .tnum { color: var(--color-accent); font-weight: 700; }
.no-js .riskledger { display: none; }

/* ---------- 10 · caught-this-quarter ticker ---------- */
.caught {
  display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap;
  margin-top: 36px; padding-top: 14px; border-top: 2px solid var(--color-text);
}
.caught__label {
  flex: none; font-size: 11px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--color-accent-700);
}
.caught__line { font-size: 14px; color: var(--ink-70); }
.js .caught__line { transition: opacity .3s ease, transform .3s ease; }
.js .caught.is-swap .caught__line { opacity: 0; transform: translateY(6px); }

/* ---------- 7 · proof strip ---------- */
.proofgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); border-top: 2px solid var(--color-text); }
.proofcard {
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
  padding: 24px 24px 28px 0; background: var(--color-bg);
  transition: background .25s ease;
}
.hairgrid.proofgrid > .proofcard { padding-left: 24px; padding-right: 24px; }
.proofcard:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.proofcard__num { font-weight: 700; font-size: 13px; color: var(--color-accent); }
.proofcard__name { font-weight: 700; font-size: 24px; letter-spacing: -0.015em; }
.proofcard__meta {
  font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-55);
}
.proofcard__line { font-size: 14px; line-height: 22px; color: var(--ink-70); margin-top: 6px; }
.proofcard .tag { margin-top: 12px; }
@media (max-width: 880px) {
  .proofgrid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- 11 · ops-log specimen ---------- */
.opslog ul li { justify-content: flex-start; gap: 14px; }
.opslog .k { flex: none; color: var(--color-accent-700); font-weight: 700; }
.opslog .v { font-weight: 400; color: var(--ink-78); }
.js .opslog.is-live ul li { opacity: 0.35; transition: opacity .4s ease; }
.js .opslog.is-live ul li.lit { opacity: 1; }

/* ---------- 12 · poster marquee ---------- */
.postermarq { overflow: hidden; margin-bottom: 40px; }
.postermarq__track { display: flex; width: max-content; animation: marq 70s linear infinite; }
.postermarq__seq {
  flex: none; white-space: nowrap;
  font-weight: 700; font-size: clamp(56px, 8vw, 110px); line-height: 1.15;
  letter-spacing: -0.02em; text-transform: uppercase;
  color: transparent; -webkit-text-stroke: 1.5px color-mix(in srgb, var(--color-bg) 50%, transparent);
}
@supports not (-webkit-text-stroke: 1px black) {
  .postermarq__seq { color: color-mix(in srgb, var(--color-bg) 30%, transparent); }
}

/* ---------- 14 · section index rail ---------- */
.rail { display: none; }
@media (min-width: 1280px) {
  .rail {
    position: fixed; left: 22px; top: 50%; transform: translateY(-50%); z-index: 60;
    display: flex; flex-direction: column; gap: 14px;
  }
  .rail a {
    display: flex; align-items: center; gap: 8px;
    font-size: 11px; letter-spacing: 0.08em; color: var(--ink-45);
    font-feature-settings: 'tnum' 1; transition: color .2s ease;
  }
  .rail a::before {
    content: ""; width: 8px; height: 8px; background: var(--color-neutral-300);
    transition: background .2s ease, transform .2s ease;
  }
  .rail a:hover { color: var(--color-text); }
  .rail a.is-active { color: var(--color-accent); font-weight: 700; }
  .rail a.is-active::before { background: var(--color-accent); transform: scale(1.25); }
}

/* ---------- market-row country flags ---------- */
.mrow__flag {
  display: inline-block; width: 20px; height: 14px;
  margin-right: 10px; vertical-align: -1px;
  border: 1px solid color-mix(in srgb, var(--color-text) 18%, transparent);
}
.mrow__panel .hairgrid .domain .mrow__flag { width: 18px; height: 12px; margin-right: 8px; }

/* ---------- pricing cards — one accent identity, whole card clicks through ---------- */
.stage, .collab { position: relative; }
.stage { border: 2px solid var(--color-accent); transition: border-color .22s ease, box-shadow .22s ease; }
/* keep the reveal entrance, add the hover transitions on top of it */
.js .collab {
  transition: opacity .7s cubic-bezier(0.22, 1, 0.36, 1), transform .7s cubic-bezier(0.22, 1, 0.36, 1),
              border-color .22s ease, box-shadow .22s ease;
}

/* every pricing CTA opens WhatsApp, so all four carry the WhatsApp green */
.stage .btn, .collab .btn-collab { background: #1c9e50; color: #fff; }
.stage .btn:hover, .collab .btn-collab:hover { background: #178443; color: #fff; }
.stage .btn:active, .collab .btn-collab:active { background: #126937; }
/* the green sweep stays with the WhatsApp buttons elsewhere; static positioning
   lets the stretched ::after below anchor to the card instead of the button */
.stage .btn, .collab .btn-collab { position: static; overflow: visible; }
.stage .btn::before, .collab .btn-collab::before { display: none; }

.stage .btn::after, .collab .btn-collab::after { content: ""; position: absolute; inset: 0; z-index: 1; }

.stage:hover, .stage:focus-within,
.collab:hover, .collab:focus-within {
  border-color: var(--color-accent-600);
  box-shadow: 0 0 0 4px var(--color-accent-200), var(--shadow-lg);
}

/* =========================================================================
   PORTFOLIO — BRAND BLOCKS, DRIFTING CAROUSEL, VIDEO FACADES, LIGHTBOX
   Same hairline language as .cases: 2px ink rules, 1px divider hairlines,
   zero radius. Each brand reads header row → brief case study → carousel →
   films → the full written case study behind a disclosure.

   The carousel is a continuous marquee, the same primitive as .postermarq
   further up this file: a flex track duplicated end to end and translated
   -50%, so the loop is seamless. Slides share one HEIGHT and keep their
   natural widths, which means a source set mixing squares, 2.3:1 browser
   screenshots and portraits stays uncropped and still reads as an even
   filmstrip. Because the translation is a percentage of the track, halving
   the slide height at a breakpoint also halves the distance travelled — the
   speed relative to the slides stays identical without retuning anything.

   Behaviour: assets/js/portfolio.js (lightbox + pause + YouTube facade).
   The case-study disclosure is wired by the generic handler in site.js.
   ========================================================================= */

.brands { border-top: 2px solid var(--color-text); margin-top: 14px; margin-bottom: 72px; }
/* Every brand after the first is pushed clear of the rule above it by its
   predecessor's 56px bottom margin. The first has only .brands' own border
   above it and no padding, so its number sat 16px under the rule against the
   other three's 72px — the same 56px, applied where the margin cannot reach.
   Matches .brand's own bottom spacing at both sizes, so the two stay in step. */
.brands > .brand:first-child { padding-top: 56px; }
.brand { padding-bottom: 56px; margin-bottom: 56px; border-bottom: 2px solid var(--color-divider); }
.brand:last-child { padding-bottom: 0; margin-bottom: 0; border-bottom: 0; }

/* ---------- brand header ----------
   An oversized accent numeral against a large name, with the category and
   market demoted to one quiet line underneath. Four identical rows of
   same-size text gave every brand the same weight and no arrival; this gives
   each block a masthead, and reads as the same family as .hero__stats, where
   the big tabular accent figure already does this job. */
.brand__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: clamp(20px, 2.4vw, 34px);
  align-items: baseline;
  padding-bottom: 20px; margin-bottom: 34px;
  border-bottom: 2px solid var(--color-text);
}
.brand__num {
  font-weight: 700; font-size: clamp(64px, 3.2vw, 88px); line-height: 0.78;
  letter-spacing: -0.045em; margin-left: -0.05em;
  color: var(--color-accent); font-feature-settings: 'tnum' 1;
}
.brand__name {
  font-weight: 700; font-size: clamp(40px, 2vw, 55px); line-height: 1;
  letter-spacing: -0.028em; margin-left: -0.04em;
}
.brand__meta {
  margin-top: 12px;
  font-size: 12px; line-height: 18px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-55);
}
.brand__meta .sep { color: var(--color-accent); padding: 0 2px; }
.brand__status { justify-self: end; }

/* ---------- brief case study: the section header ---------- */
/* 68ch, not 82: a lede is read in one pass and wants a shorter measure */
.brand__brief { max-width: 68ch; margin-bottom: 38px; }
/* Balance the ragged edge rather than hand-tuning each string: Hoasipho's lede
   was breaking to leave "art." alone on the second line, and the same sentence
   is a different length in all three locales, so a hard space in the English
   copy would only fix one of them. Browsers without text-wrap: balance keep
   the previous greedy break — no worse than before. */
.brand__brief .lede {
  font-weight: 700; font-size: 23px; line-height: 33px; letter-spacing: -0.016em;
  margin-left: -0.02em;
  text-wrap: balance;
}
.brand__brief .lede + p { margin-top: 16px; font-size: 15.5px; line-height: 27px; color: var(--ink-70); }

/* ---------- drifting carousel ---------- */
.marq {
  --marq-h: 250px;
  border-top: 2px solid var(--color-text);
  border-bottom: 1px solid var(--color-divider);
}
.marq__head {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 11px 0;
}
.marq__label {
  font-size: 12px; line-height: 18px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-55);
}
.marq__label .n { color: var(--color-accent-700); font-feature-settings: 'tnum' 1; }

/* Pause control. Motion that starts on its own and runs past a few seconds
   has to be stoppable, so this is a real control, not decoration. */
.marq__pause {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  padding: 6px 10px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-60); border: 1px solid var(--color-divider);
  transition: color .2s ease, border-color .2s ease;
}
.marq__pause:hover { color: var(--color-accent); border-color: var(--color-accent); }
.marq__pause svg { display: block; fill: currentColor; stroke: none; }
/* The label carries the state, so there is no aria-pressed to also announce
   it — a toggle that does both says it twice. */
.marq__pause .ico-play,
.marq__pause .lbl-play { display: none; }
.marq__pause[data-state="paused"] .ico-pause,
.marq__pause[data-state="paused"] .lbl-pause { display: none; }
.marq__pause[data-state="paused"] .ico-play { display: block; }
.marq__pause[data-state="paused"] .lbl-play { display: inline; }

/* A real scroll container, not a CSS transform. The drift is JS nudging
   scrollLeft, which means dragging, the arrows, a trackpad swipe and native
   touch scrolling all drive the same one position — a transform animation
   cannot be grabbed. The strip holds three identical blocks and the script
   keeps the scroll position inside the middle one, so it wraps invisibly in
   both directions and there is no start or end to hit. */
.marq__viewport {
  overflow-x: auto; overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-behavior: auto;          /* the arrows opt into smooth per call */
  scrollbar-width: none;          /* the strip is its own affordance */
  -ms-overflow-style: none;
  cursor: grab;
}
.marq__viewport::-webkit-scrollbar { display: none; }
.marq__viewport.is-dragging { cursor: grabbing; user-select: none; }
.marq__track { display: flex; width: max-content; }

.marq__ctrls { display: flex; align-items: center; gap: 8px; flex: none; }
.marq__nav {
  display: grid; place-items: center; width: 34px; height: 30px;
  border: 1px solid var(--color-divider); color: var(--ink-60);
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.marq__nav:hover { color: var(--color-bg); background: var(--color-accent); border-color: var(--color-accent); }
.marq__nav svg { display: block; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }

.marq__set { display: flex; margin: 0; padding: 0; list-style: none; }
.marq__slide { flex: none; }

.shot {
  position: relative; display: block; overflow: hidden;
  height: var(--marq-h);
  background: var(--color-surface);
  box-shadow: 1px 0 0 var(--color-divider);   /* the hairline between slides */
}
.shot img {
  display: block; height: 100%; width: auto;
  transition: transform .5s cubic-bezier(0.22, 1, 0.36, 1);
}
/* accent corner mark — drawn, not written, so it stays out of the a11y tree */
.shot::after {
  content: ""; position: absolute; right: 0; bottom: 0; z-index: 2;
  width: 30px; height: 30px;
  background-color: var(--color-accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cg fill='%23f3f2f2'%3E%3Crect x='14' y='8' width='2' height='14'/%3E%3Crect x='8' y='14' width='14' height='2'/%3E%3C/g%3E%3C/svg%3E");
  transform: translateY(100%);
  transition: transform .22s cubic-bezier(0.33, 1, 0.68, 1);
}
.shot:hover::after, .shot:focus-visible::after { transform: none; }
.shot:focus-visible { outline: 3px solid var(--color-accent); outline-offset: -3px; }
@media (hover: hover) and (pointer: fine) {
  .shot:hover img { transform: scale(1.05); }
}
/* Without JS there is no drift and no wrapping, so drop the repeat blocks and
   the controls that need script. The viewport is a scroll container in plain
   CSS, so the strip is still fully browsable — and the scrollbar comes back,
   since nothing else would signal that it scrolls. */
.no-js .marq__viewport { scrollbar-width: auto; -ms-overflow-style: auto; cursor: auto; }
.no-js .marq__viewport::-webkit-scrollbar { display: block; }
.no-js .marq__set[aria-hidden="true"] { display: none; }
.no-js .marq__ctrls { display: none; }

/* ---------- YouTube click-to-load facade ----------
   Nothing third-party is requested until the visitor presses play. */
.ytrow { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 40px; border-top: 2px solid var(--color-text); }
/* A brand with a single film takes the full width. Half a row plus an empty
   cell would let the hairgrid's divider ground show through as a solid slab. */
.ytrow--one { grid-template-columns: minmax(0, 1fr); }
.ytcell { padding: 22px; }
.ytcell__label {
  font-size: 12px; line-height: 18px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-55); margin-bottom: 12px;
}
.ytfacade { position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--color-neutral-900); }
.ytfacade__btn { position: absolute; inset: 0; display: block; }
.ytfacade__poster {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .5s cubic-bezier(0.22, 1, 0.36, 1), opacity .3s ease;
}
.ytfacade__play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 76px; height: 54px; background: var(--color-accent);
  display: grid; place-items: center;
  transition: background .22s ease, transform .22s cubic-bezier(0.33, 1, 0.68, 1);
}
.ytfacade__play::before {
  content: ""; width: 0; height: 0; margin-left: 4px;
  border-left: 18px solid var(--color-bg);
  border-top: 11px solid transparent; border-bottom: 11px solid transparent;
}
.ytfacade__btn:hover .ytfacade__poster { transform: scale(1.04); opacity: .8; }
.ytfacade__btn:hover .ytfacade__play { background: var(--color-accent-600); transform: translate(-50%, -50%) scale(1.08); }
.ytfacade__btn:focus-visible { outline: 3px solid var(--color-accent); outline-offset: -3px; }
.ytfacade iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* data-yt still holds a placeholder token: render as unavailable rather than
   loading a broken embed */
.ytfacade.is-pending .ytfacade__play { background: var(--color-neutral-500); }
.ytfacade.is-pending .ytfacade__btn { cursor: default; }
.ytfacade.is-pending .ytfacade__poster { opacity: .45; }
.ytcell__note { display: none; }
.ytcell:has(.ytfacade.is-pending) .ytcell__note {
  display: block; margin-top: 10px;
  font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-55);
}

/* ---------- the full written case study, behind a disclosure ---------- */
.brand__study { margin-top: 40px; border-top: 2px solid var(--color-text); }
.brand__read {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 20px;
  padding: 18px 20px 18px 0; text-align: left;
  font-weight: 700; font-size: 18px; letter-spacing: -0.01em;
  border-bottom: 1px solid var(--color-divider);
  transition: color .2s ease, background .2s ease, padding-left .2s ease;
}
/* the row itself reads as the control: it tints and steps in, so the whole
   width is visibly the hit area rather than just the words */
.brand__read:hover { color: var(--color-accent); background: var(--color-accent-100); padding-left: 16px; }
.brand__read .chev { width: 18px; height: 18px; flex: none; transition: transform .22s cubic-bezier(0.33, 1, 0.68, 1); }
.brand__read:hover .chev { transform: translateY(2px); }
.brand__read[aria-expanded="true"]:hover .chev { transform: rotate(180deg) translateY(2px); }
@media (prefers-reduced-motion: reduce) {
  .brand__read:hover { padding-left: 0; }
  .brand__read:hover .chev { transform: none; }
  .brand__read[aria-expanded="true"]:hover .chev { transform: rotate(180deg); }
}

/* ---------- lightbox ---------- */
.lb {
  position: fixed; inset: 0; z-index: 210; display: none;
  background: color-mix(in srgb, var(--color-neutral-900) 94%, transparent);
  color: var(--color-bg);
}
.lb.is-open { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
.lb svg { fill: none; stroke: currentColor; stroke-width: 2; }
.lb :focus-visible { outline: 2px solid var(--color-bg); outline-offset: 2px; }

.lb__top {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px clamp(16px, 4vw, 32px);
  border-bottom: 1px solid color-mix(in srgb, #f3f2f2 26%, transparent);
}
.lb__counter { font-size: 13px; letter-spacing: 0.1em; font-feature-settings: 'tnum' 1; }
.lb__close {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid color-mix(in srgb, #f3f2f2 40%, transparent);
  transition: background .2s ease, border-color .2s ease;
}
.lb__close:hover { background: var(--color-accent); border-color: var(--color-accent); }

/* The stage is inset with MARGIN, not padding, so the image can be an
   absolutely positioned inset:0 box inside it. That matters: a percentage
   max-height on a grid item whose height comes from `1fr` does not resolve,
   so the old `max-height: 100%` was silently ignored and tall pictures
   rendered at full size and overflowed. Against a positioned ancestor the
   percentage does resolve, so the box is always exactly the stage.
   `scale-down` then fits the picture inside it and — unlike `contain` —
   never enlarges a small source past its own pixels. */
.lb__stage { position: relative; min-height: 0; margin: clamp(12px, 3vw, 28px); }
.lb__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: scale-down; object-position: center;
  transition: opacity .18s ease;
  pointer-events: none;   /* click the letterboxed area to dismiss */
}
.lb__stage.is-loading .lb__img { opacity: 0; }

.lb__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 52px; height: 84px; display: grid; place-items: center;
  background: color-mix(in srgb, #201e1d 55%, transparent);
  transition: background .2s ease;
}
.lb__nav:hover { background: var(--color-accent); }
.lb__nav svg { width: 20px; height: 20px; }
.lb__prev { left: 0; }
.lb__next { right: 0; }
.lb__nav[hidden] { display: none; }

.lb__foot {
  padding: 12px clamp(16px, 4vw, 32px) 18px;
  border-top: 1px solid color-mix(in srgb, #f3f2f2 26%, transparent);
  font-size: 13px; line-height: 20px;
  color: color-mix(in srgb, #f3f2f2 74%, transparent);
  min-height: 50px;
}

/* ---------- homepage proof strip: four brands, not three ----------
   Overrides the 3-column rule earlier in this file. The 880 rule has to be
   restated here: media queries carry no specificity, so the appended 1080
   rule would otherwise win over the earlier 880 one. */
.proofgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.hairgrid.proofgrid > .proofcard { padding-left: 20px; padding-right: 20px; }
@media (max-width: 1080px) { .proofgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 880px)  { .proofgrid { grid-template-columns: minmax(0, 1fr); } }

/* ---------- portfolio responsive (the site's existing breakpoints) ---------- */
/* the strip earns more height once there is room for it */
@media (min-width: 1280px) {
  /* 15vw is 300/20, the same ratio as the measure, so the carousel block stays a
     constant 1.64x the viewport it scrolls in rather than shrinking as the wrap
     grows — that ratio is what portfolio.js needs above 1.0 for its three-block
     wrap. 411px is 300 x 1.371 and stays under the 500px source height of every
     staged slide, so nothing upscales. */
  .marq { --marq-h: clamp(300px, 15vw, 411px); }
}

@media (max-width: 1080px) {
  .brand__num { font-size: 54px; }
  .brand__name { font-size: 34px; }
}

@media (max-width: 880px) {
  /* the numeral keeps its own column and the tag drops below the name, so the
     masthead survives instead of collapsing into four stacked lines */
  .brand__row { grid-template-columns: auto minmax(0, 1fr); row-gap: 14px; }
  .brand__num { font-size: 44px; }
  .brand__name { font-size: 28px; }
  .brand__status { grid-column: 2; justify-self: start; }
  .marq { --marq-h: 200px; }
  .ytrow { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 720px) {
  .brands { margin-bottom: 48px; }
  .brand { padding-bottom: 38px; margin-bottom: 38px; }
  .brands > .brand:first-child { padding-top: 38px; }
  .brand__num { font-size: 36px; }
  .brand__name { font-size: 24px; }
  .brand__meta { margin-top: 9px; letter-spacing: 0.07em; }
  .brand__brief .lede { font-size: 19px; line-height: 28px; }
  .marq { --marq-h: 170px; }
  .ytcell { padding: 16px; }
  .ytfacade__play { width: 60px; height: 42px; }
  .ytfacade__play::before { border-left-width: 14px; border-top-width: 9px; border-bottom-width: 9px; }
  .brand__read { font-size: 16px; }
  .lb__nav { width: 40px; height: 68px; }
  .lb__close { padding: 8px 10px; }
}

@media (max-width: 380px) {
  .marq { --marq-h: 140px; }
  /* Only below ~383px does wrapping the carousel header pay: the label sheds
     5+ lines there, whereas a second row costs a fixed ~56px, which is a net
     LOSS at 390 and 430 — the widths iPhone portrait actually lives at. */
  .marq__head { flex-wrap: wrap; }
  .marq__label { flex: 1 1 100%; }
  .marq__ctrls { margin-left: auto; }
  .shot::after { width: 24px; height: 24px; background-size: 24px 24px; }
  .marq__pause { padding: 5px 8px; }
}

/* The global reduce switch at the top of this file already kills the marquee
   animation with `animation: none !important`, which would leave the strip
   frozen and most of it unreachable. Turn it into a filmstrip the reader
   scrolls themselves, and neutralise the :hover transforms the switch leaves
   behind (they would otherwise snap instantly instead of easing). */
@media (prefers-reduced-motion: reduce) {
  /* The strip stops drifting on its own (portfolio.js checks the same query)
     but stays fully browsable by drag, arrows and swipe, so the repeat blocks
     and the arrows all stay. Only the pause control goes: there is no longer
     any automatic motion for it to stop. */
  .marq__pause { display: none; }
  .marq__viewport { scrollbar-width: thin; }
  .marq__viewport::-webkit-scrollbar { display: block; height: 8px; }
  .shot::after { transform: none; }
  .shot:hover img { transform: none; }
  .ytfacade__btn:hover .ytfacade__poster { transform: none; opacity: 1; }
  .ytfacade__btn:hover .ytfacade__play { transform: translate(-50%, -50%); }
  .lb__stage.is-loading .lb__img { opacity: 1; }
}

/* =========================================================================
   TOUCH AND SMALL-VIEWPORT PLATFORM FIXES
   Appended deliberately: media queries carry no specificity, so a block after
   the 720 / 380 rules above is the only way to beat them on source order.

   On safe areas: these pages ship `width=device-width, initial-scale=1` with
   NO `viewport-fit=cover`, so iOS keeps the layout viewport inside the
   safe-area rectangle and every env(safe-area-inset-*) resolves to 0px.
   Nothing here can sit under the Dynamic Island or the home indicator, and
   adding env() padding without also adding viewport-fit=cover would be a
   no-op. Do not add one without the other.
   ========================================================================= */

/* Key off the pointer, not the width. Every iPhone in LANDSCAPE is wider than
   the 720 breakpoint (844 / 852 / 932), so the 16px form-field override in the
   audit block never fires there — and iOS Safari zooms the page in on focus
   for any field under 16px, and never zooms back out. */
@media (pointer: coarse) {
  /* The corner mark is the only cue that a slide opens larger, and it sits
     parked off-frame until :hover, which a touch reader never has. This is the
     same end state the reduced-motion block above already ships. */
  .shot::after { transform: none; }

  /* A slow swipe across the filmstrip reads as a long press on an <a><img>,
     raising iOS's Save Image / Open in New Tab sheet mid-gesture. */
  .shot { -webkit-touch-callout: none; }

  /* Tap targets up to the 44x44pt minimum. The drawn boxes stay exactly as
     drawn — only the hit area grows, via a transparent absolutely positioned
     pseudo element, which does not become a grid item on the grid buttons. */
  .marq__nav { position: relative; }
  .marq__nav::before { content: ""; position: absolute; inset: -8px -6px; }
  .marq__ctrls { gap: 12px; }              /* 6+6, so prev/next never overlap */
  /* min-height only, never padding: the drawn box stays as drawn, the
     deliberate `padding: 5px 8px` at <=380 survives, and .marq__ctrls does not
     grow by 8px on the narrowest screens. */
  .marq__pause { min-height: 44px; }

  .burger::before { content: ""; position: absolute; inset: -6px; }
  .nav-list a { position: relative; }
  /* the inline anchor's padding box measures 25.33px, so -9px reaches 43.33 */
  .nav-list a::after { content: ""; position: absolute; inset: -10px 0; }

  /* .lang cells sit flush, separated only by a 1px border, so an expanded hit
     area would overlap onto the wrong language. This one has to grow for real. */
  .lang a, .lang .lang__current { padding: 14px 16px; }

  .footer__col li { margin-bottom: 0; }
  .footer__col li a,
  .footer__col li span { display: block; padding: 9px 0; }
}

/* `inset: 0` sizes a fixed overlay to the initial containing block, which on
   iOS Safari is the TALL viewport with the toolbars retracted — the same
   reason 100vh overflows there. svh is the smallest possible viewport, so the
   lightbox is fully visible whatever the toolbars are doing, and it equals vh
   on any browser without retractable chrome. Guarded, because without svh
   support `inset: 0 0 auto 0` alone would collapse the overlay to zero height. */
/* dvh rather than svh: a reader reaches a carousel by scrolling, i.e. with the
   toolbars already retracted, and portfolio.js locks the scroll while the
   lightbox is open, which freezes whichever state they are in — removing dvh's
   only usual cost, mid-gesture jitter. `inset: 0` deliberately stays, so a
   browser that drops the height declaration renders exactly today's overlay
   rather than one collapsed to zero height. */
.lb { height: 100dvh; }

/* .lb__nav is 40px wide in portrait, under the 44pt minimum. */
@media (max-width: 720px) {
  .lb__nav { width: 44px; height: 68px; }
}

/* Short and wide is every iPhone in landscape — portrait is 568 tall at the
   shortest, so this cannot fire there. One breakpoint, three jobs. */
@media (max-height: 480px) {
  /* 37% of the viewport was lightbox chrome and the nav arrows covered 43% of
     the image, because the reductions above are keyed to max-width:720 while
     landscape iPhones are 844-932 wide. */
  .lb__top { padding-block: 8px; }
  .lb__stage { margin: 8px clamp(12px, 3vw, 28px); }
  .lb__foot {
    padding: 8px clamp(16px, 4vw, 32px) 10px;
    min-height: 0; font-size: 12px; line-height: 18px;
  }
  .lb__nav { width: 44px; height: 60px; }

  /* .drawer__list keeps its 76px padding-top: .header is z-index 100 and
     .drawer is 95, so the sticky header paints OVER the drawer and that
     padding is clearing it, not stale arithmetic. Reducing it puts the first
     link behind the header and hit-testing at its centre returns the header. */
  .drawer__list a { padding: 14px 20px; }

  /* A fixed 78px bar is the wrong trade against a ~390px-tall viewport. */
  .mobilebar { display: none; }
  body.has-mobilebar { padding-bottom: 0; }
}

/* Two breakpoints disagreed and left a dead zone: the header CTA is hidden at
   <=880 but the mobile bar only appears at <=720, so between 721 and 880 — and
   on every short landscape phone, where the bar is now suppressed — there was
   NO call to action anywhere on the page. Restore it at the compact size. */
@media (min-width: 721px) and (max-width: 880px) {
  .header .btn-cta { display: inline-flex; font-size: 13px; padding: 7px 14px; }
}
@media (max-height: 480px) and (max-width: 880px) {
  .header .btn-cta { display: inline-flex; font-size: 13px; padding: 7px 14px; }
}

/* The deliberate 15px desktop field size, gated on a real mouse rather than
   gating the SAFE 16px on touch. min-width:721 leaves the existing mobile
   form rule exactly as it is. */
@media (hover: hover) and (pointer: fine) and (min-width: 721px) {
  .input { font-size: 15px; }
}

/* =========================================================================
   LARGE-SCREEN COMPANIONS
   The measure grows above a 2000px viewport (see --measure in :root). These are
   the elements that need a cap or a guard once it does — without them, widening
   the column trades a too-narrow layout for too-long prose, which is worse.
   Every rule here is scoped so that nothing at or below 2000px changes.
   ========================================================================= */

/* The hero globe is aspect-ratio 1920/1080 inside .wrap, so a wider measure
   makes it TALLER — at a 1700px measure it is 956px, taller than a laptop
   viewport. Cap its WIDTH so the 16:9 box still fits the height, rather than
   capping height: the canvas is width/height 100% and fig01-globe.js sizes its
   backing store from clientWidth alone, so a height cap would squash the globe
   instead of letterboxing it. */
.globe-fig { max-width: calc(78vh * 16 / 9); margin-inline: auto; }

@media (min-width: 1081px) {
  /* 62ch at 14px. Uncapped this reaches 93ch at a 1700px measure. Scoped to
     where .risk__grid is still two-up, so the single-column card is untouched. */
  .risk-card__preview { max-width: 62ch; }

  /* The audit page's lead column is prose and reaches 96-108ch uncapped. Cap
     the PROSE only — not the ruled list or the surface panel, whose 2px rules
     and opaque background are load-bearing in the hairline system. */
  .audit__lede { max-width: 62ch; }
  .audit__steps .d,
  .audit__steps .t { max-width: 64ch; }
  .audit__talk p { max-width: 55ch; }
}

/* No CSS fix belongs here for the lightbox at 4K. The stage is transparent, so
   capping it changes nothing a viewer sees — the image is centred in the black
   overlay at the same painted size either way. The cause was simply that the
   sources were exported at a 1600px long edge, too small to fill a 4K panel,
   and object-fit: scale-down (correctly) refuses to enlarge them. Fixed by
   re-exporting the gallery images larger; see fetch/stage scripts. */

@media (min-width: 2001px) {
  /* An 881px single-line text input is indefensible. */
  .audit__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 660px); }

  /* The rail indexes the content, so it should sit beside it rather than stay
     pinned to a viewport edge that is now 1200px away. Above 2000px --measure
     equals the real wrap width, so the token is safe to reason from here. */
  .rail { left: calc(50% - var(--measure) / 2 - 72px); }

  /* The section sheets stop gaining inset at 1925px while the sheet keeps
     growing, so the type creeps toward the edge. */
  #problem > .wrap, #markets > .wrap, #risk > .wrap, #services > .wrap,
  #proof > .wrap, #how > .wrap, #pricing > .wrap, #faq > .wrap {
    padding: 48px clamp(20px, 4vw, 77px) 56px;
  }
}

/* =========================================================================
   BLOG — INDEX LIST AND ARTICLE
   Same Modernist language: zero radius, 2px ink rules, 1px hairlines, tokens
   only. The article column is set to a reading measure rather than the full
   .wrap, because these are the only pages on the site that are long-form prose.
   ========================================================================= */

/* ---------- index ---------- */
.blog-hero { padding: 72px 0 40px; }
.blog-hero h1 {
  font-size: clamp(52px, 2.6vw, 71px); line-height: 1.06;
  letter-spacing: -0.02em; margin-left: -0.058em; margin-top: 14px;
}
.blog-hero__sub { font-size: 17px; line-height: 28px; color: var(--ink-70); margin-top: 18px; max-width: 68ch; }

.postlist { border-top: 2px solid var(--color-text); margin-bottom: 72px; }
.post-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 32px; align-items: baseline;
  padding: 26px 0; border-bottom: 1px solid var(--color-divider);
  transition: background .2s ease, padding-left .2s ease;
}
.post-row:hover { background: var(--color-accent-100); padding-left: 16px; }
.post-row__title {
  grid-column: 1; font-weight: 700; font-size: 24px; line-height: 1.15;
  letter-spacing: -0.018em; margin: 0;
}
.post-row:hover .post-row__title { color: var(--color-accent); }
.post-row__sub { grid-column: 1; font-size: 15px; line-height: 25px; color: var(--ink-70); max-width: 74ch; }
.post-row__meta {
  grid-column: 2; grid-row: 1;
  font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-55); white-space: nowrap;
}

/* ---------- article ---------- */
.article { padding: 64px 0 0; }
.article__head { max-width: 68ch; }
.article__head h1 {
  font-size: clamp(40px, 2vw, 55px); line-height: 1.08;
  letter-spacing: -0.022em; margin-left: -0.04em; margin-top: 14px;
}
.article__standfirst { font-size: 19px; line-height: 31px; color: var(--ink-78); margin-top: 20px; }
.article__meta {
  display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: baseline;
  margin-top: 26px; padding-top: 16px; border-top: 2px solid var(--color-divider);
  font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55);
}

/* 68ch is the measure the whole article body is set to — the one number that
   decides whether long-form on this site is readable. */
.prose { max-width: 68ch; margin-top: 44px; }
.prose h2 {
  font-size: clamp(26px, 1.3vw, 34px); line-height: 1.18; letter-spacing: -0.018em;
  margin: 52px 0 0; padding-top: 22px; border-top: 2px solid var(--color-divider);
}
.prose h2:first-child { margin-top: 0; }
.prose p { font-size: 17px; line-height: 30px; color: var(--ink-82); margin: 18px 0 0; }
.prose ul { margin: 18px 0 0; padding-left: 0; list-style: none; }
.prose li {
  position: relative; padding-left: 26px; margin-top: 10px;
  font-size: 16px; line-height: 27px; color: var(--ink-78);
}
.prose li::before {
  content: ""; position: absolute; left: 0; top: 11px;
  width: 9px; height: 9px; background: var(--color-accent);
}
.prose a { color: var(--color-accent-700); text-decoration: underline; text-underline-offset: 3px; }
.prose a:hover { color: var(--color-accent); }
.prose strong { font-weight: 700; color: var(--color-text); }

.article__faq { max-width: 68ch; margin-top: 64px; border-top: 2px solid var(--color-text); }
.article__faq h2 { font-size: 26px; letter-spacing: -0.018em; margin: 22px 0 0; }
.article__faq dt { font-weight: 700; font-size: 17px; line-height: 27px; margin-top: 28px; }
.article__faq dd { margin: 8px 0 0; font-size: 16px; line-height: 28px; color: var(--ink-78); }

.article__foot { max-width: 68ch; margin: 56px 0 0; padding-top: 20px; border-top: 2px solid var(--color-divider); }
.article__cta { font-weight: 700; font-size: 19px; line-height: 29px; letter-spacing: -0.01em; }
.article__disclaimer { margin-top: 20px; font-size: 13px; line-height: 21px; color: var(--ink-55); }

.readnext { margin-top: 64px; border-top: 2px solid var(--color-text); }
.readnext__label { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-55); padding: 14px 0; }
.readnext__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.readnext a { display: block; padding: 22px; transition: background .2s ease; }
.readnext a:hover { background: var(--color-accent-100); }
.readnext a:hover .readnext__t { color: var(--color-accent); }
.readnext__t { display: block; font-weight: 700; font-size: 19px; line-height: 1.25; letter-spacing: -0.015em; }
.readnext__d { display: block; margin-top: 8px; font-size: 14px; line-height: 23px; color: var(--ink-65); }

@media (max-width: 880px) {
  .post-row { grid-template-columns: minmax(0, 1fr); }
  .post-row__meta { grid-column: 1; grid-row: auto; }
  .readnext__grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
  .blog-hero { padding: 36px 0 28px; }
  .article { padding: 36px 0 0; }
  .post-row__title { font-size: 20px; }
  .prose p { font-size: 16px; line-height: 28px; }
  .prose h2 { margin-top: 40px; }
}
