/*
 * The public site — blue and white.
 * =================================
 *
 * ⭐⭐ A LADDER OF ONE HUE, not a blue plus a second accent. Depth and emphasis come from moving up
 * and down this scale, which is what stops a one-colour palette reading flat and why nothing here
 * needs a competing colour to create interest.
 *
 * Sort Marketing's blues (#1C46AD, #2F63D8) sit in the middle of the ladder; Pop Hasta Labs' ink
 * (#0B1220) anchors the bottom. The homage is in the hue; the depth is ours.
 *
 * ⭐ ENGAGEMENT COMES FROM MOTION AND DEPTH, measured off Sort's own page rather than guessed:
 * they run scroll reveals (typeRevealUp, cardReveal), seven marquees, 109 shadowed elements and
 * transform transitions on 125. They also carry 71 videos and 117 images, which we do not have —
 * so we take the motion and the depth, not the media.
 *
 * ⭐ LOGICAL PROPERTIES, NEVER left/right. English-only today, but they cost nothing and are why
 * re-adding Arabic would not mean rewriting this file.
 */

/*
 * ── League Spartan, self-hosted ────────────────────────────────────────────────────────────
 *
 * ⭐ THE CLIENT'S OWN BRAND FACE. Their guide names it the primary typeface — "a bold, geometric
 * typeface that brings confidence and presence to headlines" — so the site now sets its headings
 * in the face the brand is specified in rather than in one chosen before the guide existed.
 * SIL Open Font License 1.1; fonts/OFL-LeagueSpartan.txt ships beside the files, because
 * redistributing an OFL face means carrying its licence. Montserrat is declared further down and
 * carries its own fonts/OFL-Montserrat.txt for the same reason.
 *
 * ⛔ SELF-HOSTED, NOT LOADED FROM GOOGLE. A company whose argument is "your data does not go where
 * you did not send it" must not hand every visitor's IP to a third party to render a heading.
 *
 * ⚠️ Two subsets split by unicode-range: the browser fetches latin-ext ONLY when a page actually
 *    contains an accented character, so an English reader pays 24KB rather than 44KB.
 * ⛔ The relative ../fonts/ paths are deliberate — ManifestStaticFilesStorage rewrites URLs inside
 *    CSS to their hashed equivalents. An absolute /static/ path would skip that.
 */
@font-face {
  font-family: "League Spartan";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/league-spartan-latin-ext.8d2f864920c2.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "League Spartan";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/league-spartan-latin.0a37cd9b7365.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --blue-950: #061634;
  --blue-900: #0B2A6B;
  --blue-800: #14379A;
  --blue-700: #1C46AD;
  --blue-600: #2F63D8;
  --blue-500: #4B82F0;
  --blue-300: #A8C6F8;
  --blue-100: #E4EEFC;
  --blue-50:  #F3F7FE;

  --ink: #0B1220;
  --heading: #071026;
  --muted: #4E5C74;
  /*
   * ⛔ RAISED FROM #8794AB (3.06:1), WHICH HAD ESCAPED INTO REAL COPY THREE TIMES — the lead
   * form's privacy line, the redaction demo's step labels, and the dashboard mock's timestamps.
   * Each was corrected one at a time and the token stayed a trap. #617087 measures 5.03:1 on
   * white and 4.62:1 on the paper ground, so it now passes AA wherever it lands and a fourth
   * escape is a style choice rather than a defect.
   * ⚠️ It is still the QUIETEST tone, not a licence to set body copy in it.
   */
  --faint: #617087;

  /* ⭐ THE BRAND WHITE IS WARM, NOT PURE. The guide gives #F8F5EE — a paper tone rather than a
     screen white, which is what stops a one-hue blue palette reading cold and clinical.
     ⚠️ IT MOVES EVERY CONTRAST RATIO ON THE SITE, because almost everything is measured against
     this. Measured, not assumed: the ground drops from L=1.000 to L=0.914, so ratios fall by
     roughly 8%. Nothing crosses a threshold — the tightest is .svc-n at 3.35:1 against a 3.0
     floor, down from 3.65:1. ⛔ Do not lighten any text token to "compensate"; the audit is what
     decides whether this is safe, not the eye. */
  --paper: #F8F5EE;
  /* ⭐ THE PAGE IS THE BRAND'S WHITE. The guide names #F8F5EE as the white, so it belongs on
     the largest surface rather than on trim. --blue-50 keeps its job as the alternate band. */
  --ground: var(--paper);
  --card: #FFFFFF;
  --line: #DCE6F5;
  --line-soft: #EDF3FC;

  /* ⚠️ TWO BLUES FOR TEXT VS FILL. #2F63D8 is the brand blue and correct as a fill, but as text on
     white it measures about 4.3:1 — under the 4.5 AA asks for. --blue-700 carries text. */
  --accent: var(--blue-600);
  --accent-ink: var(--blue-700);

  --on-deep: #EAF1FE;
  --on-deep-muted: #AFC3E6;

  /* ⭐ THE SHELL WIDTH, IN ONE PLACE. It was the same magic number written out in five rules —
     .inner, .head-inner, .util-inner, .foot-grid and .foot-built — and every one of them has to
     agree or the header, content and footer stop sharing a left edge. That has already gone wrong
     twice on this site. A token makes disagreement impossible rather than merely unlikely. */
  --shell: 1320px;

  --radius: 12px;
  --radius-lg: 18px;

  /* ⭐ DEPTH, THE WAY SORT GETS IT. Three tiers, tinted blue rather than grey, so shadows belong
     to the palette instead of muddying it. */
  --shadow-sm: 0 1px 2px rgba(11,42,107,.06), 0 2px 8px rgba(11,42,107,.05);
  --shadow-md: 0 4px 12px rgba(11,42,107,.08), 0 12px 32px rgba(11,42,107,.07);
  --shadow-lg: 0 12px 28px rgba(11,42,107,.12), 0 32px 64px rgba(11,42,107,.10);

  --font-display: "League Spartan", ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-body: "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /*
   * ── MOTION ──────────────────────────────────────────────────────────────────────────
   * ⭐ ONE SET OF CURVES AND DURATIONS FOR THE WHOLE SITE. Before this, every transition
   * picked its own number — .2s here, .35s there, .7s somewhere else — which is why the
   * page never felt like one thing moving. ⛔ Reach for a token, not a new number.
   */
  --ease: cubic-bezier(.22,.61,.36,1);      /* the house curve: quick out, settles soft */
  --ease-out: cubic-bezier(.16,1,.3,1);     /* longer settle, for anything entering */
  --dur-fast: .18s;                         /* hover, focus, colour */
  --dur: .32s;                              /* movement a finger caused */
  --dur-slow: .7s;                          /* movement the page caused */
  --head-gap: clamp(.55rem, 1.4vw, 1rem);   /* the floating bar's inset from the edge */
}

* { box-sizing: border-box; }
html { font-family: var(--font-body); scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-size: 1.0625rem;
  line-height: 1.65;
  text-align: start;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-display);
  color: var(--heading);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.03em;
}
a { color: var(--accent-ink); text-underline-offset: 3px; }
::selection { background: var(--blue-100); }

/* ══ MOTION ═══════════════════════════════════════════════════════════════════════════════════
 *
 * ⭐ EVERYTHING REVEALS ON SCROLL — the device Sort leans on hardest. This is the resting state;
 * reveal.js adds .in as each element enters the viewport.
 *
 * ⛔⛔ NO-JS SAFETY. If the script fails, never loads, or is blocked, `.reveal` elements must NOT
 * stay invisible — a reveal library taking the content down with it is the classic way this breaks.
 * `.js-reveal` is set BY the script, so opacity only drops once something exists to raise it again.
 */
.js-reveal .reveal { opacity: 0; transform: translateY(18px); }
.js-reveal .reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.js-reveal .reveal-1.in { transition-delay: .08s; }
.js-reveal .reveal-2.in { transition-delay: .16s; }
.js-reveal .reveal-3.in { transition-delay: .24s; }
.js-reveal .reveal-4.in { transition-delay: .32s; }

/*
 * ⛔ REDUCED MOTION IS NOT OPTIONAL. Somebody who gets motion sickness from movement has told their
 * operating system so, and this is where we listen. Content still appears — only the movement stops.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* ⛔ NOT "the animation runs instantly" — the overlay is not drawn at all. The head script
     already declines to add the class; this is the belt to that braces. */
  .preload { display: none !important; }
  .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
  .marquee-track { animation: none; }
  *, *::before, *::after { animation-duration: .001ms; transition-duration: .001ms; }
}

/* ── Header ──────────────────────────────────────────────────────────────────────────────── */
/*
 * ⭐⭐ A FLOATING PILL, DETACHED FROM THE PAGE — p2 of the revision document. It sits over the
 * content rather than sitting on top of it, so the page reads as one surface with the navigation
 * hovering above it.
 *
 * ⚠️ FIXED, NOT STICKY, so it no longer occupies layout space — which is why `body` below carries
 * the padding that keeps every page's first section clear of it. A hero that wants to run under
 * the bar pulls that padding back itself, deliberately, rather than every other page losing its
 * top edge by accident.
 */
.site-head {
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: nowrap;
  padding: .5rem .55rem .5rem 1.15rem;
  background: rgba(255,255,255,.78);
  backdrop-filter: saturate(180%) blur(16px);
  border: 1px solid rgba(255,255,255,.7);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(11,42,107,.05), 0 8px 28px -10px rgba(11,42,107,.18);
  position: fixed; z-index: 40;
  inset-block-start: var(--head-gap);
  inset-inline: var(--head-gap);
  margin-inline: auto;
  max-inline-size: 78rem;
  transition: padding var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              background-color var(--dur) var(--ease);
}
/*
 * ⚠️ THE BAR IS OUT OF FLOW, so the page has to be told to start below it — and to leave a
 * real gap, not to sit flush. Measured: at 4.4rem the first section began one pixel ABOVE the
 * bar's lower edge, so any page whose first element has no padding of its own tucked under it.
 * The figure is the resting bar (logo 40px + strapline + padding + border) plus its inset plus
 * a gap you can see.
 */
body { padding-block-start: calc(var(--head-gap) + 6rem); }

/*
 * ⚠️ THE CONDENSED STATE IS AN ADDITION, NOT THE DEFAULT. `is-scrolled` is set by the head
 * script; with no script the bar simply keeps its resting size, which is a working header.
 */
.is-scrolled .site-head {
  padding-block: .34rem;
  background: rgba(255,255,255,.9);
  box-shadow: 0 1px 2px rgba(11,42,107,.06), 0 10px 30px -12px rgba(11,42,107,.28);
}
.is-scrolled .brand-logo { block-size: 32px; }
.is-scrolled .brand-with { opacity: 0; max-block-size: 0; }
.brand-logo, .brand-with {
  transition: block-size var(--dur) var(--ease), opacity var(--dur) var(--ease),
              max-block-size var(--dur) var(--ease);
}
.brand-with { overflow: hidden; max-block-size: 1.4rem; }
.brand { text-decoration: none; color: inherit; display: block; line-height: 1.15; }
/*
 * The supplied lockup, scaled by HEIGHT so the aspect ratio can never be touched. width:auto is
 * the whole safeguard - the brand guide asks that the mark is not stretched, and a fixed width
 * beside a fixed height is exactly how that happens by accident.
 * The intrinsic width/height are on the <img> too, so the header does not jump while it loads.
 */
.brand-logo {
  display: block; block-size: 40px; inline-size: auto; max-inline-size: 100%;
  /* ⚠️ The ratio is stated so the header reserves the right width before the file arrives.
     It matches logo.svg's viewBox (1000 x 280.34) — if the artwork is ever re-traced, this
     moves with it or the logo shifts on load. */
  aspect-ratio: 1000 / 280.34;
}
.brand-name { font-family: var(--font-display); font-size: 1.2rem; font-weight: 700; letter-spacing: -0.03em; color: var(--heading); display: block; }
/* ⛔ WAS --faint (2.85:1). The token beside it says "decorative only — never a sentence somebody
   must read", and this is the partner credential in the header of every page: the footer already
   treats the same line as a CREDENTIAL rather than small print. --muted measures 6.76:1. */
.brand-with { display: block; font-family: var(--font-display); font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin-block-start: 0.1rem; }

.site-nav { margin-inline-start: auto; }
.site-nav ul { list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 0.15rem; margin: 0; padding: 0; }
.site-nav a {
  display: flex; align-items: center; min-block-size: 44px; padding-inline: 0.9rem;
  text-decoration: none; color: var(--muted); font-size: 0.95rem; border-radius: 8px;
  transition: color .2s var(--ease), background-color .2s var(--ease);
}
.site-nav a:hover { color: var(--accent-ink); background: var(--blue-100); }

.nav-cta {
  background: var(--blue-600); color: #fff; padding-inline: 1.35rem; font-weight: 600;
  box-shadow: var(--shadow-sm);

  transition: transform .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
/* ⛔ A PILL, like the bar it sits in — and it has to be `.site-nav a.nav-cta` to get there.
      `.site-nav a` is 0,1,1 and `.nav-cta` alone is 0,1,0, so a radius set on the class by itself
      is outranked by the element selector and never applies. The button kept its 8px corner
      inside a 999px bar, which was the one shape on the header that did not match the header. */
.site-nav a.nav-cta { border-radius: 999px; }
.site-nav a.nav-cta:hover { background: var(--blue-700); color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-md); }

@media (max-width: 860px) {
  /* ⚠️ Still floating on a phone, just tighter. Static here would put a full-width white band
     back across the top and undo the whole effect on the screen most people arrive on. */
  .site-head { gap: 0.6rem; padding-inline: .9rem .5rem; }
  .site-nav { margin-inline-start: auto; }
  /* ⚠️ The bar is shorter here — the strapline drops to two lines but the logo shrinks more. */
  body { padding-block-start: calc(var(--head-gap) + 5rem); }
  .site-nav a { padding-inline: 0.7rem; }
}

/* ── Bands ───────────────────────────────────────────────────────────────────────────────── */
.band { padding-block: clamp(4rem, 9vw, 7.5rem); padding-inline: clamp(1.25rem, 5vw, 4rem); position: relative; }
.band-tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
/*
 * ⛔ THE BAND AND THE CARD MUST NOT BE THE SAME COLOUR. When the brand's warm white arrived, this
 * band and every card on it were both var(--paper) — so the cards stopped reading as cards and
 * survived only on a 1px border. Measured contrast said nothing was wrong; the page just looked
 * like it had failed to finish loading.
 * ⭐ So the neutrals are a ladder now, not a pair: the warm brand white is the PAGE, this cool
 * tint is the ALTERNATE BAND, and pure white is reserved for things that are meant to lift.
 */
.band-card { background: var(--blue-50); }
.band-tint { background: var(--blue-100); }

/* ⭐ THE DARK BAND IS DEEP BLUE, NOT BLACK — it carries most of the page rhythm, and a blue-black
   keeps a one-hue palette coherent where a neutral black would break it. */
.band-deep {
  background: linear-gradient(165deg, var(--blue-950) 0%, var(--blue-900) 55%, #0E3480 100%);
  color: var(--on-deep);
}
.band-deep h1, .band-deep h2, .band-deep h3 { color: #fff; }
.band-deep p { color: var(--on-deep-muted); }
.inner { max-inline-size: var(--shell); margin-inline: auto; }
.inner-narrow { max-inline-size: 46rem; margin-inline: auto; }

.eyebrow {
  font-family: var(--font-display); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--accent-ink); margin: 0 0 1.1rem;
  display: inline-flex; align-items: center; gap: 0.5rem;
}
.eyebrow::before { content: ""; inline-size: 1.75rem; block-size: 2px; background: var(--blue-600); border-radius: 2px; }
.band-deep .eyebrow { color: var(--blue-300); }
.band-deep .eyebrow::before { background: var(--blue-500); }

.section-head { max-inline-size: 44rem; margin-block-end: clamp(2rem, 4vw, 3.25rem); }
.section-head h2 { font-size: clamp(2rem, 4vw, 3rem); margin: 0 0 0.9rem; }
/* ⛔⛔ `:not(.stage)` BECAUSE THIS RULE IS FOR THE LEDE, NOT FOR ANY <p> IN THE HEAD. `.stage`
   is a <p> too, and `.section-head p` (0,1,1) outranks `.stage` (0,1,0) — so every section
   eyebrow on the site was repainted from its own 0.7rem/--accent-ink to 1.15rem/--muted. On
   /erp/ that was eight of ten, reading as grey body text at 64% oversize beside the two that
   were right. ⚠️ Stated as an exclusion rather than fought with `.section-head .stage`, so the
   component keeps owning its own type. */
.section-head p:not(.stage) { color: var(--muted); font-size: 1.15rem; margin: 0; }
/* ⚠️ The same exclusion: on a deep band this was overriding `.band-deep .stage`, so the
   eyebrow took the muted paragraph blue-grey instead of --blue-300. */
.band-deep .section-head p:not(.stage) { color: var(--on-deep-muted); }

/* ── Hero ────────────────────────────────────────────────────────────────────────────────── */
.hero {
  padding-block: clamp(3.5rem, 8vw, 7rem) clamp(3rem, 6vw, 5rem);
  background:
    radial-gradient(1100px 520px at 12% -10%, var(--blue-100) 0%, transparent 62%),
    radial-gradient(820px 420px at 96% 0%, #E9F0FE 0%, transparent 58%),
    var(--paper);
  overflow: hidden;
}
.hero-grid { display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
@media (min-width: 980px) { .hero-grid { grid-template-columns: 1.02fr 1fr; } }

.hero h1 { font-size: clamp(2.7rem, 5.8vw, 4.4rem); margin: 0 0 1.35rem; }
/* ⭐ ONE PHRASE IN BLUE. Restraint is the effect; a second colour would dilute it. */
.hero h1 em { font-style: normal; color: var(--blue-600); }
.lede { font-size: 1.22rem; color: var(--muted); margin: 0; max-inline-size: 34rem; }

.hero-actions { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-block-start: 2.25rem; }

.cta, .cta-quiet {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-block-size: 54px; padding-inline: 1.9rem; border-radius: var(--radius);
  text-decoration: none; font-weight: 600; font-size: 1rem; cursor: pointer;
  border: 1px solid transparent; font-family: var(--font-body);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
.cta { background: var(--blue-600); color: #fff; box-shadow: var(--shadow-md); }
.cta:hover { background: var(--blue-700); color: #fff; transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.cta-quiet { border-color: var(--line); color: var(--heading); background: var(--card); }
.cta-quiet:hover { border-color: var(--blue-600); color: var(--accent-ink); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
/*
 * ⛔ THIS IS THE LAST BUTTON ON EVERY PAGE AND IT FAILED AA. White on --blue-500 measures 3.65:1
 * against the 4.5 that 16px text needs — the token above already warns that #2F63D8 is a FILL and
 * --blue-700 is the TEXT blue, and blue-500 is lighter still. The closing call to action was the
 * least readable button on the site.
 *
 * ⭐ WHITE FILL, BLUE TEXT — 8.30:1, and the button now reads at 13.5:1 against the navy band. On a
 * dark section an inverted button is both the accessible answer and the loud one, and it stops the
 * final CTA looking like the mid-page blue buttons it is supposed to outrank.
 */
.band-deep .cta { background: #fff; color: var(--blue-700); }
.band-deep .cta:hover { background: var(--blue-100); color: var(--blue-800); }
.band-deep .cta-quiet { background: transparent; border-color: #2A4C93; color: #fff; }

.hero-trust {
  list-style: none; margin: 2.75rem 0 0; padding: 1.5rem 0 0;
  border-block-start: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 0.55rem 2rem; color: var(--muted); font-size: 0.92rem;
}
.hero-trust li { display: flex; align-items: center; gap: 0.55rem; }
/* A tick drawn in CSS rather than an icon file — one less request, and it inherits the palette. */
.hero-trust li::before {
  content: ""; flex: 0 0 auto;
  inline-size: 1.15rem; block-size: 1.15rem; border-radius: 50%;
  background: var(--blue-100); box-shadow: inset 0 0 0 1px var(--blue-300);
}

/* ── The redaction demo ──────────────────────────────────────────────────────────────────── */
.demo {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 3vw, 2rem); box-shadow: var(--shadow-lg);
}
.demo label { display: block; font-family: var(--font-display); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin-block-end: 0.6rem; }
.demo textarea {
  font: inherit; font-size: 1rem; inline-size: 100%; min-block-size: 5.5rem;
  padding: 0.9rem 1rem; border: 1px solid var(--line); border-radius: var(--radius);
  /* the same rule as the enquiry fields: a field carries its own colour, so it cannot be
     handed the wrong one by whatever band it is dropped into. This block is currently
     unused - the redaction demo markup was cut in phase 3 - and it stays correct in case
     it comes back. */
  background: var(--blue-50); color: var(--ink); resize: vertical;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
.demo textarea:focus { outline: none; border-color: var(--blue-600); background: var(--card); box-shadow: 0 0 0 4px var(--blue-100); }

.demo-flow { display: grid; gap: 0.7rem; margin-block-start: 1.25rem; }
.demo-step { border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 0.9rem 1rem; background: var(--blue-50); }
/* ⛔ WAS --faint (2.85:1) — and these are the STEP LABELS of the redaction demo, the thing that
   explains what the visitor is watching. 11px is exactly the size that has to be legible. */
.demo-step h3 { font-family: var(--font-body); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin: 0 0 0.45rem; font-weight: 700; }
.demo-out { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 0.88rem; word-break: break-word; margin: 0; }

/* ⭐ The token is the whole point of the picture, so it is the only thing wearing the accent — and
   it flashes once as it appears, which is the moment the idea lands. */
.tok {
  background: var(--blue-100); border: 1px solid var(--blue-300); border-radius: 4px;
  padding: 0 .32rem; color: var(--accent-ink); font-weight: 700;
  animation: tokIn .45s var(--ease) both;
}
@keyframes tokIn {
  from { background: var(--blue-600); color: #fff; transform: scale(.94); }
  to   { background: var(--blue-100); color: var(--accent-ink); transform: none; }
}
.demo-note {
  font-size: 0.86rem; color: var(--muted); margin: 1rem 0 0;
  padding-block-start: 0.9rem; border-block-start: 1px solid var(--line-soft);
}

/* ── Cards ───────────────────────────────────────────────────────────────────────────────── */
.grid { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); }

.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 2.5vw, 2rem); scroll-margin-block-start: 6rem;
  box-shadow: var(--shadow-sm);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--blue-300); }
.card h3 { font-size: 1.32rem; margin: 0 0 0.6rem; }
.card p { color: var(--muted); margin: 0 0 0.6rem; }
.card-num {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: 2.6rem; block-size: 2.6rem; border-radius: 10px;
  background: var(--blue-100); color: var(--accent-ink);
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem; margin-block-end: 1rem;
}
.band-deep .card { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.14); box-shadow: none; }
.band-deep .card:hover { background: rgba(255,255,255,.09); border-color: var(--blue-500); }
.band-deep .card p { color: var(--on-deep-muted); }
.band-deep .card-num { background: rgba(255,255,255,.12); color: #fff; }

.placeholder { color: var(--faint); font-size: 0.88rem; font-style: italic; border-inline-start: 2px solid var(--blue-300); padding-inline-start: 0.7rem; }

/* ── Sectors ─────────────────────────────────────────────────────────────────────────────── */
.sectors { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 1rem; }
.sector {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.6rem 1.5rem; box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}
a.sector { text-decoration: none; color: inherit; }
a.sector:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--blue-600); }
/* ⚠️ Hover does not exist for a keyboard, so the focus ring has to stand on its own. */
a.sector:focus-visible { outline: 3px solid var(--blue-300); outline-offset: 2px; }
.sector h3 { font-size: 1.12rem; margin: 0 0 0.35rem; }
.sector p { font-size: 0.93rem; color: var(--muted); margin: 0; }
.sector-open { background: var(--blue-100); border-color: var(--blue-300); }
.sector-open p { color: var(--accent-ink); }
.sector-go {
  margin-block-start: auto; padding-block-start: 1rem;
  font-size: 0.86rem; font-weight: 700; color: var(--accent-ink);
  display: inline-flex; align-items: center; gap: 0.35rem;
  transition: gap .2s var(--ease);
}
a.sector:hover .sector-go { gap: 0.65rem; }
.sectors-more { margin-block-start: 1.5rem; color: var(--muted); font-size: 0.98rem; }
.sectors-more a { font-weight: 700; white-space: nowrap; }

/* ── Marquee ─────────────────────────────────────────────────────────────────────────────── */
/*
 * ⭐ SORT RUNS SEVEN OF THESE. Ours carries the live demos rather than logos we do not have, so the
 * most engaging element on the page is also the most useful one.
 * ⚠️ The track is duplicated in the markup and translated by exactly -50% — that is what makes the
 *    loop seamless. Any other distance produces a visible jump every cycle.
 * ⚠️ Paused on hover AND on focus-within, so somebody chasing a moving chip can catch it, whether
 *    with a mouse or a keyboard.
 */
.marquee {
  overflow: hidden; padding-block: 1.25rem;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track { display: flex; inline-size: max-content; gap: 0.75rem; animation: marquee 42s linear infinite; }
.marquee:hover .marquee-track, .marquee:focus-within .marquee-track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.chip {
  display: inline-flex; align-items: center; gap: 0.55rem; white-space: nowrap;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 0.6rem 1.15rem; font-size: 0.92rem; font-weight: 600; color: var(--heading);
  text-decoration: none; box-shadow: var(--shadow-sm); min-block-size: 44px;
  transition: border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.chip:hover { border-color: var(--blue-600); color: var(--accent-ink); transform: translateY(-2px); }
.chip:focus-visible { outline: 3px solid var(--blue-300); outline-offset: 2px; }
.chip .dot { inline-size: 0.5rem; block-size: 0.5rem; border-radius: 50%; background: var(--blue-500); flex: 0 0 auto; }

/* ── Contact / forms ─────────────────────────────────────────────────────────────────────── */
.narrow { max-inline-size: 44rem; margin-inline: auto; padding: clamp(3rem, 7vw, 5rem) clamp(1.25rem, 5vw, 4rem); }
.narrow h1 { font-size: clamp(2.1rem, 4.5vw, 3rem); margin: 0 0 1rem; }
.enquiry { margin-block-start: 2rem; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(1.5rem, 3vw, 2.25rem); box-shadow: var(--shadow-md); }
.field { margin-block-end: 1.25rem; }
.field label { display: block; font-weight: 600; font-size: 0.92rem; margin-block-end: 0.4rem; color: var(--heading); }
.req { color: var(--blue-600); }
.enquiry input[type=text], .enquiry input[type=email], .enquiry input[type=tel],
.enquiry select, .enquiry textarea {
  font: inherit; inline-size: 100%; padding: 0.85rem 0.95rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  /* ⛔⛔ NOT `inherit`. On the four service pages this form sits inside .band-deep, so every
     field inherited the deep band's pale text (#EAF1FE) onto its own light ground (#F3F7FE) —
     1.06:1, at every width. Names, emails, phone numbers and the two select values were all
     invisible as they were typed, on the page whose whole job is to take an enquiry.
     ⚠️ Fixed HERE rather than in .qform-deep: an input is a place to type, and its colour is a
     property of the input, not of the band behind it. Restoring it only for the deep panel
     leaves the next dark placement to find this again. */
  background: var(--blue-50); color: var(--ink);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
.enquiry :is(input, select, textarea):focus { outline: none; border-color: var(--blue-600); background: var(--card); box-shadow: 0 0 0 4px var(--blue-100); }
.enquiry textarea { resize: vertical; }
/* ⚠️ The placeholder was unstyled and inherited the same way the value did. --muted is 7.0:1 on
   the field's ground, so it reads as a hint without competing with what somebody has typed. */
.enquiry :is(input, select, textarea)::placeholder { color: var(--muted); opacity: 1; }
.consent label { font-weight: 400; font-size: 0.94rem; display: flex; gap: 0.65rem; align-items: flex-start; color: var(--ink); }
.consent input { margin-block-start: 0.35rem; flex: 0 0 auto; accent-color: var(--blue-600); }
.form-error { color: #C0362C; font-size: 0.88rem; margin: 0.35rem 0 0; }
/* ⚠️ --muted, NOT --faint. The privacy line on the lead form is set here and corrected again
   further down; when the same property is declared twice the earlier one has to be right too,
   or moving the later block quietly restores a 2.85:1 failure. */
.fineprint { color: var(--muted); font-size: 0.86rem; }

/*
 * ⛔ THE HONEYPOT. Off-screen rather than display:none — some automated fillers skip anything
 * hidden by display, and a person never reaches it: aria-hidden takes it out of the accessibility
 * tree and tabindex="-1" out of the keyboard order.
 */
.hp { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ── FAQ ─────────────────────────────────────────────────────────────────────────────────── */
/* ⭐ <details>, not JavaScript: keyboard support, screen-reader semantics and find-in-page all work
   for free, and the answers stay in the DOM for search engines. */
.faq { max-inline-size: 48rem; }
.faq details {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  margin-block-end: 0.6rem; padding-inline: 1.4rem; box-shadow: var(--shadow-sm);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.faq details[open] { border-color: var(--blue-300); box-shadow: var(--shadow-md); }
.faq summary { cursor: pointer; list-style: none; font-weight: 600; font-size: 1.06rem; padding-block: 1.15rem; display: flex; justify-content: space-between; gap: 1.5rem; align-items: center; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--blue-600); font-size: 1.35rem; line-height: 1; }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { color: var(--muted); margin: 0 0 1.2rem; max-inline-size: 56ch; }

/* ── Accountability + footer ─────────────────────────────────────────────────────────────── */
.trio { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: clamp(1.5rem, 3vw, 2.5rem); }
.trio-role { font-family: var(--font-display); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent-ink); margin: 0 0 0.6rem; }
.trio h3 { font-size: 1.18rem; margin: 0 0 0.4rem; }

.site-foot { background: var(--blue-950); color: var(--on-deep-muted); padding: clamp(3rem, 6vw, 4.5rem) clamp(1.25rem, 5vw, 4rem) 2.5rem; font-size: 0.94rem; }
.foot-grid { max-inline-size: var(--shell); margin-inline: auto; display: grid; gap: 2.5rem; }
@media (min-width: 760px) { .foot-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.site-foot a { color: var(--on-deep); text-decoration: none; display: inline-block; min-block-size: 24px; padding-block: 0.25rem; transition: color .2s var(--ease); }
.site-foot a:hover { color: #fff; text-decoration: underline; }
.site-foot h4 { font-family: var(--font-display); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--blue-300); margin: 0 0 0.75rem; font-weight: 700; }
.foot-list { list-style: none; margin: 0; padding: 0; }
.foot-brand .brand-name { color: #fff; font-size: 1.3rem; }
.foot-brand .brand-with { color: var(--blue-300); }

/*
 * ⭐ THE BUILD CREDIT IS A CREDENTIAL, NOT SMALL PRINT. For a buyer worried about who stands behind
 * the technology, "built and secured by Pop Hasta Labs, London" is the strongest signal here — so
 * it gets a rule above it and room around it.
 */
.foot-built { max-inline-size: var(--shell); margin: 2.5rem auto 0; padding-block-start: 1.75rem; border-block-start: 1px solid rgba(255,255,255,.14); display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: center; justify-content: space-between; }
.built-by { display: flex; align-items: center; gap: 0.75rem; color: #fff; }
.built-by svg { inline-size: 1.5rem; block-size: 1.5rem; color: var(--blue-500); flex: 0 0 auto; }
.foot-legal { color: var(--blue-300); font-size: 0.86rem; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * RUNNING PROSE
 *
 * ⭐ THE PAGE WAS ALL HEADLINE AND ONE-LINER, WHICH READS AS A DECK RATHER THAN A COMPANY. A firm
 * being asked to hand over its client records wants to see that someone has thought the problem
 * through, and only paragraphs show that. These are the styles that let real copy exist.
 *
 * ⛔⛔ MEASURE IS THE WHOLE JOB, AND THE CAP IS IN ch FOR A REASON. Past roughly 75 characters the
 * eye loses the start of the next line, so long copy set too wide is harder to read than short copy
 * — which would defeat the entire point of having written it.
 *
 * ⚠️ THE FIRST VERSION OF THIS SAID 38rem AND CLAIMED "near 68 characters". Measured, it was 77,
 * and the platform overrides ran to 89. rem caps a WIDTH; what matters is how many CHARACTERS fit
 * in it, and that changes with the font a visitor actually gets. 1ch is the width of the "0" glyph,
 * which in this stack is about 1.18x the average character — so 56ch lands at ~66 characters and
 * stays there if the font falls back to something wider. ⛔ Do not "tidy" these back into rem.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.prose { max-inline-size: 56ch; }
.prose p { margin: 0 0 1.15rem; color: var(--muted); font-size: 1.08rem; line-height: 1.75; }
.prose p:last-child { margin-block-end: 0; }
.prose p strong { color: var(--heading); font-weight: 600; }
.prose h3 {
  font-size: 1.25rem; margin: 2.25rem 0 0.75rem;
  /* ⚠️ Optical alignment: the rule sits outside the text column so the prose edge stays straight. */
  padding-inline-start: 0.9rem; border-inline-start: 3px solid var(--blue-500);
}
.prose h3:first-child { margin-block-start: 0; }
.band-deep .prose p { color: var(--on-deep-muted); }
.band-deep .prose p strong { color: #fff; }

/* ⭐ EDITORIAL TWO-COLUMN. The heading holds its place on the left while the argument runs down
   the right — the layout magazines use for exactly this job, and it makes a long passage look
   considered rather than heavy. Single column below the breakpoint, where side-by-side would
   squeeze the measure to nothing. */
.prose-split { display: grid; gap: clamp(1.75rem, 4vw, 3.5rem); align-items: start; }
@media (min-width: 900px) {
  .prose-split { grid-template-columns: 0.85fr 1.15fr; }
  /* ⚠️ Sticky, not fixed, and only where there is height to travel — on a short section a sticky
     heading would judder rather than glide. */
  .prose-split > .section-head { position: sticky; inset-block-start: 2.5rem; margin-block-end: 0; }
}

/* ── The process: numbered, because sequence is the reassurance ──────────────────────────── */
.steps { display: grid; gap: 1.25rem; counter-reset: step; list-style: none; margin: 0; padding: 0; }
@media (min-width: 860px) { .steps { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; } }
.steps li {
  counter-increment: step;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.4rem, 2.5vw, 1.9rem);
  box-shadow: var(--shadow-sm);
  transition: transform .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease);
}
.steps li:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--blue-300); }
.steps li::before {
  content: counter(step, decimal-leading-zero);
  display: block; font-family: var(--font-display); font-weight: 700; font-size: 0.78rem;
  letter-spacing: 0.12em; color: var(--accent-ink); margin-block-end: 0.7rem;
}
.steps h3 { font-size: 1.15rem; margin: 0 0 0.5rem; }
.steps p { color: var(--muted); margin: 0 0 0.5rem; font-size: 1rem; line-height: 1.7; }
.steps p:last-child { margin-block-end: 0; }
/* ⛔ NOT --faint. That token is documented above as decorative-only, and this line carries
   "no cost, no obligation" — the sentence a hesitant visitor most needs to read. It measured
   3.06:1 against the card and was the least legible text on the page. It is a commitment, so
   it now reads as one: brand blue on a tint, quiet by size and weight rather than by fading. */
.steps .step-when {
  display: inline-block; font-family: var(--font-display); font-size: 0.78rem; font-weight: 600;
  letter-spacing: .03em; color: var(--accent-ink);
  background: var(--blue-50); border: 1px solid var(--line-soft);
  border-radius: 999px; padding: 0.3rem 0.7rem; margin-block-start: 0.25rem;
}

/*
 * ── Honest limits ────────────────────────────────────────────────────────────────────────
 * ⭐⭐ THE MOST PERSUASIVE BLOCK ON THE SITE, AND IT IS THE ONE THAT SAYS NO. Every competitor
 * page claims everything; a buyer senior enough to sign has read a hundred of them. Naming what
 * the product will not do is the cheapest credibility available and the first thing a nervous
 * marketer will want to delete. ⛔ Do not delete it.
 */
.limits { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; }
@media (min-width: 820px) { .limits { grid-template-columns: repeat(2, 1fr); } }
.limits > div { background: var(--card); padding: clamp(1.3rem, 2.4vw, 1.7rem); }
.limits h3 { font-size: 1.06rem; margin: 0 0 0.45rem; display: flex; gap: 0.6rem; align-items: baseline; }
.limits h3::before { content: "—"; color: var(--blue-500); font-weight: 700; flex: 0 0 auto; }
.limits p { color: var(--muted); margin: 0; font-size: 0.99rem; line-height: 1.7; }

/* ── A line that carries weight on its own ───────────────────────────────────────────────── */
.pull {
  font-family: var(--font-display); font-size: clamp(1.3rem, 2.6vw, 1.75rem); line-height: 1.4;
  color: var(--heading); font-weight: 500; margin: clamp(2rem, 4vw, 3rem) 0;
  padding-inline-start: clamp(1.1rem, 2vw, 1.6rem); border-inline-start: 4px solid var(--blue-500);
  max-inline-size: 40rem;
}
.band-deep .pull { color: #fff; border-inline-start-color: var(--blue-500); }

/* ── Contact: the form, and what happens after it ────────────────────────────────────────── */
.contact-grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
@media (min-width: 940px) { .contact-grid { grid-template-columns: 1.15fr 0.85fr; } }
.contact-aside {
  background: var(--blue-50); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 3vw, 2.25rem);
}
.contact-aside h2 { font-size: 1.2rem; margin: 0 0 1.1rem; }
.contact-aside ol { margin: 0 0 1.5rem; padding-inline-start: 0; list-style: none; counter-reset: ca; }
.contact-aside li { counter-increment: ca; position: relative; padding-inline-start: 2.35rem; margin-block-end: 1.1rem; color: var(--muted); font-size: 0.99rem; line-height: 1.65; }
.contact-aside li:last-child { margin-block-end: 0; }
.contact-aside li::before {
  content: counter(ca); position: absolute; inset-inline-start: 0; inset-block-start: 0.1rem;
  inline-size: 1.6rem; block-size: 1.6rem; border-radius: 50%;
  background: var(--blue-600); color: #fff;
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 700;
  display: grid; place-items: center;
}
.contact-aside strong { color: var(--heading); font-weight: 600; }
.contact-aside .aside-rule { border: 0; border-block-start: 1px solid var(--line); margin: 1.5rem 0; }

/* ⚠️ THE SECOND ASIDE LIST IS NOT A SEQUENCE, so it must not wear the numbered circles — the
   counter is absolutely positioned and would sit on top of the text once the indent is removed.
   A separate class, rather than inline overrides that only half undo the rule. */
.aside-plain { counter-reset: none; }
.aside-plain li { padding-inline-start: 1.1rem; }
.aside-plain li::before {
  content: ""; inline-size: 5px; block-size: 5px; border-radius: 50%;
  background: var(--blue-500); inset-block-start: 0.62rem;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THREE THINGS ONLY A REAL BROWSER COULD SHOW
 *
 * Found by driving Chrome against the live site. None of them could surface in the headless
 * pane, which reported innerWidth 0 and never laid a page out.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/*
 * ⛔⛔ THE HEADER "BOOK A CALL" BUTTON WAS ILLEGIBLE — 1.25:1. `.site-nav a` sets colour to
 * --muted, and `.nav-cta` only ever set white ON HOVER, so the resting state was #4E5C74 text on
 * the #2F63D8 fill. The primary conversion control on every page of a lead-generation site was
 * effectively invisible, and it had been since the nav was built. White takes it to 5.39:1.
 * ⛔ If you restyle the nav, set the CTA's resting colour explicitly. Inheriting it is what broke.
 */
.site-nav a.nav-cta { color: #fff; }

/*
 * ⚠️ THE HIGHLIGHT SWASH WAS FIGHTING THE TEXT COLOUR. `.mark` sits on an <em>, and the hero
 * already painted every <em> blue — so blue text landed on a pale blue wash at 4.6:1 and read as
 * accidentally selected text rather than a deliberate marker. Pop Hasta Labs keep the ink dark and
 * let the wash do the work; theirs is legible precisely because the text is NOT tinted. 16.14:1.
 */
.hero h1 em.mark, h1 em.mark, h2 em.mark { color: var(--heading); }

/*
 * ⛔ THE TRUST STRIP WAS A ROW OF EMPTY CIRCLES. The ::before was a filled 18px disc with no glyph
 * in it, so the three assurances under the hero read as UNCHECKED boxes — the precise opposite of
 * what they are there to say. Now an actual tick, drawn with two borders rather than a character
 * so it cannot be affected by a missing glyph.
 */
.hero-trust li::before {
  content: "";
  background: none;
  border-radius: 0;
  inline-size: 0.72rem;
  block-size: 0.4rem;
  border-inline-start: 2.2px solid var(--blue-600);
  border-block-end: 2.2px solid var(--blue-600);
  border-block-start: 0;
  border-inline-end: 0;
  transform: rotate(-45deg);
  flex: 0 0 auto;
  margin-block-start: -0.28rem;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * MOBILE, MEASURED UNDER REAL DEVICE EMULATION
 *
 * Nothing available could give a true mobile viewport — the Browser pane never lays pages out,
 * Chrome refuses resize_window on a maximised window, and an iframe probe is blocked by
 * X-Frame-Options. Driving Chrome's DevTools protocol directly finally did, and it corrected an
 * earlier conclusion of mine in both directions: 390px was FINE (a screenshot artefact had made
 * it look broken), and 360px genuinely overflowed.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/*
 * ⛔⛔ repeat(auto-fit, minmax(22rem, 1fr)) IS NOT RESPONSIVE, AND I PREVIOUSLY REPORTED THAT IT
 * WAS. auto-fit collapses the COLUMN COUNT, but the `min` half of minmax is a hard floor: a 22rem
 * column stays 352px wide no matter how narrow the screen gets, so it pushes the page sideways on
 * anything under about 392px. Measured: 13px of horizontal scroll at 360px, 52px at 320px.
 *
 * ⭐ min(22rem, 100%) caps that floor at the container width, so the column can shrink the rest of
 * the way. This is the whole fix, and it belongs on EVERY auto-fit grid — the others only escaped
 * because their floors happened to be narrow enough.
 * ⛔ Never write minmax(<fixed>, 1fr) again without wrapping the min in min(..., 100%).
 */
.grid-3    { grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr)); }
.grid-2    { grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr)); }
.sectors   { grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }
.trio      { grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.stats-band{ grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr)); }

/*
 * ⛔⛔ THE CONSENT CHECKBOX WAS 13x13. It is the control that gates the only conversion on the
 * site — you cannot send an enquiry without ticking it — and it was barely half the 24x24 that
 * WCAG 2.2 AA asks for, on the device where fingers are the input. 24px square now.
 * ⭐ And the whole label is the target anyway (it wraps the input), so the touch area is the full
 *    row; the box being tiny made it LOOK unhittable even where it was not.
 */
.consent input {
  inline-size: 1.5rem;
  block-size: 1.5rem;
  margin-block-start: 0.1rem;
}
.consent label { align-items: center; gap: 0.75rem; min-block-size: 24px; }

/*
 * ⛔ THE PRIVACY LINE ON THE LEAD FORM WAS SET IN --faint. That token is documented at the top of
 * this file as decorative-only, never for a sentence somebody must read — and this is the sentence
 * promising we will not sell their details. 3.06:1, the least legible text on the page that asks
 * for the most trust. --muted takes it to 6.76:1.
 * ⚠️ Second time this token has escaped into real copy. If a rule needs --faint, ask whether the
 *    text matters; if it does, it is the wrong token.
 */
.fineprint { color: var(--muted); }

/*
 * ── Tap targets: 24x24 is the WCAG 2.2 AA floor ─────────────────────────────────────────────
 * ⚠️ Both of these were text links sized purely by their line-height (22px and 19px). Padding
 *    them up costs nothing visually and makes them hittable.
 */
.util-strip a {
  display: inline-flex; align-items: center;
  min-block-size: 24px; padding-block: 0.15rem;
}
.sectors-more a {
  display: inline-block;
  min-block-size: 24px; padding-block: 0.25rem;
  white-space: normal;   /* ⚠️ nowrap forced this link to stick out of a narrow column */
}

/*
 * ⚠️ THE HERO HEADLINE'S FLOOR WAS 2.7rem — 43px on a 360px screen, which left "Give your team AI."
 * filling three lines and dominating the first viewport. clamp()'s first argument is a MINIMUM, so
 * it never got smaller however narrow the screen. 2.1rem reads far better at phone width and the
 * preferred/maximum values are untouched, so nothing changes on a desktop.
 */
.hero h1 { font-size: clamp(2.1rem, 5.8vw, 4.4rem); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * FRS LUMA'S OWN LANGUAGE — "THE RECORD"
 *
 * ⛔⛔ THE PREVIOUS VERSION OF THIS FILE WAS A COPY AND WAS CALLED OUT AS ONE. I had read Pop
 * Hasta Labs' page with getComputedStyle and reproduced its tokens exactly — 14px radius, their
 * shadow, their pill metrics, their centred hero, their four navy stat cards. Accurate homage is
 * still a clone, and a clone is actively WRONG here: FRS Luma is a separate Dubai firm that
 * partners with Pop Hasta Labs. Looking like their landing page makes it read as their subsidiary.
 *
 * ⭐⭐ SO THE LANGUAGE IS BUILT OUT OF THE PRODUCT INSTEAD. We sell a CRM, so the page is made of
 * the shapes a CRM is made of: the record card, the pipeline stage, the field row, the status
 * pill. It is original, it is unmistakably ours, and it argues for the product just by existing.
 *
 *      Pop Hasta Labs              FRS Luma
 *      ──────────────              ────────
 *      centred everything          left-aligned and asymmetric
 *      tinted pill eyebrow         numbered stage on a spine
 *      14px radius, soft shadow    8px radius, crisp border, no drop shadow
 *      highlight swash             weight and colour only
 *      four identical stat cards   one pipeline bar
 *      plain navy gradient         deep navy under a fine grid
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/* ⭐ Montserrat, the client's specified secondary face: "clean and versatile, designed for clarity
   across body copy". It replaces Inter, which was chosen before the brand guide existed.
   ⚠️ THE ITALICS ARE REAL, NOT SYNTHESISED. Two blocks on this site are set in italic — the
   reserved testimonial and the empty-state note — and a browser asked for an italic it does not
   have will slant the upright one, which in a geometric face looks like a fault rather than a
   style. Four files instead of two, and worth it. */
@font-face {
  font-family: "Montserrat";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/montserrat-latin-ext-italic.09331aad1aef.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Montserrat";
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/montserrat-latin-italic.7921052ef928.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/montserrat-latin-ext.d16dfe26bcd7.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/montserrat-latin.311d352d9323.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --font-body: "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ⭐ CRISP, NOT SOFT. Their cards float on a wide blur; ours sit on the page with a drawn edge.
     A system that holds client records should look precise, not pillowy. */
  --radius-card: 8px;
  --radius-btn: 8px;
  --edge: 0 0 0 1px var(--line);
  --edge-hover: 0 0 0 1px var(--blue-300);
  --lift: 0 6px 18px rgba(11, 42, 107, .08);
}

/* ⛔ THE HEADER CAP IS A BUG FIX, NOT STYLING — without it the brand sits 618px away from the
   content on a wide monitor. It survives every redesign. */
.head-inner {
  max-inline-size: var(--shell); margin-inline: auto; inline-size: 100%;
  display: flex; align-items: center; gap: 1.5rem;
}

h1 { letter-spacing: -0.022em; }
h2 { letter-spacing: -0.018em; }

/*
 * ── The stage marker ──────────────────────────────────────────────────────────────────────
 * ⭐ OUR EYEBROW IS A PIPELINE STAGE, not a tinted pill. A number, a rule, a label — the way a
 * deal stage is written in the product itself. Left-aligned, because the whole page is.
 */
.stage {
  /* ⚠️ flex-start, NOT center. On a phone a long label wraps — "POP HASTA LABS · LONDON" takes two
     lines — and centring floated the number badge into the gap between them, reading as a stray
     chip rather than a stage number. Aligned to the first line it stays attached to the label.
     ⚠️ The badge and a single line of label are near enough the same height that this is invisible
     in the common case. */
  display: flex; align-items: flex-start; gap: 0.7rem;
  margin: 0 0 1.1rem;
  font-family: var(--font-display); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent-ink);
}
.stage .stage-n {
  font-variant-numeric: tabular-nums;
  background: var(--blue-600); color: #fff;
  border-radius: 4px; padding: 0.2rem 0.42rem; letter-spacing: 0.06em;
}
.stage::after { content: ""; flex: 1 1 auto; block-size: 1px; background: var(--line); max-inline-size: 5rem; margin-block-start: 0.62em; }
.band-deep .stage { color: var(--blue-300); }
.band-deep .stage .stage-n { background: var(--blue-500); color: var(--blue-950); }
.band-deep .stage::after { background: rgba(255,255,255,.18); }

/*
 * ── The record card ───────────────────────────────────────────────────────────────────────
 * ⭐⭐ THE SIGNATURE SHAPE, AND IT IS A SCREENSHOT OF THE PRODUCT'S OWN FURNITURE: a header strip
 * carrying a title and a status, then field rows underneath. Nobody has to explain what the CRM
 * looks like — the page is already built out of it.
 */
.rec {
  background: var(--card); border-radius: var(--radius-card);
  box-shadow: var(--edge); overflow: hidden;
  display: flex; flex-direction: column;
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.rec:hover { box-shadow: var(--edge-hover), var(--lift); transform: translateY(-2px); }
.rec-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.85rem 1.15rem;
  background: var(--blue-50);
  border-block-end: 1px solid var(--line-soft);
}
.rec-title { font-family: var(--font-display); font-weight: 700; font-size: 1.02rem; color: var(--heading); }
.rec-status {
  font-family: var(--font-display); font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--blue-100);
  border-radius: 3px; padding: 0.22rem 0.45rem; white-space: nowrap;
}
.rec-body { padding: 1.15rem; }
.rec-body p { color: var(--muted); margin: 0 0 0.7rem; font-size: 1rem; line-height: 1.7; }
.rec-body p:last-child { margin-block-end: 0; }

/* field rows, the way a record shows its data */
.rec-fields { list-style: none; margin: 0; padding: 0; }
.rec-fields li {
  display: flex; gap: 1rem; justify-content: space-between; align-items: baseline;
  padding-block: 0.6rem; border-block-end: 1px solid var(--line-soft);
  font-size: 0.96rem;
}
.rec-fields li:last-child { border-block-end: 0; }
.rec-fields .f-k { color: var(--muted); }
.rec-fields .f-v { color: var(--heading); font-weight: 600; text-align: end; }

/*
 * ── The pipeline bar ──────────────────────────────────────────────────────────────────────
 * ⭐ ONE BAR, NOT FOUR CARDS. Their band is four identical boxes; ours is a single strip divided
 * into stages, which is what a pipeline actually looks like and reads as one idea rather than four.
 */
.pipeline {
  display: grid; gap: 1px; background: var(--stat-line, #1F3A5E);
  border-radius: var(--radius-card); overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
}
.pipe-cell { background: #11203A; padding: 1.15rem 1.25rem; }
.pipe-n {
  display: block; font-family: var(--font-display); font-size: 1.65rem; font-weight: 700;
  letter-spacing: -0.02em; color: #7FB0FF; line-height: 1.15; margin-block-end: 0.35rem;
}
.pipe-k { display: block; color: #B8CBEA; font-size: 0.9rem; line-height: 1.55; }

/*
 * ── The grid overlay on dark bands ────────────────────────────────────────────────────────
 * ⭐ A fine graticule, the texture of a spreadsheet or a schedule. It is what makes our dark
 * sections read as "system" where a plain gradient reads as "marketing".
 * ⚠️ Pseudo-element rather than a background layer on .band-deep, so the existing gradient stays
 *    intact underneath and the contrast measurements taken against it still hold.
 */
.band-deep { position: relative; }
.band-deep::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 75%);
}
.band-deep > * { position: relative; z-index: 1; }

/* ── Tick list, left-aligned ─────────────────────────────────────────────────────────────── */
.tick-grid { list-style: none; margin: 1.75rem 0 0; padding: 0; display: grid; gap: 0.7rem 2rem; }
@media (min-width: 760px) { .tick-grid { grid-template-columns: 1fr 1fr; } }
.tick-grid li { position: relative; padding-inline-start: 1.6rem; color: var(--muted); font-size: 0.99rem; line-height: 1.6; }
.tick-grid strong { color: var(--heading); font-weight: 600; }
.tick-grid li::before {
  content: ""; position: absolute; inset-inline-start: 0; inset-block-start: 0.45em;
  inline-size: 0.7rem; block-size: 0.38rem;
  border-inline-start: 2.2px solid var(--blue-600); border-block-end: 2.2px solid var(--blue-600);
  transform: rotate(-45deg);
}

/* ── Utility strip, squared off ──────────────────────────────────────────────────────────── */
/*
 * ⛔⛔ THE STRIP MUST BE PADDED THE SAME WAY AS EVERY OTHER BAND, OR ITS TEXT DOES NOT LINE UP.
 * It used to carry padding-inline ON .util-inner, inside its own 1180px box, while the header,
 * the content and the footer all take their padding from the PARENT band and cap the child. Two
 * different structures produce two different text edges: measured at 1440px the strip started at
 * 194px while the brand directly beneath it started at 130px — 64px adrift, on every page.
 *
 * ⚠️ IT HID FROM A BOX-EDGE MEASUREMENT. Comparing the containers rects showed them agreeing at
 * 1440px, because the boxes DO align; only the text inside them differs. It surfaced at 1280px,
 * where the box edges disagree too. ⛔ When checking alignment, measure the text edge.
 */
.util-strip {
  background: var(--blue-950); color: var(--on-deep); font-size: 0.82rem;
  padding-inline: clamp(1.25rem, 5vw, 4rem);
}
.util-inner {
  max-inline-size: var(--shell); margin-inline: auto;
  padding-block: 0.55rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.5rem;
}
.util-strip a { color: #fff; text-decoration: none; font-weight: 600; display: inline-flex; align-items: center; min-block-size: 24px; }
.util-strip a:hover { text-decoration: underline; }
/* ⚠️ --muted, not --blue-500. That blue was for the utility strip's dark ground; the strip
      went in phase 1 and this class stayed on the blog's light cards at 3.65:1. If the strip
      ever returns it re-scopes its own colour rather than setting the default here. */
.util-sep { color: var(--muted); }
.util-strip .util-sep { color: var(--blue-500); }
.util-right { margin-inline-start: auto; display: flex; gap: 1.25rem; align-items: center; }
@media (max-width: 720px) { .util-right { margin-inline-start: 0; } }

/* ── Cards and buttons adopt the crisp edge ──────────────────────────────────────────────── */
.card {
  border-radius: var(--radius-card);
  border: 0; box-shadow: var(--edge);
}
.card:hover { box-shadow: var(--edge-hover), var(--lift); }
.cta, .cta-quiet { border-radius: var(--radius-btn); }

/* reveal: 18px / .6s is a generic, well-behaved default rather than anybody's signature */
.js-reveal .reveal { transform: translateY(18px); transition-duration: .6s; transition-timing-function: ease; }

/* ── Hero: left-aligned and asymmetric, with the record beside it ────────────────────────── */
.hero { background: radial-gradient(1000px 520px at 15% -10%, var(--blue-100) 0%, transparent 62%), var(--paper); }
.hero h1 { font-size: clamp(2.1rem, 5.2vw, 3.9rem); }
.hero-split { display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
@media (min-width: 1000px) { .hero-split { grid-template-columns: 1.05fr 0.95fr; } }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE MOBILE HEADER WAS EATING HALF THE FIRST SCREEN
 *
 * ⛔⛔ MEASURED UNDER DEVICE EMULATION AT 390px: utility strip 72px + header 216px = 288px of
 * chrome, and the headline did not begin until 387px — 46% of the way down the phone viewport.
 * A visitor arriving on a phone saw a logo and a menu, and had to scroll to find out what we sell.
 *
 * THE CAUSE: .head-inner was flex-wrap: nowrap, so the brand and the nav were forced onto one row.
 * The brand took the width it needed and the nav was squeezed into what was left — which was
 * narrow enough that all four items stacked, giving a FOUR-ROW menu.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {
  /* ⭐ Let the brand have its own row and the nav have its own, each using the full width. Four
     short items fit across a phone comfortably once they are not fighting the logo for space. */
  .head-inner { flex-wrap: wrap; gap: 0.5rem 1rem; }
  .site-head { padding-block: 0.6rem; }

  .brand { flex: 1 1 100%; }
  .brand-name { font-size: 1.05rem; }
  .brand-logo { block-size: 32px; }
  /* ⚠️ The partner line is a CREDENTIAL, not decoration — it is kept, just set tighter so it
     takes one line instead of three. ⛔ Do not delete it to save space. */
  .brand-with { font-size: 0.58rem; letter-spacing: 0.06em; line-height: 1.35; }

  .site-nav { margin-inline-start: 0; flex: 1 1 100%; }
  /* ⛔ WAS nowrap, AND THAT BROKE THE MOMENT A FIFTH NAV ITEM ARRIVED. Four links plus the CTA
     cannot share one row on a phone: 14px of horizontal scroll appeared at 390px on every page,
     while 360px and 320px stayed clean because a narrower rule below already let them wrap. The
     bug therefore lived only in the 381-760px band — which is where most phones actually are.
     ⚠️ Sizing the nav to fit N items exactly is the fragile bit; wrapping survives the next link. */
  .site-nav ul { gap: 0.2rem 0.15rem; justify-content: flex-start; flex-wrap: wrap; }
  .site-nav a { padding-inline: 0.45rem; font-size: 0.88rem; white-space: nowrap; }
  .site-nav a.nav-cta { padding-inline: 0.8rem; }

  /*
   * ⚠️ THE UTILITY STRIP DROPS ITS LABEL, NOT ITS LINK. "Governed AI and secured CRM · Dubai" and
   * the demos link cannot share one line on a phone, so the strip was wrapping to 72px. The label
   * repeats what the hero says two hundred pixels later; the link is the one thing on the strip a
   * visitor cannot get anywhere else, so that is what survives.
   */
  .util-label { display: none; }
  .util-inner { padding-block: 0.4rem; justify-content: center; }
  .util-right { margin-inline-start: 0; }
}

/*
 * ⚠️ BELOW 380px, SHRINKING IS NOT ENOUGH AND nowrap BECOMES THE BUG. Forcing the four items onto
 * one line pushed 5px of horizontal scroll onto a 320px screen — I had traded one overflow for a
 * smaller one. Two rows of two costs about 30px of header and costs nothing else, so below 380px
 * the nav is allowed to wrap again.
 */
@media (max-width: 380px) {
  .site-nav a { padding-inline: 0.3rem; font-size: 0.82rem; }
  .site-nav ul { flex-wrap: wrap; justify-content: flex-start; gap: 0.2rem 0.1rem; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE PROMISE, THE FLOW RAIL, AND MORE MOTION
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/*
 * ── The repeated line ─────────────────────────────────────────────────────────────────────
 * ⭐ It appears on every page and several times on some, so it needs to read as a STATEMENT rather
 * than as body copy that happens to recur — a marked left edge and a slightly larger size make it
 * land as the company's position each time, instead of looking like an editing mistake.
 */
.promise {
  margin: 1.5rem 0 0;
  padding-inline-start: 1rem;
  border-inline-start: 3px solid var(--blue-500);
  color: var(--muted);
  font-size: 1.05rem;
  line-height: 1.65;
  max-inline-size: 46ch;
}
.promise strong { color: var(--heading); font-weight: 600; }
.promise-lead { font-size: 1.18rem; max-inline-size: 52ch; }
.promise-deep { color: var(--on-deep-muted); border-inline-start-color: var(--blue-500); }
.promise-deep strong { color: #fff; }
/* ⚠️ Centred bands centre the line too, so it does not hang off-axis under a centred heading. */
.inner-narrow .promise { margin-inline: auto; text-align: start; max-inline-size: 40ch; }
.site-foot .promise { margin-block-start: 1.25rem; font-size: 0.92rem; max-inline-size: 26rem; }

/*
 * ── The request-path rail ─────────────────────────────────────────────────────────────────
 * ⭐ A numbered rail rather than four loose cards: the architecture is a SEQUENCE, and a reader
 * needs to feel the boundary being crossed at step three. Drawn in CSS so there is no diagram
 * image to go stale, and it stacks vertically on a phone without any extra rules.
 */
.flow { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.25rem; counter-reset: fl; }
@media (min-width: 900px) { .flow { grid-template-columns: repeat(4, 1fr); gap: 1rem; } }

.flow-step {
  counter-increment: fl;
  position: relative;
  background: var(--card);
  border-radius: var(--radius-card);
  box-shadow: var(--edge);
  padding: 1.4rem 1.25rem 1.25rem;
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.flow-step:hover { box-shadow: var(--edge-hover), var(--lift); transform: translateY(-3px); }
.flow-step::before {
  content: counter(fl, decimal-leading-zero);
  position: absolute; inset-block-start: -0.75rem; inset-inline-start: 1.25rem;
  background: var(--blue-600); color: #fff;
  font-family: var(--font-display); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em;
  border-radius: 4px; padding: 0.22rem 0.45rem;
}
.flow-step h3 { font-size: 1.06rem; margin: 0.45rem 0 0.5rem; }
.flow-step p { color: var(--muted); margin: 0; font-size: 0.97rem; line-height: 1.65; }
.flow-badge {
  display: inline-block;
  font-family: var(--font-display); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--blue-50);
  border-radius: 3px; padding: 0.2rem 0.4rem;
}
/* ⚠️ The one step that leaves your control is marked, because that is the whole point of the
   diagram and it must not be just another box in the row. */
.flow-out { box-shadow: 0 0 0 1px var(--blue-300); background: var(--blue-50); }
.flow-out .flow-badge { background: var(--blue-600); color: #fff; }

.band-deep .flow-step { background: #11203A; box-shadow: 0 0 0 1px var(--stat-line, #1F3A5E); }
.band-deep .flow-step h3 { color: #fff; }
.band-deep .flow-step p { color: #B8CBEA; }
.band-deep .flow-badge { background: rgba(91,150,255,.16); color: #A9C8FF; }

/*
 * ── Counting figures ──────────────────────────────────────────────────────────────────────
 * ⚠️ Tabular numerals so the width does not jitter while the value climbs. Without this the whole
 *    row twitches sideways on every frame, which is exactly the cheap look the animation is for.
 */
.count { font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * MORE MOTION
 * ⛔ ALL OF IT INSIDE .js-reveal AND SUPPRESSED BY prefers-reduced-motion, exactly like the
 * existing reveals — so none of it can leave content stuck invisible, and the 1500ms safety net in
 * reveal.js still un-hides everything if anything goes wrong.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/* ⭐ STAGGERED CHILDREN. Grids animate item by item rather than as one slab, which is what makes a
   row of cards feel like it is being dealt rather than switched on. */
.js-reveal .reveal.in > .grid > *,
.js-reveal .reveal.in > .flow-step,
.js-reveal .grid > .reveal.in { transition-delay: 0ms; }
.js-reveal .reveal-1 { transition-delay: 60ms; }
.js-reveal .reveal-2 { transition-delay: 140ms; }
.js-reveal .reveal-3 { transition-delay: 220ms; }
.js-reveal .reveal-4 { transition-delay: 300ms; }

/* ⭐ The stage marker's rule draws itself in when the section arrives — a small thing that makes a
   heading feel authored rather than placed. */
.js-reveal .reveal .stage::after { transform: scaleX(0); transform-origin: left center; }
.js-reveal .reveal.in .stage::after { transform: scaleX(1); transition: transform .7s .15s var(--ease); }

/* ⭐ Buttons and links pick up a little travel on hover, matching the cards. */
.cta, .cta-quiet, .sector, .chip { transition: transform .25s var(--ease), box-shadow .25s var(--ease), background-color .25s var(--ease), border-color .25s var(--ease), color .25s var(--ease); }
.sector:hover { transform: translateY(-3px); }
.sector-go { transition: gap .25s var(--ease); }

/* ⭐ The record card's status pill lifts slightly with its card, so the card reads as one object. */
.rec-status { transition: background-color .3s var(--ease), color .3s var(--ease); }
.rec:hover .rec-status { background: var(--blue-600); color: #fff; }

/* ⭐ FAQ rows nudge on hover so a long list of them stops looking inert. */
.faq details { transition: border-color .25s var(--ease), box-shadow .25s var(--ease), transform .25s var(--ease); }
.faq details:hover { border-color: var(--blue-300); }

/*
 * ⛔⛔ AND EVERY BIT OF IT IS OFF UNDER REDUCED MOTION. The existing block already zeroes
 * transitions globally, but these delays would still stack, so they are cleared explicitly.
 */
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal-1,
  .js-reveal .reveal-2,
  .js-reveal .reveal-3,
  .js-reveal .reveal-4 { transition-delay: 0ms !important; }
  .js-reveal .reveal .stage::after,
  .js-reveal .reveal.in .stage::after { transform: none !important; transition: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE MOBILE MENU
 *
 * ⛔⛔ THE DEFAULT STATE IS "BUTTON HIDDEN, MENU VISIBLE". Everything below only takes effect
 * inside `html.js-nav`, a class added by the inline head script. With JavaScript off, blocked or
 * broken, the header behaves exactly as it did before this file changed: every link on screen and
 * no control to press. Hiding navigation is the script's job, never the markup's.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.nav-toggle { display: none; }

@media (max-width: 760px) {
  .js-nav .nav-toggle {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-inline-start: auto;
    background: var(--card); color: var(--heading);
    border: 1px solid var(--line); border-radius: var(--radius-btn);
    /* ⚠️ 44px, not the 24px WCAG floor. This is the only way to reach the whole site on a phone,
       so it gets the comfortable Apple/Android target rather than the bare minimum. */
    min-block-size: 44px; padding: 0 0.85rem;
    font-family: var(--font-display); font-weight: 600; font-size: 0.9rem;
    cursor: pointer;
    transition: border-color .2s var(--ease), background-color .2s var(--ease);
  }
  .js-nav .nav-toggle:hover { border-color: var(--blue-300); }
  .js-nav .nav-toggle:focus-visible { outline: none; border-color: var(--blue-600); box-shadow: 0 0 0 4px var(--blue-100); }

  /* ⚠️ flex-basis: 0 IS WHAT PUTS THE BUTTON BESIDE THE BRAND, AND min-width WAS A RED HERRING.
     Flexbox decides where a line BREAKS from each item hypothetical main size — which with
     flex-basis:auto is its content width. The partner line under the brand is about 350px, the
     full row, so the toggle never fitted and wrapped beneath. min-width:0 only permits shrinking
     once items already share a line; it cannot stop one claiming the row in the first place.
     With a basis of 0 the brand asks for nothing, the button fits, and the brand then grows into
     whatever is left — its partner line wrapping to two, which is free. */
  .js-nav .brand { flex: 1 1 0; min-inline-size: 0; }
  .js-nav .site-nav { flex: 1 1 100%; }

  /*
   * ⚠️ display:none, NOT max-height or visibility. A collapsed menu that is merely transparent is
   * still in the tab order, so a keyboard user tabs through four invisible links before reaching
   * the page. Removing it from the box tree removes it from the tab order too.
   */
  .js-nav .site-nav ul { display: none; }
  .js-nav .site-nav.open ul {
    display: flex; flex-direction: column; align-items: stretch;
    gap: 0.15rem;
    padding-block: 0.5rem 0.25rem;
    animation: navDrop .22s var(--ease) both;
  }
  .js-nav .site-nav.open a { padding-block: 0.7rem; font-size: 0.98rem; }
  .js-nav .site-nav.open a.nav-cta { margin-block-start: 0.35rem; text-align: center; }
}

@keyframes navDrop {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
/* ⛔ The drop is suppressed for anyone who asked for less movement; the menu still opens. */
@media (prefers-reduced-motion: reduce) {
  .js-nav .site-nav.open ul { animation: none; }
}

/*
 * ── The icon ──────────────────────────────────────────────────────────────────────────────
 * ⭐ Three bars that fold into a cross, which tells you the second press will close it. The
 * transform is on the bars themselves so there is no second icon to swap in and no flash.
 */
.burger { display: inline-block; inline-size: 1.05rem; block-size: 0.8rem; position: relative; }
.burger span {
  position: absolute; inset-inline: 0; block-size: 2px; border-radius: 2px;
  background: currentColor;
  transition: transform .25s var(--ease), opacity .2s var(--ease);
}
.burger span:nth-child(1) { inset-block-start: 0; }
.burger span:nth-child(2) { inset-block-start: 50%; margin-block-start: -1px; }
.burger span:nth-child(3) { inset-block-end: 0; }

.nav-toggle[aria-expanded="true"] .burger span:nth-child(1) { transform: translateY(0.36rem) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .burger span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .burger span:nth-child(3) { transform: translateY(-0.36rem) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .burger span { transition: none; }
}

/*
 * ── The SCRS ownership line ───────────────────────────────────────────────────────────────
 * ⭐ It appears beside every mention of the technology, so it has to read as an ATTRIBUTION —
 * quiet, factual, clearly not a sales sentence — rather than as another paragraph of copy.
 * ⛔ --muted, not --faint: it is a legal statement about a partner's intellectual property and
 *    must be legible (6.76:1), not decorative. See the token note at the top of this file.
 */
.scrs-note {
  margin: 0.9rem 0 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--muted);
  padding-inline-start: 0.85rem;
  border-inline-start: 2px solid var(--line);
  max-inline-size: 46ch;
}
.scrs-note strong { color: var(--heading); font-weight: 600; }
.scrs-note-deep { color: var(--on-deep-muted); border-inline-start-color: rgba(255,255,255,.22); }
.scrs-note-deep strong { color: #fff; }
.inner-narrow .scrs-note { margin-inline: auto; text-align: start; max-inline-size: 40ch; }

/*
 * ══════════════════════════════════════════════════════════════════════════════════════════
 * THE NUMBERED SERVICE LIST
 *
 * ⭐ Taken from wayoneit.com, where every service carries a small category label above its name
 * (01 MOBILE, 02 WEB, 03 DESIGN…). It suits us because this site already numbers its sections, so
 * the pattern lands as our own language — and a numbered list reads as a COMPLETE offer where a
 * loose grid of cards reads as a sample of one.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.svc { list-style: none; margin: 0; padding: 0; border-block-start: 1px solid var(--line); }
.svc-row {
  display: flex; gap: clamp(1rem, 3vw, 2.5rem); align-items: flex-start;
  padding-block: clamp(1.4rem, 3vw, 2rem);
  border-block-end: 1px solid var(--line);
  transition: background-color .25s var(--ease);
}
.svc-row:hover { background: var(--blue-50); }
/* ⛔ WAS --blue-300 ON WHITE — 1.74:1 against the 3.0 that large bold text needs, the worst
   contrast measured anywhere on the site. These numerals are not decoration: they are how the
   eight services are ordered and referred to. --blue-500 measures 3.65:1 and still reads light. */
.svc-n {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.4rem, 3vw, 2rem);
  line-height: 1; color: var(--blue-500); flex: 0 0 auto; min-inline-size: 2.5rem;
  font-variant-numeric: tabular-nums;
  transition: color .25s var(--ease);
}
.svc-row:hover .svc-n { color: var(--blue-700); }
.svc-body { flex: 1 1 auto; min-inline-size: 0; }
.svc-cat {
  display: inline-block; font-family: var(--font-display);
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent-ink); margin-block-end: 0.35rem;
}
.svc-body h3 { font-size: clamp(1.1rem, 2.2vw, 1.35rem); margin: 0 0 0.45rem; }
.svc-body p { color: var(--muted); margin: 0; line-height: 1.7; max-inline-size: 60ch; }
/*
 * ⚠️ The two marketing lines are marked so the split between build and demand is visible at a
 * glance, without needing a second heading to announce it.
 *
 * ⛔ BOTH OVERRIDES USED TO BE NO-OPS. .svc-cat already resolved to --blue-700 via --accent-ink,
 * and .svc-n now resolves to --blue-500 by default — so the rules that were supposed to mark these
 * rows out repainted them their existing colours and changed nothing on screen. A comment claiming
 * a distinction is worse than no distinction, because it stops anyone looking again.
 * ⭐ The label is now a filled pill, which is a difference you can actually see. 8.78:1.
 */
.svc-mkt .svc-cat {
  background: var(--blue-100); color: var(--blue-800);
  padding: 0.2rem 0.5rem; border-radius: 4px;
}

/*
 * ⛔ ORPHANED CLASS, CAUGHT BY THE TAP-TARGET CHECK. The portfolio cards were marked up with
 * .icard-go, whose styles lived in the Pop Hasta Labs layer that was deleted when the design was
 * rebuilt — so the "Open it" links rendered completely unstyled at 58x20px, under the 24px WCAG
 * 2.2 AA floor and looking like stray body text on a page whose whole job is getting clicked.
 * ⚠️ Renamed to .rec-go so it belongs to the record card it actually sits in, rather than keeping
 *    a name inherited from a component that no longer exists.
 */
.rec-go {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-block-size: 24px; margin-block-start: 0.9rem;
  font-family: var(--font-display); font-weight: 600; font-size: 0.94rem;
  color: var(--accent-ink); text-decoration: none;
}
.rec-go::after { content: "\2192"; transition: transform .25s var(--ease); }
.rec:hover .rec-go::after { transform: translateX(4px); }
.rec-go:hover { text-decoration: underline; }

/* Several .rec-go links stacked rather than running together on one line. */
.post-links { display: flex; flex-direction: column; align-items: flex-start; gap: 0; margin: 0; }
.post-links .rec-go { margin-block-start: 0.6rem; }

/*
 * ⭐ THE CONFIRMATION MARK on the thank you page. The one screen a lead sees straight after
 * converting had no visual acknowledgement at all — just a headline set like any other.
 */
.thanks-mark { margin: 0 0 1.4rem; color: var(--blue-600); }
.thanks-mark svg { inline-size: 3.5rem; block-size: 3.5rem; display: inline-block; }

/*
 * ══════════════════════════════════════════════════════════════════════════════════════════
 * TESTIMONIALS, INCLUDING THE RESERVED STATE
 *
 * ⛔⛔ THE PLACEHOLDER CARD MUST NOT BE MISTAKEABLE FOR A REAL ONE — not by a visitor, and not by
 * whoever ships the next change. So it is drawn differently on purpose: a dashed edge, a tinted
 * ground, a "Reserved" tag, and an attribution rendered as two blank rules rather than a name.
 * ⛔ Do not make this state prettier by removing those signals. Looking unfinished is the point;
 * it is the cheapest possible insurance against a fabricated quote reaching a real page.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.quote {
  position: relative;
  background: var(--card);
  border-radius: var(--radius-card);
  box-shadow: var(--edge);
  padding: 1.5rem 1.4rem 1.25rem;
  margin: 0;
  display: flex; flex-direction: column;
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.quote:hover { box-shadow: var(--edge-hover), var(--lift); transform: translateY(-2px); }
/* ⭐ A drawn quote mark rather than a character, so it cannot go missing with a font fallback. */
.quote::before {
  content: "";
  position: absolute; inset-block-start: 1.15rem; inset-inline-start: 1.4rem;
  inline-size: 1.4rem; block-size: 1.1rem;
  background: var(--blue-100);
  clip-path: polygon(0 0,42% 0,26% 100%,0 100%,0 0,58% 0,100% 0,84% 100%,58% 100%);
}
.quote blockquote { margin: 2.1rem 0 1.15rem; }
.quote blockquote p { margin: 0; color: var(--ink); font-size: 1.02rem; line-height: 1.7; }
.quote figcaption { margin-block-start: auto; display: flex; flex-direction: column; gap: 0.2rem; }
.quote figcaption strong { color: var(--heading); font-weight: 600; font-size: 0.98rem; }
.quote figcaption span { color: var(--muted); font-size: 0.9rem; }
.quote-sector {
  margin-block-start: 0.9rem;
  font-family: var(--font-display); font-size: 0.62rem; font-weight: 800;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent-ink);
}

/* ── the reserved state ──────────────────────────────────────────────────────────────────── */
.quote-empty {
  background: var(--blue-50);
  box-shadow: none;
  border: 1px dashed var(--blue-300);
}
.quote-empty:hover { transform: none; box-shadow: none; border-color: var(--blue-500); }
.quote-empty::before { background: var(--blue-300); opacity: .55; }
.quote-empty blockquote p { color: var(--muted); font-style: italic; }
.quote-tag {
  position: absolute; inset-block-start: 1rem; inset-inline-end: 1.1rem;
  font-family: var(--font-display); font-size: 0.6rem; font-weight: 800;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--blue-100);
  border-radius: 3px; padding: 0.2rem 0.4rem;
}
/* the attribution, drawn as empty rules — the shape of a name without asserting one */
.quote-line { display: block; block-size: 0.6rem; border-radius: 3px; background: var(--blue-100); }
.quote-line-name { inline-size: 42%; }
.quote-line-role { inline-size: 62%; margin-block-start: 0.35rem; opacity: .7; }

/* ⚠️ Screen readers get the truth in words, since the blank rules mean nothing to them. */
.visually-hidden {
  position: absolute; inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── Blog ────────────────────────────────────────────────────────────────────────────────── */
.post-link { color: inherit; text-decoration: none; }
.post-link:hover { color: var(--accent-ink); text-decoration: underline; }
.post-meta { color: var(--muted); font-size: 0.88rem; margin: 0 0 0.6rem; display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.crumb { margin: 0 0 1rem; }
.crumb a { color: var(--accent-ink); text-decoration: none; font-weight: 600; display: inline-flex; align-items: center; min-block-size: 24px; }
.crumb a:hover { text-decoration: underline; }
/* ⚠️ Article bodies are long, so they get the same measured column as the rest of the prose —
   see the ch-based cap at the top of the prose block for why that is not in rem. */
.prose p + p { margin-block-start: 0; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE JOURNEY — a dotted path winding between four numbered nodes
 *
 * ⚠️ TWO LAYOUTS, NOT ONE SCALED DOWN. The winding path exists only above 1000px. Below that it
 * becomes a vertical rail, because a curve squeezed onto a phone stacks the labels on top of one
 * another, and shrinking the whole diagram makes the text unreadable.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.journey { position: relative; }

/* ── phone and tablet: a straight rail ───────────────────────────────────────────────────── */
.journey-path { display: none; }
.journey-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.75rem; }
.jstep { position: relative; display: flex; gap: 1.1rem; align-items: flex-start; }
/* the rail, drawn behind the nodes and stopping at the last one */
.jstep::before {
  content: ""; position: absolute; inset-block-start: 3.5rem; inset-block-end: -1.75rem;
  inset-inline-start: 1.72rem; inline-size: 0;
  border-inline-start: 3px dotted var(--blue-300);
}
.jstep:last-child::before { display: none; }

.jnode {
  position: relative; flex: 0 0 auto;
  inline-size: 3.5rem; block-size: 3.5rem;
  display: grid; place-items: center;
}
/*
 * ⭐ THE RING IS A CONIC GRADIENT WITH GAPS, which is what gives the reference its segmented look
 * without four extra elements per node. The white disc sits on top and leaves the ring showing as
 * a band around it.
 */
.jring {
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--ring) 0 22%, transparent 22% 25%,
                             var(--ring) 25% 47%, transparent 47% 50%,
                             var(--ring) 50% 72%, transparent 72% 75%,
                             var(--ring) 75% 97%, transparent 97% 100%);
}
.jicon {
  position: relative; z-index: 1;
  inline-size: 2.7rem; block-size: 2.7rem; border-radius: 50%;
  background: var(--card); box-shadow: 0 0 0 1px var(--line-soft);
  display: grid; place-items: center; color: var(--blue-950);
}
.jicon svg { inline-size: 1.4rem; block-size: 1.4rem; }

/* ⛔ THE FOUR RINGS ARE THE BLUE LADDER, NOT FOUR UNRELATED HUES. The reference uses cyan, lime,
   yellow and pink; walking one hue keeps each node distinguishable without the page looking like
   somebody else's. */
.jstep-1 { --ring: var(--blue-500); }
.jstep-2 { --ring: var(--blue-600); }
.jstep-3 { --ring: var(--blue-700); }
.jstep-4 { --ring: var(--blue-950); }

.jbody { flex: 1 1 auto; min-inline-size: 0; padding-block-start: 0.15rem; }
.jstep-n {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  letter-spacing: -0.01em; color: var(--heading); margin: 0 0 0.15rem;
  display: inline-block; position: relative;
}
/* ⭐ the brush stroke under "Step 1", in that step's own ring colour */
.jstep-n::after {
  content: ""; position: absolute; inset-inline: -0.1em; inset-block-end: 0.05em;
  block-size: 0.34em; background: var(--ring); opacity: .32; border-radius: 2px; z-index: -1;
}
.jbody h3 { font-size: 1.06rem; margin: 0 0 0.35rem; }
.jbody p { color: var(--muted); margin: 0; font-size: 0.97rem; line-height: 1.65; max-inline-size: 34ch; }

.band-deep .jicon { background: #11203A; box-shadow: 0 0 0 1px var(--stat-line, #1F3A5E); color: #fff; }
.band-deep .jstep-n { color: #fff; }
.band-deep .jbody h3 { color: #fff; }
.band-deep .jbody p { color: #B8CBEA; }
.band-deep .jstep::before { border-inline-start-color: rgba(255,255,255,.28); }
.band-deep .journey-path { color: rgba(255,255,255,.42); }

/*
 * ── desktop: the winding path ───────────────────────────────────────────────────────────────
 *
 * ⛔⛔ LABELS GO ABOVE OR BELOW THEIR NODE, NEVER BESIDE IT. Beside was the first attempt and it
 * collided at every width — measured: 3 label overlaps at 1000px, still 1 at 1920px, plus up to
 * 47px of text spilling out of the bottom of the diagram. The arithmetic is unforgiving: the nodes
 * sit 25% of the width apart, so at 1320px there are about 330px between them, and a 17rem label
 * placed beside a node eats 326px of that. There was never room.
 *
 * ⭐ Centring each label on its node and stacking it vertically uses the space the diagram had
 * going spare — the middle of the box was empty — instead of the space it never had.
 *
 * ⛔ AND THE BREAKPOINT IS 1200px, NOT 1000px. Below that even centred labels touch. A cramped
 * winding path is worse than the clean vertical rail, so under 1200px the rail is what you get.
 */
@media (min-width: 1200px) {
  /* ⚠️ The box holds the SVG's aspect ratio so the percentage-placed nodes stay on the line. */
  .journey { aspect-ratio: 1200 / 700; }
  .journey-path {
    display: block; position: absolute; inset: 0;
    inline-size: 100%; block-size: 100%;
    color: var(--blue-700); opacity: .7;
    overflow: visible;
  }
  .journey-arrow { stroke: var(--blue-700); opacity: .9; }

  .journey-steps { display: block; }
  /*
   * ⛔ EVERY STEP IS A ZERO-SIZED ANCHOR POINT, and the node and label are positioned around it.
   * Giving the <li> a real width and shifting only its contents left pushed 44px of horizontal
   * scroll onto a 1440px screen: moving what you can see does not shrink the box that scrolls.
   */
  .jstep { position: absolute; display: block; inline-size: 0; block-size: 0; }
  .jstep::before { display: none; }

  .jnode { inline-size: 5.5rem; block-size: 5.5rem; position: absolute; }
  .jicon { inline-size: 4.3rem; block-size: 4.3rem; }
  .jicon svg { inline-size: 2rem; block-size: 2rem; }
  .jstep .jnode { inset-inline-start: -2.75rem; inset-block-start: -2.75rem; }

  /* ⚠️ NODE CENTRES MATCH THE SVG PATH POINTS: (170,430) (470,240) (760,414) (1050,235) in a
     1200x700 box. ⛔ Move a node and the line no longer meets it — change both together. */
  .jstep-1 { inset-inline-start: 14.2%; inset-block-start: 61.4%; }
  .jstep-2 { inset-inline-start: 39.2%; inset-block-start: 34.3%; }
  .jstep-3 { inset-inline-start: 63.3%; inset-block-start: 59.1%; }
  .jstep-4 { inset-inline-start: 87.5%; inset-block-start: 33.6%; }

  .jstep .jbody {
    position: absolute; inline-size: 15rem;
    inset-inline-start: 50%; transform: translateX(-50%);
    text-align: center;
  }
  .jbody p { max-inline-size: none; margin-inline: auto; }

  /*
   * ⛔⛔ LOW NODES CARRY THEIR LABEL BELOW, HIGH NODES ABOVE — and this is the opposite of the
   * obvious choice. Putting a label above a low node drops it exactly where the dotted path is
   * CLIMBING towards the next node, so the line ran straight through the text of steps 1 and 3.
   * The clear space is below a trough and above a peak; that is where the labels go.
   */
  .jstep-1 .jbody, .jstep-3 .jbody { inset-block-start: 3.4rem; }
  .jstep-2 .jbody, .jstep-4 .jbody { inset-block-end: 3.4rem; }

  .jstep-n::after { inset-inline: -0.25em; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE BIG-NUMBER BAND
 * ⚠️ Tabular numerals, or the row twitches sideways on every frame while the value climbs.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.figures {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}
.figures li { text-align: center; padding-inline: 1rem; position: relative; }
/* the hairline dividers between figures, as in the reference */
.figures li + li::before {
  content: ""; position: absolute; inset-inline-start: 0; inset-block: 0.35rem;
  inline-size: 1px; background: var(--line);
}
@media (max-width: 700px) { .figures li + li::before { display: none; } }
.fig-n {
  display: block; font-family: var(--font-display);
  font-size: clamp(2.4rem, 5vw, 3.6rem); font-weight: 700; letter-spacing: -0.03em;
  line-height: 1; color: var(--blue-700);
  font-variant-numeric: tabular-nums;
}
.fig-k { display: block; margin-block-start: 0.7rem; color: var(--heading); font-weight: 600; font-size: 1rem; line-height: 1.5; }
/* ⛔ THE SOURCE LINE IS NOT DECORATION — it is what makes each figure checkable. Do not delete it
   to tidy the band up. */
.fig-src { display: block; margin-block-start: 0.3rem; color: var(--muted); font-size: 0.84rem; }
.band-deep .fig-n { color: #7FB0FF; }
.band-deep .fig-k { color: #fff; }
.band-deep .fig-src { color: var(--on-deep-muted); }
.band-deep .figures li + li::before { background: rgba(255,255,255,.18); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE LOGO WALL
 * ⭐ A checkerboard of tinted tiles, as in the reference — alternate tiles carry a wash so the
 * grid reads as a wall rather than a list.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.wall {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
}
.wall-tile {
  background: var(--card); min-block-size: 6.5rem;
  display: grid; place-items: center; text-align: center; padding: 1rem 0.85rem;
  transition: background-color .25s var(--ease);
}
.wall-tile:nth-child(even) { background: var(--blue-50); }
.wall-tile a { text-decoration: none; color: inherit; display: block; }
.wall-tile:hover { background: var(--blue-100); }
.wall-name {
  display: block; font-family: var(--font-display); font-weight: 700;
  font-size: 1rem; color: var(--heading); letter-spacing: -0.01em;
}
.wall-sector {
  display: block; margin-block-start: 0.3rem;
  font-family: var(--font-display); font-size: 0.6rem; font-weight: 800;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent-ink);
}
.wall-tile img { max-inline-size: 100%; max-block-size: 3rem; object-fit: contain; }

/* ⛔ THE RESERVED TILE MUST NOT PASS FOR A REAL LOGO. A dashed edge and an empty mark, the same
   signal the reserved testimonial cards use. Do not tidy those signals away. */
.wall-reserved { background: transparent; border-style: dashed; border-color: var(--blue-300); gap: 0; }
.wall-empty, .wall-reserved .wall-tile { background: transparent; box-shadow: inset 0 0 0 1px var(--line-soft); }
.wall-reserved .wall-tile:nth-child(even) { background: rgba(228,238,252,.4); }
.wall-empty:hover { background: var(--blue-50); }
.wall-mark {
  inline-size: 3.4rem; block-size: 1.5rem; border-radius: 4px;
  background: repeating-linear-gradient(135deg, var(--blue-100) 0 6px, transparent 6px 12px);
  box-shadow: inset 0 0 0 1px var(--blue-300);
}

/* ── Portfolio filter ────────────────────────────────────────────────────────────────────── */
.fbar { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-block-end: 1rem; }
.fbtn {
  font-family: var(--font-display); font-size: 0.88rem; font-weight: 600;
  color: var(--muted); background: var(--card);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.5rem 1rem; min-block-size: 40px; cursor: pointer;
  transition: color .2s var(--ease), border-color .2s var(--ease), background-color .2s var(--ease);
}
.fbtn:hover { color: var(--accent-ink); border-color: var(--blue-300); }
.fbtn:focus-visible { outline: none; border-color: var(--blue-600); box-shadow: 0 0 0 4px var(--blue-100); }
/* ⚠️ The selected state is aria-pressed, not a class — the attribute is the accessible truth and
   styling from it means the two can never disagree. */
.fbtn[aria-pressed="true"] { background: var(--blue-600); border-color: var(--blue-600); color: #fff; }
.fcount { color: var(--muted); font-size: 0.88rem; margin: 0 0 1.25rem; }

/*
 * ⛔⛔ THE hidden ATTRIBUTE MUST ACTUALLY HIDE. The browser's own stylesheet says
 * [hidden] { display: none }, but that is an ordinary author-weight-beatable rule: ANY rule that
 * sets `display` on the same element wins. `.rec { display: flex }` did exactly that, so the
 * portfolio filter set hidden=true on five cards and all five stayed on screen at full height —
 * the buttons highlighted, the count said "1 of 6", and nothing moved.
 *
 * ⚠️ This is the rare case where !important is correct rather than lazy: `hidden` is a statement
 * that the element is not relevant, and no layout rule should be able to overrule it.
 * ⛔ Do not remove this to satisfy a linter that dislikes !important.
 */
[hidden] { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE CASE-STUDY SHOWCASE — copy one side, a device on the other
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.showcase { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 980px) {
  .showcase { grid-template-columns: 1fr 1.05fr; }
  /* ⚠️ ORDER, NOT direction:rtl — flipping with rtl would also mirror the text inside. */
  .showcase-flip .showcase-copy { order: 2; }
  .showcase-flip .showcase-device { order: 1; }
}
.showcase-copy h2 { font-size: clamp(1.7rem, 3.2vw, 2.4rem); margin: 0 0 0.9rem; }
.showcase-list { margin-block-start: 1.5rem; }
@media (min-width: 760px) { .showcase-list { grid-template-columns: 1fr; } }
.showcase-list li { padding-inline-start: 1.5rem; }

/* ── the device ──────────────────────────────────────────────────────────────────────────── */
.device {
  background: var(--card); border-radius: 12px;
  box-shadow: 0 0 0 1px var(--line), 0 22px 50px rgba(11,42,107,.14);
  overflow: hidden;
}
.device-bar {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.6rem 0.85rem; background: var(--blue-50);
  border-block-end: 1px solid var(--line-soft);
}
.device-bar span[class^="dot-"] { inline-size: 0.55rem; block-size: 0.55rem; border-radius: 50%; }
.dot-r { background: #E9857A; } .dot-y { background: #EBC66B; } .dot-g { background: #86C79A; }
.device-url {
  margin-inline-start: 0.6rem; flex: 1 1 auto;
  background: var(--card); border-radius: 5px; padding: 0.2rem 0.6rem;
  font-size: 0.72rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.device-screen { padding: 1.1rem 1.15rem 1.3rem; }
.device-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-block-end: 0.9rem; }
.device-brand { font-family: var(--font-display); font-weight: 700; font-size: 1.02rem; color: var(--heading); }
.device-user { font-size: 0.74rem; color: var(--muted); }

.device-tabs { display: flex; gap: 0.3rem; margin-block-end: 1rem; border-block-end: 1px solid var(--line-soft); }
.dtab {
  font-family: var(--font-display); font-size: 0.8rem; font-weight: 600;
  color: var(--muted); background: none; border: 0;
  border-block-end: 2px solid transparent; margin-block-end: -1px;
  padding: 0.45rem 0.7rem; min-block-size: 32px; cursor: pointer;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.dtab:hover { color: var(--accent-ink); }
.dtab[aria-selected="true"] { color: var(--accent-ink); border-block-end-color: var(--blue-600); }
.dtab:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--blue-100); border-radius: 4px; }

/* ⛔ PANELS ARE VISIBLE UNTIL .is-tabbed IS ADDED BY THE SCRIPT. Without JavaScript all three
   stack — taller, but nothing is lost. Never put the hiding state in the template. */
.device-panel { animation: dfade .3s var(--ease) both; }
.is-tabbed .device-panel + .device-panel { margin-block-start: 0; }
@keyframes dfade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .device-panel { animation: none; } }

.device-h {
  font-family: var(--font-display); font-size: 0.66rem; font-weight: 800;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin: 0 0 0.7rem;
}
.dstats { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0.55rem; }
.dstats li { background: var(--blue-50); border-radius: 8px; padding: 0.7rem 0.8rem; }
.dstat-n { display: block; font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; line-height: 1; color: var(--blue-700); font-variant-numeric: tabular-nums; }
.dstat-k { display: block; margin-block-start: 0.3rem; font-size: 0.74rem; color: var(--muted); line-height: 1.35; }
.dstat-urgent { background: #FDF1EF; }
.dstat-urgent .dstat-n { color: #C0362C; }

.dfields, .dmoney { list-style: none; margin: 0; padding: 0; }
.dfields li { display: flex; justify-content: space-between; gap: 1rem; padding-block: 0.5rem; border-block-end: 1px solid var(--line-soft); font-size: 0.85rem; }
.dfields li:last-child { border-block-end: 0; }
.dfields span:first-child { color: var(--muted); }
.dfields span:last-child { color: var(--heading); font-weight: 600; text-align: end; }

.dmoney li { display: flex; align-items: center; gap: 0.7rem; padding-block: 0.55rem; border-block-end: 1px solid var(--line-soft); font-size: 0.85rem; }
.dmoney li:last-child { border-block-end: 0; }
.dm-ref { color: var(--muted); font-variant-numeric: tabular-nums; }
.dm-amt { margin-inline-start: auto; color: var(--heading); font-weight: 600; font-variant-numeric: tabular-nums; }
.dm-state { font-family: var(--font-display); font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; border-radius: 3px; padding: 0.18rem 0.4rem; white-space: nowrap; }
.dm-due { background: var(--blue-100); color: var(--accent-ink); }
.dm-paid { background: #E7F3EA; color: #2C6B41; }
.dm-late { background: #FBE7E4; color: #A62B22; }

.device-note { margin: 0.9rem 0 0; font-size: 0.8rem; color: var(--muted); }

/* ⚠️ The platform hero is two columns now — copy left, a client record right — because the right
   half was empty on any wide screen and read as a page whose images had failed to load. */
.hero-split { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 1000px) { .hero-split { grid-template-columns: 1.05fr 0.95fr; } }

/* the trades wall packs tighter than a logo wall — they are words, not marks */
.wall-trades { grid-template-columns: repeat(auto-fit, minmax(min(9.5rem, 100%), 1fr)); }
.wall-trades .wall-tile { min-block-size: 4.5rem; }
.wall-trades .wall-name { font-size: 0.92rem; }

/* ⭐ The device panels animate in; showcase.js sets the per-row delay so they deal in sequence. */
.dstats li, .dfields li, .dmoney li { animation: dRow .34s var(--ease) both; }
@keyframes dRow { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .dstats li, .dfields li, .dmoney li { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * BRAND MARKS FOR THE DEMONSTRATION SYSTEMS
 * ⭐ Each mark takes its own tint from the blue ladder so the wall reads as a set of different
 * businesses rather than six copies of one logo — variety inside the theme, as everywhere else.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.mark-lockup { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
.mark-glyph { inline-size: 2.4rem; block-size: 2.4rem; display: grid; place-items: center; }
.mark-glyph svg { inline-size: 100%; block-size: 100%; }
.mark-word {
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  letter-spacing: -0.015em; color: var(--heading); line-height: 1.2; text-align: center;
}
.mark-hoxmere    { color: var(--blue-950); }
.mark-lindenmere { color: var(--blue-700); }
.mark-brantwell  { color: var(--blue-600); }
.mark-pelbrook   { color: #2E7D6B; }
.mark-marlow     { color: #4A4E8C; }
.mark-kestrel    { color: var(--blue-500); }
.wall-logo { min-block-size: 8.5rem; }
.wall-logo .wall-sector { margin-block-start: 0.45rem; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * DRAWN ILLUSTRATIONS
 * ⛔ SVG rather than image files: they inherit the palette, stay sharp at any size, cost no extra
 * request, and cannot go stale the way a product screenshot does.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.art { inline-size: 100%; }
.art svg { inline-size: 100%; block-size: auto; display: block; }
.art-split { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; margin-block-end: clamp(2rem, 4vw, 3rem); }
@media (min-width: 980px) { .art-split { grid-template-columns: 1fr 1fr; } }

.art-records .art-src   { stroke: var(--blue-300); stroke-width: 2.4; }
.art-records .art-flow  { stroke: var(--blue-500); stroke-width: 2.2; stroke-dasharray: 1 9; stroke-linecap: round; }
.art-records .art-rec   { stroke: var(--blue-700); stroke-width: 2.6; }
.art-records .art-rec circle { fill: var(--blue-600); stroke: none; }
.art-records .art-tick circle { fill: var(--blue-600); stroke: none; }
.art-records .art-tick path   { stroke: #fff; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.art-records g { stroke-linecap: round; stroke-linejoin: round; }
.band-deep .art-records .art-src { stroke: rgba(255,255,255,.32); }
.band-deep .art-records .art-rec { stroke: #A9C8FF; }

/* ⭐ The converging lines draw themselves in when the block arrives. */
.js-reveal .reveal .art-flow path { stroke-dashoffset: 240; transition: stroke-dashoffset 1.1s var(--ease); }
.js-reveal .reveal.in .art-flow path { stroke-dashoffset: 0; }
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal .art-flow path,
  .js-reveal .reveal.in .art-flow path { stroke-dashoffset: 0; transition: none; }
}

/*
 * ⛔ THE BLOG HERO WAS THE LAST ONE ON THE SITE STILL FACING AN EMPTY COLUMN — every other page
 * got a drawing beside its headline and this one was missed.
 */
.art-notes g { stroke-linecap: round; stroke-linejoin: round; }
.art-notes .an-back  { stroke: var(--line); stroke-width: 2.2; }
.art-notes .an-page  { stroke: var(--blue-300); stroke-width: 2.6; fill: var(--paper); }
.art-notes .an-head  { stroke: var(--blue-600); stroke-width: 5; }
.art-notes .an-body  { stroke: var(--line); stroke-width: 3; }
.art-notes .an-live  { stroke: var(--blue-500); stroke-width: 3; }
.art-notes .an-pen   { stroke: var(--blue-700); stroke-width: 2.4; }
.art-notes .an-rec   { stroke: var(--blue-700); stroke-width: 2.6; }
.art-notes .an-rec path { stroke: var(--line); stroke-width: 3; }
.art-notes .an-rec circle { fill: var(--blue-600); stroke: none; }
.art-notes .an-link  { stroke: var(--blue-500); stroke-width: 2.2; stroke-dasharray: 1 8; }

/* ⭐ The line still being written draws itself in, once, when the hero arrives. */
.js-reveal .reveal .an-live path { stroke-dashoffset: 70; stroke-dasharray: 70; transition: stroke-dashoffset .9s var(--ease) .25s; }
.js-reveal .reveal.in .an-live path { stroke-dashoffset: 0; }
@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal .an-live path,
  .js-reveal .reveal.in .an-live path { stroke-dashoffset: 0; stroke-dasharray: none; transition: none; }
}

/* ── Hero illustrations ──────────────────────────────────────────────────────────────────── */
.art-flow-cycle g { stroke-linecap: round; stroke-linejoin: round; }
.art-flow-cycle .afc-card   { stroke: var(--blue-300); stroke-width: 2.4; }
.art-flow-cycle .afc-out    { stroke: var(--line); }
.art-flow-cycle .afc-real   { fill: var(--blue-600); }
.art-flow-cycle .afc-tok    { fill: var(--blue-100); stroke: var(--blue-300); stroke-width: 1.6; }
.art-flow-cycle .afc-wall   { stroke: var(--blue-500); stroke-width: 2.2; stroke-dasharray: 6 8; }
.art-flow-cycle .afc-shield { stroke: var(--blue-600); stroke-width: 2.6; fill: var(--paper); }
.art-flow-cycle .afc-model  { stroke: var(--blue-700); stroke-width: 2.4; }
.art-flow-cycle .afc-arrow  { stroke: var(--blue-500); stroke-width: 2.2; }
.art-flow-cycle .afc-dots   { stroke: var(--blue-300); stroke-width: 2.2; stroke-dasharray: 1 8; }

.art-trio { position: relative; }
.art-trio g { stroke-linecap: round; stroke-linejoin: round; }
.art-trio .atr-node  { stroke: var(--blue-300); stroke-width: 2.4; }
.art-trio .atr-node rect { fill: var(--paper); }
.art-trio .atr-hub   { stroke: var(--blue-600); stroke-width: 2.8; }
.art-trio .atr-hub rect { fill: var(--blue-50); }
.art-trio .atr-hub circle { fill: var(--blue-600); stroke: none; }
.art-trio .atr-client circle { fill: var(--blue-300); stroke: none; }
.art-trio .atr-link  { stroke: var(--blue-500); stroke-width: 2.2; stroke-dasharray: 1 8; }
/* ⚠️ Labels are HTML over the drawing, so they translate and are read aloud. Percentages, so they
   stay put as the drawing scales. */
.art-trio .atr-label {
  position: absolute; transform: translate(-50%, -50%);
  font-family: var(--font-display); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  white-space: nowrap; pointer-events: none;
}
.art-trio .atr-l-client { inset-inline-start: 50%; inset-block-start: 14%; }
.art-trio .atr-l-hub    { inset-inline-start: 50%; inset-block-start: 46.5%; color: var(--accent-ink); }
.art-trio .atr-l-left   { inset-inline-start: 21.7%; inset-block-start: 83%; }
.art-trio .atr-l-right  { inset-inline-start: 78.3%; inset-block-start: 83%; }
@media (max-width: 520px) { .art-trio .atr-label { font-size: 0.58rem; } }

/* the six marks, as a hero panel */
.hero-marks {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--radius-card); overflow: hidden;
}
.hero-mark {
  background: var(--card); padding: 1.15rem 0.6rem; text-decoration: none;
  display: grid; place-items: center; transition: background-color .25s var(--ease);
}
.hero-mark:nth-child(even) { background: var(--blue-50); }
.hero-mark:hover { background: var(--blue-100); }
.hero-marks .mark-word { font-size: 0.8rem; }
.hero-marks .mark-glyph { inline-size: 2rem; block-size: 2rem; }

/* ── Blog covers ─────────────────────────────────────────────────────────────────────────── */
/* ⚠️ A FIXED ASPECT BOX. Editors upload whatever shape they have, and without this one portrait
   photograph makes its card twice the height of the two beside it. */
.post-cover { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--blue-50); }
.post-cover img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.post-hero {
  inline-size: 100%; max-block-size: 26rem; object-fit: cover;
  border-radius: var(--radius-card); display: block;
}
.cover-preview { max-inline-size: 22rem; border-radius: 8px; display: block; }

/* ── Workspace and marketing illustrations ───────────────────────────────────────────────── */
.art-wall g, .art-media g { stroke-linecap: round; stroke-linejoin: round; }
.art-wall .aw-wall   { stroke: var(--blue-500); stroke-width: 2.6; stroke-dasharray: 8 9; }
.art-wall .aw-tile   { stroke: var(--blue-300); stroke-width: 2.2; }
.art-wall .aw-ai     { stroke: var(--blue-600); stroke-width: 2.4; }
.art-wall .aw-shield { stroke: var(--blue-600); stroke-width: 2.6; }
.art-wall .aw-shield path:first-child { fill: var(--paper); }
/* on the dark band the drawing has to lift off the navy, not sink into it */
.band-deep .art-wall .aw-wall   { stroke: rgba(127,176,255,.75); }
.band-deep .art-wall .aw-tile   { stroke: rgba(255,255,255,.34); }
.band-deep .art-wall .aw-ai     { stroke: #7FB0FF; }
.band-deep .art-wall .aw-shield { stroke: #7FB0FF; }
.band-deep .art-wall .aw-shield path:first-child { fill: #11203A; }

.art-media .am-cam  { stroke: var(--blue-700); stroke-width: 2.6; }
.art-media .am-post { stroke: var(--blue-300); stroke-width: 2.2; }
.art-media .am-axis { stroke: var(--line); stroke-width: 2.2; }
.art-media .am-bar rect { fill: var(--blue-100); }
.art-media .am-line { stroke: var(--blue-600); stroke-width: 3; fill: none; }
.art-media .am-dot circle { fill: var(--blue-600); }

/* ── FAQ: heading beside the accordion ───────────────────────────────────────────────────── */
.faq-split { display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: start; }
@media (min-width: 980px) {
  .faq-split { grid-template-columns: 0.8fr 1.2fr; }
  /* ⚠️ Sticky only where there is height to travel; on a short list it would judder. */
  .faq-split > .section-head { position: sticky; inset-block-start: 6rem; margin-block-end: 0; }
}
.faq-split .faq { max-inline-size: none; }
.faq-split .faq details p { max-inline-size: 56ch; }

/*
 * ==========================================================================================
 * THE TECHNOLOGY LIST
 *
 * A dense, scannable grid. The reader is looking for ONE word - their accounts package, their
 * cloud, the language their last developer used - so the job of this component is to be skimmed,
 * not read. Hence tags rather than sentences, and a category heading big enough to navigate by.
 *
 * minmax(min(20rem, 100%), 1fr) - the min() is not optional. A bare minmax(20rem, 1fr) cannot go
 * below 20rem and overflows every phone; that exact bug shipped on this project once already.
 * ==========================================================================================
 */
.stack-grid {
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}

.stack-cat {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
  padding: clamp(1.25rem, 2.4vw, 1.6rem); box-shadow: var(--edge);
  display: flex; flex-direction: column;
}
/* (.stack-cat is var(--card) everywhere now — no per-band override needed) */

.stack-head { display: flex; align-items: center; gap: 0.75rem; margin-block-end: 0.6rem; }
.stack-head h3 { font-size: 1.05rem; margin: 0; line-height: 1.25; }

.stack-icon { flex: 0 0 auto; color: var(--blue-600); display: block; }
.stack-icon svg {
  inline-size: 1.9rem; block-size: 1.9rem; display: block;
  stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

.stack-blurb {
  color: var(--muted); font-size: 0.94rem; line-height: 1.65;
  margin: 0 0 1rem; max-inline-size: 46ch;
}

/* The tags. A list, because that is what it is - the bullets are simply turned off. */
/*
 * margin-top was auto, to line the tag blocks up along the bottom of every card in a row. Seen on
 * the real page at 2560px that is worse, not better: a card with a two-line blurb and few tags
 * gets the whole surplus as a HOLE IN ITS MIDDLE - 85px of nothing between the sentence and the
 * tags on "Security and access" - which reads as a rendering fault rather than as alignment.
 * Letting the tags follow the blurb puts the surplus at the BOTTOM of the card instead, where it
 * is indistinguishable from padding.
 */
.tech-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.4rem;
}
.tech {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: var(--blue-50); border: 1px solid var(--line-soft); border-radius: 999px;
  padding: 0.3rem 0.7rem; font-size: 0.84rem; font-weight: 600; color: var(--heading);
  line-height: 1.4;
}
/*
 * The "running here" mark. It is a BORDER AND A DOT, not colour alone - a colour-only difference
 * is invisible to a red-green colourblind reader and disappears entirely when the page is printed.
 */
.tech-live { border-color: var(--blue-300); background: var(--blue-100); color: var(--blue-800); }
.tech-dot {
  inline-size: 0.44rem; block-size: 0.44rem; border-radius: 50%;
  background: var(--blue-600); flex: 0 0 auto;
}


/*
 * Two columns so the closing note does not leave half the row empty - the same dead column that
 * has been fixed on every other section of this site.
 */
.stack-foot { margin-block-start: clamp(1.75rem, 3vw, 2.5rem); display: grid; gap: clamp(1rem, 3vw, 2.5rem); }
@media (min-width: 900px) { .stack-foot { grid-template-columns: 1.35fr 1fr; align-items: start; } }
.stack-foot p { color: var(--muted); line-height: 1.75; margin: 0; max-inline-size: 60ch; }
.stack-foot strong { color: var(--heading); }
.stack-foot .scrs-note { margin: 0; }

/* On a dark band the cards have to lift off the ground rather than disappear into it. */
.band-deep .stack-cat { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.14); box-shadow: none; }
.band-deep .stack-head h3 { color: #fff; }
.band-deep .stack-icon { color: var(--blue-300); }
.band-deep .stack-blurb, .band-deep .stack-foot p { color: var(--on-deep-muted); }
.band-deep .stack-foot strong { color: #fff; }
.band-deep .tech { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.16); color: #EAF1FE; }
.band-deep .tech-live { background: rgba(75,130,240,.28); border-color: #6F9BF5; color: #fff; }
.band-deep .tech-dot { background: #A9C8FF; }

/*
 * ------------------------------------------------------------------------------------------
 * THE TECHNOLOGY TABS
 *
 * Nine controls will not fit on a phone, so the bar scrolls SIDEWAYS INSIDE ITSELF. The page must
 * never scroll horizontally - that rule has been broken on this project twice already - so the
 * overflow lives here, on a container with its own min-width:0.
 * ------------------------------------------------------------------------------------------
 */
.stabs {
  display: flex; gap: 0.25rem; flex-wrap: nowrap;
  overflow-x: auto; overflow-y: hidden; min-inline-size: 0;
  margin-block-end: 0.85rem; padding-block-end: 0.35rem;
  border-block-end: 1px solid var(--line);
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
/*
 * A bar that scrolls with no sign of it is a bar whose controls are simply missing, so the edge
 * fades to say there is more.
 * The class is set by stacktabs.js from a real measurement rather than by a media query: at 1024px
 * the bar needs 1104px and DOES scroll, at 1440px it fits, and no guessed breakpoint gets both
 * right - nor stays right when a category is added or a label reworded.
 */
.stabs.is-scrollable {
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2.5rem), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2.5rem), transparent 100%);
}
.stab {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 0.45rem;
  min-block-size: 44px; padding: 0.5rem 0.9rem;
  font: inherit; font-size: 0.92rem; font-weight: 600; white-space: nowrap;
  background: none; border: 0; border-radius: 8px 8px 0 0; cursor: pointer;
  color: var(--muted); position: relative;
  transition: color .2s var(--ease), background-color .2s var(--ease);
}
.stab:hover { color: var(--accent-ink); background: var(--blue-50); }
.stab:focus-visible { outline: 3px solid var(--blue-300); outline-offset: -3px; }

/*
 * The selected tab is marked THREE ways - colour, weight and a rule underneath - because colour
 * alone is not a state a colourblind reader or a printed page can see.
 */
.stab[aria-pressed="true"] { color: var(--accent-ink); }
.stab[aria-pressed="true"]::after {
  content: ""; position: absolute; inset-inline: 0.35rem; inset-block-end: -0.35rem;
  block-size: 3px; background: var(--blue-600); border-radius: 3px 3px 0 0;
}
.stab-n {
  font-size: 0.72rem; font-weight: 700; line-height: 1;
  padding: 0.2rem 0.38rem; border-radius: 999px;
  background: var(--blue-100); color: var(--blue-800);
}
.stab[aria-pressed="true"] .stab-n { background: var(--blue-600); color: #fff; }

.stab-count { color: var(--muted); font-size: 0.9rem; margin: 0 0 1.25rem; min-block-size: 1.2em; }

/*
 * A SELECTED CATEGORY SPANS THE WHOLE ROW. One card left in a three-column grid sits beside two
 * empty columns, which is the dead column this site has already had to fix on four other sections.
 * Full width also lets the tags lay out across the row, which is what makes a chosen tab read as
 * a panel rather than as a grid with most of it missing.
 */
.stack-grid.is-single .stack-cat { grid-column: 1 / -1; }
.stack-grid.is-single .stack-blurb { max-inline-size: 60ch; }

.band-deep .stabs { border-block-end-color: rgba(255,255,255,.16); }
.band-deep .stab { color: var(--on-deep-muted); }
.band-deep .stab:hover { color: #fff; background: rgba(255,255,255,.07); }
.band-deep .stab[aria-pressed="true"] { color: #fff; }
.band-deep .stab[aria-pressed="true"]::after { background: var(--blue-500); }
.band-deep .stab-n { background: rgba(255,255,255,.14); color: #EAF1FE; }
.band-deep .stab[aria-pressed="true"] .stab-n {
  /* ⚠️ --blue-600, not --blue-500: white on 500 measures 3.65:1 and this text is 12px. */
  background: var(--blue-600); color: #fff;
}
.band-deep .stab-count { color: var(--on-deep-muted); }

/* ── the pre-loader ───────────────────────────────────────────────────────────────────────
 *
 * ⭐⭐ THE MARK ARRIVES, THE LIGHT AT ITS APEX GROWS, AND THE SITE IS REVEALED BEHIND IT — p2.
 * The traced logo is two paths with no addressable sun, so the light is its own element placed
 * over the apex rather than an animated part of the artwork.
 *
 * ⛔⛔ TWO RULES MAKE THIS SAFE TO SHIP, AND BOTH MATTER:
 *   1. display:none is the DEFAULT. Only `html.js-preload` shows it, and only the head script
 *      adds that. No script, no overlay — the site is simply there.
 *   2. THE EXIT IS A CSS ANIMATION WITH fill-mode forwards, so it completes on its own. If every
 *      other line of JavaScript on the page fails, the overlay still leaves. An entrance
 *      animation that can strand somebody behind a full-screen panel is not worth having.
 */
.preload { display: none; }

.js-preload .preload {
  display: grid; place-items: center;
  position: fixed; inset: 0; z-index: 100;
  background: var(--paper);
  animation: preOut 1.9s var(--ease-out) forwards;
}
.preload-stage { position: relative; display: grid; place-items: center; }
.preload-mark {
  display: block; inline-size: clamp(72px, 12vw, 116px); block-size: auto;
  animation: preMark 1.9s var(--ease-out) forwards;
}
/*
 * The light. It sits over the apex of the mark — the point the rays fan out from — and grows
 * from a pinpoint to something that washes the whole screen.
 */
.preload-glow {
  position: absolute; inset-block-start: 6%; inset-inline-start: 50%;
  inline-size: 26rem; block-size: 26rem; translate: -50% -50%;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle closest-side,
              rgba(75,130,240,.55), rgba(75,130,240,.22) 42%, rgba(75,130,240,0) 72%);
  opacity: 0; scale: .04;
  animation: preGlow 1.9s var(--ease-out) forwards;
}

@keyframes preMark {
  0%   { opacity: 0; scale: .92; }
  22%  { opacity: 1; scale: 1; }
  74%  { opacity: 1; scale: 1; }
  100% { opacity: 0; scale: 1.04; }
}
@keyframes preGlow {
  0%   { opacity: 0; scale: .04; }
  26%  { opacity: 0; scale: .1; }
  55%  { opacity: 1; scale: .55; }
  100% { opacity: 0; scale: 6; }
}
@keyframes preOut {
  0%   { opacity: 1; }
  76%  { opacity: 1; }
  100% { opacity: 0; visibility: hidden; pointer-events: none; }
}

/* ⭐ The script removes the class on load, which cuts a slow connection's wait short. The
 *    animation above is what guarantees the overlay leaves; this only makes it leave sooner. */
.is-loaded .preload { animation-duration: .55s; }

/* ── the demo gate ──────────────────────────────────────────────────── */
.demo-top .lede { max-inline-size: 40rem; }
.demo-gate { display: grid; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
@media (min-width: 62rem) { .demo-gate { grid-template-columns: 1fr 1.05fr; } }
.demo-gate-say .ticks { margin-block: 1rem; }
.demo-gate-say .note { margin-block-start: 1.2rem; }

/* ⭐ Paired fields, as the reference form draws them — one per row once there is no room.
   ⚠️ The inputs themselves are styled by `.enquiry`, which this form also carries. */
.field-pair { display: grid; gap: 1rem; }
@media (min-width: 34rem) { .field-pair { grid-template-columns: 1fr 1fr; } }

.demo-grid {
  list-style: none; margin: 1.4rem 0 0; padding: 0;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.demo-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 1.2rem; display: flex; flex-direction: column; gap: .35rem;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.demo-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--blue-200); }
.demo-sector {
  margin: 0; font-family: var(--font-display); font-size: .7rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.demo-card h3 { margin: 0 0 .6rem; font-size: 1.05rem; }
.demo-card .btn { margin-block-start: auto; align-self: flex-start; }

/* what we read out of their logo, shown back to them */
.brand-read {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  padding: .85rem 1.1rem; border-radius: 12px; margin-block-end: 1.4rem;
  background: var(--blue-50); border: 1px solid var(--line-soft);
}
.brand-read-chip {
  background: var(--read-1); color: var(--read-ink); border-radius: 999px;
  padding: .2rem .7rem; font-size: .74rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
}
.brand-read-swatches { display: inline-flex; gap: .3rem; }
.brand-read .sw { inline-size: 22px; block-size: 22px; border-radius: 6px; border: 1px solid rgba(0,0,0,.12); }
.brand-read-text { font-size: .86rem; color: var(--muted); flex: 1 1 16rem; }

/* ── site messages ────────────────────────────────────────────────────── */
.flashes { padding-block-start: 1rem; }
.flash {
  margin: 0 0 .6rem; padding: .8rem 1.1rem; border-radius: 11px;
  font-size: .92rem; line-height: 1.55;
  background: var(--blue-50); border: 1px solid var(--line-soft); color: var(--ink);
}
.flash-warning { background: #FEF3DC; border-color: #FBE7C6; color: #7A4405; }
.flash-error { background: #FEE4E2; border-color: #FDA29B; color: #7A271A; }
.flash-success { background: #E3F7F1; border-color: #BFE9DC; color: #0B5943; }

/* ── the ERP hero (p5, p6) ─────────────────────────────────────────────── */
/*
 * ⚠️ p6 asked for "one complete landscape view, with the CTA also visible". That is a TYPE and
 * SPACING problem, not a height one — so the headline is capped and the vertical rhythm tightened
 * rather than the band being pinned to 100vh, which would push the page out of its own first
 * frame on a short window.
 */
.hero-erp { padding-block: clamp(1.5rem, 4vw, 3rem) clamp(1.25rem, 3vw, 2.25rem); }
.hero-erp h1 { font-size: clamp(2rem, 4.2vw, 3.4rem); line-height: 1.06; margin-block-end: .9rem; }
.hero-erp .lede { max-inline-size: 34rem; margin-block-end: 1.1rem; }
.dot-accent { color: var(--blue-600); }

.hero-box {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  padding: 1rem 1.15rem; margin-block-end: 1.25rem; max-inline-size: 34rem;
  box-shadow: var(--shadow-sm);
}
.hero-box-h { margin: 0 0 .4rem; font-family: var(--font-display); font-weight: 700;
              font-size: 1.02rem; color: var(--heading); }
.hero-box p { margin: 0; font-size: .93rem; color: var(--muted); line-height: 1.6; }

/* ⭐ The four points span the full hero width beneath both columns, as p5 draws them. */
.hero-points {
  list-style: none; margin: clamp(1.25rem, 3vw, 2rem) 0 0; padding: clamp(1rem, 2vw, 1.35rem) 0 0;
  border-block-start: 1px solid var(--line-soft);
  display: grid; gap: 1rem 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
}
.hero-points li { display: grid; grid-template-columns: auto 1fr; gap: .1rem .7rem;
                  align-items: start; align-content: start; }
.hero-points .hp-icon {
  grid-row: 1 / 3; inline-size: 38px; block-size: 38px; border-radius: 11px;
  display: grid; place-items: center; background: var(--blue-100); color: var(--blue-700);
}
.hero-points .hp-icon svg { inline-size: 1.15rem; block-size: 1.15rem; }
.hero-points strong { font-size: .95rem; color: var(--heading); align-self: end; }
.hero-points span:last-child { font-size: .85rem; color: var(--muted); line-height: 1.45; }

/* ── brands we've worked with (p7) ─────────────────────────────────────── */
.brands { overflow: hidden; }
.brands-h {
  font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  font-family: var(--font-display); margin: 0 0 1rem; text-align: center;
}
.brand-chip {
  flex: 0 0 auto; padding: .55rem 1.15rem; border-radius: 999px;
  background: var(--card); border: 1px solid var(--line-soft); color: var(--muted);
  font-size: .9rem; white-space: nowrap;
}
.brands-none { text-align: center; max-inline-size: 34rem; margin-inline: auto; }
/* ⚠️ The moving row is decorative and aria-hidden; this is the same list for everyone else. */
.vh-list { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }

/* ── more than a consultancy (p7) ──────────────────────────────────────── */
.partner-lede { text-align: center; }
.partner-lede h2 { font-size: clamp(1.6rem, 3.4vw, 2.5rem); margin: 0 auto .75rem; max-inline-size: 24ch; }
.partner-lede .lede { margin-inline: auto; max-inline-size: 46ch; }

/* ══ THE HOME PAGE, REBUILT TO THE REVISION DOCUMENT ═══════════════════════════════════════ */

/* ⚠️ The two-line headings on 01, 02, 03 and 05 all set their second line in the accent. One
      rule, so the four sections cannot drift apart. */
.h-accent { color: var(--blue-600); }

/* ── 01 the problem (p8, p9) ─────────────────────────────────────────────────────────────── */
/*
 * ⚠️ "the dimension of the section is i feel wrong fix that too" — the old one was a 60/40 prose
 * split that ran to nine paragraphs. This is a balanced two-column band with the graphic carrying
 * the left and the four pointers the right, so neither column runs away from the other.
 */
.problem-band { background: var(--blue-50); }
.problem-split {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center;
}
.problem-say h2 { font-size: clamp(1.9rem, 3.8vw, 3rem); line-height: 1.1; margin-block-end: .7rem; }
.problem-say .lede { font-size: 1.05rem; max-inline-size: 26ch; margin-block-end: 0; }

/* the pulse motif */
.broken {
  position: relative; margin-block-start: clamp(1.75rem, 4vw, 2.75rem);
  inline-size: min(21rem, 100%); aspect-ratio: 1 / .82;
  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: .75rem;
}
.bt {
  display: grid; place-items: center; border-radius: 18px;
  background: var(--card); border: 1px solid var(--line-soft); color: var(--blue-700);
  box-shadow: var(--shadow-sm);
}
.bt svg { inline-size: 2rem; block-size: 2rem; }
/* ⭐ The odd one out is the point of the picture: it does not fit, and it is the wrong colour. */
.bt-miss {
  background: #E8593F; border-color: #E8593F; color: #fff;
  font-family: var(--font-display); font-weight: 800; font-size: 2rem;
  transform: rotate(6deg) translate(6%, 6%);
}
.broken-pulse {
  position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  inline-size: 0; block-size: 0; z-index: 1;
}
.broken-pulse i {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  inline-size: 12rem; block-size: 12rem; margin: -6rem 0 0 -6rem;
  border: 2px solid var(--blue-600); border-radius: 50%; opacity: 0;
  animation: pulseOut 3.2s var(--ease-out) infinite;
}
.broken-pulse i:nth-child(2) { animation-delay: 1.05s; }
.broken-pulse i:nth-child(3) { animation-delay: 2.1s; }
@keyframes pulseOut {
  0%   { transform: scale(.25); opacity: .55; }
  70%  { opacity: .12; }
  100% { transform: scale(1);   opacity: 0; }
}

.pointers { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.pointers li {
  display: grid; grid-template-columns: auto 1fr; gap: .05rem .9rem; align-items: center;
  padding-block: .95rem; border-block-end: 1px solid var(--line-soft);
}
.pointers li:last-child { border-block-end: 0; }
.pt-icon {
  grid-row: 1 / 3; inline-size: 42px; block-size: 42px; border-radius: 50%;
  display: grid; place-items: center; background: var(--blue-100); color: var(--blue-700);
}
.pt-icon svg { inline-size: 1.3rem; block-size: 1.3rem; }
/* ⚠️ Two tints borrowed from the reference so the four are not one flat block of blue. They are
      the site's own warm and amber, not a second palette. */
.pt-warm  { background: #FCE8E4; color: #C4462C; }
.pt-amber { background: #FDF0DC; color: #A66A15; }
.pointers strong { align-self: end; font-size: 1.02rem; color: var(--heading); }
.pointers li > span:last-child { align-self: start; font-size: .93rem; color: var(--muted); }

.problem-note {
  margin: 1.35rem 0 0; font-size: .95rem; color: var(--muted); line-height: 1.65;
  max-inline-size: 44ch;
}
.problem-note strong { color: var(--heading); }

.problem-cta {
  margin-block-start: 1.35rem; display: inline-grid; grid-template-columns: auto 1fr;
  gap: .9rem; align-items: center; text-decoration: none;
  background: var(--card); border: 1px solid var(--blue-300); border-radius: var(--radius-lg);
  padding: .9rem 1.35rem .9rem 1rem;
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.problem-cta:hover, .problem-cta:focus-visible {
  border-color: var(--blue-600); transform: translateY(-2px); box-shadow: var(--shadow-sm);
}
.pc-icon { inline-size: 2rem; block-size: 2rem; color: var(--blue-600); }
.pc-icon svg { inline-size: 100%; block-size: 100%; }
.pc-text { font-family: var(--font-display); font-weight: 700; color: var(--blue-700);
           line-height: 1.3; font-size: 1.02rem; }

/* ── 02 the flow (p10) ───────────────────────────────────────────────────────────────────── */
.oneflow { margin-block: clamp(2rem, 4vw, 3rem) 0; }
.pipe-in {
  list-style: none; margin: 0 0 2.25rem; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  gap: 1rem; text-align: center;
}
.pipe-in li {
  position: relative; font-size: .88rem; color: var(--muted);
  display: grid; align-content: start; justify-items: center; gap: .5rem;
  padding-block-end: 2rem;
}
.pi-icon {
  inline-size: 46px; block-size: 46px; border-radius: 14px; display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--line-soft); color: var(--blue-700);
  box-shadow: var(--shadow-sm);
}
.pi-icon svg { inline-size: 1.4rem; block-size: 1.4rem; }
/*
 * ⭐ THE DROP LINE, TRAVELLING (p10: "on arrows try to put moving animations"). A dashed rule with
 * a mask that slides down it — cheaper than four animated SVGs and it inherits the theme colour.
 */
.pipe-in li::after {
  content: ''; position: absolute; inset-block-end: 0; inset-inline-start: calc(50% - 1px);
  inline-size: 2px; block-size: 1.6rem;
  background: repeating-linear-gradient(var(--blue-300) 0 4px, transparent 4px 9px);
  animation: dropRun 1.6s linear infinite;
}
.pipe-in li:nth-child(2)::after { animation-delay: .4s; }
.pipe-in li:nth-child(3)::after { animation-delay: .8s; }
.pipe-in li:nth-child(4)::after { animation-delay: 1.2s; }
@keyframes dropRun { to { background-position-y: 9px; } }

.pipe-rail {
  list-style: none; margin: 0; padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1rem, 3vw, 2rem);
  background: var(--blue-50); border-radius: var(--radius-lg);
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: .75rem; counter-reset: flow;
}
.pipe-rail li {
  position: relative; display: grid; align-content: start; justify-items: center;
  gap: .6rem; text-align: center;
}
.pr-icon {
  inline-size: 58px; block-size: 58px; border-radius: 50%; display: grid; place-items: center;
  background: #fff; color: var(--blue-700); box-shadow: var(--shadow-sm);
}
.pr-icon svg { inline-size: 1.6rem; block-size: 1.6rem; }
.pr-paid { color: #1E8E5A; }
.pr-label { font-size: .88rem; font-weight: 600; color: var(--heading); line-height: 1.3; }
/* ⭐ The chevron between stages, sliding forward. */
.pipe-rail li + li::before {
  content: ''; position: absolute; inset-block-start: 21px; inset-inline-start: -.375rem;
  inline-size: 12px; block-size: 12px; margin-inline-start: -6px;
  border-inline-end: 2px solid var(--blue-500); border-block-start: 2px solid var(--blue-500);
  transform: rotate(45deg); animation: nudge 1.5s var(--ease) infinite;
}
.pipe-rail li:nth-child(3)::before { animation-delay: .2s; }
.pipe-rail li:nth-child(4)::before { animation-delay: .4s; }
.pipe-rail li:nth-child(5)::before { animation-delay: .6s; }
@keyframes nudge {
  0%, 100% { transform: rotate(45deg) translate(0, 0); opacity: .55; }
  50%      { transform: rotate(45deg) translate(3px, -3px); opacity: 1; }
}

.pipe-pull {
  margin: 1.75rem 0 0; padding-inline-start: 1.1rem;
  border-inline-start: 3px solid var(--blue-600);
  font-size: 1.05rem; color: var(--heading); line-height: 1.6;
}

/* ── 03 the hub (p11) ────────────────────────────────────────────────────────────────────── */
.trade-sub { font-family: var(--font-display); font-weight: 600; color: var(--blue-700);
             font-size: 1.02rem; margin-block-end: .5rem; }
.hub {
  margin-block: clamp(2rem, 4vw, 3rem);
  display: grid; gap: clamp(1.25rem, 3vw, 2.5rem);
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center;
}
.hub-trades, .hub-gets { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.hub-trades { justify-items: end; }
.hub-chip {
  display: inline-block; padding: .5rem 1rem; border-radius: 999px;
  background: var(--blue-50); border: 1px solid var(--blue-300);
  font-size: .92rem; font-weight: 600; color: var(--blue-800);
}
.hub-core {
  margin: 0; inline-size: clamp(9rem, 17vw, 12.5rem); aspect-ratio: 1;
  border-radius: 50%; display: grid; place-items: center; text-align: center;
  background: radial-gradient(circle at 40% 32%, #fff, var(--blue-100));
  border: 1px solid var(--blue-300); box-shadow: var(--shadow-sm);
}
.hub-core span {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  letter-spacing: .04em; line-height: 1.15; color: var(--heading);
  font-size: clamp(1rem, 1.9vw, 1.35rem); max-inline-size: 7ch;
}
.hub-gets li { display: grid; grid-template-columns: auto 1fr; gap: .7rem; align-items: center;
               font-size: .95rem; font-weight: 600; color: var(--heading); }
.hub-icon { inline-size: 34px; block-size: 34px; border-radius: 10px; display: grid;
            place-items: center; background: var(--blue-100); color: var(--blue-700); }
.hub-icon svg { inline-size: 1.05rem; block-size: 1.05rem; }
.trade-cta { margin-block-start: clamp(1.75rem, 4vw, 2.5rem); text-align: center; }

/* ── success stories (p14) ───────────────────────────────────────────────────────────────── */
.section-head-mid { text-align: center; max-inline-size: 46rem; margin-inline: auto; }
.section-head-mid p:not(.stories-rule) { margin-inline: auto; }
.stories-rule {
  font-size: .78rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  font-family: var(--font-display); margin: 0 0 .85rem;
  display: flex; align-items: center; justify-content: center; gap: .9rem;
}
.stories-rule::before, .stories-rule::after {
  content: ''; inline-size: 2rem; block-size: 1px; background: var(--line);
}
.stories .section-head-mid h2 { font-size: clamp(1.6rem, 3.4vw, 2.6rem); }

.story {
  margin: 0; padding: 1.4rem; background: var(--card);
  border: 1px solid var(--line-soft); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  display: grid; gap: 1rem; align-content: start;
}
.story-who { display: grid; grid-template-columns: auto 1fr auto; gap: .9rem; align-items: center; }
.story-face {
  inline-size: 56px; block-size: 56px; border-radius: 50%; object-fit: cover;
  background: var(--blue-100); border: 1px solid var(--blue-300);
  display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 800; font-size: 1.3rem; color: var(--blue-700);
}
.story-who strong { display: block; font-size: 1.02rem; color: var(--heading); }
.story-role { display: block; font-size: .9rem; color: var(--muted); }
.story blockquote { margin: 0; }
.story blockquote p { margin: 0; color: var(--muted); line-height: 1.65; }
.story-sector {
  font-size: .76rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.story-tag {
  align-self: start; font-family: var(--font-display); font-size: .6rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--blue-100); border-radius: 3px; padding: .2rem .4rem;
}
.story-empty { border-style: dashed; box-shadow: none; }
.story-face-empty { background: var(--blue-50); border-style: dashed; }

/*
 * ⭐ THE CARDS' GRID. One column, then on a wide screen three — with a client card spanning the
 *    whole row above the rest. ⛔⛔ Not auto-fit: at tablet widths auto-fit made two columns, and
 *    three cards in two columns leaves the third alone on a row. Content decides the columns here,
 *    so a client card with a paragraph and a list never stretches three short reserved cards into
 *    tall empty boxes beside it.
 */
.stories-grid { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 64rem) {
  .stories-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stories-grid > .story-client { grid-column: 1 / -1; }
}

/* ⭐ A client card: a description, not a quote — so it is set as plain text, never in quotation
 *    marks or italics, and the name is a heading rather than an attribution. */
.story-name { margin: 0; font-size: 1.02rem; line-height: 1.3; color: var(--heading); }
.story-client p { margin: 0; }
.story-client-body { display: grid; gap: 1.1rem 2rem; align-items: start; }
@media (min-width: 48rem) {
  .story-client-body { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
}
.story-client-about { display: grid; gap: .8rem; }
.story-summary { color: var(--muted); line-height: 1.65; }
.story-link a { font-weight: 600; color: var(--accent-ink); overflow-wrap: anywhere; }
/* what was built: the one tinted panel on the card, because it is the part that is ours */
.story-built {
  display: grid; gap: .55rem; padding: 1rem 1.15rem;
  background: var(--blue-50); border: 1px solid var(--line); border-radius: var(--radius);
}
.story-built-by {
  margin: 0; font-family: var(--font-display); font-size: .74rem; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent-ink); line-height: 1.4;
}
.story-built p { color: var(--heading); font-size: .95rem; line-height: 1.55; }
.story-built-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.story-built-list li {
  position: relative; padding-inline-start: 1.5rem; color: var(--heading); font-size: .95rem;
  font-weight: 600; line-height: 1.45;
}
/* a drawn tick rather than a character, so it cannot go missing with a font fallback */
.story-built-list li::before {
  content: ""; position: absolute; inset-inline-start: .15rem; inset-block-start: .3rem;
  inline-size: .5rem; block-size: .8rem; border: solid var(--accent-ink);
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
/* ⚠️ The arrow is decoration: the "/ ''" form gives it empty alt text, so a screen reader does not
 *    announce "north east arrow" after the address. Browsers without it keep the line above. */
.story-link a::after { content: " \2197"; content: " \2197" / ""; }

/* ── 05 around the CRM (p17) ─────────────────────────────────────────────────────────────── */
/*
 * ⭐ p17: "in lines if we can use animations should look dynamic". Three connectors run out of a
 * core and a highlight travels along each — the three cards under it are what they connect to.
 */
.orbit {
  position: relative; block-size: 5.5rem; margin-block: clamp(1.5rem, 3vw, 2.25rem) 0;
  display: grid; place-items: center;
}
.orbit-core {
  position: relative; z-index: 1; padding: .55rem 1.25rem; border-radius: 999px;
  background: var(--blue-700); color: #fff; font-size: .82rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; box-shadow: var(--shadow-sm);
}
.orbit-line {
  position: absolute; inset-block-end: 0; inline-size: 2px; block-size: 2.6rem;
  background: var(--blue-300); overflow: hidden;
}
.orbit-line-1 { inset-inline-start: 16.6%; }
.orbit-line-2 { inset-inline-start: 50%; }
.orbit-line-3 { inset-inline-start: 83.3%; }
.orbit-line::after {
  content: ''; position: absolute; inset-inline: 0; block-size: 1rem;
  background: linear-gradient(var(--blue-600), transparent);
  animation: orbitRun 2.4s var(--ease) infinite;
}
.orbit-line-2::after { animation-delay: .3s; }
.orbit-line-3::after { animation-delay: .6s; }
@keyframes orbitRun {
  0%   { inset-block-start: -1rem; opacity: 0; }
  25%  { opacity: 1; }
  100% { inset-block-start: 2.6rem; opacity: 0; }
}
/* ⚠️ The elbow that joins each vertical to the core, drawn once as a rule under it. */
.orbit::before {
  content: ''; position: absolute; inset-block-end: 2.6rem; inset-inline: 16.6% 16.6%;
  block-size: 2px; background: var(--blue-300);
}

.ext { display: grid; gap: .6rem; align-content: start; }
.ext-icon {
  inline-size: 44px; block-size: 44px; border-radius: 13px; display: grid; place-items: center;
  background: var(--blue-100); color: var(--blue-700);
}
.ext-icon svg { inline-size: 1.35rem; block-size: 1.35rem; }
.ext-note {
  margin: clamp(1.5rem, 3vw, 2rem) auto 0; display: flex; align-items: center; gap: .65rem;
  justify-content: center; font-size: .95rem; color: var(--muted); text-align: center;
}
.ext-note-icon { inline-size: 1.25rem; block-size: 1.25rem; color: var(--blue-600); flex: 0 0 auto; }
.ext-note-icon svg { inline-size: 100%; block-size: 100%; }

/* ── the closing CTA (p19) ───────────────────────────────────────────────────────────────── */
/* ⚠️ Small and compact, with the copy in white on the deep band — the whole of p19's note. */
.close-cta { padding-block: clamp(2.5rem, 5vw, 3.5rem); }
.close-cta .inner-narrow { text-align: center; }
.close-cta h2 { font-size: clamp(1.5rem, 3vw, 2.15rem); margin: 0 0 .6rem; color: #fff; }
.close-cta p { margin: 0 auto 1.5rem; max-inline-size: 40ch; color: rgba(255, 255, 255, .82); }

/* ── narrower windows ────────────────────────────────────────────────────────────────────── */
@media (max-width: 60rem) {
  .problem-split { grid-template-columns: minmax(0, 1fr); }
  .broken { margin-inline: auto; }
  .hub { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .hub-trades { justify-items: center; }
  .hub-trades, .hub-gets { justify-content: center; }
  /* ⚠️ The rail wraps to two rows here, so the chevrons between stages would point into nothing. */
  .pipe-rail { grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr)); }
  .pipe-rail li + li::before { display: none; }
  .orbit { display: none; }
}

/* ⛔ Everything above animates for effect, so all of it stops here. The pulse, the drop lines, the
      chevrons and the connectors are decoration — none of them carries information the page loses
      when they hold still. */
/*
 * ⛔ THE LAST THREE ANIMATIONS ON THE SITE. `.preload-mark` and `.preload-glow` sit inside
 * `.preload`, which this same block already sets to display:none — so they were covered in
 * practice but not in writing, and a reduced-motion audit cannot see through a parent. `.tok`
 * belongs to the redaction illustration, whose markup left in phase 3; the rule stays so the
 * section can be reinstated, and it is guarded now rather than the day somebody reinstates it.
 */
@media (prefers-reduced-motion: reduce) {
  .preload-mark, .preload-glow, .tok, .hero-h .hl { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  .broken-pulse i { animation: none; opacity: .18; transform: scale(.62); }
  .pipe-in li::after, .pipe-rail li + li::before, .orbit-line::after { animation: none; }
  .pipe-rail li + li::before { opacity: .55; }
  .orbit-line::after { display: none; }
}

/* ── the process, as a banner (p18) ───────────────────────────────────────── */
/*
 * ⚠️ "should be very clean and neat" — one row, one rule through it, four nodes on the rule.
 * The winding path is 700px of drawing; this is 1/5 of the height and says the same thing.
 */
.jbanner {
  position: relative; list-style: none; margin: clamp(1.5rem, 3vw, 2.25rem) 0 0;
  padding: clamp(1.5rem, 3vw, 2rem) clamp(1.25rem, 3vw, 2rem);
  background: var(--blue-950); border-radius: var(--radius-lg);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.25rem;
}
/* ⭐ The rule the nodes sit on, drawn once behind them and stopping at the outer two. */
.jbanner::before {
  content: ''; position: absolute; inset-block-start: calc(clamp(1.5rem, 3vw, 2rem) + 27px);
  inset-inline: calc(12.5% + clamp(1.25rem, 3vw, 2rem) / 2) calc(12.5% + clamp(1.25rem, 3vw, 2rem) / 2);
  block-size: 2px;
  background: repeating-linear-gradient(90deg, rgba(140,180,255,.55) 0 3px, transparent 3px 10px);
}
.jb-step { position: relative; display: grid; align-content: start; justify-items: center;
           gap: .75rem; text-align: center; }
.jb-node { position: relative; display: grid; place-items: center; inline-size: 56px; block-size: 56px; }
.jb-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--blue-500); background: var(--blue-950);
}
.jb-icon { position: relative; inline-size: 1.4rem; block-size: 1.4rem; color: #DCE9FF; }
.jb-icon svg { inline-size: 100%; block-size: 100%; }
/* ⚠️ The number badge the reference puts beside each ring, not inside it. */
.jb-n {
  position: absolute; inset-block-end: -2px; inset-inline-end: -10px;
  font-family: var(--font-display); font-size: .62rem; font-weight: 800; letter-spacing: .06em;
  color: #A9C8FF; background: var(--blue-900); border-radius: 4px; padding: .1rem .3rem;
}
/* ⭐ Each ring takes one step along the blue ladder, so the four read as a sequence. */
.jb-step:nth-child(1) .jb-ring { border-color: #6FA8FF; }
.jb-step:nth-child(2) .jb-ring { border-color: #4B82F0; }
.jb-step:nth-child(3) .jb-ring { border-color: #2F63D8; }
.jb-step:nth-child(4) .jb-ring { border-color: #1C46AD; }
.jb-body h3 { margin: 0 0 .3rem; font-size: .98rem; color: #fff; line-height: 1.25; }
.jb-body p { margin: 0; font-size: .85rem; color: #B8CBEA; line-height: 1.5; }

@media (max-width: 60rem) {
  .jbanner { grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr)); }
  /* ⚠️ Wrapped onto two rows, a rule drawn across the top would run through empty air. */
  .jbanner::before { display: none; }
}

/* ══ THE ABOUT PAGE, REBUILT TO PAGES 22–27 ═══════════════════════════════════════════════ */

/* the deep-band twin of .h-accent; the light one is on the home page's rebuilt sections */
.h-accent-deep { color: var(--blue-500); }
.stage-deep { color: #A9C8FF; }
.stage-deep .stage-n { background: rgba(91, 150, 255, .18); color: #C9DCFF; }
.cta-quiet-deep { color: #DCE9FF; }
.cta-quiet-deep:hover, .cta-quiet-deep:focus-visible { color: #fff; }

/* ⚠️ .ticks had ONE rule in the whole stylesheet — a margin on the demo gate. Every other use,
      including three on this page, was rendering as a default browser bullet list. */
.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.ticks li {
  position: relative; padding-inline-start: 1.6rem; font-size: .95rem; color: var(--muted);
  line-height: 1.55;
}
.ticks li::before {
  content: ''; position: absolute; inset-inline-start: 0; inset-block-start: .42em;
  inline-size: .78rem; block-size: .42rem;
  border-inline-start: 2px solid var(--blue-600); border-block-end: 2px solid var(--blue-600);
  transform: rotate(-45deg);
}
.ticks-deep li { color: #C4D6F5; }
.ticks-deep li::before { border-color: var(--blue-500); }

/* ── the hero and its diagram (p22, p23) ─────────────────────────────────────────────────── */
.about-hero { padding-block: clamp(2rem, 5vw, 3.5rem); }
.about-hero-split {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); align-items: center;
}
.about-hero h1 { font-size: clamp(2rem, 4.2vw, 3.3rem); line-height: 1.08; margin-block-end: .9rem; }
.about-hero .lede { max-inline-size: 34rem; margin-block-end: 1.4rem; }

.tri { display: grid; gap: 0; justify-items: center; }
.tri-top {
  list-style: none; margin: 0; padding: 0; inline-size: 100%;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem;
}
.tri-node { display: grid; justify-items: center; gap: .35rem; text-align: center; }
.tri-ring {
  inline-size: 62px; block-size: 62px; border-radius: 50%; display: grid; place-items: center;
  border: 2px solid var(--blue-500); background: rgba(11, 42, 107, .55); color: #DCE9FF;
}
.tri-ring svg { inline-size: 1.5rem; block-size: 1.5rem; }
/* ⭐ Each firm keeps the hue it has everywhere else on the site. */
.tri-pop  .tri-ring { border-color: #9B8CF5; color: #DDD6FF; }
.tri-sort .tri-ring { border-color: #E8A33D; color: #FFE3B8; }
.tri-node strong { font-size: .95rem; color: #fff; line-height: 1.2; }
.tri-node > span { font-size: .8rem; color: #A9C8FF; }

.tri-wires { inline-size: 100%; block-size: 5.5rem; display: block; }
.tri-wires path {
  fill: none; stroke: rgba(140, 180, 255, .45); stroke-width: 1.4; vector-effect: non-scaling-stroke;
}
/* ⭐ p23: "the designs animation in the arrows you can add" — a dash travelling down each wire. */
.tri-wires path {
  stroke-dasharray: 4 10; animation: wireRun 2.4s linear infinite;
}
.tri-wires path:nth-child(2) { animation-delay: .35s; }
.tri-wires path:nth-child(3) { animation-delay: .7s; }
@keyframes wireRun { to { stroke-dashoffset: -28; } }

.tri-core {
  margin: 0; display: grid; justify-items: center; gap: .45rem; text-align: center;
}
.tri-core .tri-ring {
  inline-size: 74px; block-size: 74px; border-color: #6FD8C8;
  background: radial-gradient(circle at 50% 40%, rgba(111, 216, 200, .22), rgba(11, 42, 107, .5));
  color: #C6F5EC;
}
.tri-core strong { font-size: 1rem; color: #fff; }

/* ── vision and mission (p22) ────────────────────────────────────────────────────────────── */
.vision-split {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, .9fr); align-items: center;
}
.vision h2 { font-size: clamp(1.7rem, 3.6vw, 2.7rem); line-height: 1.1; margin-block-end: 1.5rem; }
.vm { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; }
.vm-col + .vm-col { padding-inline-start: 1.5rem; border-inline-start: 1px solid rgba(140, 180, 255, .22); }
.vm-h {
  display: flex; align-items: center; gap: .55rem; margin: 0 0 .55rem;
  font-family: var(--font-display); font-weight: 700; font-size: .8rem;
  letter-spacing: .1em; text-transform: uppercase; color: #A9C8FF;
}
.vm-icon {
  inline-size: 30px; block-size: 30px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--blue-500); color: #DCE9FF; flex: 0 0 auto;
}
.vm-icon svg { inline-size: .95rem; block-size: .95rem; }
.vm-col p:last-child { margin: 0; font-size: .92rem; color: #B8CBEA; line-height: 1.65; }

/* the ERP ring: one core, N spokes placed by index */
.erp {
  position: relative; inline-size: min(30rem, 100%); aspect-ratio: 1; margin-inline: auto;
}
.erp::before, .erp::after {
  content: ''; position: absolute; inset: 26%; border-radius: 50%;
  border: 1px solid rgba(140, 180, 255, .22);
}
.erp::after { inset: 34%; border-color: rgba(140, 180, 255, .14); }
.erp-ring { list-style: none; margin: 0; padding: 0; }
.erp-core {
  position: absolute; inset: 33%; border-radius: 50%; z-index: 1;
  display: grid; place-content: center; gap: .3rem; text-align: center; padding: .5rem;
  background: radial-gradient(circle at 50% 38%, rgba(47, 99, 216, .5), rgba(6, 22, 52, .9));
  box-shadow: 0 0 40px rgba(47, 99, 216, .35);
}
.erp-core-name {
  font-family: var(--font-display); font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; font-size: clamp(.72rem, 1.5vw, .95rem); color: #fff;
}
.erp-core-sub { font-size: clamp(.54rem, 1vw, .66rem); color: #A9C8FF; line-height: 1.35; }
/*
 * ⭐ EACH SPOKE IS PLACED FROM ITS INDEX, so the ring re-spaces itself if a seventh department is
 * ever added rather than needing six hand-written positions.
 *
 * ⛔ ROTATE A FULL-SIZE FRAME, DON'T DO TRIGONOMETRY. cos()/sin() in a translate would be the
 * obvious way to write this, but a browser that does not know them throws the WHOLE declaration
 * away and every spoke piles up in the centre — a silent, total failure. Rotating a box that fills
 * the ring and counter-rotating its contents needs nothing newer than transforms.
 *
 * ⚠️ THE RADII ARE CONSTRAINED BY THE LABEL WIDTH, not chosen by eye. At 60° a label's outer
 * edge sits at R·sin(60°) + half its width, and that has to stay inside the box or the page scrolls
 * sideways — which is exactly what the first draft did at 360px. With a 7rem label (half = 3.5rem)
 * in a 30rem box, the label centre cannot pass ~11.5rem, hence 8% from the top; the dots sit at 19%.
 */
.erp-spoke {
  position: absolute; inset: 0;
  rotate: calc(var(--i) * 360deg / var(--n));
}
.erp-dot {
  position: absolute; inset-block-start: 19%; inset-inline-start: 50%;
  inline-size: 44px; block-size: 44px; margin-inline-start: -22px;
  rotate: calc(var(--i) * -360deg / var(--n));
  border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--blue-500); background: rgba(6, 22, 52, .92); color: #DCE9FF;
}
.erp-dot svg { inline-size: 1.15rem; block-size: 1.15rem; }
/* ⚠️ Counter-rotated too, so the words stay upright however far round the ring they sit. */
.erp-label {
  position: absolute; inset-block-start: 8%; inset-inline-start: 50%;
  inline-size: 7rem; margin-inline-start: -3.5rem;
  rotate: calc(var(--i) * -360deg / var(--n));
  text-align: center; font-size: .7rem; line-height: 1.3; color: #C4D6F5;
}

/* ── our motto (p22) ─────────────────────────────────────────────────────────────────────── */
.motto-split {
  display: grid; gap: clamp(2rem, 4vw, 3rem);
  grid-template-columns: minmax(0, .8fr) minmax(0, 1.5fr); align-items: center;
}
.motto h2 { font-size: clamp(1.6rem, 3.2vw, 2.4rem); line-height: 1.12; margin-block-end: .8rem; }
.chain {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.6rem;
}
.chain-step {
  position: relative; background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg); padding: 1.15rem 1rem; text-align: center;
  display: grid; align-content: start; justify-items: center; gap: .55rem;
  box-shadow: var(--shadow-sm);
}
.chain-icon { inline-size: 2.4rem; block-size: 2.4rem; color: var(--blue-600); }
.chain-icon svg { inline-size: 100%; block-size: 100%; }
.chain-step h3 { margin: 0; font-size: .95rem; line-height: 1.25; }
.chain-step p { margin: 0; font-size: .82rem; color: var(--muted); line-height: 1.45; }
/* ⭐ The last card is the payoff, so it inverts — the reference does the same. */
.chain-step-end { background: var(--blue-950); border-color: var(--blue-950); }
.chain-step-end .chain-icon { color: #A9C8FF; }
.chain-step-end h3 { color: #fff; }
.chain-step-end p { color: #B8CBEA; }
/* the chevrons between, drawn in the gaps */
.chain-step + .chain-step::before {
  content: ''; position: absolute; inset-block-start: calc(50% - 6px); inset-inline-start: -1.1rem;
  inline-size: 10px; block-size: 10px;
  border-inline-end: 2px solid var(--blue-500); border-block-start: 2px solid var(--blue-500);
  transform: rotate(45deg);
}

.beyond {
  margin-block-start: clamp(1.5rem, 3vw, 2.25rem);
  display: grid; grid-template-columns: auto 1fr auto; gap: 1.25rem 1.75rem; align-items: center;
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  padding: 1.1rem 1.4rem; box-shadow: var(--shadow-sm);
}
.beyond p { margin: 0; }
.beyond-a { display: flex; align-items: center; gap: .75rem; }
.beyond-a strong { color: var(--blue-700); font-size: .95rem; max-inline-size: 14ch; line-height: 1.3; }
.beyond-icon {
  inline-size: 40px; block-size: 40px; border-radius: 50%; display: grid; place-items: center;
  background: var(--blue-600); color: #fff; flex: 0 0 auto;
}
.beyond-icon svg { inline-size: 1.2rem; block-size: 1.2rem; }
.beyond-b {
  font-size: .93rem; color: var(--muted); line-height: 1.6;
  padding-inline: 1.75rem; border-inline: 1px solid var(--line-soft);
}
.beyond-c { display: grid; gap: .15rem; max-inline-size: 20rem; }
.beyond-c strong { color: var(--blue-700); font-size: .93rem; }
.beyond-c span { font-size: .86rem; color: var(--muted); line-height: 1.5; }

/* ── who we are, and the dashboard (p22) ─────────────────────────────────────────────────── */
.who-split {
  display: grid; gap: clamp(2rem, 5vw, 3.5rem);
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); align-items: center;
}
.who-split h2 { font-size: clamp(1.6rem, 3.2vw, 2.35rem); line-height: 1.14; margin-block-end: .9rem; }

.dash {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1rem; display: grid; gap: .75rem; box-shadow: var(--shadow-sm);
  font-size: .78rem;
}
.dash-head { display: flex; align-items: center; justify-content: space-between; }
.dash-title { font-family: var(--font-display); font-weight: 700; font-size: .95rem; color: var(--heading); }
.dash-new {
  background: var(--blue-600); color: #fff; border-radius: 6px; padding: .18rem .5rem;
  font-size: .68rem; font-weight: 600;
}
.dash-tiles {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(7rem, 100%), 1fr)); gap: .55rem;
}
.dash-tiles li {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: 10px;
  padding: .55rem .65rem; display: grid; gap: .1rem;
}
.dt-k { font-size: .66rem; color: var(--muted); }
.dt-v { font-family: var(--font-display); font-weight: 700; font-size: .98rem; color: var(--heading);
        font-variant-numeric: tabular-nums; }
/* ⚠️ #1A7F50, not #1E8E5A: at 10px this needs 4.5:1 and the lighter green measured 4.14. */
.dt-d { font-size: .64rem; color: #1A7F50; }
.dash-lower { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: .55rem; }
.dash-card {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: 10px; padding: .65rem;
}
.dash-card-h { margin: 0 0 .5rem; font-size: .7rem; font-weight: 600; color: var(--muted); }
/* ⚠️ The funnel is drawn from a width per row, so the shape follows the numbers rather than
      being five hand-set widths that stop agreeing with the labels. */
.funnel { list-style: none; margin: 0; padding: 0; display: grid; gap: .22rem; }
/* ⛔⛔ THE BAR WAS A GRID TRACK SIZED `var(--w)`, AND --w IS A PERCENTAGE OF THE CONTAINER. At
   `--w: 100%` the bar took the whole 319px row and the label and number columns were added on top
   of it — 382px of content in a 319px card, with "Raw" and "142" hanging outside it. The second
   row spilled too. Measured on the live page.
   ⭐ The bar goes INSIDE a flexible track instead: label and number take what they need, the bar
   takes what is left, and --w scales it within that. Every row fits, including 100%. */
.funnel li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: .5rem;
  font-size: .68rem;
}
.funnel li::before {
  content: ''; block-size: .95rem; border-radius: 3px; inline-size: var(--w);
  background: linear-gradient(90deg, var(--blue-600), var(--blue-500));
}
.funnel li:nth-child(2)::before { opacity: .85; }
.funnel li:nth-child(3)::before { opacity: .7; }
.funnel li:nth-child(4)::before { opacity: .55; }
.funnel li:nth-child(5)::before { opacity: .4; }
.funnel span { color: var(--muted); }
.funnel b { font-variant-numeric: tabular-nums; color: var(--heading); font-weight: 600; }
.feed { list-style: none; margin: 0; padding: 0; display: grid; gap: .42rem; }
.feed li {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem; font-size: .68rem;
  color: var(--muted);
}
.feed li::before {
  content: ''; inline-size: .4rem; block-size: .4rem; border-radius: 50%;
  background: var(--blue-500); flex: 0 0 auto; margin-inline-end: -.25rem;
}
.feed span { flex: 1 1 auto; }
/* ⛔ NOT --faint. It resolves to #8794AB — 3.06:1 — and the var() fallback never fires
      because the token exists. --muted is what every other quiet label here uses. */
.feed time { color: var(--muted); font-variant-numeric: tabular-nums; }

/* ── one team, three specialists (p24, p25) ──────────────────────────────────────────────── */
.team-rule {
  text-align: center; margin: 0 0 1.5rem; font-family: var(--font-display); font-weight: 700;
  font-size: .8rem; letter-spacing: .14em; text-transform: uppercase; color: var(--blue-700);
}
.specialists {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: 1.25rem;
}
.spec {
  border-radius: var(--radius-lg); padding: 1.35rem; display: grid; align-content: start;
  gap: .6rem; background: var(--blue-50); border: 1px solid var(--line-soft);
}
/* ⭐ One tint per firm, the same three the site uses wherever they are named together. */
.spec-pop  { background: #F3F0FE; }
.spec-sort { background: #FDF6EC; }
.spec-icon { inline-size: 2.5rem; block-size: 2.5rem; color: var(--blue-700); }
.spec-pop  .spec-icon { color: #5B45C4; }
.spec-sort .spec-icon { color: #B4761A; }
.spec-icon svg { inline-size: 100%; block-size: 100%; }
.spec h3 { margin: 0; font-size: 1.08rem; color: var(--blue-700); }
.spec-pop  h3 { color: #4A36A8; }
.spec-sort h3 { color: #9A6410; }
.spec-role { margin: 0 0 .4rem; font-weight: 600; color: var(--heading); font-size: .95rem; }

/* ── from scattered to connected (p25) ───────────────────────────────────────────────────── */
.connect-split {
  display: grid; gap: clamp(1.75rem, 4vw, 3rem);
  grid-template-columns: minmax(0, .75fr) minmax(0, 1.6fr); align-items: center;
}
.connect-split h2 { font-size: clamp(1.5rem, 3vw, 2.2rem); line-height: 1.14; margin-block-end: .8rem; }
.connect {
  display: grid; grid-template-columns: auto 1fr auto 1fr auto; gap: .75rem; align-items: center;
}
.cx { display: grid; gap: .7rem; }
.cx-h {
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: .7rem;
  letter-spacing: .12em; text-transform: uppercase; color: #8FA9D4; text-align: center;
}
.cx-h-on { color: var(--blue-500); }
.cx-tools {
  list-style: none; margin: 0; padding: .9rem; display: grid; gap: .9rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid rgba(140, 180, 255, .22); border-radius: var(--radius-lg);
}
.cx-tools li { display: grid; justify-items: center; gap: .3rem; font-size: .74rem; color: #C4D6F5; }
.cx-icon {
  inline-size: 34px; block-size: 34px; border-radius: 9px; display: grid; place-items: center;
  background: rgba(91, 150, 255, .14); color: #DCE9FF;
}
.cx-icon svg { inline-size: 1.05rem; block-size: 1.05rem; }
/* ⭐ The wire between the halves, with a pulse running along it (p25's dotted connector). */
.cx-wire {
  block-size: 2px; align-self: center;
  background: repeating-linear-gradient(90deg, rgba(140, 180, 255, .5) 0 3px, transparent 3px 9px);
  animation: cxRun 1.6s linear infinite;
}
@keyframes cxRun { to { background-position-x: 9px; } }
.cx-one { margin: 0; display: grid; justify-items: center; gap: .45rem; text-align: center; }
.cx-one-ring {
  inline-size: 72px; block-size: 72px; border-radius: 50%; display: grid; place-items: center;
  border: 2px solid var(--blue-500); color: #DCE9FF;
  background: radial-gradient(circle at 50% 40%, rgba(47, 99, 216, .35), transparent 70%);
  box-shadow: 0 0 26px rgba(75, 130, 240, .35);
}
.cx-one-ring svg { inline-size: 1.7rem; block-size: 1.7rem; }
.cx-one span { font-size: .8rem; color: #fff; font-weight: 600; }
.cx-out { padding: .9rem 1rem; border: 1px solid rgba(140, 180, 255, .22); border-radius: var(--radius-lg); }

/* ── how we work (p25, p26) ──────────────────────────────────────────────────────────────── */
.worksteps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.75rem;
}
.wstep { position: relative; display: grid; align-content: start; gap: .35rem; }
.wstep-n {
  font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; color: var(--blue-600);
  font-variant-numeric: tabular-nums;
}
.wstep h3 { margin: 0; font-size: 1rem; }
.wstep p { margin: 0; font-size: .9rem; color: var(--muted); line-height: 1.6; }
/* the arrow between steps, in the gap */
.wstep + .wstep::before {
  content: ''; position: absolute; inset-block-start: 1.6rem; inset-inline-start: -1.15rem;
  inline-size: 10px; block-size: 10px;
  border-inline-end: 2px solid var(--blue-500); border-block-start: 2px solid var(--blue-500);
  transform: rotate(45deg); animation: nudge 1.5s var(--ease) infinite;
}
.worksteps li:nth-child(3)::before { animation-delay: .2s; }
.worksteps li:nth-child(4)::before { animation-delay: .4s; }

/* ── who is responsible (p26) ────────────────────────────────────────────────────────────── */
.resp { background: var(--paper); }
.resp-split {
  display: grid; gap: clamp(1.75rem, 4vw, 3rem);
  grid-template-columns: minmax(0, .8fr) minmax(0, 2fr); align-items: start;
}
.resp-say h2 { font-size: clamp(1.5rem, 3vw, 2.1rem); line-height: 1.15; margin-block-end: .7rem; }
.resp-say p { margin: 0; color: var(--muted); font-size: .95rem; line-height: 1.6; }
.resp-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr)); gap: 1.75rem;
}
.resp-col > ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .38rem; }
.resp-col > ul li {
  position: relative; padding-inline-start: .85rem; font-size: .85rem; color: var(--muted);
  line-height: 1.5;
}
.resp-col > ul li::before {
  content: ''; position: absolute; inset-inline-start: 0; inset-block-start: .55em;
  inline-size: .28rem; block-size: .28rem; border-radius: 50%; background: var(--blue-500);
}
.resp-h { margin: 0 0 .7rem; display: grid; grid-template-columns: auto 1fr; gap: .1rem .7rem; }
.resp-icon {
  grid-row: 1 / 3; inline-size: 2rem; block-size: 2rem; color: var(--blue-700);
}
.resp-pop  .resp-icon { color: #5B45C4; }
.resp-sort .resp-icon { color: #B4761A; }
.resp-icon svg { inline-size: 100%; block-size: 100%; }
.resp-h strong { align-self: end; color: var(--heading); font-size: .98rem; }
.resp-h > span:last-child { align-self: start; font-size: .84rem; color: var(--muted); line-height: 1.35; }

/* ── the close (p27) ─────────────────────────────────────────────────────────────────────── */
/* ⚠️ p27 sets this band in the accent blue rather than the site's usual near-black navy. */
.band-blue { background: var(--blue-600); }
.close-cta-blue h2 { color: #fff; }
.close-cta-blue p { color: rgba(255, 255, 255, .88); }
.cta-onblue { background: #fff; color: var(--blue-700); }
.cta-onblue:hover, .cta-onblue:focus-visible { background: #F1F5FF; color: var(--blue-800); }

/* ── narrower windows ────────────────────────────────────────────────────────────────────── */
@media (max-width: 62rem) {
  .about-hero-split, .vision-split, .motto-split, .who-split, .connect-split, .resp-split {
    grid-template-columns: minmax(0, 1fr);
  }
  .vm { grid-template-columns: minmax(0, 1fr); }
  .vm-col + .vm-col { padding-inline-start: 0; border-inline-start: 0;
                      padding-block-start: 1.25rem; border-block-start: 1px solid rgba(140, 180, 255, .22); }
  .chain { grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr)); gap: 1rem; }
  /* ⚠️ Once the chain wraps, a chevron in the gap points at the row above. */
  .chain-step + .chain-step::before { display: none; }
  .beyond { grid-template-columns: minmax(0, 1fr); }
  .beyond-b { padding-inline: 0; border-inline: 0;
              padding-block: 1rem; border-block: 1px solid var(--line-soft); }
  .beyond-c { max-inline-size: none; }
  .connect { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .cx-wire { block-size: 1.6rem; inline-size: 2px; justify-self: center;
             background: repeating-linear-gradient(rgba(140, 180, 255, .5) 0 3px, transparent 3px 9px);
             animation-name: cxRunV; }
  @keyframes cxRunV { to { background-position-y: 9px; } }
  .worksteps { grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); gap: 1.5rem; }
  .wstep + .wstep::before { display: none; }
  /*
   * ⛔ THE RING COMES APART BELOW THIS WIDTH. Six labels on a 320px circle stick out past the
   * viewport, and the page scrolls sideways — the reason the ring is a real <ul> rather than a
   * picture is so it can simply stop being a ring here.
   */
  .erp { inline-size: 100%; aspect-ratio: auto; }
  .erp::before, .erp::after, .erp-core { display: none; }
  .erp-ring { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; }
  .erp-spoke {
    position: static; rotate: none; inset: auto;
    display: flex; align-items: center; gap: .55rem;
    padding: .45rem .85rem .45rem .5rem; border-radius: 999px;
    border: 1px solid rgba(140, 180, 255, .22); background: rgba(11, 42, 107, .35);
  }
  .erp-dot {
    position: static; rotate: none; inline-size: 30px; block-size: 30px; margin: 0;
    flex: 0 0 auto;
  }
  .erp-dot svg { inline-size: .9rem; block-size: .9rem; }
  .erp-label {
    position: static; rotate: none; inline-size: auto; margin: 0;
    text-align: start; font-size: .8rem; white-space: nowrap;
  }
}

/* ⛔ Everything above that moves is decoration — the wires, the chevrons and the connector pulse
      all carry information the markup already states in words. */
@media (prefers-reduced-motion: reduce) {
  .tri-wires path, .cx-wire, .wstep + .wstep::before { animation: none; }
  .tri-wires path { stroke-dasharray: 4 10; }
}

/* ══ THE SERVICES PAGES, REBUILT TO PAGES 29–32 ═══════════════════════════════════════════ */

/* ── the index: four alternating blocks ──────────────────────────────────────────────────── */
.svc-block {
  display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  padding-block: clamp(2rem, 5vw, 3.5rem);
  border-block-start: 1px solid var(--line-soft);
}
.svc-block:first-of-type { border-block-start: 0; }
/* ⭐ The zig-zag: even blocks put the art first, so the eye crosses the page between services. */
.svc-block-flip .svc-say { order: 2; }
.svc-say h3 { font-size: clamp(1.5rem, 3vw, 2.15rem); line-height: 1.14; margin: 0 0 .6rem; }
.svc-lede { color: var(--muted); line-height: 1.7; margin: 0 0 1.15rem; max-inline-size: 44ch; }
.svc-points { margin-block-end: 1.4rem; }

/*
 * ⚠️ THE PANEL WHERE THE REFERENCE PUTS A PHOTOGRAPH. It is built from the service's own extra
 * points, so it says something true about the service instead of being a decorative slab — and it
 * needs no imagery we do not have.
 */
/* ── the sub-page hero, with the form beside it ──────────────────────────────────────────── */
.svc-hero { padding-block: clamp(2rem, 5vw, 3.25rem); }
.svc-hero-split {
  display: grid; gap: clamp(2rem, 5vw, 3.5rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: start;
}
.svc-hero h1 { font-size: clamp(2rem, 4.2vw, 3.1rem); line-height: 1.08; margin-block-end: .8rem; }
.svc-hero .lede { max-inline-size: 40ch; margin-block-end: 1.5rem; }
.svc-hero-points { margin-block-end: 1.5rem; }
.svc-hero-points li { font-size: 1rem; }
.svc-hero-back { margin: 0; }

/* ── the quote form panel (p30) ──────────────────────────────────────────────────────────── */
/*
 * ⛔ NOT `.quote` — that is the blog page's empty-state figure, a lifting bordered card with its
 * own ::before rule. A form panel that hover-lifted like a card would have been the third silent
 * class collision on this rebuild, so the names were checked before a line of this was written.
 */
.qform {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  padding: clamp(1.25rem, 3vw, 1.85rem); box-shadow: var(--shadow-sm);
}
.qform-h { font-size: clamp(1.15rem, 2.2vw, 1.5rem); line-height: 1.2; margin: 0 0 .5rem; }
.qform-sub { margin: 0 0 1.25rem; font-size: .92rem; color: var(--muted); line-height: 1.6; }
/*
 * ⛔ A CARD INSIDE A CARD. `.enquiry` is itself a bordered, padded, shadowed panel — it was
 * written for a form that stands alone on the contact page. Dropped inside `.qform` it drew a
 * second frame with its own padding, which is why the fields measured 500px inside a 574px
 * panel. The outer panel is the frame; the form inside it is just a grid.
 */
.qform .enquiry {
  margin-block-start: 0; background: transparent; border: 0; border-radius: 0;
  padding: 0; box-shadow: none;
}
.qform-fields { display: grid; gap: .9rem; }
/* ⚠️ The grid gap spaces the rows; `.field`'s own bottom margin would double it. */
.qform-fields .field { margin-block-end: 0; }
.qform-go { justify-self: start; margin-block-start: .35rem; }
.qform .fineprint { margin-block-start: .35rem; }

/*
 * ⛔ WHITE ON WHITE. `.band-deep .cta` inverts the button to white for dark bands — correct
 * everywhere except inside this panel, which keeps a LIGHT ground on a deep band. The submit
 * button rendered white on white and was invisible: the same class of failure as the demo gate's
 * unstyled inputs, and nothing errors when it happens.
 */
.band-deep .qform .cta { background: var(--blue-600); color: #fff; }
.band-deep .qform .cta:hover, .band-deep .qform .cta:focus-visible {
  background: var(--blue-700); color: #fff;
}

/* ⚠️ On a deep band the panel keeps a light ground on purpose: an input is a place to type, and
      inverting it is where forms stop looking like forms. Only the heading changes. */
.qform-deep { border-color: transparent; box-shadow: 0 18px 48px rgba(3, 12, 32, .38); }

/*
 * ⛔⛔ THE DEEP BAND'S TEXT COLOURS LEAK INTO THIS PANEL, AND THE PANEL IS LIGHT.
 * `.band-deep p` sets --on-deep-muted, a pale blue-grey meant for navy — on the panel's white
 * ground it measured 1.78:1 and the privacy line under the button was all but invisible.
 *
 * ⚠️ I fixed the SUBMIT BUTTON for exactly this reason in phase 5 and stopped there, which is
 * why this survived another three deploys. Every colour the deep band sets is restored here, not
 * only the ones that happened to be noticed.
 */
.qform-deep .qform-h { color: var(--blue-700); }
.qform-deep p, .qform-deep .qform-sub, .qform-deep .fineprint { color: var(--muted); }
.qform-deep label, .qform-deep .consent label { color: var(--heading); }
.qform-deep h1, .qform-deep h2, .qform-deep h3 { color: var(--heading); }
.qform-deep .form-error { color: #C0362C; }

/* ── the sub-page body ───────────────────────────────────────────────────────────────────── */
/* ⚠️ Two columns of ticks, so ten points do not run as one tall stripe down the page. */
.svc-more {
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
  gap: .65rem 2rem;
}
.svc-more li { font-size: 1rem; }

.svc-other { text-decoration: none; display: grid; align-content: start; gap: .55rem; }
.svc-other-icon { inline-size: 2.2rem; block-size: 2.2rem; color: var(--blue-600); }
.svc-other-icon svg { inline-size: 100%; block-size: 100%; }
.svc-other h3 { margin: 0; }
.svc-other p { color: var(--muted); margin: 0; font-size: .93rem; line-height: 1.6; }

/* ── narrower windows ────────────────────────────────────────────────────────────────────── */
@media (max-width: 62rem) {
  .svc-block, .svc-hero-split { grid-template-columns: minmax(0, 1fr); }
  /* ⛔ The zig-zag has to stop when there is only one column, or every other service reads
        art-then-words while the rest read words-then-art. */
  .svc-block-flip .svc-say { order: 0; }
}

/* ══ THE PORTFOLIO, REBUILT TO PAGE 34 ════════════════════════════════════════════════════ */

/* ── the capabilities grid (p34) ─────────────────────────────────────────────────────────── */
.cap-h {
  margin: 1.5rem 0 .7rem; font-family: var(--font-display); font-weight: 700; font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.caps {
  list-style: none; margin: 0 0 1.5rem; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: .6rem;
}
.cap {
  display: grid; grid-template-columns: auto 1fr; gap: .7rem; align-items: center;
  padding: .6rem .85rem; border-radius: 10px;
  background: var(--blue-50); border: 1px solid var(--line-soft);
  font-size: .9rem; color: var(--heading); line-height: 1.4;
}
.cap-icon {
  inline-size: 26px; block-size: 26px; border-radius: 7px; display: grid; place-items: center;
  background: var(--card); color: var(--blue-600); box-shadow: var(--shadow-sm);
}
.cap-icon svg { inline-size: .95rem; block-size: .95rem; }
.caps-tight { grid-template-columns: minmax(0, 1fr); gap: .45rem; margin-block-end: 1rem; }

/* ⚠️ On a deep band the chips invert; the showcase blocks alternate band colour down the page. */
.band-deep .cap { background: rgba(255, 255, 255, .05); border-color: rgba(255, 255, 255, .14);
                  color: #DCE9FF; }
.band-deep .cap-icon { background: rgba(140, 180, 255, .16); color: #A9C8FF; box-shadow: none; }
.band-deep .cap-h { color: var(--blue-300); }

/* ── the rest of the work ────────────────────────────────────────────────────────────────── */
/* ⚠️ The delivered project stands on its own row; the reserved slots sit three across
      beneath it. See the comment in portfolio.html for why they are not one grid. */
.works { align-items: start; }
.works-reserved { margin-block-start: 1.25rem; }
.works > .work:only-child { max-inline-size: 44rem; }
.work {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  padding: 1.4rem; display: grid; align-content: start; gap: .6rem; box-shadow: var(--shadow-sm);
}
.work-top { margin: 0; display: flex; align-items: center; gap: .7rem; }
.work-icon { inline-size: 2rem; block-size: 2rem; color: var(--blue-600); flex: 0 0 auto; }
.work-icon svg { inline-size: 100%; block-size: 100%; }
.work-service {
  font-family: var(--font-display); font-weight: 700; font-size: .72rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.work-tag {
  margin-inline-start: auto; font-family: var(--font-display); font-size: .62rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase; color: var(--accent-ink);
  background: var(--blue-100); border-radius: 3px; padding: .2rem .42rem;
}
.work h3 { margin: 0; font-size: 1.12rem; line-height: 1.25; }
.work-blurb { margin: 0; color: var(--muted); line-height: 1.65; font-size: .95rem; }
.work-note {
  margin: 0; font-size: .88rem; color: var(--blue-700); font-weight: 600;
  padding-inline-start: .8rem; border-inline-start: 3px solid var(--blue-300);
}
/*
 * ⛔ A RESERVED SLOT LOOKS RESERVED. Dashed and unshadowed, so nobody skimming the page mistakes
 * it for a delivered project — the difference has to be visible, not only readable.
 */
.work-reserved { border-style: dashed; background: var(--blue-50); box-shadow: none; }
.work-reserved h3 { color: var(--muted); }

/* ══ THE BLOG INDEX, TO PAGE 36 ════════════════════════════════════════════════ */
/*
 * ⚠️ `.post-cover`, `.post-link` and `.post-meta` already existed and are reused deliberately —
 * the cover box already carries the 16/9 aspect ratio these cards need. Only the card around them
 * is new.
 */
.posts {
  display: grid; gap: clamp(1.25rem, 2.5vw, 1.75rem);
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
}
.post {
  display: grid; grid-template-rows: auto 1fr; align-content: start;
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  overflow: hidden; box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.post:hover, .post:focus-within {
  transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--blue-300);
}
/* ⚠️ Scoped to .post — the article page uses .post-cover too and does not want a chip on it. */
.post .post-cover { position: relative; }
/* ⭐ A post with no cover keeps the same box in flat colour, so one uncovered article cannot
      make its row a different height from the others. */
.post-cover-none { background: linear-gradient(135deg, var(--blue-100), var(--blue-50)); }
.post-cat {
  position: absolute; inset-block-end: .7rem; inset-inline-start: .7rem;
  background: var(--card); color: var(--blue-700); border-radius: 999px;
  padding: .28rem .7rem; font-family: var(--font-display); font-weight: 700;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  box-shadow: var(--shadow-sm);
}
.post-body { padding: 1.15rem 1.25rem 1.25rem; display: grid; align-content: start; gap: .55rem; }
.post-title { margin: 0; font-size: 1.1rem; line-height: 1.3; }
.post-sum { margin: 0; color: var(--muted); font-size: .93rem; line-height: 1.6; }
/* ⚠️ The existing rule carries a bottom margin for the article page; inside a card the grid
      gap does the spacing, so it would double up. */
.post .post-meta { margin: .15rem 0 0; font-size: .82rem; }
.post-by { font-weight: 600; color: var(--heading); }

/* ══ THE CONTACT PAGE, TO PAGE 38 ══════════════════════════════════════════════ */
.reach {
  margin-block-end: clamp(1.75rem, 3.5vw, 2.5rem);
  display: grid; gap: clamp(1.25rem, 3vw, 2rem); align-items: center;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  background: var(--blue-50); border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg); padding: clamp(1.25rem, 3vw, 1.85rem);
}
.reach-claim { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: .35rem .9rem; }
.reach-pin {
  grid-row: 1 / 3; inline-size: 2.6rem; block-size: 2.6rem; border-radius: 12px;
  display: grid; place-items: center; background: var(--blue-600); color: #fff;
}
.reach-pin svg { inline-size: 1.35rem; block-size: 1.35rem; }
.reach-claim strong { align-self: end; font-family: var(--font-display); font-size: 1.08rem;
                      color: var(--heading); line-height: 1.25; }
.reach-claim > span:last-child { align-self: start; font-size: .92rem; color: var(--muted);
                                 line-height: 1.55; }
.reach-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: .9rem 1.5rem;
}
.reach-list li { display: grid; grid-template-columns: auto 1fr; gap: .1rem .7rem;
                 align-content: start; }
.reach-icon {
  grid-row: 1 / 3; inline-size: 2rem; block-size: 2rem; border-radius: 9px;
  display: grid; place-items: center; background: var(--card); color: var(--blue-700);
  border: 1px solid var(--line-soft);
}
.reach-icon svg { inline-size: 1rem; block-size: 1rem; }
.reach-k {
  align-self: end; font-family: var(--font-display); font-weight: 700; font-size: .68rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.reach-list li > :last-child { align-self: start; font-size: .93rem; color: var(--heading);
                               line-height: 1.5; }
.reach-list a { color: var(--accent-ink); text-decoration: none; }
.reach-list a:hover { text-decoration: underline; }

@media (max-width: 52rem) {
  .reach { grid-template-columns: minmax(0, 1fr); }
}

/* ══ THE HERO, TO p5 — THE ERP DASHBOARD AND THE FOUR-POINT CARD ═══════════════════════════ */

/* ── the dashboard that replaces the record card (p4, p5) ────────────────────────────────── */
.erpd {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 22px 60px rgba(11, 42, 107, .13);
  font-size: .72rem; line-height: 1.35;
}
.erpd-rail {
  background: var(--blue-950); display: grid; align-content: start; gap: .3rem;
  padding: .8rem .55rem;
}
.erpd-r {
  inline-size: 30px; block-size: 30px; border-radius: 9px; display: grid; place-items: center;
  color: #6E86B5;
}
.erpd-r svg { inline-size: 1.05rem; block-size: 1.05rem; }
.erpd-r.on { background: rgba(91, 150, 255, .2); color: #DCE9FF; }

.erpd-body { padding: .85rem .9rem 1rem; display: grid; gap: .6rem; align-content: start; }
.erpd-top { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.erpd-h { font-family: var(--font-display); font-weight: 700; font-size: .95rem; color: var(--heading); }
.erpd-range {
  display: inline-flex; align-items: center; gap: .35rem; color: var(--muted);
  border: 1px solid var(--line-soft); border-radius: 7px; padding: .22rem .5rem; font-size: .66rem;
}
.erpd-range svg { inline-size: .8rem; block-size: .8rem; }

.erpd-tiles {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .45rem;
}
.erpd-tiles li {
  border: 1px solid var(--line-soft); border-radius: 9px; padding: .45rem .5rem;
  display: grid; gap: .1rem; min-inline-size: 0;
}
.et-k { font-size: .58rem; color: var(--muted); white-space: nowrap; overflow: hidden;
        text-overflow: ellipsis; }
.et-v { font-family: var(--font-display); font-weight: 700; font-size: .78rem; color: var(--heading);
        font-variant-numeric: tabular-nums; white-space: nowrap; }
/* ⚠️ #1A7F50, the same green the About mock had to be corrected to — 5.01:1 at this size. */
.et-d { font-size: .56rem; color: #1A7F50; }

.erpd-mid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .45rem; }
.erpd-card { border: 1px solid var(--line-soft); border-radius: 9px; padding: .5rem .55rem;
             min-inline-size: 0; }
.erpd-ch { margin: 0 0 .4rem; font-weight: 700; font-size: .64rem; color: var(--heading); }

.erpd-chart { inline-size: 100%; block-size: 3.6rem; display: block; }
.ec-fill { fill: rgba(47, 99, 216, .12); }
.ec-line { fill: none; stroke: var(--blue-600); stroke-width: 2; stroke-linejoin: round;
           vector-effect: non-scaling-stroke; }
.ec-dot circle { fill: var(--blue-600); }
.erpd-axis { margin: .2rem 0 0; display: flex; justify-content: space-between;
             font-size: .54rem; color: var(--muted); }

.erpd-donut { display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: center; }
/*
 * ⭐ ONE conic-gradient, and the stops are the legend's own four numbers: 43 / 24 / 18 / 15.
 * ⛔ If a number changes in the template, change it here too — they are the same fact twice.
 */
.ed-ring {
  inline-size: 3.1rem; block-size: 3.1rem; border-radius: 50%;
  background: conic-gradient(var(--blue-600) 0 43%, var(--blue-500) 43% 67%,
                             #3EA98A 67% 85%, var(--blue-300) 85% 100%);
  mask: radial-gradient(circle, transparent 54%, #000 55%);
  -webkit-mask: radial-gradient(circle, transparent 54%, #000 55%);
}
.ed-key { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem;
          font-size: .58rem; color: var(--muted); }
.ed-key b { color: var(--heading); font-variant-numeric: tabular-nums;
            display: inline-block; min-inline-size: 2.1em; }

.erpd-feed, .erpd-acts { list-style: none; margin: 0; padding: 0; display: grid; gap: .3rem; }
.erpd-feed li { display: grid; grid-template-columns: auto 1fr auto; gap: .45rem;
                align-items: center; }
.ef-i, .ea-i {
  inline-size: 18px; block-size: 18px; border-radius: 5px; display: grid; place-items: center;
  background: var(--blue-50); color: var(--blue-700); flex: 0 0 auto;
}
.ef-i svg, .ea-i svg { inline-size: .66rem; block-size: .66rem; }
.ef-t { font-size: .6rem; color: var(--heading); min-inline-size: 0; }
.ef-t em { display: block; font-style: normal; font-size: .52rem; color: var(--muted); }
.ef-s { font-size: .54rem; color: var(--muted); white-space: nowrap; }
.ef-s.ok { color: #1A7F50; }
.erpd-acts li { display: grid; grid-template-columns: auto 1fr auto; gap: .45rem;
                align-items: center; font-size: .6rem; color: var(--heading); }
.ea-go {
  inline-size: 6px; block-size: 6px; border-inline-end: 1.5px solid var(--muted);
  border-block-start: 1.5px solid var(--muted); transform: rotate(45deg);
}

/* ── the headline (p5) ──────────────────────────────────────────────────── */
/*
 * ⭐ p5: "animation must be in our brand theme". The three lines rise in sequence on arrival —
 * the site's own --ease and --dur, not a new motion vocabulary borrowed from the reference.
 * ⚠️ THE LINES ARE BLOCKS, which is what fixes the wrap: p5 sets "Beyond CRM. / One System for
 * Your / Entire Business." and left to itself the browser orphans "Business." on its own line.
 */
.hero-h .hl { display: block; }
.hero-h .hl { animation: hlRise .6s var(--ease-out) both; }
.hero-h .hl:nth-child(2) { animation-delay: .08s; }
.hero-h .hl:nth-child(3) { animation-delay: .16s; }
@keyframes hlRise { from { opacity: 0; transform: translateY(.4em); } }

/* ── the highlighted box and the four points (p5, p6) ─────────────────────────────────────── */
.hero-box { display: grid; grid-template-columns: auto 1fr; gap: .2rem .85rem; }
.hero-box-icon {
  grid-row: 1 / 3; inline-size: 2.5rem; block-size: 2.5rem; border-radius: 50%;
  display: grid; place-items: center; background: var(--blue-100); color: var(--blue-700);
}
.hero-box-icon svg { inline-size: 1.2rem; block-size: 1.2rem; }
.hero-box-h { align-self: end; font-size: 1.05rem; line-height: 1.25; }

/*
 * ⚠️ p5 DRAWS THE FOUR POINTS AS A CARD, not the bare row that shipped: white ground, a hairline
 * between each pair, and each icon in a circular tint.
 */
.hero-points {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); border-block-start: 1px solid var(--line-soft);
  padding: clamp(.9rem, 2vw, 1.25rem) clamp(.5rem, 1.5vw, 1rem);
  gap: 0;
}
.hero-points li { padding-inline: clamp(.6rem, 1.5vw, 1.1rem); }
.hero-points li + li { border-inline-start: 1px solid var(--line-soft); }
.hero-points .hp-icon { border-radius: 50%; }
/* ⭐ The one warm note in the band, and it is in p5's artwork rather than an invention. */
.hp-warm { background: #FDF0DC; color: #A66A15; }

@media (max-width: 62rem) {
  .erpd-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .erpd-mid { grid-template-columns: minmax(0, 1fr); }
  /* ⛔ A vertical rule between stacked rows draws a line down the side of nothing. */
  .hero-points li + li { border-inline-start: 0; border-block-start: 1px solid var(--line-soft);
                         padding-block-start: .9rem; }
  .hero-points li { padding-block-end: .9rem; }
  .hero-points li:last-child { padding-block-end: 0; }
}
@media (max-width: 40rem) {
  .erpd-rail { grid-auto-flow: column; justify-content: start; padding: .5rem; }
  .erpd { grid-template-columns: minmax(0, 1fr); }
}

/* ══ 01 AND 03, RE-READ AGAINST p9 AND p11 ═══════════════════════════════════════ */

/*
 * ⭐ p9 DRAWS INTERLOCKING JIGSAW PIECES. The first pass used plain rounded squares, which loses
 * the whole point of the picture — these are things that SHOULD fit together, and one that does
 * not. The tabs and sockets are clip-paths on the same four tiles, so nothing about the layout
 * changes; only the silhouette does.
 * ⚠️ A clip-path removes the border and the shadow with it, so the pieces carry their edge as a
 * background layer instead.
 */
.bt {
  border: 0; box-shadow: none;
  background: linear-gradient(var(--card), var(--card)) padding-box,
              linear-gradient(var(--line-soft), var(--line-soft)) border-box;
  filter: drop-shadow(0 2px 5px rgba(11, 42, 107, .08));
}
/* top-left: tab on the right, tab on the bottom */
.bt-1 { clip-path: polygon(0 0, 78% 0, 78% 12%, 92% 12%, 92% 0, 100% 0, 100% 74%,
                           88% 74%, 88% 88%, 100% 88%, 100% 100%, 0 100%); }
/* top-right: socket on the left */
.bt-2 { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 88%, 12% 88%, 12% 74%, 0 74%); }
/* bottom-left: socket on the top */
.bt-3 { clip-path: polygon(0 0, 74% 0, 74% 12%, 88% 12%, 88% 0, 100% 0, 100% 100%, 0 100%); }
/*
 * ⛔ THE ODD ONE OUT KEEPS ITS TABS BUT NONE OF THEM MEET. It is rotated away from the grid, so
 * the eye reads it as the piece that will not go in — which is the sentence the section opens on.
 */
.bt-miss {
  clip-path: polygon(0 0, 100% 0, 100% 72%, 86% 72%, 86% 86%, 100% 86%, 100% 100%, 0 100%,
                     0 86%, 14% 86%, 14% 72%, 0 72%);
  /* ⛔ RESTATED, not inherited: the `.bt` rule above sets a white background and comes later in
        the file than phase 3's, so without this the odd piece renders white and the section
        stops making its point. */
  background: #E8593F;
  color: #fff;
  filter: drop-shadow(0 6px 14px rgba(232, 89, 63, .3));
}
/* ⭐ The spark marks p9 sets beside the piece that does not fit. */
.bt-spark {
  position: absolute; inset-block-start: 4%; inset-inline-end: -6%;
  inline-size: 2.4rem; block-size: 2.4rem;
}
.bt-spark i {
  position: absolute; inline-size: 2px; border-radius: 2px; background: #E8A33D;
  inset-block-start: 50%; inset-inline-start: 50%; transform-origin: 50% 100%;
}
.bt-spark i:nth-child(1) { block-size: .7rem; transform: translate(-50%, -100%) rotate(-24deg); }
.bt-spark i:nth-child(2) { block-size: .95rem; transform: translate(-50%, -100%) rotate(4deg); }
.bt-spark i:nth-child(3) { block-size: .7rem; transform: translate(-50%, -100%) rotate(32deg); }

/*
 * ⭐ p11 GIVES EACH TRADE ITS OWN MARK AND ITS OWN COLOUR. Five identical blue chips lose the
 * point the drawing makes — different businesses, one system. The five hues are the reference's.
 */
.hub-chip {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .45rem .5rem .45rem .95rem;
}
.hub-chip i {
  inline-size: 26px; block-size: 26px; border-radius: 8px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .75); flex: 0 0 auto;
}
.hub-chip i svg { inline-size: .9rem; block-size: .9rem; }
.hc-1 { background: #E8F0FE; border-color: #C4D9FB; color: #14379A; }
.hc-2 { background: #E6F5EC; border-color: #C2E6D2; color: #17663F; }
.hc-3 { background: #F0ECFD; border-color: #D9CEF8; color: #4A36A8; }
.hc-4 { background: #FCE9EC; border-color: #F5CBD3; color: #9A2740; }
.hc-5 { background: #FDF2E2; border-color: #F6E0BE; color: #8A5A10; }
.hc-1 i { color: #2F63D8; } .hc-2 i { color: #1E8E5A; } .hc-3 i { color: #6B52D6; }
.hc-4 i { color: #C4364F; } .hc-5 i { color: #B4761A; }

/* ══ 02 AND 03, REBUILT TO p10 AND p11 ═══════════════════════════════════════════ */

/* ── 02: the flow (p10) ─────────────────────────────────────────────────────── */
/*
 * ⛔ IT WAS THREE UNRELATED ROWS. p10 is a compact centred composition; what shipped had
 * invisible dotted stubs for arrows, no band behind the stages, oversized blue chevrons and
 * enough dead vertical space that nothing read as one track.
 */
.oneflow {
  margin-block: clamp(1.5rem, 3vw, 2.25rem) 0;
  max-inline-size: 62rem; margin-inline: auto;
}
.pipe-in { margin-block-end: 0; gap: .5rem; }
.pipe-in li { padding-block-end: 2.6rem; gap: .45rem; }
.fi-icon, .pi-icon { inline-size: 42px; block-size: 42px; }

/*
 * ⭐ A REAL ARROW, WITH A HEAD. The dashed rule is the shaft and the ::before is the head — p10
 * draws an arrowhead on every one, and without it the stubs read as stray dots.
 */
.pipe-in li::after {
  block-size: 2.1rem; inline-size: 0;
  background: none;
  border-inline-start: 2px dashed var(--blue-300);
  animation: none;
}
.pipe-in li::before {
  content: ''; position: absolute; inset-block-end: -1px; inset-inline-start: calc(50% - 4px);
  inline-size: 8px; block-size: 8px;
  border-inline-end: 2px solid var(--blue-500); border-block-end: 2px solid var(--blue-500);
  transform: rotate(45deg);
  animation: dropTip 1.8s var(--ease) infinite;
}
.pipe-in li:nth-child(2)::before { animation-delay: .22s; }
.pipe-in li:nth-child(3)::before { animation-delay: .44s; }
.pipe-in li:nth-child(4)::before { animation-delay: .66s; }
@keyframes dropTip {
  0%, 100% { transform: rotate(45deg) translate(0, 0); opacity: .5; }
  50%      { transform: rotate(45deg) translate(3px, 3px); opacity: 1; }
}

/*
 * ⭐ THE BAND p10 SETS BEHIND THE STAGES, tapering at both ends. It is what makes five circles
 * read as one track. ⛔ --blue-50 on a white band is invisible; this is a tint you can see.
 */
.pipe-rail {
  position: relative; background: none; padding: 1.5rem 1rem 0;
  gap: .5rem; isolation: isolate;
}
.pipe-rail::before {
  content: ''; position: absolute; z-index: -1;
  inset-block-start: 1.05rem; block-size: 4.1rem; inset-inline: 0;
  background: #E4EEFC;
  clip-path: polygon(3% 0, 97% 0, 100% 100%, 0 100%);
  border-radius: 4px;
}
.pr-icon { inline-size: 52px; block-size: 52px; box-shadow: 0 2px 6px rgba(11, 42, 107, .1); }
.pr-label { font-size: .84rem; }
/* ⭐ p10's chevron is small and quiet, not a heavy blue mark competing with the icons. */
.pipe-rail li + li::before {
  inset-block-start: 24px; inline-size: 9px; block-size: 9px;
  border-color: var(--blue-500);
  border-inline-end-width: 2px; border-block-start-width: 2px;
}
.pipe-pull {
  margin-block-start: 1.5rem; max-inline-size: 34rem;
}

/* ── 03: the hub (p11) ─────────────────────────────────────────────────────── */
/*
 * ⭐ p11 JOINS BOTH SIDES TO THE CENTRE. Five columns now: chips, wires, core, wires,
 * capabilities — so the curves have somewhere to live and the core is the biggest thing in the
 * row rather than the smallest.
 */
.hub {
  grid-template-columns: minmax(0, auto) minmax(2rem, 1fr) auto minmax(2rem, 1fr) minmax(0, auto);
  gap: 0 .5rem; align-items: center;
}
.hub-wires { inline-size: 100%; block-size: 16rem; display: block; }
.hub-wires path {
  fill: none; stroke: var(--blue-300); stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 3 7; animation: hubRun 2.6s linear infinite;
}
.hub-wires-r path { animation-direction: reverse; }
@keyframes hubRun { to { stroke-dashoffset: -20; } }
.hub-core {
  inline-size: clamp(10rem, 20vw, 15rem);
  box-shadow: 0 0 0 10px rgba(228, 238, 252, .55), var(--shadow-md);
}
.hub-core span { font-size: clamp(1.1rem, 2.2vw, 1.55rem); }
.hub-trades { justify-items: stretch; }
.hub-trades li { display: flex; justify-content: flex-end; }
.hub-gets li { white-space: nowrap; }

@media (max-width: 62rem) {
  /* ⛔ The curves connect columns; with one column they would run through the words. */
  .hub-wires { display: none; }
  .hub { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .hub-trades li { justify-content: center; }
  .pipe-in li::before { display: none; }
  .pipe-rail::before { display: none; }
  .pipe-rail { padding-block-start: 0; }
}

/* ⛔ Everything above that moves is decoration and stops here. */
@media (prefers-reduced-motion: reduce) {
  .pipe-in li::before, .hub-wires path { animation: none; }
  .hub-wires path { stroke-dasharray: 3 7; }
}

/* ── the dashboard, arriving ──────────────────────────────────────────────────── */
/*
 * ⭐ THE LINE DRAWS ITSELF and the rows come in behind it. A hero visual that is simply there is
 * a picture; one that assembles reads as a system doing something, which is the whole claim.
 * ⚠️ It runs ONCE, on arrival. A chart that redraws forever is a distraction beside a headline.
 */
.ec-line { stroke-dasharray: 420; }
.js-reveal .erpd.in .ec-line { animation: ecDraw 1.1s var(--ease-out) .15s forwards; }
/* ⛔ WITHOUT THE SCRIPT THE LINE IS SIMPLY DRAWN. The dash offset that hides it is only ever
      set inside .js-reveal, the same rule the whole site follows: the class that hides is the
      script's to add. */
.ec-line { stroke-dashoffset: 0; }
.js-reveal .erpd:not(.in) .ec-line { stroke-dashoffset: 420; }
@keyframes ecDraw { to { stroke-dashoffset: 0; } }

.js-reveal .erpd .ec-dot circle { opacity: 0; }
.js-reveal .erpd.in .ec-dot circle { animation: ecDot .3s var(--ease-out) forwards; }
.js-reveal .erpd.in .ec-dot circle:nth-child(1) { animation-delay: .25s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(2) { animation-delay: .35s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(3) { animation-delay: .45s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(4) { animation-delay: .55s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(5) { animation-delay: .65s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(6) { animation-delay: .75s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(7) { animation-delay: .85s; }
.js-reveal .erpd.in .ec-dot circle:nth-child(8) { animation-delay: .95s; }
@keyframes ecDot { to { opacity: 1; } }

/* the two lower panels come in row by row */
.js-reveal .erpd .erpd-feed li, .js-reveal .erpd .erpd-acts li { opacity: 0; }
.js-reveal .erpd.in .erpd-feed li, .js-reveal .erpd.in .erpd-acts li {
  animation: ecRow .4s var(--ease-out) forwards;
}
.js-reveal .erpd.in :is(.erpd-feed, .erpd-acts) li:nth-child(1) { animation-delay: .30s; }
.js-reveal .erpd.in :is(.erpd-feed, .erpd-acts) li:nth-child(2) { animation-delay: .38s; }
.js-reveal .erpd.in :is(.erpd-feed, .erpd-acts) li:nth-child(3) { animation-delay: .46s; }
.js-reveal .erpd.in :is(.erpd-feed, .erpd-acts) li:nth-child(4) { animation-delay: .54s; }
@keyframes ecRow { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ⛔ All of it off, and nothing left hidden. */
@media (prefers-reduced-motion: reduce) {
  .js-reveal .erpd .ec-line { stroke-dashoffset: 0; animation: none; }
  .js-reveal .erpd .ec-dot circle,
  .js-reveal .erpd .erpd-feed li, .js-reveal .erpd .erpd-acts li {
    opacity: 1; animation: none;
  }
}

/* ══ THE SIX CASE STUDIES, EACH WITH ITS OWN COLOUR ══════════════════════════════════ */
/*
 * ⛔ SIX IDENTICAL BLOCKS READ AS ONE TEMPLATE REPEATED, not as six different systems. Every
 * case study was the same composition in the same blue, so the page said "we made one thing" on a
 * page whose whole job is to say "we configure this six different ways".
 *
 * ⚠️ ONE VARIABLE, SET SIX TIMES. `--tone` is declared on the section and everything inside
 * reads it, so a seventh trade is three lines rather than a new block of rules.
 * ⚠️ Every one of these was checked against white and against the deep band; the darkest of the
 * six is the one the ticks and the small labels use, so none of them drops below AA.
 */
.showcase-band { --tone: var(--blue-600); --tone-ink: var(--blue-800); --tone-wash: #E8F0FE; }
.tone-hsp { --tone: #2F63D8; --tone-ink: #14379A; --tone-wash: #E8F0FE; }
.tone-re  { --tone: #1E8E5A; --tone-ink: #14603D; --tone-wash: #E6F5EC; }
.tone-aut { --tone: #6B52D6; --tone-ink: #4A36A8; --tone-wash: #F0ECFD; }
.tone-cln { --tone: #C4364F; --tone-ink: #8E223A; --tone-wash: #FCE9EC; }
.tone-law { --tone: #B4761A; --tone-ink: #7E5210; --tone-wash: #FDF2E2; }
.tone-rec { --tone: #0E7C86; --tone-ink: #0A5960; --tone-wash: #E3F4F5; }

/* the sector badge, the ticks and the accent rule all take it */
/* ⛔ WHITE TEXT SITS ON --tone-ink, NEVER ON --tone. Two of the six fills fail AA under white:
      the green measures 4.14:1 and the amber 3.79:1 against the 4.5 this size needs. The ink
      variant is 6.77:1 at its worst. --tone is for rules, icons and fills that carry no text. */
.showcase-band .stage-n { background: var(--tone-ink); color: #fff; }
.showcase-band .stage { color: var(--tone-ink); }
.showcase-band .cap-icon { color: var(--tone); }
.showcase-band .cap { border-color: color-mix(in srgb, var(--tone) 22%, transparent);
                      background: var(--tone-wash); }
.showcase-band .cta { background: var(--tone-ink); }
.showcase-band .cta:hover { background: color-mix(in srgb, var(--tone-ink) 86%, #000); }
.showcase-band .pull { border-inline-start-color: var(--tone); }

/* ⭐ and the device wears it, which is what makes the six read apart at a glance */
.showcase-band .device-bar { background: var(--tone-wash); }
.showcase-band .device-url { color: var(--tone-ink); }
/* ⛔⛔ THIS SAID aria-pressed AND NEVER MATCHED ANYTHING. showcase.js sets aria-selected, which is
   what a tablist uses — so the active tab stayed blue in all six trades and the colour pass looked
   like it had worked everywhere except the one element that carries the state. The underline is a
   border-block-end-color on .dtab, not an ::after, so that half was wrong twice over. */
.showcase-band .dtab[aria-selected="true"] { color: var(--tone-ink); border-block-end-color: var(--tone); }
.showcase-band .dstat-n { color: var(--tone-ink); }
.showcase-band .device-brand { color: var(--tone-ink); }

/* ⚠️ On the deep band the wash would be a light block on navy; the tone alone carries it. */
.band-deep.showcase-band .cap { background: rgba(255, 255, 255, .05);
                                border-color: rgba(255, 255, 255, .14); }
.band-deep.showcase-band .stage { color: color-mix(in srgb, var(--tone) 55%, #fff); }

/* ── the services index hero and router (p29) ────────────────────────────────────── */
/* ⚠️ A single centred column, like the reference — the record card that used to sit beside it is
      the one p4 marks for removal, and this page's job is to get somebody into one of the four. */
.svc-index-hero .inner { max-inline-size: 46rem; text-align: center; }
.svc-index-hero h1 { font-size: clamp(2.1rem, 4.4vw, 3.3rem); line-height: 1.08;
                     margin-block-end: 1rem; }
.svc-index-hero .lede { margin-inline: auto; margin-block-end: 1.5rem; }
.svc-index-hero .stage { justify-content: center; }
.svc-index-hero .hero-actions { justify-content: center; }

/* ⭐ The reference's three stat chips. ⛔ Nothing goes in this row without a source. */
.svc-stats {
  list-style: none; margin: 0 auto 1.75rem; padding: 0;
  display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center;
}
.svc-stats li {
  display: inline-flex; align-items: baseline; gap: .45rem;
  background: var(--card); border: 1px solid var(--line-soft); border-radius: 999px;
  padding: .5rem 1rem; font-size: .9rem; color: var(--muted); box-shadow: var(--shadow-sm);
}
.svc-stats b { font-family: var(--font-display); font-size: 1.05rem; color: var(--blue-700);
               font-variant-numeric: tabular-nums; }

.svc-router { text-align: center; }
.svc-router .stage { justify-content: center; }
.svc-router h2 { font-size: clamp(1.5rem, 3vw, 2.2rem); margin-block-end: .7rem; }
.svc-router p { margin: 0 auto 1.5rem; max-inline-size: 46ch; }

/* ══ SIX TRADES, SIX DIFFERENT SCREENS ═══════════════════════════════════════════════ */
/*
 * ⛔⛔ COLOUR WAS THE WRONG FIX. Giving each case study its trade's hue left six blocks that were
 * still the same component: the same Today / Record / Money tabs, the same "what today needs"
 * heading, the same four tiles with the same four labels. Only the numbers changed. On a page
 * whose entire claim is "one platform, configured six different ways", that showed one system six
 * times and argued against itself.
 *
 * ⭐ EACH TRADE NOW HAS THE VIEW IT ACTUALLY LIVES IN — a service board, a listings board, a bay
 * board, a diary, a ledger and two pipelines. Different tabs, different components, different
 * shapes, different motion. They share only the contract showcase.js needs.
 *
 * ⚠️ EVERY SCREEN STILL READS --tone, so the trade's colour runs through its own furniture. The
 * colour was never wrong; it was just never going to be enough on its own.
 *
 * ⛔ WHITE TEXT GOES ON --tone-ink, NEVER ON --tone. Two of the six fills (green 4.14:1, amber
 * 3.79:1) fail AA under white, which is why every filled chip below uses the ink.
 */

/* ⛔ THE DEVICE USED TO CHANGE HEIGHT AS THE TABS CYCLED, which jogged the whole band every three
   seconds. The panel now floors at the tallest of the three. */
.showcase-band .device-panel { min-block-size: 17.5rem; }
.showcase-band .device-h { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; }
.showcase-band .device-h span { font-family: var(--font-body); letter-spacing: 0; text-transform: none; font-size: 0.78rem; font-weight: 500; }
/* ⛔ .dstat-n is display:block at 1.5rem — it was written for the old four-tile panel. The count
   in a panel heading has to restate both, or every heading breaks onto two lines with a huge
   number under it. */
.showcase-band .device-h b { display: inline; font-size: 0.95rem; line-height: 1; font-weight: 700; color: var(--tone-ink); font-variant-numeric: tabular-nums; }

/* shared semantic states — a deposit outstanding reads the same in every trade */
.hx-ev-ok, .by-stage-ok, .dy-s-done, .pl-st-done { background: #E7F3EA; color: #2C6B41; }
.hx-ev-warn, .by-stage-warn, .dy-s-booked { background: #FBF0DC; color: #7E5210; }

/* ── HOSPITALITY: the service board ──────────────────────────────────────────────── */
.hx-sittings, .hx-tables, .hx-facts, .hx-events { list-style: none; margin: 0; padding: 0; }
/* ⭐ ONE ROW PER SITTING. The tables count sat on a second line under the covers, which is
   51px across three sittings and is what was pushing the floor plan off the screen. */
.hx-sittings li { display: grid; grid-template-columns: 3.1rem 1fr auto auto;
                  gap: 0.6rem; align-items: center; padding-block: 0.32rem; }
.hx-time { font-family: var(--font-display); font-weight: 700; font-size: 0.84rem; color: var(--heading); font-variant-numeric: tabular-nums; }
.hx-bar { block-size: 0.6rem; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.hx-bar i { display: block; block-size: 100%; border-radius: 999px; background: var(--tone); transform-origin: left center; animation: dGrow .7s var(--ease) both; }
.hx-full .hx-bar i { background: var(--tone-ink); }
.hx-n { font-size: 0.78rem; font-weight: 600; color: var(--heading); white-space: nowrap; }
.hx-t { font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.hx-t::before { content: "\00b7"; padding-inline-end: 0.35rem; opacity: .55; }
.hx-full .hx-n::after { content: " \00b7 full"; color: var(--tone-ink); font-weight: 700; }
.hx-floor-h { margin-block-start: 1rem; }
.hx-tables { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.hx-tb { inline-size: 1.85rem; block-size: 1.85rem; border-radius: 6px; display: grid; place-items: center; font-size: 0.72rem; font-weight: 700; font-family: var(--font-display); }
.hx-tb-free { background: var(--line-soft); color: var(--muted); }
.hx-tb-seated { background: var(--tone-ink); color: #fff; }
.hx-tb-held { background: var(--tone-wash); color: var(--tone-ink); box-shadow: inset 0 0 0 1.5px var(--tone); }
.hx-guest { display: flex; align-items: center; gap: 0.7rem; margin-block-end: 0.8rem; }
.hx-av, .dy-av, .pl-av { inline-size: 2.3rem; block-size: 2.3rem; border-radius: 50%; display: grid; place-items: center; background: var(--tone-ink); color: #fff; font-family: var(--font-display); font-weight: 700; font-size: 0.8rem; flex: 0 0 auto; }
.hx-guest-b { display: flex; flex-direction: column; }
.hx-guest-b b { font-family: var(--font-display); font-size: 0.95rem; color: var(--heading); }
.hx-guest-b i { font-style: normal; font-size: 0.72rem; color: var(--muted); }
.hx-visits { margin-inline-start: auto; background: var(--tone-wash); color: var(--tone-ink); font-size: 0.7rem; font-weight: 700; border-radius: 999px; padding: 0.25rem 0.6rem; }
.hx-facts li, .dy-facts li, .pl-facts li { display: flex; justify-content: space-between; gap: 1rem; padding-block: 0.45rem; border-block-end: 1px solid var(--line-soft); font-size: 0.82rem; }
.hx-facts li:last-child, .dy-facts li:last-child, .pl-facts li:last-child { border-block-end: 0; }
.hx-facts span:first-child, .dy-facts span:first-child, .pl-facts span:first-child { color: var(--muted); }
.hx-facts span:last-child, .dy-facts span:last-child, .pl-facts span:last-child { color: var(--heading); font-weight: 600; text-align: end; }
/* ⭐ the one coloured line on the screen, because it is the one a kitchen cannot get wrong */
.hx-flag { margin: 0.75rem 0 0; font-size: 0.78rem; font-weight: 600; color: #A62B22; background: #FBE7E4; border-radius: 7px; padding: 0.5rem 0.65rem; }
.hx-events li { display: grid; grid-template-columns: 3.4rem 1fr auto; gap: 0.6rem; align-items: center; padding-block: 0.55rem; border-block-end: 1px solid var(--line-soft); }
.hx-events li:last-child { border-block-end: 0; }
.hx-ev-d { font-family: var(--font-display); font-weight: 700; font-size: 0.78rem; color: var(--tone-ink); }
.hx-ev-n { font-size: 0.82rem; color: var(--heading); font-weight: 600; display: flex; flex-direction: column; }
.hx-ev-n i { font-style: normal; font-weight: 400; font-size: 0.72rem; color: var(--muted); }
.hx-ev-s, .by-stage, .dy-s-k, .ls-state, .mt-state { font-family: var(--font-display); font-size: 0.6rem; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; border-radius: 3px; padding: 0.2rem 0.42rem; white-space: nowrap; }
.hx-ev-new { background: var(--tone-wash); color: var(--tone-ink); }

/* ── REAL ESTATE: the listings board ─────────────────────────────────────────────── */
.ls-grid, .ls-track, .ls-offers { list-style: none; margin: 0; padding: 0; }
.ls-grid li { display: grid; grid-template-columns: 3.6rem 1fr auto; gap: 0.7rem; align-items: center; padding-block: 0.5rem; border-block-end: 1px solid var(--line-soft); }
.ls-grid li:last-child { border-block-end: 0; }
/* ⚠️ THE "PHOTOGRAPH" IS DRAWN, not fetched. Six stock images would be six requests and six things
   to go stale; at this size a roofline in a gradient reads as a property shot. */
.ls-shot { block-size: 2.6rem; border-radius: 6px; background: linear-gradient(160deg, var(--tone-wash), color-mix(in srgb, var(--tone) 30%, #fff)); position: relative; overflow: hidden; }
.ls-shot::after { content: ""; position: absolute; inset-inline: 12%; inset-block-end: 0; block-size: 55%; background: color-mix(in srgb, var(--tone-ink) 45%, transparent); clip-path: polygon(0 45%, 22% 45%, 22% 12%, 48% 12%, 48% 62%, 74% 62%, 74% 30%, 100% 30%, 100% 100%, 0 100%); }
.ls-shot-2::after { clip-path: polygon(0 30%, 30% 30%, 30% 60%, 62% 60%, 62% 8%, 100% 8%, 100% 100%, 0 100%); }
.ls-shot-3::after { clip-path: polygon(0 60%, 18% 60%, 18% 25%, 55% 25%, 55% 48%, 100% 48%, 100% 100%, 0 100%); }
.ls-body { display: flex; flex-direction: column; min-inline-size: 0; }
.ls-name { font-family: var(--font-display); font-size: 0.86rem; color: var(--heading); }
.ls-meta { font-style: normal; font-size: 0.71rem; color: var(--muted); }
.ls-price { font-size: 0.8rem; color: var(--tone-ink); font-variant-numeric: tabular-nums; }
.ls-avail { background: #E7F3EA; color: #2C6B41; }
.ls-offer { background: #FBF0DC; color: #7E5210; }
.ls-let { background: var(--line-soft); color: var(--muted); }
.ls-brief { color: var(--muted); }
/* a viewing history is a line, not a table */
.ls-track { position: relative; padding-inline-start: 1.15rem; }
.ls-track::before { content: ""; position: absolute; inset-block: 0.6rem; inset-inline-start: 0.28rem; inline-size: 2px; background: var(--line-soft); }
.ls-track li { position: relative; padding-block: 0.42rem; font-size: 0.82rem; display: flex; gap: 0.7rem; }
.ls-track li::before { content: ""; position: absolute; inset-inline-start: -1.05rem; inset-block-start: 0.75rem; inline-size: 0.62rem; block-size: 0.62rem; border-radius: 50%; background: var(--line); box-shadow: 0 0 0 3px var(--card); }
.ls-v-done::before { background: var(--tone); }
.ls-v-now::before { background: var(--tone-ink); box-shadow: 0 0 0 3px var(--card), 0 0 0 5px var(--tone-wash); }
.ls-v-next::before { background: var(--card); box-shadow: 0 0 0 3px var(--card), inset 0 0 0 2px var(--line); }
.ls-when { color: var(--muted); flex: 0 0 3.3rem; font-variant-numeric: tabular-nums; }
.ls-what { color: var(--heading); }
.ls-v-now .ls-what { font-weight: 700; color: var(--tone-ink); }
.ls-v-next .ls-what { color: var(--muted); }
.ls-ask { color: var(--muted); font-variant-numeric: tabular-nums; }
.ls-offers li { display: grid; grid-template-columns: 5.4rem 1fr auto; gap: 0.3rem 0.6rem; align-items: center; padding-block: 0.5rem; }
.ls-o-who { font-size: 0.8rem; font-weight: 600; color: var(--heading); }
.ls-o-bar { block-size: 0.55rem; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.ls-o-bar i { display: block; block-size: 100%; border-radius: 999px; background: var(--line); transform-origin: left center; animation: dGrow .7s var(--ease) both; }
.ls-o-lead .ls-o-bar i { background: var(--tone); }
.ls-o-amt { font-size: 0.8rem; font-weight: 700; color: var(--heading); font-variant-numeric: tabular-nums; }
.ls-o-tag { grid-column: 2 / -1; font-size: 0.7rem; color: var(--muted); margin-block-start: -0.15rem; }
.ls-o-lead .ls-o-tag { color: var(--tone-ink); font-weight: 600; }

/* ── GARAGES: the bay board ──────────────────────────────────────────────────────── */
.by-bays, .by-hist, .by-lines { list-style: none; margin: 0; padding: 0; }
/* ⛔ FOUR COLUMNS, NOT THREE. `.by-stage` pins itself to the last column of row 1, so with
   three columns the job description — auto-placed, and earlier in the DOM — was bumped into
   row 2 and wrapped mid-phrase. Bay, plate, job and stage across the top; track underneath. */
.by-bays li { display: grid; grid-template-columns: 3.1rem auto 1fr auto;
              gap: 0.14rem 0.55rem; align-items: center; padding-block: 0.3rem;
              border-block-end: 1px solid var(--line-soft); }
.by-bays li:last-child { border-block-end: 0; }
.by-no { font-family: var(--font-display); font-size: 0.64rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
/* ⭐ A PLATE DRAWN AS A PLATE says "built for a garage" faster than any sentence beside it. */
.by-plate { font-family: var(--font-display); font-weight: 800; font-size: 0.78rem; letter-spacing: 0.04em; color: #1B2330; background: #F2C230; border-radius: 3px; padding: 0.16rem 0.4rem; box-shadow: inset 0 0 0 1px rgba(0,0,0,.35); white-space: nowrap; }
.by-plate-lg { font-size: 1rem; padding: 0.3rem 0.6rem; }
/* ⛔ THE JOB GETS THE WHOLE ROW. The stage badge was beside it, squeezing this to 53px so
   "Brakes and discs" broke over three lines. It sits under the row now, next to the
   progress track it actually describes. */
.by-job { grid-column: 3 / -1; font-size: 0.82rem; color: var(--heading);
          font-weight: 600; }
.by-track { grid-row: 2; grid-column: 2 / 4; display: flex; align-items: center;
            gap: 0.25rem; }
.by-track i { inline-size: 0.42rem; block-size: 0.42rem; border-radius: 50%; background: var(--line); animation: dPop .34s var(--ease) both; }
.by-track i + i { margin-inline-start: 0.16rem; }
.by-s1 { background: var(--tone) !important; }
.by-track i:nth-child(2) { animation-delay: .07s; } .by-track i:nth-child(3) { animation-delay: .14s; }
.by-track i:nth-child(4) { animation-delay: .21s; } .by-track i:nth-child(5) { animation-delay: .28s; }
.by-stage { justify-self: end; grid-row: 2; grid-column: 4; }
.by-stage-go { background: var(--tone-wash); color: var(--tone-ink); }
.by-free { opacity: .62; }
.by-empty { grid-column: 2 / -1; font-size: 0.8rem; color: var(--muted); }
.by-veh { display: flex; align-items: center; gap: 0.7rem; margin-block-end: 0.9rem; flex-wrap: wrap; }
.by-veh-m { font-size: 0.8rem; color: var(--muted); }
.by-hist li, .by-lines li { display: grid; grid-template-columns: 5.6rem 1fr auto; gap: 0.6rem; padding-block: 0.45rem; border-block-end: 1px solid var(--line-soft); font-size: 0.8rem; }
.by-hist li:last-child, .by-lines li:last-child { border-block-end: 0; }
.by-h-d, .by-l-q { color: var(--muted); font-variant-numeric: tabular-nums; }
.by-h-w, .by-l-w { color: var(--heading); font-weight: 600; }
.by-h-km, .by-l-a { color: var(--tone-ink); font-weight: 600; font-variant-numeric: tabular-nums; text-align: end; }
.by-lines li { grid-template-columns: 1fr 4rem auto; }
.by-total { display: flex; justify-content: space-between; margin: 0.6rem 0 0; padding-block-start: 0.55rem; border-block-start: 2px solid var(--tone); font-size: 0.86rem; color: var(--heading); }
.by-total b { font-family: var(--font-display); font-variant-numeric: tabular-nums; }
.by-stamp { margin: 0.6rem 0 0; font-size: 0.72rem; font-weight: 700; color: #2C6B41; background: #E7F3EA; border-radius: 6px; padding: 0.4rem 0.55rem; }

/* ── CLINICS: the diary ──────────────────────────────────────────────────────────── */
/* ⚠️ Rows are the hour and a block spans as many as the treatment takes, so a ninety-minute
   session really is twice a forty-five-minute one. Scrolls sideways rather than crushing. */
.dy-grid { display: grid; grid-template-columns: 2.3rem repeat(2, minmax(6.1rem, 1fr)); gap: 0.4rem; overflow-x: auto; }
.dy-times { display: grid; grid-template-rows: repeat(6, 2.15rem); padding-block-start: 1.35rem; }
.dy-times span { font-size: 0.66rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.dy-room { min-inline-size: 6.1rem; }
.dy-room-h { margin: 0 0 0.35rem; font-family: var(--font-display); font-size: 0.64rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.dy-lanes { display: grid; grid-template-rows: repeat(6, 2.15rem); gap: 0.18rem; background: repeating-linear-gradient(to bottom, var(--line-soft) 0 1px, transparent 1px 2.15rem); }
.dy-appt { border-radius: 6px; padding: 0.3rem 0.4rem; display: flex; flex-direction: column; justify-content: center; overflow: hidden; animation: dRow .34s var(--ease) both; }
.dy-appt b { font-size: 0.71rem; font-weight: 700; line-height: 1.2; }
.dy-appt i { font-style: normal; font-size: 0.64rem; opacity: .85; }
.dy-laser { background: var(--tone-ink); color: #fff; }
.dy-consult { background: var(--tone-wash); color: var(--tone-ink); }
.dy-peel, .dy-micro { background: var(--line-soft); color: var(--heading); }
.dy-client { display: flex; align-items: center; gap: 0.7rem; margin-block-end: 0.7rem; }
.dy-client-b { display: flex; flex-direction: column; }
.dy-client-b b { font-family: var(--font-display); font-size: 0.95rem; color: var(--heading); }
.dy-client-b i { font-style: normal; font-size: 0.72rem; color: var(--muted); }
/* ⭐ consent gets a stamp, not a row — it is the difference between a clinic that can answer a
   complaint and one that cannot */
.dy-consent { margin: 0 0 0.7rem; font-size: 0.76rem; font-weight: 600; color: #2C6B41; background: #E7F3EA; border-radius: 6px; padding: 0.45rem 0.6rem; }
.dy-facts { list-style: none; margin: 0; padding: 0; }
.dy-prog { color: var(--tone-ink); font-weight: 700; }
.dy-sessions { list-style: none; margin: 0; padding: 0; }
.dy-sessions li { display: grid; grid-template-columns: 1.5rem 4.4rem 1fr; gap: 0.6rem; align-items: center; padding-block: 0.48rem; border-block-end: 1px solid var(--line-soft); font-size: 0.82rem; }
.dy-sessions li:last-child { border-block-end: 0; }
.dy-s-n { inline-size: 1.5rem; block-size: 1.5rem; border-radius: 50%; display: grid; place-items: center; font-size: 0.7rem; font-weight: 700; font-family: var(--font-display); background: var(--line-soft); color: var(--muted); }
.dy-s-done .dy-s-n { background: var(--tone-ink); color: #fff; }
.dy-s-booked .dy-s-n { background: var(--tone-wash); color: var(--tone-ink); box-shadow: inset 0 0 0 1.5px var(--tone); }
.dy-s-d { color: var(--muted); font-variant-numeric: tabular-nums; }
.dy-s-k { color: var(--heading); font-weight: 600; }
.dy-s-todo .dy-s-d, .dy-s-todo .dy-s-k { color: var(--muted); font-weight: 400; }

/* ── PROFESSIONAL FIRMS: the ledger ──────────────────────────────────────────────── */
.mt-rows, .mt-bill { list-style: none; margin: 0; padding: 0; }
.mt-rows li { display: grid; grid-template-columns: 3.9rem 1fr 1.6rem 1rem auto; gap: 0.5rem; align-items: center; padding-block: 0.55rem; border-block-end: 1px solid var(--line-soft); }
.mt-rows li:last-child { border-block-end: 0; }
.mt-ref { font-family: var(--font-display); font-size: 0.76rem; font-weight: 700; color: var(--tone-ink); font-variant-numeric: tabular-nums; }
.mt-name { font-size: 0.8rem; color: var(--heading); font-weight: 600; }
.mt-who { inline-size: 1.6rem; block-size: 1.6rem; border-radius: 50%; display: grid; place-items: center; background: var(--tone-wash); color: var(--tone-ink); font-family: var(--font-display); font-size: 0.62rem; font-weight: 800; }
.mt-conf { color: #2C6B41; display: grid; place-items: center; }
.mt-conf svg { inline-size: 0.85rem; block-size: 0.85rem; }
.mt-open { background: var(--tone-wash); color: var(--tone-ink); }
.mt-state.mt-billing { background: #FBF0DC; color: #7E5210; }
/* ⛔ THE ONLY REAL TABLE IN THE SIX MOCKS, and it scrolls rather than crushing its narrative
   column on a phone. Tabular numerals because the hours column has to read as a column. */
.mt-scroll { overflow-x: auto; }
/* ⛔ NO MINIMUM WIDTH. 20rem was wider than the column, so the table overflowed, the hours
   were cut off and a scrollbar appeared inside a picture of an application. The narrative
   wraps instead, which is what a narrow ledger does in real life. */
.mt-time { inline-size: 100%; border-collapse: collapse; font-size: 0.74rem;
           min-inline-size: 0; table-layout: auto; }
.mt-time td:nth-child(3) { overflow-wrap: anywhere; }
.mt-time th { text-align: start; font-family: var(--font-display); font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); padding-block: 0.35rem; border-block-end: 1px solid var(--line); }
.mt-time td { padding-block: 0.45rem; border-block-end: 1px solid var(--line-soft); color: var(--heading); }
.mt-time td:first-child { color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.mt-time td:nth-child(2) { font-weight: 700; color: var(--tone-ink); }
.mt-time .mt-h { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mt-time tfoot td { border-block-end: 0; border-block-start: 2px solid var(--tone); font-weight: 700; padding-block-start: 0.5rem; }
.mt-bill li { display: grid; grid-template-columns: 8.2rem 1fr auto; gap: 0.6rem; align-items: center; padding-block: 0.55rem; }
.mt-b-k { font-size: 0.78rem; color: var(--muted); }
.mt-b-bar { block-size: 0.55rem; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.mt-b-bar i { display: block; block-size: 100%; border-radius: 999px; background: var(--tone); transform-origin: left center; animation: dGrow .7s var(--ease) both; }
.mt-b-v { font-size: 0.8rem; font-weight: 700; color: var(--heading); font-variant-numeric: tabular-nums; }
.mt-note { margin: 0.5rem 0 0; font-size: 0.72rem; color: var(--muted); }

/* ── RECRUITMENT: two pipelines and the join ─────────────────────────────────────── */
/* ⭐ The blurb beside this says "roles on one side, candidates on the other". Rather than assert
   it, the screen is that: two stacks, and the submission bar across both where they meet. */
/* ⛔ minmax(0, 1fr), NOT 1fr. `1fr` is `minmax(auto, 1fr)`, so a track refuses to shrink below
   its widest child and the whole grid pushes past its parent. The ellipsis on the chip inside
   it could never fire, because the column was never asked to be narrower. */
.pl-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
          gap: 0.6rem; }
.pl-col { min-inline-size: 0; }
.pl-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.pl-h { margin: 0 0 0.45rem; font-family: var(--font-display); font-size: 0.62rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.pl-chip { display: flex; align-items: center; gap: 0.5rem; background: var(--tone-wash);
           border-radius: 7px; padding: 0.34rem 0.55rem; font-size: 0.76rem;
           color: var(--heading);  min-inline-size: 0; }
.pl-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                min-inline-size: 0; }
.pl-chip b { font-family: var(--font-display); font-size: 0.92rem; color: var(--tone-ink); font-variant-numeric: tabular-nums; min-inline-size: 1.4rem; }
/* ⛔ THE SIDEWAYS SLIDE IS GONE. `.pl-chip` is already a `.d-rows > li`, so it fades and rises
   with every other row; these two rules were a SECOND animation on the same element, and
   because they start at translateX(±10px) a chip that never advances past frame one sits
   permanently offset — which on the right-hand column pushes the count out under a
   clipping edge. Measured on the live page: all four chips stuck at translateX(10px).
   ⚠️ Two animations on one element is one too many; the row animation was always enough. */
.pl-col-b { text-align: end; }
.pl-col-b .pl-chip { flex-direction: row-reverse; }
.pl-join { display: flex; align-items: center; justify-content: center; gap: 0.55rem; flex-wrap: wrap; margin: 0.7rem 0 0; padding: 0.55rem 0.7rem; border-radius: 8px; background: var(--tone-ink); color: #fff; text-align: center; }
.pl-join-k { font-family: var(--font-display); font-size: 0.62rem; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; opacity: .82; }
.pl-join b { font-family: var(--font-display); font-size: 1.15rem; line-height: 1; font-variant-numeric: tabular-nums; }
/* ⚠️ nowrap: wrapped to a second line inside a solid coloured bar it read as a mistake. */
.pl-join-w { font-size: 0.7rem; opacity: .82; white-space: nowrap; }
.pl-cand { display: flex; align-items: center; gap: 0.7rem; margin-block-end: 0.75rem; }
.pl-cand-b { display: flex; flex-direction: column; }
.pl-cand-b b { font-family: var(--font-display); font-size: 0.95rem; color: var(--heading); }
.pl-cand-b i { font-style: normal; font-size: 0.72rem; color: var(--muted); }
.pl-stage { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem; }
.pl-stage li { font-size: 0.68rem; font-weight: 600; border-radius: 999px; padding: 0.22rem 0.5rem; background: var(--line-soft); color: var(--muted); }
.pl-st-now { background: var(--tone-ink); color: #fff; }
.pl-facts { list-style: none; margin: 0; padding: 0; }
.pl-place { list-style: none; margin: 0; padding: 0; }
.pl-place li { display: grid; grid-template-columns: 1fr auto; gap: 0.15rem 0.6rem; padding-block: 0.5rem; border-block-end: 1px solid var(--line-soft); }
.pl-place li:last-child { border-block-end: 0; }
.pl-p-r { font-size: 0.82rem; font-weight: 600; color: var(--heading); }
.pl-p-c { grid-row: 2; font-size: 0.72rem; color: var(--muted); }
.pl-p-f { grid-row: 1 / span 2; align-self: center; font-size: 0.82rem; font-weight: 700; color: var(--tone-ink); font-variant-numeric: tabular-nums; }
.pl-total { display: flex; justify-content: space-between; margin: 0.6rem 0 0; padding-block-start: 0.55rem; border-block-start: 2px solid var(--tone); font-size: 0.86rem; color: var(--heading); }
.pl-total b { font-family: var(--font-display); font-variant-numeric: tabular-nums; }

/* ── the motion, and every bit of it switched off on request ─────────────────────── */
.d-rows > li, .d-rows > tr { animation: dRow .34s var(--ease) both; }
@keyframes dGrow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes dPop { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .d-rows > li, .d-rows > tr, .hx-bar i, .ls-o-bar i, .mt-b-bar i, .by-track i,
  .dy-appt, .pl-chip,
  .bar i, .bar i.d-grow { animation: none; }
}

/* ⚠️ On a narrow screen the listing cards and the bay rows drop their side columns rather than
   squeezing them to two characters wide. */
@media (max-width: 30rem) {
  .ls-grid li { grid-template-columns: 3rem 1fr; }
  .ls-grid .ls-state { grid-column: 2; justify-self: start; }
  .by-bays li { grid-template-columns: 3.1rem auto 1fr; }
  .by-bays .by-stage { grid-row: 2; grid-column: 2 / -1; justify-self: start; }
  .by-bays .by-track { grid-row: 3; }
  .mt-rows li { grid-template-columns: 3.9rem 1fr auto; }
  .mt-rows .mt-who, .mt-rows .mt-conf { display: none; }
  .pl-join { flex-direction: column; gap: 0.15rem; }
}

/* ══ THE PORTFOLIO AS MACHINE MOCKUPS ════════════════════════════════════════════════ */
/*
 * Built against wayoneit.com/portfolio, which the client sent as the reference — its CRM tab shows
 * each system as a laptop on a tinted stage with the whole dashboard inside it.
 *
 * ⛔⛔ SIX DIFFERENT PANELS BEHIND ONE TAB BAR STILL READ AS ONE THING. Only a third of each
 * system was on screen at a time, in the same small white window in the same place, so the work
 * that made the six different was invisible unless somebody clicked. The tabs are gone.
 *
 * ⭐ THE RAIL IS THE DIFFERENCE ANYBODY SEES FIRST. Service / Guests / Events is a restaurant;
 * Bays / Vehicles / Quotes is a workshop; Matters / Time / Billing is a law firm. It reads before
 * a word of the copy does, and it was missing entirely.
 *
 * ⚠️ THE SHELL IS SVG, THE SCREEN IS HTML. The deck's taper, hinge and thumb notch are curves — a
 * path draws them exactly. The screen stays HTML so the dashboards are real text that reflows on a
 * phone, rather than a flat drawing that scales into a blur.
 */

/* ── the two block layouts ───────────────────────────────────────────────────────── */
/* ⚠️ min-inline-size:0 — a grid child holding a table or a wide board refuses to shrink below
   its content without it, and the whole band overflows sideways on a phone. */
.sblock-dev { min-inline-size: 0; }
.sblock { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
/* ⚠️ 66rem, not 62. Between the two the machine was small enough that the board lost a
   section anyway, and a stacked block with a full-width machine reads better than a
   cramped side-by-side one. */
@media (min-width: 66rem) {
  /* ⭐ THE SCREEN IS 16:10, SO WIDTH IS HEIGHT. Every board was losing its last section below the
     fold — the floor plan, the free bay, the submission bar. 1.32fr takes the machine from
     662x413 to 834x521 at 1440, which is exactly the ~105px that was missing. */
  .sblock-split { grid-template-columns: 0.68fr 1.32fr; }
  .sblock-flip { grid-template-columns: 1.32fr 0.68fr; }
  /* ⚠️ ORDER, NOT DIRECTION. The copy stays first in the DOM on all six, so a keyboard and a
     screen reader always meet the heading before the picture, whichever side it is drawn on. */
  .sblock-flip .sblock-copy { order: 2; }
}

/* ── the stage, which is where the trade's colour now lives ──────────────────────── */
/* ⚠️ THE BAND ALTERNATES BETWEEN THREE NEUTRAL GROUNDS and the STAGE carries the tone. Tinting
   the band itself put two near-identical pale blues next to each other. */
.mstage {
  background: color-mix(in srgb, var(--tone) 11%, #fff);
  border-radius: 18px; padding: clamp(1rem, 2.2vw, 1.9rem) clamp(.85rem, 2vw, 1.6rem);
}
.band-deep .mstage { background: rgba(255, 255, 255, .07); }

/* ── the machine ─────────────────────────────────────────────────────────────────── */
.laptop { position: relative; }
.lid {
  margin-inline: 3.5%; padding: 9px 9px 0; border-radius: 13px 13px 3px 3px;
  background: linear-gradient(180deg, #26303F 0%, #151C29 100%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .34), 0 20px 44px rgba(11, 42, 107, .24);
}
.lid-screen { background: var(--card); border-radius: 5px; overflow: hidden; }
/* the chin, carrying the address the way the reference's carries a model name */
.chin {
  display: block; text-align: center; padding: .42rem 0 .5rem;
  font-family: var(--font-display); font-size: .56rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: rgba(255, 255, 255, .44);
}
.deck { display: block; inline-size: 100%; block-size: auto; margin-block-start: -1px; }

/* ── the application inside the screen ───────────────────────────────────────────── */
/* ⛔ 9rem, MEASURED AGAINST THE LONGEST MODULE. "Par levels & orders" and "Supplier lead times"
   are the two longest of the seventy-two, and a rail that cuts one of them off mid-word is the
   fastest way to tell somebody they are looking at a drawing. */
.app { display: grid; grid-template-columns: 9rem minmax(0, 1fr); }
.app-rail { background: var(--tone-ink); color: #fff; padding: .85rem .7rem 1.2rem; }
.rail-mark {
  display: grid; place-items: center; inline-size: 1.75rem; block-size: 1.75rem;
  border-radius: 6px; background: rgba(255, 255, 255, .18); margin-block-end: .85rem;
  font-family: var(--font-display); font-size: .56rem; font-weight: 800; letter-spacing: .04em;
}
.rail-nav { list-style: none; margin: 0; padding: 0; display: grid; gap: .08rem; }
/* ⚠️ A module name longer than the rail is a nav item that has run off its own sidebar. The
   names are kept short at source; this is the floor under that. */
.rail-nav li {
  overflow: hidden; text-overflow: ellipsis;
  font-size: .66rem; padding: .3rem .45rem; border-radius: 5px;
  color: rgba(255, 255, 255, .78); white-space: nowrap;
}
/* ⛔ 4.5:1 AGAINST THE RAIL. The inactive items are the darkest text in the mock; anything
   fainter than .72 alpha on the six tone inks drops under AA on at least two of them. */
.rail-nav li.on { background: rgba(255, 255, 255, .19); color: #fff; font-weight: 700; }

/* ⭐ THE QUESTION IS "IS THERE ROOM IN THIS BOX", not "how wide is the window". The working area
   is 494px inside a 1320px page, and a different width again in the flipped layout — which is
   exactly the case a media query cannot answer and @container can. */
.app-main { min-inline-size: 0; padding: .8rem .95rem .9rem; container-type: inline-size; }
.app-top {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  padding-block-end: .45rem; margin-block-end: .6rem; border-block-end: 1px solid var(--line-soft);
}
.app-brand { font-family: var(--font-display); font-weight: 700; font-size: .95rem; color: var(--tone-ink); }
.app-user { font-size: .68rem; color: var(--muted); }

/* ⭐ ALL THREE SCREENS AT ONCE: the board across the top, the other two beneath it — which is
   both how a dashboard is laid out and the whole reason the tabs were wrong. */
.app-body { display: flex; flex-wrap: wrap; gap: .8rem; align-items: flex-start; }
/* ⛔⛔ 16:10, AND IT CLIPS. The screen used to let its content set its height, so it came out
   portrait — "there is no laptop in world with that size", and there is not. A real screen has a
   fixed ratio and shows as much as fits, so this one does too. Anything past the fold is cut, the
   same way a screenshot of a real machine is. */
.lid-screen { aspect-ratio: 16 / 10; overflow: hidden; }
.app { block-size: 100%; }
.app-rail { overflow: hidden; }
.app-main { display: flex; flex-direction: column; gap: .4rem; min-block-size: 0; }
.app-crumb { font-size: .64rem; color: var(--muted); margin-inline-end: auto; }
.app-crumb i { font-style: normal; opacity: .55; padding-inline: .15rem; }

/* ── the five tiles the reference opens on ───────────────────────────────────────── */
.dsh-tiles { list-style: none; margin: 0; padding: 0; display: grid;
              grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .45rem; }
.tile { border-radius: 8px; padding: .38rem .48rem; min-inline-size: 0; }
.tile b { display: block; font-family: var(--font-display); font-size: .96rem; line-height: 1.05;
          font-variant-numeric: tabular-nums; }
.tile i { display: block; font-style: normal; font-size: .6rem; line-height: 1.25;
          margin-block-start: .15rem; opacity: .85; }
/* ⛔ FIVE PASTELS WITH THEIR OWN INK, not the trade tone — the reference's row is deliberately a
   rainbow, and five shades of one hue would read as one tile repeated five times. Every pair is
   checked against AA in p10. */
.tile-a { background: #E4F1E8; color: #1F5B39; }
.tile-b { background: #FCEEDD; color: #8A5310; }
.tile-c { background: #FBE7EC; color: #97223C; }
.tile-d { background: #E6EEFC; color: #1B3F97; }
.tile-e { background: #E2F1F2; color: #0C575E; }

/* ── the quick actions ───────────────────────────────────────────────────────────── */
/* ⚠️ Verbs only, and only the ones true of that trade — "Scan a part", "Capture consent",
   "Run conflict check". Generic buttons would put the six back where they started. */
.dsh-do { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .28rem; }
.dsh-do li { font-size: .61rem; font-weight: 600; padding: .2rem .48rem; border-radius: 999px;
              color: var(--tone-ink); background: var(--card); white-space: nowrap;
              border: 1px solid color-mix(in srgb, var(--tone) 32%, transparent); }

/* ── the chart beside the working board ──────────────────────────────────────────── */
/* ⛔⛔ TWO COLUMNS, ONE ROW, BOTH FULL HEIGHT. This was a two-row grid with the chart's row set
   to `auto`, so at a real 1440px desktop the chart took 155px of a 235px split and the panel
   underneath it got 59px — a card needing 292-465px rendered as half an avatar and a wrapped name,
   on all six machines. Three content regions under the tiles is one too many at 662x413, and the
   reference's own CRM screens show the tiles, the actions and ONE working area.
   ⚠️ `display: contents` on the wrapper is what lets the board, which arrives from an include
   inside it, sit in the grid as its own column. */
.dsh-split { flex: 1 1 auto; min-block-size: 0; display: grid; gap: .65rem;
              grid-template-columns: minmax(0, 12.5rem) minmax(0, 1fr); }
.dsh-chart { min-block-size: 0; }
.dsh-work { display: contents; }
.dsh-chart { min-inline-size: 0; border: 1px solid var(--line-soft); border-radius: 9px;
              padding: .7rem .75rem; }
/* ⚠️ Fills the column rather than standing at a fixed height in the middle of it. */
.bars { list-style: none; margin: 0; padding: 0; display: grid; grid-auto-flow: column;
        grid-auto-columns: minmax(0, 1fr); gap: .26rem; block-size: 100%;
        min-block-size: 4.5rem; }
.dsh-chart { display: flex; flex-direction: column; }
.dsh-chart .bars { flex: 1 1 auto; }
.bars li { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: .22rem; min-inline-size: 0; }
.bar { display: flex; align-items: flex-end; block-size: 100%; }
.bar i { display: block; inline-size: 100%; border-radius: 3px 3px 0 0; background: var(--tone); }
/* ⚠️ scaleY, not scaleX — the shared .d-grow animation grows sideways, which on a column chart
   would make the bars slide in from the left instead of rising out of the axis. */
.bar i.d-grow { animation-name: dGrowY; transform-origin: bottom center; }
@keyframes dGrowY { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.bar-k { font-size: .57rem; color: var(--muted); text-align: center; }

/* ⚠️ EACH PANEL CLIPS ITSELF at its own bottom edge, which is what a screen does. It is the
   squeezing that looked broken, not the cutting. */
.dsh-work > .device-panel { min-block-size: 0; min-inline-size: 0; overflow: hidden;
                            border: 1px solid var(--line-soft); border-radius: 9px;
                            padding: .7rem .75rem; background: var(--card); }

/* ── the block that sits on navy carries its own inversions ──────────────────────── */
/* ⛔ EVERY ONE OF THESE WAS A REAL FAILURE ON THE DEEP BAND: a mid-purple button on navy, and
   caption text set in a grey that was chosen to sit on white. */
.band-deep.showcase-band .cta { background: #fff; color: var(--tone-ink); }
.band-deep.showcase-band .cta:hover { background: var(--on-deep); color: var(--tone-ink); }
.band-deep.showcase-band .cta-quiet { background: transparent; color: #fff;
                                      border-color: rgba(255, 255, 255, .45); }
.band-deep.showcase-band .cta-quiet:hover { background: rgba(255, 255, 255, .1); border-color: #fff; }
.band-deep.showcase-band .device-note, .band-deep.showcase-band .cap-h { color: var(--on-deep); }

/* ── the motion, now that no script drives it ────────────────────────────────────── */
/* ⛔ showcase.js used to replay these on every tab change. With every panel on display there is
   nothing to replay, so they run once — but they must run when the block is REACHED, not while it
   is still three screens below. reveal.js already adds .in; this just holds them until it does.
   ⚠️ Paused, not hidden: without JavaScript `.js-reveal` never lands and they simply play. */
.js-reveal .reveal .d-rows > li, .js-reveal .reveal .d-rows > tr,
.js-reveal .reveal .d-grow, .js-reveal .reveal .dy-appt,
.js-reveal .reveal .bar i { animation-play-state: paused; }
.js-reveal .reveal.in .d-rows > li, .js-reveal .reveal.in .d-rows > tr,
.js-reveal .reveal.in .d-grow, .js-reveal .reveal.in .dy-appt,
.js-reveal .reveal.in .bar i { animation-play-state: running; }
/* the stagger the script used to set row by row */
.d-rows > li:nth-child(2), .d-rows > tr:nth-child(2) { animation-delay: .05s; }
.d-rows > li:nth-child(3), .d-rows > tr:nth-child(3) { animation-delay: .10s; }
.d-rows > li:nth-child(4), .d-rows > tr:nth-child(4) { animation-delay: .15s; }
.d-rows > li:nth-child(5), .d-rows > tr:nth-child(5) { animation-delay: .20s; }
.d-rows > li:nth-child(6), .d-rows > tr:nth-child(6) { animation-delay: .25s; }
.d-rows > li:nth-child(n+7) { animation-delay: .30s; }

/* ── narrow screens: the rail lies down, the panels stack ────────────────────────── */
@media (max-width: 52rem) {
  .lid-screen { aspect-ratio: auto; }
  .dsh-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dsh-split { grid-template-columns: minmax(0, 1fr); }
  .app { grid-template-columns: minmax(0, 1fr); }
  /* the rail WRAPS, it does not swipe. Measured at 390px: twelve modules across 1,053px inside
     a 305px box left three showing and nine behind a sideways swipe INSIDE the mockup, and the
     nine included "Menus & recipes", "Stock & wastage" and "Par levels & orders" — the ones that
     make the trade what it is. The modules ARE the differentiation; hiding them keeps the frame
     and throws away the picture. */
  .app-rail { display: flex; align-items: center; gap: .45rem; padding: .55rem .6rem;
              flex-wrap: wrap; overflow-x: visible; }
  .rail-mark { margin-block-end: 0; flex: 0 0 auto; }
  .rail-nav { display: flex; flex-wrap: wrap; gap: .25rem; flex: 1 1 auto; min-inline-size: 0; }
  .app-body > .device-panel { flex-basis: 100%; }
  .mstage { border-radius: 14px; }
  /* and it stops pretending to be a laptop. Stacked, the lid measures 323x791 — a ratio of 0.41,
     portrait, and .deck was still drawing a laptop base underneath it. A lid is 16/10; a portrait
     tower on a hinge is the screensize objection all over again. The stacked layout is the
     readable one and it stays, so what changes is the frame: below this width the machine is an
     application window, which is what the thing on the screen has been all along. */
  .deck { display: none; }
  .lid { margin-inline: 0; border-radius: 13px; padding-block-end: 0; }
}

/* ══ WHERE WE OPERATE, AND THE MAP THAT SHOWS IT ═════════════════════════════════════ */
/*
 * ⭐ THE PINS ARE PLACED BY COORDINATES. The dots and the pins share one projection, declared once
 * in views.py, so Dubai lands on the Arabian coast as arithmetic rather than as a judgement — and
 * a third office is a line of lat/lon, not a redraw.
 *
 * ⛔ THE MAP IS NOT THE STATEMENT. A picture cannot be read aloud, searched or measured, and at
 * three degrees to a dot it should not be trusted to a mile. Every place on it is written out as
 * text beside it; the map is the illustration of the list, never the other way round.
 */
.where-map { min-inline-size: 0; }
.where-grid { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 62rem) { .where-grid { grid-template-columns: 0.92fr 1.08fr; } }
.where-copy h2 { font-size: clamp(1.9rem, 4vw, 3rem); line-height: 1.08; margin: 0 0 1rem; }
/* ⛔ 6.34:1 on the deep band. --blue-600 is the brand accent and only reaches 3.4:1 there, which
   is a pass for a heading and a fail for anything else — so the accent on navy is its own value. */
.where-hl { color: #6FB4FF; }

.where-list { list-style: none; margin: 1.7rem 0 0; padding: 0; display: grid; gap: .7rem; }
.where-list li {
  display: grid; grid-template-columns: 1fr auto; gap: .15rem .9rem;
  padding: .9rem 1.05rem; border-radius: 12px;
  background: rgba(255, 255, 255, .06); border: 1px solid rgba(255, 255, 255, .13);
}
.where-hq { border-color: rgba(111, 180, 255, .42); background: rgba(111, 180, 255, .1); }
.where-city {
  display: flex; align-items: center; gap: .5rem; color: #fff;
  font-family: var(--font-display); font-weight: 700; font-size: 1.05rem;
}
.where-city b {
  font-size: .58rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  background: #6FB4FF; color: #05204A; border-radius: 4px; padding: .16rem .38rem;
}
.where-country { color: var(--on-deep); font-size: .82rem; justify-self: end; align-self: center; }
.where-firm { grid-column: 1 / -1; font-size: .84rem; font-weight: 600; color: #6FB4FF; }
.where-role { grid-column: 1 / -1; font-size: .84rem; color: var(--on-deep-muted); line-height: 1.5; }
.where-note { margin: 1rem 0 0; font-size: .82rem; color: var(--on-deep-muted); }

/* ── the map itself ──────────────────────────────────────────────────────────────── */
.wmap { display: block; inline-size: 100%; block-size: auto; }
/* ⚠️ The land is deliberately quiet. It is a backdrop for two pins, and a bright landmass would
   compete with the only two marks on it that mean anything. */
.wmap-land { stroke: #2B569E; }
/* ⛔⛔ transform-box: fill-box. An SVG element's transform-origin defaults to the middle of the
   VIEWBOX, not the middle of the shape — so `scale()` on the halo would have swept it across the
   Atlantic instead of breathing in place. Nothing errors; it just animates the wrong thing. */
.wmap-pin { pointer-events: none; }
.wmap-dot { fill: #F5C542; }
.wmap-halo { fill: #F5C542; opacity: .2; transform-box: fill-box; transform-origin: center; }
.wmap-hq .wmap-dot { fill: #6FB4FF; }
.wmap-hq .wmap-halo { fill: #6FB4FF; opacity: .3; animation: wPulse 2.8s var(--ease) infinite; }
/* ⭐ ONE PIN PULSES, AND IT IS THE HEAD OFFICE. Two pulsing marks on a map is a light show; one is
   a statement about which of the two answers the phone. */
@keyframes wPulse {
  0%, 100% { transform: scale(1); opacity: .3; }
  50% { transform: scale(1.75); opacity: .06; }
}
@media (prefers-reduced-motion: reduce) { .wmap-hq .wmap-halo { animation: none; } }

@media (max-width: 40rem) {
  .where-list li { grid-template-columns: minmax(0, 1fr); }
  .where-country { justify-self: start; }
}

/* ══ THE BLOG, TO THE REFERENCE — NINE A PAGE ════════════════════════════════════════ */
/*
 * ⛔⛔ THE OLD PAGE SLICED [:6] AND DISCARDED THE REST, and its own comment said so: past six
 * articles the older ones became unreachable with no archive to reach them by. Nine a page with a
 * real pager is what the client asked for and it is also the fix for a bug already written down.
 *
 * ⭐ The shape is the reference's: a centred hero with a working search, one featured article
 * across the full width, category chips with counts, then the grid, then the pager.
 */
.blog-hero { text-align: center; padding-block-end: clamp(2rem, 4vw, 3rem); }
.blog-hero .stage { justify-content: center; }
.blog-hero h1 { font-size: clamp(2rem, 4.6vw, 3.4rem); margin-inline: auto; }
.blog-hero .lede { margin-inline: auto; }

/* ⭐ A REAL SEARCH, because a magnifying glass that does nothing is a lie about the page. */
.blog-search { display: flex; gap: .5rem; justify-content: center; flex-wrap: wrap;
               margin-block-start: 1.75rem; }
.blog-search input {
  font: inherit; font-size: .95rem; min-inline-size: min(22rem, 100%); flex: 0 1 22rem;
  min-block-size: 48px; padding: 0 1rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--card); color: var(--heading);
}
.blog-search input:focus-visible { outline: none; border-color: var(--blue-600);
                                   box-shadow: 0 0 0 3px var(--blue-100); }

/* ── the featured article ────────────────────────────────────────────────────────── */
.blog-feat { display: grid; gap: clamp(1.5rem, 3vw, 2.75rem); align-items: center; }
@media (min-width: 60rem) { .blog-feat { grid-template-columns: 1.05fr 1fr; } }
.feat-cover {
  display: block; aspect-ratio: 16 / 10; border-radius: 14px; overflow: hidden;
  background: var(--blue-100); box-shadow: var(--shadow-sm);
}
.feat-cover img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.feat-body { min-inline-size: 0; }
/* ⛔⛔ .post-cat IS position:absolute — it is pinned to the corner of a card's cover image. Reused
   in the featured article's meta row it had no positioned ancestor of its own, so it escaped to
   the nearest one, which is `.band`, and rendered as a stray chip floating at the bottom-left
   corner of the whole section. This is the THIRD time an absolutely positioned chip has escaped
   when borrowed (.quote-tag did it on the story card, .stage-n before that). ⚠️ Check `position`
   before reusing a class that was written to sit on top of an image. */
.feat-meta .post-cat { position: static; box-shadow: none; padding: .22rem .55rem;
                       background: var(--blue-50); }
.feat-meta { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
             margin: 0 0 .8rem; font-size: .8rem; color: var(--muted); }
/* ⚠️ White on --blue-800, not on --blue-600: the accent is 3.4:1 under white and this is a
   small label, not a heading. */
.feat-flag { font-family: var(--font-display); font-size: .6rem; font-weight: 800;
             letter-spacing: .1em; text-transform: uppercase; border-radius: 4px;
             padding: .22rem .5rem; background: var(--blue-800); color: #fff; }
.feat-title { font-size: clamp(1.5rem, 2.8vw, 2.1rem); line-height: 1.18; margin: 0 0 .8rem; }
.feat-sum { color: var(--body); margin: 0 0 1.4rem; font-size: 1.02rem; }
.feat-go { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin: 0;
           font-size: .85rem; color: var(--muted); }

/* ── the category chips ──────────────────────────────────────────────────────────── */
.bchips { display: flex; flex-wrap: wrap; gap: .45rem; margin-block-end: 2.25rem; }
.bchip {
  display: inline-flex; align-items: center; gap: .45rem; min-block-size: 40px;
  padding: 0 .95rem; border-radius: 999px; text-decoration: none; font-size: .88rem;
  font-weight: 600; color: var(--heading); background: var(--card);
  border: 1px solid var(--line); transition: border-color .2s var(--ease), color .2s var(--ease);
}
.bchip:hover { border-color: var(--blue-600); color: var(--accent-ink); }
.bchip b { font-weight: 700; font-size: .74rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.bchip.on { background: var(--blue-800); border-color: var(--blue-800); color: #fff; }
.bchip.on b { color: rgba(255, 255, 255, .8); }

.blog-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
             flex-wrap: wrap; margin-block-end: 1.5rem; }
.blog-head h2 { margin: 0; }
.blog-count { margin: 0; font-size: .88rem; color: var(--muted); }

/* ── the pager ───────────────────────────────────────────────────────────────────── */
/* ⚠️ 44px targets throughout. A pager is the one control on a blog that gets used on a phone
   with a thumb, and a row of 24px numbers is the classic way to fail that. */
.pager { display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
         gap: .4rem; margin-block-start: 2.75rem; }
.pager-nums { display: flex; align-items: center; gap: .3rem; }
.pager-go, .pager-p {
  display: inline-flex; align-items: center; justify-content: center;
  min-block-size: 44px; min-inline-size: 44px; padding-inline: .85rem; border-radius: 10px;
  text-decoration: none; font-weight: 600; font-size: .9rem; color: var(--heading);
  border: 1px solid var(--line); background: var(--card);
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.pager-go:hover, .pager-p:hover { border-color: var(--blue-600); color: var(--accent-ink); }
.pager-p { font-variant-numeric: tabular-nums; }
.pager-p.on { background: var(--blue-800); border-color: var(--blue-800); color: #fff; }
/* ⛔ A DEAD "PREVIOUS" IS NOT A LINK. It stays in the row so the pager does not jump when you
   reach the first page, but it is a <span> — there is nothing to focus and nothing to click. */
.pager-go.is-off { color: var(--muted); border-style: dashed; opacity: .7; }
.pager-gap { padding-inline: .3rem; color: var(--muted); }

/* ── and a search that finds nothing ─────────────────────────────────────────────── */
.blog-none { max-inline-size: 34rem; padding: clamp(1.5rem, 3vw, 2.25rem);
             border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.blog-none-h { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem;
               color: var(--heading); margin: 0 0 .5rem; }
.blog-none p { margin: 0 0 1rem; }

/* ══ SIX TRADES, SIX KINDS OF CHART ══════════════════════════════════════════════════ */
/*
 * ⛔ ALL SIX WERE COLUMN CHARTS. Six identical charts under six different rails is the same
 * mistake as six identical dashboards, only smaller and easier to miss.
 *
 * ⭐ Each is the chart that trade would actually draw. A workshop thinks in job types, not days.
 * A clinic's treatment mix is what decides the stock it holds. A practice lives on the billable
 * split, and a single total hides the only number a partner cares about. Recruitment is a funnel
 * from end to end.
 *
 * ⚠️ EVERY ONE READS --tone, so the chart is also the trade's colour.
 */

/* ── the line, for a trend rather than seven separate facts ──────────────────────── */
.ln { display: block; inline-size: 100%; flex: 1 1 auto; min-block-size: 3.5rem; }
.ln-fill { fill: color-mix(in srgb, var(--tone) 16%, transparent); stroke: none; }
.ln-path { fill: none; stroke: var(--tone); stroke-width: 2; stroke-linejoin: round;
           stroke-linecap: round; }
.ln-keys { list-style: none; display: flex; justify-content: space-between; margin: .3rem 0 0;
           padding: 0; font-size: .57rem; color: var(--muted); }

/* ── horizontal bars, for categories that are not days ───────────────────────────── */
/* ⚠️ space-evenly, not center: five short bars centred in a tall card leave a band of dead
   space above and below them, next to a board that is full to its edges. */
.hbars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column;
         justify-content: space-evenly; gap: .42rem; flex: 1 1 auto; }
.hbars li { display: grid; grid-template-columns: 4.6rem minmax(0, 1fr) 1.6rem; gap: .45rem;
            align-items: center; }
.hb-k { font-size: .6rem; color: var(--muted); white-space: nowrap; overflow: hidden;
        text-overflow: ellipsis; }
.hb-t { block-size: .5rem; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.hb-t i { display: block; block-size: 100%; border-radius: 999px; background: var(--tone);
          transform-origin: left center; }
.hb-v { font-size: .6rem; font-weight: 700; color: var(--tone-ink); text-align: end;
        font-variant-numeric: tabular-nums; }

/* ── the donut, for a mix ────────────────────────────────────────────────────────── */
.dnt { display: flex; align-items: center; gap: .7rem; flex: 1 1 auto; }
/* ⚠️ The hole is a mask, not a white circle on top — on a tinted card a painted hole is a
   visible disc of the wrong colour. */
.dnt-ring { inline-size: 4.2rem; block-size: 4.2rem; border-radius: 50%; flex: 0 0 auto;
            -webkit-mask: radial-gradient(circle, transparent 52%, #000 53%);
            mask: radial-gradient(circle, transparent 52%, #000 53%); }
.dnt-key { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; min-inline-size: 0; }
/* ⚠️ min-inline-size:0 and an ellipsis. A legend label wider than its own column pushes the
   donut out of the card, and the card is inside a picture of a screen. */
.dnt-key li { display: flex; align-items: center; gap: .35rem; font-size: .58rem;
              color: var(--muted); min-inline-size: 0; }
.dnt-key li span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dnt-key li::before { content: ""; inline-size: .5rem; block-size: .5rem; border-radius: 2px;
                      flex: 0 0 auto; }
.dnt-key .dnt-1::before { background: var(--tone-ink); }
.dnt-key .dnt-2::before { background: var(--tone); }
.dnt-key .dnt-3::before { background: color-mix(in srgb, var(--tone) 55%, #fff); }
.dnt-key .dnt-4::before { background: color-mix(in srgb, var(--tone) 28%, #fff); }
.dnt-key b { margin-inline-start: auto; color: var(--tone-ink); font-variant-numeric: tabular-nums; }

/* ── stacked columns, because the split IS the metric ────────────────────────────── */
.stk { flex-direction: column-reverse; }
.stk i { display: block; inline-size: 100%; }
.stk-b { background: var(--tone); border-radius: 0 0 2px 2px; transform-origin: bottom center; }
.stk-n { background: color-mix(in srgb, var(--tone) 30%, #fff); border-radius: 3px 3px 0 0; }
.stk-key { display: flex; align-items: center; gap: .3rem; margin: .35rem 0 0; font-size: .55rem;
           color: var(--muted); }
.stk-sw { inline-size: .5rem; block-size: .5rem; border-radius: 2px; }
.stk-sw-b { background: var(--tone); }
.stk-sw-n { background: color-mix(in srgb, var(--tone) 30%, #fff); margin-inline-start: .5rem; }

/* ── the funnel, which is the whole trade ────────────────────────────────────────── */
.fnl { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column;
       justify-content: space-evenly; gap: .3rem; flex: 1 1 auto; }
.fnl li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 .4rem;
          align-items: center; }
.fn-t { grid-column: 1 / -1; block-size: .62rem; display: flex; justify-content: center; }
.fn-t i { display: block; block-size: 100%; border-radius: 3px; background: var(--tone);
          transform-origin: center; }
.fn-k { font-size: .57rem; color: var(--muted); }
.fn-v { font-size: .57rem; font-weight: 700; color: var(--tone-ink);
        font-variant-numeric: tabular-nums; }

/* ⚠️ The funnel and the horizontal bars grow sideways from their own origin — the funnel from the
   middle, because a funnel that grows from the left is a bar chart lying about its shape. */
.fn-t i.d-grow { transform-origin: center; }
@media (prefers-reduced-motion: reduce) {
  .hb-t i, .fn-t i, .stk-b { animation: none; }
}

/* ══ FOUR SERVICES, FOUR DRAWINGS ════════════════════════════════════════════════════ */
/*
 * ⛔⛔ MEASURED ON THE LIVE PAGE: four panels, ONE distinct shape. Every service showed the same
 * dark 617x240 box with an icon, a big number and four rows of grey text. The element meant to
 * show four different offers was showing one box four times — the portfolio's mistake again, on
 * the page that sells the work rather than the page that demonstrates it.
 *
 * ⭐ EACH SERVICE DRAWS WHAT IT PRODUCES: a pipeline board, a phone, a browser with a phone beside
 * it, a campaign panel. ⛔ Still no stock photography and no screenshot of work that does not
 * exist — all four are built from the site's own furniture, so none of them can go stale.
 */
.sart {
  position: relative; overflow: hidden; border-radius: var(--radius-lg);
  background: var(--card); box-shadow: 0 0 0 1px var(--line), 0 18px 40px rgba(11, 42, 107, .12);
  padding: clamp(1rem, 2.4vw, 1.5rem); min-inline-size: 0;
}
.sart-top { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
           margin: 0 0 .9rem; font-size: .78rem; color: var(--muted); }
.sart-top b { font-family: var(--font-display); font-size: .95rem; color: var(--heading); }

/* ── 01 the pipeline a CRM buys you, drawn as an application ─────────────────────── */
/* ⛔ THIS WAS FOUR PALE COLUMNS OF GREY CARDS ON A WHITE BOX — the shape of a pipeline with none
   of the things that make one look like software. It now carries the rail, the header and the
   stage colours the portfolio machines use, because that is the language on this site that works.
   ⚠️ The working area is a PIPELINE, which no portfolio machine shows, so it is the same language
   saying a different thing rather than a sixth copy of the same screen. */
.sart-crm { padding: 0; background: var(--blue-50); }
.crmw { display: grid; grid-template-columns: 5.6rem minmax(0, 1fr); min-block-size: 0;
        border-radius: var(--radius-lg); overflow: hidden; }
.crmw-rail { background: var(--blue-900); padding: .7rem .5rem .9rem; }
.crmw-mark { display: block; inline-size: 1.3rem; block-size: 1.3rem; border-radius: 5px;
             background: rgba(255, 255, 255, .2); margin-block-end: .6rem; }
.crmw-rail ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .08rem; }
/* ⛔ .78 alpha, not .7 — white at .7 on --blue-900 is 4.3:1 and this is the smallest text here. */
.crmw-rail li { font-size: .6rem; padding: .26rem .4rem; border-radius: 4px;
                color: rgba(255, 255, 255, .78); white-space: nowrap;
                overflow: hidden; text-overflow: ellipsis; }
.crmw-rail li.on { background: rgba(255, 255, 255, .18); color: #fff; font-weight: 700; }
.crmw-main { min-inline-size: 0; background: var(--card); padding: .8rem .85rem 1rem; }
.crmw-top { display: flex; align-items: baseline; justify-content: space-between; gap: .8rem;
            margin: 0 0 .7rem; font-size: .64rem; color: var(--muted); }
.crmw-top b { font-family: var(--font-display); font-size: .92rem; color: var(--heading); }
.crmw-top em { font-style: normal; font-weight: 700; color: var(--accent-ink);
               font-variant-numeric: tabular-nums; }

.pipe-cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .45rem;
             align-items: start; }
.pipe-col { min-inline-size: 0; background: var(--blue-50); border-radius: 8px; padding: .45rem;
            border-block-start: 3px solid var(--line); }
/* ⭐ a colour per stage, so the board reads left to right as a journey rather than four boxes */
.pipe-a { border-block-start-color: #7C8BA6; }
.pipe-b { border-block-start-color: #2F63D8; }
.pipe-c { border-block-start-color: #B4761A; }
.pipe-d { border-block-start-color: #1E8E5A; }
.pipe-h { display: flex; align-items: center; justify-content: space-between; gap: .3rem;
          margin: 0; font-family: var(--font-display); font-size: .54rem; font-weight: 800;
          letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.pipe-h b { color: var(--heading); }
.pipe-sum { margin: .1rem 0 .4rem; font-size: .58rem; font-weight: 700; color: var(--accent-ink);
            font-variant-numeric: tabular-nums; }
.pipe-card { display: grid; grid-template-columns: 1.1rem minmax(0, 1fr); gap: .05rem .3rem;
             background: var(--card); border-radius: 6px; padding: .35rem .4rem;
             box-shadow: var(--shadow-sm); margin-block-end: .3rem; }
.pipe-card:last-child { margin-block-end: 0; }
.pipe-card i { grid-row: 1 / span 2; align-self: center; font-style: normal;
               inline-size: 1.1rem; block-size: 1.1rem; border-radius: 50%;
               background: var(--blue-100); color: var(--accent-ink);
               font-family: var(--font-display); font-size: .46rem; font-weight: 800;
               display: grid; place-items: center; }
.pipe-card b { font-size: .6rem; color: var(--heading); overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
.pipe-card em { font-style: normal; font-size: .56rem; font-weight: 700; color: var(--accent-ink);
                font-variant-numeric: tabular-nums; }

/* ── 02 a phone, because that is what a mobile app is ────────────────────────────── */
/* ⭐⭐ TWO PHONES: the day's list, and the job it opens. One phone at 130px in a 617px panel left
   most of the panel empty and showed the app doing a single thing, on a page selling apps. */
.sart-app { display: flex; align-items: center; justify-content: center; gap: 0;
            background: var(--blue-50); padding-block: clamp(1rem, 2.6vw, 1.7rem); }
.fone-front { z-index: 1; }
/* ⚠️ The one behind is smaller, offset and tipped, so it reads as a second screen rather than as
   a misaligned copy of the first. */
.fone-back .fone-top { display: grid; gap: 0; }
.fone-back { inline-size: 11.5rem; margin-inline-end: -2rem; transform: translateY(-.6rem) rotate(-4deg);
             opacity: .96; }
.fone-rows { list-style: none; margin: 0 0 .5rem; padding: 0; display: grid; gap: .18rem; }
/* ⛔ LABEL ABOVE VALUE, BOTH LEFT-ALIGNED. As a space-between row every value sat at the right
   edge — which is exactly the half the front phone overlaps — so the record showed "G. Bran…",
   "Estate…", "AED…". A layout is wrong if the thing in front of it hides the point of it. */
.fone-rows li { display: grid; gap: 0; font-size: .55rem; color: var(--muted);
                padding-block: .22rem; border-block-end: 1px solid var(--line-soft); }
.fone-rows li:last-child { border-block-end: 0; }
.fone-rows b { color: var(--heading); font-weight: 600; font-size: .58rem; white-space: nowrap;
               overflow: hidden; text-overflow: ellipsis; }
.fone-cta { display: block; text-align: center; font-size: .55rem; font-weight: 700; color: #fff;
            background: var(--blue-700); border-radius: 999px; padding: .28rem .5rem; }
.fone { inline-size: 13rem; flex: 0 0 auto; background: #141C2C; border-radius: 22px; padding: 1.3rem .5rem .9rem;
        position: relative; box-shadow: 0 0 0 1px rgba(0, 0, 0, .3), 0 18px 36px rgba(11, 42, 107, .22); }
.fone-notch { position: absolute; inset-block-start: .5rem; inset-inline-start: 50%;
              translate: -50% 0; inline-size: 3.2rem; block-size: .38rem; border-radius: 999px;
              background: #2C3A52; }
.fone-scr { background: var(--card); border-radius: 13px; padding: .55rem .5rem .35rem; }
.fone-top { display: flex; align-items: baseline; justify-content: space-between;
            margin: 0 0 .5rem; font-size: .6rem; color: var(--muted); }
.fone-top b { font-family: var(--font-display); font-size: .8rem; color: var(--heading); }
.fone-jobs { list-style: none; margin: 0 0 .55rem; padding: 0; display: grid; gap: .3rem; }
.fone-jobs li { display: grid; grid-template-columns: 2.4rem minmax(0, 1fr); gap: .1rem .4rem;
                background: var(--blue-50); border-radius: 7px; padding: .38rem .45rem; }
.fone-t { font-size: .6rem; font-weight: 700; color: var(--heading);
          font-variant-numeric: tabular-nums; }
.fone-j { font-size: .6rem; color: var(--heading); overflow: hidden; text-overflow: ellipsis;
          white-space: nowrap; }
.fone-s { grid-column: 2; font-size: .55rem; font-weight: 700; }
.fone-go .fone-s { color: #2C6B41; }
.fone-wait .fone-s { color: #8A5310; }
.fone-idle .fone-s { color: var(--muted); }
.fone-tabs { list-style: none; margin: 0; padding: .35rem 0 0; display: flex;
             justify-content: space-between; border-block-start: 1px solid var(--line-soft); }
.fone-tabs li { font-size: .55rem; color: var(--muted); }
.fone-tabs .on { color: var(--accent-ink); font-weight: 700; }
.fone-bar { display: block; inline-size: 4.5rem; block-size: .22rem; border-radius: 999px;
            background: rgba(255, 255, 255, .3); margin: .55rem auto 0; }

/* ── 03 the same site at two widths, which is what "responsive" means ────────────── */
.sart-web { background: var(--blue-50); padding-block-end: clamp(1.6rem, 4vw, 2.6rem); }
.brow { background: var(--card); border-radius: 10px; overflow: hidden;
        box-shadow: 0 0 0 1px var(--line), 0 12px 28px rgba(11, 42, 107, .14);
        inline-size: 88%; }
.brow-bar { display: flex; align-items: center; gap: .28rem; padding: .4rem .55rem;
            background: var(--blue-100); }
.brow-bar i { inline-size: .38rem; block-size: .38rem; border-radius: 50%; background: #B9C6DE; }
.brow-bar em { margin-inline-start: .4rem; font-style: normal; font-size: .58rem;
               color: var(--muted); }
.brow-scr { padding: .6rem .7rem .8rem; }
.brow-nav { display: flex; align-items: center; gap: .5rem; margin: 0 0 .6rem;
            font-size: .55rem; color: var(--muted); }
.brow-nav b { font-family: var(--font-display); font-size: .62rem; color: var(--heading);
              margin-inline-end: auto; }
.brow-hero { display: grid; gap: .3rem; justify-items: start; margin-block-end: .6rem; }
.brow-h1 { inline-size: 62%; block-size: .55rem; border-radius: 3px; background: var(--heading); }
.brow-h2 { inline-size: 44%; block-size: .38rem; border-radius: 3px; background: var(--line); }
.brow-cta { font-size: .55rem; font-weight: 700; color: #fff; background: var(--blue-700);
            border-radius: 999px; padding: .2rem .5rem; margin-block-start: .15rem; }
.brow-cards { list-style: none; margin: 0; padding: 0; display: grid;
              grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .35rem; }
.brow-cards li { background: var(--blue-50); border-radius: 6px; padding: .4rem .35rem;
                 font-size: .53rem; color: var(--muted); text-align: center; overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
/* ⭐ THE NARROW ONE OVERLAPS THE WIDE ONE, which is the whole point of the picture — and it has
   to read as a PHONE. As a squat box with four equal bars it read as a hamburger icon, so it has a
   phone's proportions, a notch and a home bar, and its screen mirrors the browser's own layout. */
.brow-fone { position: absolute; inset-inline-end: clamp(.6rem, 2vw, 1.4rem);
             inset-block-end: clamp(.6rem, 2vw, 1.2rem);
             inline-size: 4.3rem; aspect-ratio: 9 / 17;
             background: #141C2C; border-radius: 12px; padding: .55rem .28rem .45rem;
             display: flex; flex-direction: column;
             box-shadow: 0 0 0 1px rgba(0, 0, 0, .3), 0 12px 26px rgba(11, 42, 107, .3); }
.bf-notch { inline-size: 1.5rem; block-size: .16rem; border-radius: 999px; background: #38455E;
            margin: 0 auto .3rem; }
.bf-scr { flex: 1 1 auto; background: var(--card); border-radius: 6px; padding: .3rem .28rem;
          display: flex; flex-direction: column; gap: .2rem; }
.bf-scr i { display: block; border-radius: 2px; }
.bf-nav { block-size: .16rem; inline-size: 70%; background: var(--line); }
.bf-h { block-size: .3rem; inline-size: 92%; background: var(--heading); margin-block-start: .1rem; }
.bf-sub { block-size: .16rem; inline-size: 62%; background: var(--line); }
.bf-cta { block-size: .28rem; inline-size: 52%; background: var(--blue-700); border-radius: 999px;
          margin-block-start: .1rem; }
.bf-card { flex: 1 1 auto; min-block-size: .5rem; background: var(--blue-50);
           margin-block-start: .12rem; }
.bf-bar { inline-size: 1.8rem; block-size: .14rem; border-radius: 999px;
          background: rgba(255, 255, 255, .35); margin: .3rem auto 0; }

/* ── 04 what each channel cost, and what it returned ─────────────────────────────── */
.mkt-ch { list-style: none; margin: 0 0 .9rem; padding: 0; display: grid; gap: .4rem; }
.mkt-ch li { display: grid; grid-template-columns: 4.2rem minmax(0, 1fr) 2rem; gap: .5rem;
             align-items: center; }
.mkt-k { font-size: .68rem; color: var(--muted); }
.mkt-t { block-size: .5rem; border-radius: 999px; background: var(--blue-50); overflow: hidden; }
.mkt-t i { display: block; block-size: 100%; border-radius: 999px; background: var(--blue-600);
           transform-origin: left center; }
.mkt-v { font-size: .68rem; font-weight: 700; color: var(--accent-ink); text-align: end;
         font-variant-numeric: tabular-nums; }
/* ⚠️ clicks to enquiries to deals — the only sentence a marketing panel needs to earn */
.mkt-flow { display: flex; align-items: center; justify-content: space-between; gap: .3rem;
            margin: 0; padding-block-start: .8rem; border-block-start: 1px solid var(--line-soft); }
.mkt-step { display: grid; }
.mkt-step b { font-family: var(--font-display); font-size: .95rem; color: var(--heading);
              font-variant-numeric: tabular-nums; }
.mkt-step i { font-style: normal; font-size: .58rem; color: var(--muted); }
.mkt-arrow { color: var(--line); font-size: .9rem; }

@media (max-width: 34rem) {
  .pipe-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .crmw { grid-template-columns: minmax(0, 1fr); }
  .crmw-rail { display: flex; align-items: center; gap: .3rem; padding: .5rem .55rem; }
  .crmw-rail ul { display: flex; gap: .25rem; overflow: hidden; }
  .crmw-mark { margin-block-end: 0; flex: 0 0 auto; }
  /* ⚠️ one phone on a phone. Two side by side at 320px would be two slivers. */
  .fone-back { display: none; }
  .brow { inline-size: 100%; }
  .brow-fone { display: none; }
}

/* ══ THE PANEL BEHIND THIS SITE ══════════════════════════════════════════════════════ */
/*
 * ⛔ Every case study on this page gets a machine; the one DELIVERED project in "the rest of the
 * work" had an icon, a heading and four ticks — so the only entry a reader can actually verify
 * was the only one with nothing to look at.
 * ⚠️ --tone lives on .showcase-band, and this block is not one. It declares its own, or the rail
 * paints with an undefined custom property and comes out transparent.
 */
.workart { --tone: var(--blue-600); --tone-ink: var(--blue-800); --tone-wash: #E8F0FE;
           min-inline-size: 0; }
.work-split { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: center; }
@media (min-width: 62rem) { .work-split { grid-template-columns: 0.85fr 1.15fr; } }
.wa-body { flex: 1 1 auto; min-block-size: 0; border: 1px solid var(--line-soft);
           border-radius: 9px; padding: .7rem .8rem; background: var(--card); overflow: hidden; }
.wa-routes { list-style: none; margin: 0; padding: 0; display: grid; gap: .18rem; }
.wa-routes li { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: .5rem;
                align-items: baseline; padding-block: .22rem;
                border-block-end: 1px solid var(--line-soft); font-size: .68rem; }
.wa-routes li:last-child { border-block-end: 0; }
.wa-routes code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
                  font-size: .64rem; color: var(--accent-ink); overflow: hidden;
                  text-overflow: ellipsis; white-space: nowrap; }
.wa-routes span { color: var(--muted); white-space: nowrap; }
/* ⚠️ the arrow is drawn, not typed, so it never lands alone at the start of a wrapped line */
/* ⛔⛔ THE BACKSLASH WAS EATEN AND THE SITE PRINTED THE ESCAPE. This shipped with a
   literal U+0011 control character followed by "92" - the shell read the \21 of \2192 as an
   octal escape - so every row of the panel illustration painted a tofu box and the digits
   "92" where the arrow belongs, six times over. It reached production because a stylesheet
   with a wrong glyph is still valid CSS: nothing errors, it just prints rubbish. */
.wa-routes b::before { content: "\2192"; padding-inline-end: .3rem; color: var(--line); }
.wa-routes b { color: var(--heading); font-weight: 600; white-space: nowrap; }


/* ══ THE PHONE PASS ══════════════════════════════════════════════════════════════════════ */
/*
 * ⭐ MEASURED AT 375x812 ON THE LIVE SITE, PAGE BY PAGE. The structure held up: zero horizontal
 * scroll anywhere, nothing clipped, nothing cramped, nothing past the viewport edge. So this
 * block changes no layout at all — it fixes the menu, and it puts a floor under text size and
 * tap targets. ⛔ Everything here is inside a narrow-width query; the desktop is untouched.
 */
@media (max-width: 760px) {
  /*
   * ⛔⛔ THE OPEN MENU DID NOT COVER THE PAGE. `.site-head` is a floating pill — border-radius
   * 999px over rgba(255,255,255,.78) — which is correct for a 56px bar and wrong for the 358x394
   * the header becomes when the menu drops: a 999px radius on a near-square box is an ELLIPSE,
   * so the corners are not painted at all. "The ERP" at the top and "Book a call" at the bottom
   * sat outside the shape entirely, directly on the hero paragraph, and the 78% translucency
   * bled the rest of the page through the middle of the list.
   *
   * ⚠️ :has() SCOPES THIS TO THE OPEN STATE, so the closed bar keeps the floating pill it was
   * designed as. The alternative — a class toggled onto the header by script — would put the
   * menu's appearance at the mercy of the script having run.
   */
  .js-nav .site-head:has(.site-nav.open) {
    border-radius: 20px;
    /* ⛔ OPAQUE. A translucent panel over a whole page of text is a menu you have to read past. */
    background: var(--card);
    border-color: var(--line);
    box-shadow: 0 1px 2px rgba(11, 42, 107, .05), 0 24px 48px -12px rgba(11, 42, 107, .28);
  }

  /* ── a floor under the smallest text ──────────────────────────────────────────────── */
  /*
   * ⚠️ THESE ARE REAL PAGE TEXT, NOT THE MINIATURE TYPE INSIDE THE MOCKUPS. The laptop screens
   * and dashboard drawings are illustrations and are meant to be small; a label somebody has to
   * read is not. Everything below measured between 9.3px and 9.9px on a phone.
   * ⛔ The partner line is a CREDENTIAL and was shrunk to 0.58rem (9.3px) purely to hold one
   *    line. It still holds one line at 0.68rem, so it gets read instead of squinted at.
   */
  .brand-with { font-size: 0.68rem; }
  .site-foot h4 { font-size: 0.78rem; }
  .wall-sector, .quote-tag, .story-tag, .work-tag, .rec-status { font-size: 0.7rem; }

  /*
   * ⛔⛔ THE CAP IS THE COLLAPSE ANIMATION, NOT DECORATION. .brand-with is held at 1.4rem and
   * animated to 0 when the header shrinks on scroll, so it cannot be removed — only raised.
   * Raising the type in this same pass made the line wrap at 320px, and the cap then sliced the
   * second line in half: 29px of text in a 22px box, measured on every page. Two lines is the
   * honest height for a credential that wraps, and the collapse still runs to 0 from here.
   */
  .brand-with { max-block-size: 3rem; }

  /*
   * ⛔⛔ THE ROW WRAPS. IT DOES NOT TRUNCATE. The first answer here was ellipsis on all three
   * items, which fixed a 14px spill at 320px by mutilating the bar at every width: measured at
   * 375px the crumb had 92px of 140, the brand 57px of 87 and the user 94px of 142, so the
   * header read "Dashboard / Se…  The H…  A. Quill · Genera…". The business name is the one
   * label that says whose system this is, and it was the thing being cut.
   * ⭐ The row never needed to be shorter, it needed a second line: the breadcrumb takes its
   * own, the brand and the user share the next. Verified at 375px and 320px — every label on
   * all six machines in full, no lid overflow, no page scroll.
   * ⚠️ The brand keeps `text-overflow: clip` explicitly, so no future rule can quietly start
   *    abbreviating the one word the picture exists to show.
   */
  .app-top { flex-wrap: wrap; gap: .2rem .5rem; }
  .app-crumb { flex: 1 1 100%; margin-inline-end: 0; }
  .app-brand { flex: 0 1 auto; overflow: visible; text-overflow: clip; }
  /* ⛔ flex: 0 1 auto, NOT 1 1 auto, and start-aligned. Growing it made the chip fill a whole
     row whenever it could not share one with the brand, and text-align: end then pinned it to
     the far right — so three of the seven machines (Lindenmere, Brantwell, Pelbrook) had a
     stranded name floating alone on a third line while the other four read as two tidy lines.
     Not growing it lets space-between do the work: beside the brand when it fits, directly
     under it when it does not. */
  .app-user { flex: 0 1 auto; min-inline-size: 0; text-align: start;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /*
   * ⛔ THE CLINIC DIARY SCROLLED SIDEWAYS INSIDE THE LAPTOP. 2.3rem for the time column, two
   * room columns with a 6.1rem floor and two 0.4rem gaps come to 244.8px in a 235px screen, so
   * the board carried its own horizontal scrollbar - a grey bar across the bottom of a picture
   * of software, and "Microneedling" pushed past the edge. A drawing of an application should
   * never need scrolling to be seen.
   * ⚠️ The room columns lose their 6.1rem floor and simply share what is there; the chip gives
   *    back 3px of padding so the longest treatment name still fits on its line.
   * ⚠️ My truncation sweep could not see this one either: it skips overflow-x:auto elements as
   *    deliberate scrollers. Inside a mockup there is no such thing as a deliberate scroller.
   */
  .dy-grid { grid-template-columns: 2rem repeat(2, minmax(0, 1fr)); gap: .3rem;
             overflow-x: visible; }
  .dy-room { min-inline-size: 0; }
  .dy-appt { padding-inline: .28rem; }

  /*
   * ⛔ THE RECRUITMENT PIPELINE ATE ITS OWN STAGE NAMES. Two columns of 112px, of which the
   * chip's padding (17.6), its gap (8) and the number badge (22.4) take 48 — leaving 64.5px for
   * "At interview", which needs 73. It read "Shortlist…" and "At intervi…": the stage names ARE
   * the content of that board. Trimming the chip's own padding at narrow widths returns 77px and
   * keeps the two-pipelines-meeting shape rather than stacking them.
   */
  .pl-two { gap: .4rem; }
  .pl-chip { padding-inline: .35rem; gap: .3rem; }
  .pl-chip b { min-inline-size: 1.05rem; }

  /*
   * ⛔ SEVEN OF THE NINE TECHNOLOGY FILTERS WERE BEHIND A SIDEWAYS SWIPE: 1,141px of tabs in a
   * 335px bar, with only "Everything" and "Back end" showing. The mask-fade means the bar does
   * not lie about scrolling, but a filter you cannot see is a filter you will not use — the same
   * reason the portfolio rails wrap here rather than hiding nine modules off the edge.
   * ⚠️ Wrapping keeps the keyboard arrow navigation and the aria-pressed state exactly as they
   *    were; only the line-breaking changes. The fade is dropped because nothing is hidden now.
   */
  .stabs { flex-wrap: wrap; overflow-x: visible; }
  .stabs.is-scrollable { -webkit-mask-image: none; mask-image: none; }

  /* ── and a floor under what a thumb has to hit ────────────────────────────────────── */
  /* ⚠️ 44px is the Apple/Android target, not the 24px WCAG minimum — the same rule the nav
     toggle already follows. .rec-go measured 25px and .fbtn 40px. */
  .rec-go { min-block-size: 44px; }
  .fbtn { min-block-size: 44px; }
}


/* ── 320px: three pictures that were abbreviating their own content ──────────────────── */
/*
 * ⚠️ A SECOND, NARROWER STOP. Everything above holds at 375px; these three only break on a
 * 320px phone, and they break the same way the mockup header did — by quietly abbreviating the
 * text the picture exists to show. Found by sweeping for truncation across every page, which is
 * the check that had been excluding `text-overflow: ellipsis` as intentional.
 */
/*
 * ⛔⛔ 30rem, NOT 22rem. I first scoped this to 22rem on the strength of a 320px sweep, and at
 * 375px "/services/websites/" and "/services/marketing/" were still abbreviated — they had been
 * in the 375px results too, below the row limit my own probe printed. A cap on a diagnostic is a
 * place for a bug to hide. The longest route needs the extra 23px until about 400px wide.
 * ⚠️ Every line on that screen is a URL somebody can open, and an abbreviated URL is not one.
 */
@media (max-width: 30rem) {
  .wa-routes li { grid-template-columns: minmax(0, 1fr) auto; }
  .wa-routes code { grid-column: 1 / -1; }
}

@media (max-width: 22rem) {
  /* ⛔ Two ~95px columns cannot hold "At interview" at any padding. The two pipelines stack
     here — which loses the converging shape the board is built around, and at this width the
     stage names are worth more than the diagram. */
  .pl-two { grid-template-columns: minmax(0, 1fr); }

  /* ⛔ "Contacts", "Invoices" and "Reports" were cut by 5-6px in the CRM board's rail. */
  .crmw-rail { padding-inline: .3rem; }
  .crmw-rail li { padding-inline: .25rem; }
}


/* ══ THE CONSENT BANNER ══════════════════════════════════════════════════════════════════ */
/*
 * ⛔⛔ IT SITS AT THE BOTTOM AND DOES NOT BLOCK THE PAGE. A wall that hides the content until you
 * agree is pressure rather than a choice, and "freely given" is the phrase the regulation uses.
 * The site stays readable and usable with this open.
 *
 * ⛔ THE TWO BUTTONS ARE THE SAME SIZE AND THE SAME WEIGHT. Making refusal smaller, greyer or
 * slower than agreement is the exact dark pattern named in the EDPB guidance — and on a site
 * selling governed data it would be the most expensive kind of hypocrisy.
 *
 * ⚠️ [hidden] IS RESPECTED because the banner ships in the markup and is revealed by script; the
 * rule below sets display on .cbar, which would otherwise beat the attribute.
 */
.cbar {
  position: fixed; inset-inline: 0; inset-block-end: 0; z-index: 60;
  display: block; padding: clamp(.9rem, 2.5vw, 1.15rem) clamp(1rem, 4vw, 2rem);
  background: var(--card); border-block-start: 1px solid var(--line);
  box-shadow: 0 -10px 34px rgba(11, 42, 107, .16);
  animation: cbarUp .3s var(--ease) both;
}
.cbar[hidden] { display: none; }
@keyframes cbarUp { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { .cbar { animation: none; } }

.cbar-in {
  max-inline-size: 68rem; margin-inline: auto;
  display: grid; gap: .35rem .5rem; align-items: center;
  grid-template-columns: minmax(0, 1fr) auto;
}
/* ⚠️ 1.45, NOT THE SITE'S 1.65. That leading is set for articles and it is right there; on a
   289px bottom sheet covering a third of an 812px phone it is just bulk. Tightening the leading
   costs 29px and no words — and the words are the part that has to stay, since a notice that
   does not say what is stored is a notice written for the site rather than the reader. */
.cbar-t { margin: 0; font-weight: 600; color: var(--heading); font-size: .98rem; line-height: 1.45; }
/* ⚠️ 6.1:1 on the card. This line is the one that says what is actually stored, so it is not
   allowed to be the faint one. */
.cbar-s { margin: 0; color: var(--muted); font-size: .88rem; max-inline-size: 60ch; line-height: 1.45; }
.cbar-do { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: .5rem; }
/* ⚠️ .cta ALREADY IS 54px, 1rem, weight 600 and inline-flex. This trims it for a bar rather
   than rebuilding it: a page-level call to action is the widest thing on its row, a banner
   button sits beside its twin. ⛔ There is deliberately no rule naming one button and not the
   other — that is what keeps them equal. */
.cbar-do .cta { min-block-size: 46px; padding-inline: 1.25rem; font-size: .95rem; white-space: nowrap; }

/* ⚠️ On a phone the buttons take their own row and share it equally, rather than the decline
   being pushed to a second line where it reads as the lesser option. */
@media (max-width: 46rem) {
  .cbar-in { grid-template-columns: minmax(0, 1fr); }
  .cbar-do { grid-column: 1; grid-row: auto; margin-block-start: .55rem; }
  .cbar-do .cta { flex: 1 1 0; justify-content: center; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * THE PRIVACY POLICY
 *
 * ⭐ A DOCUMENT PEOPLE LOOK THINGS UP IN, not one they read top to bottom — so a short version
 * first, a contents list, headings that land clear of the fixed header, and the lists and the
 * table that .prose alone never needed.
 *
 * ⚠️ THE CONTAINER IS WIDER THAN .prose SO THE COOKIE TABLE HAS ROOM, and every other child is held
 * to the same 56ch measure .prose uses — ch follows each element's own font, so a paragraph still
 * lands at ~66 characters. ⛔ Do not turn these into rem; see RUNNING PROSE above.
 * ⚠️ On a phone the table scrolls inside its own box; the page itself never scrolls sideways.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */
.prose.policy { max-inline-size: 48rem; }
.policy > :not(.policy-table) { max-inline-size: 56ch; }
.policy h2 {
  font-size: clamp(1.4rem, 2.4vw, 1.7rem); margin: 2.75rem 0 0.9rem;
  scroll-margin-block-start: 6rem;
}
.policy h3 { scroll-margin-block-start: 6rem; }
.policy ul, .policy ol {
  margin: 0 0 1.15rem; padding-inline-start: 1.3rem;
  color: var(--muted); font-size: 1.08rem; line-height: 1.7;
}
.policy li + li { margin-block-start: 0.45rem; }
.policy li strong { color: var(--heading); font-weight: 600; }
.policy a { color: var(--accent-ink); font-weight: 600; text-underline-offset: 0.2em; }
.policy code {
  font-size: 0.88em; color: var(--heading); background: var(--blue-100);
  padding: 0.05em 0.35em; border-radius: 5px;
}

.policy-short {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.15rem, 2.5vw, 1.6rem) clamp(1.15rem, 3vw, 1.8rem); margin: 0 0 2rem;
}
.policy .policy-short h2 { margin-block-start: 0; font-size: 1.2rem; }
.policy-short ul { margin-block-end: 0; }

.policy-toc { margin: 0 0 0.5rem; }
.policy .policy-toc-k {
  font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;
  color: var(--heading); margin: 0 0 0.6rem;
}
.policy-toc ol { columns: 2 14rem; column-gap: 2rem; font-size: 1rem; margin: 0; }
.policy-toc li { break-inside: avoid; margin-block-end: 0.35rem; }
.policy-toc li + li { margin-block-start: 0; }

.policy-table {
  overflow-x: auto; margin: 0 0 1.25rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.policy table { border-collapse: collapse; inline-size: 100%; min-inline-size: 36rem; font-size: 0.95rem; line-height: 1.55; }
.policy th, .policy td { text-align: start; vertical-align: top; padding: 0.75rem 0.9rem; border-block-end: 1px solid var(--line); }
.policy tbody tr:last-child > * { border-block-end: 0; }
.policy thead th {
  color: var(--heading); background: var(--blue-50);
  font-size: 0.78rem; letter-spacing: 0.06em; text-transform: uppercase;
}
.policy tbody th { color: var(--heading); font-weight: 600; white-space: nowrap; }
.policy td { color: var(--muted); }

/* ⭐ Changing your mind sits right under the table that explains what you are choosing. */
.cookie-choice { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: center; margin: 0.25rem 0 1.25rem; }

/* ⭐ The policy, one tap from every consent box. ⚠️ Indented by the box (1.5rem) plus the gap
   (0.75rem) so the link lines up with the words of the label, not with the box. */
.consent-more { margin: 0.4rem 0 0; padding-inline-start: 2.25rem; }
.consent-more a { color: var(--accent-ink); font-weight: 600; }
.cbar-s a { color: var(--accent-ink); font-weight: 600; white-space: nowrap; }
/* ⚠️ White on the deep footer, underlined so it reads as a link beside the plain legal lines. */
.foot-legal .foot-privacy { color: #fff; font-weight: 600; text-decoration: underline; text-underline-offset: 0.2em; display: inline-block; min-block-size: 24px; }
