/* ==========================================================================
   BG Performance Consulting
   --------------------------------------------------------------------------
   The logo is the design system. It contains exactly five devices:
   a serif monogram, a gold hairline, a letterspaced sans wordmark,
   a second gold hairline, and negative space. Nothing else is invented here.

   Brand law: gold is never a fill. Only hairlines, rules, numerals, accents.

   Order: faces -> tokens -> reset -> type -> primitives -> ground contexts
          -> components -> sections -> responsive -> reduced motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Faces  (self-hosted; no Google CDN request at runtime, per GDPR)
   unicode-range means English pages never download the latin-ext files.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/inter-latin-ext.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: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/inter-latin.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: 'Source Serif 4';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/source-serif-4-latin-ext.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: 'Source Serif 4';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/source-serif-4-latin.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;
}

/* --------------------------------------------------------------------------
   2. Tokens
   Colours sampled from the logo file, not approximated.
   -------------------------------------------------------------------------- */

:root {
  /* Brand ---------------------------------------------------------------- */
  --navy:        #001C4B;
  --navy-lift:   #0A2A5E;
  --gold:        #B07D21;  /* hairlines, rules, numerals >=24px          */
  --gold-ink:    #90661B;  /* same hue 38.6deg, darker: small type on light */
  --gold-light:  #D7C185;  /* type and hairlines on navy only            */
  --paper:       #FFFFFF;
  --paper-warm:  #F6F4F0;  /* pulled from the portrait's taupe ground    */
  --taupe:       #8B8178;  /* decorative and large text only, 3.81:1 */
  --taupe-ink:   #756C65;  /* same hue 28.4deg, darker: small text, 5.14:1 */
  --ink:         #1A2230;  /* body text; never pure black               */
  --line:        #E3E0DA;
  --navy-text:   #CBD3E1;  /* body copy on navy grounds                 */

  /* Ground context. Sections override these rather than fighting
     descendant selectors -- see section 5. -------------------------------- */
  --ground:    var(--paper);
  --text:      var(--ink);
  --heading:   var(--navy);
  --meta:      var(--gold-ink);
  --rule:      var(--gold);
  --hairline:  var(--line);

  /* Type ----------------------------------------------------------------- */
  --font-display: 'Source Serif 4', 'Iowan Old Style', Georgia, serif;
  --font-ui: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;

  --step-meta:    0.75rem;
  --step-ui:      0.9375rem;
  --step-nav:     0.8125rem;  /* 13px; see note in the header block */
  --step-body:    1.0625rem;
  --step-lead:    clamp(1.125rem, 1.05rem + 0.35vw, 1.375rem);
  --step-h3:      clamp(1.5rem,   1.30rem + 0.90vw, 2rem);
  --step-h2:      clamp(2rem,     1.50rem + 2.00vw, 3rem);
  --step-h1:      clamp(2.5rem,   1.75rem + 3.20vw, 4.25rem);
  --step-numeral: clamp(3rem,     2.00rem + 4.00vw, 5.5rem);

  --track-meta:    0.18em;
  --track-display: -0.02em;

  /* Motion --------------------------------------------------------------- */
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.30, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.50, 1);
  --ease-inout:     cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 240ms;
  --dur-base: 480ms;
  --dur-slow: 720ms;
  --reveal-y: 24px;

  /* Radius ---------------------------------------------------------------
     Everything is rounded. The scale rises with the size of the element so a
     16px input and a full-bleed section do not share a curve. */
  --r-xs:   8px;    /* tags, chips, small marks       */
  --r-sm:   12px;   /* inputs, textareas, small cards */
  --r-md:   18px;   /* buttons                        */
  --r-lg:   26px;   /* cards, media inside content    */
  --r-xl:   36px;   /* large media, feature panels    */
  --r-2xl:  48px;   /* inset full-bleed sections      */
  --r-pill: 999px;  /* pills, nav, badges             */

  /* Layout --------------------------------------------------------------- */
  --container: 1200px;
  --gutter: 24px;
  /* Vertical breathing room between inset panels. Without it the rounded
     corners are geometrically present and visually absent: a navy panel
     sitting flush on the navy footer merges into one mass. */
  --panel-gap: clamp(20px, 2.4vw, 32px);
  --section-pad: clamp(72px, 9vw, 140px);
  --measure: 68ch;
  --measure-lead: 54ch;
}

@media (min-width: 768px) {
  :root { --gutter: 40px; }
}

/* --------------------------------------------------------------------------
   3. Reset
   Every corner is rounded; the scale above decides how much. No shadows.
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--step-body);
  line-height: 1.7;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
/* height:auto is not optional alongside max-width:100%. Without it an image
   whose rendered width differs from its width/height attributes keeps the
   attribute height and stretches vertically. */
img, picture, video, canvas, svg { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; padding: 0; }
input, textarea, select { border-radius: var(--r-sm); }

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--gold-ink);
  outline-offset: 3px;
}

.panel--navy :focus-visible { outline-color: var(--gold-light); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: 100;
  background: var(--navy);
  color: var(--paper);
  padding: 12px 24px;
  font-size: var(--step-ui);
  letter-spacing: 0.06em;
  transition: transform var(--dur-fast) var(--ease-out-quart);
}
.skip-link:focus { transform: translate(-50%, 0); }

/* --------------------------------------------------------------------------
   4. Type
   Extreme scale contrast: 12px letterspaced caps against 96px serif.
   Very little in between. That tension is the whole voice.
   -------------------------------------------------------------------------- */

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: var(--track-display);
  color: var(--heading);
  text-wrap: balance;
  /* Display type is where a single long word does the most damage: at 320px
     one unbreakable heading word pushes the whole page wider. */
  overflow-wrap: break-word;
}

h1 { font-size: var(--step-h1); }
h2 { font-size: var(--step-h2); line-height: 1.14; }
h3 { font-size: var(--step-h3); line-height: 1.22; }

.u-meta,
.eyebrow {
  font-family: var(--font-ui);
  font-size: var(--step-meta);
  font-weight: 500;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  line-height: 1.4;
}

.lead {
  font-size: var(--step-lead);
  line-height: 1.6;
  max-width: var(--measure-lead);
  color: var(--text);
}

p {
  max-width: var(--measure);
  color: var(--text);
  overflow-wrap: break-word;
}

/* --------------------------------------------------------------------------
   5. Primitives
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  background: var(--ground);
  color: var(--text);
  padding-block: var(--section-pad);
  position: relative;
}

/* Ground contexts. Setting local custom properties keeps every component
   ground-agnostic and avoids descendant-selector specificity wars. */
.panel--warm { --ground: var(--paper-warm); }

/* Full-bleed navy sections become inset rounded blocks so their corners are
   actually visible against the paper ground.

   margin-block is not decoration: a corner is only legible when something of a
   different colour sits behind it. Adjacent siblings collapse their vertical
   margins, so this one declaration produces a single gap between two panels
   rather than a doubled one, and needs no first/last-child exceptions. */
.section.panel--navy,
.section.panel--warm {
  margin-block: var(--panel-gap);
  margin-inline: var(--gutter);
  border-radius: var(--r-2xl);
  overflow: hidden;
}

/* Below the tablet breakpoint every inset panel goes full-bleed.
   The inset costs twice on a phone, not once: the panel gives up a gutter on
   each side and the container inside it gives up another, so a 390px screen
   was rendering its content into 294px -- a quarter of the width spent on
   nothing. Edge to edge, the same content gets 342px.

   The radius stays. An earlier version dropped it here, reasoning that a corner
   needs a different colour behind it to read -- which was simply wrong: the
   page is behind it, and white against navy is as much contrast as a corner
   ever needs. A full-width band with rounded ends is a normal shape. A
   square-cornered one just looks unfinished. */
@media (max-width: 767px) {
  .section.panel--navy,
  .section.panel--warm { margin-inline: 0; }

  /* Cards inside those panels are the third layer of indent, so they give a
     little back too -- but they keep a radius, because they do have the panel
     behind them. */
  .route,
.programme,
.diag-cta { padding: 22px; }
}

.panel--navy {
  --ground:    var(--navy);
  --text:      var(--navy-text);
  --heading:   var(--paper);
  --meta:      var(--gold-light);
  --rule:      var(--gold-light);
  --hairline:  rgba(215, 193, 133, 0.22);
}

/* The eyebrow: a 40px gold hairline followed by letterspaced caps.
   Lifted directly from the logo's construction. The rule draws itself
   in when its ancestor reveals. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 16px;
  color: var(--meta);
  margin-bottom: 28px;
}

.eyebrow::before {
  content: '';
  display: block;
  width: 40px;
  height: 1px;
  flex: none;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease-out-expo) 120ms;
}

.is-revealed .eyebrow::before,
.eyebrow.is-revealed::before { transform: scaleX(1); }

/* Standalone hairline. Observed and drawn by initHairlines(). */
/* The draw state is a scalar, never a whole transform. Each rule's base
   declaration owns its direction, so a horizontal draw can never silently
   flatten a vertical spine -- and no rule depends on its own (zero-width)
   geometry to know it has arrived. */
.rule-gold {
  display: block;
  height: 1px;
  background: var(--rule);
  transform: scaleX(var(--draw, 0));
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.rule-gold--vertical {
  width: 1px;
  height: auto;
  transform: scaleY(var(--draw, 0));
  transform-origin: top center;
}

.rule-gold.is-revealed,
.is-revealed .rule-gold { --draw: 1; }

.rule-hairline {
  display: block;
  height: 1px;
  background: var(--hairline);
  border: 0;
  margin: 0;
}

/* Reveal. Note the no-JS escape hatch below -- if site.js never runs,
   the page must be fully readable, not invisible. */
.reveal {
  opacity: 0;
  transform: translateY(var(--reveal-y));
  transition:
    opacity var(--dur-slow) var(--ease-out-expo),
    transform var(--dur-slow) var(--ease-out-expo);
}

.reveal.is-revealed {
  opacity: 1;
  transform: none;
}

/* If site.js never runs, nothing may be left hidden. These two declarations
   are the difference between a readable page and a blank one. */
html:not(.js) .reveal,
html:not(.js) .eyebrow::before {
  opacity: 1;
  transform: none;
}

html:not(.js) .rule-gold { --draw: 1; }

/* Link and button language ------------------------------------------------ */

.link-rule {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-ui);
  font-size: var(--step-meta);
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
  padding-bottom: 6px;
  position: relative;
}

.link-rule::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0; right: 0;
  height: 1px;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out-expo);
}

.link-rule .arrow {
  transition: transform var(--dur-base) var(--ease-out-quart);
}

@media (hover: hover) {
  .link-rule:hover::after { transform: scaleX(1); }
  .link-rule:hover .arrow { transform: translateX(5px); }
}

.link-rule:focus-visible::after { transform: scaleX(1); }

/* A sentence-length link is a different object from a two-word call to action,
   and the small-caps treatment was making two of them misbehave.

   .link-rule is inline-flex, so when its label wraps, the arrow stops being
   part of the sentence and becomes a flex sibling pinned to the end of the
   row -- on ro/results it ended up alone at the far right, one line above the
   words it belonged to. Measured across seven pages, two languages and five
   widths, exactly two links wrap; both are whole sentences.

   So the sentences get a prose variant instead: normal case, body size, and an
   underline drawn as a background rather than an absolutely positioned rule,
   because an absolute rule only ever covers the first line box of a wrapped
   inline. box-decoration-break keeps it drawn on every line. */
/* Touch targets.

   These links are 21-26px tall, which is right for the type and too small for
   a thumb. WCAG 2.5.8 exempts links that sit inline in a sentence, and most of
   the ones on this site do -- but "Discover our services", "About Bogdan" and
   "Prefer to write to me?" are standalone rows, and the exemption does not
   cover those.

   Padding rather than a min-height, so the underline stays where the type
   wants it: the extra area is above and below the rule, invisible and
   tappable. Coarse pointers only, so the desktop rhythm is untouched. */
@media (pointer: coarse) {
  .link-rule {
    padding-top: 12px;
    padding-bottom: 18px;
  }

  /* The prose variant sits in running text, where the inline exemption does
     apply and extra padding would break the line rhythm. */
  .link-rule--prose { padding-top: 0; padding-bottom: 2px; }
}

.link-rule--prose {
  display: inline;
  font-family: var(--font-ui);
  font-size: var(--step-ui);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  padding-bottom: 2px;
  background-image: linear-gradient(var(--rule), var(--rule));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--dur-base) var(--ease-out-expo);
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}

.link-rule--prose::after { display: none; }
.link-rule--prose .arrow { margin-left: 6px; }

@media (hover: hover) {
  .link-rule--prose:hover { background-size: 100% 1px; }
}
.link-rule--prose:focus-visible { background-size: 100% 1px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 17px 34px;
  font-family: var(--font-ui);
  font-size: var(--step-meta);
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  transition:
    background-color var(--dur-base) var(--ease-out-quart),
    color var(--dur-base) var(--ease-out-quart),
    border-color var(--dur-base) var(--ease-out-quart),
    transform var(--dur-base) var(--ease-out-quart);
}

/* Primary is navy on light and paper on navy. Never gold -- gold is not a fill. */
.btn--primary {
  background: var(--navy);
  color: var(--paper);
  border-color: var(--navy);
}

.btn--ghost {
  background: transparent;
  color: var(--heading);
  border-color: var(--hairline);
}

.panel--navy .btn--primary {
  background: var(--paper);
  color: var(--navy);
  border-color: var(--paper);
}

.panel--navy .btn--ghost {
  color: var(--paper);
  border-color: rgba(255, 255, 255, 0.28);
}

@media (hover: hover) {
  .btn--primary:hover { background: var(--navy-lift); border-color: var(--navy-lift); transform: translateY(-2px); }
  .btn--ghost:hover { border-color: var(--gold); color: var(--gold-ink); transform: translateY(-2px); }
  .panel--navy .btn--primary:hover { background: var(--paper-warm); border-color: var(--paper-warm); transform: translateY(-2px); }
  .panel--navy .btn--ghost:hover { border-color: var(--gold-light); color: var(--gold-light); transform: translateY(-2px); }
}

/* --------------------------------------------------------------------------
   6. Chrome -- logo, header, navigation, footer
   -------------------------------------------------------------------------- */

/* The logo is set as live text, not an image: crisp at every density,
   selectable, translatable, and it inherits the ground's colour. It is also
   the literal source of the whole type system, so it belongs in CSS. */
.logo {
  position: relative;
  z-index: 90;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex: none;
}

.logo__mark {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--heading);
}

.logo__rule {
  width: 1px;
  height: 30px;
  background: var(--rule);
  flex: none;
}

.logo__word {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-family: var(--font-ui);
  font-size: 0.625rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--heading);
}

/* Header ------------------------------------------------------------------ */

.site-header {
  position: fixed;
  /* Longhands before the shorthand: Safari before 14.5 (iOS 14.4 and
     older) does not know `inset`, and a fixed header with no offsets
     is not at the top of anything. Same pair everywhere `inset` is used. */
  top: 0; right: 0; bottom: auto; left: 0;
  inset: 0 0 auto 0;
  z-index: 80;
  background: transparent;
  transition:
    background-color var(--dur-base) var(--ease-out-quart),
    padding var(--dur-base) var(--ease-out-quart);
  padding-block: 26px;
}

.site-header::after {
  content: '';
  position: absolute;
  top: auto; right: 0; bottom: 0; left: 0;
  inset: auto 0 0 0;
  height: 1px;
  background: var(--line);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.site-header.is-scrolled {
  /* Translucent over a blur rather than solid paper: the page shows
     through, faintly, which is what tells the reader the bar is floating
     over content rather than sitting on top of it. Browsers without
     backdrop-filter get the solid paper they always had. */
  background: var(--paper);
  padding-block: 16px;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .site-header.is-scrolled {
    background: rgba(255, 255, 255, 0.86);
    -webkit-backdrop-filter: blur(12px) saturate(1.2);
    backdrop-filter: blur(12px) saturate(1.2);
  }

  /* Never while the phone menu is open. A backdrop-filter makes its element
     the containing block for every position:fixed descendant -- and the
     menu panel is a fixed descendant of the header. With the blur on, the
     panel's `inset: 0` meant the header's 80px, not the screen's: the whole
     menu drew inside the bar, "Home" on top of the logo, and scrolled in
     there. That is the client's screenshot. It only happened when the menu
     was opened after scrolling, because the blur only exists when scrolled,
     which is why opening it at the top of the page never showed it. The
     panel is opaque paper, so the header needs no blur behind it anyway.
     (Found 2026-09-18 in WebKit; the same rule exists for filter and
     transform, so neither may ever be set on the header either.) */
  body.nav-open .site-header.is-scrolled {
    background: var(--paper);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

.site-header.is-scrolled::after { transform: scaleX(1); }

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
}

.site-header__collapse {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(16px, 2.2vw, 32px);
  flex: 1 1 auto;
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: clamp(13px, 1.5vw, 22px);
}

.site-nav__list a {
  position: relative;
  display: block;
  font-size: var(--step-nav);
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--heading);
  padding-block: 6px;
  white-space: nowrap;
}

.site-nav__list a::after {
  content: '';
  position: absolute;
  top: auto; right: 0; bottom: 0; left: 0;
  inset: auto 0 0 0;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out-expo);
}

@media (hover: hover) {
  .site-nav__list a:hover::after { transform: scaleX(1); }
}
.site-nav__list a:focus-visible::after { transform: scaleX(1); }
.site-nav__list a[aria-current='page']::after { transform: scaleX(1); }

.site-header__aside {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 22px);
}

.lang {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: var(--step-nav);
  font-weight: 600;
  letter-spacing: 0.13em;
}

.lang__current { color: var(--heading); }

.lang__sep {
  width: 1px;
  height: 11px;
  background: var(--gold);
}

.lang__alt {
  color: var(--taupe-ink);
  transition: color var(--dur-fast) var(--ease-out-quart);
}
@media (hover: hover) {
  .lang__alt:hover { color: var(--gold-ink); }
}

.btn--sm {
  padding: 13px 19px;
  font-size: 0.6875rem;
  white-space: nowrap;
}

/* Decision rows -----------------------------------------------------------
   One layout for every place a section offers a way out: a button or two
   and, usually, a quieter link beside them. Built by tools/cta.py, so the
   home page's seven rows and the four on the services page are one thing
   in one place. The row wraps; on a phone the pieces stack in order, the
   primary first. Section-specific classes (.methodology__actions and so on)
   adjust only the spacing above. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 28px;
  margin-top: clamp(28px, 3.2vw, 40px);
  max-width: none;
}
.actions--center { justify-content: center; }

/* The WhatsApp button, in the page rather than floating. The glyph is the
   same path as the floating button's, at text size; navy, like everything
   else -- tools/contact.py says why it is not green. */
.btn--wa { gap: 10px; }
.btn__glyph { display: inline-flex; }
.btn__glyph svg { width: 18px; height: 18px; display: block; }

/* On a phone the rows centre. The client's screenshot: a full-width
   button with its link hanging off the left edge beneath it. Centred, the
   link sits under the middle of the button it accompanies. */
@media (max-width: 639px) {
  .actions,
  .hero__actions { justify-content: center; text-align: center; }
}
@media (max-width: 479px) {
  .actions .btn,
  .hero__actions .btn { width: 100%; }
}

.header-sentinel {
  position: absolute;
  top: 0; left: 0;
  width: 1px; height: 110px;
  pointer-events: none;
}

/* Menu trigger: two hairlines, because the whole site is hairlines.
   No morphing-to-X gimmick. */
.nav-toggle {
  position: relative;
  z-index: 90;
  display: none;
  align-items: center;
  gap: 12px;
  font-size: var(--step-meta);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--heading);
  padding: 8px 0;
}

.nav-toggle__bars {
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.nav-toggle__bar {
  display: block;
  width: 26px;
  height: 1px;
  background: var(--navy);
  transition: transform var(--dur-base) var(--ease-out-expo);
}

/* Open state shortens the lower hairline rather than morphing into an X.
   The rest of the site says what it means with rule lengths; so does this. */
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:last-child {
  transform: scaleX(0.5);
  transform-origin: left center;
}

/* Footer ------------------------------------------------------------------
   Generated by tools/footer.py. Rebuilt 2026-09-16 from a three-column
   afterthought into the page's closing statement: brand, navigation, the
   four services as tags, the company's legal identity, consumer-protection
   routes, and the policies.

   Colour, inside the brand's rules. The ground is navy; the light comes from
   a gold hairline along the top edge, a radial lift in the top corner
   (--navy-lift, the same device the methodology uses), gold-light headings
   and icons, and paper for the one sentence that matters. Gold is never a
   fill here either -- the tags are hairline pills, the icons are strokes. */

.site-footer {
  position: relative;
  background: var(--ground);
  color: var(--text);
  padding-block: clamp(64px, 7vw, 104px) 36px;
  /* Full-bleed to the bottom edge on purpose: inset here would leave a strip
     of paper under the footer. The top corners still take the panel radius so
     the page closes on a shape rather than on a butt join. */
  margin-top: var(--panel-gap);
  border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  overflow: hidden;
  /* The lift: a soft light in the top-right, so the panel reads as lit
     rather than flat. Same gradient the methodology section carries. */
  background-image: radial-gradient(120% 70% at 85% 0%, var(--navy-lift) 0%, transparent 60%);
}

/* The gold edge. A gradient hairline, brightest at the left where the brand
   sits and fading out -- a rule, not a border, so it reads as light along the
   top of the panel. */
.site-footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--gold) 0%, rgba(215, 193, 133, 0.35) 45%, transparent 100%);
}

.site-footer .ico { flex: none; color: var(--gold-light); }

/* -- Top: brand and navigation --------------------------------------------- */

.site-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 56px;
}

.site-footer__descriptor {
  margin-top: 22px;
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--gold-light);
  max-width: none;
}

/* The approved headline, once more, where the page ends. Serif, paper, and
   the only line in the footer set larger than body size. */
.site-footer__headline {
  margin: 18px 0 0;
  max-width: 22ch;
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.3;
  color: var(--paper);
}

/* Icon-only channels under the brand. Hairline circles, gold-light strokes;
   the circle fills to navy-lift on hover, never to gold. */
.site-footer__social {
  display: flex;
  gap: 10px;
  margin-top: clamp(22px, 2.6vw, 30px);
}

.site-footer__social a {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgba(215, 193, 133, 0.32);
  border-radius: 50%;
  transition: border-color var(--dur-fast) var(--ease-out-quart),
              background-color var(--dur-fast) var(--ease-out-quart),
              transform var(--dur-fast) var(--ease-out-quart);
}

@media (hover: hover) {
  .site-footer__social a:hover {
    border-color: var(--gold-light);
    background: var(--navy-lift);
    transform: translateY(-2px);
  }
}

.site-footer__nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 40px;
}

.site-footer__heading {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--gold-light);
  margin-bottom: 18px;
  font-weight: 600;
}

.site-footer__nav ul { display: grid; gap: 11px; }

.site-footer__nav a {
  font-size: var(--step-ui);
  color: var(--text);
  overflow-wrap: anywhere;
  transition: color var(--dur-fast) var(--ease-out-quart);
}

/* Thumb room. A 16px line of text is a 16px target; WCAG 2.5.8 asks for 24.
   Padding widens the hit area and the negative margin gives the space back,
   so the lists look exactly as they did and are simply easier to tap. */
.site-footer__nav a,
.site-footer__consumer a,
.site-footer__legal a,
.site-footer__top-link {
  padding-block: 6px;
  margin-block: -6px;
}
/* The uppercase link keeps its 6px of room under the text for the rule
   that draws on hover; the extra room above is the hit area. The prose
   variant is inline and sets its own padding. */
.link-rule:not(.link-rule--prose) { padding-top: 6px; margin-top: -6px; }

/* Contact channels carry an icon each. Aligned as a row so the text sits on
   the icon's centre line, and the icon lifts with the text on hover rather
   than staying dim on its own. */
.site-footer__channels a {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.site-footer__channels .ico { width: 18px; height: 18px; }

@media (hover: hover) {
  .site-footer__nav a:hover { color: var(--paper); }
  .site-footer__nav a:hover .ico { color: var(--paper); }
}

/* -- Tags: the four services ----------------------------------------------- */

.site-footer__tags { margin-top: clamp(44px, 5vw, 64px); }

.site-footer__tags ul {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.site-footer__tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px 9px 12px;
  border: 1px solid rgba(215, 193, 133, 0.32);
  border-radius: var(--r-pill);
  font-size: var(--step-nav);
  letter-spacing: 0.02em;
  color: var(--text);
  transition: border-color var(--dur-fast) var(--ease-out-quart),
              background-color var(--dur-fast) var(--ease-out-quart),
              color var(--dur-fast) var(--ease-out-quart);
}

.site-footer__tag .ico { width: 16px; height: 16px; }

@media (hover: hover) {
  .site-footer__tag:hover {
    border-color: var(--gold-light);
    background: var(--navy-lift);
    color: var(--paper);
  }
}

.site-footer__divider { margin-block: clamp(36px, 4vw, 52px) clamp(28px, 3vw, 36px); }

/* -- The record: identity and consumer protection -------------------------- */

.site-footer__record {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 36px 64px;
}

.site-footer__id {
  display: grid;
  gap: 8px 24px;
  margin: 0;
  font-size: 0.8125rem;
}

.site-footer__id-row {
  display: grid;
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  gap: 12px;
}

.site-footer__id dt { color: var(--gold-light); }
.site-footer__id dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }

.site-footer__id-more { margin: 16px 0 0; max-width: none; }
.site-footer__id-more .link-rule { font-size: var(--step-meta); }

.site-footer__consumer ul { display: grid; gap: 9px; font-size: 0.8125rem; }
.site-footer__consumer a { color: var(--text); transition: color var(--dur-fast) var(--ease-out-quart); }

@media (hover: hover) {
  .site-footer__consumer a:hover { color: var(--paper); }
}

/* -- Bottom ---------------------------------------------------------------- */

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 16px 32px;
  font-size: 0.8125rem;
  color: var(--text);
}

.site-footer__legal p { max-width: none; margin: 0; }
.site-footer__legal ul { display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: center; }

.site-footer__top-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--gold-light);
}
.site-footer__top-link .ico { width: 16px; height: 16px; }

@media (hover: hover) {
  .site-footer__legal a:hover { color: var(--paper); }
  .site-footer__top-link:hover .ico { color: var(--paper); }
}

@media (max-width: 899px) {
  /* Two columns of links at phone width leaves Contact 160px, which breaks a
     phone number across two lines and an email address mid-word. The third
     column takes the whole row below the other two. */
  .site-footer__nav > div:last-child { grid-column: 1 / -1; }
  .site-footer__channels a { overflow-wrap: normal; }
}

@media (max-width: 559px) {
  /* Label above value: "BG PERFORMANCE SYSTEMS S.R.L." does not fit beside
     an 11rem label on a phone. */
  .site-footer__id-row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .site-footer__id { gap: 12px; }
}

@media (min-width: 720px) {
  .site-footer__record { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
}

@media (min-width: 900px) {
  .site-footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 72px; }
  /* Three columns, the third wider: it carries the email address, which is
     the one item that must not wrap mid-word, and at equal widths it did --
     "bgperforman / ce.ro". */
  .site-footer__nav { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.5fr); }
  .site-footer__channels a { overflow-wrap: normal; }
}

/* -- Navigation panels ------------------------------------------------------
   Generated by tools/nav.py: two items (Services, Insights) carry a
   disclosure button and a panel. On desktop the panel floats under the bar,
   paper on a hairline, with a gold rule along its top edge; on the phone the
   same panel unfolds inline inside the menu. Both states are driven by the
   `hidden` attribute, which the script toggles -- there is no second
   mechanism to disagree with it. */

.site-nav__item { position: relative; display: flex; align-items: center; gap: 2px; }

/* The chevron. A real button, set to look like part of the link beside it:
   same colour, no chrome, and it turns when its panel is open. */
.site-nav__more {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--heading);
  cursor: pointer;
  transition: transform var(--dur-base) var(--ease-out-expo),
              background-color var(--dur-fast) var(--ease-out-quart);
}
.site-nav__more svg { transition: transform var(--dur-base) var(--ease-out-expo); }
.site-nav__more[aria-expanded="true"] svg { transform: rotate(180deg); }
.site-nav__more:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: 2px; }

@media (hover: hover) {
  .site-nav__more:hover { background: var(--paper-warm); }
}

/* The panel sits inside .site-nav__list, so every <a> in it inherits the
   bar's link treatment -- small caps, tracking, nowrap, the gold underline.
   None of that belongs on a list of article titles. Reset it at the panel
   boundary, once, rather than fighting it rule by rule below. */
.menu a {
  font-size: inherit;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  white-space: normal;
  padding-block: 0;
}
.menu a::after { content: none; }
.menu .link-rule { font-size: var(--step-meta); letter-spacing: var(--track-meta); text-transform: uppercase; font-weight: 600; }

.menu__eyebrow { color: var(--gold-ink); margin-bottom: 14px; }

.menu__list { display: grid; gap: 2px; }

/* Two classes, because `.site-nav__list a` (0,1,1) outranks `.menu__link`
   (0,1,0) and sets display: block -- which put a wrapped title's second
   line under the numeral instead of under the title. */
.menu a.menu__link {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 10px 12px;
  margin-inline: -12px;
  border-radius: var(--r-sm);
  color: var(--heading);
  transition: background-color var(--dur-fast) var(--ease-out-quart);
}

.menu a.menu__link--article { flex-direction: column; align-items: flex-start; gap: 5px; padding-block: 12px; }

@media (hover: hover) {
  .menu__link:hover { background: var(--paper-warm); }
}
.menu__link:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: -2px; }

.menu__num {
  flex: none;
  min-width: 2ch;
  font-family: var(--font-display);
  font-size: 0.95rem;
  color: var(--gold);
}

.menu__title {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 1.3;
  color: var(--heading);
}

.menu__meta { color: var(--meta); }

.menu__all { margin: 16px 0 0; max-width: none; }
.menu__all .link-rule { font-size: var(--step-meta); }

/* The featured card beside the services: the diagnostic entry, on warm.
   A card, not a button -- the whole thing is one link. */
.menu__feature {
  display: block;
  padding: clamp(18px, 2vw, 24px);
  border-radius: var(--r-lg);
  background: var(--paper-warm);
  color: var(--heading);
  transition: background-color var(--dur-fast) var(--ease-out-quart);
}
.menu__feature .menu__eyebrow { margin-bottom: 10px; }
.menu__feature-title {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 1.125rem;
  line-height: 1.3;
  color: var(--heading);
  max-width: none;
}
.menu__feature-body { margin: 0 0 14px; font-size: var(--step-meta); letter-spacing: 0.02em; color: var(--taupe-ink); max-width: none; }
.menu__feature-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--gold-ink);
}
@media (hover: hover) {
  .menu__feature:hover { background: #F0ECE4; }
}
.menu__feature:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: 2px; }

/* Desktop: the panel floats. ------------------------------------------------ */
@media (min-width: 1240px) {
  .menu {
    position: absolute;
    top: calc(100% + 14px);
    left: 50%;
    transform: translate(-50%, 6px);
    width: max-content;
    max-width: min(720px, calc(100vw - 2 * var(--gutter)));
    padding: clamp(20px, 2.2vw, 28px);
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: 0 24px 60px rgba(0, 28, 75, 0.14), 0 2px 8px rgba(0, 28, 75, 0.06);
    opacity: 0;
    transition: opacity var(--dur-fast) var(--ease-out-quart),
                transform var(--dur-base) var(--ease-out-expo);
    z-index: 90;
  }
  /* The gold rule along the top edge -- the same device the footer's top
     edge uses, so the two ends of the page speak the same language. */
  .menu::before {
    content: "";
    position: absolute;
    top: -1px;
    left: var(--r-lg);
    right: var(--r-lg);
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
  }
  /* A hover bridge over the 14px gap, so the cursor can travel from the
     item down into the panel without the panel closing under it. */
  .menu::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -16px;
    height: 16px;
  }
  .menu:not([hidden]) { opacity: 1; transform: translate(-50%, 0); }

  .menu__inner--services {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
    gap: clamp(24px, 2.6vw, 36px);
    min-width: 640px;
  }
  .menu__inner--insights { min-width: 440px; }
  .menu__list--articles { max-width: 46ch; }

  /* Panels are centred under their item, except Services: second of seven,
     its centred panel spilled ten pixels off the left of a 1240px viewport.
     Anchored to the item's left edge instead. */
  .site-nav__item--start .menu { left: 0; transform: translate(0, 6px); }
  .site-nav__item--start .menu:not([hidden]) { transform: translate(0, 0); }
}

/* Phone: the panel unfolds in place inside the menu. ------------------------ */
@media (max-width: 1239px) {
  .site-nav__item { flex-wrap: wrap; align-items: center; }
  .site-nav__item > a { flex: 1 1 auto; }
  .site-nav__more { width: 44px; height: 44px; margin-right: -9px; }
  .menu {
    flex: 1 1 100%;
    padding: 4px 0 18px;
  }
  .menu__inner--services { display: grid; gap: 18px; }
  .menu__feature { margin-top: 6px; }
  .menu__link { margin-inline: 0; padding-inline: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .menu, .site-nav__more svg { transition: none; }
}

/* --------------------------------------------------------------------------
   7. Mobile navigation
   Not a shrunken desktop menu: a full-screen editorial index, large serif,
   hairline-divided.
   -------------------------------------------------------------------------- */

/* Collapse point is set by Romanian, the longest language, not English.
   Measured: the RO header needs 1207px of viewport (logo 164 + nav 541 +
   aside 358 + gaps 64 + gutters 80). 1240 leaves margin and keeps common
   1280px laptops on the full desktop nav. */
@media (max-width: 1239px) {
  .nav-toggle { display: inline-flex; }

  .site-header__collapse {
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    inset: 0;
    z-index: 70;
    /* Sits under the header band so the logo and trigger stay visible. */
    flex-direction: column;
    align-items: stretch;
    /* Start, not centre. Centred, a list taller than the screen overflows
       equally in both directions: the top half slides up under the header
       and no amount of scrolling reaches it -- on a phone, "Acasă" sat
       on top of the logo. Started from the top, the list begins below the
       header and scrolls as a list should. The safe-area insets are for
       the notch and the home indicator. */
    justify-content: flex-start;
    gap: 0;
    /* The panel starts under the header band, not under the screen's top
       edge: --header-h is measured by initNavigation when the menu opens.
       Started at 0 and padded instead, a scrolled list showed its upper
       items through the band, cut by the header's hairline. */
    top: var(--header-h, 96px);
    padding: 20px var(--gutter) calc(48px + env(safe-area-inset-bottom, 0px));
    background: var(--paper);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition:
      opacity var(--dur-base) var(--ease-out-expo),
      transform var(--dur-base) var(--ease-out-expo),
      visibility 0s linear var(--dur-base);
  }

  .site-header__collapse.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }

  .site-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .site-nav__list li + li { border-top: 1px solid var(--line); }

  .site-nav__list a {
    font-family: var(--font-display);
    font-size: clamp(1.75rem, 1.2rem + 2.6vw, 2.5rem);
    font-weight: 400;
    letter-spacing: var(--track-display);
    text-transform: none;
    padding-block: 18px;
  }

  .site-nav__list a::after { inset: auto auto 14px 0; width: 34px; }

  .site-header__aside {
    margin-top: 44px;
    justify-content: space-between;
  }

  body.nav-open { overflow: hidden; }
}

/* The language switch and the CTA sit side by side in the open panel. In
   Romanian that row needs ~369px, which does not fit a 375px screen, so it
   stacks. Sized by the longest language, like the nav collapse point. */
@media (max-width: 460px) {
  .site-header__aside {
    flex-direction: column;
    align-items: stretch;
    gap: 20px;
  }
  .site-header__aside .btn { width: 100%; }
}

@media (max-width: 420px) {
  .logo__word { font-size: 0.5625rem; letter-spacing: 0.16em; }
  .logo__mark { font-size: 1.5rem; }

  /* Hidden visually, still the button's accessible name. display:none here
     would leave the trigger nameless for screen readers. */
  .nav-toggle__label {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* --------------------------------------------------------------------------
   8. Section 01 -- Hero
   Editorial left, portrait right, bleeding to the viewport edge with a gold
   seam on the join. The portrait keeps its taupe ground: that colour is
   already in the palette as --paper-warm, so it reads as part of the system
   rather than a photograph dropped onto white.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  background: var(--paper);
  overflow: hidden;
}

.hero__inner { display: grid; grid-template-columns: minmax(0, 1fr); }

.hero__editorial {
  padding-block: clamp(132px, 17vh, 190px) clamp(40px, 6vw, 64px);
  padding-inline: var(--gutter);
  /* Typography leads the parallax; the portrait trails it. */
  transform: translate3d(calc(var(--px, 0) * 4px), calc(var(--py, 0) * 2px), 0);
}

.hero__title { margin-bottom: clamp(26px, 3vw, 38px); }

/* Masked line reveal. The padding/margin pair keeps descenders and Romanian
   diacritics from being clipped by the mask. */
.hero__title .line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.hero__title .line > span {
  display: block;
  transform: translateY(105%);
  transition: transform 1s var(--ease-out-expo);
}

.hero__title .line:last-child > span { color: var(--navy); }

.hero__lead { margin-bottom: clamp(34px, 4vw, 46px); }

.hero__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 20px 34px;
}

.hero__portrait {
  position: relative;
  background: var(--paper-warm);
  border-radius: var(--r-xl);
  overflow: hidden;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.1s var(--ease-out-expo);
}

.hero__portrait picture { display: block; height: 100%; }

.hero__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 22%;
  transform: translate3d(calc(var(--px, 0) * -8px), calc(var(--py, 0) * -5px), 0) scale(1.03);
}

.hero__seam {
  position: absolute;
  z-index: 2;
  top: 0; right: auto; bottom: 0; left: 0;
  inset: 0 auto 0 0;
  width: 1px;
  background: var(--gold);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 1s var(--ease-out-expo);
}

/* The hero's proof band.

   Adapted from a component whose equivalent slot read "2.4K currently on the
   waitlist" beside four stock avatars. The slot was worth keeping; the
   contents were invented. These three figures are the founder's, already
   published on Results and already translated.

   The caption is not decoration and must not be dropped. Acceptance item 6
   requires the figures to be attributed to experience before the company; a
   band showing "EUR 30K -> EUR 250K" without it would read as BG Performance
   Consulting's own result on the first screen of the site.

   No box, no fill, no pill. Hairline-separated values on the page's own
   ground, which is how every other set of figures on this site is set. */
/* Below the buttons now, so the margin that used to separate it from the
   headline separates it from the actions instead. */
.proof { margin-top: clamp(32px, 4vw, 48px); }

/* One figure per row, values aligned on their right edge.

   Two earlier attempts are worth keeping in view. The first was a wrapping
   flex row with a hairline drawn by `.proof__item + .proof__item::before`;
   the growth figure is long enough to wrap on its own and took the separator
   with it, leaving a rule hanging at the start of a line. The second -- two
   columns with the growth figure forced onto its own row -- fixed that and
   produced the complaint that brought us here: two short figures above one
   long one reads as a broken grid, and the client saw it as three different
   type sizes. Measured, all three are 32px with a 22px cap height. Nothing
   was the wrong size; the composition was wrong.

   Three rows settle it. Each figure gets the same weight because each gets
   the same amount of line, and the numerals align on the right, which is how
   figures are actually compared. No separator is needed once nothing shares
   a line. */
.proof__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  justify-content: start;
  gap: clamp(8px, 1vw, 12px) clamp(14px, 1.8vw, 20px);
}

/* Without subgrid each row is simply value-then-label. Ragged, readable, and
   never broken -- which is the bar a fallback has to clear. */
.proof__item {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: 10px;
}

/* With it, all three values share the list's first column and right-align
   inside it, so the labels start at one x. */
@supports (grid-template-columns: subgrid) {
  .proof__item {
    display: grid;
    grid-template-columns: subgrid;
    align-items: baseline;
    gap: 0 clamp(14px, 1.8vw, 20px);
  }
  .proof__value { justify-self: end; }
}

.proof__value {
  font-family: var(--font-display);
  font-size: var(--step-h3);
  font-weight: 400;
  line-height: 1;
  color: var(--heading);
  white-space: nowrap;
}

.proof__label {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
}

.proof__note { margin: clamp(12px, 1.4vw, 16px) 0 0; }

.proof__link {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--taupe-ink);
}

.proof__link:hover { color: var(--heading); }

@media (max-width: 560px) {
  /* The value column stops being its own column: at 360px, reserving 198px
     for "EUR30K -> EUR250K" leaves the label beside it about twelve
     characters of measure. Value above label instead. */
  .proof__list { grid-template-columns: minmax(0, 1fr); }
  .proof__item { display: block; }
  .proof__label { display: block; margin-top: 4px; }
}

/* Entrance choreography. One rule maps data-seq to a delay, so the sequence
   is legible in the markup instead of buried in JavaScript. */
.hero [data-seq] {
  opacity: 0;
  transition-property: opacity, transform, clip-path;
  transition-duration: var(--dur-slow);
  transition-timing-function: var(--ease-out-expo);
}

.hero [data-seq='2'] { transition-delay: 60ms; }
.hero [data-seq='3'] { transition-delay: 340ms; }
.hero [data-seq='4'] { transition-delay: 520ms; }
.hero [data-seq='5'] { transition-delay: 640ms; }
.hero [data-seq='6'] { transition-delay: 760ms; }
.hero [data-seq='7'] { transition-delay: 900ms; }
.hero [data-seq='8'] { transition-delay: 1020ms; }
.hero [data-seq='9'] { transition-delay: 1140ms; }

.hero.is-entered [data-seq] { opacity: 1; }
.hero.is-entered .hero__portrait { clip-path: inset(0 0 0 0); }
.hero.is-entered .hero__seam { transform: scaleY(1); }
.hero.is-entered .hero__title .line > span { transform: none; }
.hero.is-entered .hero__eyebrow::before { transform: scaleX(1); }

/* Fallback: if the script never runs, the hero is simply present. */
html:not(.js) .hero [data-seq] { opacity: 1; }
html:not(.js) .hero__portrait { clip-path: none; }
html:not(.js) .hero__seam { transform: scaleY(1); }
html:not(.js) .hero__title .line > span { transform: none; }

@media (max-width: 1023px) {
  .hero__portrait {
    height: clamp(320px, 52vh, 460px);
    border-top: 1px solid var(--line);
  }

  /* The 1.03 scale exists only to give the pointer parallax headroom, and
     parallax is disabled below 1024px. Without it the image overhangs its
     clipped box for no reason. */
  .hero__portrait img { transform: none; }
  .hero__seam { inset: 0 0 auto 0; width: auto; height: 1px; transform: scaleX(0); transform-origin: left center; }
  .hero.is-entered .hero__seam { transform: scaleX(1); }
}

@media (min-width: 1024px) {
  .hero__inner {
    grid-template-columns:
      [full-start] minmax(var(--gutter), 1fr)
      [content-start] minmax(0, calc(var(--container) * 0.60))
      [mid] minmax(0, calc(var(--container) * 0.40))
      [content-end] minmax(var(--gutter), 1fr)
      [full-end];
    align-items: center;
    padding-top: clamp(92px, 10vh, 124px);
    min-height: min(96vh, 940px);
  }

  .hero__editorial {
    grid-column: content-start / mid;
    padding-inline: 0 clamp(32px, 4vw, 64px);
    padding-block: clamp(56px, 8vh, 96px) clamp(64px, 9vh, 110px);
  }

  .hero__portrait {
    grid-column: mid / full-end;
    align-self: stretch;
    min-height: 100%;
  }
}

/* --------------------------------------------------------------------------
   8b. Staggered groups
   Children reveal in sequence when their ancestor enters. The index is set
   once by JS; the timing lives here.
   -------------------------------------------------------------------------- */

.stagger > * {
  opacity: 0;
  transform: translateY(var(--reveal-y));
  transition:
    opacity var(--dur-slow) var(--ease-out-expo),
    transform var(--dur-slow) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 90ms);
}

/* --i was read but never written, anywhere, so every child fell back to 0 and
   the whole class arrived at once -- a stagger in name only since it was
   written. nth-child supplies the index in CSS, which keeps it working with
   scripting off and needs no JavaScript to walk the DOM. Ten covers the
   longest group on the site; anything past it simply arrives with the tenth. */
.stagger > *:nth-child(1) { --i: 0; }
.stagger > *:nth-child(2) { --i: 1; }
.stagger > *:nth-child(3) { --i: 2; }
.stagger > *:nth-child(4) { --i: 3; }
.stagger > *:nth-child(5) { --i: 4; }
.stagger > *:nth-child(6) { --i: 5; }
.stagger > *:nth-child(7) { --i: 6; }
.stagger > *:nth-child(8) { --i: 7; }
.stagger > *:nth-child(9) { --i: 8; }
.stagger > *:nth-child(n+10) { --i: 9; }

.is-revealed .stagger > * { opacity: 1; transform: none; }

html:not(.js) .stagger > * { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   8c. Section 02 -- The Problem
   The four factors are the client's own sentence, set as a ledger. Each
   phrase is followed by a gold leader rule that draws out to the margin.
   No numerals here: these are simultaneous conditions, not a sequence, and
   numbering them would be decoration pretending to be structure.
   -------------------------------------------------------------------------- */


.factors { margin-bottom: clamp(40px, 5vw, 60px); }

.factor {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 40px);
  padding-block: clamp(13px, 1.5vw, 19px);
}

.factor__label {
  /* Was flex: none, so the leader rule could take the remainder -- and so a
     phrase longer than the row ran straight out of the container. Below
     640px the ledger stacks and above 1000px it stacks again inside the
     split, which left the 640-999px band with nothing: on a 768px tablet
     the Romanian home page scrolled sideways by 300px. Shrinkable now: the
     phrase wraps and the rule still takes what is left. */
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--step-h3);
  line-height: 1.2;
  letter-spacing: var(--track-display);
  color: var(--heading);
}

.factor__rule { flex: 1 1 auto; min-width: 32px; }

/* The phrase is flex:none so the leader rule takes the remainder. That works
   only while the phrase fits. Romanian phrases are ~20% longer and overflow a
   narrow screen, so below 640px the ledger stacks: phrase, then rule beneath. */
@media (max-width: 639px) {
  .factor { flex-wrap: wrap; gap: 10px; }
  .factor__label { flex: 1 1 100%; }
  .factor__rule { flex: 1 1 100%; }
}


/* --------------------------------------------------------------------------
   8d. Section 03 -- Services
   Four editorial fields, not four cards. No borders, no boxes, no icons.
   -------------------------------------------------------------------------- */


/* The service overview: a bento rather than four equal boxes.

   The asymmetry is the content's, not a flourish. The masters give service 04
   roughly twice the copy of the other three because it is the
   organisation-level one, so it takes the full row below and splits its own
   contents in two -- which gives that paragraph a readable measure instead of
   squeezing it into a third of the width.

   Cards are hairline-bordered, never filled. The ordinal is the only large
   element and it is set in the rule colour, so it reads as structure rather
   than as a number that means something. Gold stays a 56px line. */
.svc-bento {
  list-style: none;
  margin: clamp(30px, 3.6vw, 46px) 0 0;
  padding: 0;
  display: grid;
  gap: clamp(14px, 1.6vw, 20px);
}

.svc-card {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(26px, 3vw, 38px);
  display: grid;
  align-content: start;
  gap: clamp(16px, 2vw, 24px);
  transition: border-color var(--dur-base) var(--ease-out-quart);
}

.svc-card:hover { border-color: var(--gold); }

.svc-card__num {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(2rem, 1.4rem + 2vw, 2.75rem);
  font-weight: 300;
  line-height: 0.85;
  color: var(--rule);
}

.svc-card__rule { width: 56px; display: block; margin-bottom: clamp(16px, 2vw, 22px); }
.svc-card__title { font-size: var(--step-h3); margin-bottom: 14px; max-width: 22ch; }
.svc-card__body { max-width: 52ch; color: var(--text); opacity: 0.78; }

@media (min-width: 860px) {
  .svc-bento { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .svc-card { grid-column: span 2; }

  /* The full row, and a two-column interior: the ordinal and the rule on the
     left, the long paragraph on the right. */
  .svc-card--wide {
    grid-column: span 6;
    grid-template-columns: minmax(0, 5rem) minmax(0, 1fr);
    align-items: start;
    column-gap: clamp(28px, 4vw, 56px);
  }
  .svc-card--wide .svc-card__body { max-width: 68ch; }

  /* No rule in the wide card. In the narrow ones it sits directly under the
     ordinal and separates it from the title; here the ordinal has its own
     column, so the rule ends up floating at the top of the text column with
     nothing above it to separate. The column division already does its job. */
  .svc-card--wide .svc-card__rule { display: none; }
}

@media (max-width: 859px) {
  /* Two per row once there is room for two, one below that. The wide card has
     no interior columns here -- there is not the width for them. */
  .svc-card--wide { grid-template-columns: minmax(0, 1fr); }
}

@media (min-width: 600px) and (max-width: 859px) {
  .svc-bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .svc-card--wide { grid-column: span 2; }
}

@media print {
  .svc-card { break-inside: avoid; border-color: #999; }
}


@media (hover: hover) {
  /* The rule extends, the title steps forward, the copy resolves.
     No scaling, no lift, no shadow. */
      }

.services__cta { margin-top: clamp(48px, 6vw, 78px); max-width: none; }

/* The card's own way out, pinned to the bottom edge. Cards in one row
   stretch to the tallest, so four links at four different heights would
   read as a mistake; the text column becomes a flex column and the link
   takes whatever is left above it. The wide card is alone in its row and
   keeps its two-column interior, so it is left out of the stretch. */
.svc-card:not(.svc-card--wide) { grid-template-rows: auto minmax(0, 1fr); }
.svc-card__text { display: flex; flex-direction: column; align-items: flex-start; }
/* Two classes: the tap-target rule on .link-rule:not(.link-rule--prose)
   sets a -6px margin-top at (0,2,0), which beat the single class here and
   put the four links back at four heights. */
.svc-card__text .svc-card__link { margin-top: auto; padding-top: 12px; }


/* --------------------------------------------------------------------------
   8e. Section 04 -- Methodology  (the signature section)
   A gold spine crosses the section; five numerals hang beneath it and
   activate in sequence. Numerals appear nowhere else on the page, because
   this is the only content that is genuinely ordered.
   -------------------------------------------------------------------------- */

.methodology {
  position: relative;
  overflow: hidden;
  /* Fallback ground for the light field. If WebGL never initialises, the
     section is still lit -- it just stops moving. */
  background-image: radial-gradient(120% 80% at 78% 12%, var(--navy-lift) 0%, transparent 60%);
  background-color: var(--navy);
}

.methodology__inner { position: relative; z-index: 1; }

.light-field {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.6s var(--ease-out-expo);
}

.light-field.is-live { opacity: 1; }

.methodology__head { margin-bottom: clamp(56px, 7vw, 96px); }
.methodology__head h2 { max-width: 20ch; margin-bottom: clamp(20px, 2.4vw, 30px); }
.methodology__lead { max-width: 58ch; }

/* Rebuilt 2026-09-16 at the client's request: cards, icons, a rail that
   fills as the sequence activates. The note above about the halo that was
   cut still stands as history -- the section's premise changed when the
   client asked for it to. What did not change: gold is a stroke, a ring, a
   line and a numeral here, and never a surface. */

.stages {
  position: relative;
  display: grid;
  gap: clamp(20px, 2.4vw, 28px);
  padding-top: clamp(40px, 4.6vw, 56px);
  /* Set by initMethodology as stages activate: 0, 0.2, 0.4 ... 1. */
  --progress: 0;
}

/* The rail. Two lines on top of each other: a dim hairline the full width,
   and over it the progress, gold-light, scaled to how far the sequence has
   activated. The reader scrolls, stages light up one by one, and the line
   fills in behind them -- a process, drawn as one. */
.stages__rail,
.stages__progress {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  pointer-events: none;
}
.stages__rail { background: rgba(215, 193, 133, 0.22); }
.stages__progress {
  /* The 1px is repeated here on purpose: the gold-fill lint reads each rule
     on its own, and a gold background with no hairline constraint in the
     same block is exactly what it exists to refuse. */
  height: 1px;
  background: var(--gold-light);
  transform: scaleX(var(--progress));
  transform-origin: left center;
  transition: transform 900ms var(--ease-out-expo);
}

.stage { position: relative; }

/* The node on the rail: a ring, navy inside, that brightens and gains a soft
   halo when its stage activates. A ring and a halo, never a filled dot --
   the interior stays navy, so gold stays what it is everywhere else. */
.stage__node {
  position: absolute;
  top: calc(-1 * clamp(40px, 4.6vw, 56px));
  left: 50%;
  width: 11px;
  height: 11px;
  margin: -5px 0 0 -5px;
  border: 1px solid rgba(215, 193, 133, 0.45);
  border-radius: 50%;
  background: var(--navy);
  transition: border-color 600ms var(--ease-out-quart), box-shadow 600ms var(--ease-out-quart);
}
.stage.is-active .stage__node {
  border-color: var(--gold-light);
  box-shadow: 0 0 0 4px rgba(215, 193, 133, 0.18), 0 0 18px rgba(215, 193, 133, 0.35);
}

/* The card. Hairline on navy, the faintest lift of white behind it so it
   reads as a plane rather than a box drawn on the ground. Cards in one row
   stretch to the tallest, so five bodies of five different lengths end on
   one line -- which was the complaint. */
.stage__card {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 28px);
  border: 1px solid rgba(215, 193, 133, 0.18);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, 0.025);
  /* dim-exempt: transient. This is the not-yet state; the observer activates
     any card that reaches the viewport, and without JavaScript the rule
     below sets it to 1. Nobody reads a card at 0.45. */
  opacity: 0.45;
  transform: translateY(18px);
  transition:
    opacity var(--dur-slow) var(--ease-out-expo) calc(var(--i, 0) * 90ms),
    transform var(--dur-slow) var(--ease-out-expo) calc(var(--i, 0) * 90ms),
    border-color var(--dur-base) var(--ease-out-quart),
    background-color var(--dur-base) var(--ease-out-quart);
}
.stage.is-active .stage__card {
  opacity: 1;
  transform: none;
  border-color: rgba(215, 193, 133, 0.38);
  background: rgba(255, 255, 255, 0.045);
}

@media (hover: hover) {
  .stage.is-active .stage__card:hover {
    transform: translateY(-4px);
    border-color: var(--gold-light);
    background: rgba(255, 255, 255, 0.06);
    transition-delay: 0s;
  }
}

/* Icon left, numeral right, on one baseline row. */
.stage__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: clamp(18px, 2vw, 24px);
}

.stage__glyph {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(215, 193, 133, 0.32);
  border-radius: 50%;
  color: rgba(215, 193, 133, 0.6);
  transition: color 600ms var(--ease-out-quart), border-color 600ms var(--ease-out-quart), transform 600ms var(--ease-out-expo);
}
.stage.is-active .stage__glyph { color: var(--gold-light); border-color: rgba(215, 193, 133, 0.6); }

@media (hover: hover) {
  .stage.is-active .stage__card:hover .stage__glyph { transform: rotate(-8deg) scale(1.06); }
}

.stage__num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem);
  font-weight: 300;
  line-height: 1;
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--gold-light) 35%, transparent);
  transition: color 900ms var(--ease-out-expo);
}
.stage.is-active .stage__num { color: var(--gold-light); }

/* Smaller than the section's h3 step. Five columns on a 1200px container are
   about 168px of measure each, and "Diagnosticare" at 2rem serif broke into
   "Diagnostica / re" -- a word that breaks mid-syllable in a heading reads
   as a bug, not as a wrap. 1.375rem fits the longest Romanian title on one
   line with room to spare. */
.stage__title {
  color: var(--heading);
  font-size: clamp(1.25rem, 1.1rem + 0.5vw, 1.5rem);
  line-height: 1.2;
  margin-bottom: 12px;
  overflow-wrap: normal;
}

.stage__body {
  color: var(--text);
  font-size: var(--step-ui);
  max-width: none;
  margin: 0;
}

html:not(.js) .stage__card { opacity: 1; transform: none; }
html:not(.js) .stage__num, html:not(.js) .stage__glyph { color: var(--gold-light); }
html:not(.js) .stages__progress { transform: scaleX(1); }

/* Tablet: two and three columns. The rail stays -- each row's nodes sit
   above their own row -- but the progress line only makes sense across one
   row, so it is hidden until the five-across layout. */
@media (min-width: 640px) {
  .stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 900px) {
  .stages { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 640px) and (max-width: 1179px) {
  .stages__rail, .stages__progress, .stage__node { display: none; }
  .stages { padding-top: 0; }
}

/* Desktop: five across, the rail with its five nodes above them. */
@media (min-width: 1180px) {
  .stages { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(16px, 1.8vw, 24px); }
  .stage__title { font-size: 1.375rem; }
  /* The two long bodies (02, 04) set the height of all five. A step down in
     size buys roughly a hundred pixels across the row; below 0.875rem the
     text starts to read as a caption, so this is the floor. */
  .stage__body { font-size: 0.875rem; line-height: 1.6; }
  .stage__card { padding: clamp(20px, 2vw, 24px); }
}

/* Phone: one column, the rail runs vertically down the left and each node
   sits beside its card. The progress line grows downward. */
@media (max-width: 639px) {
  .stages { padding-top: 0; padding-left: 28px; gap: 18px; }
  .stages__rail, .stages__progress {
    top: 8px;
    bottom: 8px;
    left: 5px;
    right: auto;
    width: 1px;
    height: auto;
  }
  .stages__progress { transform: scaleY(var(--progress)); transform-origin: top center; }
  .stage__node {
    top: 28px;
    left: -28px;
    margin: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .stage__card, .stage__node, .stage__glyph, .stage__num, .stages__progress { transition: none; }
  .stage__card { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   8f. Section 05 -- Credibility
   The training photograph as evidence. Duotone is applied in CSS rather than
   baked into the file, so the original stays reusable and the treatment
   stays tunable.
   -------------------------------------------------------------------------- */


/* A real duotone, not a colour filter: the photograph keeps its own
   luminance while an overlay in `color` blend mode supplies hue and
   saturation. Blending the image itself in `luminosity` over navy pushed
   everything to a single saturated blue, which read as a filter. */


/* Muted on arrival, then the treatment settles and detail emerges. */


/* --------------------------------------------------------------------------
   8g. Section 06 -- Founder
   The human counterweight after the systems-heavy methodology, so the
   typography holds still here. Only the portrait carries any depth.
   -------------------------------------------------------------------------- */

.founder__inner { display: grid; gap: clamp(36px, 4.5vw, 72px); align-items: center; }

.founder__figure { overflow: hidden; border-radius: var(--r-xl); }

.founder__figure img {
  width: 100%;
  /* Capped and anchored high: a founder portrait should lead with the face,
     not run to the shoes. */
  max-height: clamp(420px, 64vh, 620px);
  object-fit: cover;
  object-position: 50% 10%;
  transition: transform 1.6s var(--ease-out-expo);
}

.is-revealed .founder__figure img { transform: scale(1.02); }

.founder__name { margin-bottom: 8px; }

.founder__role {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--gold-ink);
  margin-bottom: clamp(24px, 2.8vw, 34px);
}

.founder__text p + p { margin-top: 20px; }
/* The conviction the whole page argues from. Set apart from the two
   paragraphs above it the same way About sets it apart -- serif, larger, on a
   gold hairline -- because it is the sentence, not a third paragraph. */
.founder__conviction {
  margin-top: clamp(20px, 2.4vw, 28px);
  padding-left: clamp(16px, 1.8vw, 22px);
  border-left: 1px solid var(--hairline);
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.34;
  color: var(--heading);
  max-width: 42ch;
}

.founder__cta { margin-top: clamp(28px, 3vw, 40px); max-width: none; }

@media (min-width: 900px) {
  .founder__inner { grid-template-columns: minmax(0, 0.78fr) minmax(0, 1fr); gap: clamp(48px, 6vw, 88px); }
}

/* --------------------------------------------------------------------------
   8h. Section 07 -- Final CTA
   Two gold hairlines draw outward from the centre to frame the composition.
   The button is paper on navy. Gold stays a line.
   -------------------------------------------------------------------------- */

.closing__inner {
  display: grid;
  justify-items: center;
  gap: clamp(40px, 5vw, 64px);
}

.closing__frame {
  width: min(100%, 760px);
  transform-origin: center;
}

.rule-gold--center { transform-origin: center; }

.closing__body { text-align: center; max-width: 720px; }

/* Everything in here centres itself.

   `text-align: center` centres the *lines inside* a box; it does nothing about
   where the box sits. Every paragraph here is narrower than the block -- the
   global `p { max-width: var(--measure) }` sees to that -- so without auto
   margins a centred paragraph sits hard against the left edge with its text
   neatly centred inside its own narrower column. It looks almost right, which
   is why it survived.

   h2 and .closing__lead each got `margin-inline: auto` of their own, added
   separately as somebody noticed them. .closing__micro and .closing__alt never
   did, and the micro line -- "30 minutes / Online / No obligation" -- sat 80px
   left of centre on all twelve pages carrying this block. The client spotted it
   before the stylesheet did.

   One rule on the parent instead of four on the children: the next element
   added here is centred by default rather than by somebody remembering. */
.closing__body > * { margin-inline: auto; }

.closing__body h2 { max-width: 18ch; margin-bottom: clamp(20px, 2.4vw, 28px); }

.closing__lead {
  margin-bottom: clamp(32px, 4vw, 44px);
  max-width: 52ch;
}

/* The footer is navy and so is this section. Without a hairline the two
   read as one undifferentiated block. */
.site-footer { border-top: 1px solid rgba(215, 193, 133, 0.16); }

/* --------------------------------------------------------------------------
   8j. Interior pages -- page hero, steps, prose, contact
   No new visual language: the same eyebrow, hairline and serif system, at
   interior scale.
   -------------------------------------------------------------------------- */

.page-hero {
  padding-block: clamp(148px, 18vh, 208px) clamp(36px, 4.5vw, 56px);
}

.page-hero__title { max-width: 20ch; }
.page-hero__standfirst { margin-top: clamp(20px, 2.4vw, 28px); }

/* Prose: a narrow measure for reading pages. The element is also the
   .container, so constraining IT would override the container width and let
   margin-inline:auto centre the whole column away from the page's left edge.
   Constrain the text instead; the column stays aligned with every section. */
.prose > p,
.prose > h2 { max-width: 68ch; }
.prose h2 { margin-bottom: clamp(18px, 2vw, 26px); }
.prose p + p { margin-top: 20px; }

/* A section heading needs air above it, and rather more than below: the space
   belongs to the section it opens, not to the paragraph it leaves behind.
   Nothing needed this until the launch articles arrived with eight to eleven
   headings each -- the drafts they replaced had three, far enough apart that a
   heading landing flush against the paragraph above never showed. */
.prose p + h2,
.prose .prose__chain + h2,
.prose .prose__lift + h2 { margin-top: clamp(44px, 5vw, 72px); }
.prose__rule { width: 64px; margin-block: clamp(38px, 4.5vw, 60px); }

/* Company identification: the ledger treatment again -- hairline-separated
   rows, letterspaced labels. Same grammar as the problem section. */
.legal-id {
  display: grid;
  margin-block: clamp(24px, 3vw, 34px) clamp(34px, 4vw, 48px);
  max-width: 68ch;
}

.legal-id dt {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--gold-ink);
  padding-top: 15px;
  border-top: 1px solid var(--line);
}

.legal-id dd {
  margin: 5px 0 16px;
  color: var(--text);
  /* `anywhere`, not `break-word`: this is a grid item, and only `anywhere`
     reduces min-content width so the track can shrink. `break-word` would
     still size the column to the full email address. */
  overflow-wrap: anywhere;
}

/* Deliberately conspicuous: these must be filled before launch, and
   tools/check.py fails the build while any remain. */
.legal-todo {
  color: var(--taupe-ink);
  font-style: italic;
  border-bottom: 1px dotted var(--taupe);
  padding-bottom: 1px;
}

@media (min-width: 640px) {
  .legal-id { grid-template-columns: minmax(0, 0.46fr) minmax(0, 1fr); column-gap: clamp(20px, 3vw, 40px); }
  .legal-id dd { padding-top: 15px; border-top: 1px solid var(--line); margin: 0 0 16px; }
}
/* An arrow chain. The author writes these in capitals as a sequence, so they
   are set as one: letterspaced, hairline-ruled top and bottom, and allowed to
   wrap on narrow screens rather than scrolling sideways. h2 is wrong for them
   -- they are not sections, and a screen reader walking the heading outline
   should not meet "UNDERSTANDING PRACTICE FEEDBACK REPETITION" between two
   real headings. */
.prose__chain {
  max-width: 68ch;
  margin-block: clamp(30px, 3.4vw, 42px);
  padding-block: clamp(18px, 2vw, 24px);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  font-size: var(--step-ui);
  letter-spacing: 0.1em;
  line-height: 1.7;
  color: var(--gold-ink);
  text-transform: none;  /* the copy is already capitalised; do not re-case it */
}

/* A closing line the author lifts off the page. Serif, larger, and set on the
   gold hairline -- the same rule the sections already use, turned vertical so
   it reads as a margin note rather than a new section. */
.prose__lift {
  max-width: 46ch;
  margin-block: clamp(28px, 3.2vw, 40px);
  padding-left: clamp(18px, 2vw, 26px);
  border-left: 1px solid var(--hairline);
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.32;
  color: var(--heading, var(--text));
}

/* Nothing here zeroes a margin between a chain or a lift and the paragraph
   beside it. An earlier version did, to stop `.prose p + p`'s 20px adding to
   the block's own 40px -- but adjacent margins in normal flow collapse to the
   larger of the two, so there was never any doubling to prevent. What the rule
   did instead was hold every lift flush against the paragraph above it, which
   is exactly the paragraph it is being lifted away from.

   These two selectors carry two classes so they outrank `.prose p + p`, which
   is one class and two elements and therefore more specific than `.prose__lift`
   on its own. Without them the block's own margin-top loses to the generic
   paragraph rhythm and the gap collapses to 20px again. */
.prose .prose__chain { margin-block: clamp(30px, 3.4vw, 42px); }
.prose .prose__lift { margin-block: clamp(28px, 3.2vw, 40px); }

/* Where to go next. Two links, no button, nothing asked for: the masters allow
   a discreet link to the next article or to the diagnostic and nothing more. */
.prose__onward { max-width: 68ch; }
.prose__onward-label { margin-bottom: clamp(14px, 1.6vw, 18px); }
.prose__onward-item + .prose__onward-item { margin-top: 14px; }

.prose__note { color: var(--taupe-ink); }

/* Contact */
.contact__inner { display: grid; gap: clamp(44px, 5vw, 80px); align-items: start; }

.contact__details {
  display: grid;
  gap: 22px;
  margin-top: clamp(32px, 4vw, 48px);
}

.contact__details li {
  display: grid;
  gap: 6px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.contact__label { color: var(--gold-ink); }

.contact__details a { color: var(--heading); overflow-wrap: anywhere; }
@media (hover: hover) { .contact__details a:hover { color: var(--gold-ink); } }

.contact__form { display: grid; gap: 20px; }

/* An explanatory sentence, not a label. It was set in the letterspaced
   uppercase meta style, which made a full sentence shout above the fields it
   was explaining and outrank every label on the form. */
.contact__demo-note {
  font-size: var(--step-ui);
  color: var(--taupe-ink);
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline);
  max-width: 46ch;
}

.field { display: grid; gap: 8px; }

/* The honeypot. Not display:none -- some bots skip hidden inputs, and a few
   password managers refuse to fill a form that contains one. Off-screen,
   one pixel, out of the tab order and aria-hidden in the markup. */
.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field label {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--heading);
}

.field input,
.field textarea {
  width: 100%;
  padding: 13px 14px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  font-size: var(--step-body);
  transition: border-color var(--dur-fast) var(--ease-out-quart);
}

.field input:focus,
.field textarea:focus { border-color: var(--gold); outline-offset: 1px; }

.contact__form .btn[disabled] { opacity: 0.45; cursor: not-allowed; /* dim-exempt: WCAG 1.4.3 exempts disabled controls */ }

@media (min-width: 900px) {
  .contact__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   8k. Conversion architecture
   Routes, programmes, booking, FAQ, lead capture, sticky action bar.
   Every surface here is rounded paper with a 1px hairline. No shadows.
   -------------------------------------------------------------------------- */

/* Routes ------------------------------------------------------------------ */


.route {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(26px, 3vw, 36px);
  display: flex;
  flex-direction: column;
  /* Longhands, not the shorthand. The shorthand resets transition-delay to
     0 and replaces the opacity/transform entries .stagger sets, so this one
     card class silently opted itself out of the stagger it sits inside.
     border-color keeps a zero delay of its own: a hover response must not
     wait 180ms because the card happens to be third in the row. */
  transition-property: opacity, transform, border-color;
  transition-duration: var(--dur-slow), var(--dur-slow), var(--dur-base);
  transition-timing-function:
    var(--ease-out-expo), var(--ease-out-expo), var(--ease-out-quart);
  transition-delay: calc(var(--i, 0) * 90ms), calc(var(--i, 0) * 90ms), 0s;
}


@media (hover: hover) {
  .route:hover { border-color: var(--gold); }
}

/* Three equal thirds said the three routes matter equally. They do not: every
   primary button on the site, the sticky bar and the closing section all point
   at booking a consultation. The layout now says the same thing the rest of
   the site already says.

   This is the only group on the site that gets this treatment. The three
   programmes, the four principles, the three practice statements and the
   services 2x2 are all deliberately equal, and sizing one of them larger would
   assert an importance that is not there. */
@media (min-width: 760px) {
  
  .route:first-child {
    grid-row: 1 / span 2;
    padding: clamp(32px, 3.6vw, 46px);
  }

    
  /* The tall card is sized by the two stacked beside it, not by its own copy,
     so margin-top:auto flung its link to the floor and left 300px of gap in
     the middle -- which reads as a bug, not as restraint. Centring the block
     puts that space above and below it instead, where it reads as room. */
  .route:first-child { justify-content: center; }
  }

/* Programmes -------------------------------------------------------------- */


.programme {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(26px, 3vw, 36px);
  display: flex;
  flex-direction: column;
}


/* Booking ----------------------------------------------------------------- */
.booking__head { margin-bottom: clamp(36px, 4.5vw, 56px); }
.booking__head h2 { max-width: 18ch; margin-bottom: clamp(18px, 2vw, 26px); }

/* One booking mechanism. The button opens the real calendar, where a slot the
   visitor takes is confirmed the instant they take it.

   There used to be a month grid and six time buttons below this, feeding a
   mailto. It could not know Bogdan's availability, so every slot it showed was
   a guess -- and the line introducing it said so: "Or send a request instead:
   pick a day below and we will confirm by email", directly under a button that
   promises the opposite. The masters asked for one mechanism; this is it. */
.booking__direct { margin: 0 0 clamp(16px, 1.8vw, 22px); }

.booking__micro { color: var(--meta); margin: 0; }

.booking__note {
  margin: 16px 0 0;
  max-width: none;
  font-size: var(--step-meta);
  color: var(--taupe-ink);
}

/* Two sections sat in the left third of a 1280px container with nothing
   beside them. Their blocks are centred; their text is not.

   Centred body copy is harder to read -- every line starts at a different
   place, so the eye has to hunt for the next one. What was wrong here was the
   position of the column, not the alignment of the words in it. */
/* The AI/human section is two columns: the team clip on the left, the
   argument on the right. The left column held the rain choice until
   2026-09-16 -- see tools/team_video.py. */
.human__inner { display: grid; gap: clamp(36px, 4.5vw, 72px); align-items: start; }
.human__text { max-width: 62ch; }

@media (min-width: 1000px) {
  /* The photograph takes the argument's height (see .human__figure), so
     the two columns end together. */
  .human__inner { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr); align-items: stretch; }
}

/* The photograph beside the argument. Until 2026-09-18 this was the team
   clip (a phone video in a 9:16 frame); the client asked for a picture. The
   frame is the photograph's own proportions, rounded like every other frame
   on the page; on desktop it fills its column and matches the text's height
   the way the mindset photograph does -- out of flow, so the row is sized by
   the words and the picture fills whatever that comes to. */
.human__figure {
  margin: 0;
  border-radius: var(--r-xl);
  overflow: hidden;
}
.human__figure img { width: 100%; height: auto; display: block; }

@media (min-width: 1000px) {
  .human__figure { position: relative; align-self: stretch; min-height: 420px; }
  .human__figure picture { position: absolute; top: 0; right: 0; bottom: 0; left: 0; display: block; }
  .human__figure img { height: 100%; object-fit: cover; object-position: 50% 30%; }
}

@media print {
  .human__figure { break-inside: avoid; }
}


/* FAQ --------------------------------------------------------------------- */
/* Centred, head included. The accordion below is a symmetrical stack of
   full-width cards, so a left-aligned heading over it reads as misplaced
   rather than as a grid decision -- and the list itself was leaving 420px
   empty on the right. */
.faq__head {
  margin-bottom: clamp(32px, 4vw, 48px);
  text-align: center;
}

/* The eyebrow draws its rule with ::before, which a centred line would leave
   hanging off the left of the text. Flex re-centres the pair together. */
.faq__head .eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  /* The eyebrow carries its own max-width elsewhere, so justify-content was
     centring the rule and the words inside a 527px box that still started at
     the container's left edge. Releasing the width is what actually centres
     it in the section. */
  max-width: none;
}

/* Under the questions: whoever still has one gets the two fastest routes.
   Centred, like the head above the list. */
.faq__ask {
  margin-top: clamp(36px, 4.5vw, 56px);
  text-align: center;
}
.faq__ask-label { max-width: none; }
.faq__actions { margin-top: 18px; }

.faq__list {
  display: grid;
  gap: 12px;
  max-width: 860px;
  margin-inline: auto;
}

.qa {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 2px clamp(20px, 2.4vw, 30px);
  transition: border-color var(--dur-base) var(--ease-out-quart);
}

.qa[open] { border-color: var(--gold); }

.qa__q {
  list-style: none;
  cursor: pointer;
  padding-block: 22px;
  font-family: var(--font-display);
  font-size: var(--step-h3);
  line-height: 1.25;
  letter-spacing: var(--track-display);
  color: var(--heading);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
}

.qa__q::-webkit-details-marker { display: none; }

.qa__q::after {
  content: "+";
  flex: none;
  font-family: var(--font-ui);
  font-size: 1.5rem;
  font-weight: 300;
  color: var(--gold-ink);
}

.qa[open] .qa__q::after { content: "\2013"; }

.qa__a { padding-bottom: 24px; }
.qa__a p { color: var(--text); max-width: 62ch; }

/* Lead capture ------------------------------------------------------------ */


.enquiry { display: grid; gap: 16px; }

/* A sentence, not a label -- so it is set as one. In letterspaced uppercase it
   outshouted every field label beneath it, which is the same fault the contact
   form's note had. */

.enquiry .field label { color: var(--heading); }

.enquiry input,
.enquiry select,
.enquiry textarea {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.26);
  color: var(--paper);
}

.enquiry input:focus,
.enquiry select:focus,
.enquiry textarea:focus { border-color: var(--gold-light); }

.enquiry select { appearance: none; padding-right: 40px; }


/* --------------------------------------------------------------------------
   8s. The contact dial
   One floating control, on every page but Contact, since 2026-09-18. It
   replaced two: the sticky "Book a conversation" bar and the floating
   WhatsApp button, which the client circled on a phone and asked to be one
   button that opens a small menu -- phone, WhatsApp, email, and whatever
   else fits. Markup in tools/dial.py; the routes it lists are the ones the
   phone menu's contact section lists, from the same list.

   Navy pill, paper sheet. The booking row is the sheet's one navy fill,
   because booking is the one route the brief calls the mechanism. Gold is
   the hover border, as everywhere; never a fill.

   Shown between the hero and the closing (initDial, IntersectionObserver),
   so it never covers the hero's own buttons or the closing's.
   -------------------------------------------------------------------------- */
.dial {
  position: fixed;
  z-index: 75;   /* under the header at 80, over content */
  right: var(--gutter);
  bottom: max(14px, env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  /* Off until initDial says otherwise; slides in from below like the bar
     used to. */
  transform: translateY(150%);
  transition: transform var(--dur-base) var(--ease-out-expo);
}
.dial.is-visible { transform: none; }
/* Not over the open phone menu, which is fixed beneath it. */
body.nav-open .dial { visibility: hidden; }

.dial__toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 22px 0 18px;
  border: 1px solid var(--navy);
  border-radius: var(--r-pill);
  background: var(--navy);
  color: var(--paper);
  font-family: var(--font-ui);
  font-size: var(--step-meta);
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(11, 27, 58, 0.22);
  transition: border-color var(--dur-fast) var(--ease-out-quart),
              transform var(--dur-base) var(--ease-out-expo);
}
.dial__toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
@media (hover: hover) {
  .dial__toggle:hover { border-color: var(--gold); transform: translateY(-2px); }
}

/* Two icons, one shown. The pill says "Contact" closed and shows a cross
   open; the word stays so the control keeps its name. */
.dial__icon { display: none; line-height: 0; }
.dial__icon svg { display: block; }
.dial__toggle[aria-expanded="false"] .dial__icon--open,
.dial__toggle[aria-expanded="true"] .dial__icon--close { display: block; }

/* The sheet. Anchored above the pill, right-aligned, as wide as the routes
   need on desktop and as wide as the screen allows on a phone. */
.dial__sheet {
  width: min(360px, calc(100vw - 2 * var(--gutter)));
  padding: 18px 18px 16px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px rgba(11, 27, 58, 0.18);
  transform-origin: bottom right;
  animation: dial-in var(--dur-base) var(--ease-out-expo);
}
@keyframes dial-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

.dial__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.dial__title { margin: 0; max-width: none; color: var(--heading); }

.dial__close {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin: -8px -8px -8px 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--meta);
  cursor: pointer;
}
.dial__close:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: 2px; }
@media (hover: hover) {
  .dial__close:hover { background: var(--paper-warm); color: var(--heading); }
}

.dial__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

.dial__route {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 56px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 14px;
  color: var(--heading);
  transition: border-color var(--dur-fast) var(--ease-out-quart),
              background-color var(--dur-fast) var(--ease-out-quart);
}
.dial__route:focus-visible { outline: 2px solid var(--gold-ink); outline-offset: 2px; }
@media (hover: hover) {
  .dial__route:hover { border-color: var(--gold); background: var(--paper-warm); }
}

.dial__glyph {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--gold-ink);
}
.dial__glyph svg { display: block; }

.dial__text { display: grid; gap: 2px; min-width: 0; }
.dial__label { font-family: var(--font-ui); font-size: var(--step-ui); font-weight: 600; line-height: 1.2; }
.dial__note {
  font-family: var(--font-ui);
  font-size: 0.8125rem;
  color: var(--meta);
  line-height: 1.3;
  overflow-wrap: anywhere;
}

/* The booking route: the one navy fill in the sheet. */
.dial__route--book {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--paper);
}
.dial__route--book .dial__glyph { border-color: rgba(255, 255, 255, 0.28); color: var(--gold-light); }
.dial__route--book .dial__note { color: rgba(255, 255, 255, 0.72); }
@media (hover: hover) {
  .dial__route--book:hover { background: var(--navy-lift); border-color: var(--gold); }
}

.dial__start {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  max-width: none;
  font-size: 0.875rem;
  color: var(--meta);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  align-items: baseline;
}

/* Phone: the sheet pins to both gutters and the pill drops its word so it
   stays a thumb-sized circle in the corner. */
@media (max-width: 479px) {
  .dial { right: var(--gutter); left: var(--gutter); align-items: flex-end; }
  .dial__sheet { width: 100%; }
  .dial__toggle { width: 56px; height: 56px; padding: 0; justify-content: center; }
  .dial__toggle .dial__word {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* --------------------------------------------------------------------------
   8s2. The phone menu, organised
   Eyebrows above the pages and the contact rows, and the contact rows
   themselves -- phone, WhatsApp, email with the number and the address in
   full -- between the pages and the language switch. Desktop hides all of
   it: the bar has no room and the footer already carries the routes.
   -------------------------------------------------------------------------- */
.site-nav__eyebrow,
.site-nav__contact { display: none; }

@media (max-width: 1239px) {
  .site-nav__eyebrow { display: block; margin: 0 0 6px; max-width: none; color: var(--gold-ink); }
  .site-nav__eyebrow--pages { margin-bottom: 2px; }

  .site-nav__contact { display: block; margin-top: 36px; }
  .site-nav__routes { list-style: none; margin: 0; padding: 0; display: grid; }
  .site-nav__routes li + li { border-top: 1px solid var(--line); }

  .site-nav__route {
    display: grid;
    grid-template-columns: 36px minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: 14px;
    align-items: center;
    padding: 14px 0;
    color: var(--heading);
  }
  .site-nav__route-glyph {
    grid-row: 1 / span 2;
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 1px solid var(--line);
    border-radius: 50%;
    color: var(--gold-ink);
  }
  .site-nav__route-glyph svg { display: block; }
  .site-nav__route-label { font-family: var(--font-ui); font-size: var(--step-ui); font-weight: 600; line-height: 1.2; }
  .site-nav__route-note { font-family: var(--font-ui); font-size: 0.8125rem; color: var(--meta); overflow-wrap: anywhere; }
}

/* --------------------------------------------------------------------------
   8t. Writing
   An index of articles, and the hero an article page uses.

   Set as a list, not a grid of tiles. These are things to read, and a tile
   grid is the visual grammar of a shop -- it invites scanning for the cheapest
   one. A list invites reading the first line of each, which is what a
   standfirst is for.

   The whole card is one link. A title-only link makes a 40px target out of
   something occupying 200px of screen, and leaves the standfirst as dead
   decoration that a keyboard user tabs past twice.
   -------------------------------------------------------------------------- */

.writing__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 74ch;
}

.writing__item + .writing__item {
  margin-top: clamp(28px, 3.4vw, 44px);
  padding-top: clamp(28px, 3.4vw, 44px);
  border-top: 1px solid var(--hairline);
}

.writing__link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.writing__meta { display: block; margin-bottom: 14px; }

/* The eyebrow as a chip: the same hairline pill the footer's service tags
   use, with a clock for the reading time. Ink and stroke only. */
.writing__chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  color: var(--meta);
  transition: border-color var(--dur-fast) var(--ease-out-quart), background-color var(--dur-fast) var(--ease-out-quart);
}
.writing__chip .ico { color: var(--gold); }

/* The whole item lifts a touch on hover; the title takes the gold ink. */
.writing__link { transition: transform var(--dur-base) var(--ease-out-expo); }
@media (hover: hover) {
  .writing__link:hover { transform: translateX(6px); }
  .writing__link:hover .writing__chip { border-color: var(--gold); background: var(--paper-warm); }
}

.writing__title {
  margin: 0 0 14px;
  max-width: 24ch;
  font-size: var(--step-h3);
  line-height: 1.15;
  color: var(--heading);
  transition: color var(--dur-base) var(--ease-out-expo);
}

.writing__standfirst {
  margin: 0 0 18px;
  max-width: 58ch;
}

/* The same small-caps call the rest of the site uses, with the rule drawn on
   hover rather than sitting there -- a list of six permanent underlines reads
   as noise. */
.writing__more {
  display: inline-block;
  position: relative;
  font-size: var(--step-meta);
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
}

.writing__more::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 1px;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-base) var(--ease-out-expo);
}

.writing__more .arrow {
  display: inline-block;
  margin-left: 6px;
  transition: transform var(--dur-base) var(--ease-out-expo);
}

@media (hover: hover) {
  .writing__link:hover .writing__more::after { transform: scaleX(1); }
  .writing__link:hover .writing__more .arrow { transform: translateX(5px); }
}

.writing__link:focus-visible .writing__more::after { transform: scaleX(1); }

/* An article hero carries a headline and a standfirst and nothing else, so it
   holds a narrower measure than a section hero that has an aside beside it. */
.page-hero--narrow .page-hero__title { max-width: 20ch; }
.page-hero--narrow .page-hero__standfirst { max-width: 54ch; }

/* --------------------------------------------------------------------------
   8u. Home: the human argument, the mindset ledger, the bridge
   Three sections that replaced the old problem ledger when the positioning
   moved from "people, processes and systems" to the people who sell and the
   leaders above them.
   -------------------------------------------------------------------------- */

.human__title { max-width: 20ch; margin-bottom: clamp(28px, 3.4vw, 44px); }

/* Set at reading width and left-aligned rather than centred: this is an
   argument, and an argument reads as a column, not as a poster. */
.human__body { max-width: 62ch; }
.human__body p + p { margin-top: clamp(18px, 2vw, 26px); }

.mindset__title { max-width: 18ch; margin-bottom: clamp(20px, 2.4vw, 30px); }
.mindset__lead { max-width: 64ch; margin-bottom: clamp(34px, 4vw, 52px); }

/* The heading and the lead stay full width; the four factors and the
   photograph share the space under them. Splitting from the top instead would
   have squeezed a 64ch lead into half a container, and the section directly
   above this one is already a two-column layout -- running the same shape
   twice reads as a template rather than as a decision. */
.mindset__split { display: grid; gap: clamp(32px, 4vw, 56px); align-items: start; }

.mindset__figure { margin: 0; }

.mindset__figure img {
  width: 100%;
  height: auto;
  border-radius: var(--r-lg);
}

@media (min-width: 1000px) {
  .mindset__split { grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr); }
  /* The factors list carries its own bottom margin for the stacked case,
     where the photograph follows it. Side by side there is nothing under it
     to be pushed away from. */
  .mindset__split .factors { margin-bottom: 0; }

  /* .factor__label is flex:none so the gold leader rule can take whatever is
     left of the row -- and the comment above that rule says, in as many words,
     that it works only while the phrase fits. In half a container it does not:
     the Romanian phrases run straight out of the column and under the
     photograph.

     The arrangement below 640px already solves this -- phrase, then rule
     beneath it -- so the narrow column borrows it rather than inventing a
     third behaviour for the same problem. */
  .mindset__split .factor { flex-wrap: wrap; gap: 10px; }
  .mindset__split .factor__label { flex: 1 1 100%; }
  .mindset__split .factor__rule { flex: 1 1 100%; }

  /* The photograph takes the height of the list beside it rather than its own.
     Left at its natural height it ran 230px past the last factor, and a column
     that stops while the one next to it keeps going reads as an unfinished
     layout rather than as a deliberate one. The crop holds the subject because
     object-position puts him above centre, which is where he is. */
  /* The decision row is the split's third child and lands in a second row
     of the first column; the photograph spans both rows, so it grows by the
     row's height rather than stopping above it. */
  .mindset__figure { align-self: stretch; position: relative; grid-row: 1 / span 2; }
  /* The grid gap already separates the row from the last factor. */
  .mindset__actions { margin-top: 0; }
  /* Out of flow, for the same reason as the ticket stub: a grid row is as tall
     as its tallest item, so while the photograph is in flow it *is* the tallest
     item and `align-self: stretch` stretches it to its own height -- which
     changes nothing. Taken out, the row is sized by the four factors and the
     photograph fills whatever that comes to. */
  .mindset__figure picture { position: absolute; inset: 0; display: block; }
  .mindset__figure img { height: 100%; object-fit: cover; object-position: 50% 26%; }
}

/* -- The workshop band, above the service cards ---------------------------
   The one frame on the home page where the work itself is visible: the room,
   the group, the whiteboard. It is wide and short on purpose -- a tall image
   here would put the cards below the fold on a laptop, and the cards are what
   the section is for. */
.svc-figure {
  margin: 0 0 clamp(40px, 5vw, 64px);
  border-radius: var(--r-xl);
  overflow: hidden;
}

.svc-figure img {
  width: 100%;
  height: auto;
  display: block;
  /* 16:10, which is the crop's own ratio (1.58:1), so the band shows the
     whole frame at every width from 640px up. The previous band cut a
     portrait to 2:1 and the client saw half a person on a phone; below
     640px the photograph now shows at its own proportions, uncropped. */
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: 50% 50%;
}

@media (max-width: 639px) {
  .svc-figure img { aspect-ratio: var(--ratio, 16 / 10); }
}

/* The band under an interior page's hero (Services, How We Work, About).
   Same rule as the services band: 16:10 from 640px, whole below. --focus is
   set per photograph in tools/photos.py, because where the head is differs
   from frame to frame. */
.band-figure {
  margin: clamp(32px, 4vw, 48px) 0 0;
  border-radius: var(--r-xl);
  overflow: hidden;
}
.band-figure img {
  width: 100%;
  height: auto;
  display: block;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: var(--focus, 50% 50%);
}
@media (max-width: 639px) {
  .band-figure img { aspect-ratio: var(--ratio, 16 / 10); }
}

/* The bridge is one sentence on navy: the page has made its case and now says
   who does something about it. Large enough to read as a statement, short
   enough that it does not become a paragraph. */
.bridge__text {
  max-width: 34ch;
  margin: 0 auto clamp(40px, 5vw, 64px);
  font-family: var(--font-display);
  font-size: var(--step-h3);
  line-height: 1.35;
  color: var(--heading);
  text-align: center;
  text-wrap: balance;
}

@media (max-width: 767px) {
  .bridge__text { max-width: none; text-align: left; }
}

/* --------------------------------------------------------------------------
   8v. Services: where performance breaks down
   Seven situations, each with what sits underneath it. Replaced a coverage
   matrix -- a grid of marks asks the reader to decode a mapping, a list of
   situations asks whether they recognise their own team, and the second is
   the question the page exists to ask.
   -------------------------------------------------------------------------- */

.breakdown__head { max-width: 62ch; margin-bottom: clamp(36px, 4.5vw, 56px); }
.breakdown__head h2 { max-width: 20ch; margin-bottom: clamp(18px, 2vw, 26px); }

.breakdown__prompt {
  margin-top: clamp(20px, 2.4vw, 28px);
  font-size: var(--step-ui);
  color: var(--meta);
}

.breakdown__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(22px, 2.6vw, 32px);
}

@media (min-width: 900px) {
  .breakdown__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.breakdown__item {
  padding-top: clamp(18px, 2vw, 24px);
  border-top: 1px solid var(--hairline);
}

/* The situation in the heading voice, the cause underneath in body voice. The
   reader is meant to scan the first lines and stop at the one that is theirs. */
.breakdown__symptom {
  margin: 0 0 10px;
  max-width: none;
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.3;
  color: var(--heading);
}

.breakdown__cause {
  margin: 0;
  max-width: 46ch;
  font-size: var(--step-ui);
}

.breakdown__close {
  margin-top: clamp(36px, 4.5vw, 56px);
  max-width: 54ch;
  font-size: var(--step-lead);
  color: var(--heading);
}

/* A service chapter now carries a hook line and several paragraphs rather than
   one block of body copy. */
.chapter__hook {
  max-width: 30ch;
  margin-bottom: clamp(18px, 2vw, 26px);
  font-family: var(--font-display);
  color: var(--heading);
}

.chapter__body { max-width: 58ch; }
.chapter__body + .chapter__body { margin-top: 16px; }

/* The closing call to action gained microcopy and a quiet alternative. */
.closing__actions { margin-top: 0; }
.closing__micro { margin-top: 22px; }
.closing__alt { margin-top: 10px; }

/* --------------------------------------------------------------------------
   8w. Results: experience in numbers, the two stories, what we track
   The figures on this page are the founder's own, from before the company
   existed. That is why the attribution is a heading and a paragraph rather
   than a footnote -- and why the figures are set as a plain list rather than
   as the glowing stat cards every consultancy site uses. Restraint is the
   argument here.
   -------------------------------------------------------------------------- */

.figures__head { max-width: 60ch; margin-bottom: clamp(40px, 5vw, 64px); }
.figures__head h2 { max-width: 22ch; margin-bottom: clamp(18px, 2vw, 26px); }

.figures__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(26px, 3vw, 38px);
}

@media (min-width: 640px) { .figures__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .figures__list { grid-template-columns: repeat(5, 1fr); } }

.figure {
  padding-top: clamp(16px, 1.8vw, 22px);
  border-top: 1px solid var(--hairline);
}

/* Serif, because a number is the loudest thing on the page and the serif is
   the voice the site uses when it means something. Gold is allowed here: the
   brand rule permits numerals at 24px and above. */
.figure__value {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
  line-height: 1.05;
  color: var(--meta);
}

.figure__unit {
  display: block;
  margin-top: 4px;
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
}

.figure__label {
  display: block;
  margin-top: 10px;
  font-size: var(--step-ui);
  max-width: 22ch;
}

.figures__note {
  margin-top: clamp(34px, 4vw, 50px);
  max-width: 62ch;
  font-size: var(--step-ui);
}

/* The two stories -------------------------------------------------------- */

.story__inner { display: grid; gap: clamp(30px, 4vw, 56px); }

@media (min-width: 1040px) {
  .story__inner { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); }
  .story__head { position: sticky; top: calc(var(--header-h, 80px) + 32px); align-self: start; }
}

.story__head h2 { max-width: 16ch; margin-bottom: clamp(16px, 2vw, 24px); }

/* The headline figure, set once and large. Repeating it inside the prose
   would make it an advertisement; stating it once and then explaining it is
   what makes it evidence. */
.story__figure {
  margin: clamp(18px, 2vw, 26px) 0 6px;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 1.1rem + 2vw, 2.25rem);
  line-height: 1.1;
  color: var(--meta);
}

.story__span { display: block; }

.story__body { max-width: 64ch; }
.story__body p + p { margin-top: clamp(16px, 1.8vw, 22px); }

.story__rule { display: block; width: 64px; margin-block: clamp(30px, 3.4vw, 44px); }

.story__subtitle {
  font-size: var(--step-h3);
  margin-bottom: clamp(16px, 1.8vw, 22px);
}

/* A sentence the page is willing to be quoted on. */
.story__claim {
  margin-top: clamp(20px, 2.4vw, 28px);
  padding-left: clamp(16px, 2vw, 22px);
  border-left: 1px solid var(--rule);
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.35;
  color: var(--heading);
}

/* What we track ---------------------------------------------------------- */

.tracked__head { max-width: 58ch; margin-bottom: clamp(36px, 4.5vw, 56px); }
.tracked__head h2 { max-width: 18ch; margin-bottom: clamp(18px, 2vw, 26px); }

.tracked__list { list-style: none; margin: 0; padding: 0; max-width: 74ch; }

.tracked__item {
  display: grid;
  gap: 6px 18px;
  padding-block: clamp(16px, 1.8vw, 22px);
  border-top: 1px solid var(--hairline);
  align-items: baseline;
}

@media (min-width: 760px) {
  .tracked__item { grid-template-columns: 11rem 64px minmax(0, 1fr); }
}

.tracked__name {
  font-family: var(--font-display);
  font-size: var(--step-lead);
  color: var(--heading);
}

.tracked__rule { align-self: center; }

@media (max-width: 759px) { .tracked__rule { display: none; } }

.tracked__body { font-size: var(--step-ui); }

.tracked__close {
  margin-top: clamp(34px, 4vw, 50px);
  max-width: 54ch;
  font-size: var(--step-lead);
  color: var(--heading);
}

.prep__claim {
  margin-top: clamp(22px, 2.6vw, 30px);
  font-family: var(--font-display);
  font-size: var(--step-lead);
  color: var(--heading);
}

/* The body paragraphs here had no spacing at all -- they ran together as one
   block of text. Every other reading section on the site has its own
   `X p + p` rule (.act__body, .about-intro__text, .adapted__body, .story__body
   and four more); this one's lived on `.prep__body`, a class the markup stopped
   carrying. The rule kept existing and stopped applying, and nothing said so.

   Found while pruning dead CSS, not by looking for it. It had been shipping
   that way. */
.prep__inner p + p { margin-top: clamp(16px, 1.8vw, 22px); }

/* Two classes, so this outranks the rule above and keeps the claim's wider
   gap. Same arrangement, and same reason, as the `.prose` lift selectors. */
.prep__inner .prep__claim { margin-top: clamp(22px, 2.6vw, 30px); }

/* The methodology spine gained a stage name above its hook. */
.spine__name { display: block; margin-bottom: 10px; }
.spine__body + .spine__body { margin-top: 14px; }

/* --------------------------------------------------------------------------
   8x. About: five acts and five principles
   The longest reading on the site by a wide margin -- around 130 paragraphs.
   Everything here is in service of one thing: that somebody can actually read
   it. A measure near 66 characters, a sticky act label so the reader always
   knows which chapter they are in, and no decoration competing with the text.
   -------------------------------------------------------------------------- */

.about-intro__inner { display: grid; }
.about-intro__text { max-width: 62ch; }

.about-intro__name {
  font-size: var(--step-h2);
  margin-bottom: 6px;
}

.about-intro__role { display: block; margin-bottom: clamp(24px, 3vw, 34px); }
.about-intro__text p + p { margin-top: clamp(16px, 1.8vw, 22px); }

.act__inner { display: grid; gap: clamp(24px, 3vw, 44px); }

@media (min-width: 1040px) {
  .act__inner { grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); }

  /* The act label follows the reader down its own chapter. In a page this
     long, losing your place is the failure mode -- not boredom. */
  .act__mark {
    position: sticky;
    top: calc(var(--header-h, 80px) + 28px);
    align-self: start;
    display: grid;
    grid-template-columns: auto 1px;
    gap: 14px;
    align-items: start;
  }
  .act__seam { height: 4rem; }
}

@media (max-width: 1039px) {
  .act__seam { display: none; }
}

.act__label { display: block; line-height: 1.4; max-width: 16ch; }

.act__body { max-width: 66ch; }

.act__title {
  font-size: var(--step-h2);
  max-width: 18ch;
  margin-bottom: clamp(20px, 2.4vw, 30px);
}

.act__body p + p { margin-top: clamp(16px, 1.8vw, 22px); }

/* The five principles close the page. Numbered, ruled, and set slightly
   tighter than the acts so the ending reads as a summary rather than as more
   of the same. */
.principles {
  list-style: none;
  margin: clamp(30px, 3.4vw, 44px) 0 0;
  padding: 0;
  counter-reset: principle;
}

.principle {
  padding-top: clamp(22px, 2.6vw, 30px);
  margin-top: clamp(22px, 2.6vw, 30px);
  border-top: 1px solid var(--hairline);
}

.principle:first-child { margin-top: 0; }

.principle__title {
  font-size: var(--step-h3);
  margin-bottom: clamp(14px, 1.6vw, 20px);
  max-width: 26ch;
}

.principle p + p { margin-top: 14px; }

/* On paper the whole story is the point, so nothing here collapses -- but an
   act must not be split immediately after its own heading. */
@media print {
  .act__title, .principle__title { break-after: avoid; }
  .principle { break-inside: avoid; }
}

/* --------------------------------------------------------------------------
   8l. Form states
   No red: the palette has no chroma, and WCAG requires that an error is never
   signalled by colour alone. The message text carries the meaning; a gold rule
   and border carry the emphasis. Tokens are ground-aware so the same rules
   work on paper and on the navy closing panel.
   -------------------------------------------------------------------------- */

.form-error {
  margin: 4px 0 0;
  max-width: none;
  padding: 14px 18px;
  border-left: 2px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--paper-warm);
  color: var(--ink);
  font-size: var(--step-meta);
  letter-spacing: 0.04em;
}

.panel--navy .form-error {
  background: rgba(255, 255, 255, 0.08);
  color: var(--paper);
  border-left-color: var(--gold-light);
}

[aria-invalid="true"] {
  border-color: var(--gold) !important;
}

.panel--navy [aria-invalid="true"] {
  border-color: var(--gold-light) !important;
}

.form-success {
  margin-top: 20px;
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper-warm);
}

.panel--navy .form-success {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.24);
}

.form-success:focus-visible { outline: 2px solid var(--rule); outline-offset: 3px; }

.form-success__title {
  margin: 0 0 10px;
  max-width: none;
  font-family: var(--font-display);
  font-size: var(--step-h3);
  line-height: 1.25;
  color: var(--heading);
}

.form-success__body {
  margin: 0 0 16px;
  max-width: 56ch;
  font-size: var(--step-meta);
  line-height: 1.7;
  color: var(--text);
}

.form-success__copy {
  width: 100%;
  resize: vertical;
  font-size: 0.8125rem;
  line-height: 1.6;
}

.panel--navy .form-success__copy {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.26);
  color: var(--paper);
}

/* --------------------------------------------------------------------------
   8m. Interior pages -- per-page art direction
   Every page below owns a hero composition and one section that appears
   nowhere else. Nothing here introduces a new visual language: it is the same
   five devices from the logo -- serif, hairline, letterspaced sans, numeral,
   negative space -- arranged differently per page.

   Gold discipline in this block: every gold surface is either a 1px hairline
   or a 1px ring. The matrix marks are hairline ticks rather than dots for
   exactly that reason -- a filled gold dot would be a fill, however small.
   -------------------------------------------------------------------------- */

/* -- Shared hero scaffolding ---------------------------------------------- */

.page-hero__inner {
  display: grid;
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
}

.page-hero__text { min-width: 0; }
.page-hero__aside { min-width: 0; }

/* A hero with a companion column carries more weight than a title band, so it
   gets the room to breathe that the plain legal-page hero does not need. */
.page-hero--index,
.page-hero--portrait,
.page-hero--invite {
  padding-block: clamp(140px, 17vh, 196px) clamp(56px, 7vw, 96px);
}

/* The statement heroes have no companion column, so the headline itself has to
   hold the page. It gets a wider measure and a hairline below it. */
.page-hero--statement .page-hero__title { max-width: 16ch; }
.page-hero--statement .page-hero__standfirst { max-width: var(--measure-lead); }
.page-hero--statement {
  padding-block: clamp(148px, 18vh, 208px) clamp(56px, 7vw, 96px);
}

@media (min-width: 900px) {
  .page-hero--index .page-hero__inner,
  .page-hero--invite .page-hero__inner {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  }
  .page-hero--portrait .page-hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr);
  }
}

/* -- Services hero: an index into the page ------------------------------- */

.hero-index { position: relative; }

.hero-index__seam {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  display: none;
}

.hero-index__list {
  list-style: none;
  display: grid;
}

.hero-index__item + .hero-index__item { border-top: 1px solid var(--hairline); }

.hero-index__item a {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding-block: 14px;
  color: var(--heading);
  text-decoration: none;
  transition: transform var(--dur-base) var(--ease-out-quart);
}

.hero-index__num {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  color: var(--meta);
}

.hero-index__label {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 1.35;
}

@media (hover: hover) {
  .hero-index__item a:hover { transform: translateX(6px); }
  .hero-index__item a:hover .hero-index__label { color: var(--meta); }
}

@media (min-width: 900px) {
  .hero-index { padding-left: clamp(28px, 3.5vw, 48px); }
  .hero-index__seam { display: block; }
}

/* -- Contact hero: the direct channels, before any form ------------------- */

.channels { position: relative; }

.channels__seam {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  display: none;
}

.channels__list { list-style: none; display: grid; }
.channels__list li { padding-block: 14px; }
.channels__list li + li { border-top: 1px solid var(--hairline); }
.channels__label { display: block; margin-bottom: 6px; }

.channels__list a {
  color: var(--heading);
  text-decoration: none;
  font-size: var(--step-ui);
  overflow-wrap: anywhere;
}

@media (hover: hover) { .channels__list a:hover { color: var(--meta); } }

@media (min-width: 900px) {
  .channels { padding-left: clamp(28px, 3.5vw, 48px); }
  .channels__seam { display: block; }
}

/* -- About hero: the portrait --------------------------------------------- */

.portrait { position: relative; margin: 0; }

.portrait__seam {
  position: absolute;
  left: calc(-1 * clamp(16px, 2.4vw, 32px));
  top: 0;
  bottom: 0;
  width: 1px;
  display: none;
}

.portrait img {
  width: 100%;
  border-radius: var(--r-xl);
}

@media (min-width: 900px) {
  .portrait__seam { display: block; }
}

/* -- Services: the coverage matrix ---------------------------------------
   A real table, because it is real tabular data. The marks are decorative;
   the meaning is carried by visually-hidden text so a screen reader announces
   "Addressed" rather than describing a rectangle. ------------------------- */


/* Wide content scrolls inside its own container; the page body never does. */


/* -- Services: the chapters ------------------------------------------------ */

.chapter__inner {
  display: grid;
  gap: clamp(20px, 3vw, 40px);
}

.chapter__mark {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 28px);
}

/* The chapter's icon, in a hairline ring, from the same set the home page's
   methodology draws from. Gold as a stroke on paper and on warm alike. */
.chapter__glyph {
  display: grid;
  place-items: center;
  flex: none;
  width: 48px;
  height: 48px;
  border: 1px solid rgba(176, 125, 33, 0.35);
  border-radius: 50%;
  color: var(--gold);
}

.chapter__num {
  font-family: var(--font-display);
  font-size: var(--step-numeral);
  font-weight: 300;
  line-height: 0.85;
  color: var(--rule);
}

.chapter__seam { display: none; }

.chapter__title { margin-bottom: clamp(14px, 1.6vw, 20px); }
.chapter__body { max-width: 56ch; }

.chapter__list {
  list-style: none;
  display: grid;
  margin-top: clamp(24px, 3vw, 36px);
  max-width: 56ch;
}

.chapter__item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding-block: 12px;
  border-top: 1px solid var(--hairline);
  font-size: var(--step-ui);
  color: var(--text);
}

/* The check before each scope item. Gold-ink, sized to the x-height, and
   nudged down so it sits on the first line rather than floating above it. */
.chapter__check { flex: none; margin-top: 4px; color: var(--gold-ink); }

.chapter__item:last-child { border-bottom: 1px solid var(--hairline); }


@media (min-width: 860px) {
  .chapter__inner { grid-template-columns: auto minmax(0, 1fr); gap: clamp(40px, 5vw, 80px); }
  /* Stacked in the mark column: ring, then numeral, then the seam below. */
  .chapter__mark { flex-direction: column; align-items: flex-start; gap: 18px; }
  /* Three children in a two-column grid: the scope list wrapped onto row two
     under the numeral, 223px wide. It belongs under the copy it describes. */
  .chapter__list { grid-column: 2; }
  .chapter__seam {
    display: block;
    width: 1px;
    align-self: stretch;
    min-height: 100%;
  }
}

/* -- How We Work: the vertical spine --------------------------------------
   The home page carries the horizontal reading of this idea. Here it is the
   full one: one stage per band, each drawing its own segment of the rail.

   Segmenting the rail per stage is what lets the whole thing run on the
   existing .stage IntersectionObserver. A single rail drawn from scroll
   position would need a scroll listener, which the site does not have and
   does not want. ---------------------------------------------------------- */

.spine__head { max-width: var(--measure); margin-bottom: clamp(44px, 6vw, 80px); }
.spine__list { list-style: none; }

.spine__stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(20px, 3vw, 40px);
}

.spine__rail {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 12px;
}

.spine__dot {
  width: 11px;
  height: 11px;
  flex: none;
  margin-top: 10px;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  opacity: 0.4;  /* dim-exempt: decorative ring, meaning is on the numeral */
  transition: opacity var(--dur-slow) var(--ease-out-quart);
}

.spine__line {
  width: 1px;
  flex: 1 1 auto;
  margin-top: 8px;
}

.spine__stage:last-child .spine__line { display: none; }

.spine__text {
  padding-bottom: clamp(44px, 6vw, 88px);
  opacity: 0.68;  /* dim-floor: text, see section 2 note */
  transition: opacity var(--dur-slow) var(--ease-out-quart);
}

.spine__stage:last-child .spine__text { padding-bottom: 0; }

.spine__stage.is-active .spine__text { opacity: 1; }
.spine__stage.is-active .spine__dot { opacity: 1; }

html:not(.js) .spine__text,
html:not(.js) .spine__dot { opacity: 1; }

.spine__num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 1.6rem + 2.4vw, 3.75rem);
  font-weight: 300;
  line-height: 1;
  color: var(--rule);
  margin-bottom: clamp(10px, 1.2vw, 16px);
}

.spine__title { margin-bottom: clamp(10px, 1.2vw, 16px); }
.spine__body { max-width: 54ch; }

@media (min-width: 860px) {
  .spine__stage { grid-template-columns: auto minmax(0, 1fr); gap: clamp(32px, 4vw, 56px); }
  .spine__num {
    position: absolute;
    margin-left: calc(-1 * clamp(32px, 4vw, 56px) - 12px);
    transform: translateX(-100%);
  }
  .spine__text { position: relative; padding-left: 0; }
}

/* -- How We Work: the adapted intervention -------------------------------- */

/* The adapted-intervention section. The Romanian master asks for it to be set
   apart from the navy timeline above ("diferentiata vizual"), which the warm
   ground does; the two-column split gives the heading room to breathe and the
   five paragraphs a readable measure rather than a full-bleed line. */
.adapted__inner { display: grid; gap: clamp(28px, 3.4vw, 48px); }
.adapted__head h2 { max-width: 20ch; }
.adapted__body { max-width: 62ch; }
.adapted__body p + p { margin-top: clamp(16px, 1.8vw, 22px); }

/* The last line is the section's conclusion, not a fifth paragraph. */
.adapted__close {
  margin-top: clamp(24px, 2.8vw, 34px);
  padding-top: clamp(20px, 2.4vw, 28px);
  border-top: 1px solid var(--hairline);
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.34;
  color: var(--heading);
}

@media (min-width: 1000px) {
  .adapted__inner { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr); }
}

/* -- Results: the evidence chain -------------------------------------------
   Horizontal on desktop, because the point is that each term produces the
   next. Vertical on mobile, for the same reason -- a squeezed horizontal row
   would read as four unrelated labels. ------------------------------------ */


/* -- Results: what we measure ---------------------------------------------- */


/* -- Results: the placeholder, labelled as one -----------------------------
   Section 30 forbids disguising a placeholder as a claim. So it is framed,
   tagged "In preparation", and says plainly that nothing is published yet. -- */


.prep__lead {
  font-family: var(--font-display);
  font-size: var(--step-h3);
  font-weight: 300;
  line-height: 1.32;
  color: var(--heading);
  margin-bottom: clamp(18px, 2.2vw, 26px);
}


/* -- About: the principles -------------------------------------------------- */
/*
   Deleted 2026-09-15, and worth recording because the damage was visible on a
   live page for weeks.

   This block styled an earlier version of the principles list, one built from
   `.principles__head`, `.principles__list`, `.principle__num`,
   `.principle__rule` and `.principle__body`. That markup is gone -- about.py
   now emits a plain `h3.principle__title` followed by paragraphs -- and not
   one of those five class names appears on any of the thirty pages.

   The rules did not go quiet when their markup did. Two of them kept applying
   to the new markup and broke it:

     * `.principle { display: grid; grid-template-columns: auto minmax(0,1fr) }`
       Written for two known children, a number and a body. The new list item
       has a heading and three or four paragraphs, and grid auto-flow dealt
       them alternately into the two columns: heading left, first paragraph
       right, second paragraph left. In the narrow column a Romanian word set
       one letter per line, vertically, which is how the client found it.

     * `.principle { opacity: 0.68 }`, lifted to 1 by `.is-active`. Only
       initMethodology adds `is-active`, and only to `.stage`, so every
       principle on the page sat permanently dimmed.

   Both also quietly overrode the correct `.principle` and `.principle__title`
   rules in section 8x, which were and are the ones that match the markup.

   The lesson is not "delete dead CSS". It is that a class name surviving a
   rewrite is more dangerous than one that does not: nothing errors, nothing
   logs, and the rules simply start decorating the wrong thing.

   `.session` went with it -- also zero uses.
*/

/* -- Contact: the form sits on navy, so the panel needs its own rhythm ------ */

.contact.panel--navy .contact__details a { color: var(--gold-light); }


/* --------------------------------------------------------------------------
   8n. Interior pages -- corrections found in the browser
   -------------------------------------------------------------------------- */

/* Anchor targets were landing under the fixed header: the services hero index,
   the contact routes and every in-page link on the home page all scrolled the
   heading out of sight. scroll-margin-top is the whole fix; it costs nothing
   and applies to keyboard focus jumps as well as clicks. */
:target,
[id] { scroll-margin-top: clamp(88px, 12vh, 116px); }

/* -- Spine: the numeral now owns a column ---------------------------------- */

.spine__stage { grid-template-columns: auto auto minmax(0, 1fr); }

.spine__num {
  margin-bottom: 0;
  line-height: 0.92;
  min-width: 2.2ch;
  opacity: 0.68;  /* dim-floor: large text */
  transition: opacity var(--dur-slow) var(--ease-out-quart);
}

.spine__stage.is-active .spine__num { opacity: 1; }
html:not(.js) .spine__num { opacity: 1; }

.spine__body { max-width: 62ch; }

@media (min-width: 860px) {
  .spine__stage { gap: clamp(24px, 3vw, 44px); }
  /* The desktop override that absolutely positioned the numeral is gone: it
     lifted the numeral out of flow and dropped it on top of the rail. */
  .spine__num { position: static; margin-left: 0; transform: none; }
  .spine__text { position: static; }
}

/* A quiet measurement grid behind the spine. Section 12 asks for architectural
   planes and measurement lines in the methodology; this is that idea in CSS
   rather than WebGL, so it costs nothing and degrades to nothing. */
.spine .container { position: relative; }

.spine .container::after {
  content: "";
  position: absolute;
  inset-block: 0;
  right: 0;
  width: min(38%, 420px);
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to right,
    var(--navy-lift) 0 1px,
    transparent 1px 72px
  );
  opacity: 0.55;  /* dim-exempt: background grid, carries no text */
  display: none;
}

@media (min-width: 1080px) { .spine .container::after { display: block; } }

/* -- Chapters: scope list moves to its own column -------------------------- */

.chapter__lead { min-width: 0; }
.chapter__list { margin-top: clamp(24px, 3vw, 36px); max-width: none; }

@media (min-width: 1040px) {
  .chapter__inner {
    grid-template-columns: auto minmax(0, 1.05fr) minmax(0, 0.72fr);
    align-items: start;
  }
  .chapter__list { grid-column: 3; margin-top: 0; }
  .chapter__body { max-width: 46ch; }
}

/* -- Matrix: hold the table to a readable width ---------------------------- */


/* -- About: the biography reads as an opening spread ----------------------- */


/* -- About: the session photograph is a crop, not a full plate ------------- */

.session img {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: 50% 38%;
  border-radius: var(--r-xl);
}

/* -- About: principles read better on two lines than three ----------------- */

.principle__title { max-width: 26ch; }

/* -- Forms on navy ---------------------------------------------------------
   The contact form moved onto the navy panel, where the inherited white input
   fills read as a generic web form dropped onto the brand. Section 20 asks for
   hairline fields; on navy that means a transparent well with a gold-light
   hairline, which is the same grammar as every other rule on the site.

   The note above the form was inheriting --taupe-ink, which is 3.2:1 on navy
   at 12px -- an AA failure introduced by the move. It takes the navy body
   colour instead, at 11:1.
   -------------------------------------------------------------------------- */

.panel--navy .field label { color: var(--gold-light); }

.panel--navy .field input,
.panel--navy .field select,
.panel--navy .field textarea {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(215, 193, 133, 0.34);
  color: var(--paper);
}

.panel--navy .field input:focus,
.panel--navy .field select:focus,
.panel--navy .field textarea:focus { border-color: var(--gold-light); }

.panel--navy .field input::placeholder,
.panel--navy .field textarea::placeholder { color: var(--navy-text); opacity: 0.7; }

.panel--navy .contact__demo-note { color: var(--navy-text); }

/* Autofill repaints the field white in Chrome, which would put white-on-white
   text in a navy well. The inset shadow is the only way to override it. */
.panel--navy .field input:-webkit-autofill {
  -webkit-text-fill-color: var(--paper);
  box-shadow: inset 0 0 0 1000px var(--navy-lift);
}

/* -- Contact: the form column needs its own breathing room on navy --------- */

.contact.panel--navy .contact__details li { border-color: rgba(215, 193, 133, 0.22); }

/* --------------------------------------------------------------------------
   8o. Diagnostic -- the sales performance self-assessment

   Brand note on the bars: a score bar is a filled surface, so it cannot be
   gold. The fills are paper on navy. Gold does what it does everywhere else on
   this site -- it marks the one row that matters, as a hairline and a label
   colour, and nothing more.
   -------------------------------------------------------------------------- */

/* The hidden attribute has to win. Any later rule that sets display on one of
   these blocks would otherwise reveal a readout that has no scores in it. */
[hidden] { display: none !important; }


.diag__list { list-style: none; }

.diag__q { border-bottom: 1px solid var(--hairline); }

.diag__field {
  border: 0;
  margin: 0;
  padding: clamp(20px, 2.4vw, 28px) 0;
  display: grid;
  gap: clamp(16px, 2vw, 28px);
}

/* A legend cannot be a grid item in every engine, so the layout hangs off the
   fieldset and the legend is displayed as a contents-level block. */
.diag__legend {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 0;
  float: left;
  width: 100%;
}

.diag__num {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  color: var(--meta);
}

.diag__stem {
  font-family: var(--font-display);
  font-size: clamp(1.0625rem, 0.95rem + 0.45vw, 1.3125rem);
  font-weight: 300;
  line-height: 1.35;
  color: var(--heading);
  max-width: 52ch;
}

.diag__scale {
  display: grid;
  /* One column on a phone. Two columns put five answers as 2 + 2 + 1, with
     the longer Romanian labels ("În mare parte adevărat") wrapping inside
     their pills while the shorter ones did not -- five pills of three
     different heights in a scale that should read as one line. A column of
     five equal pills reads as the scale it is, and each is a full-width
     thumb target. Two columns from 480px, five on desktop. */
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  clear: both;
}

@media (min-width: 480px) {
  .diag__scale { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.diag__opt { position: relative; display: block; }

/* The native control stays in the accessibility tree and keeps arrow-key
   navigation inside the radio group; only its painting is replaced. */
.diag__opt input {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.diag__optlabel {
  display: block;
  padding: 10px 14px;
  text-align: center;
  font-size: var(--step-ui);
  color: var(--text);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  transition:
    background-color var(--dur-fast) var(--ease-out-quart),
    border-color var(--dur-fast) var(--ease-out-quart),
    color var(--dur-fast) var(--ease-out-quart);
}

.diag__opt input:checked + .diag__optlabel {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--paper);
}

.diag__opt input:focus-visible + .diag__optlabel {
  outline: 2px solid var(--rule);
  outline-offset: 2px;
}

@media (hover: hover) {
  .diag__opt input:not(:checked):hover + .diag__optlabel { border-color: var(--rule); }
}

@media (min-width: 720px) {
  .diag__field { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
  .diag__legend { float: none; width: auto; }
  /* Five, not four. The instrument moved from a four-point scale to the
     approved five on 2026-09-14 and this rule did not move with it, so the
     fifth answer -- "Completely true" -- sat alone on a second row under the
     other four, on every question, on both languages, for two days. */
  .diag__scale { grid-template-columns: repeat(5, minmax(0, 1fr)); min-width: 480px; }
}

/* The foot sticks so the count and the submit stay reachable through twelve
   questions. This page carries no action bar for the same reason -- two sticky
   bars competing at the bottom of one screen is one too many. */
.diag__foot {
  position: sticky;
  bottom: 0;
  z-index: 20;
  /* Set by the script as answers arrive: answered / total. */
  --progress: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px clamp(16px, 2vw, 28px);
  margin-top: clamp(28px, 3.5vw, 44px);
  padding-block: clamp(16px, 2vw, 22px);
  background: var(--ground);
  border-top: 1px solid var(--hairline);
}

/* The rail: the foot's own top hairline, with a gold line drawn over it as
   far as the answers have reached. Twelve questions, twelve steps. The count
   beside it says the number; this says the shape. */
.diag__foot::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  height: 1px;
  width: 100%;
  background: var(--gold);
  transform: scaleX(var(--progress));
  transform-origin: left center;
  transition: transform 600ms var(--ease-out-expo);
}

.diag__progress {
  margin: 0;
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--taupe-ink);
}

.diag__foot .form-error { flex: 1 0 100%; margin: 0; }

.diag__nojs { margin-top: 28px; max-width: 60ch; color: var(--taupe-ink); }
html.js .diag__nojs { display: none; }

/* -- The entry band, on the home page and How We Work ---------------------- */

.diag-cta {
  display: grid;
  gap: clamp(20px, 2.4vw, 32px);
  align-items: center;
  margin-top: clamp(36px, 4.5vw, 56px);
  padding: clamp(26px, 3vw, 40px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
}

.diag-cta__rule { display: block; width: 44px; }
.diag-cta__title { font-size: var(--step-h3); margin-bottom: 10px; }
.diag-cta__body { max-width: 48ch; margin: 0; }
.diag-cta__btn { justify-self: start; }

@media (min-width: 860px) {
  .diag-cta {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: clamp(24px, 3vw, 44px);
  }
  .diag-cta__rule { align-self: start; margin-top: 14px; }
  .diag-cta__btn { justify-self: end; }
}

/* -- The ticket ------------------------------------------------------------
   The same entry, on the home page, set as something the reader is handed
   rather than something they are told. Markup in tools/ticket.py; the three
   strings inside it are diagnostic.py's.

   It was built as the one large light surface on a navy ground anywhere on
   the site; since 2026-09-16 the ground under it is warm (see the border
   note below). Either way the idea holds: a ticket is paper. Gold stays what
   it is everywhere else -- hairlines, small caps and the stamp's two rules.
   Nothing here is a filled gold surface.
   -------------------------------------------------------------------------- */

.ticket {
  /* The ticket resets the ground tokens rather than overriding properties on
     each descendant. .panel--navy redefines --heading, --text, --meta and
     --hairline for everything inside it; the ticket is not "inside" the panel
     in any visual sense, it is a light object lying on it, so it declares its
     own ground and every child inherits the right colour without knowing the
     ticket exists. */
  --ground:    var(--paper);
  --text:      var(--ink);
  --heading:   var(--navy);
  --meta:      var(--gold-ink);
  --rule:      var(--gold);
  --hairline:  var(--line);

  /* Where the perforation runs. Used twice -- once to size the stub column,
     once to place the punched notches -- so the two cannot drift apart. */
  --seam: 220px;
  --seam-y: 260px;
  --notch: 13px;
  /* The perforation itself. --hairline is #E3E0DA, which is the right weight
     for a rule that separates two blocks of text and completely the wrong one
     for the line the reader is supposed to believe they could tear along: at
     that contrast the notches read as two dents in a plain card. Taupe at 55%
     is still a hairline, and it is visible. */
  --perf: rgba(139, 129, 120, 0.55);

  position: relative;
  display: grid;
  margin-top: clamp(36px, 4.5vw, 56px);
  background: var(--ground);
  color: var(--text);
  border-radius: var(--r-lg);
  /* The section under the ticket was navy until 2026-09-16, and a paper
     ticket on navy needed no edge. It is warm now -- the diagnostic moved to
     follow the methodology, and the grounds around it were re-dealt so no
     two in a row match -- and paper on warm is a difference of six percent.
     A hairline gives the ticket back its edge; the mask cuts it at the
     notches along with everything else, which is what a perforation does. */
  border: 1px solid var(--line);

  /* The notches. Two masks, each opaque except for one circle; where a
     browser supports mask-composite they intersect and both circles are
     punched. Where it does not, the default `add` unions them, which resolves
     to "no holes" -- the ticket simply reads as a card. That is the reason
     this is done with two layers instead of one cleverer gradient: the
     failure mode of the clever version is an invisible ticket. */
  -webkit-mask:
    radial-gradient(circle var(--notch) at 0 var(--seam-y), transparent 98%, #000 100%),
    radial-gradient(circle var(--notch) at 100% var(--seam-y), transparent 98%, #000 100%);
  mask:
    radial-gradient(circle var(--notch) at 0 var(--seam-y), transparent 98%, #000 100%),
    radial-gradient(circle var(--notch) at 100% var(--seam-y), transparent 98%, #000 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* The stub: the photograph, and nothing else. A stub is where a ticket puts
   its picture. */
/* The photograph is taken out of flow so it cannot size the ticket.
   Left in flow it does: a 440x1053 image in a 220px column resolves to a
   526px tall cell, the grid row grows to match, and the main column ends up
   with 150px of empty paper under its button that nobody asked for. Out of
   flow, the row is sized by the words -- which is the right way round, since
   the words are the reason the block exists -- and object-fit crops the
   photograph to whatever height that turns out to be. */
.ticket__stub { position: relative; overflow: hidden; }
.ticket__stub picture { position: absolute; inset: 0; display: block; }
.ticket__stub img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 28%; }

.ticket__main {
  position: relative;
  padding: clamp(28px, 3.4vw, 44px);
  border-top: 1px dashed var(--perf);
}

.ticket__eyebrow { margin-bottom: 20px; max-width: none; }
.ticket__title { font-size: var(--step-h3); color: var(--heading); margin: 0 0 12px; }
.ticket__body { max-width: 48ch; margin: 0; color: var(--text); }

/* The two fields. Set as ticket furniture -- label above value, small caps
   over serif -- because that is how a ticket prints what it is for. */
.ticket__fields {
  display: flex;
  flex-wrap: wrap;
  gap: 18px clamp(28px, 4vw, 52px);
  margin: clamp(24px, 2.8vw, 32px) 0 0;
  padding-top: clamp(20px, 2.4vw, 26px);
  border-top: 1px solid var(--hairline);
}

.ticket__field dt {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
  margin-bottom: 7px;
}

.ticket__field dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-lead);
  line-height: 1.25;
  color: var(--heading);
}

.ticket__btn { margin-top: clamp(24px, 2.8vw, 32px); }

/* The navy panel turns .btn--primary white so it can be seen on navy. Inside
   the ticket the ground is paper again, so the button has to be turned back.
   Two classes rather than one: .panel--navy .btn--primary is (0,2,0) and
   source order alone deciding this would break the first time somebody moved
   a block. */
.ticket .btn.btn--primary {
  background: var(--navy);
  color: var(--paper);
  border-color: var(--navy);
}

@media (hover: hover) {
  .ticket .btn.btn--primary:hover { background: var(--navy-lift); border-color: var(--navy-lift); }
}

/* The stamp. Two hairline rules with paper between them, which is how a
   rubber stamp's frame actually prints, and no fill: this is gold as ink.
   It arrives oversized and presses down to size when the section reveals --
   the one piece of theatre in the block. */
.ticket__stamp {
  position: absolute;
  right: clamp(24px, 3vw, 40px);
  bottom: clamp(24px, 3vw, 40px);
  display: grid;
  justify-items: center;
  gap: 3px;
  padding: 11px 20px;
  border-radius: 7px;
  color: var(--gold-ink);
  box-shadow:
    0 0 0 1px var(--gold),
    0 0 0 4px var(--ground),
    0 0 0 5px var(--gold);
  opacity: 0;
  transform: scale(1.55);
  transition:
    opacity var(--dur-fast) linear,
    transform var(--dur-base) var(--ease-out-expo);
}

.ticket__stamp-main {
  font-family: var(--font-display);
  font-size: 1rem;
  line-height: 1;
  letter-spacing: 0.02em;
}

.ticket__stamp-sub {
  font-size: 0.625rem;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
}

.reveal.is-revealed .ticket__stamp { opacity: 0.9; transform: scale(1); }

/* Both escape hatches. If site.js never runs nothing may be left invisible,
   and a stamp that slams into place is exactly the kind of motion somebody
   turns off at the operating system. */
html:not(.js) .ticket__stamp { opacity: 0.9; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .ticket__stamp { transition: none; opacity: 0.9; transform: none; }
}

/* Below 560px the stamp would sit on top of the button. It is decoration;
   the button is the point. */
@media (max-width: 559px) {
  .ticket__stamp { display: none; }
}

@media (min-width: 900px) {
  .ticket {
    grid-template-columns: var(--seam) minmax(0, 1fr);
    /* The seam turns vertical, so the notches move to the top and bottom
       edges of the column boundary. */
    -webkit-mask:
      radial-gradient(circle var(--notch) at var(--seam) 0, transparent 98%, #000 100%),
      radial-gradient(circle var(--notch) at var(--seam) 100%, transparent 98%, #000 100%);
    mask:
      radial-gradient(circle var(--notch) at var(--seam) 0, transparent 98%, #000 100%),
      radial-gradient(circle var(--notch) at var(--seam) 100%, transparent 98%, #000 100%);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
  }

  /* The extra 30px of left padding went with the vertical seam text it was
     making room for. */
  .ticket__main {
    border-top: 0;
    border-left: 1px dashed var(--perf);
  }
}

/* Stacked, the stub is a band across the top. Its height is --seam-y so the
   punched notches land exactly on the dashed line. */
@media (max-width: 899px) {
  .ticket__stub { height: var(--seam-y); }
}

/* Paragraph spacing on the diagnostic, which had none.

   Four places on this page set consecutive paragraphs with a 0px gap, so they
   ran together as one block: the instrument's two notes under the lead, the
   readout's area copy -- three approved paragraphs about the weakest dimension,
   which is the whole output of the thing -- and the paragraphs under it.

   None of `.diag__note`, `.area__body` or `.readout__next` had a single rule in
   this stylesheet. They are not styled and then broken; they were never styled
   at all. Found by measuring every `p + p` on every page after the same fault
   turned up in the Results page's case-study section. */
.diag__head .lead + .diag__note { margin-top: clamp(14px, 1.6vw, 20px); }
.diag__note + .diag__note { margin-top: 8px; }
.area__body + .area__body { margin-top: clamp(14px, 1.6vw, 20px); }
.readout__next p + p { margin-top: clamp(14px, 1.6vw, 20px); }

/* The brief: the profile as one line, for pasting into the booking note.
   It reuses .share's field and button, so this is only the frame around
   them. */
.brief {
  margin: clamp(24px, 3vw, 34px) 0;
  padding-top: clamp(20px, 2.4vw, 26px);
  border-top: 1px solid var(--hairline);
}

.brief__label { color: var(--meta); margin-bottom: clamp(12px, 1.4vw, 16px); }

/* -- The readout ----------------------------------------------------------- */

/* Focus lands here after submit so a screen-reader user arrives at their
   result rather than at the top of the page. The panel is not interactive and
   nobody tabbed to it, so it must not draw a focus ring: a 2px gold outline
   around a full-width navy block reads as a rendering fault. The focus move --
   the part that carries the accessibility -- stays. */
.readout[tabindex="-1"]:focus,
.readout[tabindex="-1"]:focus-visible { outline: none; }

.readout__head { max-width: var(--measure); margin-bottom: clamp(36px, 4.5vw, 56px); }
.readout__bars { list-style: none; display: grid; }

.readout__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 6px 16px;
  align-items: center;
  padding-block: clamp(16px, 2vw, 22px);
  border-top: 1px solid var(--hairline);
}

.readout__row:last-child { border-bottom: 1px solid var(--hairline); }

.readout__label {
  font-family: var(--font-display);
  font-size: clamp(1.0625rem, 0.95rem + 0.4vw, 1.25rem);
  font-weight: 300;
  color: var(--heading);
}

.readout__value {
  font-family: var(--font-display);
  font-size: 1.125rem;
  color: var(--heading);
  white-space: nowrap;
}

.readout__of { color: var(--text); font-size: 0.875rem; }

.readout__track {
  grid-column: 1 / -1;
  order: 3;
  height: 6px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}

.readout__fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--paper);
  transition: width var(--dur-slow) var(--ease-out-expo);
}

.readout__band {
  grid-column: 1 / -1;
  order: 4;
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--text);
}

/* Gold marks the one row that matters, and only that row -- as a rule and a
   label colour. The bar itself stays paper: a filled gold bar is a gold fill
   however meaningful it is, and the gold-fill lint caught it as one. Marking
   the row with its own hairline says the same thing in the site's grammar. */
.readout__row.is-constraint { border-top-color: var(--rule); }
.readout__row.is-constraint .readout__label { color: var(--meta); }
.readout__row.is-constraint .readout__band { color: var(--meta); }

.readout__rule { display: block; width: 56px; margin-bottom: clamp(20px, 2.4vw, 28px); }


.readout__caveat {
  margin-top: clamp(36px, 4.5vw, 56px);
  max-width: 58ch;
  font-size: var(--step-ui);
}

/* The findings block -------------------------------------------------------
   The part of the readout that gives something away. Above it the visitor
   learns a score they supplied; here they get their own lowest answers back,
   each with something to go and verify.

   Set as a list of pairs rather than a card grid on purpose: this is reading
   matter, it is meant to be printed or forwarded, and a card would make it
   look like an offer. The gold appears only as the 1px rule down the left of
   the action, which is the site's existing device for "this is the thing to
   do", and remains a hairline. */

/* The shareable profile ----------------------------------------------------
   A readonly field holding a link, because the link is the deliverable. It is
   shown rather than hidden behind the button so that the fallback -- select
   and press Ctrl+C -- is always available: navigator.clipboard needs a secure
   context and is simply absent on file:// and plain http, and a copy button
   that silently does nothing is worse than no button. */
.readout__share { margin-top: clamp(36px, 4.5vw, 56px); }

.readout__sharetitle {
  font-size: var(--step-h3);
  margin-block: clamp(20px, 2.4vw, 28px) 12px;
}

.readout__sharelead {
  max-width: 58ch;
  font-size: var(--step-ui);
  margin-bottom: clamp(20px, 2.4vw, 26px);
}

.share {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: stretch;
  max-width: 60ch;
}

.share__field {
  flex: 1 1 22ch;
  min-width: 0;
  padding: 12px 16px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  font-family: var(--font-ui);
  font-size: var(--step-ui);
  text-overflow: ellipsis;
}

.share__field:focus-visible { border-color: var(--rule); }

.share__copy { flex: 0 0 auto; }

.share__note {
  margin: 16px 0 0;
  max-width: 58ch;
  font-size: var(--step-meta);
  color: var(--meta);
}

/* Restored from somebody else's link. Marked with the hairline rather than a
   colour so it reads as a note on the heading, not as a warning. */
.readout__restored {
  margin: 18px 0 0;
  max-width: 54ch;
  padding-left: clamp(16px, 2vw, 22px);
  border-left: 1px solid var(--rule);
  font-size: var(--step-ui);
  color: var(--meta);
}

/* On paper a link nobody can click, and a copy button that does nothing, are
   both noise -- but the fact that the profile was shared is worth keeping. */
@media print {
  .share, .readout__sharelead { display: none !important; }
}


/* Their sentence, in the heading voice, because they wrote it. */

/* The thing to do, marked by the hairline rather than by a colour change. */

/* On paper the findings are the point of the printout, so they must not be
   split down the middle by a page break. */

/* The enquiry sits inside the readout because that is where the profile is.
   Sending the visitor to the contact page to retype it was the version that
   threw away the only qualifying information they had just given. */

.readout__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px 28px;
  margin-top: clamp(32px, 4vw, 48px);
}

@media (min-width: 860px) {
  .readout__row {
    grid-template-columns: minmax(0, 12rem) minmax(0, 1fr) auto minmax(0, 16rem);
    gap: clamp(16px, 2vw, 28px);
  }
  .readout__track { grid-column: auto; order: 0; }
  .readout__band { grid-column: auto; order: 0; text-align: right; }
}

/* --------------------------------------------------------------------------
   8q. The orbit -- a travelling hairline on the action a page wants

   Adapted from a glowing-border component. The technique is worth having: a
   registered custom property lets an angle animate, which lets a conic
   gradient rotate, which is the only way to make light travel around a border
   in pure CSS.

   What was not worth having was its palette. The original cycles hue 0-360
   with a blurred neon glow and a flickering white shadow -- forbidden outright
   by the brief (no neon, no startup gradients, no gold gradients) and, at
   roughly two large brightness changes per second with no reduced-motion
   guard, a WCAG 2.3.1 flash risk on every page it appeared on.

   So the mechanism stays and the light becomes what light is everywhere else
   here: one gold hairline, one lap.

   It is invisible at rest. Gold marks the action you are already reaching for,
   which is the only reason a moving thing earns a place on a page this quiet:
   remove it and you lose the response, not just the decoration.
   -------------------------------------------------------------------------- */

@property --orbit {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.btn--primary,
.route,
.programme,
.diag-cta { position: relative; }

/* padding + mask-composite is what makes this a ring rather than a fill: the
   gradient paints the whole box, then the mask subtracts the content box and
   leaves exactly the 1px padding band. A hairline, drawn the long way round. */
.btn--primary::before,
.route::before,
.programme::before,
.diag-cta::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--orbit),
              transparent 0deg,
              var(--orbit-ink, var(--rule)) 24deg,
              transparent 58deg,
              transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out-quart);
}

/* The cards already carry a 1px border. inset:-1px lays the ring exactly over
   it rather than just inside it, which would read as a doubled edge. The
   button's own border is transparent, so it stays at inset:0. */
.route::before,
.programme::before,
.diag-cta::before { inset: -1px; }

/* On a navy panel the primary button inverts: it is a white surface inside a
   dark section. The ground context there resolves --rule to --gold-light,
   which the contrast table records as 1.77:1 on paper -- invisible. The ring
   belongs to the surface it is drawn on, not to the section around it, so on
   that one button it takes the light-ground gold instead. This is the closing
   CTA on every page. */
.panel--navy .btn--primary { --orbit-ink: var(--gold); }

.btn--primary:focus-visible::before,
.route:focus-within::before,
.programme:focus-within::before,
.diag-cta:focus-within::before {
  opacity: 1;
  animation: orbit 1.9s linear infinite;
}

@media (hover: hover) {
  .btn--primary:hover::before,
  .route:hover::before,
  .programme:hover::before,
  .diag-cta:hover::before {
    opacity: 1;
    animation: orbit 1.9s linear infinite;
  }
}

@keyframes orbit { to { --orbit: 360deg; } }

/* Nothing travels for a reader who asked for no motion. The static hover
   states underneath -- the lifted navy on a button, the gold border on a card
   -- already say the same thing without moving, so this simply steps aside. */
@media (prefers-reduced-motion: reduce) {
  .btn--primary::before,
  .route::before,
  .programme::before,
  .diag-cta::before { display: none; }
}

/* A ring around a card that is about to be printed is noise on paper. */
@media print {
  .btn--primary::before,
  .route::before,
  .programme::before,
  .diag-cta::before { display: none; }
}

/* --------------------------------------------------------------------------
   8r. Client quotes

   Columns of quote cards drifting upward at slightly different speeds, adapted
   from a React marquee. The motion is a CSS transform on a doubled list -- the
   track holds the same cards twice and travels -50%, so the second copy lands
   exactly where the first began and the loop has no seam.

   Nothing renders until tools/testimonials.py holds at least one entry that is
   complete, consented and approved. The list is empty today, so this whole
   block is dormant. That is deliberate: an empty testimonial band reads as a
   business with no customers, which is worse than not raising the subject.

   No portraits. The site has no photographs of clients, and a stock face
   standing in for a real person is the same fabrication in another medium.
   -------------------------------------------------------------------------- */

.quotes__head { max-width: var(--measure); margin-bottom: clamp(36px, 4.5vw, 56px); }

.quotes__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(20px, 2.4vw, 28px);
  max-height: 680px;
  overflow: hidden;
  /* Cards enter and leave through nothing rather than through a hard edge. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
}

.quotes__track {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.4vw, 28px);
  animation: quotes-drift var(--quote-speed, 36s) linear infinite;
  will-change: transform;
}

.quotes__track > div {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.4vw, 28px);
}

@keyframes quotes-drift {
  to { transform: translateY(-50%); }
}

/* Reading a quote should not be a chase. */
@media (hover: hover) {
  .quotes__stage:hover .quotes__track { animation-play-state: paused; }
}

/* Keyboard users get the same courtesy: a card that takes focus stops the
   column it is in, otherwise focus would scroll out from under them. */
.quotes__stage:focus-within .quotes__track { animation-play-state: paused; }

.quote {
  margin: 0;
  padding: clamp(24px, 2.8vw, 34px);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
}

.quote__text {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.0625rem, 0.98rem + 0.35vw, 1.1875rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--heading);
}

/* The quotation mark is set outside the measure so the first line of the quote
   still starts on the text column, the way it would in print. */
.quote__text::before {
  content: "\201C";
  float: left;
  margin-left: -0.42em;
  font-size: 1.6em;
  line-height: 0.9;
  color: var(--rule);
}

.quote__by {
  display: grid;
  gap: 3px;
  margin-top: clamp(18px, 2vw, 24px);
  padding-top: clamp(16px, 1.8vw, 22px);
  border-top: 1px solid var(--hairline);
}

.quote__name { font-size: var(--step-ui); color: var(--heading); }

.quote__role {
  font-size: var(--step-meta);
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--meta);
}

@media (min-width: 760px) {
  .quotes__stage { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1080px) {
  .quotes__stage { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* One column, no drift, no mask, no clipped height. A reader who has asked for
   no motion gets the quotes as a plain list -- all of them, in full. */
@media (prefers-reduced-motion: reduce) {
  .quotes__stage {
    grid-template-columns: minmax(0, 1fr);
    max-height: none;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .quotes__track { animation: none; }
  .quotes__track > div[aria-hidden="true"] { display: none; }
}

@media print {
  .quotes__stage { max-height: none; mask-image: none; }
  .quotes__track { animation: none; }
  .quotes__track > div[aria-hidden="true"] { display: none; }
}

/* --------------------------------------------------------------------------
   8i. Page transition
   A single gold hairline crosses the viewport as the page dims. No white
   flash, no spinner, no blocking overlay -- the wipe is pointer-transparent
   throughout, so it can never trap a click.
   -------------------------------------------------------------------------- */

.page-wipe {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  overflow: hidden;
}

.page-wipe::before {
  content: '';
  position: absolute;
  top: 0; right: auto; bottom: 0; left: 0;
  inset: 0 auto 0 0;
  width: 1px;
  background: var(--gold);
  opacity: 0;
  transform: translateX(-2px);
}

body.is-leaving {
  opacity: 0.32;  /* dim-exempt: transient page-transition state */
  transition: opacity 420ms var(--ease-inout);
}

body.is-leaving .page-wipe::before {
  opacity: 1;
  transform: translateX(100vw);
  transition: transform 520ms var(--ease-inout), opacity 120ms linear;
}

/* --------------------------------------------------------------------------
   9. Reduced motion
   Everything still works; nothing moves. Content is never withheld.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }

  .reveal { opacity: 1; transform: none; }
  .eyebrow::before { transform: scaleX(1); }
  .rule-gold { --draw: 1; }

  /* The hero arrives fully composed rather than assembling itself. */
  .hero [data-seq] { opacity: 1; }
  .hero__portrait { clip-path: none; }
  .hero__seam { transform: scaleY(1); }
  .hero__title .line > span { transform: none; }
  .hero__editorial, .hero__portrait img { transform: none; }
  .stagger > * { opacity: 1; transform: none; }
  .dial__toggle,
  .dial__sheet { transition: none; }

  /* Every stage arrives active: the sequence is readable, it just does not
     illuminate. Information is never carried by motion alone. The interior
     pages dim their own stages too, so they are listed here for the same
     reason -- a reader who has asked for no motion still gets full contrast. */
  .stage__body { opacity: 1; transform: none; }
  .stage__num { color: var(--gold-light); }
  .spine__text,
.spine__num,
.spine__dot,
.principle { opacity: 1; }
    .readout__fill { transition: none; }
      .founder__figure img { transform: none; }
}

@media (prefers-reduced-motion: reduce) and (max-width: 1023px) {
  .hero__seam { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   10. Print
   Printing never fires an IntersectionObserver, so without this every
   revealed section would come out blank. Chrome is dropped, decoration is
   dropped, content is forced visible.
   -------------------------------------------------------------------------- */

@media print {
  .site-header,
.page-wipe,
.light-field,
.skip-link,
.dial,
.enquiry,
.contact__form,
.diag__foot,
.diag__nojs,
.readout__actions { display: none !important; }

  /* A closed <details> prints blank; force every answer open on paper. */
  .qa { break-inside: avoid; }
  .qa__a { display: block !important; }

  /* Anything the page dims until it is scrolled into view prints faded
     otherwise -- the spine would come out at 42% grey on paper. */
  .reveal,
.stagger > *,
.stage__body,
.spine__text,
.spine__num,
.spine__dot,
.principle,
.hero [data-seq] {
    opacity: 1 !important;
    transform: none !important;
  }

    .spine .container::after { display: none; }

  .rule-gold { --draw: 1; }
  .hero__title .line > span { transform: none !important; }
  .hero__portrait { clip-path: none !important; }
    
  .panel--navy {
    --ground: #FFFFFF;
    --text: #1A2230;
    --heading: #001C4B;
    --meta: #90661B;
    --rule: #B07D21;
  }

  .section { padding-block: 24px; }
  .section.panel--navy,
  .section.panel--warm,
  .site-footer { margin: 0; border-radius: 0; }
  a[href^="http"]::after { content: ' (' attr(href) ')'; font-size: 0.75em; color: #756C65; }

  /* The WhatsApp link carries a percent-encoded greeting in its query string.
     Printed out in full it is two lines of noise for a link nobody can click
     on paper, and the number is already printed beside it. */
  a[href^="https://wa.me"]::after { content: none; }

  /* What the diagnostic prints depends on how far the visitor got.
     Unanswered, the printout is the twelve statements -- something two people
     can sit down with. Answered, the statements drop away and the result
     stands alone, because a result is what gets forwarded to whoever signs. */
  .has-readout .diagnostic { display: none !important; }
  .readout { break-inside: avoid; }
}

