/*
 * Nevado AI — design system
 * ==========================
 * Source of truth: the designer's Figma preview (tmp/nevado-preview.html),
 * itself measured from the Figma "Desktop_Homepage" frame at 1440.
 *
 * Sections below marked "extends the preview" are components the preview does
 * not specify (it is homepage-only) but which the interior pages need. They are
 * built strictly from the preview's own tokens and primitives so the interior
 * pages read as the same system.
 *
 * Nothing here hard-codes a color or a size: everything reads from the tokens
 * in :root, and light surfaces flip the semantic set via .theme-* classes.
 */

:root {
  /* ---- Color: ground & surfaces ---- */
  --n-black: #181818;
  --n-black-2: #1e1e1e;
  --n-gray-900: #1c1c1c;
  --n-gray-800: #373737;
  --n-gray-700: #525252;
  --n-gray-600: #555555;
  --n-gray-650: #404040;
  --n-gray-400: #a3a3a3;
  --n-gray-200: #d9d9d9;
  --n-sage: #ccdbd5;
  --n-white: #ffffff;

  /* ---- Color: brand ---- */
  --n-mint: #00ffb2;          /* the single accent */
  --n-mint-soft: #00ffcc;     /* glow gradient mid-stop */
  --n-green-700: #007242;
  --n-green-800: #008059;
  --n-green-900: #004d2e;
  --n-green-950: #00501b;
  --n-danger: #ff6b6b;        /* form validation only; not a brand color */

  /* ---- Semantic: default (dark) theme. Sections override these. ---- */
  --bg: var(--n-black);
  --fg: var(--n-white);
  --fg-body: rgb(255 255 255 / 0.8);     /* paragraphs: text color at 80% */
  --fg-muted: var(--n-gray-400);
  --fg-faint: rgb(255 255 255 / 0.5);    /* footer labels */
  --line: var(--n-gray-800);
  --line-strong: var(--n-gray-600);
  --accent: var(--n-mint);
  --bracket: var(--n-gray-400);          /* default bracket stroke */
  --surface: rgb(24 24 24 / 0.03);       /* cards: black at 3% */
  --img-border: rgb(0 0 0 / 0.2);

  /* ---- Typography ---- */
  --font-sans: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  /* Calluna (Adobe Fonts) is the production italic. Source Serif 4 stands in
     until the client's Typekit kit is wired up — swap this one line. */
  --font-serif: "Calluna", "Source Serif 4", Georgia, serif;

  /* sizes / line pitches, anchored to the 1440 Figma frame.

     Reading roles grow on large screens: the layout is already fluid (--frame,
     --gutter, --col-gap are percentages of --vwc), so static type would drift out
     of ratio with its own frame above 1440.

     Pattern: max(<floor in rem>, <rem offset> + var(--vwc) * <slope>)
       · the rem floor holds the ≤1440 value, so the Figma frame is untouched and
         user text-size / zoom still scales the type (WCAG 1.4.4)
       · --vwc caps at 1920px, so growth stops where the rails stop
       · slope = (target@1920 − target@1440) / 480

     Keep these in :root, not at the end of the file — the max-width: 767px block
     below must still override --fs-display / --fs-lead, and media queries add no
     specificity, so a later :root would ship 76px display on mobile. */
  --fs-display: max(4.75rem,   3.25rem   + var(--vwc) * 0.0166667);  /* 76→84px, 61px mobile */
  --lh-display: 0.85;
  --fs-h2: max(2.4375rem, 1.6875rem + var(--vwc) * 0.0083333);       /* 39→43px, keeps the 2.4× ratio to body */
  --lh-h2: 1;
  --fw-h2: 500;
  --fs-h3: max(1.25rem,    0.875rem  + var(--vwc) * 0.0041667);      /* 20→22px, step + card titles stay above body */
  --lh-h3: 1;
  /* Scales in lockstep with --fs-body. It is not in the handoff's table, but it
     has to move: .feature titles are <h3 class="h4"> above a <p class="body">,
     and holding h4 at a flat 16px while body reaches 18px inverts the
     hierarchy — the title would render smaller than its own copy. */
  --fs-h4: max(1rem,       0.625rem  + var(--vwc) * 0.0041667);      /* 16→18px feature titles */
  --lh-h4: normal;
  --fs-lead: max(1.5625rem, 0.8125rem + var(--vwc) * 0.0083333);     /* 25→29px, 20px mobile */
  --lh-lead: 1;                                 /* single-line uses; wrapping leads take --lh-lead-multi */
  --fs-body: max(1rem,     0.625rem  + var(--vwc) * 0.0041667);      /* 16→18px — the headline fix */
  --lh-body: 1;                                 /* single-line uses; wrapping copy takes --lh-body-multi */
  --fs-small: max(0.875rem, 0.5rem   + var(--vwc) * 0.0041667);      /* 14→16px, carries wrapping copy too */
  --lh-small: 1.143;
  --fs-eyebrow: 0.8125rem;                      /* 13px caps, 0 tracking — a label, steps once at 1920 */
  --lh-eyebrow: 1;
  --fs-nav: 1rem;
  --fs-btn: 1rem;
  --fs-cta-p: max(1.25rem,  0.875rem + var(--vwc) * 0.0041667);      /* 20→22px closing CTA paragraph */

  /* Reading leading. --lh-body / --lh-lead stay at 1 because footer lists, nav,
     .stat__num and tabular labels all depend on solid leading; multi-line
     paragraphs opt into these instead (see the end of this file). */
  --lh-body-multi: 1.45;
  --lh-lead-multi: 1.2;

  --ls-display: -0.06em;
  --ls-lead: -0.06em;
  --ls-btn: -0.06em;
  --ls-h2: -0.06em;
  --ls-h3: -0.06em;
  --ls-body: -0.06em;
  /* −6% is a display setting. It is correct for headlines and single-line
     labels, but on multi-line reading copy it closes up the word spacing and is
     as much to blame for the "too small" feedback as the 16px was. Wrapping
     paragraphs ease off to −2%. */
  --ls-reading: -0.02em;
  --ls-eyebrow: 0;

  /* ---- Layout: one centred stage.

     The 1px rules ("rails") mark the edge of a centred stage rather than sitting
     a fixed distance from the viewport. Up to 1920 the stage is the proportional
     frame (40px @1440, exactly Figma's margin); past that it locks at
     --stage-max and the surplus becomes even outer margin. The two definitions
     meet continuously at 1920 (1920 − 2 × 53.5 ≈ 1813), so there's no jump.

     Section *backgrounds* still bleed full width — only the rules and the content
     inset respect the stage. Inside the rails text sits --text-inset in, while
     full-bleed art runs flush to the rail.

     --rail uses a percentage rather than 100vw so both rails stay symmetric when
     a classic scrollbar is present. It resolves against the containing block, so
     only apply it to full-width elements. ---- */
  --vwc: min(100vw, 1920px);                 /* proportional reference width */
  --frame: calc(var(--vwc) * 0.02778);       /* 40px @1440, 53.3px @1920 */
  --stage-max: 1813px;                       /* widest the rails ever spread */
  --rail: max(var(--frame), calc((100% - var(--stage-max)) / 2));
  --text-inset: 40px;                        /* rail → text; 20px under 768 */
  --pad-x: calc(var(--rail) + var(--text-inset));  /* rail → content box */
  --gutter: calc(var(--vwc) * 0.05556);      /* 80px @1440; panel interiors */
  --col-gap: calc(var(--vwc) * 0.01389);     /* 20px @1440 */
  --col-gap-wide: calc(var(--vwc) * 0.05556);/* 80px @1440 */
  --nav-h: 60px;

  --section-pad: 100px;       /* standard vertical padding */
  --section-pad-sm: 50px;     /* the tighter variant (some sections use 100 top / 50 bottom) */
  --eyebrow-gap: 150px;       /* eyebrow → headline row (same on mobile) */
  --eyebrow-gap-sm: 50px;     /* the tighter variant: Industries, Approach, Principles, Insights */
  --head-gap: 60px;           /* headline row → section content (same on mobile) */
  --head-stack-gap: 20px;     /* h2 → body when they stack on mobile */
  --text-max: 700px;          /* longest a headline should run */

  /* ---- Components ---- */
  --radius-card: 7px;
  --btn-h: 36px;
  --icon-frame: 50px;
  --bracket-w: 12px;          /* chamfered L: 12 wide × 14 tall, 1px stroke */
  --bracket-h: 14px;

  /* ---- Motion ---- */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-bracket: cubic-bezier(0.47, 0, 0.53, 1);
  --dur: 0.3s;
  --dur-slow: 0.6s;
}

/* Light surfaces: flip the semantic set, not the components.
   NB the class is still called theme-offwhite for markup compatibility, but the
   ground is plain white — the old #f9fbfa read as a grey panel against the white
   nav and frame on the hubs and the platform page. */
.theme-offwhite {
  --bg: var(--n-white);
  --fg: var(--n-black-2);
  --fg-body: rgb(30 30 30 / 0.8);
  --fg-muted: var(--n-gray-600);
  --line: var(--n-gray-200);
  --line-strong: var(--n-black);
  --bracket: var(--n-gray-400);
}
.theme-sage {
  --bg: var(--n-sage);
  --fg: var(--n-black);
  --fg-body: var(--n-gray-800);
  --fg-muted: var(--n-gray-600);
  --line: var(--n-gray-400);
  --line-strong: var(--n-black);
  --bracket: var(--n-gray-400);
}
.theme-green {
  --bg: var(--n-green-900);
  --fg: var(--n-white);
  --fg-body: rgb(255 255 255 / 0.8);
  --line: rgb(255 255 255 / 0.35);
  --bracket: var(--n-white);
}

@media (max-width: 1024px) {
  :root { --gutter: 48px; --col-gap-wide: 40px; --section-pad: 80px; }
}
@media (max-width: 767px) {
  :root {
    --gutter: 20px; --text-inset: 20px; --col-gap-wide: 24px; --nav-h: 56px;
    /* The stage collapses to a flat 20px margin on phones — the proportional
       frame would resolve to ~10px and read as a hairline crop. */
    --frame: 20px;
    --section-pad: 64px;
    /* Flat overrides, not max() — these must beat the fluid :root definitions,
       and they do because they come later in the file at equal specificity.
       The fluid floors are the 1440 sizes, so every other role is already
       held at its floor by the time the viewport is this narrow. */
    --fs-display: 3.8125rem;  /* 61px */
    --fs-lead: 1.25rem;       /* 20px */
  }
}

/* The one step for --fs-eyebrow. It is a label rather than reading text, so it
   gets a single 13→14px bump at the top end instead of a fluid ramp. */
@media (min-width: 1920px) {
  :root { --fs-eyebrow: 0.875rem; }
}



/* Nevado AI — base: reset, type roles, layout primitives */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
::selection { background: var(--n-mint); color: var(--n-black); }

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; text-wrap: balance; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---- Type roles ---- */
.display { font-size: var(--fs-display); line-height: var(--lh-display); letter-spacing: var(--ls-display); }
.h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); letter-spacing: var(--ls-h2); font-weight: var(--fw-h2); }
.h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); letter-spacing: var(--ls-h3); }
.h4 { font-size: var(--fs-h4); line-height: var(--lh-h4); letter-spacing: var(--ls-h3); }
/* Reading roles. .body and .small carry wrapping paragraphs all over the site,
   so they take the multi-line leading and eased tracking. The single-line uses
   that genuinely want solid leading (footer lists, nav, .stat__num, tabular
   labels) set their own line-height locally and are unaffected. */
.lead { font-size: var(--fs-lead); line-height: var(--lh-lead-multi); letter-spacing: var(--ls-lead); color: var(--n-gray-200); }
.body { font-size: var(--fs-body); line-height: var(--lh-body-multi); letter-spacing: var(--ls-reading); color: var(--fg-body); }
.small { font-size: var(--fs-small); line-height: var(--lh-small); letter-spacing: var(--ls-reading); color: var(--fg-body); }
.muted { color: var(--fg-muted); }

.eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  line-height: var(--lh-eyebrow);
}

/* The one serif moment: a single italic word inside a sans headline. */
.em { font-family: var(--font-serif); font-style: italic; font-weight: 400; letter-spacing: -0.01em; }

/* ---- Layout primitives ----
   .container is the standard content box: it spans the full width of its band
   (so backgrounds bleed) and pads in to the stage plus the text inset, putting
   copy 40px inside the rail. Use .container--flush where art must meet the
   rail instead. */
.container {
  width: 100%;
  padding-inline: var(--pad-x);
}
/* Content box that stops exactly on the rails — for hero art and any panel
   whose edge is meant to read as the line itself. */
.container--flush { padding-inline: var(--rail); }
/* Nested content boxes must NOT re-apply the rail. --rail is a percentage of the
   containing block, so a .container inside a panel that already cleared the rail
   would resolve it a second time (and, past --stage-max, against the panel's own
   width — compounding badly). Inside a panel the inset is purely interior, which
   is --gutter, matching what these panels drew before the stage existed. */
.solution .container,
.aud-solution__panel .container,
.sage-panel .container,
.platform-panel .container { padding-inline: var(--gutter); }
.section { padding-block: var(--section-pad); background: var(--bg); color: var(--fg); }

/* Section header: eyebrow, then headline left / paragraph right (baseline-aligned at the bottom) */
.section-head { display: grid; gap: var(--eyebrow-gap); margin-bottom: var(--head-gap); }
.section-head--tall { gap: var(--eyebrow-gap); } /* kept for markup compatibility; same as default now */
.section-head__row {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--col-gap-wide);
  align-items: center; /* h2 and its inline supporting copy sit vertically centred */
}
/* The measure cap is in em, not the --text-max 700px, so it tracks --fs-h2.
   With a fixed px cap the headline wrapped non-monotonically once h2 went
   fluid: it fits at 1480-1800, then the text outgrows a static 700px as h2
   reaches 43px and wraps again above ~1820. 17.95em is exactly 700px at the
   39px base, so the character count — and the 1440 frame — are unchanged. */
.section-head__row .h2 { max-width: 17.95em; }
/* Measures switch rem → em so the character count stays constant as the type
   scales; in rem the cap would hold still and the line would grow past a
   comfortable 60–75 characters exactly when the font got bigger. */
.section-head__row .body { max-width: 30em; justify-self: end; width: 100%; }
/* Opt out of the two-column head and drop the supporting copy under the
   headline, matching the stacked heads elsewhere on the page (e.g. the
   .aud-how "Start with one workflow" block, which is stacked in markup). Same
   declarations the <=991 breakpoint applies, so the two agree by construction. */
.section-head__row--stacked { grid-template-columns: minmax(0, 1fr); align-items: start; gap: var(--head-stack-gap); }
.section-head__row--stacked .body { justify-self: start; max-width: var(--text-max); }
@media (max-width: 991px) {
  .section-head__row { grid-template-columns: minmax(0, 1fr); align-items: start; gap: var(--head-stack-gap); }
  .section-head__row .body { justify-self: start; }
}

.cols-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--col-gap-wide); }
.cols-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--col-gap); }
.cols-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--col-gap); }
@media (max-width: 991px) { .cols-3, .cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .cols-2, .cols-3, .cols-4 { grid-template-columns: 1fr; } }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* The contact form swaps itself for a success panel by adding .hidden. The class
   was never defined, so the form stayed on screen next to the "thanks" message. */
.hidden { display: none !important; }



/* Nevado AI — components: brackets, buttons, nav, cards, icon frames, steps, accordion */

/* =====================================================================
   Corner brackets — the site's signature device.
   Figma geometry: a 12×14 L with a 45° chamfer at the corner, 1px stroke,
   sitting exactly on the element's box corner. Drawn once as an SVG mask
   and colored with --bracket; the other three corners are mirrored.
   Two brackets live on the host's pseudo-elements (top corners), two on
   an inner <span class="corners"> (bottom corners).
   ===================================================================== */
.bracketed { position: relative; }
/* The .corners span exists only to host the two bottom bracket pseudos, so take
   it out of flow everywhere. Left static it is a real flex/grid item on the
   host: in .btn it collected its own 8px gap slot after the chevron, making the
   trailing space 21px against the 17px lead, and in grid hosts it claimed a
   whole track. Sizing it to the host's box also gives the bottom brackets the
   right origin. */
.bracketed > .corners { position: absolute; inset: 0; pointer-events: none; }
.bracketed::before, .bracketed::after,
.bracketed > .corners::before, .bracketed > .corners::after {
  content: "";
  position: absolute;
  width: var(--bracket-w);
  height: var(--bracket-h);
  background: var(--bracket);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath d='M12 .5H5.08L.5 4.6V14' fill='none' stroke='%23000'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath d='M12 .5H5.08L.5 4.6V14' fill='none' stroke='%23000'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
  pointer-events: none;
  transition: transform var(--dur) var(--ease-bracket);
}
.bracketed::before { top: 0; left: 0; }
.bracketed::after { top: 0; right: 0; transform: scaleX(-1); }
.bracketed > .corners::before { bottom: 0; right: 0; transform: scale(-1, -1); }
.bracketed > .corners::after { bottom: 0; left: 0; transform: scaleY(-1); }

/* Hover / focus: the frame opens — brackets step 3px outward. */
.bracketed.is-interactive:hover::before, .bracketed.is-interactive:focus-visible::before { transform: translate(-3px, -3px); }
.bracketed.is-interactive:hover::after, .bracketed.is-interactive:focus-visible::after { transform: translate(3px, -3px) scaleX(-1); }
.bracketed.is-interactive:hover > .corners::before, .bracketed.is-interactive:focus-visible > .corners::before { transform: translate(3px, 3px) scale(-1, -1); }
.bracketed.is-interactive:hover > .corners::after, .bracketed.is-interactive:focus-visible > .corners::after { transform: translate(-3px, 3px) scaleY(-1); }

.bracketed--accent { --bracket: var(--accent); }
.bracketed--ink { --bracket: var(--fg); }

/* Single ticks on panels/cards: same chamfered L, top-left and/or bottom-right. */
.tick-tl, .tick-br { position: relative; }
.tick-tl::before, .tick-br::after {
  content: "";
  position: absolute;
  width: var(--bracket-w);
  height: var(--bracket-h);
  background: var(--fg);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath d='M12 .5H5.08L.5 4.6V14' fill='none' stroke='%23000'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 14'%3E%3Cpath d='M12 .5H5.08L.5 4.6V14' fill='none' stroke='%23000'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
  pointer-events: none;
}
.tick-tl::before { top: 0; left: 0; }
.tick-br::after { bottom: 0; right: 0; transform: scale(-1, -1); }

/* ---- Buttons: 36px tall, 16px text, chevron 8px after the label ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--btn-h);
  /* 13px trailing, not 17px: the chevron glyph carries ~2px of dead space inside
     its own box, so an even pad reads optically wider on the right than the left. */
  padding: 0 13px 0 17px;
  font-size: var(--fs-btn);
  line-height: normal;
  letter-spacing: var(--ls-btn);
  color: var(--fg);
  white-space: nowrap;
  border-radius: var(--radius-card); /* invisible, but matches the Figma hit area */
}
.btn .chev { width: 6px; height: 10px; flex: none; }
.btn--accent { color: var(--accent); --bracket: var(--accent); }

/* Text link with a trailing arrow */
.link { display: inline-flex; align-items: center; gap: 6px; color: var(--fg-muted); font-size: var(--fs-btn); line-height: normal; letter-spacing: var(--ls-btn); }
.link:hover { color: var(--fg); }
.link svg { width: 10px; height: 10px; }

/* ---- Icon frame: a 50px bracketed square holding a glyph ---- */
.icon-frame { width: var(--icon-frame); height: var(--icon-frame); display: grid; place-items: center; flex: none; }
.icon-frame svg { width: 24px; height: 24px; }
/* An exported icon is drawn at the frame's own size with its padding already in
   the artwork (Figma 1834:14169), so it fills the 50px box rather than being
   centred at 24px like the inline glyphs. */
.icon-frame img { width: 100%; height: 100%; display: block; }
/* Some icons are drawn at 40 inside the 50 frame (Figma 1734:17529 and its
   siblings), so an explicit width attribute wins over filling the box. */
.icon-frame img[width="40"] { width: 40px; height: 40px; }
.icon-frame--accent { --bracket: var(--accent); }
.icon-frame--accent svg { color: var(--accent); }
/* The two hub sage panels (insurance, financial services) still carry inline
   glyphs rather than exported artwork, so they sat at 24px while the homepage's
   sage icons fill the frame. Draw them at the homepage's footprint — content
   over ~72% of the 50px box — and thin the stroke so the effective weight stays
   at the ~1.17px the exported icons use. The artwork itself is still pending the
   Figma assets (1860:61315 and 1834:13986); this rule can go once those land. */
.sage-panel .icon-frame > svg { width: 36px; height: 36px; stroke-width: 0.8; }

/* ---- Nav ---- */
.nav {
  position: sticky; top: 0; z-index: 50;
  height: var(--nav-h);
  background: var(--n-black);
  color: var(--n-white);
  border-bottom: 1px solid var(--n-gray-800);
}
/* The rails run up through the nav and meet its bottom rule, closing the stage's
   top corners. */
.nav::before, .nav::after { content: ""; position: absolute; top: 0; bottom: 0; width: 1px; background: var(--n-gray-800); pointer-events: none; }
/* Same --rail offset as every band below, so the line is continuous from the nav
   through the page to the footer. */
.nav::before { left: var(--rail); }
.nav::after { right: var(--rail); }
/* The three groups (lockup | menu | CTA) sit on the stage with centres aligned
   and a 20px horizontal cushion each. Vertically everything is centred in the
   60px bar (no vertical padding: the CTA is 36px).
   The nav is part of the stage: it shares the body's max width instead of
   running to the screen edge. The container pads to the rail and the brand/CTA
   keep their own 20px, so the lockup starts 20px inside the left rail and the
   CTA ends 20px inside the right one — matching Figma (1755:7977 / 1755:8143),
   where the 1360 inner frame insets its groups by 20 on each side. */
.nav .container { padding-inline: var(--rail); }
.nav__inner { height: 100%; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 0; }
.nav__brand { display: inline-flex; align-items: center; padding: 0 20px; justify-self: start; }
.nav__lockup { width: 130px; height: 20px; display: block; }
.nav__links { display: flex; gap: 28px; padding: 0 20px; }
.nav__link { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-nav); color: var(--n-gray-400); letter-spacing: var(--ls-body); padding-block: 6px; }
.nav__link:hover { color: var(--n-white); }
.nav__link svg { width: 9px; height: 9px; }
.nav__cta { justify-self: end; margin: 0 20px; --bracket: var(--n-gray-400); } /* margin, not padding: the brackets sit on the button's own box */
.nav__toggle { display: none; justify-self: end; width: 40px; height: 40px; place-items: center; }
.nav__toggle span { display: block; width: 20px; height: 1px; background: currentColor; box-shadow: 0 -6px 0 currentColor, 0 6px 0 currentColor; }
@media (max-width: 991px) {
  /* Figma's mobile bar keeps a compact CTA beside a filled menu button.
     Mobile_Top Navigation menu (1755:8309) insets the bar's groups 10px inside
     the rails, so the toggle carries that cushion and the CTA sits 10px off it. */
  .nav__inner { grid-template-columns: 1fr auto auto; }
  .nav__links { display: none; }
  .nav__brand { padding: 0 10px; }
  .nav__cta { display: inline-flex; margin: 0 10px 0 0; height: 32px; padding: 0 12px; font-size: var(--fs-small); }
  /* The chip matches the bar it sits in: dark bar -> dark chip, light bar ->
     light chip (body.nav-light below inverts it). Figma draws it white on the
     dark bar, which reads as a slip rather than an intent. */
  .nav__toggle {
    display: grid; width: 40px; height: 32px; border-radius: 3px;
    background: var(--n-black); color: var(--n-white);
    margin-right: 10px;
  }
  .nav__toggle span { width: 18px; box-shadow: 0 -5px 0 currentColor, 0 5px 0 currentColor; }
}
/* Figma keeps the bar CTA at 440 and below (1755:8309); it shortens rather
   than disappearing, so only drop it on the very narrowest screens. */
@media (max-width: 359px) { .nav__cta { display: none; } }

/* ---- Card (approach) — Figma auto-layout: padding 40px, column, align start, gap 16px, flex 1, stretch ---- */
.card {
  background: var(--surface);
  border-radius: var(--radius-card);
  padding: 40px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 16px;
  flex: 1 0 0; align-self: stretch; min-width: 0;
}
.card .body { max-width: 22rem; }

/* Feature item: icon frame + title + copy, no surface (principles: frame → 8 → title → 8 → copy) */
.feature { display: flex; flex-direction: column; }
.feature .icon-frame { margin-bottom: 8px; }
.feature .h4 { margin-bottom: 8px; }
.feature .small { max-width: 17rem; }

/* Three-up column (problem): Figma auto-layout — 40px 20px padding, 16px gap, flex 1 */
.rule-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  padding: 40px 20px;
  flex: 1 0 0;
}
.rule-col__top { display: flex; align-items: center; gap: 16px; width: 100%; }
.rule-col__top::after { content: ""; flex: 1; height: 1px; background: var(--n-gray-400); }
.rule-col .body { max-width: 17.5rem; }

/* Media card (industries, insights) — Figma auto-layout: column, align start, gap 16px, flex 1 */
.media-card { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; flex: 1 0 0; min-width: 0; }
/* The card art is not one shape: the blog images are 1200x630 (1.9:1) and the
   industry ones 520x400 (1.3:1). Forcing 3:1 cropped 37% and 57% off them, so
   each image keeps its own ratio — cards in a row still line up because every
   card in a given row draws from the same set. */
/* The hairline lives on the frame, not the image, so the hover zoom scales the
   artwork inside it without dragging the border along. */
.media-card__link:has(> .media-card__img) { border: 1px solid var(--img-border); }
.media-card__img { width: 100%; height: auto; display: block; }

/* ---- Steps list (how it works) — Figma auto-layout:
   row: padding 8px 8px 24px 8px, gap 32px, align flex-start, stretch
   text frame: column, gap 16px, flex 1 ---- */
.steps { display: flex; flex-direction: column; align-items: stretch; }
.step {
  display: flex;
  align-items: flex-start;
  align-self: stretch;
  gap: 32px;
  padding: 8px 8px 24px 8px;
  color: var(--n-gray-600);
  transition: color var(--dur-slow) var(--ease-out);
}
.step__num { font-size: 13px; line-height: 16px; letter-spacing: -0.06em; padding-top: 2px; font-variant-numeric: tabular-nums; flex: none; }
.step__text { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; flex: 1 0 0; min-width: 0; }
.step__title { font-size: var(--fs-h3); line-height: 1; letter-spacing: var(--ls-h3); font-weight: 500; }
/* 36em ≈ 85 characters. Was 40rem, which ran to ~95 at 16px — well past the
   comfortable range, and the single biggest contributor to this block reading
   as a wall of text. */
.step__body { font-size: var(--fs-body); line-height: var(--lh-body-multi); letter-spacing: var(--ls-reading); max-width: 36em; }
.step.is-active { color: var(--n-white); }
/* hover: inactive steps lift to the grey used for supporting copy; the number goes with it */
.step:not(.is-active):hover, .step:not(.is-active):has(.step__title:focus-visible) { color: var(--n-gray-400); }
/* The keyboard handle is .step__title (role=button), not the <li> -- putting the
   role on the <li> would override its listitem semantics. Focus styling follows. */
.step__title:focus-visible { outline: none; text-decoration: underline; text-underline-offset: 0.15em; text-decoration-thickness: 1px; }

/* ---- Accordion (FAQ): 700px column, 76px rows, 20px question ---- */
.faq { max-width: 800px; margin-inline: auto; } /* ~108 characters per line at 16px — the readable ceiling for short answers */
.faq__item { border-bottom: 1px solid var(--line-strong); }
.faq__item:first-child { border-top: 1px solid var(--line-strong); }
.faq__q {
  width: 100%;
  display: flex; justify-content: space-between; align-items: center; gap: 2rem;
  padding: 28px 0;
  text-align: left;
  font-size: var(--fs-h3); letter-spacing: var(--ls-h3); line-height: 1;
}
.faq__icon { width: 20px; height: 20px; flex: none; position: relative; }
.faq__icon::before, .faq__icon::after {
  content: ""; position: absolute; background: currentColor;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  transition: transform var(--dur) var(--ease-out);
}
.faq__icon::before { width: 20px; height: 1.5px; }
.faq__icon::after { width: 1.5px; height: 20px; }
.faq__item.is-open .faq__icon::after { transform: translate(-50%, -50%) scaleY(0); }
.faq__a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-slow) var(--ease-out); }
.faq__a > div { overflow: hidden; }
.faq__a p { padding: 0 0 28px; color: var(--fg-body); font-size: var(--fs-body); line-height: var(--lh-body-multi); letter-spacing: var(--ls-reading); }
.faq__item.is-open .faq__a { grid-template-rows: 1fr; }

/* ---- Pager arrows ---- */
.pager { display: flex; gap: 6px; justify-content: flex-end; }
.pager button { width: 40px; height: 40px; display: grid; place-items: center; border: 1px solid var(--n-gray-600); color: var(--n-gray-600); border-radius: 3px; }
.pager button.is-active { background: var(--n-white); color: var(--n-black); border-color: var(--n-white); }
.pager svg { width: 12px; height: 12px; }
.pager svg path { stroke: currentColor; }

@media (prefers-reduced-motion: reduce) {
  .bracketed::before, .bracketed::after, .bracketed > .corners::before, .bracketed > .corners::after,
  .faq__a, .faq__icon::before, .faq__icon::after, .step { transition: none; }
}



/* Nevado AI — homepage sections (all distances from the 1440 Figma frame) */

/* Outer black mount with 1px rules around hero → problem → solution, and the footer. */
/* The stage's rails (see --rail in :root) are drawn as a pair of opaque
   "curtains" over everything outside it, so a full-bleed section background
   stops on the line. The 1px rule is the curtain's inner border.

   Curtains rather than padding on .framed: --rail resolves against its own
   containing block, so padding .framed would shrink every child and make the
   nested --pad-x count the rail twice. Overlaying leaves child layout untouched —
   which is also why .framed carries NO padding-inline. */
.framed { background: var(--n-black); position: relative; --curtain: var(--n-black); }
/* z-index keeps the curtains above the sections' own backgrounds. */
.framed::before, .framed::after {
  content: ""; position: absolute; top: 0; bottom: 0;
  width: calc(var(--rail) + 1px);
  background: var(--curtain);
  pointer-events: none; z-index: 2;
}
.framed::before { left: 0; border-right: 1px solid var(--n-gray-800); }
.framed::after { right: 0; border-left: 1px solid var(--n-gray-800); }

/* Close the stage along the bottom. The rails stop where .framed ends, so the
   boundary where the full-bleed sections take over was left open — two vertical
   lines just terminating in mid-page. A 1px rule on whatever follows .framed caps
   them off and reads as the bottom edge of the frame. It spans the full width
   because the bands below are full-bleed; on pages where nothing follows .framed
   (company, contact, insights) the selector simply doesn't match. */
.framed + * { border-top: 1px solid var(--n-gray-800); }

/* Sage bands come in two kinds, structurally:

   Outside .framed — a standalone full-bleed band. Raised above the curtain
   (z-index 3 > 2) so its ground covers the rail strips.

   Inside .framed — nested content, so it must never bleed. Default stacking lets
   the curtain cover its edges, reading as contained with the line down both
   sides (Figma 1834:13983 draws it at x=40, w=1360, inside the rails).

   The inset .sage-panel is never raised either — it sits well within the stage. */
.theme-sage, .solution-wrap { position: relative; z-index: 3; }
.framed .theme-sage, .framed .solution-wrap { z-index: auto; }
/* Opt-out for a sage band that should read full-bleed rather than contained:
   raises it back above the curtain so the ground runs edge to edge and the rails
   stop at its boundary, the same treatment .cta / .theme-green get below.
   Used by the security page's #evolving band. */
.framed .theme-sage.is-bleed { z-index: 3; }

/* The green bands are the exception to the nesting rule above: the deep-green CTA
   and newsletter grounds (and the sweeping artwork some of them carry) are always
   full-bleed, whether or not they sit inside .framed. They close a page rather
   than sitting within the stage, so they read as a band across the whole screen —
   which is how they already appear on the pages where the markup happens to place
   them after .framed (homepage, platform, the audience pages). Raising them above
   the curtain makes that consistent for the ones nested inside it (company,
   insights, news, pr-official-launch, security-compliance). */
.cta, .band-green, .theme-green { position: relative; z-index: 3; }

/* ---- Hero: copy sits 40px inside the rail, art runs flush to it ---- */
.hero { background: var(--n-black); color: var(--n-white); padding: clamp(64px, 9vw, 130px) 0 80px; }
/* The grid pads to the rail rather than the text inset, and the copy column
   re-adds the inset itself — so the headline stays aligned with the sections
   below while the art's outer edge meets the line. */
.hero > .container { padding-inline: var(--rail); }
.hero__grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: var(--col-gap-wide); align-items: center; }
.hero__copy { display: flex; flex-direction: column; padding-left: var(--text-inset); }
.hero__copy .display { margin-bottom: 32px; }
.hero__copy .lead { max-width: 30.5em; margin-bottom: 57px; }
/* Figma (1834:13979) sets the row 745 wide with the button at x=0 and the
   "Learn more" link at x=655 — pushed to the far end rather than trailing the
   button on a fixed gap.

   align-items is center, not flex-end: the link is a 19.5px text line next to a
   36px button, so aligning their bottom edges dropped the link's optical centre
   8px below the button's and it read as sagging. Centring is also what keeps the
   two aligned if the button's height ever changes. */
.hero__actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 2rem 4.5rem; flex-wrap: wrap;
  /* Mirror .hero__copy's left inset on the right so the "Learn more" link
     never sits flush against the frame rail. Without this padding, once the
     layout stacks (≤991px) the copy column spans the full stage and the link
     lands on the rail. Applies at every breakpoint; on desktop it just tightens
     the space-between distribution by --text-inset. */
  padding-right: var(--text-inset);
}
/* Two-column: the art holds its full 554px and never shrinks with the column, so
   below ~1300px it overflows its track to the left and overlaps the copy. The
   copy is raised above it so the display type stays readable. */
/* The 554px box is Figma's art rect (1834:13974) and the accent brackets are
   baked into the asset itself, so the box takes no padding — the artwork must
   run edge to edge or it renders ~12% under the design. `cover` matches Figma's
   object-cover: the asset is 1108x1148 (0.965), so filling the square crops
   ~10px off the top and bottom, which is empty margin (the artwork carries 92px
   of it at this scale) and never the cube cluster or its brackets. */
.hero__art { justify-self: end; width: 554px; aspect-ratio: 1; --bracket: var(--accent); }
.hero__art img { width: 100%; height: 100%; object-fit: cover; }
.hero__copy { position: relative; z-index: 1; }
@media (max-width: 991px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, the art leads and always fills the column — no cap, no overlap. */
  .hero__art { justify-self: stretch; width: 100%; order: -1; }
  .hero__copy .lead { margin-bottom: 40px; }
}
/* Phones: from Figma Mobile_Homepage (1834:15737), measured on its 440 frame
   (= 20px --frame + 20px --gutter each side, so the 360px content column is
   exactly ours). The previous rule capped the art at 260px and pinned it flush
   under the nav; the design instead runs the art the full width of the column
   with a 50px gap above it, and closes the art→headline gap to zero. */
@media (max-width: 767px) {
  /* Art at y=110 under a 60px nav → 50px above. Headline (1834:15742) starts at
     y=470, flush against the art's bottom edge, so the grid row-gap goes to 0. */
  .hero { padding-top: 50px; }
  .hero__grid { row-gap: 0; }
  /* Square box (1834:15738 is 360x360) with the photo cropped to fill, i.e. a
     centred crop — Figma's image rect is 360x406 at y=-23.13, overflowing the
     frame 23px top and bottom. Both the fill and the crop are inherited from
     the base rule above. */
  /* 1834:15742 → 1834:15743 is 40px; lead → actions (1834:15744) is 60px. */
  .hero__copy .display { margin-bottom: 40px; }
  .hero__copy .lead { margin-bottom: 60px; }
}

/* ---- Problem (sage, inside the frame): 50px vertical padding, same on mobile ---- */
.problem { padding-top: var(--section-pad-sm); padding-bottom: var(--section-pad-sm); }
.problem .cols-3 { gap: 0; } /* columns carry their own 20px side padding */

/* ---- Solution: off-white panel stepped in 80px from the sage on the left ---- */
/* Figma (Desktop_Homepage 1834:14053) puts the panel at x=120 w=1280 while the
   problem band above it runs x=40 w=1360 — so the panel is tabbed --gutter in
   from the frame's left rail, flush right, and the sage shows down its left
   edge. Same relationship as .aud-solution__panel and .platform-panel.

   The panel then applies its own --gutter inside (Figma: px-[80px]), so content
   lands at 200px @1440 against the problem section's 120px. That stacked inset
   is intentional — the tab IS the visual device. Do not "correct" it to align
   with the other sections. */
/* The sage ground bleeds the full viewport width (only the rail marks the stage),
   so it carries the rail offset in its own padding to keep .solution's slab at
   the same x=120 it had when .framed supplied that 40px. */
.solution-wrap { background: var(--n-sage); padding-left: calc(var(--rail) + var(--gutter)); padding-right: var(--rail); }
.solution { padding: var(--section-pad) 0; }
/* Headline row → video: 60px (--head-gap). Video → four-column block: 40px. */
/* 16/9 to match the source video (1920x1080) so the whole frame is visible
   rather than cropped to the old placeholder's 1119/439 letterbox. */
.solution__scene { border: 1px solid var(--n-gray-600); background: var(--n-black); aspect-ratio: 16 / 9; overflow: hidden; position: relative; }
.solution__scene video, .solution__scene img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Sound toggle, bottom-right over the video. The video autoplays muted (browser
   policy), so this is the gesture that unmutes it. */
.solution__sound {
  position: absolute;
  right: 16px;
  bottom: 16px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid rgb(255 255 255 / 0.35);
  border-radius: 999px;
  background: rgb(0 0 0 / 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--n-white);
  font: inherit;
  font-size: var(--fs-small);
  letter-spacing: -0.04em;
  line-height: 1;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
.solution__sound:hover { background: rgb(0 0 0 / 0.75); border-color: rgb(255 255 255 / 0.6); }
.solution__sound:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.solution__sound-icon { display: inline-flex; }
/* Muted (default): show the x. Unmuted: show the waves. */
.solution__sound .solution__sound-on { display: none; }
.solution__sound[aria-pressed="true"] .solution__sound-off { display: none; }
.solution__sound[aria-pressed="true"] .solution__sound-on { display: inline; }
@media (max-width: 767px) {
  .solution__sound { right: 10px; bottom: 10px; padding: 7px 11px; }
  .solution__sound-text { display: none; }
}
@media (prefers-reduced-motion: reduce) { .solution__sound { transition: none; } }
.solution__features { margin-top: 40px; background: var(--surface); border-radius: var(--radius-card); padding: 40px; }
.solution__features .feature { gap: 16px; }
/* Match .panel-card on the audience pages: the title carries the same weight. */
.solution__features .feature .h4 { margin-bottom: 0; font-weight: 500; }
@media (max-width: 767px) { .solution-wrap { padding-left: calc(var(--rail) + var(--gutter)); } }

/* ---- How it works (dark): pinned while the cubes scrub through three states ----
   .how-pin is the block's scroll runway (height + pinning set by
   js/how-it-works.js); the section keeps its designed spacing, only the steps +
   cube block pins.

   --how-travel is the scroll spent on the cubes once pinned — in scrub mode the
   runway's length IS the animation's duration, so each of the two tweens gets
   about one screen. At the old 150 each read as a flick. Usable range 220–320. */
.how-pin { --how-travel: 260; position: relative; }
/* The block keeps its designed height while pinned (Figma 1834:14689 is a fixed
   861px frame), so there is no slack to distribute: the steps sit their designed
   60px under the headline and nothing is pushed to the bottom. */
.how-pin > .container { display: flex; flex-direction: column; justify-content: flex-start; } /* height set by JS while pinned */
/* Figma's How It Works block is 1360x495: 100px top padding, the headline row,
   then the steps at y=259 — a 60px headline-to-content gap and no tall eyebrow
   gap. The preview inherited the generic --eyebrow-gap here, which pushed 150px
   of empty space above the animation. */
.how .section-head { gap: var(--eyebrow-gap-sm); }
.how .section-head__row .body { color: var(--n-gray-400); } /* supporting copy: grey, 16/16, −6% */
.how__grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--col-gap-wide); align-items: end; } /* steps bottom-aligned to the cube */
/* The cube is bottom-aligned to the steps but takes no height of its own, so it
   can grow upward into the empty space beside the headline without making the
   pinned group taller. */
.how__art { justify-self: end; width: min(100%, 350px); height: 0; position: relative; color: var(--n-white); } /* 350 = largest that clears the supporting copy in the exploded state */
.how__art svg { position: absolute; bottom: 0; right: 0; width: 100%; height: auto; overflow: visible; }
.how .step { transition: color var(--dur-slow) var(--ease-out); }
@media (max-width: 991px) {
  .how-pin { --how-travel: 200; }
  /* The headline block is static copy and, at these widths, is allowed to
     scroll up out of the fold so the cube + steps can hold it (see
     js/how-it-works.js layout()). 60px of air under it is dead scroll the
     reader has to push through before the animation starts, so tighten it. */
  .how .section-head { margin-bottom: 32px; }
  .how__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .how__art { justify-self: center; width: min(70vw, 300px); height: auto; order: -1; }
  .how__art svg { position: static; }
}

/* Short phones (a 375x667 fold is 611px, the stacked grid 715px): "Operate"
   fell below the fold for the whole runway, so the third state animated out of
   sight. Trim the cube and the gap above the steps — the cube is decorative and
   still reads at 210px, whereas losing a step defeats the section. */
@media (max-width: 991px) and (max-height: 740px) {
  .how__grid { gap: 24px; }
  .how__art { width: min(55vw, 210px); }
  .how .section-head { margin-bottom: 24px; }
  /* The 24px bottom padding is what separates the rows (.steps has no gap), so
     trimming it buys 48px across the three — enough for the last step's third
     line to clear the fold. */
  .how .step { padding-bottom: 8px; }
}

/* ---- Industries (off-white) ---- */
/* The Insurance / Financial Services cards on the homepage and platform page
   reuse each hub page's own hero art rather than carrying their own near-
   duplicate crops. Figma draws these cards' art at 600x200 (3:1) with
   object-cover (Desktop_Homepage 1834:14152 / Desktop_Platform 1734:17664), so
   the 1.117-ratio hero is cropped to that band here instead of shipping a
   second, separately-cropped file. */
.industries .media-card__img {
  aspect-ratio: 600 / 200;
  object-fit: cover;
  object-position: center;
}
.industries .media-card__link:has(> .media-card__img) { border-color: var(--img-border); }

/* The sub-page router grids on the two hub pages ("Serving every role in the
   insurance ecosystem" / "One platform across Financial Services"). These reuse
   each sub-page's hero art, which is a near-square 1600x1433, so without a crop
   the tiles inherited that 1.117 ratio and ran ~164px too tall. Figma draws them
   as a wide, short band — 373x200 at 1440 — so pin that ratio and let object-fit
   crop to it. Ratio rather than a fixed height because the column width is
   fluid; the selector matches only these two grids (verified: the homepage and
   platform industries pairs are .industries .cols-2 and are unaffected). */
.cols-3:has(> .media-card) .media-card__img {
  aspect-ratio: 373 / 200;
  object-fit: cover;
  object-position: center;
}

/* Same 40px row gap as the other wrapped grids, for the same reason: the shared
   --col-gap is proportional, so once these wrap it closes to ~5px and the card
   below rides up under the previous one's caption. Applies to the homepage
   industries pair and the explore rows on the two hub pages — all media cards,
   all of which wrap. Columns keep --col-gap / --col-gap-wide. */
.industries .cols-2,
.cols-3:has(> .media-card) { row-gap: 40px; }

/* ---- Approach (sage) ---- */
/* Same 40px row gap again. These carry a visible tinted surface, so unlike the
   .rule-col bands (whose own 40px padding supplies the air) the proportional
   --col-gap leaves the card grounds only ~5px apart once they stack. */
.approach .cols-3 { row-gap: 40px; }
.industries .section-head, .approach .section-head, .principles .section-head, .insights .section-head { gap: var(--eyebrow-gap-sm); } /* 50px eyebrow → headline from Industries down */

/* ---- Principles (dark) — Figma auto-layout: wrap, align start, gap 40px 20px, stretch;
   item: icon → 16px → title → 4px → copy ---- */
.principles__grid { display: flex; flex-wrap: wrap; align-items: flex-start; align-content: flex-start; gap: 40px var(--col-gap); align-self: stretch; }
.principles__grid > .feature { flex: 0 0 calc((100% - 2 * var(--col-gap)) / 3); gap: 0; }
.principles .feature .icon-frame { margin-bottom: 16px; }
.principles .feature .h4 { margin-bottom: 4px; }
.principles .feature .small { color: var(--fg-body); }
/* Two across all the way down — Figma's Mobile_Homepage grid (1834:16044) is
   grid-cols-[repeat(2,minmax(0,1fr))] / grid-rows-[repeat(3,fit-content)] with
   gap-x-[20px] gap-y-[40px], and its items measure 190px at x=0 and x=210 in the
   400px content box. So these never stack to one; the pair holds at 440 and
   below. The 13px body copy is sized for that narrow column.

   That same node pins the column gap at 20px, where the shared --col-gap is
   proportional and would close to ~6px at 440 (the two columns would nearly
   touch). Pin it, and keep the basis in step so the pair still fits. */
@media (max-width: 991px) {
  .principles__grid { column-gap: 20px; }
  .principles__grid > .feature { flex-basis: calc((100% - 20px) / 2); }
}

/* ---- Insights (dark) ---- */
.section-head__row .insights__intro { margin-top: 20px; max-width: 24rem; justify-self: start; }
/* The blog cards are 1200x630 social-share art (1.9:1), not the 3:1 strips the
   Industries cards use. The shared 3/1 crop sliced ~37% off their height — the
   Nevado lockup off the top, the author's title off the bottom. `contain` shows
   each card whole; the box keeps a fixed 1.9:1 so the grid rows stay even, and
   the letterboxing behind the one odd 600x200 asset picks up the section ground
   rather than a bar of a different colour. */
.insights .media-card__link:has(> .media-card__img) { border: 0; }
.insights .media-card__img {
  aspect-ratio: 1200 / 630;
  object-fit: contain;
  background: var(--bg);
}
.insights .media-card .h3 { max-width: 30rem; }
.insights .pager { margin-top: 24px; }

/* ---- FAQ (dark) ---- */
.faq-section { padding-top: 135px; }
.faq-section .h2 { text-align: center; margin-bottom: 59px; }
/* Variant: heading in a left column, accordion in the right — fills the section
   without lengthening the lines. Add .faq-section--split to the section. */
.faq-section--split .container { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); gap: var(--col-gap-wide); align-items: start; }
.faq-section--split .h2 { text-align: left; margin-bottom: 0; max-width: 8em; }
.faq-section--split .faq { max-width: none; margin-inline: 0; }
@media (max-width: 991px) { .faq-section--split .container { grid-template-columns: minmax(0, 1fr); gap: 40px; } }

/* ---- CTA band (green gradient): h2 → 27 → copy → 18 → button ---- */
.cta {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--n-green-900) 0%, var(--n-green-800) 100%);
  color: var(--n-white);
  padding: 150px 0;
  text-align: center;
}
/* h2 → copy → button, 20px apart; copy capped for a comfortable centred measure */
.cta__inner { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 20px; }
.cta__inner .h2 { max-width: var(--text-max); }
.cta__inner p { max-width: 27em; color: var(--fg-body); font-size: var(--fs-cta-p); line-height: var(--lh-lead-multi); letter-spacing: var(--ls-reading); }
.cta__inner .btn { --bracket: var(--n-white); }
/* Decorative shapes (Figma exports, full size 1127×1525). Positions are the
   1440 frame's: LEFT's origin at (−740, 135) from the section's top-left,
   RIGHT's at 603px from the left = 290px past the right edge, −340 from the
   top. Each is anchored to its own edge so they simply spread apart on wider
   screens; the section clips them. */
.cta__art { position: absolute; inset: 0; pointer-events: none; }
.cta__shape { position: absolute; top: 0; width: 1127px; height: 1525px; }
.cta__shape--left { left: -740px; top: 135px; }
.cta__shape--right { right: -290px; top: -340px; }
@media (max-width: 767px) { .cta { padding: 96px 0 120px; } .cta__art { display: none; } /* revisit in the mobile pass */ }

/* ---- Footer: brand cell | bordered link panel (558px), then a 95px bar ---- */
.footer { background: var(--n-black); color: var(--n-white); overflow: hidden; } /* clips the bar's full-width rule */
/* The footer is part of the stage: its content box stops on the rails like every
   band above it, instead of running to the screen edge. The black ground still
   bleeds full width (.footer itself is unpadded); .footer__main carries the
   inset, so the brand cell and link panel sit on the line. */
.footer__main { display: grid; grid-template-columns: minmax(0, 41fr) minmax(0, 59fr); margin-inline: var(--rail); }
/* The top rule runs across both cells, not just the link panel: on pages with a
   green band above the footer the band supplies the break, but the legal pages
   (privacy, terms, contact) end on flat black and the brand side had no seam. */
.footer__brand { position: relative; display: grid; place-items: center; padding: 3rem; overflow: hidden; border-top: 1px solid var(--n-gray-800); }
/* z-index keeps the mark above the trail; pointer-events lets the cursor reach
   the cells underneath it, so the logo isn't a dead square in the middle of the
   grid. Safe because the mark is decorative — it is not a link. */
.footer__brand svg { position: relative; z-index: 1; width: 101px; height: 101px; pointer-events: none; }
/* Pixel trail on the brand cell: invisible until hovered — a cell lights up
   under the cursor, drops to a 30% trail when the cursor leaves, then fades
   out. Column/row counts are set inline by js/pixel-grid.js: the nearest whole
   number of ~20px cells, stretched to fill, so the grid is flush on every edge
   with nothing overflowing or cropped. */
.pixel-grid { position: absolute; inset: 0; display: grid; gap: 2px; }
.pixel-grid > i { display: block; background: var(--accent); opacity: 0; transition: opacity 0.2s linear; }
.pixel-grid > i.is-hot { opacity: 1; }
.pixel-grid > i.is-warm { opacity: 0.3; transition: opacity 0.2s linear 0.5s; }
.pixel-grid > i.is-cooling { opacity: 0; transition: opacity 0.5s var(--ease-out); }
/* Pointer-only flourish, and no trail for reduced-motion visitors. The JS
   bails on the same query, so the cells are never even built. */
@media (hover: none), (prefers-reduced-motion: reduce) { .pixel-grid { display: none; } }
.footer__links {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)); /* three equal columns across the panel, whatever its width */
  gap: 0;
  min-height: 558px;
  padding: 121px calc(var(--vwc) * 0.0278) 80px calc(var(--vwc) * 0.0694); /* 40px right / 100px left @1440, proportional */
  border-top: 1px solid var(--n-gray-800);
  border-left: 1px solid var(--n-gray-800); /* the divider from the brand cell; the bottom rule is the bar's full-width line */
  align-content: start;
}
.footer__col { display: flex; flex-direction: column; gap: 38px; }   /* list end → next label */
.footer__group { display: flex; flex-direction: column; gap: 25px; } /* label → first link */
.footer__label { font-size: var(--fs-eyebrow); letter-spacing: var(--ls-eyebrow); text-transform: uppercase; color: var(--fg-faint); line-height: 1; }
.footer__list { display: flex; flex-direction: column; gap: 15px; font-size: var(--fs-body); line-height: 1; }
/* Dot-hover: a 4px mint dot fades in at the link's left while the text nudges
   over to make room. */
.footer__list li a {
  position: relative; display: inline-block;
  transition: color var(--dur) var(--ease-out), padding-left var(--dur) var(--ease-out);
}
.footer__list li a::before {
  content: ""; position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 4px; height: 4px; background: var(--accent);
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
}
.footer__list li a:hover { color: var(--n-mint); padding-left: 12px; }
.footer__list li a:hover::before { opacity: 1; }
/* sub-pages: each item carries its own short tick, 16px left of the text, centred on it */
.footer__sub { margin-top: 15px; display: flex; flex-direction: column; gap: 14px; }
.footer__sub li { position: relative; padding-left: 16px; }
.footer__sub li::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 1px; height: 14px; background: var(--n-gray-800); }
.footer__bar {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem;
  height: 95px;
  /* On the stage like .footer__main; the 17/20px are the design's optical pads,
     measured from the rail rather than the viewport edge. */
  margin-inline: var(--rail);
  padding: 0 20px 0 17px;
  font-size: var(--fs-body); color: var(--n-gray-400); line-height: 1;
}
/* the rule above the bar runs edge to edge — through the brand cell and past the rails */
.footer__bar::before { content: ""; position: absolute; top: 0; left: -100vw; right: -100vw; height: 1px; background: var(--n-gray-800); }
.footer__bar .tag { margin-right: auto; display: flex; gap: 24px; flex-wrap: wrap; }
.footer__bar .legal { display: flex; gap: 24px; font-size: var(--fs-small); }
.footer__bar .legal a { transition: color var(--dur) var(--ease-out); }
.footer__bar .legal a:hover { color: var(--n-white); } /* colour-only, same as the top nav */
/* Mail + LinkedIn travel as one unit. Figma groups them in a single frame
   (2095584161) at every tier; as bare flex siblings the 36px chip would wrap
   to a line of its own in the ~30px band just above the 1140 breakpoint,
   leaving it stranded under the mail address. */
.footer__contact { display: flex; align-items: center; gap: 24px; }
.footer__mail { color: var(--n-white); font-size: 1.75rem; letter-spacing: -0.03em; display: inline-flex; align-items: center; gap: 6px; transition: color var(--dur) var(--ease-out); }
.footer__mail svg { width: 0.6em; height: 0.6em; transition: transform var(--dur) var(--ease-out); }
.footer__mail:hover { color: var(--n-mint); }
.footer__mail:hover svg { transform: translate(2px, -2px); } /* the ↗ travels its own direction */
.footer__social { width: 36px; height: 36px; background: var(--n-white); color: var(--n-black); display: grid; place-items: center; border-radius: 3px; font-size: 1rem; font-weight: 700; transition: background var(--dur) var(--ease-out); }
.footer__social:hover { background: var(--accent); color: var(--n-black); }
/* Stacked footer per the reference "Footer (with breakpoints)": a small
   left-aligned logo strip, then TWO link columns (Industries + Financial
   Services stacked in the left one; Platform/About/Resources in the right),
   then the bar. The usual "3-up then straight to 1-up" rule is for wrapped
   card grids of equal siblings — this is a designed nav layout where the third
   column deliberately spans both rows, so 2-up is the intended tier. */
@media (max-width: 1140px) {
  .footer__main { grid-template-columns: minmax(0, 1fr); }
  /* No bottom border: stacked, the brand cell sits directly on the link panel,
     and a rule here reads as a stray line hanging under the logo. Confirmed
     against Figma 1953:18443 "Footer (with breakpoints)": the 850 and 440
     frames carry no horizontal rule between the logo and the links — every
     border in them is a border-l (the sub-page ticks), and even the desktop
     frame only ever uses border-t. The desktop seam between the two cells is
     the link panel's border-left, a vertical divider with no horizontal
     equivalent once they stack. */
  .footer__brand { place-items: start; padding: 60px var(--text-inset) 0; }
  .footer__brand svg { width: 50px; height: 50px; }
  .footer__links {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* Figma stacked tiers: 50px between the columns, 24px between the stacked
       Industries / Financial Services rows, and 80px from the logo down to the
       first label (60 brand top + 50 logo + 80 = the 190 in the 850 frame). */
    row-gap: 24px; column-gap: 50px;
    min-height: 0;
    padding: 80px var(--text-inset) 60px;
    border-inline: 0; border-top: 0;
  }
  .footer__col:nth-child(1) { grid-column: 1; grid-row: 1; }
  .footer__col:nth-child(2) { grid-column: 1; grid-row: 2; }
  .footer__col:nth-child(3) { grid-column: 2; grid-row: 1 / span 2; }
  /* The second column's label is a &nbsp; spacer that only exists to align
     Financial Services with the other columns' first row on desktop. Once it
     sits *below* Industries it is a phantom blank line, so drop it and let the
     list run flush against the one above. */
  .footer__col[data-spacer-label] > .footer__label { display: none; }
  /* Once the bar can wrap to two rows the fixed 95px height crams the text
     against the top border — let it grow with real padding instead. Figma's 850
     bar is 172 tall: 20 top, two 36 rows 40 apart, 40 bottom. */
  .footer__bar { height: auto; padding: 20px var(--text-inset) 40px; row-gap: 40px; }
}
@media (max-width: 767px) {
  /* Fully stacked bar, per Figma's 440 frame: mail + LinkedIn on row 1,
     Privacy/Terms on row 2 as two columns matching the links above, then
     copyright and tagline. The bar switches later than the columns (1140)
     because it fits in one row far longer — at 850 Figma still sets legal
     beside the tagline, which the 1140 block above handles.

     The link columns stay 2-up here rather than collapsing to 1-up: Figma's
     440 frame keeps two 155px columns side by side. */
  .footer__bar { flex-wrap: wrap; row-gap: 40px; padding: 20px var(--text-inset) 40px; }
  /* The contact group is the bar's first row and spans it, so mail sits left and
     the LinkedIn chip is pushed to the right edge. */
  .footer__contact { order: 1; flex-basis: 100%; justify-content: space-between; }
  .footer__bar .legal {
    order: 2; flex-basis: 100%;
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px;
  }
  .footer__bar .tag { order: 3; flex-basis: 100%; margin-right: 0; flex-direction: column; gap: 12px; }
}



/* Nevado AI — motion: hero load sequence and scroll reveals
   Settings: --reveal-dur (how long each reveal takes), --reveal-ease,
   --reveal-rise (how far elements travel up), --reveal-stagger (gap between
   siblings in a group).
   Everything here is gated on html.js (set by an inline script in <head>),
   so with JavaScript off nothing is ever hidden. */

:root {
  --reveal-dur: 0.9s;
  --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --reveal-rise: 24px;
  --reveal-stagger: 70ms;
}

/* ---- Reveal primitives (only while html.js and not yet revealed) ---- */
html.js [data-reveal]:not(.is-revealed) {
  opacity: 0;
  transform: translateY(var(--reveal-rise));
}
html.js [data-reveal="scale"]:not(.is-revealed) { transform: scale(0.96); }
/* The hero art is the LCP element. Fading it in from opacity:0 means the largest
   paint can't happen until reveal.js (the last of five end-of-body scripts) adds
   .is-revealed -- which cost ~1.15s of "element render delay" on mobile. Keep it
   fully opaque and let the reveal animate transform only: the motion is retained,
   but the pixels are paintable the moment the bitmap decodes. */
html.js .hero__art[data-reveal]:not(.is-revealed) { opacity: 1; }
html.js [data-reveal="none"]:not(.is-revealed) { transform: none; }
[data-reveal] {
  transition: opacity var(--reveal-dur) var(--reveal-ease), transform var(--reveal-dur) var(--reveal-ease);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
[data-reveal].is-revealed { will-change: auto; }

/* Line-split headlines: each line rises out of its own clipped box */
[data-reveal="lines"] { opacity: 1 !important; transform: none !important; }
[data-reveal="lines"] .line { display: block; overflow: clip; padding: 0 0.15em 0.16em 0; margin: 0 -0.15em -0.16em 0; } /* clip box padded for italic/descender overhang, margin-compensated so line width is unchanged */
[data-reveal="lines"] .line-inner { display: block; transform: translateY(110%); transition: transform var(--reveal-dur) var(--reveal-ease); transition-delay: var(--line-delay, 0ms); }
[data-reveal="lines"].is-revealed .line-inner { transform: none; }
html:not(.js) [data-reveal="lines"] .line-inner { transform: none; }

/* Grouped children stagger; the group itself stays put */
html.js [data-reveal-group] > * { opacity: 0; transform: translateY(var(--reveal-rise)); transition: opacity var(--reveal-dur) var(--reveal-ease), transform var(--reveal-dur) var(--reveal-ease), color var(--dur-slow) var(--ease-out); }
html.js [data-reveal-group].is-revealed > * { opacity: 1; transform: none; }

/* Brackets draw in after their host has arrived */
html.js [data-reveal] .bracketed::before, html.js [data-reveal] .bracketed::after,
html.js [data-reveal] .bracketed > .corners::before, html.js [data-reveal] .bracketed > .corners::after,
html.js [data-reveal].bracketed::before, html.js [data-reveal].bracketed::after,
html.js [data-reveal].bracketed > .corners::before, html.js [data-reveal].bracketed > .corners::after,
html.js [data-reveal-group] .bracketed::before, html.js [data-reveal-group] .bracketed::after,
html.js [data-reveal-group] .bracketed > .corners::before, html.js [data-reveal-group] .bracketed > .corners::after {
  opacity: 0; transition: opacity 0.4s linear 0.45s, transform var(--dur) var(--ease-bracket);
}
html.js .is-revealed .bracketed::before, html.js .is-revealed .bracketed::after,
html.js .is-revealed .bracketed > .corners::before, html.js .is-revealed .bracketed > .corners::after,
html.js .is-revealed.bracketed::before, html.js .is-revealed.bracketed::after,
html.js .is-revealed.bracketed > .corners::before, html.js .is-revealed.bracketed > .corners::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  html.js [data-reveal]:not(.is-revealed), html.js [data-reveal-group] > * { opacity: 1; transform: none; transition: none; }
  [data-reveal="lines"] .line-inner { transform: none !important; transition: none; }
  html.js [data-reveal] .bracketed::before, html.js [data-reveal] .bracketed::after,
  html.js [data-reveal] .bracketed > .corners::before, html.js [data-reveal] .bracketed > .corners::after,
  html.js [data-reveal-group] .bracketed::before, html.js [data-reveal-group] .bracketed::after,
  html.js [data-reveal-group] .bracketed > .corners::before, html.js [data-reveal-group] .bracketed > .corners::after { opacity: 1; transition: none; }
}



/* =====================================================================
   INTERIOR PAGES — extends the preview
   The preview specifies the homepage only. Everything below is built from
   the tokens and primitives above so interior pages read as the same system:
   no new colors, no new type sizes, no new spacing values.
   ===================================================================== */

/* ---- Page hero: the homepage hero's little brother. Same frame inset and
   type roles, but shorter, and the headline uses --fs-h1-page so it sits
   between the homepage display and a section h2. ---- */
:root { --fs-h1-page: 3.5rem; }        /* 56px desktop */
@media (max-width: 767px) { :root { --fs-h1-page: 2.5rem; } }  /* 40px */

/* Editorial index headers (insights.html, news.html). Figma Desktop_Insights
   (732:52303) and Desktop_News (1989:23795) draw the page title much larger than
   a standard interior hero: 95px on an 81px box (0.85 leading, -0.06em tracking).
   Mobile (1989:24185) drops to 61px, coinciding with --fs-display's mobile step. */
:root { --fs-h1-index: 5.9375rem; }    /* 95px desktop */
@media (max-width: 767px) { :root { --fs-h1-index: 3.8125rem; } }  /* 61px */

/* Vertical rhythm is shared with the homepage .hero rather than the flat 100px
   Figma's interior hero (1734:17495) drew, so the two read as one system. The
   headline still uses --fs-h1-page; only the spacing is unified. */
.page-hero { background: var(--n-black); color: var(--n-white); padding: clamp(64px, 9vw, 130px) 0 80px; }

/* Interior heroes are not inside .framed, so they take the same container as
   the sections below. In Figma the hero headline and the first section
   headline both sit 120px from the frame edge (40 frame + 80 gutter), so the
   hero uses --gutter, the same as every other content column. */
.page-hero .container {
  padding-inline: var(--pad-x);
}
/* Where the hero carries art, the container drops to the rail and the copy
   column re-adds the text inset, so the image meets the line while the title
   stays aligned with everything below. Heroes without art are unaffected. */
.page-hero:has(.page-hero__art) > .container { padding-inline: var(--rail); }
.page-hero:has(.page-hero__art) .page-hero__copy { padding-left: var(--text-inset); }
.page-hero__grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: var(--col-gap-wide); align-items: center; }
.page-hero__title { font-size: var(--fs-h1-page); line-height: 0.95; letter-spacing: var(--ls-display); max-width: 16em; }
.page-hero__lead { font-size: var(--fs-lead); line-height: var(--lh-lead-multi); letter-spacing: var(--ls-lead); color: var(--n-gray-200); max-width: 34em; margin-top: 24px; }
.page-hero__actions { display: flex; align-items: center; gap: 2rem 3rem; flex-wrap: wrap; margin-top: 40px; }

/* ---- Hero plate art (company) ----
   Two bands of line art, pinned to the hero's top and bottom edges.

   Split into two bands rather than one overlay SVG: preserveAspectRatio="slice"
   scales to cover, so it always crops one axis and drops two plates out of view
   outside ~1280-1600px. Two edge-anchored bands each keep their own ratio, so
   only the gap between them flexes with hero height.

   Note the distinct block name: .page-hero__art is the interior-hero photo slot
   above, and carries :has() rules that re-inset the container.

   Height comes from the air around the headline, not the bands (Figma
   1753:29609 desktop, 1753:29929 mobile). The bands are allowed to overlap the
   copy — Figma does the same, and it's thin line work sited away from the text.
   Reserving their full height instead pushes the hero ~72px past the frame. */
.page-hero--plates {
  position: relative;
  overflow: hidden;
  /* Figma's 174px holds to its 1440 reference and below. Past that the bands
     keep growing with the viewport while the hero no longer does, and the top
     band's lowest line — y=138 of its 142-unit box — descends into the headline;
     at 1920 it crosses "the future" outright. So the top air yields to the art
     once the art needs more, and the hero grows with it. The bottom band's
     lowest run sits 76/160 up from its own edge, which stays inside 141px at
     every width, so only this side needs the floor. */
  padding-top: max(174px, calc(min(100vw, 1920px) * 138 / 1280 + 8px));
  padding-bottom: 141px;
  /* A floor, not the height: the bottom band is pinned to the hero's bottom
     edge, so a hero that sizes purely to its content rides that band up into the
     copy whenever the headline takes fewer lines than the 3 Figma drew — with
     the webfont missing, the fallback sets it in 2 and the bottom-right plate's
     top rule crosses line 1. 558px is the frame's own height, so the art can
     never climb higher than Figma puts it. */
  min-height: 558px;
  display: flex;
  flex-direction: column;
  /* Not centred: Figma holds the headline 174px from the hero's top whatever it
     wraps to, and centring spends the floor's slack above the copy — pushing it
     down toward the bottom band instead of away from it. */
  justify-content: flex-start;
}
/* Figma draws this headline at the editorial index size rather than the
   interior-page one: 95px on 0.85 leading with the shared display tracking, 61px
   on mobile — --fs-h1-index exactly. The grid drops to a single column because
   this hero has no second slot; at 1.35fr the copy caps at 689px and the
   headline can never reach the 920px measure Figma wraps it against. */
.page-hero--plates .page-hero__grid { grid-template-columns: minmax(0, 1fr); }
.page-hero--plates .page-hero__title {
  /* Between 768 and 1024 the column is narrower than 95px can wrap in, so the
     size ramps up to meet it: 9.28vw is 95px at 1024. */
  font-size: min(var(--fs-h1-index), 9.28vw);
  line-height: 0.85;
  max-width: 9.7em;   /* 921px at 95px — Figma's 920px measure */
}
.page-hero--plates > .container { position: relative; z-index: 1; }
.page-hero__plate-art { position: absolute; inset: 0; pointer-events: none; }
/* Each band scales in width and keeps its aspect ratio, so no plate is ever
   stretched or pushed out of frame. The max-width cap matters: the bands grow
   with the viewport, and past ~2000px the two would meet in the middle and
   overlap. 1920px is also where --vwc stops scaling, so the art stops growing
   with the rest of the system. */
.page-hero__plates {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 1920px;
  height: auto;
}
.page-hero__plates--top { top: 0; }
.page-hero__plates--bottom { bottom: 0; }
/* Phones keep the art — it is the only thing in the hero besides the headline.
   Mobile_Company's air is all but symmetric where the desktop frame's is
   top-heavy, so the two figures swap round. The headline holds its 61px here
   rather than following the vw ramp above, which would undercut it below 1024. */
@media (max-width: 767px) {
  .page-hero--plates { padding-top: 146px; padding-bottom: 154px; min-height: 508px; }
  .page-hero--plates .page-hero__title { font-size: var(--fs-h1-index); }
}
/* Below ~414px the longest word — "infrastructure", 5.45em — no longer fits the
   column at 61px, and the hero clips rather than wraps it, so the headline steps
   down instead of overflowing. */
@media (max-width: 413px) {
  .page-hero--plates .page-hero__title { font-size: clamp(2.625rem, 13.5vw, 3.25rem); }
}
/* The homepage's 32px title→lead and 57px lead→buttons steps, applied to the
   interior heroes. Scoped to .page-hero__grid on purpose: __title, __lead and
   __actions are also reused inside .aud-hero on the 13 audience pages, which
   have their own rhythm and must not move. Only these 8 pages use the grid. */
.page-hero__grid .page-hero__lead { margin-top: 32px; }
.page-hero__grid .page-hero__actions { margin-top: 57px; }
/* Two-column: the art holds its full 580px and never shrinks with the column.
   Below ~1400px the 1fr column is narrower than that, so the art overflows its
   own track to the left and overlaps the copy — deliberate, and the reason the
   copy is raised above it. Right edge still lands on the container's rail. */
/* Figma (Desktop_Platform 1734:17504, Desktop_Security & Compliance 1746:22719)
   draws the art in a 580x360 box, object-cover. Matching the box exactly — and
   letting the image fill it rather than letterboxing — is the same correction
   applied to the homepage hero, which rendered ~12% under the design while it
   used `contain` inside a padded box. */
.page-hero__art { justify-self: end; width: 580px; }
.page-hero__art img {
  display: block;
  width: 100%;
  aspect-ratio: 580 / 360;
  object-fit: cover;
}
/* Any remaining inline-<svg> art keeps its intrinsic ratio. */
.page-hero__art > svg { display: block; width: 100%; height: auto; }
/* Copy above the art, so the medium-range overlap keeps the headline readable. */
.page-hero__copy { position: relative; z-index: 1; }
/* Centred variant, for pages with no hero art (legal, company, contact) */
.page-hero--center .page-hero__grid { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; }
.page-hero--center .page-hero__title, .page-hero--center .page-hero__lead { max-width: 24em; }
.page-hero--center .page-hero__actions { justify-content: center; }

/* Contact (Figma 2323:27003) is left-aligned rather than centred like the other
   art-less heroes, and its title is 49px — between --fs-h1-page (56) and an h2,
   on a 0.85 leading. Fluid so the design's tight pitch holds across the range,
   matching how .article-hero__title expresses the same 49px.
   Figma's rhythm here is 146px nav→title and only 40px title→form, so the hero
   drops its default 80px bottom padding and the form section supplies the 40. */
.page-hero--contact { padding: 146px 0 0; }
.page-hero--contact .page-hero__grid { grid-template-columns: minmax(0, 1fr); }
.page-hero--contact .page-hero__title { font-size: clamp(2.5rem, 3.4vw, 3.0625rem); line-height: 0.85; }
@media (max-width: 991px) {
  .page-hero__grid { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, the art leads and always fills the column — no cap, no overlap. */
  .page-hero__art { justify-self: stretch; width: 100%; order: -1; }
  /* Stretching the box would otherwise hold the 360px height while the width
     grows, skewing the crop. Mobile_Platform (1734:18034) is 360x223.45 — the
     same 580/360 ratio as desktop — so the ratio is kept at every width and
     only the scale changes. */
  .page-hero__art img { height: auto; }
  /* Stacked, the lead→buttons step tightens to 40px, as the homepage's does. */
  .page-hero__grid .page-hero__actions { margin-top: 40px; }
}
/* Phones: from Figma Mobile_Platform (1734:18026) and Mobile_Security & Compliance
   (1763:33843). Both draw the art at y=160 under the 60px nav → 100px above it,
   full-width in the 360px column (not a 260px thumbnail), then 40px to the
   headline, 40px title→lead, and 60px lead→buttons. Everything is scoped to
   :has(.page-hero__art) — only platform and security-compliance have art; the
   other 6 .page-hero pages are text-only (legal, contact, company, insights, 404)
   and must keep their existing rhythm. */
@media (max-width: 767px) {
  .page-hero:has(.page-hero__art) { padding-top: 100px; }
  .page-hero:has(.page-hero__art) .page-hero__grid { row-gap: 40px; }
  .page-hero:has(.page-hero__art) .page-hero__grid .page-hero__lead { margin-top: 40px; }
  .page-hero:has(.page-hero__art) .page-hero__grid .page-hero__actions { margin-top: 60px; }
}

/* ---- Struck-through "before → after" card (platform's challenge grid) ----
   The old copy pairs a struck phrase with what replaces it. */
.swap-card { display: flex; flex-direction: column; gap: 8px; }
.swap-card__was { color: var(--fg-muted); text-decoration: line-through; font-size: var(--fs-body); line-height: var(--lh-body); }
.swap-card__now { font-size: var(--fs-h3); line-height: 1.1; letter-spacing: var(--ls-h3); font-weight: 500; }

/* ---- Stat: a large figure over a label (audience pages) ---- */
.stat { display: flex; flex-direction: column; gap: 8px; }
.stat__num { font-size: var(--fs-h2); line-height: 1; letter-spacing: var(--ls-h2); font-weight: var(--fw-h2); color: var(--accent); }
.stat__label { font-size: var(--fs-small); line-height: var(--lh-small); color: var(--fg-body); max-width: 18rem; }

/* ---- Team grid (company) — from Figma Desktop_Company (1753:29609):
   dark cards with a single tick at top-left and bottom-right, a small square
   grayscale portrait top-left, the LinkedIn badge top-right, then name and role.
   Each card is a single link to the person's LinkedIn; hover/focus turns the
   ticks, name, role and badge mint and brings the portrait to full color. ---- */
/* --col-gap is proportional, so as a row gap it tightens to ~5px on phones just
   as the cards stack — see the note on the card rows below. The portraits would
   sit almost flush, so the rows keep the same 40px the other wrapped grids use. */
.team-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--col-gap); row-gap: 40px; }
@media (max-width: 900px) { .team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .team-grid { grid-template-columns: 1fr; } }
.team-card {
  position: relative;
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 32px;
  background: var(--n-black-2);
  min-height: 232px;
  /* The whole card is the LinkedIn link, so reset the anchor defaults. */
  color: inherit; text-decoration: none;
}
.team-card:hover, .team-card:focus-visible { --bracket: var(--accent); }
/* The wrapper is the fixed 96x96 frame that clips, so the portrait can zoom
   inside it like every other clickable image — see the shared zoom block. */
.team-card__img-wrap {
  display: block; width: 96px; height: 96px;
  margin-bottom: 20px;
}
.team-card__img {
  width: 100%; height: 100%; object-fit: cover; object-position: top center;
  filter: grayscale(1);
}
/* The grayscale and zoom transitions are declared together in the shared
   clickable-imagery block below, since one `transition` shorthand owns both. */
.team-card:hover .team-card__img, .team-card:focus-visible .team-card__img { filter: grayscale(0); }
/* Figma draws these at the section-headline size (39px/39px, -0.06em) rather
   than a card-scale 28px, so use the --fs-h2 token instead of a local value. */
.team-card__name {
  font-size: var(--fs-h2); line-height: var(--lh-h2); letter-spacing: var(--ls-h2); font-weight: 400;
  transition: color var(--dur) var(--ease-out);
}
.team-card:hover .team-card__name, .team-card:focus-visible .team-card__name { color: var(--accent); }
/* Role lines are 20px/20px white in Figma, not 14px grey. Every role — the lead
   card included — sits white at rest and turns green on hover alongside the
   name, so the CEO card matches its neighbours until you interact with it. */
.team-card__role {
  font-size: var(--fs-h3); line-height: var(--lh-h3); letter-spacing: var(--ls-h3); color: var(--fg);
  transition: color var(--dur) var(--ease-out);
}
.team-card:hover .team-card__role, .team-card:focus-visible .team-card__role { color: var(--accent); }
/* LinkedIn badge, pinned to the card's top-right. Decorative: the card itself
   carries the href, so this is a <span> and must not trap pointer events.

   Framed with .tick-tl/.tick-br rather than .bracketed: as a grid container, the
   static <span class="corners"> that .bracketed needs became a second grid *row*,
   splitting the 36px box into 28px + 8px and riding the glyph 8px high. The tick
   utility draws on pseudo-elements, so the box stays a single centred cell. */
.team-card__link {
  position: absolute; top: 32px; right: 32px;
  width: 52px; height: 36px; display: grid; place-items: center;
  color: var(--n-white);
  pointer-events: none;
  transition: color var(--dur) var(--ease-out);
}
/* .tick-* paints the tick with --fg, which is white on this dark card. Both the
   card and the badge should track --bracket instead, which is what the cards
   already retint to mint on hover. */
.team-card::before, .team-card::after,
.team-card__link::before, .team-card__link::after {
  background: var(--bracket);
  transition: background var(--dur) var(--ease-out);
}
/* A tick-framed glyph now, so hover tints the stroke and glyph rather than
   filling a solid chip. */
.team-card:hover .team-card__link, .team-card:focus-visible .team-card__link { color: var(--accent); }
.team-card__link svg { width: 20px; height: 20px; }

/* ---- Section boundary hairline.
   Adjacent dark bands otherwise run together with no visible seam. Figma's
   Desktop_Company draws a 1px rule at each boundary.

   Opt-in via a class rather than a `.page-hero + .section` sibling selector,
   because that shape also matches platform.html and security-compliance.html,
   whose boundaries are deliberately seamless.

   Uses a pseudo-element so the rule stops on the rails: .section's own
   background is full-bleed, so border-top would run edge to edge. ---- */
.section--ruled { position: relative; }
.section--ruled::before {
  content: ""; position: absolute; left: var(--rail); right: var(--rail); top: 0;
  border-top: 1px solid var(--n-gray-700);
}
.section--ruled-soft::before { border-top-color: var(--n-gray-600); }

/* ---- Prose (legal pages, blog posts): the one place long-form copy runs.
   Line-height opens up here — the tokens' 1.0 pitch is for display type. ---- */
/* Long-form copy keeps its own 1.65 leading — looser than --lh-body-multi
   (1.45) on purpose, because an article is read continuously rather than
   scanned. Only the tracking is eased. */
.prose { max-width: 46rem; margin-inline: auto; font-size: var(--fs-body); line-height: 1.65; letter-spacing: var(--ls-reading); color: var(--fg-body); }
.prose > * + * { margin-top: 1.1em; }
.prose h2 { font-size: var(--fs-h2); line-height: 1.05; letter-spacing: var(--ls-h2); font-weight: var(--fw-h2); color: var(--fg); margin-top: 2.2em; }
.prose h3 { font-size: var(--fs-h3); line-height: 1.2; letter-spacing: var(--ls-h3); font-weight: 500; color: var(--fg); margin-top: 1.8em; }
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose a { color: var(--fg); text-decoration: underline; text-underline-offset: 0.15em; text-decoration-color: var(--line-strong); }
.prose a:hover { text-decoration-color: currentColor; }
.prose ul, .prose ol { padding-left: 1.25em; }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li + li { margin-top: 0.5em; }
.prose li::marker { color: var(--fg-muted); }
.prose strong { color: var(--fg); font-weight: 600; }
.prose table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.prose th, .prose td { text-align: left; padding: 12px 16px; border: 1px solid var(--line); vertical-align: top; }
.prose th { font-weight: 600; color: var(--fg); }

/* ---- In-body figure (blog art / stat cards) ----
   Images ship at their natural ratio, so no aspect-ratio or crop here; the
   width cap comes from .article-body__copy > * (53.75rem). */
.prose-figure { margin: 2.2em 0; }
.prose-figure img { display: block; width: 100%; height: auto; }
.prose-figure figcaption,
.prose-embed figcaption {
  margin-top: 12px;
  font-size: var(--fs-small);
  letter-spacing: var(--ls-body);
  color: var(--fg-muted);
}

/* ---- In-body video embed ----
   The iframe is absolutely positioned inside a 16/9 frame so the player scales
   with the measure instead of carrying fixed pixel dimensions. */
.prose-embed { margin: 2.2em 0; }
.prose-embed__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--n-black-2);
}
.prose-embed__frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}

/* Long documents scroll their wide tables rather than the page */
.prose__scroll { overflow-x: auto; }

/* ---- Article hero (blog posts, press releases) ----
   Figma "Desktop_Editorial Page - Blog" (692:8307): content runs x:40..1400 —
   --frame, not the centred --container measure, so the article is its own
   full-bleed column — with the copy bottom-aligned left and the art filling the
   right, flush to the top of the byline bar below it.

   Proportionally that is a ~48/52 split with the art squared off against the
   right frame edge, so the columns are expressed as fractions and the art
   takes a fixed aspect rather than a pixel height. ---- */
/* The byline bar deliberately overflows the content box to the right (see
   .article-byline below). Nothing else in an article does, so clip here rather
   than adding a global overflow-x rule — otherwise the bleed produces a
   horizontal scrollbar on every post page. */
.article-wrap {
  width: 100%;
  overflow-x: clip;
  /* Flush to the rails at every width: the hero art and the body ground meet the
     frame line rather than sitting --text-inset inside it. Copy that needs a
     readable measure re-adds its own inset (.article-hero__copy, .article-body__copy). */
  padding-inline: var(--rail);
  box-sizing: border-box;
}

.article-hero {
  display: grid;
  grid-template-columns: minmax(0, 690fr) minmax(0, 670fr);
  gap: 40px;
  align-items: end;
  padding-top: 0;
}
/* The hero art butts straight up against the nav: the article section drops the
   shared --section-pad top so there is no gap between the two. Bottom padding
   is untouched, and the :has() keeps this to article pages only. */
.section:has(> .article-wrap > .article-hero) { padding-top: 0; }
/* The art's bottom edge is the byline bar's top edge in the design
   (both at y:672), so the hero sits flush on the bar with no gap. */
.article-hero + .article-body { margin-top: 0; }
/* The copy column is bottom-aligned against the art: in the design the title
   baseline sits near the foot of the image, not at the top of the block.

   The wrap is flush to the rail at every width, so the copy column re-adds the
   text inset — the back-link, title and dateline stay off the line (Figma
   692:8465 draws them at x:60) while the art beside them runs flush to it. */
.article-hero__copy { display: flex; flex-direction: column; justify-content: space-between; align-self: stretch; padding: 20px 20px 40px var(--text-inset); }
.article-hero__title {
  /* 49px/49px, ls -2.94 at 1440 — fluid so it holds the design's tight
     one-to-one pitch across the range. */
  font-size: clamp(2rem, 3.4vw, 3.0625rem);
  line-height: 1;
  letter-spacing: -0.06em;
  font-weight: 400;
  color: var(--fg);
  margin-top: auto;
  max-width: 13em;
}
.article-hero__dateline { font-size: var(--fs-small); color: var(--fg-muted); margin-top: 20px; }
/* A press release has no hero art. Without the image column there is nothing
   to bottom-align against, so the block sits at its natural height and the
   title keeps its measure instead of running the full width. */
.article-hero:not(:has(.article-hero__art)) { grid-template-columns: 1fr; padding-bottom: 60px; }
.article-hero:not(:has(.article-hero__art)) .article-hero__copy { gap: 40px; padding-bottom: 0; }
.article-hero:not(:has(.article-hero__art)) .article-hero__title { margin-top: 0; }

/* "‹ ALL POSTS": 13px small caps with the design's tracking */
.article-hero__back {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg);
  align-self: flex-start;
}
.article-hero__back:hover { color: var(--accent); }
.article-hero__back-chev { width: 10px; height: 10px; transform: rotate(180deg); }

/* The art is a plain block flush to the frame — no border, unlike the old
   header treatment. */
/* The cover art is square and is shown square. The design's own art box is
   670x600; if per-post art is ever supplied at that ratio, change the one
   `aspect-ratio` line below back to `670 / 600`. */
/* The wrap is flush to the rail now, so the art needs no negative pull — it
   already meets the line. */
.article-hero__art { align-self: end; line-height: 0; }
.article-hero__img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  /* The design's art box is 600px tall; the square cover is capped to it
     so the header keeps the design's proportions instead of growing to the
     column width. */
  max-height: 600px;
  object-fit: cover;
}

@media (max-width: 900px) {
  /* Mobile (Figma 732:47542) stacks: back-link and title, then the art full
     width above the byline bar. */
  .article-hero { grid-template-columns: 1fr; gap: 32px; padding-top: 0; }
  .article-hero__title { max-width: none; }
  .article-hero__art { width: 100%; }
  .article-hero__img { aspect-ratio: 1 / 1; max-height: none; height: auto; }
  .article-hero__copy { padding: 20px 20px 0 var(--text-inset); gap: 40px; }
  /* Flush to the rails there is no ground left of the copy panel to show. */
  .article-body__copy.prose { margin-left: 0; }
}

/* ---- Article body: byline bar + copy on a shared grey ground ----
   Measured from Figma "Insights - Index & Editorial Page" (node 732:52291
   desktop, 732:47776 mobile; the ground is rect 732:47800).

   The #373737 rectangle starts at the top of the byline bar and runs the full
   height of the copy. The copy sits on its own dark panel inset 20px from the
   left, so what stays visible of the grey is the bar plus a rail down the
   article's left edge — bar and rail are one continuous shape.

   These two neutrals are fixed rather than the semantic --bg/--fg pair, which
   flips under the .theme-* classes. ---- */
.article-body { background: var(--n-gray-800); margin-top: 40px; }
/* Flush into the subscribe band below; the top padding stays as the gap under
   the nav. */
.section:has(> .article-wrap) { padding-bottom: 0; }
/* The panel spans everything right of the rail — .prose's own 46rem cap would
   otherwise leave the grey showing down the right-hand side too, which the
   design does not do. The measure is restored on the text inside instead. */
.article-body__copy.prose {
  background: var(--bg);
  max-width: none;
  margin: 0 0 0 var(--article-rail, 20px);
  /* Figma: the copy runs x:300..1160 inside the x:40..1400 ground — a 860px
     measure centred in the panel rather than set against its left edge. The
     padding is what centres it; the measure caps it on wider viewports. */
  padding: 80px 40px;
}
.article-body__copy > * { max-width: 53.75rem; margin-inline: auto; }

@media (max-width: 640px) {
  /* .prose raises the specificity on the base rule, so match it here too or the
     desktop 80/40 padding wins. */
  .article-body__copy.prose { padding: 32px 20px; }
}

/* ---- Sticky byline bar ----
   .article-body is the sticky container, so the bar releases at the end of the
   article instead of following the reader into the bands below. */
.article-byline {
  position: sticky;
  top: var(--nav-h);
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  /* Figma: 16px block, 60px right, 20px left — the left inset lines up the
     avatar with the bar edge the way the design has it. */
  padding: 16px 60px 16px 20px;
  background: var(--n-gray-800);
  color: var(--n-white);
  /* The bar bleeds past the wrap's padding to the screen edge (Figma models this
     as a deliberate overflow, node 732:57823); .article-wrap's overflow-x: clip
     keeps it from scrolling the page. */
  margin-right: calc(var(--rail) * -1);
  padding-right: calc(60px + var(--rail));
}
/* Past --stage-max the rail keeps growing while the negative margin above is
   capped, leaving the grey short of the edge on very wide screens. This runs the
   background the rest of the way.

   Scoped to wide viewports on purpose: below 768 the bar is static (see the
   767 block) so it escapes .article-wrap's clip, and an unclipped bleed would
   scroll the page. It is only ever needed once the stage stops growing. */
@media (min-width: 1814px) {
  .article-byline::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 100%;
    width: 50vw;
    background: var(--n-gray-800);
  }
}
.article-byline__author { display: flex; align-items: center; gap: 20px; min-width: 0; }
.article-byline__img {
  width: 50px; height: 50px;
  flex: none;
  object-fit: cover;
  background: var(--n-gray-200); /* the design's placeholder square, if art is missing */
}
/* 20px/20px with the design's -1.2px tracking, expressed as a ratio so it
   holds if the size changes. */
.article-byline__name {
  font-size: 1.25rem;
  line-height: 1.25;
  letter-spacing: -0.06em;
  color: rgb(255 255 255 / 0.8);
  min-width: 0;
  /* "Written by <name>" is a single unit — it should never break across two
     lines. The author row is allowed to shrink instead (min-width:0 above), and
     if the viewport is genuinely too narrow the ≤767px rule below stacks the
     byline, giving the name the full width. */
  white-space: nowrap;
}
/* Figma wraps the share cluster in its own px-[20px] frame (732:52298), but that
   sits *inside* the bar's 60px right padding — so the last glyph lands 80px from
   the bar's edge, which reads as a gap rather than as alignment. Dropping the
   inner padding lets the cluster end flush with the content box, i.e. the 60px
   the bar actually specifies. */
.article-byline__share { display: flex; align-items: center; gap: 20px; flex: none; }
/* A press release has no named author: the issuer stands in its place, and
   the left inset matches the avatar's so the two bars line up. */
.article-byline--share-only { justify-content: space-between; padding-left: 20px; }
/* Figma desktop 732:52294 and mobile 732:57823 both draw a 36px box per link.
   The copy-link glyph is 20px inside it (x=8,y=8 — a 20px icon in a 36px hit
   target); the LinkedIn mark fills the box at 36px. Identical at every screen
   size, so no breakpoint overrides these. */
.article-byline__link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  color: var(--n-white);
  border-radius: var(--radius-card);
  transition: color var(--dur) var(--ease-out);
}
/* Mint on hover, matching every other interactive element on the site
   (.link, .article-hero__back, .team-card, .media-card h3 a). */
.article-byline__link:hover { color: var(--accent); }
.article-byline__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.article-byline__link svg { width: 20px; height: 20px; }
/* The LinkedIn mark is filled art rather than a line icon, and Figma runs it to
   the full 36px box — at 20px it read noticeably lighter than the link glyph. */
.article-byline__link--in svg { width: 36px; height: 36px; }

/* js/editorial.js swaps the glyph for this "Copied" flash on click. */
.article-share__copied {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

/* Pinned, the bar would eat ~82px of a short phone viewport for the whole
   article — and it stacks into two rows at 640px. */
@media (max-width: 767px) {
  .article-byline { position: static; }
}

/* Figma mobile (node 732:57823): the bar stacks into a single centred column —
   author row above, share row below, 20px apart. Figma models this as an inner
   wrapper with items-center inside an items-start parent; with no such wrapper
   in the markup we centre the two children directly, which resolves the same.
   Only the avatar changes size; the share cluster is identical to desktop. */
@media (max-width: 640px) {
  .article-byline { flex-direction: column; align-items: center; gap: 20px; }
}
/* The 36px avatar is measured on Figma's ~400px mobile canvas (732:57822 is
   399px wide). Between 400 and 640px the bar is much wider than that, so 36px
   reads undersized — the 50px desktop avatar holds until we're actually at
   phone width. */
@media (max-width: 420px) {
  .article-byline__img { width: 36px; height: 36px; }
}

/* ---- Form (contact) — inputs inherit the bracket language via .bracketed ---- */
.form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; max-width: 46rem; }
.form__row--full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 8px; }
.field__label { font-size: var(--fs-small); color: var(--fg-muted); }
.field__input {
  width: 100%; height: 44px; padding: 0 14px;
  background: transparent; color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: var(--radius-card);
  font-family: var(--font-sans); font-size: var(--fs-body); letter-spacing: var(--ls-body);
  transition: border-color var(--dur) var(--ease-out);
}
textarea.field__input { height: auto; min-height: 132px; padding: 12px 14px; line-height: 1.5; resize: vertical; }
.field__input::placeholder { color: var(--fg-muted); }
.field__input:focus { outline: none; border-color: var(--accent); }
.field__error { font-size: var(--fs-small); color: var(--n-danger); }
.field__input[aria-invalid="true"] { border-color: var(--n-danger); }

/* Submit carries the bracket frame like the site's other CTAs. It rests in the
   body colour (Figma 2323:27169 draws white text on white brackets) and only
   picks up the accent on hover/focus, so green reads as the interaction. */
.btn--submit { color: var(--fg); --bracket: var(--fg); transition: color var(--dur) var(--ease-out); }
.btn--submit:hover, .btn--submit:focus-visible { color: var(--accent); --bracket: var(--accent); }
.btn--submit[disabled] { opacity: 0.6; cursor: not-allowed; }
.btn--submit[disabled]:hover { color: var(--fg); --bracket: var(--fg); }

/* ---- Contact form (Figma 2323:27152) ----
   The contact form is tighter than the base .form: 36px fields on an 8px column
   / 16px row rhythm, brighter hairline borders, and no visible labels — the
   placeholder carries the name, so the <label>s are .visually-hidden rather
   than removed. Scoped to .form--contact because .field__input is shared with
   the newsletter signups on insights.html and news.html. */
.form--contact { column-gap: 8px; row-gap: 16px; max-width: 785px; }
/* Figma 2323:27152 draws the inputs and their placeholders in full white, not
   the muted grey the base .field__input inherits, so the fields read as bright
   hairline boxes. */
.form--contact .field__input {
  height: 36px;
  padding: 8px 16px;
  border-color: rgb(255 255 255 / 0.8);
  border-radius: 0;
  color: var(--n-white);
}
.form--contact .field__input::placeholder { color: var(--n-white); opacity: 1; }
.form--contact .form__note p { font-size: var(--fs-body); letter-spacing: var(--ls-body); color: rgb(255 255 255 / 0.8); }

/* Figma 2323:27167 stacks the note and Submit 40px apart, where the field rows
   above are 16px. The grid row-gap already supplies 16px, so the button row
   tops up the remaining 24px. */
.form--contact .form__note + .form__row--full { margin-top: 24px; }

/* ---- Contact section rhythm (Figma 2323:27003) ----
   The design runs 146 nav→title, 40 title→form, 120 form→mail, 120 mail→band,
   so the form section replaces the standard 100/100 --section-pad: the hero
   already carries the space above, and the bottom 120 is the mail link's own
   margin so the section closes tight on the band.
   The 48 top reads as Figma's 40: the title sits on a 0.85 leading, so its box
   crops ~8px tighter than the 42px text block Figma measures from. */
.section--contact { padding-block: 48px 120px; }
.contact__mail { margin-top: 120px; }
@media (max-width: 767px) {
  .page-hero--contact { padding-top: 80px; }
  .section--contact { padding-block: 32px 64px; }
  .contact__mail { margin-top: 64px; }
}

/* ---- Section variant: a plain centred header (no eyebrow/split row) ---- */
.section-head--simple { display: flex; flex-direction: column; align-items: center; gap: 20px; text-align: center; margin-bottom: var(--head-gap); }
.section-head--simple .h2 { max-width: var(--text-max); }
.section-head--simple .body { max-width: 34em; }

/* ---- Utility: a rule that spans the container ---- */
.hr { height: 1px; background: var(--line); border: 0; margin: 0; }


/* ---- Mega-menu and mobile nav — extends the preview ----
   The preview's nav ships plain links and a stub toggle. The live site's IA
   needs dropdown panels and a mobile menu, built here from the same tokens. */
.nav__group { position: relative; display: inline-flex; align-items: center; }
/* Stretch the group to the full bar height so the panel below it starts at the
   bar's bottom edge, and so the pointer can travel from link to panel without
   crossing a dead gap. The links stay optically centred. */
.nav__links { align-items: stretch; }
.nav__group { min-height: var(--nav-h); }
.nav__group > .nav__link { cursor: pointer; }
.nav__group.is-open > .nav__link { color: var(--n-white); }
.nav__group.is-open > .nav__link svg { transform: rotate(180deg); }
.nav__link svg { transition: transform var(--dur) var(--ease-out); }

/* Geometry from Figma "Top Navigation bar v2.1" (1755:7976): the panel ground is
   dark-grey-2, each column is a fixed 288px block of --n-black with 24px padding,
   and the panel is square-cornered. */
/* The panel's left edge sits on the trigger's left edge (Figma: both at x=516
   on the 1440 frame), so it opens from the item rather than centring under it. */
.menu {
  /* Figma 1755-7976 puts the panel's top flush with the bar's bottom edge (bar
     y=0 h=60, panel y=59) rather than overlapping it. .nav__group stretches to
     the full bar height above, so `top: 100%` lands on that edge. */
  position: absolute; left: 0;
  /* Figma 1755-7976 hangs the panel off the bar's bottom edge (bar y=0 h=60,
     panel y=59) rather than overlapping it. The trigger is shorter than the bar
     and centred in it, so `top: 100%` opens mid-bar; anchoring to the bar's own
     bottom via the stretched .nav__group hit-area lands it on the edge. */
  top: 100%;
  background: var(--n-gray-600);
  padding: 0;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur) var(--ease-out), visibility var(--dur);
  z-index: 60;
}
.nav__group.is-open .menu { opacity: 1; visibility: visible; pointer-events: auto; }
/* A panel only flips to right-aligned when left-aligning would push it off the
   viewport; JS sets this, so narrow panels near the right edge stay aligned to
   their trigger like the rest. */
.nav__group.menu-flip .menu { left: auto; right: 0; }
/* .menu__inner carries .container in the markup, so it picks up the nav's frame
   padding from `.nav .container`. Null it out here (matching that selector's
   specificity) so the two 288px columns make the panel exactly 576 wide. */
.nav .menu__inner { max-width: none; padding: 0; display: flex; gap: 0; }
/* Each column is a 288px block of the grey ground; the rows inside it are the
   black cards (Figma 1755:8001 — column #555, row #181818).

   Width is in em, not the Figma 288px, so the card grows with the type it has to
   hold: 288px at the 16px base (exact Figma parity at 1440) → 324px at 1920.
   With a fixed px width the descriptions wrapped to a third line as --fs-small
   climbed to 16px and overflowed the row. */
.menu__col { display: flex; flex-direction: column; gap: 16px; width: 18em; flex: none; padding: 24px; background: var(--n-gray-600); box-sizing: border-box; }
/* Figma 1755-7976 gives the two columns different grounds: the first (Insurance)
   sits on #555, the second (Financial Services) on #404040. */
.menu__col + .menu__col { background: var(--n-gray-650); }
/* The single-column panels (Platform, Insights) are tighter than the Industries
   mega-menu: Figma 1756-9737 / 1755-8009 give them a 256px panel with an 8px
   pad and an 8px row gap, against the mega-menu's 360px column and 24px pad. */
/* 256px at the 16px base, same em treatment as the mega-menu column above. */
.menu:not(.menu--wide) .menu__col { width: 16em; padding: 8px; gap: 8px; }
.menu:not(.menu--wide) .menu__list { gap: 8px; }
/* Rows in these panels size to their content rather than the mega-menu's fixed
   66px: Figma 1755-8009 draws the one-line "News" row at 53px. */
.menu:not(.menu--wide) .menu__item { height: auto; min-height: 50px; }
.menu__head {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  font-size: 1rem; line-height: 13px; font-weight: 500; letter-spacing: 0;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.5);
  padding: 0 0 16px;               /* 13px text, rule 16px beneath (Figma y=24 -> y=53) */
  border-bottom: 1px solid rgb(255 255 255 / 0.15);
  width: 100%;
}
.menu__head svg { width: 12px; height: 12px; }
.menu__head:hover { color: var(--n-white); }
/* Figma shows each menu row as a thumbnail beside the title and description. */
/* Row: 240x66 with an 8px inset, a 50px thumbnail, 16px gap (Figma 1755:8001). */
.menu__item {
  position: relative;
  /* Top-aligned, not centred: rows wrap to two and three lines, and a centred
     thumbnail/icon then floats against the middle of the copy block instead of
     lining up with the title it belongs to. */
  display: flex; align-items: flex-start; gap: 16px;
  padding: 8px; min-height: 66px; width: 100%;
  background: var(--n-black);
  /* Figma 1755-7976 cuts each of the four corners at 45deg rather than rounding
     them, so the card ground itself is chamfered. */
  --chamfer: 5px;
  clip-path: polygon(
    var(--chamfer) 0, calc(100% - var(--chamfer)) 0,
    100% var(--chamfer), 100% calc(100% - var(--chamfer)),
    calc(100% - var(--chamfer)) 100%, var(--chamfer) 100%,
    0 calc(100% - var(--chamfer)), 0 var(--chamfer)
  );
  /* Brackets are tinted with --bracket (like .bracketed) rather than a white
     SVG dimmed by opacity, so the colour is themable and matches the rest of
     the site's brackets exactly. */
  --bracket: var(--n-gray-400);
  transition: background var(--dur) var(--ease-out);
}
/* An L bracket hugs each corner, following the chamfer. Brightens on hover;
   the row ground does not change. */
.menu__item::before,
.menu__item::after {
  content: ""; position: absolute; left: 0; right: 0; height: 14px;
  pointer-events: none;
  background: var(--bracket);
  /* Stroke centred on 0.5 so the 1px line lands on the card's box edge. The
     old artwork drew at 1, holding the brackets a pixel inside the corner. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M.5 13.5V5.7L5.7 .5H13.5' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat left top / 14px 14px,
                url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cg transform='translate(14,0) scale(-1,1)'%3E%3Cpath d='M.5 13.5V5.7L5.7 .5H13.5' stroke='%23000' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E") no-repeat right top / 14px 14px;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M.5 13.5V5.7L5.7 .5H13.5' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat left top / 14px 14px,
                url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cg transform='translate(14,0) scale(-1,1)'%3E%3Cpath d='M.5 13.5V5.7L5.7 .5H13.5' stroke='%23000' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E") no-repeat right top / 14px 14px;
  transition: background-color var(--dur) var(--ease-out);
}
/* the right-hand copy mirrors horizontally; the ::after pair flips vertically */
.menu__item::before { top: 0; }
.menu__item::after  { bottom: 0; transform: scaleY(-1); }
.menu__item:hover, .menu__item:focus-visible { --bracket: var(--n-white); }
/* The thumbnail is chamfered the same way (Figma 1755-7976) — the image itself
   is clipped, not just outlined. */
.menu__thumb-wrap { position: relative; display: block; width: 50px; height: 50px; flex: none; --t-bracket: var(--n-gray-400); }
.menu__thumb-wrap::before,
.menu__thumb-wrap::after {
  content: ""; position: absolute; left: 0; right: 0; height: 11px;
  pointer-events: none;
  background: var(--t-bracket);
  /* Native 11x11 viewBox: the old 14x14 artwork was scaled down to 11px, which
     shrank the 1px stroke to 0.79px and rendered soft. Stroke on 0.5 puts the
     line on the thumbnail's edge instead of a pixel inside it. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 11 11' fill='none'%3E%3Cpath d='M.5 10.5V4.9L4.9 .5H10.5' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat left top / 11px 11px,
                url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 11 11' fill='none'%3E%3Cg transform='translate(11,0) scale(-1,1)'%3E%3Cpath d='M.5 10.5V4.9L4.9 .5H10.5' stroke='%23000' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E") no-repeat right top / 11px 11px;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 11 11' fill='none'%3E%3Cpath d='M.5 10.5V4.9L4.9 .5H10.5' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat left top / 11px 11px,
                url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 11 11' fill='none'%3E%3Cg transform='translate(11,0) scale(-1,1)'%3E%3Cpath d='M.5 10.5V4.9L4.9 .5H10.5' stroke='%23000' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E") no-repeat right top / 11px 11px;
  transition: background-color var(--dur) var(--ease-out);
}
.menu__thumb-wrap::before { top: 0; }
.menu__thumb-wrap::after  { bottom: 0; transform: scaleY(-1); }
/* the bracket sits above the image, which is clipped beneath it */
.menu__thumb-wrap::before, .menu__thumb-wrap::after { z-index: 1; }
.menu__thumb { position: relative; z-index: 0; display: block; }
.menu__item:hover .menu__thumb-wrap,
.menu__item:focus-visible .menu__thumb-wrap { --t-bracket: var(--n-white); }
/* Thumbnails per Figma (node 1755-8399): a 50px rounded square at 80% opacity,
   image cover-cropped and centred. */
.menu__thumb {
  width: 50px; height: 50px; flex: none;
  object-fit: cover; object-position: center;
  /* clipped corners, matching the card (Figma 1755-7976) */
  --t-chamfer: 4px;
  clip-path: polygon(
    var(--t-chamfer) 0, calc(100% - var(--t-chamfer)) 0,
    100% var(--t-chamfer), 100% calc(100% - var(--t-chamfer)),
    calc(100% - var(--t-chamfer)) 100%, var(--t-chamfer) 100%,
    0 calc(100% - var(--t-chamfer)), 0 var(--t-chamfer)
  );
  opacity: 0.8;
  background: var(--n-green-700);
  transition: opacity var(--dur) var(--ease-out);
}
/* Platform/Insights use a 30px line icon on no tile (Figma 1756-9737, 1755-8009),
   not the 50px photographic thumbnail the Industries menu uses. Dimmed at rest
   like the label beside it, full strength on hover (shared .menu__thumb rule). */
.menu__thumb--icon {
  width: 30px; height: 30px;
  object-fit: contain;
  border-radius: 0;
  opacity: 0.6;
  background: none;
}
.menu__text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.menu__list { display: flex; flex-direction: column; gap: 8px; width: 100%; }
/* line-height in a unit that tracks the font. The Figma 13px pitch is right for
   the one-line labels it was measured from, but these descriptions wrap to two
   and three lines — at 16px the solid pitch overflowed the card. */
.menu__item-title { font-size: var(--fs-body); line-height: 1.25; letter-spacing: -0.06em; color: var(--n-gray-400); transition: color var(--dur) var(--ease-out); }
.menu__item-sub { font-size: var(--fs-small); line-height: 1.25; color: var(--n-gray-400); transition: color var(--dur) var(--ease-out); }
/* Hover (Figma 1755-7976): title goes white and the thumbnail to full opacity.
   The description goes with the title — leaving it grey read as the row only
   half lighting up. */
.menu__item:hover .menu__item-title,
.menu__item:focus-visible .menu__item-title,
.menu__item:hover .menu__item-sub,
.menu__item:focus-visible .menu__item-sub { color: var(--n-white); }
.menu__item:hover .menu__thumb,
.menu__item:focus-visible .menu__thumb { opacity: 1; }
.menu__item[aria-current="page"] .menu__item-title, .menu__head[aria-current="page"] { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .menu, .nav__link svg, .menu__item { transition: none; } }

/* Mobile panel: a full-height sheet under the bar */
.nav__mobile {
  position: fixed; inset: var(--nav-h) 0 0;
  background: var(--n-black);
  border-top: 1px solid var(--n-gray-800);
  overflow-y: auto; overscroll-behavior: contain;
  z-index: 45;
}
.nav__mobile-inner { padding: 24px var(--gutter) 64px; display: flex; flex-direction: column; gap: 4px; }
/* Figma's mobile sheet has no rules between the closed rows (1755:8309). */
.acc { border-bottom: 0; }
.acc__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 0; cursor: pointer; list-style: none;
  font-size: var(--fs-h3); letter-spacing: var(--ls-h3); color: var(--n-white);
}
.acc__head::-webkit-details-marker { display: none; }
.acc__head svg { width: 10px; height: 10px; color: var(--n-gray-400); transition: transform var(--dur) var(--ease-out); }
.acc[open] .acc__head svg { transform: rotate(180deg); }
/* The expanded submenu holds one .acc__group per desktop .menu__col, so the
   mobile sheet inherits the same two-tone alternation the mega-menu uses
   (Figma 1755:8327 — #555 tray, 6px pad, 4px gap; rows on #181818). Rows
   inherit .menu__item wholesale (66px, 8px pad, chamfer + corner brackets)
   rather than overriding the ground. */
.acc__body { display: flex; flex-direction: column; gap: 0; margin-bottom: 12px; }
.acc__group { display: flex; flex-direction: column; gap: 4px; padding: 6px; background: var(--n-gray-600); }
.acc__group + .acc__group { background: var(--n-gray-650); }
.acc__body .menu__list { gap: 4px; }
/* On the tray's grey ground the desktop rule reads correctly; --n-gray-800 was
   darker than the ground it sat on. */
.acc__body .menu__head { padding: 12px 0 8px; border-bottom: 1px solid rgb(255 255 255 / 0.15); margin-bottom: 4px; }
.acc__link { display: block; padding: 18px 0; font-size: var(--fs-h3); letter-spacing: var(--ls-h3); color: var(--n-white); }
/* Figma's mobile CTA is not the accent button the desktop bar uses: the label is
   full white and the brackets are --n-gray-400, on the sheet's own dark ground
   (Primary Button 1755:8628 inside the mobile menu 1755:8309 / 1755:8567). */
.nav__mobile-cta { align-self: flex-start; margin-top: 28px; --bracket: var(--n-gray-400); color: var(--n-white); }
body.nav-open { overflow: hidden; }
@media (min-width: 992px) { .nav__mobile { display: none; } }


/* ---- Insights carousel — extends the preview ----
   The preview shows a static two-up with pager arrows. The site has more posts
   than fit, so the same grid doubles as a scroll track when JS enhances it. */
.insights__track.is-carousel {
  display: grid;
  /* .cols-2 sets two explicit tracks; clear them so every card lands in an
     auto column of its own rather than the first two sharing the template. */
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - var(--col-gap-wide)) / 2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* the reveal transform on children would otherwise clip against the scroller */
  padding-bottom: 4px;
}
.insights__track.is-carousel::-webkit-scrollbar { display: none; }
.insights__track.is-carousel > * { scroll-snap-align: start; }
@media (max-width: 640px) { .insights__track.is-carousel { grid-auto-columns: 100%; } }
.media-card__link { display: block; width: 100%; }
.media-card h3 a:hover { color: var(--accent); }

/* ---- Clickable imagery: subtle zoom on hover ----
   The link is the fixed frame and clips; only the image inside moves, 1 → 1.05
   over 0.5s ease-out. Applies to media cards (industries, insights, explore
   rows), insight post tiles and the team portraits, so any future image link
   picks it up by reusing .media-card__link, .post-tile__img or a like frame.
   Hover-capable pointers only, so touch devices don't latch the zoom on tap. */
.media-card__link,
.post-tile__img,
.team-card__img-wrap { overflow: hidden; }
.media-card__link > img,
.post-tile__img > img { transition: transform 0.5s ease-out; }
/* The portrait already transitions its grayscale filter on hover, so name both
   properties here rather than overwriting that transition. */
.team-card__img-wrap > img { transition: filter var(--dur) var(--ease-out), transform 0.5s ease-out; }
@media (hover: hover) {
  .media-card__link:hover > img,
  .media-card:hover .media-card__link > img,
  .post-tile:hover .post-tile__img > img,
  .team-card:hover .team-card__img-wrap > img { transform: scale(1.05); }
}
/* Keyboard users reach the card through the heading/CTA link, not the image
   frame (which is aria-hidden), so mirror the zoom on those. The team card is
   itself the link, so it takes :focus-visible directly. */
.media-card:focus-within .media-card__link > img,
.post-tile:focus-within .post-tile__img > img,
.team-card:focus-visible .team-card__img-wrap > img { transform: scale(1.05); }
@media (prefers-reduced-motion: reduce) {
  .media-card__link > img,
  .post-tile__img > img,
  .team-card__img-wrap > img { transition: none; transform: none !important; }
}
.pager button[disabled] { opacity: 0.35; cursor: not-allowed; }


/* ---- Tag row and tick list — extends the preview ----
   Used by Security & Compliance for its model-selection criteria and the
   control-map responsibility list. */
.tag-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.tag-chip {
  display: inline-flex; align-items: center;
  padding: 6px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: var(--fs-small); line-height: 1; color: var(--fg-body);
}
.tick-list { display: flex; flex-direction: column; gap: 14px; align-self: center; }
.tick-list li { position: relative; padding-left: 18px; font-size: var(--fs-body); line-height: 1.3; color: var(--fg-body); }
.tick-list li::before {
  content: ""; position: absolute; left: 0; top: 0.45em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
}

/* Security & Compliance "control map" figure: Figma scales the responsibility
   diagram up ~15% against the generic .cols-2 width, and pairs it with a list
   whose rows are separated by hairlines rather than gapped like .tick-list —
   markers are small square outlines, and the first row is a caps eyebrow label
   ("defined together, per deployment") rather than a list item of its own. */
.control-map img { width: 115%; max-width: 115%; }
@media (max-width: 991px) { .control-map img { width: 100%; max-width: 100%; } }
/* The responsibility list beside it, per Figma 1751:25495. Every row is the same
   shape — a 10px square marker, a 16px gap, then the text, with 16px block
   padding and the two centred against each other. The first row is a caps label
   ("defined together, per deployment") and is distinguished only by its type
   (13px, uppercase, --n-gray-400) and by its marker being hollow; it is not a
   different layout. Hairlines sit only BETWEEN the four items — not above the
   first one, and not between the label and the list. */
.control-list { display: flex; flex-direction: column; }
.control-list li {
  display: flex;
  gap: 16px;
  align-items: center;
  padding-block: 16px;
  font-size: var(--fs-body);
  line-height: 20px;
  letter-spacing: var(--ls-body);
  color: var(--fg);
}
/* Markers are drawn rather than imaged: a 0.625px hairline border (Figma's value,
   which browsers round to a sub-pixel line) over a vertical --n-gray-600 →
   ground gradient, so each square fades into the section as it descends. */
.control-list li::before {
  content: "";
  flex: 0 0 auto;
  width: 10px; height: 10px;
  border: 0.625px solid var(--n-gray-600);
  background: linear-gradient(180deg, var(--n-gray-600) 0%, var(--bg) 100%);
}
/* The label: caps 13px in --n-gray-400, and an empty marker — ground fill with a
   --n-gray-400 hairline — since it names the list rather than being an item of it.
   Tracking stays --ls-body (Figma: -0.78px on 13px = -0.06em), not --ls-eyebrow's
   0, so it is tighter than a section eyebrow despite looking like one. */
.control-list li.control-list__label {
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  color: var(--fg-muted);
  list-style: none;
}
.control-list li.control-list__label::before {
  border-color: var(--n-gray-400);
  background: var(--bg);
}
/* Dividers between items only: --n-gray-600 to match Figma's #555555 line, which
   is a step stronger than the --line used for section rules. */
.control-list li:not(.control-list__label) + li { border-top: 1px solid var(--line-strong); }

/* AICPA SOC logo under the "trust layer keeps evolving" copy. AICPA's guidelines
   require the mark to link to their SOC for Service Organizations page, so it is
   an <a> — block with fit-content width so the link box hugs the artwork.

   The section is .theme-sage, where the _Blk artwork reads unaided (the (tm) at
   11.4:1), so it needs no backing plate. On a dark ground it measured 1.09:1,
   which is what earlier versions were working around.

   Deliberately served as the original PNG at its native 359px, NOT converted by
   scripts/png-to-webp.mjs: AICPA's guidelines don't permit altering the mark, and
   it is only ~28KB. Do not "optimise" this asset. */
.soc2-badge {
  display: block;
  width: fit-content;
  margin-top: 40px;
}
.soc2-badge img { display: block; width: 300px; height: auto; }
/* The link's content is the artwork, so there's no underline or colour to
   inherit; on sage the default --bracket grey is too weak for the focus ring. */
.soc2-badge:focus-visible { outline: 2px solid var(--fg); outline-offset: 6px; }

/* Below 640 the .cols-2 collapses to one column and the timeline lands directly
   beneath the badge, so tighten the gap and step the mark down. 240 keeps it
   proportionate to the narrower column without the abrupt jump a much smaller
   value would give, and stays well above the width where the ring type breaks up. */
@media (max-width: 640px) {
  .soc2-badge { margin-top: 32px; }
  .soc2-badge img { width: 240px; }
}


/* ---- No-JS fallback nav ----
   Navigation and the footer are injected by components/site-chrome.js. With
   JavaScript off that never runs, so each page carries a <noscript> list of
   every destination — plain, legible, and enough to reach any page. */
.nav-fallback {
  display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: center;
  padding: 16px max(var(--rail), 20px);
  background: var(--n-black); color: var(--n-white);
  border-bottom: 1px solid var(--n-gray-800);
  font-size: var(--fs-small);
}
.nav-fallback a { color: var(--n-gray-400); }
.nav-fallback a:hover { color: var(--n-white); }
.nav-fallback__brand { color: var(--n-white); font-size: var(--fs-h3); letter-spacing: var(--ls-h3); margin-right: 12px; }


/* ---- Before/after comparison (Platform: "AI-generated code is only the beginning") ----
   From Figma Desktop_Platform (1734:17363): a light "Prototype" panel and a dark
   "Production System" panel, joined by a mint diamond carrying the lockup. Rows
   are separated by hairlines; the left column's marks are empty squares, the
   right column's are filled mint. */
/* Figma (1810:8720) draws the two panels as separate boxes with the connector
   and its diamond in the space between them — 496 / 210 / 494 of the 1200 row —
   rather than one joined slab. Each keeps its own edge and radius. */
.compare {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: stretch;
}
.compare__panel {
  padding: 40px; display: flex; flex-direction: column; gap: 0; min-width: 0;
  border: 1px solid var(--line);
  /* Figma (1810:8720) draws both panels square-cornered, unlike the generic
     card radius most surfaces use. */
  border-radius: 0;
}
.compare__panel--was { background: transparent; color: var(--n-black); }
.compare__panel--now { background: var(--n-black); color: var(--n-white); border-color: var(--n-black); }
.compare__label {
  font-size: var(--fs-eyebrow); line-height: 1; letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase; margin-bottom: 24px;
}
.compare__panel--was .compare__label { color: var(--n-gray-800); }
.compare__panel--now .compare__label { color: var(--accent); }
.compare__list { display: flex; flex-direction: column; }
.compare__list li {
  display: flex; align-items: center; gap: 18px;
  padding: 18px 0;
  font-size: var(--fs-body); line-height: 1.2; letter-spacing: var(--ls-body);
}
.compare__list li + li { border-top: 1px solid currentColor; }
.compare__panel--was .compare__list li + li { border-top-color: rgb(24 24 24 / 0.15); }
.compare__panel--now .compare__list li + li { border-top-color: rgb(255 255 255 / 0.15); }
.compare__list li::before {
  content: ""; flex: none; width: 12px; height: 12px; border-radius: 2px;
}
.compare__panel--was .compare__list li::before { border: 1px solid var(--n-gray-600); }
.compare__panel--now .compare__list li::before { background: var(--accent); }

/* The hinge: a mint diamond with the lockup beneath it */
.compare__hinge {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  padding: 0 clamp(24px, 4vw, 56px);
  /* the hinge sits on the section ground now that the panels are separate */
  position: relative;
}
/* the rule that runs through the hinge, joining the two lists */
.compare__hinge::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: rgb(24 24 24 / 0.15);
}
/* Figma nudges the diamond above the mid-line and hangs the lockup beneath it */
.compare__hinge { justify-content: center; }
.compare__diamond { position: relative; z-index: 1; width: 128px; height: 128px; flex: none; }
.compare__mark { position: relative; z-index: 1; display: flex; align-items: center; gap: 6px; font-size: var(--fs-small); color: var(--n-black); }
.compare__mark svg { width: 14px; height: 14px; }
@media (max-width: 860px) {
  .compare { grid-template-columns: minmax(0, 1fr); }
  .compare__hinge { padding: 24px 0; }
  .compare__hinge::before { display: none; }
}


/* =====================================================================
   AUDIENCE PAGE TEMPLATE — from Figma
   The twelve audience pages (Desktop_For Carriers 1734:21378 and siblings)
   share one layout. Distances and surfaces below are read from those frames.
   ===================================================================== */

/* ---- Hero: light art panel on the left, copy on the right. The grid pads to the
   rails so the art meets the line, and the copy column re-adds the 40px text
   inset so headlines stay aligned with the sections below. ---- */
.aud-hero { background: var(--n-white); color: var(--n-black-2); }
.aud-hero__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center; gap: var(--col-gap-wide);
  /* The hero art is meant to meet the rail, so the grid pads to the stage edge
     (--rail) and the copy column re-adds the text inset itself. That keeps the
     image flush with the line while the headline still sits 40px inside it. */
  width: 100%;
  padding-inline: var(--rail);
}
/* Art-first pages put the image on the left rail, copy-first on the right; in
   both cases only the copy carries the text inset. */
.aud-hero__art { align-self: stretch; min-height: 420px; background: var(--n-white); overflow: hidden; }
.aud-hero__art img { width: 100%; height: 100%; object-fit: cover; }
.aud-hero__copy { padding-block: clamp(40px, 5vw, 80px); padding-inline: var(--text-inset); display: flex; flex-direction: column; align-items: flex-start; }
.aud-hero__copy .page-hero__title { color: var(--n-black-2); }
/* .page-hero__lead is sized for the dark ground, where it uses a near-white
   grey; on this light panel it has to take the light theme's body color. */
.aud-hero__copy .page-hero__lead { color: rgb(30 30 30 / 0.8); }
.aud-hero__copy .btn { --bracket: var(--n-gray-600); color: var(--n-black-2); }
@media (max-width: 860px) {
  .aud-hero__grid { grid-template-columns: minmax(0, 1fr); }
  .aud-hero__art { min-height: 260px; }
}

/* Hub variant (insurance, financial-services). These two pages sit a level above
   the sub-industry pages, so the banner keeps the dark ground while the nav and
   page frame stay white like every other industry page (body.nav-light). Copy
   leads on the left with the art panel on the right — the mirror of
   the sub-industry hero — while inheriting its stretch + object-fit: cover, so the
   image fills the full banner height instead of floating in a capped box. */
.aud-hero--dark { background: var(--n-black); color: var(--n-white); }
.aud-hero--dark .aud-hero__art { background: var(--n-black-2); }
.aud-hero--dark .aud-hero__copy .page-hero__title { color: var(--n-white); }
.aud-hero--dark .aud-hero__copy .page-hero__lead { color: var(--n-gray-200); }
/* Resting state is white on the dark ground; accent is the hover/focus state.
   The homepage hero opts into a permanently accent button via .btn--accent in
   its markup instead, so it is unaffected by this. */
.aud-hero--dark .aud-hero__copy .btn { --bracket: var(--n-white); color: var(--n-white); }
.aud-hero--dark .aud-hero__copy .btn:hover,
.aud-hero--dark .aud-hero__copy .btn:focus-visible { --bracket: var(--accent); color: var(--accent); }
/* Stacked, the art leads, matching the sub-industry pages where the image comes
   first in the banner on small screens. */
@media (max-width: 860px) {
  .aud-hero--dark .aud-hero__art { order: -1; }
}

/* ---- Problem (dark): headline left, network art right, then bracketed cards ---- */
.aud-problem { background: var(--n-black); color: var(--n-white); padding-block: var(--section-pad); }
.aud-problem__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--col-gap-wide); align-items: center; margin-bottom: 56px; }
.aud-problem__head .h2 { max-width: 14em; }
.aud-problem__art img, .aud-problem__art svg { width: 100%; height: auto; }
@media (max-width: 860px) {
  /* Figma's mobile frames stack headline → art → cards, with the art sized to
     the column and the gaps tighter than the desktop rhythm. */
  .aud-problem__head { grid-template-columns: minmax(0, 1fr); gap: 24px; margin-bottom: 32px; }
  .aud-problem__art { max-width: 340px; margin-inline: auto; }
  .aud-problem .cols-3 { gap: 16px; }
}

/* A bracketed panel used for the cards on dark and light grounds alike */
/* The cards wrap rather than thin out — no track below 200px (four across at
   992 was 149px) — and they wrap evenly: the four solution cards go 4 up or
   2x2, never 3+1, and the three problem cards go 3 up or stack. Explicit
   counts rather than auto-fit, which fills the row and would leave the
   orphan. Scoped to the grids holding .panel-card so the shared .cols-3 and
   .cols-4 keep their behaviour for stat, media and feature cards.
   The homepage solution features (.solution__features) are the same four-up
   card row, so they follow the same wrapping. */
.aud-problem .cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.aud-solution__panel .cols-4,
.solution__features .cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* Once they wrap, the rows need more air between them than the 20px column gap
   — which is proportional, so it tightens to ~9px on phones just as the cards
   start stacking. Figma's stacked variant (Desktop_Homepage 1834:15900) sets
   gap-[40px] on the column, the same row gap .principles__grid uses. Only the
   row gap changes; the columns keep --col-gap, and at 4-up there is a single
   row so this has no effect until they wrap. */
.aud-solution__panel .cols-4,
.solution__features .cols-4 { row-gap: 40px; }
/* 4 up needs ~1320 before a track clears 200px; below that pair them 2x2. */
@media (max-width: 1319px) {
  .aud-solution__panel .cols-4,
  .solution__features .cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* 3 up holds to ~860; below that the problem cards stack rather than orphan. */
@media (max-width: 859px) {
  .aud-problem .cols-3 { grid-template-columns: minmax(0, 1fr); }
}

/* Same rule for the three-up sage grids: the homepage Problem and Approach
   bands and the hub pages' nested .sage-panel. The shared .cols-3 goes 2-up at
   991, which for a set of three leaves a single card orphaned on the second row
   — two across, one below. These hold 3 across and then stack one per row, so
   the row is always even. Breakpoint matches .aud-problem above: at 860 the
   narrowest of the three (the sage panel, inset 80px on the left) still gives
   each column ~190px of content inside its 20-40px padding. */
.problem .cols-3,
.approach .cols-3,
.sage-panel .cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 859px) {
  .problem .cols-3,
  .approach .cols-3,
  .sage-panel .cols-3 { grid-template-columns: minmax(0, 1fr); }
}
/* the 2x2 pair still needs 200px a side; under ~640 both grids stack. */
@media (max-width: 639px) {
  .aud-solution__panel .cols-4,
  .solution__features .cols-4 { grid-template-columns: minmax(0, 1fr); }
}

/* The remaining three-up card rows on the shared .cols-3: hold three across then
   stack, so a row is never left with an orphaned third card.
     - the black hub rows: inset feature cards and the .aud-how steps
     - .aud-stats, the sage outcomes row on the audience pages

   These stack at 1023 rather than the sage grids' 859 because their floors are
   higher — 40px of padding a side, .aud-step's fixed 146px cube, and h2-size
   numerals in .stat-card__num. */
.aud-how .cols-3,
.aud-stats .cols-3,
.cols-3:has(> .feature-icon--inset) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1023px) {
  .aud-how .cols-3,
  .aud-stats .cols-3,
  .cols-3:has(> .feature-icon--inset) { grid-template-columns: minmax(0, 1fr); }
}

/* And once stacked they need the same 40px of air as the solution cards above:
   --col-gap is proportional, so it tightens to ~9px on phones exactly when the
   cards go one per row. Only the row gap changes; at 3-up there is a single row,
   so this has no effect until they stack. */
.aud-how .cols-3,
.aud-stats .cols-3,
.cols-3:has(> .feature-icon--inset) { row-gap: 40px; }

/* Figma gives the card its own ground so it reads as a surface on the section
   rather than bare text: #1c1c1c on the dark problem band (1735:26733) and
   black at 3% on the white solution panel (1797:6880), both 7px radius with
   40px padding. .panel-card sits on both, so the fill follows the section. */
.panel-card {
  padding: 40px;
  display: flex; flex-direction: column; gap: 8px; min-width: 0;
  background: var(--n-gray-900);
  border-radius: var(--radius-card);
}
.aud-solution__panel .panel-card { background: var(--surface); }
.panel-card .h4 { font-weight: 500; }
.panel-card .small { color: var(--fg-body); }
.aud-solution__panel .panel-card .small { color: var(--n-gray-800); }
@media (max-width: 767px) { .panel-card { padding: 24px; } }

/* ---- Solution: a white panel inset from the frame, with bracketed columns ---- */
.aud-solution { background: var(--n-black); padding-block: 0; }
/* The white panel is nested inside the dark ground rather than filling it:
   Figma (1734:20669) insets it 80px from the frame's left edge and runs it
   flush to the right, so the dark section reads as the layer behind it. */
.aud-solution__panel {
  background: var(--n-white); color: var(--n-black-2);
  /* Vertical only. The interior 80px inset (Figma: content spans x=200..1320
     inside a panel at x=120 w=1280) is supplied by the nested .container's
     padding-inline: var(--gutter) above. Setting it here too stacked the two and
     insets content 160px, narrowing the card row and reading over-centered. */
  padding-block: clamp(40px, 5vw, 72px);
  /* Figma insets the panel 80px from the frame's left edge and runs it flush
     right. The 80px is measured from the rail, and .framed no longer pads, so
     the rail offset is added here; the right edge lands on the rail itself. */
  margin-left: calc(var(--rail) + 80px);
  margin-right: var(--rail);
}
/* Mobile keeps the nest, just shallower: Figma's 440 frame (1734:21104) puts
   the panel at x=33 inside the 400-wide slab, where desktop uses 80 inside
   1360 — both measured from the slab, both flush right. */
@media (max-width: 767px) { .aud-solution__panel { margin-left: calc(var(--rail) + 33px); } }
.aud-solution__panel .section-head__row .body { color: rgb(30 30 30 / 0.8); }

/* Figma (1734:17526) lists these as an icon over a title and body, not as
   cards — no ground, no card padding, just the bracketed 50px icon frame. */
.feature-icon { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.feature-icon__title { font-size: var(--fs-body); line-height: normal; letter-spacing: var(--ls-body); color: var(--fg); }
.feature-icon__body { font-size: var(--fs-small); line-height: var(--lh-body-multi); letter-spacing: var(--ls-reading); color: var(--fg-body); }

/* Insurance hub "Built for insurance operations" (1860:61089): same icon/title/body
   stack, but Figma insets each column's content by 40px and tightens the
   title-to-body gap to 4px, keeping 16px only under the icon frame. */
.feature-icon--inset { padding: 40px; gap: 16px; }
.feature-icon--inset .feature-icon__text { display: flex; flex-direction: column; gap: 4px; }
@media (max-width: 767px) { .feature-icon--inset { padding: 24px 0; } }

/* ---- Platform panel: the dark slab nested in the sage challenge section ----
   Figma (1734:17520) puts it at x=80 w=1280 inside the 1360 slab — inset 80px
   on the left, flush right — so the sage runs on past its own content and
   shows down the panel's left edge. The same relationship the audience pages
   draw with their white solution panel on the dark problem band. */
.platform-panel {
  background: var(--n-black);
  color: var(--n-white);
  --bg: var(--n-black);
  --fg: var(--n-white);
  --fg-body: var(--n-gray-200);
  --fg-muted: var(--n-gray-400);
  --line: var(--n-gray-800);
  --bracket: var(--n-gray-600);
  /* Figma puts the panel at x=80 measured from the frame's left rail, flush
     right. .framed no longer pads, so the rail offset is added here. */
  margin-left: calc(var(--rail) + 80px);
  margin-right: var(--rail);
  padding: 100px 0 40px;
  /* Figma (1734:17507) ends the sage section's content at y=508 and starts this
     panel at y=608, so a full section's worth of sage shows between the two.
     Without it the challenge copy butts straight into the dark slab and the sage
     band reads as if it has no bottom at all. */
  margin-top: var(--section-pad);
}
@media (max-width: 767px) { .platform-panel { margin-left: calc(var(--rail) + 33px); padding: 64px 0 32px; } }
/* Figma (1734:17520) runs the panel flush to the bottom of the sage section —
   the how-it-works band sits directly under it, and the only sage visible at
   that seam is the stepped left margin, not a full-width strip. So the host
   section contributes no bottom padding of its own (mirrors .sage-panel's
   rule below); see the border rule on .aud-how for how the two black grounds
   (panel + how-it-works) stay visually separated instead. */
.section:has(> .platform-panel) { padding-bottom: 0; }

/* ---- Sage panel: the sage slab nested in the black band (insurance hub) ----
   The inverse of .platform-panel. Figma (1860:61315) puts it at x=80 w=1280
   inside the 1360 slab — inset 80px on the left, flush right — so the black
   ground runs on past it and shows down the panel's left edge. The panel
   carries its own vertical rhythm (100 top / 40 bottom), so the host section
   contributes no padding of its own and the sage meets the bands above and
   below flush, as in the frame. */
.sage-panel {
  background: var(--n-sage);
  --bg: var(--n-sage);
  --fg: var(--n-black);
  --fg-body: var(--n-gray-800);
  --fg-muted: var(--n-gray-600);
  --line: var(--n-gray-400);
  --line-strong: var(--n-black);
  --bracket: var(--n-gray-400);
  color: var(--fg);
  /* x=80 from the left rail, flush right — the rail offset is added here now
     that .framed carries no padding. */
  margin-left: calc(var(--rail) + 80px);
  margin-right: var(--rail);
  padding: 100px 0 40px;
}
/* Mobile keeps the nest, just shallower — same 33px the audience pages use. */
@media (max-width: 767px) { .sage-panel { margin-left: calc(var(--rail) + 33px); padding: 64px 0 32px; } }
.section:has(> .sage-panel) { padding-block: 0; }

/* ---- How it works: three columns, each with its isometric cube ---- */
.aud-how { background: var(--n-black); color: var(--n-white); padding-block: var(--section-pad); }
/* On the insurance hub the preceding band is also black (the "Built for
   insurance operations" section), so the two would merge into one unbroken
   slab. Figma (1843:18331) draws a 1px #555 rule along this frame's top edge
   in exactly that case. Themed (light) predecessors already separate
   themselves, so the border is scoped to an unthemed — i.e. dark — sibling. */
.section:not([class*="theme-"]) + .aud-how { border-top: 1px solid var(--n-gray-600); }
/* Platform's predecessor is themed sage, so the rule above does not match it,
   but the border is still needed there — the panel now sits flush against this
   section (no bottom sage gap), so the two dark grounds need the same seam. */
.theme-sage:has(> .platform-panel) + .aud-how { border-top: 1px solid var(--n-gray-600); }
/* Figma (1998:36003) reads number, cube, then copy across a single row — the
   number is its own column to the left of the art, not stacked above the text.
   Source order keeps art before the number, so both are placed explicitly. */
.aud-step { display: grid; grid-template-columns: auto auto 1fr; gap: 16px; align-items: start; }
.aud-step__num {
  grid-column: 1; grid-row: 1;
  font-size: var(--fs-eyebrow); line-height: 1; color: var(--n-gray-600);
  font-variant-numeric: tabular-nums;
}
/* Figma draws the cube at 146 (Frame 2095584377), not 96. */
.aud-step__art { grid-column: 2; grid-row: 1; width: 146px; display: flex; justify-content: center; }
.aud-step > div:last-child { grid-column: 3; grid-row: 1; }
/* Render the export 1:1 rather than stretching it to the column. All three cubes
   are drawn at the same scale, but the Operate export ships a larger canvas to
   hold its core glow — scaling that to 146 shrank its cube ~9% against the other
   two. Natural size plus centring lets the glow bleed out of the column instead. */
.aud-step__art img, .aud-step__art svg { width: auto; max-width: none; height: auto; flex: none; }
.aud-step__title { font-size: var(--fs-h3); line-height: 1; letter-spacing: var(--ls-h3); font-weight: 500; margin-bottom: 8px; }
.aud-step__body { font-size: var(--fs-small); line-height: 1.35; color: var(--fg-body); max-width: 15rem; }

/* ---- Outcomes (sage): stat cards with an icon ---- */
.aud-stats { padding-block: var(--section-pad); }
/* Same treatment as the solution cards: Figma (1957:30700) gives the stat card
   its own ground on the sage band — black at 3%, 7px radius, 40px padding. */
.stat-card {
  display: flex; flex-direction: column; gap: 16px; padding: 40px; min-width: 0;
  background: var(--surface);
  border-radius: var(--radius-card);
}
@media (max-width: 767px) { .stat-card { padding: 24px; gap: 10px; } }
/* Figma's "Menu Image container" (1957:30701) is a 50px frame holding a 40px
   glyph, and the exported SVGs bake their own corner brackets into a 51x51
   viewBox — so the file is drawn at its natural size and must not be scaled
   down, or the brackets shrink with it. */
.stat-card__icon { width: 50px; height: 50px; flex: none; color: var(--fg); }
.stat-card__icon svg,
.stat-card__icon img { display: block; width: 100%; height: 100%; }
.stat-card__num { font-size: var(--fs-h2); line-height: 1; letter-spacing: var(--ls-h2); font-weight: var(--fw-h2); }
/* Figma sets the supporting line at body size (its 20px text box), not --fs-small. */
.stat-card__label { font-size: var(--fs-body); line-height: 1.35; color: var(--fg-body); }


/* =====================================================================
   SECURITY & COMPLIANCE — from Figma Desktop_Security & Compliance (1746:22578)
   The page is dark throughout. Its approval-gate row is five cells joined by
   mint chevrons, with the gate cell outlined in mint and tagged above.
   ===================================================================== */
.gates {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--n-black-2);
  position: relative;
}
.gate {
  position: relative;
  padding: 32px 24px 36px;
  display: flex; flex-direction: column; gap: 12px;
  border-right: 1px solid rgb(255 255 255 / 0.08);
  /* Each cell carries its own surface fill inside the bordered strip, rather
     than sitting transparent on the section ground. */
  background: rgb(255 255 255 / 0.03);
}
.gate:last-child { border-right: 0; }
/* mint chevron between cells */
.gate + .gate::before {
  content: ""; position: absolute; left: -7px; top: 50%; transform: translateY(-50%);
  width: 0; height: 0; z-index: 2;
  border-top: 7px solid transparent; border-bottom: 7px solid transparent;
  border-left: 7px solid var(--accent);
}
.gate__num { font-size: var(--fs-eyebrow); line-height: 1; color: var(--n-gray-600); font-variant-numeric: tabular-nums; }
/* Figma (node 1746:23528): "Menu Image container" is 50x50 with the glyph
   instance 40x40 centred inside it (x=5.33, y=5). The tile size was already
   right; the glyph was drawing at 32px, which read too small. */
/* The icon tile is a bracketed box; its <span class="corners"> is taken out of
   flow by the shared .bracketed > .corners rule, which also matches Figma's
   bracket frame (1756:9975) — a full-size 50x50 overlay on the tile. */
.gate__icon { width: 50px; height: 50px; display: grid; place-items: center; margin-block: 4px; }
/* Icons are Figma's own glyphs (node 1746:23528), exported at their native 40px
   and inlined so they inherit colour. They are filled art, not stroked, and
   carry their own opacity:0.4 secondary layers — so colour comes from `fill`
   via currentColor, which is also what lets .gate--key tint them mint. */
.gate__icon svg { width: 40px; height: 40px; color: var(--n-gray-400); fill: currentColor; }
.gate__title { font-size: var(--fs-body); line-height: 1.15; font-weight: 500; }
.gate__body { font-size: var(--fs-small); line-height: 1.35; color: var(--fg-body); }
/* the approval gate: mint frame, a lighter surface, and a soft glow on its
   border — not a plain 1px outline — plus a tag that sits on its top edge */
.gate--key {
  outline: 1px solid var(--accent); outline-offset: -1px; z-index: 1;
  background: rgb(0 255 178 / 0.06);
  box-shadow: 0 0 24px 0 rgb(0 255 178 / 0.25);
}
.gate--key .gate__title { color: var(--accent); }
.gate--key .gate__icon svg { color: var(--accent); }
.gate--key::after {
  content: attr(data-tag);
  position: absolute; left: 50%; top: 0; transform: translate(-50%, -50%);
  background: var(--n-black); color: var(--accent);
  border: 1px solid var(--accent); border-radius: 2px;
  padding: 3px 10px; white-space: nowrap;
  font-size: 0.625rem; line-height: 1; letter-spacing: 0.04em; text-transform: uppercase;
}
/* Wrapped, the row of five becomes a grid, and the single-row divider logic no
   longer holds: .gate:last-child clears only the final cell, so whichever cell
   ends each row drew a stray rule against the container's right edge, and the
   stacked rows had no divider between them at all (the cells sit flush — .gates
   has no gap). So on each wrapped breakpoint the dividers are redrawn: verticals
   only between columns, horizontals between rows.
   Five cells in two columns also leaves the last one ragged, which would strand
   a border under the cell above it, so the final odd cell spans the full row. */
@media (max-width: 1024px) {
  .gates { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .gate + .gate::before { display: none; }
  .gate:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .gate:nth-child(2n) { border-right: 0; }
  .gate { border-bottom: 1px solid rgb(255 255 255 / 0.08); }
  .gate:last-child { border-bottom: 0; }
}
@media (max-width: 620px) {
  .gates { grid-template-columns: 1fr; }
  .gate { border-right: 0; }
}

/* Tag chips carry the bracket device on this page */
.tag-row--bracketed { gap: 12px; }
.tag-row--bracketed .tag-chip {
  border: 0; border-radius: 0; padding: 8px 14px;
  --bracket: var(--accent); color: var(--fg);
}

/* Timeline list: a mint rule with square nodes down the left.
   Figma 1791:20382 (finance hub "An evolving trust layer", also used by
   security-compliance): a ~20px node with a mint→green gradient fill, a 1px
   mint border and a soft mint glow, joined by an 80px gradient connector.
   Nodes are drawn per item so the last node can sit on top of the connector
   rather than the rule running through it. */
.timeline {
  --tl-node: 20px;               /* ~20px glowing marker per Figma */
  --tl-node-gap: 20px;          /* node right edge → text (rail x=40, text x=76) */
  --tl-node-start: var(--accent); /* gradient head of the node and connector */
  --tl-node-end: #00996b;       /* gradient foot of the node and connector */
  --tl-node-glow: var(--accent);  /* halo around each node */
  --tl-node-top: 16px;          /* centres the node on the 20px title line */
  display: flex; flex-direction: column;
  padding-left: calc(var(--tl-node) + var(--tl-node-gap));
  position: relative;
}
/* 16px of air top and bottom per item, and a 16px title→body gap, giving the
   100px item pitch Figma draws. The old flex `gap` is gone: the padding is what
   separates the rows, so the connector can span the join without a hole in it. */
.timeline__item { position: relative; padding-block: 16px; }
.timeline__item::before {
  content: ""; position: absolute; left: calc(-1 * (var(--tl-node) + var(--tl-node-gap)));
  top: var(--tl-node-top);
  width: var(--tl-node); height: var(--tl-node);
  background: linear-gradient(180deg, var(--tl-node-start) 0%, var(--tl-node-end) 100%);
  border: 1px solid var(--tl-node-start);
  box-shadow: 0 0 6px 0 var(--tl-node-glow);
}
/* The connector hangs from this item's node to the next one's, so it ends under
   the following node instead of being a single rule behind every node. */
.timeline__item::after {
  content: ""; position: absolute;
  left: calc(-1 * (var(--tl-node-gap) + var(--tl-node) / 2));
  top: calc(var(--tl-node-top) + var(--tl-node));
  bottom: calc(-1 * var(--tl-node-top));
  width: 1px;
  background: linear-gradient(180deg, var(--tl-node-start) 0%, var(--tl-node-end) 100%);
}
/* Last step is outlined rather than filled — ink ground, mint edge — so it reads
   as "not yet reached" and masks the connector arriving behind it. Its own tail
   fades into the section ground instead of stopping dead. */
/* var(--bg) rather than a literal --n-black so the "not yet reached" node keeps
   knocking through to whatever ground the section actually has. */
.timeline__item:last-child::before { background: var(--bg); }
.timeline__item:last-child::after {
  bottom: auto; height: 80px;
  background: linear-gradient(180deg, var(--tl-node-start) 0%, transparent 100%);
}
.timeline__title {
  font-size: var(--fs-h3); line-height: 1; font-weight: 400;
  letter-spacing: var(--ls-h3); margin-bottom: 16px;
}
.timeline__body { font-size: var(--fs-body); line-height: 1.25; color: var(--fg-muted); max-width: 34em; }

/* On a sage ground the mint node washes out — --accent measures 1.09:1 against
   --n-sage — so the ramp flips to the dark end of the green scale (green-800 ->
   green-900, 3.5:1 and 7:1). The halo is kept, but as green rather than mint:
   a light-on-light glow reads as haze, whereas a dark one behaves like a soft
   drop shadow and still gives the nodes the same lift they have on black.
   Scoped to .theme-sage so the dark timelines (financial-services, and this
   page's own #trust section) keep the mint treatment untouched. */
.theme-sage .timeline {
  --tl-node-start: var(--n-green-800);
  --tl-node-end: var(--n-green-900);
  --tl-node-glow: rgb(0 77 46 / 0.45);
}


/* ---- Editorial index header (insights.html, news.html) ----
   Figma 732:52436 / 1989:23928: title left, a 555-wide subscribe column
   right-aligned on the same row, the form's baseline 36px under the label.

   The row is expressed against .container rather than the frame, so the header
   lines up with the post tiles; Figma's extra 20px group pad is dropped because
   .container already supplies the inset.

   Vertical rhythm comes straight from Desktop_News (1989:23795) — 146px below the
   nav, 99px to the first tile — because the generic 100px --section-pad left both
   gaps ~40px tight. */
.section:has(> .container > .index-head) { padding-top: 146px; }
.index-head {
  display: grid;
  /* The aside holds Figma's 555px measure rather than collapsing to its content
     width, so the email field and the privacy note wrap where the design wraps
     them. It yields proportionally on narrower desktops. */
  grid-template-columns: minmax(0, 1fr) min(555px, 42%);
  gap: var(--col-gap-wide);
  align-items: start;
  /* 99px from the title's box to the first tile. The title's 0.85 line-height
     leaves it ~6px shorter than Figma's 81px box, so the margin carries the
     remainder rather than a flat 99. */
  margin-bottom: 94px;
}
.index-head__title {
  font-size: var(--fs-h1-index);
  line-height: 0.85;
  letter-spacing: var(--ls-display);
  margin: 0;
}
/* 555px at 1440 is the Figma measure; the track above carries it, so the aside
   simply fills its column. */
.index-head__aside { width: 100%; }

@media (max-width: 991px) {
  .index-head {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--head-stack-gap);
  }
  .index-head__aside { width: 100%; }
}

/* The 146/94 pair is the 1440 figure; on smaller screens the title drops to
   61px and the header stacks, so both gaps come back to the section rhythm
   rather than leaving a canyon above the first tile. */
@media (max-width: 991px) {
  .section:has(> .container > .index-head) { padding-top: 100px; }
  .index-head { margin-bottom: 64px; }
}
@media (max-width: 767px) {
  .section:has(> .container > .index-head) { padding-top: 72px; }
  .index-head { margin-bottom: 48px; }
}

/* ---- Post tile — from Figma 732:52439 (Insights / News index) ----
   A full-bleed row split into two 680px panels (517 tall): copy one side, art the
   other, sides swapping every row. The copy panel lays its title and byline out
   `space-between`, so the byline sits at the foot whatever the title length.

   Shared by news.html and insights.html; .news-row is kept as an alias for
   markup that already shipped. ---- */
/* The post list ignores the text inset: it widens out of .container's --pad-x by
   --text-inset so the tile borders land on the rails themselves, closing against
   the line rather than floating 40px inside it. */
.post-list { margin-inline: calc(var(--text-inset) * -1); }

/* The design rules the tile on all four sides plus the seam between its two
   panels — a 1px --n-gray-700 grid, lighter than the --line the rest of the page
   uses. The tile draws all of it: .framed's rails sit at the same x, but the
   section background paints over them, so the left edge would go missing. */
.post-tile {
  position: relative;
  z-index: 1;   /* over .framed's rails, which sit at the same x in a darker grey */
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  border-top: 1px solid var(--n-gray-700);
  border-inline: 1px solid var(--n-gray-700);
}
.post-tile:last-of-type { border-bottom: 1px solid var(--n-gray-700); }
/* the seam between the copy and art panels, whichever side the copy is on */
.post-tile__copy { border-right: 1px solid var(--n-gray-700); }
.post-tile:nth-of-type(even) .post-tile__copy { border-right: 0; border-left: 1px solid var(--n-gray-700); }

.post-tile__copy {
  /* Figma 80/100; the panel's 517 height comes from the art's box ratio */
  padding: clamp(40px, 5.5vw, 80px) clamp(24px, 7vw, 100px);
  display: flex; flex-direction: column; justify-content: space-between; gap: 32px;
  text-align: center;
  background: var(--bg);
  transition: background var(--dur) var(--ease-out);
}
/* 25px/25px with the design's -1.5 tracking */
.post-tile__title {
  font-size: clamp(1.25rem, 1.7vw, 1.5625rem);
  line-height: 1.1;
  letter-spacing: -0.06em;
  font-weight: 400;
  max-width: 20em;
  margin-inline: auto;
}
/* The whole tile is the click target: the title's link stretches over the row
   rather than wrapping it, so there is still one link in the accessibility tree
   and the heading keeps its own text. */
.post-tile__title a::after { content: ""; position: absolute; inset: 0; z-index: 1; }
/* Hover is the component's own Variant2 (Figma 732:52508 carries an ON_HOVER
   that swaps to 732:52515): the copy panel lifts from the page ground to
   #373737. The title itself does not change colour. */
.post-tile:hover .post-tile__copy { background: var(--n-gray-800); }

/* Author line: 50px avatar + name, centred as a group (Figma 702:18642) */
.post-tile__by {
  display: flex; align-items: center; justify-content: center; gap: 20px;
  font-size: 1.25rem; line-height: 1.25; letter-spacing: -0.06em;
  color: rgb(255 255 255 / 0.8);
}
.post-tile__avatar {
  width: 50px; height: 50px; flex: none;
  object-fit: cover;
  background: var(--n-gray-200);
}

/* The art panel is 680x517 in the design (a ~1.31:1 landscape box), not a
   square — the panel ratio is what sets the row height, and the art is cropped
   to fill it. */
.post-tile__img { background: var(--n-black-2); overflow: hidden; aspect-ratio: 680 / 517; }
.post-tile__img img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* every second tile swaps the columns */
.post-tile:nth-of-type(even) .post-tile__copy { order: 2; }

@media (max-width: 780px) {
  /* Figma's mobile frame stacks every row image-first, with no alternation. */
  .post-tile { grid-template-columns: minmax(0, 1fr); }
  .post-tile__img { order: 0; }
  .post-tile__copy, .post-tile:nth-of-type(even) .post-tile__copy { order: 1; }
  /* The seam only exists to divide two side-by-side panels. Stacked, it lands on
     top of the tile's own side border and reads as a 2px edge. */
  .post-tile__copy,
  .post-tile:nth-of-type(even) .post-tile__copy { border-inline: 0; }
  .post-tile__avatar { width: 36px; height: 36px; }
  .post-tile__by { font-size: 1rem; gap: 12px; }
}

/* The list ends on a centred "Load more" (Figma 732:52483). It is only rendered
   when there is another page of posts to fetch. */
.post-list__more { display: flex; justify-content: center; padding: 40px 20px 120px; }

/* Inline newsletter: a single field with the signup button beside it */
.subscribe { display: flex; flex-direction: column; gap: 8px; }
/* The row never wraps: the button stays to the right of the input at every width
   (narrow phones included), so the input shrinks instead of the button dropping
   to its own line. min-width:0 lets the flex item shrink below its 320px basis. */
.subscribe__row { display: flex; gap: 12px; align-items: center; flex-wrap: nowrap; }
.subscribe__row .field__input { flex: 1 1 auto; width: auto; max-width: 320px; min-width: 0; height: 36px; }
.subscribe__row .btn { flex: none; }
.subscribe__note { font-size: var(--fs-small); color: var(--fg-muted); }
/* on the green band the field has to read against the gradient */
.theme-green .field__input { border-color: rgb(255 255 255 / 0.5); color: var(--n-white); }
.theme-green .field__input::placeholder { color: rgb(255 255 255 / 0.6); }
.theme-green .subscribe__note { color: rgb(255 255 255 / 0.8); }
.theme-green .btn { --bracket: var(--n-white); }

/* Green newsletter band */
.band-green {
  background: linear-gradient(180deg, var(--n-green-900) 0%, var(--n-green-800) 100%);
  color: var(--n-white);
  padding-block: 72px;
}
.band-green__inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--col-gap-wide); align-items: center; }
@media (max-width: 780px) { .band-green__inner { grid-template-columns: minmax(0, 1fr); } }

/* Company story: the copy column carries the weight here, so don't split the
   row 50/50 — Figma's Desktop_Company (1753:29845) gives the text 670px and the
   blueprint mark only 248px, hard right. An even split clipped the headline to
   546px and wrapped it to three lines where the design has two, while the art
   column sat two-thirds empty. Track the art's intrinsic 248px instead and let
   the spare width fall to the copy. */
.story-grid { grid-template-columns: minmax(0, 1fr) 248px; align-items: center; }
.story-grid__text { display: flex; flex-direction: column; gap: 20px; }
/* 670px is Figma's measure: two lines of headline, and a body line no longer
   than ~67 characters. */
.story-grid__text > * { max-width: 670px; }
@media (max-width: 860px) { .story-grid { grid-template-columns: minmax(0, 1fr) 180px; } }

/* Company story: Figma's mobile frame leads with the mark, then the copy. */
@media (max-width: 640px) {
  .story-grid { display: flex; flex-direction: column; gap: 32px; }
  .story-grid__text { order: 2; }
}


/* ---- White nav variant — Figma "Top Navigation bar v2.1_WHITE" (1755:8143) ----
   Pages whose hero sits on a light ground (the audience pages and the industry
   hubs) take a white bar with dark type. The dropdown panel stays dark in both
   variants, so only the bar itself flips.
   Opt in with <body class="nav-light">. */
body.nav-light .nav { background: var(--n-white); color: var(--n-black-2); border-bottom-color: var(--n-gray-800); }
/* The rails are the stage's own structure, not part of the bar's colour scheme:
   they stay --n-gray-800 in both variants so the line is one continuous grey
   from the nav, down the page, to the footer (whose rails never flipped). */
body.nav-light .nav__lockup path[fill="white"] { fill: #181818; }
body.nav-light .nav__link { color: var(--n-gray-700); }
body.nav-light .nav__link:hover, body.nav-light .nav__group.is-open > .nav__link { color: var(--n-black-2); }
body.nav-light .nav__cta { color: var(--n-black-2); --bracket: var(--n-gray-600); }
body.nav-light .nav__toggle { background: var(--n-white); color: var(--n-black-2); }
/* The mobile sheet is deliberately NOT inverted on light pages. Figma draws the
   same "Mobile_Top Navigation menu v2.1" component under both the dark bar
   (1755:8309) and the white bar (1868:11006): the bar stays white, the sheet
   that drops below it stays --n-black. The menu is one component in the
   designer's model, so it must read identically on every page — only the bar
   above it changes. Nothing to override here; the dark base styles apply. */


/* The frame is the page ground, so on a white bar it has to be white too
   (Figma Finance_Hub_template 1735:27709 runs the white margin the full page
   height). --curtain drives the strips outside the stage so they match. The rails
   keep the one shared grey, so the line reads continuous from nav to footer.

   :not(.footer) because .framed is also the footer's padding shell, and the
   footer stays dark on every page. */
body.nav-light .framed:not(.footer) { background: var(--n-white); --curtain: var(--n-white); }

/* Figma shortens the bar CTA on mobile: "Talk to Our Team" -> "Talk to Us"
   (desktop 1755:7977, mobile 1755:8309). */
.nav__cta-short { display: none; }
@media (max-width: 991px) {
  .nav__cta-long { display: none; }
  .nav__cta-short { display: inline; }
}

/* ---- Related posts ----
   Three flush square tiles below the subscribe band, rendered by
   components/site-chrome.js.

   No block padding, so the row meets the subscribe band above and the footer
   below. The grid runs to the rails — the frame's border lines — not the viewport
   edge: .framed's curtains paint over everything outside the stage, so a
   full-bleed grid would hide a rail's worth of the outer tiles (374px each side
   past --stage-max). Each tile is a third of the stage, not of the screen.

   Each .related__body carries a full border minus its top (the cover supplies
   that edge); tiles after the first drop the left border so neighbours don't
   double up to 2px. */
.related { background: var(--bg); padding-block: 0; }
.related > .container { padding-inline: var(--rail); }
.related__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.related__tile {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
  text-align: center;
}
.related__media { display: block; overflow: hidden; }
.related__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
/* Hover/focus zoom, clipped by .related__media's overflow. */
.related__tile:hover .related__media img,
.related__tile:focus-visible .related__media img { transform: scale(1.05); }
.related__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 28px 30px 40px;
  border: 1px solid var(--n-gray-800);
  border-top: 0;
  flex: 1;   /* equal-height copy blocks across the row */
}
.related__tile + .related__tile .related__body { border-left: 0; }
.related__tile h3 {
  margin: 0;
  font-size: 1.125rem;
  line-height: 1.3;
  font-weight: 500;
  letter-spacing: var(--ls-body);
  color: var(--fg);
  max-width: 20em;
}
.related__byline { display: flex; align-items: center; justify-content: center; gap: 10px; }
.related__avatar { width: 26px; height: 26px; flex: none; object-fit: cover; }
.related__byline p { margin: 0; font-size: var(--fs-small); color: var(--fg-muted); }
/* 3-up then straight to 1-up — never 2-up, which orphans the last tile. */
@media (max-width: 859px) {
  .related__grid { grid-template-columns: minmax(0, 1fr); }
  /* Stacked, the doubled edge is horizontal. */
  .related__tile + .related__tile .related__body { border-left: 1px solid var(--n-gray-800); }
  .related__tile + .related__tile .related__media { border-top: 0; }
}

.article-cta { margin-top: 4em; text-align: center; }
.article-cta .btn {
  /* .prose underlines every link; a button is not body copy. */
  text-decoration: none;
  /* Matches the CTA buttons elsewhere; the default grey read as disabled here. */
  --bracket: var(--n-white);
}
