/* ============================================================
   Business Automations — styles.css
   Palette derived entirely from ba-logo-1024.png (sampled, PIL).
   Every contrast ratio below is computed, not estimated.
   ============================================================ */

/* ---------- fonts (self-hosted latin subsets, zero third-party) ---------- */
@font-face{font-family:'Anton';src:url('fonts/anton-400.woff2')format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Barlow';src:url('fonts/barlow-400.woff2')format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Barlow';src:url('fonts/barlow-500.woff2')format('woff2');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:'Barlow';src:url('fonts/barlow-600.woff2')format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'Barlow';src:url('fonts/barlow-700.woff2')format('woff2');font-weight:700;font-style:normal;font-display:swap}

/* ---------- tokens ---------- */
:root{
  /* ground */
  --paper:#FFFFFF;
  --paper-2:#F4F5F6;          /* logo slate @6% on white */

  /* ink — the logo slate, darkened along its own axis */
  --ink:#1B2025;              /* 16.41:1 on paper */
  --ink-2:#333C44;            /* 11.23:1 on paper */
  --slate:#4E5C69;            /* THE LOGO MARK COLOUR — 6.87:1 on paper */
  --slate-2:#333C44;        /* THE RAISED GROUND. Was used 4x and never declared. */
  --slate-soft:#A2ABB4;      /* 7.05:1 on --ink, 4.82:1 on --slate-2 (both AA) */

  /* accent — the logo aperture colour */
  --sky:#68B6DD;              /* 7.28:1 on ink · 2.25:1 on paper → NEVER text on light */
  /* TRADITIONAL POST-IT COLOURS (operator ruling 2026-09-08). The flow is
     a wall of real sticky notes, so it wears the real palette: canary,
     Marseille blue, limeade. Colour still carries the meaning and the
     legend still spells it out: yellow is a person, blue is the software,
     green is the AI. All three are light grounds carrying --ink text, so
     the sky-never-on-light law is not engaged anywhere in this block. */
  --postit-yellow:#FDF79A;
  --postit-pink:#FBB9D3;      /* was Marseille blue; the operator moved the
                                 software notes to pink so the three notes read
                                 as the classic pad rather than as brand colour */
  --postit-green:#C6EC9E;
  --sky-bright:#86C5E4;       /* 8.69:1 on ink */
  --sky-link:#447690;         /* 4.95:1 on paper — the text-safe member of the same hue family */

  --body-dark:#B9C3CB;      /* body copy on --ink */
  --text-dark:#E7EBEE;
  --line:#DDE1E4;
  --line-dark:#33414D;

  /* type */
  --display:'Anton',Impact,'Haettenschweiler','Arial Narrow Bold',sans-serif;
  --body:'Barlow',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;

  /* scale — 1.25 major third, clamped */
  --t-md:1rem; --t-lg:1.125rem;
  --t-xl:clamp(1.25rem,1.1rem + .6vw,1.5rem);
  --t-3xl:clamp(2.1rem,1.5rem + 2.8vw,3.4rem);
  --t-4xl:clamp(2.6rem,1.4rem + 5.4vw,5.5rem);

  /* space — 4px base */
  --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem;
  --s6:2rem; --s7:3rem; --s8:4rem; --s9:6rem; --s10:8rem;

  --radius:4px;
  --measure:64ch;
  --shell:1200px;
  --ease:cubic-bezier(.22,.61,.36,1);
}

/* ---------- reset ---------- */
*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink-2);
  font-family:var(--body);font-size:var(--t-lg);line-height:1.6;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img,svg{max-width:100%;display:block}
h1,h2,h3,h4,p,ul,ol,dl,dd,figure,blockquote{margin:0}
p,dd,li{text-wrap:pretty}
ul,ol{padding:0;list-style:none}
a{color:var(--sky-link);text-decoration-thickness:1px;text-underline-offset:3px}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}

/* ---------- focus: never sky on light (2.25:1) ---------- */
:focus-visible{outline:3px solid var(--ink);outline-offset:3px;border-radius:2px}
.dark :focus-visible,.nav :focus-visible,
.foot :focus-visible,.hero :focus-visible,.book :focus-visible{outline-color:var(--sky-bright)}
.test :focus-visible{outline-color:var(--ink)}

section[id],:target{scroll-margin-top:84px}
.skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--ink);color:#fff;padding:var(--s3) var(--s4)}
.skip:focus{left:var(--s4);top:var(--s4)}

/* ---------- layout primitives ---------- */
.shell{width:min(100% - 2.5rem,var(--shell));margin-inline:auto}
/* ONE RHYTHM (operator ruling 2026-09-05: "the spacing hits the eye wrong").
   Every section carries the same edge, --sec-pad, 115px at 1440. Where two
   sections share a ground (steps into the CTA) each side carries HALF, so
   the air between two blocks of content is the same 115 whether or not the
   colour changes. No section is pinned to a screen height except the hero.
   Measured before the change: sky pinned to 72svh (a dead band on any tall
   window), iceberg top 64, steps bottom 48, CTA 48/101. */
.section{--sec-pad:clamp(var(--s8),8vw,var(--s10));--sec-pad-half:clamp(var(--s6),4vw,var(--s8));
  padding-block:var(--sec-pad)}
.dark{background:var(--ink);color:var(--text-dark)}
.dark h2,.dark h3,.dark .h{color:#fff}
.tint{background:var(--paper-2)}

/* ---------- type roles ---------- */
.eyebrow{
  font-size:.78rem;font-weight:600;letter-spacing:.22em;text-transform:uppercase;
  color:var(--slate);display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s4);
}
.dark .eyebrow{color:var(--sky-bright)}
.eyebrow::after{content:"";flex:1;height:1px;background:var(--line);max-width:120px}
.dark .eyebrow::after{background:var(--line-dark)}

.h,h1,h2{font-family:var(--display);font-weight:400;text-transform:uppercase;
  line-height:.94;letter-spacing:.005em;color:var(--ink);text-wrap:balance}
h1{font-size:var(--t-4xl)}
h2{font-size:var(--t-3xl)}
h3{font-family:var(--body);font-weight:700;font-size:var(--t-xl);line-height:1.25;color:var(--ink)}
.dark h3{color:#fff}
.lede{font-size:var(--t-xl);line-height:1.5;color:var(--slate);max-width:52ch;margin-top:var(--s6)}  /* headline-to-body: --s6 everywhere but the hero */
/* THE STRAPLINE: one definition, three placements. Display face, one line,
   the second clause carrying the sky accent. Size is set per placement because
   each is constrained by something different. */
.strap{font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:.008em;line-height:1.06;white-space:nowrap;
  width:max-content;max-width:100%}
.strap em{font-style:normal;color:var(--sky)}

/* THE HERO'S SECOND-LEVEL LINE, in the display face like the slam above it.
   TWO LINES since 2026-09-08, when the operator swapped the header and the
   strapline: the pair is a parallel construction and reads as two lines, the
   second carrying the sky accent. Each line measured off the loaded Anton
   with a hidden 100px probe:
     "We handle the work in your business"   1460px  ->  14.60
     "You handle the work on your business"  1513px  ->  15.13  <- the constraint
   15.9 leaves the same margin the old 28.6 left over 27.17. The 36px cap came
   down to 28px because the H1 above it halved: at 36 against a 68px headline
   the hierarchy read as two headlines rather than one and its second level.
   Below 768 it wraps, unchanged. */
.subhead{margin-top:var(--s5);font-family:var(--display);font-weight:400;
  text-transform:uppercase;letter-spacing:.008em;line-height:1.06;color:#fff;
  width:max-content;max-width:100%;
  /* 83vw, not 100vw, and capped at 28px rather than 43: the hero puppet's
     painted edge starts 23.5% into his own host, and the room left for this
     line was measured at 619px @768 up to 1200 @1920, dipping to 1078 @1440
     where the shell starts centring. 43px ran the last word into his head. */
  white-space:nowrap;font-size:min(calc((83vw - 22px) / 15.9), 28px)}
.subhead span{display:block}
.subhead em{font-style:normal;color:var(--sky)}          /* 7.28:1 on ink */
@media (max-width:767px){
  /* 24ch was cut for the old single 65-character line. Each of the two new
     lines is 35 characters, and 24ch forced every one of them onto two rows,
     making a four-row subhead under a wrapped headline. */
  .subhead{white-space:normal;font-size:clamp(1.05rem,3.3vw,1.6rem);
    line-height:1.1;max-width:40ch}
}
.dark .lede{color:var(--body-dark)}
/* "sticky notes" in our blue (operator ruling 2026-09-08). This lede sits
   on the ink ground, where --sky is 7.28:1, so the sky-never-on-light law
   is respected: the rule is scoped to .dark and cannot reach a light one. */
.dark .lede em{font-style:normal;color:var(--sky)}
p{max-width:var(--measure)}
.mono{font-variant-numeric:tabular-nums;letter-spacing:.02em}


/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:var(--s3);
  font-family:var(--body);font-weight:700;text-transform:uppercase;letter-spacing:.07em;
  font-size:.94rem;line-height:1;padding:var(--s4) var(--s5);
  background:var(--ink);color:#fff;border-radius:var(--radius);
  text-decoration:none;transition:transform .18s var(--ease),background .18s var(--ease);
}
.btn:hover{background:#10161A;transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn--ghost{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 2px var(--ink)}
.btn--ghost:hover{background:var(--ink);color:#fff}
.dark .btn{background:var(--sky);color:var(--ink)}      /* 7.28:1 — passes */
.dark .btn:hover{background:var(--sky-bright)}
.dark .btn--ghost{background:transparent;color:#fff;box-shadow:inset 0 0 0 2px rgba(255,255,255,.45)}
.dark .btn--ghost:hover{background:#fff;color:var(--ink)}

/* ---------- reduced motion: the mandatory floor ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
  .aperture--spin circle{animation:none}
  .reveal{opacity:1!important;transform:none!important}
}

/* ============================================================
   STOP DOWN — component layer
   Drenched ink. Sky detonates exactly five times. Left-aligned throughout.
   ============================================================ */
.vis-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- THE BLADE ---------- */
.bl{fill:currentColor}
.ap{width:3.5rem;height:3.5rem;color:var(--sky);flex:none}
.ap--md{width:3.4rem;height:3.4rem}
.ap--lg{width:5.25rem;height:5.25rem}
.ap--sky{color:var(--sky)}
.blade-bg{
  position:absolute;color:var(--slate);opacity:.13;pointer-events:none;
  width:46vw;height:46vw;z-index:0;
}
.blade-bg--hero{right:-13vw;top:8vh}
.blade-bg--test{color:#fff;opacity:.2;left:-14vw;bottom:-12vw;width:52vw;height:52vw}
.blade-bg--book{left:-15vw;bottom:-10vw}
.blade-bg--call{right:-12vw;top:-8vw;width:40vw;height:40vw;opacity:.14}
/* three lines of copy do not need a full section's padding */
/* asymmetric on purpose: the TOP edge meets the four steps, so it carries
   the trimmed padding, while the bottom still separates it from the chips. */
.call{position:relative;overflow:hidden;
  padding-top:var(--sec-pad-half);padding-bottom:var(--sec-pad)}
/* the button sits BESIDE the question, wrapping under it only when the
   headline needs the whole line */
.call__in{display:flex;flex-wrap:wrap;align-items:center;
  gap:var(--s6) clamp(var(--s6),5vw,var(--s8))}
.btn--lg{padding:1.05rem var(--s7);font-size:1rem}

/* 13 · THE BUTTON WIGGLES. Still for most of the cycle, then a short shake,
   so it draws the eye without becoming a fidget. It stops entirely on hover
   and focus, because a moving target is a harder target to click, and it is
   switched off wholesale by the reduced-motion block further down: that rule
   sets animation-duration to .001ms on everything, which is not a special
   case anyone has to remember here. */
@keyframes btn-wiggle{
  0%,86%,100%{transform:none}
  89%{transform:rotate(-2.2deg) scale(1.025)}
  92%{transform:rotate(2.2deg)  scale(1.025)}
  95%{transform:rotate(-1.2deg) scale(1.012)}
}
.btn--wiggle{animation:btn-wiggle 4.5s ease-in-out infinite;transform-origin:50% 60%}
.btn--wiggle:hover,.btn--wiggle:focus-visible{animation:none}

/* 12 · he rides in beside the button and points back at it */
.bam--cta{width:clamp(96px,11vw,148px);margin:0;flex:none}
.call .shell{position:relative;z-index:1}

/* ---------- NAV ---------- */
.nav{position:sticky;top:0;z-index:50;background:var(--slate-2);
  border-bottom:1px solid var(--line-dark)}
.nav__in{width:min(100% - 2.5rem,var(--shell));margin-inline:auto;
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4);min-height:64px}
.lockup{display:flex;align-items:center;gap:.6rem;text-decoration:none;color:#fff}
.lockup span{font-size:.72rem;font-weight:700;line-height:1.06;letter-spacing:.13em;text-transform:uppercase}
.nav__right{display:flex;align-items:center;gap:var(--s4)}
.ring{width:26px;height:26px;color:var(--sky);flex:none}
.ring .bl{opacity:.42;transition:opacity .2s var(--ease)}
.ring .bl.on{opacity:1}
@media (prefers-reduced-motion:reduce){.ring .bl{transition:none}}
.btn--sm{padding:.6rem var(--s4);font-size:.8rem}
.btn--sky{background:var(--sky);color:var(--ink)}          /* 7.28:1 */
.btn--sky:hover{background:var(--sky-bright);transform:translateY(-2px)}
.btn--sky:active{transform:scale(.985)}
@media (max-width:560px){.lockup span{display:none}.ring{display:none}}

/* ---------- HERO ---------- */
.hero{position:relative;overflow:hidden;display:flex;align-items:center;
  /* --consent-h is set by consent.js while the notice is up and removed the
     moment a choice is made. Without it the hero's CTA row lands under a
     fixed bar on a phone and the second call to action is invisible on
     first load. Falls back to 0px, so a page with no notice is unchanged. */
  min-height:calc(100svh - 64px - var(--consent-h, 0px));padding-block:clamp(var(--s8),8vw,var(--s10))}
.hero__in{position:relative;z-index:1}
h1.slam,h2.slam{display:flex;flex-direction:column}
/* ONE LINE at every width, per the operator's ruling.
   h2.slam is (0,1,1) and declared later, so this needs (0,2,1) to win.
   The size is derived, not guessed: measured against the loaded Anton, all
   36 characters occupy font-size x 12.77 px, so the largest size that fits
   the shell is (shell width / 12.77). 13.1 leaves a margin, and 92px caps it
   where the shell itself stops growing. */
h2.slam.slam--line{display:block;line-height:1.06;white-space:nowrap;
  font-size:min(calc((100vw - 46px) / 13.1), 92px)}
h1.slam span,h2.slam span{display:block}
h1.slam{font-size:clamp(3.05rem,12.2vw,8.6rem);line-height:.88;letter-spacing:-.006em;color:#fff}
/* TWO LINES, per the operator's ruling. Both lines run past the direction's
   18-character guide, so the size is derived FROM the longer line rather than
   the line being cut to fit the size.
   Never estimated from a character count: measured off the loaded Anton with a
   hidden 100px probe carrying the real computed font stack.
     "Systems and process understanding first"  1675px  ->  16.75  <- the constraint
     "Automating software and AI second"        1431px  ->  14.31
   Was 20.25 until the operator cut "thinking" on 2026-09-08, which bought
   back real size: the largest that still fits the 1200px shell on one line
   went 59px -> 71.6px, so the cap is 70 rather than 58.
   RE-MEASURE ON ANY CHANGE TO EITHER LINE. This has moved five times:
   10.66 -> 9.52 when "so" was dropped, 9.80 when "chores" became "business",
   20.07 on 2026-09-08 when the operator swapped the header and the strapline
   round, then 16.75 the same day when he cut "thinking". The operator ruled
   "slightly smaller if necessary": at 20.07 that meant a 58px cap against the
   old 120px, and cutting one word bought it back up to 70px.
   The size is a function of the string, not of the design.

   AND THE FORM CHANGED WITH IT. The old rule divided 100vw, which works only
   while the shell grows with the viewport. It does not: .shell caps at
   --shell, so at 1920 a 100vw-derived 91px put the first clause onto three
   lines inside a 1200px box. Measured, not assumed. This is a clamp instead:
   58px is the largest size at which the 20.07 line still fits the capped
   shell (58 x 20.07 = 1164 against 1200), and below that it scales with the
   viewport and wraps by itself. No crossover breakpoint, so no step where a
   narrower screen gets a BIGGER headline, which is what a hard wrap rule at
   560 produced (25px on one line at 561, 42px wrapped at 560).
   (0,2,1) so it beats h1.slam at (0,1,1). */
/* The 2rem floor put the first clause onto THREE rows at 390 and 320,
   which with the demoted line is four and over the BRIEF's cap of three.
   1.65rem is the largest floor at which it holds two rows at 320. */
h1.slam.slam--two{font-size:clamp(1.65rem, 5.3vw, 70px);line-height:1.02}
h2.slam{font-size:clamp(2.35rem,7.4vw,5.2rem);line-height:.92;color:#fff}
.slam em{font-style:normal;color:var(--sky)}               /* SKY EVENT */
.row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s5);margin-top:var(--s6)}
/* the hero carries three things and no lede now, so it can afford the air.
   Line-height 1.02 and --s7 by operator ruling 2026-09-05: more room between
   the two headline lines and down to the strapline. */
.hero .subhead{margin-top:var(--s7)}
/* Hard left like everything else in the hero. It was centred for one
   round on 2026-09-05 and the operator reversed it the same day: it looked
   better the first time. */
.hero .row{margin-top:var(--s8)}
.txtlink{color:var(--sky-bright);font-weight:600;font-size:var(--t-md)} /* 8.69:1 */
.micro{margin-top:var(--s4);font-size:.82rem;letter-spacing:.12em;text-transform:uppercase;
  font-weight:600;color:var(--slate-soft)}

/* ---------- 2 · THE TEST · the one sky viewport ---------- */
.test{position:relative;overflow:hidden;background:var(--sky);
  display:flex;align-items:center;padding-block:clamp(var(--s8),8vw,var(--s10))}
.test .shell{position:relative;z-index:1}
/* text left, plot right */
.test__in{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(var(--s6),4vw,var(--s8));align-items:center}
@media (max-width:900px){.test__in{grid-template-columns:1fr}}

/* THE PLOT. Seats are derived from the viewBox, never nudged, the same way
   BAM is seated on the iceberg. He no longer rides the peak: by operator
   ruling 2026-09-05 he stands on the chart's BASELINE beside the inflection
   and gestures at it (pointL, arm up and to the left).
     inflection      (325, 254) of a 520 x 360 viewBox, dashed line down to 330
     baseline        y = 320, so his feet sit at plotw * 320/520 = plotw * 0.61538
     his centre      x = 372 = the dashed line + 47, which keeps his painted
                     body (23.5% in from each edge of his box) clear of the
                     dashed line AND under the rising curve, which passes
                     y = 245 over his left shoulder and y = 222 over his right
     BAM's feet      bamw * 480/400 * 437/480 = bamw * 1.09248 from his top
   so his top is the difference. His width is a FRACTION OF THE PLOT rather
   than a viewport clamp, so he is the same size in viewBox units at every
   width and the head clearance from the curve never changes with the screen.
   engine-parity.py asserts 372/520 and 320/360; keep the pair in step. */
.plot{--plotw:clamp(280px,40vw,540px);--bamw:calc(var(--plotw) * 0.125);width:var(--plotw);flex:none}
/* .plot__fig is the positioning context, NOT .plot: the title now sits above
   the drawing, so a seat measured from the plot box would be wrong by the
   title's height. Everything below is measured from the SVG's own box. */
.plot__fig{position:relative;width:100%}
.plot__svg{display:block;width:100%;height:auto;aspect-ratio:520 / 360}

/* THE CHART'S TITLE. Black, not white: --ink on --sky is 7.29:1 and passes,
   where the white version was 2.13:1 and only shipped under an operator
   waiver. That waiver is now deleted from the gate. Sized off the plot's own
   width so it always holds one line: 12.61px per px of font-size, measured. */
.plot__title{color:var(--ink);margin:var(--s6) 0 var(--s4);
  font-size:min(calc(var(--plotw) / 12.99), 36px)}

/* names the inflection, centred on the dashed line, just under the baseline
   (y=320 of 360, so 0.6154 of the plot's width in px) */
.plot__mark{position:absolute;margin:0;white-space:nowrap;
  left:calc(var(--plotw) * 325 / 520);
  top:calc(var(--plotw) * 0.6154 + 10px);
  transform:translateX(-50%);
  font-size:.78rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink)}

.bam--test{position:absolute;
  left:calc(var(--plotw) * 372 / 520);
  top:calc(var(--plotw) * 0.61538 - var(--bamw) * 1.09248);
  transform:translateX(-50%);width:var(--bamw);margin:0}
.eyebrow--ink{color:var(--ink)}
.eyebrow--ink::after{background:rgba(27,32,37,.28)}
.test__h{color:var(--ink);font-size:clamp(2.2rem,7.2vw,5.4rem)}
.test__h span{color:var(--ink)}
/* --slate on --sky is about 3.05:1 and fails AA for body text. The sky panel
   takes --ink, which is 7.29:1 on it. */
/* the cost list: the display face, because these three words ARE the argument */
.costs{list-style:none;margin-top:var(--s5);display:grid;gap:var(--s2)}
.costs li{font-family:var(--display);font-weight:400;text-transform:uppercase;
  font-size:clamp(1.6rem,4.6vw,3.4rem);line-height:1.04;color:var(--ink);
  display:flex;align-items:baseline;gap:var(--s4)}
/* centred on the CAP height, not the baseline. Anton's caps run about .73em,
   so the cap's middle is .365em up; a .44em square needs its bottom .145em
   above the baseline to sit there. */
.costs li::before{content:"";flex:none;width:.42em;height:.42em;border-radius:2px;
  background:var(--ink);opacity:.55;transform:translateY(-.155em)}

/* ---------- 2b · CRITERIA ---------- */
/* .crit / .crit__i removed with the three-criteria section (merged into the four steps) */
/* 1 · the block sits higher in its band. Its own top padding was the full
   section clamp, which put the first ink 115px down. */
/* .sect--berg no longer has a short top: the 4 Sep raise existed to close
   the gap under a sky section that was pinned to 72svh. The pin is gone. */
/* two dark bands meet here: half an edge each side, so content-to-content
   air across the joint equals the standard edge */
.sect--steps{padding-bottom:var(--sec-pad-half)}
.berg{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(var(--s6),4vw,var(--s8));align-items:center}
/* No grid-template-areas any more. Two items, two columns, auto-placed: the
   aperture is gone and the ice sits BESIDE the text rather than under it.
   Areas and columns are one decision, and with only two children the areas
   were the thing most likely to fall out of step at a breakpoint. */
.berg__head{margin-bottom:0}
/* the same air the hero got, by the same 2026-09-05 ruling: line-height 1.02
   on the headline, one step more between each strapline. (0,2,0) beats
   h2.slam at (0,1,1). */
.berg__head .slam{line-height:1.02}
.berg__strap{margin-top:var(--s6);color:#fff;
  /* ONE LINE EACH, by operator ruling 2026-09-04. It wrapped before because
     forcing one line means the size must follow the COLUMN, and the column
     halves when the ice moves beside it. Sized off the constraining line at
     22.41px of width per px of font-size (hidden probe), against 47vw which
     is the head column's share above 900. Capped at 30px so it still fits at
     1920, where the shell stops growing but the ice does not shrink.
     KNOWN AND ACCEPTED: the size steps down at the 900 breakpoint, because
     that is where the column halves. That is the cost of one line. */
  white-space:nowrap;width:max-content;max-width:100%;
  font-size:min(calc((47vw - 20px) / 22.41), 30px);line-height:1.08}
@media (max-width:900px){
  /* one column below here, so the head has the whole shell */
  .berg__strap{font-size:min(calc((88vw - 22px) / 22.41), 30px)}
}
/* ONE LINE STOPS BELOW 575. Measured on a real iPhone SE profile against the
   deployed site, one line at 320 resolves to 11.6px of Anton uppercase, which
   is not a size anyone reads on a phone. The operator asked for one line and
   gets it everywhere it is legible; below 575 it wraps instead of shrinking.
   Same principle already applied to the hero strapline: a rule obeyed into
   unreadability is not worth obeying. Threshold chosen so the wrapped size
   (21.6px) and the one-line size just above it (20.7px) are near-continuous
   rather than a visible step. */
@media (max-width:575px){
  .berg__strap{white-space:normal;width:auto;max-width:26ch;
    font-size:clamp(1.05rem,3.8vw,1.35rem);line-height:1.12}
}
/* both new straplines wrap on a phone for the same reason the hero's does:
   85 characters on one line at 360 would be 8px. */
/* .berg__strap is NOT here any more: it wraps at every width now, so its own
   clamp covers the phone too. Leaving it in made it jump 25.3px to 20.8px
   crossing 767 to 768, which is a size that shrinks as the screen grows. */


.berg__ice{justify-self:start}
@media (max-width:900px){
  /* the AREAS must be reset with the columns. Leaving a three-column template
     against a 1fr column list makes the browser honour the areas, which
     squeezed the ice to 129px while --icew still said 150 and floated BAM
     11px off it. Columns and areas are one decision, not two. */
  .berg{grid-template-columns:1fr;gap:var(--s6)}
}

/* ---------- 3 · JOBS ---------- */


/* ---------- 4 · PROOF · daylight ---------- */
.proof{background:var(--paper)}
.faq__h{font-family:var(--display);font-weight:400;text-transform:uppercase;
  font-size:clamp(1.9rem,4.4vw,3rem);line-height:.96;letter-spacing:.005em}
.lede--ink{color:var(--slate)}
/* THE CLIENT'S MARK IS THE HEADING. Pale on white, so it keeps the plate the
   caption version had; without it the logo dissolves into the paper. */
/* logo, the line about the client, and BAM: ONE COMPOSED ROW (operator
   ruling 2026-09-05). Three columns, auto 1fr auto: the logo left, the line
   centred in whatever is left, BAM right with an inset from the shell edge,
   so the two gaps either side of the line match instead of one enormous one. */
.proof__head{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;
  gap:var(--s4) clamp(var(--s5),3vw,var(--s7));margin-top:var(--s5)}
@media (max-width:900px){
  .proof__head{grid-template-columns:1fr;justify-items:start}
  .proof__about{text-align:left}
}
.proof__logo{margin:0;line-height:0}
.proof__logo img{width:clamp(210px,26vw,300px);height:auto;display:block;
  border:1px solid var(--line);border-radius:var(--radius);
  padding:.7rem 1rem;background:#fff}
/* the display face the rest of the page uses, small, in --ink at 16.41:1 */
/* ONE OR TWO LINES, by operator ruling 2026-09-05. The string is split at
   the client's own comma into two nowrap halves, so it is one line where the
   row has the room (575px at 1440, measured 24.78px per px of font-size) and
   two lines everywhere else. It can never reach three. No ch width: a
   character count is not a width. */
.proof__about{margin:0;color:var(--ink);text-align:center;justify-self:center;
  font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:.01em;line-height:1.1;
  font-size:clamp(1.05rem,1.7vw,1.45rem)}
.proof__about span{white-space:nowrap}

/* Explicit placement, because the figcaption has to be the LAST child of the
   figure to be valid and to caption the whole quote, while sitting visually
   under the pull-out. Source order is quote, body, caption; grid order is
   quote and caption left, body right. */
.tcard{margin:var(--s6) 0 0;border-top:1px solid var(--line);padding-top:var(--s6);
  display:grid;gap:var(--s5) clamp(2rem,5vw,4.5rem);
  grid-template-columns:minmax(0,5fr) minmax(0,7fr);align-items:start}
.tpull{grid-column:1;grid-row:1}
.twho{grid-column:1;grid-row:2}
.tbody{grid-column:2;grid-row:1 / span 2}
@media (max-width:1000px){
  /* columns and placement are one decision */
  .tcard{grid-template-columns:1fr}
  .tpull,.twho,.tbody{grid-column:1;grid-row:auto}
}
.tpull{font-weight:600;font-size:clamp(1.7rem,4vw,2.75rem);line-height:1.22;
  letter-spacing:-.015em;color:var(--ink);
  border-bottom:4px solid var(--sky);padding-bottom:var(--s5)}   /* SKY EVENT (rule, not text) */
.tbody{display:grid;gap:var(--s4);max-width:64ch}
.tbody p{color:var(--ink-2);font-size:1.05rem}
/* no rule above it any more: it reads as part of the quote, not as a separate
   credential block bolted to the bottom of the card */
.twho{margin-top:var(--s3);display:grid;gap:.1rem}
.tname{display:block;font-weight:700;color:var(--ink)}
.trole{display:block;font-size:.86rem;color:var(--slate)}

/* ---------- 5 · STEPS · the aperture stopping down ---------- */
.steps{display:grid;gap:var(--s6);margin-top:var(--s7);grid-template-columns:repeat(4,1fr)}
@media (max-width:1040px){.steps{grid-template-columns:repeat(2,1fr)}}
@media (max-width:860px){.steps{grid-template-columns:1fr}}
.step__h{font-family:var(--display);text-transform:uppercase;font-weight:400;
  font-size:clamp(1.85rem,4.2vw,2.9rem);line-height:1;color:#fff;margin:var(--s4) 0 var(--s3)}
.step p{color:var(--body-dark);font-size:1rem;max-width:36ch}

/* ---------- 6 · CHIPS ---------- */
.chips{display:grid;gap:var(--s3);margin-top:var(--s7);
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));align-items:start}
.chip{background:var(--slate-2);border:1px solid var(--line-dark);border-radius:var(--radius)}
.chip summary{list-style:none;cursor:pointer;padding:var(--s4) var(--s5);font-weight:700;
  font-size:1.02rem;line-height:1.3;color:#fff;
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4)}
.chip summary::-webkit-details-marker{display:none}
.chip summary::after{content:"";width:11px;height:11px;flex:none;
  border-right:2px solid var(--sky-bright);border-bottom:2px solid var(--sky-bright);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .2s var(--ease)}
.chip[open] summary::after{transform:rotate(-135deg) translate(-3px,-3px)}
.chip{border-left:3px solid transparent}
.chip[open]{border-left-color:var(--sky)}
.chip summary:hover{box-shadow:inset 0 0 0 1px var(--slate)}
.chip summary:active{background:rgba(255,255,255,.04)}

/* THE CHIPS MOVED TO A LIGHT GROUND when the worries and the questions became
   one section. Every colour that was tuned for --slate-2 is retuned here.
   The chevron is the one that matters: --sky-bright is 1.85:1 on paper and the
   sky law forbids it as an icon stroke on light, so it becomes --sky-link. */
.faq .chip{background:var(--paper);border-color:var(--line)}
.faq .chip summary{color:var(--ink)}
.faq .chip summary::after{border-right-color:var(--sky-link);border-bottom-color:var(--sky-link)}
.faq .chip summary:hover{box-shadow:inset 0 0 0 1px var(--slate)}
.faq .chip summary:active{background:rgba(27,32,37,.04)}
.faq .chip p{color:var(--ink-2)}
.chip p{padding:0 var(--s5) var(--s5);color:var(--body-dark);font-size:1rem;max-width:60ch}
@media (prefers-reduced-motion:reduce){.chip summary::after{transition:none}}

/* ---------- 7 · BOOK ---------- */
.book{position:relative;overflow:hidden}
/* THE MAP SECTION (operator ruling 2026-09-05, the fourth on this section
   that day): the panel with BAM and the button LEFT it and became its own
   closing section, #book, so the maps get the whole shell. */
/* map and book share a ground: half an edge each side of the joint, the
   same rule as steps into the CTA */
.map{position:relative;overflow:hidden;padding-bottom:var(--sec-pad-half)}
.book{padding-top:var(--sec-pad-half)}
/* (0,2,1) so the ceiling beats h2.slam at (0,1,1); at (0,1,0) it never
   applied and the line clipped at 320. An override that cannot reach what
   it overrides is not an override. */
h2.slam.map__h{display:block;white-space:nowrap;
  font-size:min(clamp(2.35rem,7.4vw,5.2rem), calc((100vw - 46px) / 8.3))}
.map__maps{min-width:0;margin-top:var(--s7)}   /* the grid gap this block used to inherit */
/* ---------- 9 · BOOK: THE CLOSING SCENE (operator ruling 2026-09-05) ----------
   A beam walks the four steps into the button; BAM rides the roofline above
   it carrying the map. track.js measures the boxes and the button and draws
   the connectors and the beam in one overlay SVG, so the path survives any
   width. Everything here is in the site's tokens: the beam and the lit boxes
   are sky (7.28:1 on ink), the head is white with a sky-bright glow, the
   resting connectors are --line-dark. The markup default is the FINISHED
   frame; the script un-finishes it and animates. */
.book__in{position:relative;z-index:1}
h2.slam.book__h{display:block;white-space:nowrap;   /* (0,2,1), see .map__h */
  font-size:min(clamp(2.35rem,7.4vw,5.2rem), calc((100vw - 46px) / 9.1))}
.track{--ridew:clamp(72px,7vw,104px);position:relative;margin-top:var(--s7);
  padding-top:calc(var(--ridew) * 1.3)}   /* headroom for the rider */
.track__svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none;z-index:1}
.track__row{position:relative;z-index:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr)) auto;
  gap:var(--s7);align-items:center}
.tbox{position:relative;min-height:104px;display:flex;align-items:center;justify-content:center;
  background:var(--slate-2);border:2px solid var(--line-dark);border-radius:var(--radius);
  color:#fff;font-family:var(--display);text-transform:uppercase;letter-spacing:.02em;
  font-size:clamp(1.25rem,2vw,1.9rem);
  transition:background .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease)}
.tbox.is-lit{background:var(--sky);border-color:var(--sky);color:var(--ink)}
/* the tick, drawn, so it is not a glyph the copy gates would count */
.tbox::after{content:"";position:absolute;right:12px;top:10px;width:9px;height:17px;
  border:solid var(--ink);border-width:0 3px 3px 0;transform:rotate(45deg);opacity:0;transition:opacity .2s}
.tbox.is-lit::after{opacity:1}
.track__end{display:grid;justify-items:center;position:relative;z-index:2}
.track .formback{padding:0}
.formback.is-hot .btn{box-shadow:0 0 0 4px rgba(134,197,228,.35),0 0 34px rgba(104,182,221,.75)}
.track.is-live .formback.is-hot .btn{animation:txtflash .5s steps(2,end) 1}
@keyframes txtflash{0%{color:var(--ink)}50%{color:#fff}100%{color:var(--ink)}}
.track__edges .edge{fill:none;stroke:var(--line-dark);stroke-width:2;stroke-dasharray:4 6}
.lhl{fill:none;stroke:var(--sky);stroke-width:2.5;stroke-dasharray:7 6;opacity:0;
  filter:drop-shadow(0 0 6px rgba(104,182,221,.8))}
.lhl.live{opacity:1;animation:beamRun .7s linear infinite}
.lhl.done{opacity:.75;stroke-dasharray:none;animation:none}
@keyframes beamRun{to{stroke-dashoffset:-13}}
.lhead{fill:#fff;filter:drop-shadow(0 0 8px rgba(134,197,228,1))}
.ltrail{fill:var(--sky-bright)}
/* the rider: positioned by track.js off the beam's head and the roofline;
   width is the only thing CSS decides. aspect-ratio comes from .bam. */
.bam--ride{position:absolute;left:0;top:0;width:var(--ridew);margin:0;opacity:0;
  will-change:transform;pointer-events:none;z-index:3}   /* track.js sets opacity once he is placed */
@media (max-width:900px){
  .track__row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .track__end{grid-column:1 / -1}
}
@media (prefers-reduced-motion:reduce){.lhl,.formback.is-hot .btn{animation:none!important}}
/* THE MAPS, drawn in sticky notes (operator rulings 2026-09-05, twice). Two
   jobs side by side, six real HTML notes each in a 2 x 3 portrait grid, so a
   visitor sees the difference between a job that only needs rules and one
   that needs reading and writing. THREE KINDS OF HANDS, by colour:
     paper       = A PERSON       ink on paper 16.41:1
     sky         = THE SOFTWARE   ink on sky 7.29:1
     sky-bright  = THE AI         ink on sky-bright 8.69:1, plus the aperture
                   in the corner so the two blues cannot be confused
   Two blues, one family, so software and AI read as one system; paper
   stands apart as the human. The small tag is --slate on paper (6.87:1) and
   must be --ink on either blue (slate is 3.05:1 on sky, 3.6:1 on sky-bright).
   Sky as a FILL on a dark ground is inside the sky law. Nothing animates. */
.notes__key{margin:0;display:flex;flex-wrap:wrap;gap:var(--s3) var(--s5);
  font-size:.78rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--slate-soft)}
.key{display:inline-flex;align-items:center;gap:.5rem}
.key::before{content:"";width:14px;height:14px;border-radius:2px;flex:none}
.key--human::before{background:var(--postit-yellow)}
.key--soft::before{background:var(--postit-pink)}
.key--ai::before{background:var(--postit-green)}
.notes__cap{margin:var(--s3) 0 0;font-size:.86rem;letter-spacing:.06em;color:var(--slate-soft)}
/* the two maps STACK, and each job is ONE ROW OF SIX (operator ruling
   2026-09-05, the second on the shape that day: "six on one line"). With the
   panel gone the row has the whole 1200px shell: notes of 180px at 1440.
   Three per row below 900, two below 560. */
.notes__pair{margin-top:var(--s6);display:grid;grid-template-columns:1fr;gap:var(--s7)}
.notes{margin:0}
.notes__h{margin:0 0 var(--s4);color:#fff;font-weight:700;font-size:1rem;line-height:1.3}
.notes__h span{display:block;font-size:.78rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--sky-bright);margin-top:.2rem}
.notes__row{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(6,minmax(0,1fr));gap:var(--s5)}
@media (max-width:900px){.notes__row{grid-template-columns:repeat(3,minmax(0,1fr))}}
.note{position:relative;aspect-ratio:1;padding:var(--s4);border-radius:2px;
  display:flex;flex-direction:column;justify-content:space-between;gap:var(--s2);
  color:var(--ink);box-shadow:0 12px 26px rgba(0,0,0,.38)}
.note--human{background:var(--postit-yellow)}
.note--soft{background:var(--postit-pink)}
.note--ai{background:var(--postit-green)}
/* EVERY note carries a mark now (operator ruling 2026-09-08): a person, a
   computer, or BAM himself on the AI ones. All three are static files drawn
   in one vocabulary — the computer is built from BAM's own chest screen and
   shell gradient, so it reads as his sibling rather than a stock icon — and
   each is an <img>, which gets its own document, so the rig's gradient ids
   cannot collide with the 22 live puppets. 44px because 28 is below the size
   at which BAM is recognisable, and the three must share one optical size. */
.note__mark{position:absolute;top:8px;right:8px;width:44px;height:auto;opacity:1}

/* a little tilt each way so they read as paper on the wall, not as tiles.
   transform does not affect layout, so the grid and the gate are untouched. */
.note:nth-child(odd){transform:rotate(-1.6deg)}
.note:nth-child(even){transform:rotate(1.4deg)}
.note__who{font-size:.76rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--slate)}
.note--soft .note__who,.note--ai .note__who{color:var(--ink)}
.note__txt{font-weight:600;font-size:clamp(.92rem,1.1vw,1.02rem);line-height:1.25;text-wrap:balance}
/* three across at 320 would be 85px notes; two across keeps them readable */
@media (max-width:560px){.notes__row{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s4)} .note{padding:var(--s4)}}

  color:var(--sky-bright);margin-bottom:var(--s4)}
/* the booking states, set by app.js: the note appears only after a
   fallback or a booking; the email alternative sits under the micro line so
   #formLink's box never moves (the rider's rest frame is asserted on it) */
.calnote{font-size:.86rem;color:var(--slate-soft);max-width:34ch;margin:0}
.calnote a{color:var(--sky-bright)}
.calalt{display:inline-flex;align-items:center;min-height:24px;padding:.2rem 0;font-size:.86rem;color:var(--sky-bright);font-weight:600}   /* 24px target, SC 2.5.8 */
.formback.is-booked .btn{background:var(--sky-bright)}
/* squared up with the puppet above, by operator ruling 2026-09-05: he is on
   the panel's centre line, so the button and its line are too */
.formback{display:grid;justify-items:center;text-align:center;gap:var(--s4);padding:var(--s5) 0 var(--s4)}

/* ---------- 8 · FAQ · the reading room ---------- */
.faq{background:var(--paper-2)}

/* ---------- 9 · FIRM + FOOTER ---------- */
.foot{border-top:1px solid var(--line-dark);padding-block:var(--s7)}
.foot__in{display:grid;gap:var(--s5)}
/* brand block left, the four profile pills right, on one row (operator ruling
   2026-09-05); the text links keep their own quiet row underneath */
.foot__top{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:var(--s5)}
.foot__brand{display:flex;align-items:center;gap:var(--s4)}
.foot__name{display:block;font-weight:700;letter-spacing:.13em;text-transform:uppercase;font-size:.82rem;color:#fff}
.foot__ltd{display:block;font-size:.86rem;color:var(--slate-soft);margin-top:.15rem}
.foot__mail{display:inline-flex;align-items:center;min-height:24px;padding:.2rem 0;
  margin-top:.25rem;font-size:.92rem;color:var(--sky-bright);font-weight:600}
.foot__links{display:flex;flex-wrap:wrap;gap:var(--s1) var(--s3)}
.foot__links a{color:var(--body-dark);font-size:.92rem;text-decoration:none;
  display:inline-flex;align-items:center;min-height:24px;padding:.35rem .5rem;margin-inline:-.5rem}
.foot__links a:hover{color:#fff;text-decoration:underline}
/* the social row: pill buttons, 44px tall for the tap target, sky-bright on
   ink (8.69:1) with the platform mark in currentColor. Text links stay in
   the nav above; these are the profiles. */
.foot__social{display:flex;flex-wrap:wrap;gap:var(--s3)}
.soc{display:inline-flex;align-items:center;gap:.55rem;min-height:44px;padding:0 var(--s4);
  border:1px solid var(--line-dark);border-radius:999px;color:var(--sky-bright);
  font-size:.86rem;font-weight:700;letter-spacing:.06em;text-decoration:none}
.soc:hover{border-color:var(--sky-bright);color:#fff}
.soc__ic{width:18px;height:18px;fill:currentColor;flex:none}
/* a profile that does not exist yet: present, dashed, not a link, so nobody
   lands on a dead page. --slate-soft is 7.05:1 on ink, so it still passes. */
.soc--soon{border-style:dashed;color:var(--slate-soft);cursor:default}
.foot__note{font-size:.82rem;color:var(--slate-soft);max-width:74ch;line-height:1.6}

/* ---------- CONSENT (consent.js) ----------
   One notice for cookies and analytics, by operator ruling 2026-09-08.
   ACCEPT AND REJECT ARE THE SAME BUTTON in size, shape and distance: the only
   difference is the fill, and a reject that is cheaper to skip than to click
   is not a choice. Both the ICO and the EDPB have said as much.
   The bar is chrome, not content, and lives outside <main>, so the audit's
   no-JS word count is untouched by it. */
.consent{position:fixed;left:0;right:0;bottom:0;z-index:60;
  background:var(--slate-2);border-top:1px solid var(--line-dark);
  box-shadow:0 -18px 40px rgba(0,0,0,.35)}
.consent__in{width:min(100% - 2.5rem,var(--shell));margin-inline:auto;
  padding-block:var(--s5);display:flex;align-items:center;gap:var(--s6);flex-wrap:wrap}
.consent__copy{flex:1 1 22rem;min-width:0}
.consent__h{font-family:var(--body);font-weight:700;font-size:1rem;line-height:1.3;
  color:#fff;margin:0 0 .35rem}
.consent__copy p{margin:0;font-size:.92rem;line-height:1.5;color:var(--text-dark);max-width:62ch}
/* 85x18 on an iPhone, measured 2026-09-09. SC 2.5.8 exempts a link inside a
   sentence, so the gate passed it — but this is the only link in a banner
   people meet on a phone, and 18px is a mean tap. Padded to 24 without
   breaking the line it sits in. */
.consent__copy a{color:var(--sky-bright);font-weight:600;
  display:inline-block;padding-block:.2rem;min-height:24px}
.consent__row{display:flex;gap:var(--s4);flex:0 0 auto}
/* identical box on both, so neither is the easy one */
.consent__btn{min-width:9.5rem;justify-content:center}
@media (max-width:560px){
  .consent__in{gap:var(--s5)}
  .consent__row{width:100%}
  .consent__btn{flex:1 1 0;min-width:0}
}
/* the re-opener, injected by consent.js so it never shows without script */
.foot__consent{font:inherit;font-size:.92rem;background:none;border:0;cursor:pointer;
  display:inline-flex;align-items:center;min-height:24px;
  padding:.35rem .5rem;margin-inline:-.5rem;color:var(--body-dark);
  text-decoration:underline;text-underline-offset:3px}
.foot__consent:hover{color:#fff}
.foot__note a{color:var(--slate-soft)}

/* ---------- entrance: shutter, not slide ----------
   PROGRESSIVE ENHANCEMENT. Content is visible by default. The hidden state
   applies ONLY under html.js, which script sets synchronously. If script never
   runs, errors, or the observer misfires, every word is still on the page. */
.js .reveal{opacity:0;clip-path:inset(0 100% 0 0);
  transition:opacity .18s linear,clip-path .26s cubic-bezier(.2,0,0,1)}
.js .reveal.in{opacity:1;clip-path:inset(0 0 0 0)}
.js .slam.reveal{clip-path:none;transform:scale(1.03);transition:opacity .12s linear,transform .12s cubic-bezier(.2,0,0,1)}
.js .slam.reveal.in{transform:scale(1)}
@media (prefers-reduced-motion:reduce){
  .js .reveal,.js .reveal.in{opacity:1;clip-path:none;transform:none;transition:none}}

/* ---------- narrow ---------- */
@media (max-width:560px){
  /* BRIEF.md caps the H1 at three lines. The longer tagline overflowed
     "chores so you can" at 13.5vw, so the narrow size comes down until
     every span holds one line. Measured, not guessed. */
  h1.slam:not(.slam--two){font-size:clamp(2.1rem,11.9vw,3.6rem)}
  /* the demoted clause is 33 characters; 1.25rem ran it onto two lines at
     390, which put the H1 over the BRIEF's three-line cap. Measured down. */
  .slam--two .slam__demote{font-size:1.05rem}
  .slam__demote{font-family:var(--body);font-weight:600;font-size:1.4rem;
    text-transform:none;letter-spacing:0;line-height:1.3;margin-top:.55rem}
  .slam__demote em{font-style:normal;color:var(--sky-bright)}
  .blade-bg{opacity:.09}
  .test{min-height:auto;padding-block:var(--s8)}
}

/* ---------- document pages: privacy / terms / cookies / 404 ---------- */
.doc{padding-block:clamp(3rem,7vw,5.5rem)}
.doc .shell{max-width:74ch}
.doc h1{font-size:clamp(2.1rem,5.4vw,3.4rem);margin-bottom:var(--s3)}
.doc .stamp{font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;
  font-weight:600;color:var(--slate);margin-bottom:var(--s6)}
.doc h2{font-family:var(--body);font-weight:700;text-transform:none;font-size:1.25rem;
  letter-spacing:-.01em;margin-top:var(--s6);margin-bottom:var(--s3);color:var(--ink)}
.doc p,.doc li{color:var(--ink-2);font-size:1.04rem;margin-bottom:var(--s3);max-width:70ch}
.doc ul{list-style:disc;padding-left:1.15rem;margin-bottom:var(--s4)}
.doc li{margin-bottom:var(--s2)}
.doc li a{display:inline-flex;align-items:center;min-height:24px}
.doc a{color:var(--sky-link);font-weight:600}
.back{display:inline-flex;align-items:center;gap:.5rem;margin-bottom:var(--s5);min-height:28px;padding:.25rem 0;
  font-size:.86rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--slate);text-decoration:none}
.back:hover{color:var(--ink)}
.nf{min-height:64svh;display:flex;align-items:center}

/* ============================================================
   BAM — the mascot layer
   ------------------------------------------------------------
   Every host is an empty, aria-hidden, untabbable <div>. Nothing
   here reaches the accessibility tree, adds a text node, or takes
   a focus ring, so the measured gates see the page they always saw.
   :empty collapses every host when bam.js is absent or bails.
   Skin per ground: t-ba (bone) on --ink and --slate-2,
                    t-ba-ink (slate) on --paper, --paper-2 and --sky.
   ============================================================ */
.bam{display:block;line-height:0;pointer-events:none;contain:layout paint;
  aspect-ratio:400 / 480}
.bam:empty{display:none}
/* EVERY DIMENSION DEFINITE, DERIVED FROM THE HOST'S OWN WIDTH.
   height:auto on an inline SVG asks the engine to infer an intrinsic ratio
   from the viewBox, and engines disagree about that. Chromium infers it;
   WebKit need not, and can resolve the SVG against the available space
   instead. That is how a puppet measuring 66px in headless Chromium renders
   enormous in Safari. aspect-ratio on the host removes the inference: width
   is definite, height follows 400/480, and the SVG simply fills it. */
.bam svg{display:block;width:100%;height:100%}
.bam > div{height:100%}
.bam .ms-body{height:100%}

/* 1 · hero — he drives in from OFF THE SCREEN EDGE, right to left.
   The rig's rollIn travels left-to-right in its own coordinate space, so the
   host is mirrored to reverse it. BAM is front-facing and near-symmetric, so
   the only thing that flips is the eye catchlights, which is invisible.
   THE HOST IS ANCHORED TO THE SCREEN EDGE, and that is the whole trick.
   BAM is clipped by his own 400-unit viewBox, not by the section, so wherever
   the host's right edge sits is where he appears to pop into existence. At
   right:3rem he was materialising 43px inside the frame, measured, which is
   the teleport. At right:0 the clip boundary IS the screen edge, so he drives
   in from genuinely off-screen. He still rests inset, because his own viewBox
   carries 94 units of empty margin either side of him. */
.bam--mirror{transform:scaleX(-1)}
.bam--hero{position:absolute;z-index:0;width:clamp(132px,22vw,320px);
  right:0;bottom:clamp(0px,2vh,26px)}


/* the architecture block: the visible work is the tip, not the mass */
/* HE STANDS ON THE ICEBERG'S TIP.
   Seating is derived, not nudged. The plateau sits at y=62 of a 300-unit
   viewBox, so (300-62)/300 = 79.33% up from the iceberg's foot. Dividing by
   the viewBox WIDTH rather than its height first gave 74.4% and sat him 12px
   under the ice. BAM's own ground line is at
   437/480 of his viewBox, so his feet sit 8.96% of his height above his box
   bottom; translateY pushes him down by exactly that, which lands his feet on
   the plateau at any size. Same constant composite.py uses for plates. */
/* The ice's own width is a variable so the labels can be positioned off it
   without a second clamp drifting out of step. The box is wider than the ice
   to make room for them; its HEIGHT still comes from the ice, which is the
   only in-flow child, so the percentage seats below stay true. */
.berg__ice{--icew:clamp(150px,19vw,238px);--bamw:clamp(44px,5vw,66px);
  position:relative;width:calc(var(--icew) + 13.5rem);flex:none}
.berg__ice .ice{display:block;width:var(--icew);height:auto;aspect-ratio:320 / 300}

/* the two labels, seated off the same viewBox percentages BAM is */
.ice__lbl{position:absolute;left:calc(var(--icew) + 1.25rem);width:12rem;margin:0;
  font-size:.82rem;font-weight:600;line-height:1.35;letter-spacing:.02em}
.ice__lbl::before{content:"";position:absolute;right:100%;top:.55em;
  width:1rem;height:1px;background:currentColor;opacity:.5}
/* mid-tip, not the plateau line: at the plateau the label ran into BAM's
   toolbox by 2px. The visible tip spans y 62..108, so its middle is y=85,
   i.e. (300-85)/300 = 71.7% up from the foot. */
.ice__lbl--tip{bottom:71.7%;transform:translateY(50%);color:var(--sky-bright)}
.ice__lbl--deep{bottom:33.3%;transform:translateY(50%);color:var(--slate-soft)}

/* Placed HERE, not up with the other narrow rules, because the base .berg__ice
   block sits 200 lines BELOW those and would win on order. An override that
   cannot reach what it overrides is not an override. Keep them adjacent. */
@media (max-width:900px){
  /* the labels stop pointing and start listing */
  .berg__ice{width:100%}
  .ice__lbl{position:static;transform:none;width:auto;max-width:36ch;
    margin-top:var(--s3);padding-left:1.5rem}
  .ice__lbl::before{top:.62em;right:auto;left:0}
  .ice__lbl--tip{margin-top:var(--s4)}
}
/* SEATED OFF THE ICE, NOT OFF ITS BOX. left:50% and bottom:% both measure the
   BOX, and the box grew when the labels were added (and grows again when they
   stack on narrow), which floated him 108px right at 1440 and 345px at 900.
   Both coordinates are now pure functions of --icew and --bamw:
     ice height          = icew * 300/320
     plateau from ice top= icew * 300/320 * 62/300 = icew * 0.19375
     BAM's feet from top = bamw * 480/400 * 437/480 = bamw * 1.09248
   so his top is the difference, and his centre is half the ice. */
.bam--berg{position:absolute;
  /* Not the viewBox's centre (160) and not quite the plateau's (174).
     His tracks span 104..296 of his own 400-unit viewBox, which lands 42.6
     ice-units wide against a 56-unit plateau, so centring left 5px of slack
     each side and the toolbox, which sits 112 units right of the front
     socket, hung over the right edge. 167 puts his LEFT track edge on the
     plateau's left edge at 146 and pulls the toolbox back to about 2px. */
  left:calc(var(--icew) * 167 / 320);
  top:calc(var(--icew) * 0.19375 - var(--bamw) * 1.09248);
  transform:translateX(-50%);
  width:var(--bamw);margin:0}

/* 7 · chips — the small register */
.bam--sm{width:64px;margin-bottom:var(--s3)}
.chip .bam--sm{width:62px;float:right;margin:0 0 var(--s3) var(--s4);shape-outside:margin-box}

/* 4 · proof — a held bow, now up in the header row beside the client's own
   line rather than at the foot of the card. He bows AT the introduction,
   which is the moment that wants a bow. */
.bam--proof{width:clamp(70px,7vw,98px);margin:0 clamp(var(--s5),3vw,var(--s7)) 0 0}

/* 5 · the four steps, performed. In flow BELOW the copy, never over it.
   The four puppets align with EACH OTHER, not with their own paragraphs: the
   step copy runs to different line counts, so a BAM that simply follows its
   own text lands at four different heights. The cards already stretch to the
   grid row, so pushing each puppet to the bottom of its own card puts all
   four on one line. */
.step{display:flex;flex-direction:column}
.bam--step{width:clamp(96px,12vw,150px);margin:var(--s5) 0 0;align-self:flex-start}
/* margin-top:auto, never padding. aspect-ratio and padding resolve differently
   in Chromium and WebKit (204px vs 229px tall for the same 150px puppet), so
   the breathing room lives on the paragraph above instead. */
.step .bam--step{margin-top:auto}
.step p{padding-bottom:var(--s5)}

/* 8 · book — the rider; see .bam--ride in the closing-scene block */

/* 9 · faq — the question badge, floated beside the questions rather than
   left stranded in the gap between the chips and the list */
.bam--faq{float:right;width:clamp(84px,10vw,124px);margin:var(--s4) 0 var(--s4) var(--s6);
  shape-outside:margin-box}

/* 10 · 404 */
.bam--404{width:clamp(120px,20vw,190px);margin:0 0 var(--s5)}

/* PROPS AND EFFECTS CARRY THE MASCOT LANE'S OWN COLOURS, DELIBERATELY.
   A clipboard is a brown clipboard and sparks are orange sparks: these are
   depicted objects, not brand marks, and the page already publishes a
   third-party logo (dws-logo.jpg) in its own colours. The palette law in
   BRIEF.md governs the page's grounds, type and controls, which is where
   contrast and recognition live.
   THE ONE EXCEPTION is the pop badge: a mark in a circle is annotation, not
   an object, so it reads as brand chrome and is brought onto the palette.
   A CSS property beats an SVG presentation attribute, so this needs no fork
   of the vendored stage.js. The gate asserts the override is still landing,
   because a silent revert here would be invisible. */
.bam [fill="#FFC978"]{fill:var(--sky-bright)}
.bam [fill="#10162B"]{fill:var(--ink)}

@media (max-width:560px){
  .bam--hero{width:104px;right:0}
  /* The test headline runs the full width at 360, so the corner BAM was
     landing on the type. He keeps his corner and the section is given the
     height to hold him, rather than him being hidden on the half of the
     traffic that is a phone. */
  .plot{--plotw:min(92vw,340px)}
  .bam--step{width:104px;margin:var(--s4) 0 0}
  .bam--proof{position:static;width:96px;margin:var(--s5) 0 0}
  .berg__ice{--icew:150px;--bamw:42px}
  .bam--berg{width:42px}
  .chip .bam--sm{width:48px}
}

/* Reduced motion is handled in bam.js, which renders exactly one settled
   frame and never starts a rAF. This rule is the belt to that brace. */
@media (prefers-reduced-motion:reduce){
  .bam,.bam *{animation:none!important;transition:none!important}
}
