/* ============================================================================
   ARGOS - base.css
   Shared foundation for every page. A rule belongs here when 2+ pages use it;
   anything used by exactly one page lives in that page's own stylesheet.

   Tokens are from the source design-system extraction (2026-07-21). The
   normative spec is DESIGN-SYSTEM.md at the project root; its frontmatter
   maps to the custom properties in §1 below.

   Palette as of 2026-08-13: the site ships Argos blue as the single accent.
   The legacy cyan is kept, commented, directly beneath it. Swapping
   the two commented blocks in §1 "Active palette" switches the whole site;
   nothing else anywhere needs editing.
   ========================================================================= */

/* ---------- 1. Tokens ---------- */
:root{
  /* Every neutral carries a blue cast - DESIGN-SYSTEM.md "The Tinted Neutral Rule".
     Pure #000/#fff are prohibited as surface or text values. Re-tinting cost no
     contrast and gained some: muted-on-hovered-card went 4.52:1 -> 4.89:1. */
  --bg-base:#0A0B0D;
  --bg-raised:#14161A;
  --bg-hover:#1B1E24;
  --bg-inverse:#F4F7FB;

  --text-primary:#E8EEF6;        /* from Argos's own #E3EEF9 cool near-white */
  --text-secondary:#A9B6C6;
  --text-muted:#7F8C9E;          /* floor of the system - 4.89:1 on --bg-hover */
  --text-on-light:#0A0B0D;
  --text-on-light-soft:rgba(10,11,13,.72);  /* body copy on the gradient CTA panel */

  /* ---- Palette definitions. Both are always defined; neither is active
         until the "Active palette" block below aliases one of them. Nothing
         outside that block should reference --cyan-* or --blue-* directly. */

  /* A - legacy cyan. Signature gradient is cyan only; the lime terminus of
         the source gradient is deliberately dropped. */
  --cyan-accent:#00BFBF;
  --cyan-bright:#17EFEF;
  --cyan-rgb:0,191,191;
  --cyan-grad:linear-gradient(98.57deg,#5FF4F4 22.04%,#00BFBF 98.78%);
  --cyan-check:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.5l2.5 2.5L12 5.5' stroke='%2317EFEF' stroke-width='1.8' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* B - Argos blue.
         --blue-bright is Argos's own accentLight, verbatim.
         --blue-accent is raised from Argos's accentDeep #0066BF, a border/link
         colour on the original site, not a text colour: it measures
         3.42:1 on this #0A0B0D canvas and 2.76:1 on Argos's own #162333, so it
         fails WCAG AA as nav-link text on either. #3E9EE8 sits between the two
         recorded blues and measures 6.81:1 here.
         The gradient stays in the light half of the ramp because .cta-panel
         sets black display text on it: running down to #0066BF would drop that
         to 3.65:1, whereas #9FD4FE -> #64BCFC holds 13.3:1 -> 10.1:1, matching
         cyan's 9.2:1. */
  --blue-accent:#3E9EE8;
  --blue-bright:#64BCFC;
  --blue-rgb:62,158,232;
  --blue-grad:linear-gradient(98.57deg,#9FD4FE 22.04%,#64BCFC 98.78%);
  --blue-check:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8.5l2.5 2.5L12 5.5' stroke='%2364BCFC' stroke-width='1.8' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* Severity ramp. Semantic only - DESIGN-SYSTEM.md "The Earned Colour Rule": these
     appear where they encode a real finding severity or control state, never as
     decoration. On --bg-raised: 9.49 / 6.04 / 4.63 / 9.66:1. */
  --sev-warn:#F2B01E;
  --sev-alert:#ED712F;
  --sev-critical:#E5484D;
  --sev-clear:#3DD68C;

  /* ================= ACTIVE PALETTE - THE ONLY SWITCH =================
     Exactly one of the two blocks below is uncommented. To switch the whole
     site, comment this one and uncomment the other. No other edit is needed
     anywhere: every accent use in every stylesheet resolves through these five
     names, and each alpha tint routes through --accent-rgb.
     To preview the inactive palette without editing, add class="palette-cyan"
     to <html> - set from the PALETTE constant in _build/layout.mjs, or toggled
     live in devtools.
     ================================================================== */

  /* B - Argos blue. ACTIVE. */
  --accent:var(--blue-accent);
  --accent-bright:var(--blue-bright);
  --accent-rgb:var(--blue-rgb);
  --grad-brand:var(--blue-grad);
  --check-svg:var(--blue-check);

  /* A - legacy cyan. Inactive; this is what the site shipped until
         2026-08-13. Uncomment, and comment the block above, to restore it.
  --accent:var(--cyan-accent);
  --accent-bright:var(--cyan-bright);
  --accent-rgb:var(--cyan-rgb);
  --grad-brand:var(--cyan-grad);
  --check-svg:var(--cyan-check);
  */

  --strip-bg:#2B2F36;            /* one clear step above --bg-raised, deliberately not equal to it */

  --border-subtle:rgba(255,255,255,.07);
  --border-subtle-hover:rgba(255,255,255,.12);
  --border-accent-hover:rgba(var(--accent-rgb),.25);

  --font-display:'Outfit',sans-serif;
  --font-body:'Archivo',sans-serif;

  /* Five functional steps and one body size - DESIGN-SYSTEM.md "The Five Step Rule".
     The system previously carried ten sizes between 10px and 22px. Anything
     that seems to need a sixth step wants a weight or colour change instead. */
  --fs-label:12px;               /* uppercase labels, eyebrows, badges */
  --fs-small:14px;               /* metadata, footer links, captions */
  --fs-body:16px;                /* all sustained reading */
  --fs-lead:18px;                /* leads and card headings */
  --lh-small:20px;
  --lh-body:24px;
  --lh-lead:28px;

  --container:1500px;            /* site-wide content width, hero included */
  --measure:68ch;                /* reading width for long-form prose */
  --section-y:112px;

  /* Spacing anchors - the five values the layout is actually built from.
     Everything else is a local adjustment, not a scale step. */
  --gap-grid:20px;               /* gap of every .grid */
  --pad-card:28px;               /* card padding */
  --pad-panel:48px;              /* panel and split-panel padding */
  --gap-col:64px;                /* the two-column gutter (editorial, pull, hero) */

  --r-btn:8px;
  --r-card:10px;
  --r-panel:16px;
  --r-card-lg:20px;

  /* One brand curve. Ease-out-quint: fast arrival, no overshoot - the same
     curve the .path connector draws with. The old --ease-spring overshot
     (fourth parameter 1.35), which is exactly the bounce DESIGN-SYSTEM.md bans. */
  --ease-out:cubic-bezier(.22,1,.36,1);
  --hover-fill:background-color .25s var(--ease-out);

  --header-h:88px;               /* offsets in-page anchor targets under the fixed header */
}

/* Preview hook for the inactive palette. Higher specificity than :root, so it
   wins regardless of source order. Same five names, nothing else. */
:root.palette-cyan{
  --accent:var(--cyan-accent);
  --accent-bright:var(--cyan-bright);
  --accent-rgb:var(--cyan-rgb);
  --grad-brand:var(--cyan-grad);
  --check-svg:var(--cyan-check);
}

/* ---------- 2. Reset and element defaults ---------- */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{
  background:var(--bg-base);
  color:var(--text-primary);
  font-family:var(--font-body);
  font-size:var(--fs-body);line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
ul{list-style:none}
:target{scroll-margin-top:calc(var(--header-h) + 16px)}

/* Available to screen readers, removed from the visual layout - table captions,
   redundant labels and similar. */
.visually-hidden{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* Skip link - the fixed header means keyboard users would otherwise tab the
   whole nav on every page of the site before reaching content. */
.skip-link{
  position:absolute;left:16px;top:-60px;z-index:1000;
  background:var(--accent-bright);color:var(--text-on-light);
  padding:10px 18px;border-radius:var(--r-btn);
  font-family:var(--font-display);font-size:var(--fs-small);font-weight:600;
  transition:top .2s;
}
.skip-link:focus{top:16px}

/* ---------- 3. Layout and section rhythm ---------- */
.container{width:95%;max-width:var(--container);margin:0 auto}
.container-narrow{width:90%;max-width:900px;margin:0 auto}

/* Section rhythm. DESIGN-SYSTEM.md "The Rhythm Rule": no two adjacent sections may
   share both a structure and a ground. These variants are what make that
   possible, and their absence is why 20 pages rendered as one repeated band.
   --knockout is the current ground, published so line-work components can cut
   their connectors out of whatever they are sitting on. */
:root{--knockout:var(--bg-base)}
.section{padding:var(--section-y) 0 50px}
.section-tight{padding:64px 0 40px}
/* Open: sections carrying an argument, where the reader needs room. */
.section-open{padding:calc(var(--section-y) + 24px) 0 84px}
/* Dense: list-like sections. DESIGN-SYSTEM.md "compress list-like sections, open up
   the sections carrying an argument". */
.section-dense{padding:76px 0 60px}
/* Raised ground. Symmetric padding and two hairlines, because unlike the canvas
   sections this one has a visible edge and the 112/50 asymmetry reads as a
   mistake once you can see where the band starts and stops. */
.section-alt{
  background:var(--bg-raised);
  border-top:1px solid var(--border-subtle);
  border-bottom:1px solid var(--border-subtle);
  padding:96px 0;
  --knockout:var(--bg-raised);
}
.section-alt + .section,.section-alt + .section-dense,.section-alt + .section-open{padding-top:96px}

h1,h2,h3,h4,.display{font-family:var(--font-display)}
h1{font-size:clamp(36px,5.2vw,62px);line-height:1.13;font-weight:600;letter-spacing:-.02em}
h2.section-title{font-size:clamp(30px,3.4vw,42px);line-height:1.14;font-weight:600;letter-spacing:-.01em;margin-bottom:20px}
/* Class, not h3.class: a subsection heading has to be able to sit at whatever
   level the document outline requires. "Advisory board" is a peer of "Meet the
   team", so it is an h2 that happens to be set at subsection scale. */
.subsection-title{font-size:clamp(22px,2.4vw,30px);line-height:1.2;font-weight:600;letter-spacing:-.01em;margin-bottom:16px}
.section-head{margin-bottom:56px}
/* :not(.section-index) throughout. The index is a <p>, and a bare `.section-head p`
   outranks `.section-index` on specificity, so the head'''s lead styling was
   silently repainting the pill at 18px in secondary ink. Same guard as
   `.statement p:not(.section-index)` below, for the same reason. */
.section-head p:not(.section-index){color:var(--text-secondary);font-size:var(--fs-lead);line-height:var(--lh-lead);max-width:70ch}
.section-head.centered{text-align:center}
.section-head.centered p:not(.section-index){margin-inline:auto}
/* Indexed head: index, title, then the lead directly beneath the title. The
   lead sits at a shorter measure than a plain head so the block reads as one
   column of argument rather than as a paragraph that happens to follow a
   heading. */
.section-head--indexed{
  display:grid;grid-template-columns:minmax(0,1fr);gap:18px;align-items:start;
  justify-items:start;
}
.section-head--indexed h2.section-title{margin-bottom:0}
.section-head--indexed p:not(.section-index){max-width:62ch}

/* Section index. A tracked number and label above a section title, in the
   accent-outlined pill the page hero used to wear.
   The treatment moved rather than died: on an h1 it was decoration, because
   "About Us" is a filing label and the ring gave it the weight of a button.
   Attached to a numbered step in a sequence it is a marker, and it only appears
   where the order of the sections is part of the claim. One badge language, used
   once per section, and never floating above an h1.
   The declarations are the page hero's former eyebrow verbatim, so the figure
   and the label are separated by an ordinary word space carrying the .08em
   tracking, and .i inherits rather than overriding anything.
   The hairline that used to trail from here to the container edge is not coming
   back. See DESIGN-SYSTEM.md section 6, Numbering Rule. */
.section-index{
  display:inline-block;font-family:var(--font-display);
  font-size:var(--fs-label);line-height:18px;font-weight:500;letter-spacing:.08em;
  color:var(--accent);border:1px solid rgba(var(--accent-rgb),.3);
  padding:4px 12px;border-radius:20px;margin-bottom:24px;
  text-transform:uppercase;
}

/* Page kicker. It names the section of the site a page belongs to, one line
   above the h1, in the voice of a running head.
   It used to be an accent-outlined uppercase pill. A small badge floating above
   the h1 is the most recognisable machine-default hero move there is, and it was
   also lying about its own importance: "About Us" is a filing label, not a
   claim, and the accent ring gave it the emphasis of a button. Plain text at
   reading size, set in the capitalisation the author actually wrote. */
.eyebrow{
  display:block;font-family:var(--font-display);
  font-size:var(--fs-body);line-height:1.4;font-weight:500;letter-spacing:normal;
  color:var(--text-secondary);margin-bottom:20px;
}

.lead{font-size:var(--fs-lead);line-height:var(--lh-lead);color:var(--text-secondary);max-width:70ch}
.muted{color:var(--text-muted)}

/* ---------- 4. Buttons - .button-common system, verbatim from tokens ---------- */
.btn{
  display:inline-block;height:44px;line-height:44px;padding:0 28px;
  border-radius:var(--r-btn);font-family:var(--font-display);
  font-size:var(--fs-body);font-weight:500;text-align:center;cursor:pointer;
  transition:var(--hover-fill),color .25s var(--ease-out),border-color .25s var(--ease-out);
  border:none;
}
/* Solid accent, not an alpha wash: rgba(accent,.5) composited on the canvas
   measured 2.48:1, under the 3:1 that WCAG 2.2 SC 1.4.11 requires of a focus
   indicator. Solid --accent measures 6.81:1 on canvas and 6.26:1 on raised. */
.btn:focus-visible,a:focus-visible,button:focus-visible,summary:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;border-radius:2px;
}
/* On the light gradient panel the accent ring would sit on its own hue family
   at ~2:1; dark ink measures 12.49:1 on the pale terminus. */
.cta-panel a:focus-visible,.cta-panel button:focus-visible{outline-color:var(--text-on-light)}
/* Disabled: 40% opacity, pointer events off. aria-disabled is honoured so
   links acting as buttons can disable without losing focusability. */
.btn[disabled],.btn[aria-disabled="true"]{opacity:.4;cursor:default;pointer-events:none}
.btn-aqua{background:var(--accent-bright);color:var(--text-on-light)}
.btn-aqua:hover{background:var(--bg-inverse)}
.btn-outline{background:transparent;border:1px solid var(--text-primary);color:var(--text-primary);line-height:42px}
.btn-outline:hover{background:var(--accent-bright);color:var(--text-on-light);border-color:var(--accent-bright)}
.btn-white{background:var(--bg-inverse);color:var(--text-on-light)}
.btn-white:hover{background:var(--accent)}
.btn-black{background:var(--text-on-light);color:var(--text-primary)}
.btn-black:hover{background:var(--bg-inverse);color:var(--text-on-light)}
/* On the gradient panel the outline button sits on light ground, so it inverts. */
.btn-outline-dark{background:transparent;border:1px solid var(--text-on-light);color:var(--text-on-light);line-height:42px}
.btn-outline-dark:hover{background:var(--text-on-light);color:var(--bg-inverse)}
.btn-row{display:flex;gap:14px;flex-wrap:wrap}

/* Inline text link - used in prose and card bodies across pages. */
.link{color:var(--accent-bright);transition:color .2s}
.link:hover{color:var(--text-primary);text-decoration:underline;text-underline-offset:3px}

/* ---------- 5. Header and navigation ---------- */
.site-header{
  position:fixed;inset:0 0 auto 0;z-index:990;
  padding:22px 40px;display:flex;align-items:center;justify-content:space-between;
  /* padding is deliberately not transitioned: it is a layout property, and
     animating it forces layout on every scroll frame. */
  transition:background .3s ease;
}
.site-header.scrolled{background:rgba(10,11,13,.88);backdrop-filter:blur(12px);padding:12px 40px}
.brand{display:flex;align-items:center;gap:12px}
.brand img{height:44px;width:44px;object-fit:contain}
.brand span{font-family:var(--font-display);font-weight:600;font-size:var(--fs-lead);letter-spacing:.02em}
.brand small{display:block;font-size:var(--fs-label);font-weight:400;color:var(--text-muted);letter-spacing:.12em;text-transform:uppercase}

.nav{display:flex;align-items:center;gap:32px}
/* Home appears only in the full-screen mobile menu (the brand mark is the way
   home on desktop); the 767px block turns it back on. */
.nav .nav-mobile-only{display:none}
.nav a,.nav .nav-link{
  font-family:var(--font-display);font-size:var(--fs-body);font-weight:500;color:var(--text-primary);
  transition:color .2s;background:none;border:none;cursor:pointer;
}
/* :not(.btn) is load-bearing. Unscoped, this rule is (0,2,1) and out-specifies
   .btn-outline:hover at (0,2,0), so it took `color` while the button took
   `background` - rendering the Log in label at 1.6:1 against its own fill on
   all 20 pages, at the exact moment of click. */
.nav a:not(.btn):hover,.nav .nav-link:hover{color:var(--accent)}
/* Current page. aria-current is the source of truth, so the visual state cannot
   drift from what assistive tech is told. */
.nav a[aria-current="page"]{color:var(--accent)}
.nav-item[data-current="true"]>.nav-link{color:var(--accent)}
.nav .btn{height:38px;line-height:38px;padding:0 20px;font-size:var(--fs-small)}
.nav-toggle{display:none;background:none;border:1px solid var(--border-subtle-hover);border-radius:8px;color:var(--text-primary);width:42px;height:42px;font-size:var(--fs-lead);cursor:pointer}

/* Dropdowns. The trigger is a real <button aria-expanded>, so the state is
   announced; JS toggles .open. Hover is layered on top for pointer devices
   only - on touch, hover would fire on the tap that also navigates. */
.nav-item{position:relative;display:flex;align-items:center}
.nav-link{display:inline-flex;align-items:center;gap:6px;padding:0}
.nav-link .caret{
  width:9px;height:9px;flex:none;
  border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;
  transform:rotate(45deg) translateY(-2px);transition:transform .25s;
}
.nav-item.open .nav-link .caret{transform:rotate(-135deg) translateY(-2px)}
.nav-menu{
  position:absolute;top:100%;left:-16px;margin-top:14px;min-width:250px;
  background:rgba(20,22,26,.97);backdrop-filter:blur(12px);
  border:1px solid var(--border-subtle-hover);border-radius:var(--r-card);
  padding:8px;display:flex;flex-direction:column;gap:2px;
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .2s,transform .2s,visibility .2s;
  box-shadow:0 18px 40px rgba(0,0,0,.5);
}
.nav-item.open>.nav-menu{opacity:1;visibility:visible;transform:none}
@media (hover:hover){
  .nav-item:hover>.nav-menu{opacity:1;visibility:visible;transform:none}
}
.nav-menu a{
  font-size:var(--fs-small);line-height:var(--lh-small);font-weight:400;color:var(--text-secondary);
  padding:10px 12px;border-radius:6px;transition:background .2s,color .2s;
}
.nav-menu a:hover{background:var(--bg-hover);color:var(--text-primary)}
.nav-menu a[aria-current="page"]{color:var(--accent);background:rgba(var(--accent-rgb),.08)}

/* ---------- 6. Inner-page hero ---------- */
/* Compact header band for every page except home: eyebrow, h1, one lead line.
   Roughly half the homepage hero's height and carries no product visual. */
.page-hero{
  position:relative;padding:184px 0 72px;overflow:hidden;
  /* One soft accent wash so inner pages are not flat black above the fold.
     Decorative only - the copy carries the meaning. Lives on the section
     itself so it is not confined to a pseudo-element's box. */
  background:radial-gradient(ellipse at 82% 100%,rgba(var(--accent-rgb),.2),transparent 62%) no-repeat;
}
.page-hero h1{max-width:20ch;margin-bottom:22px}
.page-hero .lead{margin-bottom:32px}
.page-hero-wide h1{max-width:26ch}

/* Split hero. The right column was empty on every inner page - a layout that
   looked like it had two columns while only ever filling one. */
/* min-width:0 is load-bearing. A grid item defaults to min-width:auto, so the
   482px max-content width of the hero figure became the column's floor: below a
   482px viewport the column, and with it the stacked h1, ran off the screen. */
.page-hero-split .page-hero-inner{
  display:grid;grid-template-columns:1.05fr .95fr;gap:56px;align-items:center;
}
.page-hero-split .page-hero-inner > *{min-width:0}
.page-hero-split h1{max-width:18ch}
.page-hero-split .lead{margin-bottom:18px;max-width:52ch}
/* Source copy that previously floated under a caption with no role. */
.page-hero-sub{
  font-size:var(--fs-body);line-height:var(--lh-body);
  color:var(--text-muted);max-width:52ch;margin-bottom:32px;
}
.page-hero-visual figure{margin-left:auto;width:max-content;max-width:100%}
/* The asset is 482x346 native, so it is never upscaled - at this size in the
   hero it reads as deliberate rather than as an undersized afterthought. */
.page-hero-visual img{
  width:100%;max-width:482px;height:auto;
  border-radius:var(--r-card);border:1px solid var(--border-subtle);
}
.page-hero-visual figcaption{
  margin-top:14px;font-size:var(--fs-small);line-height:var(--lh-small);
  color:var(--text-muted);max-width:482px;
}

/* Hero side note. Source copy that continues the lead, set beside it instead of
   in a thin section of its own. DESIGN-SYSTEM.md: put something in the right half of an
   inner-page hero, or do not draw two columns at all. */
.page-hero-note{display:grid;gap:16px;max-width:54ch;margin-left:auto}
/* Secondary ink, not muted: the note is sustained reading opposite a Display
   h1, and the floor token dressed it as a caption. The first paragraph answers
   the h1, so it takes the Lead scale. */
.page-hero-note p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-secondary)}
.page-hero-note p:first-child{font-size:var(--fs-lead);line-height:var(--lh-lead)}

/* Breadcrumb - case studies and blog posts. */
.crumbs{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:var(--fs-small);color:var(--text-muted);margin-bottom:20px}
.crumbs a:hover{color:var(--accent)}
.crumbs span[aria-hidden]{opacity:.5}

/* ---------- 7. Cards and grids ---------- */
/* One card surface for the whole site. Variants add only what differs:
   --lift adds the hover rise, --lg the larger heading, --accent the tinted border. */
.card{
  background:var(--bg-raised);border:1px solid var(--border-subtle);
  border-radius:var(--r-card);padding:var(--pad-card);
  transition:border-color .3s,background .3s,transform .3s var(--ease-out);
}
.card:hover{background:var(--bg-hover);border-color:var(--border-accent-hover)}
.card--lift:hover{transform:translateY(-4px)}
/* No zero-offset coloured glow - DESIGN-SYSTEM.md "The No Glow Rule". Elevation is
   tonal: the accent card steps up the ramp instead. */
.card--accent{border-color:rgba(var(--accent-rgb),.35);background:var(--bg-hover)}
.card--flat:hover{background:var(--bg-raised);border-color:var(--border-subtle);transform:none}
/* Comparison pair. Grid items stretch to the tallest sibling, so a card holding
   one line against a card holding a list sits in a tall empty box. Centre it. */
.card--centered{display:flex;flex-direction:column;justify-content:center}
/* Overline inside a card: names the engagement while the h3 names the outcome.
   Sentence case at reading size, same register as the other running labels. */
.card .n{
  display:block;margin-bottom:8px;
  font-family:var(--font-display);font-size:var(--fs-small);line-height:1.4;
  font-weight:500;letter-spacing:normal;color:var(--text-muted);
}
.card h3{font-size:var(--fs-lead);line-height:26px;font-weight:600;margin-bottom:10px;color:var(--text-primary)}
.card--lg h3{font-size:clamp(22px,2.4vw,30px);line-height:1.2;letter-spacing:-.01em}
.card p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-muted)}
.card p + p{margin-top:12px}

/* Icon tile inside a card. */
.glyph{
  width:44px;height:44px;border-radius:10px;margin-bottom:20px;
  display:grid;place-items:center;
  background:rgba(var(--accent-rgb),.08);border:1px solid rgba(var(--accent-rgb),.2);
}
.glyph svg{width:22px;height:22px;stroke:var(--accent-bright);fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

/* Numbered step marker - the "how it works" flows on several pages. */
.step-num{
  width:44px;height:44px;border-radius:50%;margin-bottom:20px;
  display:grid;place-items:center;
  font-family:var(--font-display);font-size:var(--fs-body);font-weight:600;color:var(--accent-bright);
  background:rgba(var(--accent-rgb),.08);border:1px solid rgba(var(--accent-rgb),.2);
}

.grid{display:grid;gap:var(--gap-grid)}
.grid-2{grid-template-columns:repeat(2,1fr)}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-4{grid-template-columns:repeat(4,1fr)}
/* A grid must never render fewer items than it has columns. A four-column grid
   holding one team member reads as a broken layout, which is what About Us
   shipped. --min lets the grid size to its content instead. */
.grid-auto{grid-template-columns:repeat(auto-fit,minmax(var(--min,220px),max-content))}

/* ============================================================================
   7b. STRUCTURAL VOCABULARY - line-work and nodes
   ---------------------------------------------------------------------------
   DESIGN-SYSTEM.md has always said the first answer to "this section needs shape" is a
   connecting rule, a numbered progression or a hairline division, and that the
   bordered rectangle is the last answer. Until now the stylesheet shipped only
   the rectangle: .card, .split-panel, .panel and .quote are the same object at
   four widths. With nothing else available, every page reached for .card, which
   is how the site arrived at 61 byte-identical card elements and four
   consecutive card grids on a single page.

   These are the missing answers. None of them is a box. Each one carries the
   same vocabulary as the homepage attack-path constellation: node, connector,
   live path, flagged terminus.
   ========================================================================= */

/* These carry their own numbering as content, so the marker is suppressed here
   rather than in the reset: `ol` still renders normally in prose and articles. */
.ledger,.path,.arc,.attests,.roster,.ramp{list-style:none}

/* ---------- Ledger - indexed hairline rows ---------- */
/* For a set of peers that each carry a label and an explanation. This is the
   replacement for the six-card capability grid: same content, no containers,
   and the eye reads down a ruled column instead of bouncing around a lattice.
   Compress it with .section-dense; a list should read as a list. */
.ledger{border-top:1px solid var(--border-subtle)}
.ledger > li{
  display:grid;
  grid-template-columns:56px minmax(190px,.38fr) 1fr;
  gap:0 40px;align-items:baseline;
  padding:26px 16px 26px 8px;
  border-bottom:1px solid var(--border-subtle);
  /* Background and border only. Animating the padding would animate layout. */
  transition:background .35s ease,border-color .35s ease;
}
/* Hover marks rows that lead somewhere, and only the posts archive links its
   rows. The capability, deliverable and terms ledgers are static text; a hover
   state there promises a click that does nothing. */
.ledger--posts > li:hover{background:rgba(255,255,255,.022);border-bottom-color:var(--border-subtle-hover)}
.ledger .n{
  font-family:var(--font-display);font-size:var(--fs-label);line-height:18px;
  font-weight:500;letter-spacing:.08em;color:var(--text-muted);
  transition:color .35s ease;
}
.ledger h3{
  font-family:var(--font-display);font-size:var(--fs-lead);line-height:26px;
  font-weight:600;letter-spacing:-.01em;color:var(--text-primary);
}
/* Secondary ink: up to 64ch of multi-sentence copy is sustained reading, which
   DESIGN-SYSTEM.md assigns to ink-secondary; muted is for captions and metadata. */
.ledger p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-secondary);max-width:64ch}
/* Trailing marker: the output a capability produces, in the severity ramp where
   the capability genuinely ranks risk. Earned Colour Rule still applies. */
.ledger .out{
  grid-column:3;justify-self:start;margin-top:12px;
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--font-display);font-size:var(--fs-label);line-height:18px;
  font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-muted);
}
.ledger .out .dot{width:8px;height:8px;border-radius:50%;flex:none}
.ledger .out .dot ~ .dot,.ledger .out .dot:not(:first-child){margin-left:12px}

/* Terms-only variant: a ruled list of short phrases with no explanation, for
   constraint lists and criteria. Two columns, tighter, because there is no
   description column to balance. */
/* Archive variant: the first column carries a date rather than an index, so it
   needs room for "12 August 2026" instead of "01". */
.ledger--posts > li{grid-template-columns:150px minmax(240px,.5fr) 1fr;padding:24px 16px 24px 8px}
.ledger--posts .n{font-size:var(--fs-small);letter-spacing:normal}
.ledger--posts h3{font-size:var(--fs-lead);line-height:26px}
.ledger--posts h3 a{color:inherit;text-decoration:none}
.ledger--posts > li:hover h3 a{color:var(--accent-bright)}

.ledger--terms{max-width:860px}
.ledger--terms > li{grid-template-columns:56px 1fr;padding:18px 16px 18px 8px}
/* The figures carry the accent here. On the capability ledger the number is a
   row marker and the accent belongs to the severity dots in .out; on a list of
   constraints there is nothing else to colour and the numbers are the only
   line-work the row has. */
.ledger--terms .n{color:var(--accent)}
.ledger--terms h3{font-weight:500;color:var(--text-secondary)}

/* ---------- Brief - a titled prose block under a rule ---------- */
/* Two or three substantial passages side by side. .card puts each one in a
   bordered rectangle, which is how About Us ended up as a grid of boxes holding
   nothing but paragraphs. A rule is enough to say where one ends. */
.brief{border-top:1px solid var(--border-subtle-hover);padding-top:30px}
.brief h3{
  font-family:var(--font-display);font-size:clamp(20px,2vw,26px);line-height:1.2;
  font-weight:600;letter-spacing:-.01em;color:var(--text-primary);margin-bottom:18px;
}
/* Secondary ink for the same reason as .ledger p: 56ch prose is reading, not a
   caption. */
.brief p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-secondary);max-width:56ch}
.brief p + p{margin-top:14px}
.brief .link{display:inline-block;margin-top:20px}

/* ---------- Path - nodes on a live connector ---------- */
/* The system's signature, finally reusable. Any sequence renders as numbered
   nodes joined by one rule, the accent travelling it, the terminus filled.
   This is the answer to "How it works", not a four-column card grid. */
.path{display:grid;grid-auto-flow:column;grid-auto-columns:1fr}
.path > li{position:relative;padding-right:36px}
/* The rule. Two layers: a dormant hairline, and a live accent segment that
   scales in from the left on reveal. transform only, never a layout property. */
.path > li::before,
.path > li::after{content:"";position:absolute;left:0;right:0;top:17px;height:1px}
.path > li::before{background:var(--border-subtle-hover)}
.path > li::after{
  background:var(--accent);transform-origin:left center;transform:scaleX(1);
  transition:transform .85s var(--ease-out);
}
.path > li:last-child::before,.path > li:last-child::after{display:none}
/* Armed but not yet revealed: the live segment is collapsed. Defaulting to
   drawn means a JS failure leaves a complete line rather than a bare one. */
.reveal-armed.path > li::after{transform:scaleX(0)}
.reveal-armed.path.in > li::after{transform:scaleX(1)}
.reveal-armed.path.in > li:nth-child(2)::after{transition-delay:.18s}
.reveal-armed.path.in > li:nth-child(3)::after{transition-delay:.36s}
.reveal-armed.path.in > li:nth-child(4)::after{transition-delay:.54s}
.reveal-armed.path.in > li:nth-child(5)::after{transition-delay:.72s}
.reveal-armed.path.in > li:nth-child(6)::after{transition-delay:.9s}
.path .node{
  position:relative;z-index:1;
  width:34px;height:34px;border-radius:50%;margin-bottom:26px;
  display:grid;place-items:center;
  font-family:var(--font-display);font-size:var(--fs-small);font-weight:600;
  color:var(--accent-bright);
  /* Knocked out of the current ground so the connector passes behind, not through. */
  background:var(--knockout);border:1px solid rgba(var(--accent-rgb),.35);
}
/* The terminus is filled. A path that ends nowhere is a row of dots. */
.path > li:last-child .node{
  background:var(--accent-bright);color:var(--text-on-light);border-color:var(--accent-bright);
}
.path h3{font-family:var(--font-display);font-size:var(--fs-lead);line-height:26px;font-weight:600;color:var(--text-primary);margin-bottom:10px}
.path p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-muted);max-width:32ch}

/* ---------- Arc - an ascending progression ---------- */
/* For beats that are not peers but stages that grow: assess, then remediate,
   then hold. Three equal boxes say these are interchangeable. A staircase says
   one leads to the next and the last is worth the most, which is the actual
   argument the copy is making. */
/* gap:0 is load-bearing. The riser sits at each column's right edge and the next
   tread starts at the next column's left edge, so any column gap opens a break
   in the staircase. Breathing room comes from padding-right, which is inside
   the column and therefore inside the rule. */
.arc{display:grid;grid-template-columns:repeat(3,1fr);gap:0;align-items:start}
.arc > li{position:relative;padding-right:40px}
.arc > li:nth-child(1){margin-top:96px}
.arc > li:nth-child(2){margin-top:48px}
.arc > li:nth-child(3){margin-top:0;padding-right:0}
/* Tread: the horizontal rule at each step's own level. */
.arc > li::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--border-subtle-hover);
}
/* Riser: the vertical climb to the next step's level. */
.arc > li:not(:last-child)::after{
  content:"";position:absolute;right:0;top:-48px;width:1px;height:48px;
  background:var(--border-subtle-hover);
}
/* The final tread is live: the payoff of the progression is where it arrives. */
.arc > li:last-child::before{background:var(--accent)}
/* Stage label. Sentence case for the same reason .section-index is: it is a
   running head for the step, not a badge. */
.arc .n{
  display:block;margin:22px 0 12px;
  font-family:var(--font-display);font-size:var(--fs-small);line-height:1.4;
  font-weight:500;letter-spacing:normal;color:var(--text-muted);
}
.arc > li:last-child .n{color:var(--accent-bright)}
.arc h3{
  font-family:var(--font-display);font-size:clamp(20px,2vw,26px);line-height:1.2;
  font-weight:600;letter-spacing:-.01em;color:var(--text-primary);margin-bottom:12px;
}
.arc p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-muted);max-width:38ch}

/* ---------- Pull - one testimonial at scale, the rest as evidence ---------- */
/* Three equal quote boxes flatten three unequal endorsements into wallpaper.
   Lead with the one that says the most and let the others corroborate it. */
.pull{display:grid;grid-template-columns:1.25fr 1fr;gap:var(--gap-col);align-items:start}
/* Scoped to .pull-lead, not to .pull: .attests sits inside .pull, so an
   unscoped rule gave the two corroborating quotes the lead quote's display face
   and its accent rule, which is exactly the flattening this component exists to
   undo. */
.pull-lead blockquote{
  font-family:var(--font-display);font-size:clamp(20px,2vw,26px);line-height:1.4;
  font-weight:600;letter-spacing:-.01em;color:var(--text-primary);max-width:38ch;
}
.pull-lead blockquote::before{
  content:"";display:block;width:44px;height:2px;background:var(--accent);margin-bottom:28px;
}
.pull .attr{margin-top:28px}
.pull .attr b{display:block;font-family:var(--font-display);font-size:var(--fs-body);font-weight:600;color:var(--text-primary)}
.pull .attr span{display:block;font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-muted)}
/* Corroborating quotes: hairline-separated, body scale, no container. */
.attests{border-top:1px solid var(--border-subtle)}
.attests > li{padding:24px 0;border-bottom:1px solid var(--border-subtle)}
.attests blockquote{
  font-family:var(--font-body);font-size:var(--fs-body);line-height:var(--lh-body);
  font-weight:400;letter-spacing:normal;color:var(--text-secondary);margin-bottom:14px;
}
.attests b{font-family:var(--font-display);font-size:var(--fs-small);font-weight:600;color:var(--text-primary)}
.attests span{font-size:var(--fs-label);line-height:18px;color:var(--text-muted)}
.attests .lang-note{font-style:italic}

/* ---------- Ramp - the severity legend ---------- */
/* The one place the severity hues belong outside real findings data: saying
   what "prioritised" means. It is a legend, not invented counts. */
.ramp{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.ramp li{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--font-display);font-size:var(--fs-label);line-height:18px;
  font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);
  padding:5px 14px 5px 11px;border-radius:20px;
  background:rgba(255,255,255,.04);border:1px solid var(--border-subtle);
}
.ramp .dot{width:8px;height:8px;border-radius:50%;flex:none}
.path .ramp{margin-top:18px}
.dot-critical{background:var(--sev-critical)}
.dot-alert{background:var(--sev-alert)}
.dot-warn{background:var(--sev-warn)}
.dot-clear{background:var(--sev-clear)}

/* ---------- Editorial - copy beside a visual, no container ---------- */
/* .split-panel puts this same content in a raised rectangle, and features.html
   stacks five of them in a row. This is the unboxed version: the copy sits on
   the page and only the visual carries an edge. */
.editorial{display:grid;grid-template-columns:1fr 1.15fr;gap:var(--gap-col);align-items:center}
.editorial--reverse > :first-child{order:2}
.editorial h3{
  font-family:var(--font-display);font-size:clamp(22px,2.4vw,30px);line-height:1.2;
  font-weight:600;letter-spacing:-.01em;margin-bottom:16px;
}
.editorial p{color:var(--text-secondary);font-size:var(--fs-lead);line-height:var(--lh-lead);max-width:52ch}

/* ---------- Roster - people without boxes ---------- */
/* A portrait is already a shape. Putting it inside a bordered rectangle adds
   nothing and turns a team into a card grid. */
.roster{display:flex;flex-wrap:wrap;gap:56px 64px}
.roster > li{width:min(200px,100%);text-align:left}
.roster img{
  width:104px;height:104px;border-radius:50%;object-fit:cover;
  margin-bottom:18px;background:var(--bg-hover);
  border:1px solid var(--border-subtle);
  transition:border-color .35s ease;
}
.roster > li:hover img{border-color:var(--border-accent-hover)}
.roster h3{font-family:var(--font-display);font-size:var(--fs-lead);line-height:26px;font-weight:600;color:var(--text-primary);margin-bottom:4px}
.roster p{font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-muted);margin-bottom:12px}
.roster .in{display:inline-grid;place-items:center;width:32px;height:32px;border-radius:50%;border:1px solid var(--border-subtle);transition:background .3s,border-color .3s}
.roster .in:hover{background:var(--bg-hover);border-color:var(--border-accent-hover)}
.roster .in svg{width:14px;height:14px;fill:var(--text-muted);transition:fill .3s}
.roster .in:hover svg{fill:var(--accent-bright)}

/* ---------- Statement - a claim at display scale, no container ---------- */
/* Replaces the accent-tinted .panel that About Us built with inline styles.
   The claim is the only thing in the band, so it does not need a box to be
   found; it needs room and a rule. */
/* The rule is the only thing tying the claim to its action across a wide
   container, so it carries the live hairline rather than the resting one. */
.statement{
  border-top:1px solid var(--border-subtle-hover);padding-top:44px;
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:40px 64px;align-items:end;
}
/* :not(.section-index) so a statement may carry its own index label without the
   label inheriting display scale. */
.statement p:not(.section-index){
  font-family:var(--font-display);font-size:clamp(24px,3vw,38px);line-height:1.22;
  font-weight:600;letter-spacing:-.01em;color:var(--text-primary);max-width:44ch;
}
.statement .lede{
  font-family:var(--font-body);font-size:var(--fs-lead);line-height:var(--lh-lead);
  font-weight:400;letter-spacing:normal;color:var(--text-secondary);max-width:46ch;margin-top:20px;
}
/* Sub-scale variant: a closing claim that lives inside an indexed section,
   where full display scale would compete with the section title. Same hairline
   and action layout; the type steps down to the subsection scale. */
.statement--sub p:not(.section-index){font-size:clamp(20px,2vw,26px);line-height:1.3}

/* ---------- 8. Lists ---------- */
.checklist{display:grid;grid-template-columns:1fr 1fr;gap:12px 24px}
.checklist-1{grid-template-columns:1fr}
.checklist li{
  display:flex;gap:10px;align-items:flex-start;
  font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-secondary);
}
.checklist li::before{
  content:"";flex:0 0 16px;height:16px;margin-top:2px;border-radius:4px;
  background:rgba(var(--accent-rgb),.12);
  /* A data-URI cannot read a custom property, so the whole URL is the token,     this was the one place the accent used to be hardcoded. */
  background-image:var(--check-svg);
}
.checklist-lg li{font-size:var(--fs-body);line-height:var(--lh-body)}

/* Pill list - capability tag rows (Features, For MSPs). */
.pills{display:flex;flex-wrap:wrap;gap:10px}
/* One item per row. The About Us list is six phrases describing delivery
   realities - they read as a list, not as a tag cloud. align-items:flex-start
   keeps each pill sized to its own content instead of stretching full width. */
.pills-stack{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;gap:8px}
.pills li{
  font-family:var(--font-display);font-size:var(--fs-small);font-weight:500;
  padding:8px 16px;border-radius:20px;
  background:rgba(255,255,255,.04);border:1px solid var(--border-subtle);
  color:var(--text-secondary);
}

/* Flow row - renders a "Connect → Assess → Visualise → Report" line as chips.
   The arrows are generated, so the text content stays exactly as written. */
.flow{display:flex;flex-wrap:wrap;align-items:center;gap:12px}
.flow li{
  font-family:var(--font-display);font-size:var(--fs-body);font-weight:500;color:var(--text-primary);
  padding:12px 22px;border-radius:var(--r-btn);
  background:var(--bg-raised);border:1px solid var(--border-subtle);
}
/* The arrows are real elements rather than generated content: the source line is
   literally "Connect → Assess → Visualise → Report", so they are part of the
   copy, and a generated ::before could not be a flex item between the chips. */
.flow .arrow{color:var(--accent);font-size:var(--fs-lead);line-height:1}

/* Team and advisory board portraits. */
.team-card{text-align:center}
.team-card img{
  width:132px;height:132px;border-radius:50%;object-fit:cover;
  margin:0 auto 18px;border:1px solid var(--border-subtle);
  background:var(--bg-hover);
}
.team-card h3{font-size:var(--fs-lead);line-height:26px;font-weight:600;margin-bottom:4px}
.team-card p{font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-muted);margin-bottom:14px}
.team-card .in{
  display:inline-grid;place-items:center;width:34px;height:34px;border-radius:50%;
  border:1px solid var(--border-subtle);transition:background .3s,border-color .3s;
}
.team-card .in:hover{background:var(--bg-hover);border-color:var(--border-accent-hover)}
.team-card .in svg{width:15px;height:15px;fill:var(--text-muted);transition:fill .3s}
.team-card .in:hover svg{fill:var(--accent-bright)}

/* ---------- 9. Panels ---------- */
.panel{
  background:var(--bg-raised);border:1px solid var(--border-subtle);
  border-radius:var(--r-panel);padding:var(--pad-panel);
}
/* Two-column panel: copy beside a list or image. */
.split-panel{
  margin-top:64px;background:var(--bg-raised);border:1px solid var(--border-subtle);
  border-radius:var(--r-panel);padding:var(--pad-panel);
  display:grid;grid-template-columns:1fr 1.2fr;gap:var(--pad-panel);align-items:center;
}
.split-panel h2{font-size:clamp(26px,2.6vw,34px);line-height:1.2;font-weight:600;margin-bottom:16px}
.split-panel .lead{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-secondary);margin-bottom:28px}

/* The one gradient use per page, reserved for the closing CTA. A full-bleed
   band rather than a floating panel: the gradient runs edge to edge and sits
   flush against the footer; the content keeps the container's gutters. The
   rhythm gap above is a margin, not padding, so it stays canvas-dark. */
.cta-band{background:var(--grad-brand);margin-top:var(--section-y)}
/* After raised ground the rhythm gap would read as a canvas stripe pinched
   between two non-canvas grounds, so the band sits flush on the preceding
   section's bottom hairline instead. Applies to the alt ground and to the
   logo strip, which carries the same banded treatment. */
.section-alt+.cta-band,.trust-strip+.cta-band{margin-top:0}
.cta-panel{
  padding:80px 0;color:var(--text-on-light);
  display:flex;align-items:center;justify-content:space-between;gap:40px;flex-wrap:wrap;
}
/* Buttons-only variant: the homepage's closing block is the source's CTA bar,
   which carries no copy of its own. */
.cta-panel-bar{justify-content:center;gap:16px;padding:48px 0}
.cta-panel h2{font-size:clamp(30px,3.4vw,42px);line-height:1.14;font-weight:600;letter-spacing:-.01em;max-width:560px}
.cta-panel p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-on-light-soft);margin-top:10px;max-width:480px}

/* Stat figure - pricing, ROI, case studies. */
.stat-value{display:block;font-family:var(--font-display);font-size:clamp(30px,3vw,42px);line-height:1.14;font-weight:600;letter-spacing:-.02em;color:var(--accent-bright)}
.stat-value.dim{color:var(--text-primary)}
.stat-note{display:block;font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-muted);margin-top:4px}
/* Figure. A screenshot with an optional caption. This existed only as the
   inline pair `border-radius:var(--r-card);border:1px solid var(--border-subtle)`
   repeated on every shot() call across five pages, which meant a screenshot had
   no way to carry a caption and no single place to change its treatment. */
.figure{margin:0}
.figure img{display:block;width:100%;height:auto;border-radius:var(--r-card);border:1px solid var(--border-subtle)}
.figure figcaption{
  margin-top:14px;font-size:var(--fs-small);line-height:var(--lh-small);
  color:var(--text-muted);max-width:70ch;
}
.figure--inset{max-width:1080px;margin-inline:auto}

.rule{border:none;border-top:1px solid var(--border-subtle);margin:24px 0}

/* Badge - section and plan labels. */
.badge{
  display:inline-block;font-family:var(--font-display);font-size:var(--fs-label);font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
  padding:6px 14px;border-radius:20px;margin-bottom:28px;
  background:rgba(255,255,255,.06);color:var(--text-secondary);
}
.badge-accent{background:rgba(var(--accent-rgb),.14);color:var(--accent-bright)}

/* Video frame - used by the homepage and the videos page, so it belongs here.
   Without the width:100% the element falls back to its intrinsic size and
   overflows the viewport on narrow screens. */
.video-wrap{
  border-radius:var(--r-card-lg);overflow:hidden;
  border:1px solid var(--border-subtle);background:var(--bg-raised);
}
/* contain, not cover: the frame and the video are both 16:9 so the video renders
   identically either way, but the poster is a much wider product screenshot and
   cover crops it to an unreadable zoom. */
.video-wrap video{width:100%;display:block;aspect-ratio:16/9;object-fit:contain;background:var(--bg-raised)}

/* ---------- 10. Quotes ---------- */
.quote{
  background:var(--bg-raised);border:1px solid var(--border-subtle);
  border-radius:var(--r-card);padding:28px;display:flex;flex-direction:column;gap:16px;
  transition:border-color .3s,background .3s;
}
.quote:hover{background:var(--bg-hover);border-color:var(--border-subtle-hover)}
.quote blockquote{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-secondary);flex:1}
/* A testimonial whose entire body is one line reads as an empty card if set as
   body copy, so it is set as a pull quote instead. */
.quote blockquote.headline{
  font-family:var(--font-display);font-size:clamp(22px,2.4vw,30px);line-height:1.2;
  font-weight:600;letter-spacing:-.01em;color:var(--accent-bright);
}
.quote .attr{border-top:1px solid var(--border-subtle);padding-top:14px}
.quote .attr b{display:block;font-family:var(--font-display);font-size:var(--fs-small);font-weight:600;color:var(--text-primary)}
.quote .attr span{display:block;font-size:var(--fs-label);line-height:18px;color:var(--text-muted)}
.quote .lang-note{font-size:var(--fs-label);font-style:italic;margin-top:2px}

/* ---------- 11. Trust strip and logo marquee ---------- */
.trust-strip{
  background:var(--strip-bg);padding:28px 0;
  border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle);
}
/* Edge-fade mask is the standard pattern for this,
   recoloured to the strip background. */
.marquee{
  display:flex;overflow:hidden;   /* no gap here - the track's padding-right IS the seam spacing */
  -webkit-mask-image:linear-gradient(to right,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(to right,transparent,#000 8%,#000 92%,transparent);
}
.marquee-track{
  display:flex;align-items:center;gap:64px;flex:0 0 auto;
  padding-right:64px;
  animation:marquee var(--marquee-duration,34s) linear infinite;
}
.marquee:hover .marquee-track{animation-play-state:paused}
/* Set by JS when one full set of logos already fits the viewport: nothing to
   scroll, so the strip renders as a plain centred row instead of drifting. */
.marquee.is-static{-webkit-mask-image:none;mask-image:none;justify-content:center}
.marquee.is-static .marquee-track{animation:none;padding-right:0}
.marquee.is-static .marquee-track[aria-hidden="true"]{display:none}
/* Partner logos are dark-on-transparent, so they are knocked out to white,   without the invert they would be invisible on this background. */
.marquee img{
  height:42px;width:auto;max-width:170px;object-fit:contain;
  filter:brightness(0) invert(1);opacity:.55;transition:opacity .3s;
}
.marquee img:hover{opacity:1}
@keyframes marquee{to{transform:translateX(-100%)}}

/* ---------- 12. Footer ---------- */
.site-footer{
  border-top:1px solid var(--border-subtle);padding:80px 0 40px;
  /* The hero wash's quiet echo, vertically mirrored: the page opens with a
     glow rising from the hero's bottom-right and closes with one falling from
     the footer's top-right. Kept at .14 - well under the .20 wash ceiling - so
     under a cta-band it reads as the band's light bleeding down, not as a
     second colour moment. Decorative only. */
  background:radial-gradient(ellipse at 82% 0%,rgba(var(--accent-rgb),.14),transparent 55%) no-repeat;
}
.footer-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:48px;margin-bottom:64px}
.footer-grid h2{font-family:var(--font-display);font-size:var(--fs-small);font-weight:600;color:var(--text-primary);margin-bottom:18px;letter-spacing:.06em;text-transform:uppercase}
.footer-grid ul{display:flex;flex-direction:column;gap:10px}
.footer-grid a{font-size:var(--fs-small);line-height:var(--lh-small);color:var(--text-muted);transition:color .2s}
.footer-grid a:hover{color:var(--accent)}
.footer-about p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-muted);margin-top:16px;max-width:300px}
.footer-social{display:flex;gap:12px;margin-top:22px}
.footer-social a{
  width:38px;height:38px;border-radius:50%;display:grid;place-items:center;
  border:1px solid var(--border-subtle);transition:background .3s,border-color .3s;
}
.footer-social a:hover{background:var(--bg-hover);border-color:var(--border-accent-hover)}
.footer-social svg{width:17px;height:17px;fill:var(--text-muted);transition:fill .3s}
.footer-social a:hover svg{fill:var(--accent-bright)}
.footer-bottom{
  border-top:1px solid var(--border-subtle);padding-top:24px;
  display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;
  font-size:var(--fs-small);color:var(--text-muted);
}
.footer-bottom nav{display:flex;gap:24px;flex-wrap:wrap}
.footer-bottom a:hover{color:var(--accent)}

/* ---------- 13. Reveal motion ---------- */
/* Content is visible by default; JS adds .reveal-armed to hide, so a script
   failure can never blank the page. */
.reveal-armed{opacity:0;transform:translateY(24px)}
.reveal-armed.in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s var(--ease-out)}

@media (prefers-reduced-motion:reduce){
  .reveal-armed{opacity:1;transform:none}
  /* The connector is drawn, not drawing. */
  .path > li::after{transform:scaleX(1);transition:none}
  .marquee-track{animation:none}
  .marquee{flex-wrap:wrap;justify-content:center;-webkit-mask-image:none;mask-image:none}
  .marquee-track[aria-hidden="true"]{display:none}
  .nav-menu{transition:none}
}

/* ---------- 14. Responsive ---------- */
/* Single-direction breakpoints only (max-width), consistently. */
@media (max-width:1023px){
  .site-header{padding:22px 24px}
  /* Below lg the desktop bar (eight links plus Log in) no longer fits - at md
     labels wrapped onto two lines - so the full-screen menu panel takes over
     from 1023px down, not only on phones. */
  .nav{display:none}
  .nav .nav-mobile-only{display:block}
  .nav.open{
    display:flex;flex-direction:column;align-items:flex-start;gap:8px;
    position:fixed;inset:0;background:rgba(10,11,13,.97);padding:110px 32px 32px;
    overflow-y:auto;overscroll-behavior:contain;
  }
  /* The scrolled header's backdrop-filter makes it the containing block for
     fixed descendants, which collapsed the open panel from full screen to the
     header's own box as soon as the page scrolled past 40px. Dropped while the
     panel is open (it covers the page anyway). site.js also locks page scroll
     via .nav-locked so the page cannot move underneath. */
  .site-header:has(.nav.open){backdrop-filter:none}
  html.nav-locked{overflow:hidden}
  /* 22px is the floor of the Title step, used here at full-screen menu scale. */
  .nav.open a,.nav.open .nav-link{font-size:22px;line-height:44px}
  /* Log in is a .btn in the desktop bar; in the panel it is a plain row, so it
     drops the button's side padding and fixed height to line up with the rest. */
  .nav.open .btn{padding:0;height:auto}
  .nav-toggle{display:block;position:relative;z-index:2}
  /* In the panel the dropdown is not a floating layer - it expands inline, so the
     menu can never open off the edge of a narrow screen. */
  .nav-item{display:block;width:100%}
  .nav-menu{
    position:static;opacity:1;visibility:visible;transform:none;
    background:none;border:none;box-shadow:none;padding:0 0 0 16px;margin-top:0;
    min-width:0;display:none;
  }
  .nav-item.open>.nav-menu{display:flex}
  .nav-item:hover>.nav-menu{display:none}
  .nav-item.open:hover>.nav-menu{display:flex}
  .nav-menu a{font-size:var(--fs-lead);line-height:36px;padding:2px 0}
  .grid-3,.grid-4{grid-template-columns:repeat(2,1fr)}
  .split-panel{grid-template-columns:1fr}
  .footer-grid{grid-template-columns:1fr 1fr}
  .page-hero{padding:160px 0 60px}
  .page-hero-split .page-hero-inner{grid-template-columns:1fr;gap:40px}
  .page-hero-split h1,.page-hero-split .lead{max-width:none}
  .page-hero-visual figure{margin-left:0;width:auto}
  .page-hero-note{margin-left:0;max-width:none}
  /* A split hero that carries a product image opens the way the homepage does
     once it stacks: full screen, with the image under the h1 and the rest of
     the copy following. The copy block dissolves (display:contents) so the
     image can slot between the heading and the lead; gap 0 because each copy
     element carries its own margin. Visual order only - the DOM keeps the
     copy together, and the image is not interactive. Text-note split heroes
     (About) keep the compact band, hence the :has(img) guard. */
  .page-hero-split:has(.page-hero-visual img){
    display:flex;align-items:center;
    min-height:100vh;min-height:100svh;
    padding:calc(var(--header-h) + 32px) 0 48px;
  }
  .page-hero-split:has(.page-hero-visual img) .page-hero-inner{gap:0}
  .page-hero-split:has(.page-hero-visual img) .page-hero-copy{display:contents}
  .page-hero-split:has(.page-hero-visual img) .page-hero-copy > :is(.crumbs,.eyebrow,h1){order:-2}
  .page-hero-split:has(.page-hero-visual img) .page-hero-visual{order:-1;margin-bottom:28px}

  /* Structural vocabulary. Each component keeps its idea at narrow widths
     rather than degrading to a stack of boxes: the ledger keeps its rules, the
     path and the arc turn their connector from a horizontal run into a spine. */
  .section-head--indexed{gap:16px}
  .section-head--indexed p:not(.section-index){max-width:70ch}
  .editorial,.pull{grid-template-columns:1fr;gap:40px}
  .editorial--reverse > :first-child{order:0}
  .pull-lead blockquote{max-width:46ch}
  .statement{grid-template-columns:1fr;align-items:start}

  .ledger > li{grid-template-columns:44px 1fr;gap:0 20px;padding:22px 8px}
  .ledger--posts > li{grid-template-columns:120px 1fr}
  .ledger h3{grid-column:2}
  .ledger p{grid-column:2;margin-top:8px}
  .ledger .out{grid-column:2}

  /* Path and arc become a vertical spine: the connector runs down the left,
     nodes sit on it. Same vocabulary, rotated. */
  .path,.arc{grid-auto-flow:row;grid-template-columns:1fr;gap:0}
  .path > li,.arc > li{padding:0 0 34px 62px;margin-top:0}
  .path > li::before,.path > li::after,
  .arc > li::before,.arc > li:not(:last-child)::after{
    left:17px;right:auto;top:17px;bottom:0;width:1px;height:auto;
  }
  .path > li::after{transform-origin:center top;transform:scaleY(1)}
  .reveal-armed.path > li::after{transform:scaleY(0)}
  .reveal-armed.path.in > li::after{transform:scaleY(1)}
  .path .node{position:absolute;left:0;top:0;margin-bottom:0}
  .path > li:last-child{padding-bottom:0}
  .arc > li::before{top:0}
  .arc > li:not(:last-child)::after{display:none}
  .arc .n{margin:0 0 12px}
  .arc > li:last-child::before{background:var(--border-subtle-hover)}
  .arc > li{padding-left:0;border-top:1px solid var(--border-subtle-hover);padding-top:22px;margin-bottom:26px}
  .arc > li::before{display:none}
  .arc > li:last-child{border-top-color:var(--accent)}
  .path p,.arc p{max-width:52ch}
  .roster{gap:40px 48px}
}
/* Between the mobile breakpoint and roughly 790px the desktop nav is about 17px
   too wide for the header and the Log in button clipped off the right edge of
   every page, 768px being iPad portrait. Tightening the gap and the header
   padding is enough; collapsing the whole layout a breakpoint earlier would be
   a much larger change to buy 20px. */

@media (max-width:767px){
  :root{--section-y:60px}
  .site-header{padding:16px 24px}
  .site-header.scrolled{padding:12px 24px}
  /* Button pairs fill the width in equal halves. height goes auto with padded
     lines so a long label ("Book an MSP walkthrough") can wrap instead of
     overflowing the fixed 44px slug; a padded single line still lands at
     ~45px so short labels look unchanged. Flex-centred so a one-line label
     beside a wrapped one sits in the middle of the shared height, not at
     the top. */
  .btn-row{width:100%}
  .btn-row .btn{
    flex:1 1 0;min-width:0;height:auto;line-height:1.3;padding:12px 14px;
    display:flex;align-items:center;justify-content:center;
  }
  /* Opt-in for a pair whose labels only just fit side by side at 16px (MSP
     hero: ~366px of the 370px a 390px phone leaves). Buttons size to their
     label with minimal side padding instead of splitting the row in halves;
     where even that does not fit, the second button wraps to its own row. */
  .btn-row--tight{gap:8px}
  .btn-row--tight .btn{flex:1 1 auto;white-space:nowrap;padding:12px 5px}
  .grid-2,.grid-3,.grid-4{grid-template-columns:1fr}
  .checklist{grid-template-columns:1fr}
  .panel,.split-panel{padding:28px}
  .section-alt{padding:64px 0}
  .section-alt + .section,.section-alt + .section-dense,.section-alt + .section-open{padding-top:64px}
  .section-open{padding:var(--section-y) 0 48px}
  .section-dense{padding:52px 0 40px}
  .ledger > li{grid-template-columns:1fr;gap:0;padding:20px 0}
  .ledger .n{display:block;margin-bottom:8px}
  .ledger h3,.ledger p,.ledger .out{grid-column:1}
  .roster{gap:36px 40px}
  .roster > li{width:calc(50% - 20px)}
  .page-hero{padding:140px 0 48px}
  .marquee-track{gap:40px;padding-right:40px}
  .marquee img{height:26px;max-width:110px}
  .cta-panel{padding:56px 0}
  /* Two columns, no orphan: brand full width on top, then the two short lists
     (Product, Solutions) stacked on the left beside the long one (Company) on
     the right, so both columns end at about the same height. Placement only;
     DOM and reading order stay Product, Solutions, Company. */
  .footer-grid{grid-template-columns:1fr 1fr;gap:32px 24px}
  .footer-about{grid-column:1 / -1}
  .footer-grid > :nth-child(2){grid-column:1;grid-row:2}
  .footer-grid > :nth-child(3){grid-column:1;grid-row:3}
  .footer-grid > :nth-child(4){grid-column:2;grid-row:2 / span 2}
}
