/* ═══════════════════════════════════════════════════════════════════════════
   TSB Labs design system. SINGLE THEME, dark, on purpose.

   Blueprint blue on near-black, deliberately nothing like Madar's void and
   copper. Different companies, and mixing their identities is the one rule
   that does not bend.

   Written fresh rather than patched: the previous stylesheet had drifted from
   the markup, which is how a page ends up styling classes that no longer exist.
   Every selector here is used. Every token here is referenced.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ground and structure. neutrals carry a blue bias, never flat grey */
  --void:#05070d;
  --ink:#0a0d15;
  --raise:#0f1421;
  --line:#1b2334;
  --line-lit:#27324a;

  /* brand.

     One hue, three jobs, because one value cannot do all three and still be
     readable. Measured against --ink, the lightest ground text ever sits on:
       --blue        #3d6ef7  the identity. Borders, rules, large numerals.
                              4.41:1, which clears the 3:1 floor that large
                              bold text answers to, and is used nowhere else.
       --blue-fill   #3767f3  the ground under white text. White on --blue is
                              4.40:1 and fails; on this it is 4.78:1. Three
                              percent less luminance, invisible side by side.
       --blue-label  #4b78f8  blue as small text on dark. 4.94:1 on --ink,
                              5.12:1 on --void, where --blue managed 4.41.
     Do not collapse these back into one. They were split by measurement. */
  --blue:#3d6ef7;
  --blue-fill:#3767f3;
  --blue-label:#4b78f8;
  --blue-deep:#2a52c9;
  --ice:#8fb4ff;
  --tint:rgba(61,110,247,.09);

  /* the single warm value. used sparingly, and never next to another accent */
  --warm:#e08a52;

  /* Text on the blue fill. Pure white is off the palette everywhere, and the
     obvious swap, --text at #e2e8f6, measures 3.89:1 on --blue-fill, under
     the 4.5 floor for the 12px button labels that sit on it. #f8faff keeps
     the ice tint and measures 4.58:1. */
  --on-blue:#f8faff;

  /* Arrivals. The default `ease` curve is the browser's, not ours. */
  --ease-out:cubic-bezier(.16,1,.3,1);

  /* text. three tiers, and all three now clear 4.5:1 on every ground here.
     --faint was #5e687e, which measured 3.60:1 on --void and 3.47:1 on --ink
     and carried most of the metadata on the site. Lifted to #767f96: 5.03:1
     on --void, 4.85:1 on --ink. Still a clear step below --mute (luminance
     .212 against .308), so the hierarchy reads exactly as before. */
  --text:#e2e8f6;
  --mute:#8d97ac;
  --faint:#767f96;

  /* type */
  --display:'Bricolage Grotesque','Inter',system-ui,sans-serif;
  --sans:'Inter',system-ui,-apple-system,BlinkMacSystemFont,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,Menlo,monospace;

  /* small-label scale. Two steps, not six.

     --lbl  12px  anything you click, and anything that states a fact the page
                  does not repeat elsewhere: buttons, links, chips, status
                  pills, stat captions, table meta.
     --orn  12px  was 10.5px, on the argument that an eyebrow labelling the
                  heading directly below it is ornament, and that raising it
                  would flatten it into the content it introduces.

                  That argument held for most of them and not for all. Checked
                  every sub-12px string on all nine pages against the text of
                  every element 12px and over: about twenty carry information
                  that appears nowhere else at a readable size. "Industries"
                  and "Why this venture" on the homepage head sections that
                  have no heading. "In practice" and "What we do not do" are
                  the only labels those blocks get. The company tagline,
                  "Systems for the AI era", sat at 10px on four pages and is
                  written nowhere else on the site.

                  The classes are shared between the ornamental and the
                  load-bearing uses, so there is no clean split without
                  restructuring the markup. 12px under a 28 to 40px heading is
                  still a 2.5x gap, so the hierarchy survives the raise where
                  the readability did not survive the drop.

     Before this there were sizes at 8, 9, 9.5, 10, 10.5 and 11px. Six steps
     inside a 3px range is not a hierarchy, it is drift. */
  --lbl:12px;
  --orn:12px;

  /* rhythm */
  --gut:32px;
  --max:1180px;
  --sec:clamp(72px,9vw,116px);
}

/* The viewer theme toggle is honoured, but both states stay dark: this
   identity is committed to one visual world, deliberately. */
:root[data-theme="dark"],:root[data-theme="light"]{
  --void:#05070d; --ink:#0a0d15; --raise:#0f1421;
  --line:#1b2334; --line-lit:#27324a;
  --blue:#3d6ef7; --blue-fill:#3767f3; --blue-label:#4b78f8;
  --ice:#8fb4ff; --tint:rgba(61,110,247,.09); --warm:#e08a52;
  --text:#e2e8f6; --mute:#8d97ac; --faint:#767f96;
}

/* ── reset ─────────────────────────────────────────────────────────────── */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{background:var(--void);color:var(--text);font-family:var(--sans);
  font-size:16.5px;line-height:1.62;-webkit-font-smoothing:antialiased;
  overflow-x:hidden}
img,svg,canvas{display:block;max-width:100%}
/* Both halves stated. Setting only the background hands the text colour to
   the browser, and the browser's choice is not on this palette. */
::selection{background:rgba(61,110,247,.30);color:var(--on-blue)}
input,textarea,select{caret-color:var(--blue)}
:focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:2px}

/* The scrollbar is the one surface every visitor sees on every page, and it
   was rendering the OS default: light grey against #05070d on Windows and
   Linux Chrome. Thin, blue-biased neutrals, void track, same as the rest of
   the chrome. */
html{scrollbar-color:var(--line-lit) var(--void);scrollbar-width:thin}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:var(--void)}
::-webkit-scrollbar-thumb{background:var(--line-lit);border:3px solid var(--void);border-radius:6px}
::-webkit-scrollbar-thumb:hover{background:var(--blue-deep)}

/* Reduced motion strips the spatial part and keeps the informative part.
   The old rule here zeroed every transition on the site, which also killed
   the colour and opacity changes that tell a keyboard or hover user their
   input registered. Transform work stops; acknowledgement stays. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    transition-property:color,background-color,border-color,
      text-decoration-color,opacity,outline-color,box-shadow !important;
    transition-duration:.16s !important;
    animation:none !important}
  .btn-primary:active,.btn-ghost:active,.ghost:active,.cta:active,
  .btn:active,.navcta:active{transform:none}
}

.wrap{max-width:var(--max);margin:0 auto;padding:0 var(--gut)}
@media(max-width:640px){:root{--gut:20px}}
/* The skip link is an accessibility control. It was the one thing on the page
   that could not afford to be 11px. */
.skip{position:absolute;left:-9999px;top:0;background:var(--blue-fill);color:var(--on-blue);
  padding:11px 18px;z-index:99;font-family:var(--mono);font-size:var(--lbl);
  letter-spacing:.14em;text-transform:uppercase}
.skip:focus{left:0}

/* ── type ──────────────────────────────────────────────────────────────── */
h1,h2,h3,h4{font-family:var(--display);letter-spacing:-.028em;text-wrap:balance;
  color:var(--text);line-height:1.04}
h1{font-size:clamp(2.3rem,4.7vw,4.2rem);font-weight:800;line-height:.97}
h2{font-size:clamp(1.75rem,3.3vw,2.7rem);font-weight:700;max-width:22ch}
h3{font-size:clamp(1.25rem,1.9vw,1.55rem);font-weight:700}
h4{font-size:1.1rem;font-weight:700;line-height:1.2}
p{color:var(--mute)}
strong,b{color:var(--text);font-weight:600}

.kick{font-family:var(--mono);font-size:var(--orn);letter-spacing:.2em;
  text-transform:uppercase;color:var(--blue-label);font-weight:700;margin-bottom:16px}
/* Bricolage ships no italic file, so an <em> in a headline was being
   synthesised: the browser shears the upright glyphs, and at 67px the shear
   is visible. Emphasis in a headline is colour on this site, not slant. */
h1 em,h2 em{font-style:normal;color:var(--ice)}
/* The "Typically:" lines on the service cards are the only place the page
   says what is inside each service. They were inline-styled to 55% opacity,
   which measured 2.74:1. --faint was lifted to clear 4.5:1 for exactly this
   kind of line; it gets used. */
.typical{margin-top:12px;font-size:13px;color:var(--faint);line-height:1.6}
.lede{font-size:clamp(1.02rem,1.5vw,1.18rem);line-height:1.6;max-width:52ch;color:var(--mute)}
.prose{max-width:64ch}
.prose p + p{margin-top:15px}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}

/* ── rhythm inside running content ───────────────────────────────────────
   case-studies, book and pay are straight prose: a heading, some paragraphs,
   another heading, a list. The system styled each of those elements but never
   the space between them, so consecutive paragraphs fused into one block and
   every h3 sat flush on the paragraph above it. That is the "not organised
   into boxes and text" complaint at its root, and it is a rhythm rule rather
   than a redesign.

   Deliberately keyed off p, ul and ol rather than a bare `div + h3`, because
   the eyebrow labels are divs sitting directly above their heading and must
   stay tight to it. */
p + p{margin-top:15px}
p + h2,ul + h2,ol + h2,
p + h3,ul + h3,ol + h3,
p + h4,ul + h4,ol + h4{margin-top:34px}

/* A heading that follows a finished block needs clearance too, but this is an
   allow list on purpose. A deny list would have to remember every eyebrow, and
   an eyebrow div sits directly above its heading and must stay tight to it, so
   the cost of forgetting one is a broken lockup on every page that uses it. */
:is(.note,.honest,.panel,.ev,.steps,.grid,.stats,.tiles,.cards,.svc,.svc-grid,
    .work-grid,.method-grid,.methods,.method-note,.tags,.cta-row,.contact,
    .founder,.work-group) + :is(h2,h3,h4){margin-top:46px}
h3 + p,h3 + ul,h3 + ol,h4 + p,h4 + ul{margin-top:14px}

/* The reset zeroes padding on everything, which left list markers hanging
   outside the content box with nothing to sit in. */
main ul,main ol{margin-top:14px;padding-left:20px;color:var(--mute)}
main li{line-height:1.6}
main li + li{margin-top:10px}


/* ── links ─────────────────────────────────────────────────────────────
   There was no base rule here, so any link not explicitly styled fell back
   to the browser's own blue and visited purple. That is what a Word document
   looks like, and it was showing on the brand lockup and on inline body links.

   Structural links (nav, brand, cards) inherit their colour from context.
   Only links inside running text get the accent, with a hairline underline
   that firms up on hover, so a link reads as considered rather than default. */
a{color:inherit;text-decoration:none}
a:visited{color:inherit}

.prose a, .body a, .head a, .honest a, .cell p a, p a, li a, .lede a{
  color:var(--ice);
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb, var(--ice) 34%, transparent);
  transition:color .16s var(--ease-out), text-decoration-color .16s var(--ease-out);
}
.prose a:visited, .body a:visited, .honest a:visited, p a:visited, li a:visited{color:var(--ice)}
.prose a:hover, .body a:hover, .head a:hover, .honest a:hover,
.cell p a:hover, p a:hover, li a:hover, .lede a:hover{
  color:var(--text);
  text-decoration-color:var(--blue);
  text-decoration-thickness:2px;
}

/* ── nav ───────────────────────────────────────────────────────────────── */
.nav{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--void) 88%,transparent);
  backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.navin{display:flex;align-items:center;justify-content:space-between;gap:24px;
  height:66px}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none}
/* .mark is the logotype, three letters locked in a 28px square. It is a mark,
   not text, so it keeps its size. */
.mark{width:28px;height:28px;flex:none;background:var(--blue-fill);color:var(--on-blue);
  font-family:var(--mono);font-size:var(--orn);font-weight:700;letter-spacing:.02em;
  display:grid;place-items:center}
.bt{font-family:var(--display);font-weight:800;font-size:1.02rem;
  letter-spacing:-.02em;color:var(--text);line-height:1}
/* 8px was past the point where the tagline was doing any work, and 10px was
   still under the floor. This line is the only place the company positioning
   is written on the whole site, so it is not ornament and it gets 12px. The
   wordmark above it is 16px and bolder, which carries the hierarchy. */
.bs{display:block;font-family:var(--mono);font-size:12px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint);margin-top:2px}
.bt{white-space:nowrap}
/* Below 640 the bar holds three controls and the tagline was wrapping to
   three lines and spilling past the 66px bar. The wordmark carries the
   brand on a phone; the positioning line returns at the first width where
   it fits on one line. */
/* Measured 3 September at 390 and 768: the bar overflowed by 25px and 37px on
   every page. Below 880 the links are already behind the hamburger, so the
   bar holds the lockup, the hamburger and Book a call; the tagline does not
   fit beside them until the links return, so it goes at the same width the
   links go. Under 480 the call to action loses its air, not its words. */
@media(max-width:880px){.bs{display:none}}
@media(max-width:480px){
  .navin{gap:12px}
  .navcta{padding:10px 12px;letter-spacing:.08em}
}
.navlinks{display:flex;gap:26px}
.navlinks a{font-size:13.5px;color:var(--mute);text-decoration:none;transition:color .15s}
.navlinks a:hover,.navlinks a[aria-current="page"]{color:var(--text)}
.navcta{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.13em;
  text-transform:uppercase;font-weight:700;color:var(--text);
  border:1px solid var(--blue);padding:10px 16px;text-decoration:none;
  transition:background .16s,color .16s;white-space:nowrap}
.navcta:hover{background:var(--blue-fill);color:var(--on-blue)}

/* ── mobile navigation ───────────────────────────────────────────────────
   Below 880px the primary links used to be display:none with nothing in
   their place, which left six of nine pages unreachable from a phone unless
   a body link happened to point there. A details disclosure carries them
   now: the browser ships the open and close state machine, so the menu
   works with scripting off, and nav.js only adds Escape and a soft close.

   The control is the word "Menu", not a hamburger. This site has no icon
   set, and a word needs no explanation. */
.mnav{display:none}
/* Chrome hides a closed details' children with a UA rule that an author
   display:flex on .mnav nav overrides, so the closed menu stayed in layout
   61px wide with every link clipped inside it. Open, it spans the viewport
   and is fine; closed, it was a phantom box the audit kept reporting as a
   37px overflow on every page. Closed means gone. */
.mnav:not([open]) nav{display:none}
.mnav summary{list-style:none;cursor:pointer;display:grid;place-items:center;
  min-width:44px;min-height:44px;padding:0 12px;font-family:var(--mono);
  font-size:var(--lbl);letter-spacing:.13em;text-transform:uppercase;
  font-weight:700;color:var(--text);border:1px solid var(--line-lit);
  transition:border-color .16s var(--ease-out)}
.mnav summary::-webkit-details-marker{display:none}
.mnav summary:hover{border-color:var(--blue)}
.mnav .mc{display:none}
.mnav[open] .mo{display:none}
.mnav[open] .mc{display:inline}
/* The panel positions against the sticky header itself, so it hangs from
   the bar edge to edge and never drifts from the control that opened it. */
.mnav nav{position:absolute;left:0;right:0;top:66px;display:flex;
  flex-direction:column;background:var(--ink);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  animation:mnav-in .22s var(--ease-out)}
.mnav nav a{padding:14px var(--gut);font-size:15px;color:var(--text);
  text-decoration:none}
.mnav nav a + a{border-top:1px solid var(--line)}
/* an indicator, not a card stripe: inset, and only on the current page */
.mnav nav a[aria-current="page"]{box-shadow:inset 2px 0 0 var(--blue);
  color:var(--ice)}
@keyframes mnav-in{from{opacity:0;transform:translateY(-6px)}}
@keyframes mnav-out{to{opacity:0;transform:translateY(-6px)}}
@media(max-width:880px){.navlinks{display:none}.mnav{display:block}}

/* ── sections ──────────────────────────────────────────────────────────── */
section{padding:var(--sec) 0;border-top:1px solid var(--line)}
section.raise{background:var(--ink)}
section:first-of-type{border-top:none}
.head{max-width:64ch}
.head .lede{margin-top:18px}

/* ── grid of cards, the workhorse ──────────────────────────────────────── */
.grid{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);
  margin-top:38px}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.gauto{grid-template-columns:repeat(auto-fit,minmax(262px,1fr))}
@media(max-width:860px){.g3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.g2,.g3{grid-template-columns:1fr}}
.cell{background:var(--void);padding:28px 26px 30px;display:flex;
  transition:background .22s var(--ease-out);
  flex-direction:column;gap:9px}
section.raise .cell{background:var(--ink)}
.cell:hover{background:var(--raise)}
/* These two are div eyebrows, not chips. Every one of them is a short label
   sitting directly above the heading it introduces: "Pillar 01" over "Quantum
   optimisation", "Research" over "Deep Tech Research". They take the ornament
   step. The span chips in .tag-row and .tags are a different thing entirely,
   they carry the stack and the status, and those stay at the label step. */
.cell .tag{font-family:var(--mono);font-size:var(--orn);letter-spacing:.16em;
  text-transform:uppercase;color:var(--warm);font-weight:700}
.cell .role{font-family:var(--mono);font-size:var(--orn);letter-spacing:.16em;
  text-transform:uppercase;color:var(--blue-label);font-weight:700}
.cell p{font-size:.94rem;line-height:1.56}
/* was declared twice, .9rem then 10px, so 10px silently won. One value now. */
.cell a{color:var(--ice);text-decoration:none;
  font-family:var(--mono);letter-spacing:.08em;text-transform:uppercase;
  font-size:var(--lbl);font-weight:700;margin-top:auto;padding-top:8px}
.cell a:hover{text-decoration:underline;text-underline-offset:3px}

/* ── two column split ──────────────────────────────────────────────────── */
.split{display:grid;grid-template-columns:.85fr 1.15fr;gap:56px;align-items:start}
@media(max-width:860px){.split{grid-template-columns:1fr;gap:30px}}

/* ── stat strip ────────────────────────────────────────────────────────── */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);margin-top:34px}
.stat{background:var(--void);padding:18px 20px}
section.raise .stat{background:var(--ink)}
/* The caption is what makes the number mean anything. It is content, not trim. */
.stat .k{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);font-weight:700}
.stat .v{font-family:var(--mono);font-size:1.55rem;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--text);margin-top:6px;line-height:1}
.stat .v.hot{color:var(--warm)}
.stat .v small{font-size:.7rem;font-weight:400;color:var(--faint);margin-left:3px}

/* ── the honesty block, a recurring device across the site ───────────────
   A rule above the block, not a stripe beside it. The coloured edge stripe
   on a card is the single most recognisable template tell there is, and
   this device is too good to wear one: the warm stays, moved to the top
   edge in a fade, which is the vocabulary of a technical document. The
   warm mono label inside is where the accent actually lives. */
.honest{border:none;border-top:1px solid color-mix(in srgb,var(--warm) 46%,transparent);
  background:var(--ink);padding:24px 28px 26px;margin-top:34px;max-width:72ch}
section.raise .honest{background:var(--void)}
.honest .k{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.15em;
  text-transform:uppercase;color:var(--warm);font-weight:700}
.honest p{margin-top:11px;font-size:.99rem}
.honest p + p{margin-top:12px}

/* ── 3D scenes ─────────────────────────────────────────────────────────── */
.scene{touch-action:pan-y;cursor:grab;width:100%;height:100%}
.scene.drag{cursor:grabbing}
/* "Drag to rotate" is the only thing telling anyone the hero is interactive.
   That is an instruction, not decoration. */
.hint{position:absolute;left:var(--gut);bottom:24px;z-index:3;font-family:var(--mono);
  font-size:var(--lbl);letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  border:1px solid var(--line);padding:6px 10px;transition:opacity .6s;background:var(--void)}
.hint.gone{opacity:0}

.stage{position:relative;min-height:min(100svh,860px);display:flex;
  flex-direction:column;justify-content:center;overflow:hidden}
.stage canvas{position:absolute;inset:0}
.stage::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(112% 84% at 76% 48%,transparent 24%,var(--void) 86%)}
.stage .wrap{position:relative;z-index:2}
.stage h1 + .lede{margin-top:22px}
/* pointer-events off on the grid, back on for the copy: the canvas sits
   underneath, and this is what lets a drag that starts over the scene cell
   reach it while every link in the copy still clicks. */
/* The copy owns the stage. At 50/50 the canvas, which moves and re-labels
   itself, wins a fight the h1 should never be in: motion beats size. Two
   thirds to the words, and the scene becomes the counterweight. */
.stage-grid{display:grid;grid-template-columns:minmax(0,1.32fr) minmax(280px,.68fr);
  gap:clamp(28px,5vw,64px);align-items:center;pointer-events:none}
.stage-copy{pointer-events:auto;max-width:600px}
.stage-copy .cta-row{margin-top:30px}
.stage-copy .now{margin-top:34px}
.stage-zone{min-height:clamp(380px,58vh,560px)}
@media(max-width:900px){
  .stage{min-height:0;padding:clamp(40px,7vw,72px) 0 0}
  .stage-grid{grid-template-columns:1fr;gap:8px}
  .stage-zone{min-height:340px}
  .stage::after{background:linear-gradient(180deg,transparent 55%,var(--void) 96%)}
}

/* ── the measured strip under the hero ──────────────────────────────────
   Four figures, each one verifiable today, set in the display face. This is
   the page's one loud data moment; everything after it can stay quiet. */
.statband{border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background:var(--ink)}
.sb-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--line);border-left:1px solid var(--line);border-right:1px solid var(--line)}
.sb{background:var(--ink);padding:28px 26px 30px}
/* Mono, and two steps down from where these sat. At 46px in the display
   face at 800, four of them in a row read as four more headlines, and the
   h1 directly above is 67px in the same face: a 1.46:1 ratio, which is a
   fight. In the mono at 34px the figures read as instrumentation, the h1
   ratio opens to 1.98:1, and the strip goes back to being evidence. */
.sb .v{font-family:var(--mono);font-weight:700;letter-spacing:-.01em;
  font-size:clamp(26px,2.4vw,34px);color:var(--text);font-variant-numeric:tabular-nums;line-height:1}
.sb .v em{font-style:normal;color:var(--blue-label);font-size:.6em;font-weight:700}
.sb .k{margin-top:9px;font-family:var(--mono);font-size:var(--lbl);color:var(--faint);
  letter-spacing:.04em;line-height:1.65}
@media(max-width:860px){.sb-grid{grid-template-columns:repeat(2,1fr)}}

/* capability grid, deliberately uneven: two wide cards frame two narrow ones
   so the row has a reading order instead of four equal weights */
.gcap{grid-template-columns:repeat(3,1fr)}
.gcap .wide{grid-column:span 2}
@media(max-width:860px){.gcap{grid-template-columns:repeat(2,1fr)}.gcap .wide{grid-column:span 2}}
@media(max-width:640px){.gcap{grid-template-columns:1fr}.gcap .wide{grid-column:auto}}

/* the close. One band, one message, both actions clickable at the exact
   moment a reader who scrolled everything decides. */
.ctaband{border-top:1px solid var(--line);
  background:linear-gradient(180deg,var(--ink),var(--void) 88%);
  text-align:center;padding:calc(var(--sec)*1.02) var(--gut)}
.ctaband h2{max-width:22ch;margin:0 auto}
.ctaband .lede{margin:18px auto 0;max-width:54ch}
.ctaband .cta-row{justify-content:center;margin-top:32px}
.ctaband .mail{margin-top:26px;font-family:var(--mono);font-size:var(--lbl);
  letter-spacing:.06em;color:var(--faint)}
.ctaband .mail a{color:var(--ice)}

.panel{display:grid;grid-template-columns:1.3fr .95fr;border:1px solid var(--line);
  background:var(--void);margin-top:1px;min-height:430px;position:relative}
section.raise .panel{background:var(--ink)}
@media(max-width:900px){.panel{grid-template-columns:1fr;min-height:0}}
.panel .canvas-hold{position:relative;min-height:400px}
.panel .side{padding:30px;border-left:1px solid var(--line);display:flex;
  flex-direction:column;gap:20px;justify-content:center}
@media(max-width:900px){.panel .side{border-left:none;border-top:1px solid var(--line)}}

.btn{align-self:flex-start;font-family:var(--mono);font-size:var(--lbl);
  letter-spacing:.13em;text-transform:uppercase;font-weight:700;color:var(--text);
  background:transparent;border:1px solid var(--blue);padding:11px 18px;
  cursor:pointer;transition:background .16s,color .16s}
.btn:hover{background:var(--blue-fill);color:var(--on-blue)}

/* The assembling readout under the hero headline. No stripe and no warm:
   it sits under the two CTAs, and as the only warm pixel in the first
   viewport it was pulling the eye off the buttons that are the point of
   the hero. It is a readout, so it dresses like one: quiet. */
.now{display:flex;align-items:center;gap:14px;padding:10px 0;margin-top:34px;
  border-top:1px solid var(--line)}
.now .k{font-family:var(--mono);font-size:var(--orn);letter-spacing:.17em;
  text-transform:uppercase;color:var(--faint);font-weight:700;white-space:nowrap}
/* The formation name cycles and the longest of them ran 9px past the stage
   copy at every width, measured 3 September. The name is the one flexible
   thing in the row: it shrinks and ellipsises, the label and the dots keep
   their size. */
.now{min-width:0}
.now .v{font-family:var(--display);font-weight:700;font-size:1.08rem;
  letter-spacing:-.015em;color:var(--mute);flex:1 1 auto;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dots{display:flex;gap:6px;margin-left:auto;flex:none}
/* 6px stays the visual; the target is the ::after. A 6x6 button is a fifth
   of the minimum a finger needs. */
.dot{width:6px;height:6px;border-radius:50%;background:var(--line-lit);
  cursor:pointer;border:none;padding:0;position:relative;
  transition:transform .18s var(--ease-out),background .18s var(--ease-out)}
.dot::after{content:"";position:absolute;left:50%;top:50%;width:24px;height:24px;
  transform:translate(-50%,-50%)}
.dot.on{background:var(--blue-label)}
/* a size change alone on a 6px object is invisible; the colour moves too */
.dot:hover{transform:scale(1.4);background:var(--ice)}

/* ── numbered sequence, used only where order is real ──────────────────── */
.steps{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:36px}
/* Scoped to my own steps markup. work-with-us uses .step inside .method-grid
   for entirely different content, and the fixed 44px + 178px columns forced a
   minimum width that overflowed the page by 132px. */
.steps > .step{background:var(--void);padding:24px 26px;display:grid;
  grid-template-columns:minmax(44px,auto) 1fr minmax(0,178px);gap:24px;align-items:start}
/* minmax rather than a fixed 44px: book.html uses .steps with text labels like
   "02 A CUSTOMISED PROGRAM" in the first cell, which is 110px of content that
   a fixed column simply clipped. */
/* A .step that is NOT inside .steps is a plain card, and is given the card
   treatment down in the legacy layer alongside .tile and .card.

   There used to be a `.method-grid .step,.methods .step{background:transparent;
   padding:0}` rule here, added to kill a 132px overflow. The overflow was
   really caused by the three fixed columns above, and scoping those to
   `.steps > .step` already fixed it. The transparent override was doing
   nothing but stripping the box off work-with-us, which rendered the four
   method cards as one unbroken slab with the text of each running into the
   next. Removed. The scoping is what holds the overflow, not this. */
section.raise .steps > .step{background:var(--ink)}
@media(max-width:820px){.steps > .step{grid-template-columns:1fr;gap:9px}}
.steps > .step .n{font-family:var(--mono);font-size:1.45rem;font-weight:700;color:var(--blue);line-height:1}
.steps > .step p{font-size:.93rem}
/* The meta column carries the deliverable and the timescale. That is the part
   a client actually scans for, so it stops being 9.5px. */
.steps > .step .meta{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint);line-height:1.75}
.steps > .step .meta b{display:block;color:var(--text);font-weight:700}

/* ── footer ────────────────────────────────────────────────────────────── */
footer{padding:54px 0 70px;border-top:1px solid var(--line)}
/* Scoped to the div, never the element. Four legacy pages use
   <footer class="foot"> whose child is .wrap, and as a flex item .wrap gets
   min-width:auto and refuses to shrink below its 1180px content, which made
   those pages scroll sideways by 396px on a phone. */
footer.foot{display:block}
div.foot{display:flex;justify-content:space-between;gap:22px;flex-wrap:wrap;
  font-family:var(--mono);font-size:var(--lbl);letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint)}
div.foot a{color:var(--faint);text-decoration:none}
div.foot a:hover{color:var(--text)}
div.foot .m{font-family:var(--display);font-weight:800;font-size:1.04rem;
  letter-spacing:-.02em;color:var(--text);text-transform:none}

/* ═══════════════════════════════════════════════════════════════════════════
   Legacy compatibility layer.

   The pages written before this system use their own class vocabulary. Rather
   than rewrite seven pages of markup and risk the invisible-content failure
   this repo has already had once, those names are mapped onto the tokens
   above. Same design, no markup churn, and every page moves at once.

   Delete a block here only when its page has genuinely been rebuilt.
   ═══════════════════════════════════════════════════════════════════════════ */

/* sections and heads */
.band{padding:var(--sec) 0;border-top:1px solid var(--line)}
.band:first-of-type{border-top:none}
/* var(--gut), never 0. These sit on an element that is also .wrap, so a
   padding shorthand with a zero horizontal slot overrides .wrap's gutter and
   every page headline sits flush against the edge of a phone. It does not
   overflow or clip, so nothing caught it until the audit started measuring
   glyph positions against the viewport. */
.hero,.phero{padding:calc(var(--sec) * .9) var(--gut) var(--sec);border-top:none}
.bandhead,.sec-head,.case-head,.ev-head{max-width:64ch;margin-bottom:6px}
.pkick,.kicker,.group-label,.mn-label,.flab{font-family:var(--mono);font-size:var(--orn);
  letter-spacing:.2em;text-transform:uppercase;color:var(--blue-label);font-weight:700;
  margin-bottom:14px;display:block}
/* .flab is not an eyebrow. Nothing sits under it but a list of 14px links, so
   it is the heading for that column and was the quietest thing in the footer. */
.flab{color:var(--faint);margin-bottom:10px;font-size:var(--lbl);letter-spacing:.14em}
/* .mn-label is a span, but it is set block and sits directly above an h3, so
   it is an eyebrow by role and keeps the ornament step. */
.sub,.sec-note,.foot-note{color:var(--mute);max-width:62ch}
.sec-note{font-size:.95rem;margin-top:16px}
.h{font-family:var(--display);font-weight:700;letter-spacing:-.025em}

/* card families all resolve to one surface */
.tiles,.svc-grid,.work-grid,.method-grid,.cards{display:grid;
  grid-template-columns:repeat(auto-fit,minmax(262px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);margin-top:36px}

/* .methods is a row of payment method chips on book.html: USDT, BTC, Bank
   transfer. It was in the card grid family above, which handed seven one word
   spans a 262px column each on a bare --line ground, so it painted as a grey
   slab with words floating in it and no box on anything. It is a chip row, so
   it is now built like one, matching .chip and .tag. */
.methods{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}
.methods .m{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.09em;
  text-transform:uppercase;font-weight:700;color:var(--faint);
  border:1px solid var(--line);padding:6px 11px;display:inline-block}
/* .case is NOT a card. It is only ever used as <section class="case">, one per
   case study, and being in this family gave a full page section a card's 26px
   padding and flex column in place of the section rhythm, so three long case
   studies ran together with no separation and no breathing room. It is a
   section, so it is left to the section rules. */
.tile,.card,.svc,.ev,.rec,.step{background:var(--void);padding:26px 24px 28px;
  display:flex;flex-direction:column;gap:9px;text-decoration:none}
.band.raise .tile,.band.raise .card,.band.raise .step,
section.raise .tile,section.raise .card,section.raise .step{background:var(--ink)}
.tile h3,.card h3,.svc h3,.case h3,.tile h4,.card h4{font-family:var(--display);
  font-weight:700;letter-spacing:-.018em;color:var(--text);line-height:1.2}
/* .case p dropped with it: these are 300 word paragraphs of long form prose,
   and .94rem is a card size, not a reading size. They take body copy. */
.tile p,.card p,.svc p,.ev p{color:var(--mute);font-size:.94rem;line-height:1.56}
.card-top,.tag-row,.ev-row,.row,.case-head,.tags{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.card-index,.step-num,.n{font-family:var(--mono);font-size:1.35rem;font-weight:700;
  color:var(--blue);line-height:1;font-variant-numeric:tabular-nums}
/* .n is a big numeral in a .steps row, where index.html puts "A", "B", "C".
   services.html reuses it inside a tile for a whole category label, and
   "01 · PLATFORMS AND APPS" at 21.6px wrapped onto two lines and shouted over
   the heading it was meant to introduce. Inside a tile it is an eyebrow.
   --blue-label rather than --blue because at 10.5px it has to clear 4.5:1
   rather than the 3:1 it was getting away with at 21.6px. */
.tile .n{font-size:var(--orn);letter-spacing:.16em;line-height:1.5;
  color:var(--blue-label)}
/* These are the outbound links on every card. They were 10px, which is small
   for the one thing on the card you are meant to click.

   Some of them are bare URLs with no spaces to break on, so at the larger size
   they ran past the card edge in the two column layout. overflow-wrap lets a
   URL break mid token, which is the only place it can break. */
.card-link,.golink,.link-line a{font-family:var(--mono);font-size:var(--lbl);font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ice);text-decoration:none;
  margin-top:auto;padding-top:10px;display:inline-block;
  max-width:100%;overflow-wrap:anywhere}
.card-link a,.link-line a{overflow-wrap:anywhere}
.card-link:hover,.golink:hover{text-decoration:underline;text-underline-offset:3px}
.work-group{margin-top:44px}

/* ── the evidence table ──────────────────────────────────────────────────
   .ev is a standalone block, not a cell inside one of the grids above. The
   card family gave it a --void background and relied on a parent grid's 1px
   line for its edge, but it has no parent grid, so a --void box sat on a
   --void section and there was no box at all: the rows read as stray indented
   text. It gets a real edge, and the rows become label against value.

   The value was also collapsing under its own label on a phone, because the
   row was a centred flex line that simply wrapped. Now the label takes the
   remaining space and the value stays on the right and never breaks. */
.ev{background:var(--ink);border:1px solid var(--line);gap:0;margin-top:18px;
  padding:8px 22px 18px;max-width:72ch}

/* A case study is long form reading, and with .case out of the card family its
   paragraphs were running the full 1180px, which is about 150 characters a
   line. Held to the same measure the rest of the system uses. */
.case p,.case ul,.case ol{max-width:68ch}
.tags{margin-top:26px}
.case .golink{margin-top:20px}
.ev-head{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint);font-weight:700;
  padding:12px 0 4px;margin-bottom:0}
.ev-row{justify-content:space-between;gap:18px;align-items:baseline;
  border-top:1px solid var(--line);padding:11px 0 3px}
.ev-row span{color:var(--mute);font-size:.94rem;line-height:1.5;flex:1 1 60%}
.ev-row b{font-family:var(--mono);font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums;white-space:nowrap;flex:0 0 auto}

/* chips, status pills */
/* Chips carry the stack, the status and the confidentiality flag. That is the
   densest real information on the portfolio and case study pages, and it was
   set at 9px. Tracking comes down as the size goes up so the rows still fit. */
.chip,.tag,.tags span,.status,.nda{font-family:var(--mono);font-size:var(--lbl);
  letter-spacing:.09em;text-transform:uppercase;font-weight:700;color:var(--faint);
  border:1px solid var(--line);padding:4px 9px;display:inline-block}
.status.live,.s-live,.live{color:var(--warm);border-color:var(--warm)}
.nda{color:var(--faint)}

/* notes. Same treatment as .honest, same reasoning: the top rule carries
   the signal the side stripe used to, without the template smell. */
.note,.founder,.method-note{border:none;
  border-top:1px solid color-mix(in srgb,var(--warm) 46%,transparent);
  background:var(--ink);padding:24px 26px 26px;margin-top:30px;max-width:72ch}
.note p,.founder p,.method-note p{color:var(--mute);font-size:.97rem}
/* The after delivery block was a bare eyebrow, heading and paragraph with no
   treatment at all, sitting hard against the bottom of the method cards. It is
   a note about what happens next, so it takes the note treatment and enough
   clearance to read as its own block rather than as spill from the grid. */
.method-note{margin-top:38px}
.method-note h3{margin-bottom:10px}
section.raise .note,section.raise .method-note{background:var(--void)}
.founder{display:grid;grid-template-columns:56px 1fr;gap:20px;align-items:start}
@media(max-width:640px){.founder{grid-template-columns:1fr}}
.founder-tile,.monogram{width:56px;height:56px;background:var(--blue-fill);color:var(--on-blue);
  font-family:var(--mono);font-weight:700;font-size:1.05rem;display:grid;place-items:center}
/* initials locked in a 28px square, a mark rather than text */
.monogram{width:28px;height:28px;font-size:var(--orn)}
/* same reasoning: .role is always an eyebrow above a heading */
.role{font-family:var(--mono);font-size:var(--orn);letter-spacing:.16em;
  text-transform:uppercase;color:var(--blue-label);font-weight:700}

/* buttons */
.btn-primary{background:var(--blue-fill);color:var(--on-blue);border:1px solid var(--blue-fill)}
.btn-primary:hover{background:var(--blue-deep);border-color:var(--blue-deep)}
.btn-ghost,.ghost{background:transparent;border:1px solid var(--line-lit);color:var(--text)}
.btn-ghost:hover,.ghost:hover{border-color:var(--blue)}
.btn-primary,.btn-ghost,.ghost,.cta{font-family:var(--mono);font-size:var(--lbl);
  letter-spacing:.13em;text-transform:uppercase;font-weight:700;padding:12px 20px;
  text-decoration:none;display:inline-block;cursor:pointer;
  transition:color .16s var(--ease-out),background-color .16s var(--ease-out),
    border-color .16s var(--ease-out),transform .16s var(--ease-out)}
/* Buttons press. Scale, not colour, is what makes a click feel received. */
.btn-primary:active,.btn-ghost:active,.ghost:active,.cta:active,.btn:active,.navcta:active{
  transform:scale(.97)}
/* A button that cannot be pressed says so. No page ships one today; the
   first page that does should not have to invent the state. */
.btn-primary:disabled,.btn-ghost:disabled,.ghost:disabled,.btn:disabled,
.cta[aria-disabled="true"]{opacity:.45;cursor:not-allowed;transform:none;
  pointer-events:none}
.cta-row,.contact{display:flex;gap:12px;flex-wrap:wrap;margin-top:28px}
.close{margin-top:32px}

/* pricing */
.amt{font-family:var(--mono);font-size:1.5rem;font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums;line-height:1.15}
/* the qualifier ran straight into the figure at almost the figure's size, so
   "$5,500setup, then $550 monthly" read as one string */
.amt small{display:block;font-size:.5em;font-weight:400;color:var(--mute);
  letter-spacing:.02em;margin-top:4px}
.right{text-align:right}
@media(max-width:640px){.right{text-align:left}}

/* ── standalone pricing rows ─────────────────────────────────────────────
   book.html lists twelve tiers as bare .svc divs rather than cards inside a
   .svc-grid, so they inherited the card treatment but never the grid line
   that draws a card's edge: an invisible box with the price adrift somewhere
   to the right of the description. As a direct child of .wrap a .svc is a
   row, so it is built as one. Inside .svc-grid, on services.html, it stays
   the card it already was. */
.wrap > .svc{flex-direction:row;justify-content:space-between;align-items:center;
  gap:26px;border:1px solid var(--line);background:var(--ink);margin-top:12px}
.wrap > .svc > :first-child{flex:1 1 auto;min-width:0}
.wrap > .svc .right{flex:0 0 auto;display:flex;flex-direction:column;
  align-items:flex-end;gap:12px}
/* .rec is the recommended tier and had no treatment separating it from the
   rest, which is the whole point of the class */
.wrap > .svc.rec{border-color:var(--blue)}
@media(max-width:640px){
  .wrap > .svc{flex-direction:column;align-items:stretch;gap:16px}
  .wrap > .svc .right{align-items:flex-start}
}

/* footer */
.footin,.foot-inner{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:32px;padding-bottom:30px}
.footcol,.foot-brand,.foot-links{display:flex;flex-direction:column;gap:8px}
.fmark{font-family:var(--display);font-weight:800;font-size:1.05rem;
  letter-spacing:-.02em;color:var(--text)}
.fsm{color:var(--mute);font-size:.88rem;line-height:1.55}
.footcol a,.foot-links a{color:var(--mute);text-decoration:none;font-size:.9rem}
.footcol a:hover,.foot-links a:hover{color:var(--text)}
/* The right hand span here is a full sentence of confidentiality wording, not
   a label. Uppercase mono at 10px was the wrong place to put a sentence. */
.footbar{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  padding-top:22px;border-top:1px solid var(--line);font-family:var(--mono);
  font-size:var(--lbl);letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}

/* ── one build, in section ───────────────────────────────────────────────
   A sticky viewport inside a tall track; scenes.js scrubs the dissection
   against how far the track has been scrolled. The pin only exists on wide
   screens with motion allowed. Everyone else gets the list, which is the
   actual content; the canvas is its illustration. */
.stackband{border-top:1px solid var(--line);background:var(--ink)}
.stack-track{height:340vh;position:relative}
.stack-pin{position:sticky;top:0;height:100svh;overflow:hidden}
#stack{position:absolute;inset:0;width:100%;height:100%}
.stack-copy{position:absolute;left:0;right:0;top:9vh;z-index:2;pointer-events:none;max-width:none}
.stack-copy h2{max-width:24ch;transition:opacity .3s}
.stack-copy .lede{margin-top:14px;max-width:44ch;transition:opacity .3s}
.stack-legend{position:absolute;right:var(--gut);top:50%;transform:translateY(-50%);
  z-index:2;list-style:none;display:grid;gap:12px;padding:0;margin:0;
  font-family:var(--mono);font-size:var(--lbl);letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint)}
.stack-legend li{opacity:0;transform:translateX(8px);
  transition:opacity .4s var(--ease-out),transform .4s var(--ease-out),color .3s}
.stack-legend li.on{opacity:1;transform:none}
.stack-legend li.warm.on{color:var(--warm)}
/* the fallback list is the content, styled as content */
.stack-fallback{padding:var(--sec) var(--gut)}
.stack-fallback ol{list-style:none;padding:0;max-width:64ch;display:grid;gap:14px}
.stack-fallback b{font-family:var(--mono);font-size:var(--lbl);letter-spacing:.13em;
  text-transform:uppercase;color:var(--blue-label);margin-right:10px}
.stack-fallback li{color:var(--mute);line-height:1.6}
/* wide + motion: the canvas carries it, the list yields */
@media (min-width:720px) and (prefers-reduced-motion: no-preference){
  .stack-fallback{display:none}
}
/* narrow or reduced motion: no pin, no tall track, no canvas */
@media (max-width:719px), (prefers-reduced-motion: reduce){
  .stack-track{height:auto}
  .stack-pin{position:static;height:auto;min-height:0;padding:var(--sec) 0 0}
  #stack{display:none}
  .stack-copy{position:static;padding:0 var(--gut)}
  .stack-legend{display:none}
}

/* ── the blueprint cursor ────────────────────────────────────────────────
   Painted by cursor.js on fine pointers only. The html.bpc-on gate means a
   phone, a missing script, or an error leaves the native cursor untouched.
   Text fields keep their I-beam: editing beats theatre. */
html.bpc-on, html.bpc-on a, html.bpc-on button, html.bpc-on summary{cursor:none}
html.bpc-on input, html.bpc-on textarea, html.bpc-on select{cursor:auto}
#bpc{position:fixed;inset:0;z-index:9999;pointer-events:none}
#bpc > div{position:absolute;top:0;left:0;will-change:transform}
.bpc-dot{width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;
  background:var(--ice);transition:background .15s}
.bpc-ring{width:34px;height:34px;margin:-17px 0 0 -17px;border-radius:50%;
  border:1px solid var(--blue);opacity:.75;
  transition:width .18s var(--ease-out),height .18s var(--ease-out),
    margin .18s var(--ease-out),border-color .18s,opacity .18s}
/* over a link or button the ring opens and firms: the site meets the hand */
#bpc.bpc-hot .bpc-ring{width:52px;height:52px;margin:-26px 0 0 -26px;
  border-color:var(--ice);opacity:1}
#bpc.bpc-hot .bpc-dot{background:var(--warm)}
#bpc.bpc-press .bpc-ring{width:26px;height:26px;margin:-13px 0 0 -13px}
/* over a scene the instrument changes: reticle, hairlines, coordinates */
.bpc-x,.bpc-y,.bpc-co{opacity:0;transition:opacity .2s}
#bpc.bpc-scene .bpc-x{opacity:1;left:0;right:0;height:1px;width:100%;
  background:linear-gradient(90deg,transparent,rgba(61,110,247,.28) 20%,
    rgba(61,110,247,.28) 80%,transparent)}
#bpc.bpc-scene .bpc-y{opacity:1;top:0;bottom:0;width:1px;height:100%;
  background:linear-gradient(180deg,transparent,rgba(61,110,247,.28) 20%,
    rgba(61,110,247,.28) 80%,transparent)}
#bpc.bpc-scene .bpc-co{opacity:1;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.14em;color:var(--faint);white-space:nowrap;
  text-shadow:0 1px 3px rgba(5,7,13,.9)}
#bpc.bpc-scene .bpc-ring{opacity:0}
#bpc.bpc-text .bpc-dot,#bpc.bpc-text .bpc-ring{opacity:0}
html.bpc-on input:hover, html.bpc-on textarea:hover{cursor:text}
/* over prose the native I-beam returns and the instrument stands down */
html.bpc-on p, html.bpc-on li, html.bpc-on h1, html.bpc-on h2,
html.bpc-on h3, html.bpc-on figcaption, html.bpc-on .lede{cursor:text}
html.bpc-on p a, html.bpc-on li a{cursor:none}

/* the old scroll-reveal is neutralised; content must never start hidden */
.reveal{opacity:1;transform:none}

/* Headings never sit flush against the paragraph that follows them. */
h1 + .sub, h1 + .lede, h1 + p,
h2 + .sub, h2 + .lede, h2 + p,
.h + .sub, .h + p{margin-top:20px}
.hero,.phero{padding-bottom:calc(var(--sec) * .55)}

/* The hero already pays a bottom margin, and the first section then paid a
   full one on top of it. On a short hero that stacked into roughly 180px of
   empty screen with no rule and no content in it, which reads as a page that
   failed to load rather than as breathing room. The first section after a hero
   takes a half stop. Every later section keeps the full rhythm. */
.phero + section,.hero + section,.phero + .band,.hero + .band{
  padding-top:calc(var(--sec) * .5)}
