/* ═══ site-ui.css — interaction states, shared by every page ═══
   One stroke weight, three stroke values, one hover fill, one "on" state,
   one focus ring. Every control family below takes these and nothing else.

   rest    1px var(--stroke-ctrl)
   hover   1px var(--stroke-hover), a 6% ink tint laid over the control's own ground, ink text
   on      fill var(--fill-on), text var(--text-on)
   focus   2px var(--ring) outline, 3px offset, never animated
   cards   1px var(--stroke) at rest, var(--stroke-ctrl) on hover
   links   ink on hover */

/* outlines a step stronger site-wide; high contrast keeps its own, stronger values */
:root:not([data-contrast="high"]){--edge:rgba(25,21,23,.14);--edge-ctrl:rgba(25,21,23,.30)}

:root{
  --stroke:var(--edge);            /* passive surfaces: cards, figures */
  --stroke-ctrl:var(--edge-ctrl);  /* controls at rest */
  --stroke-hover:var(--ink);       /* controls under the pointer */
  --fill-hover:rgba(25,21,23,.06);   /* hover darkens a touch; it never whitens */
  --fill-on:var(--ink);
  --text-on:var(--page);
  --ring:var(--ink);
}

/* ═══ strokes, shadows and accents: one value per role ═══
   stroke      1px --stroke       passive surfaces: cards, figures, frames, bubbles
   stroke-ctrl 1px --stroke-ctrl  controls, labels, chips and tags
   accent      2px ink            the decision and pushback rules
   shadow-1                       small floating things: badges, knobs, bubbles
   shadow-2                       raised device frames: the phone, the laptop
   recess                         the inset wash of a diagram frame */
:root{
  --shadow-1:0 1px 2px rgba(25,21,23,.08);
  --shadow-2:0 1px 2px rgba(25,21,23,.08),0 14px 36px -10px rgba(25,21,23,.18);
}
/* passive surfaces */
.rfig,.rfig .tile,.rfig .tr,.rfig .st,.r5-list li{border-color:var(--stroke)}
.dgm,.gap-card,.ann,.carousel,.cmp-stage,.proto-frame{box-shadow:inset 0 0 0 1px var(--stroke)}
/* labels, chips and tags */
.chip,.tag,.cmp-tag,.stat-upd-k{box-shadow:inset 0 0 0 1px var(--stroke-ctrl)}
/* raised frames */
.phone,.mac-frame{box-shadow:inset 0 0 0 1px var(--stroke),var(--shadow-2)}
/* small floating things */
.rfig .st,.rd-bub{box-shadow:var(--shadow-1)}
.zoom{box-shadow:inset 0 0 0 1px var(--stroke-ctrl),var(--shadow-1)}
.cmp-seam i,.lb-x{box-shadow:var(--shadow-1)}
/* table rules are passive too */
tr,th,td{border-color:var(--stroke)}
/* accent rules */
.dec-p,.pushed{border-color:var(--ink)}


/* Mindure's research toggle is a .bp-btn without .bp-toggle: it takes the same
   ruled row and + circle as Tribe's blueprint, at every width */
.bp-btn:not(.bp-toggle){display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
  column-gap:var(--stack);width:100%;margin:0;padding:1.1em 0;text-align:left;cursor:pointer;
  font:inherit;color:inherit;background:none;border:0;border-radius:0;
  border-top:1px solid var(--stroke-ctrl);border-bottom:1px solid var(--stroke-ctrl);-webkit-appearance:none;appearance:none}
.bp-btn:not(.bp-toggle) .bp-t,.bp-btn:not(.bp-toggle) .bp-e{display:block;grid-column:1}
.bp-btn:not(.bp-toggle)::after{content:"";grid-column:2;grid-row:1 / span 2;
  width:var(--control);height:var(--control);border-radius:var(--r-pill);
  box-shadow:inset 0 0 0 1px var(--stroke-ctrl);
  background:linear-gradient(var(--muted),var(--muted)) center / 14px 1px no-repeat,
             linear-gradient(var(--muted),var(--muted)) center / 1px 14px no-repeat}
.bp-btn:not(.bp-toggle)[aria-expanded="true"]::after{
  background:linear-gradient(var(--page),var(--page)) center / 14px var(--ico-w) no-repeat,var(--ink);
  box-shadow:none}

/* ═══ shape and movement ═══
   radius   pill for controls, labels and chips; 20px (--r-lg) for every
            surface; 12px (--r-sm) for a mark nested inside a surface; device
            frames keep their hardware radius
   movement only toward meaning, and only 2px: a link card lifts, an arrow
            steps in its own direction; nothing scales */
:root{--r-sm:12px}
.rfig .tile,.rd-bub{border-radius:var(--r-lg)}
.car-mark{border-radius:var(--r-sm)}
.claim-c.is-u{border-top-width:2px}
@media (hover:hover){
  .cs-back:hover svg{translate:-2px 0}
  .next a:hover .next-go{translate:2px -2px}
  .noise-vol input[type="range"]:hover::-webkit-slider-thumb{scale:1}
}

/* ═══ icons and buttons ═══
   icon     one line weight, 1.5px on screen at any size; 18px inside a
            button, 14px beside a word (the back chevron, the arrows)
   button   one height (--control); pill with a label, circle without; label
            13px sans 500 caps, 12px on a phone
   plus     the + and the - of the toggle circles are drawn in the same 1.5px */
:root{--ico:18px;--ico-sm:14px;--ico-w:1.5px}
:is(.btn,.car-btn,.zoom,.ph-nav,.lb-x,.mlb-x) svg{width:var(--ico);height:var(--ico)}
:is(.cs-back) svg{width:var(--ico-sm);height:var(--ico-sm)}
:is(.btn,.cs-back,.car-btn,.zoom,.ph-nav,.lb-x,.mlb-x) svg :is(path,line,polyline,polygon,circle,rect,ellipse){
  stroke-width:var(--ico-w);vector-effect:non-scaling-stroke}
:is(.cw-ico,.bp-ico)::before,:is(.cw-ico,.bp-ico)::after{border-top-width:var(--ico-w)}
.cw-card .cw-ico,.bp-toggle .bp-ico{width:var(--control);height:var(--control)}
.btn:not(.btn--icon):not(.noise-btn),a.proto,.hero-link,.hero-cue{font-size:13px;letter-spacing:.07em}
@media (max-width:767px){ .btn:not(.btn--icon):not(.noise-btn),a.proto{font-size:12px} }
.noise .noise-btn{font-size:13px;letter-spacing:.07em}

/* text button: a secondary action with no outline and no fill; the label and an
   arrow that says where it goes. Same type as a button label, same height. */
.btn-text{background:none;border:0;cursor:pointer;display:inline-flex;align-items:center;gap:.45em;min-height:var(--control);padding:0 .2em;
  font-family:var(--sans);font-weight:500;font-size:13px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink);text-decoration:none;transition:color var(--t-instant) var(--ease-enter)}
.btn-text span{text-decoration:underline;text-decoration-color:var(--stroke-ctrl);text-underline-offset:.35em;
  transition:text-decoration-color var(--t-instant) var(--ease-enter)}
.btn-text svg{width:var(--ico-sm);height:var(--ico-sm);transition:translate var(--t-instant) var(--ease-enter)}
.btn-text svg *{stroke-width:var(--ico-w);vector-effect:non-scaling-stroke}
@media (hover:hover){
  .btn-text:hover span{text-decoration-color:var(--ink)}
  .btn-text:hover svg{translate:2px -2px}
}
@media (max-width:767px){ .btn-text{font-size:12px} }
/* a download: the arrow is sized off the label, so it stands as tall as the
   capitals, and on hover it moves the way it points - down */
.btn-text--down svg{width:1.15em;height:1.15em}
@media (hover:hover){ .btn-text--down:hover svg{translate:0 2px} }
/* a text button that opens something points down, and up once it is open */
.btn-text[aria-expanded] svg{translate:none !important;transition:rotate var(--t-instant) var(--ease-enter)}
.btn-text[aria-expanded="true"] svg{rotate:180deg}
@media (hover:hover){ .btn-text[aria-expanded="false"]:hover svg{translate:0 2px !important} }

/* tooltips and the menu keep their capitals */
[data-tip]::after,.btn[data-tip]::after,.tab{text-transform:none}

/* ── controls: pill and circle buttons ── */
.btn,.noise,.car-btn,.ph-nav,.say,.zoom,.v10-tg button,.bp-ico,.cw-ico{
  transition:background-color var(--t-instant) var(--ease-enter),border-color var(--t-instant) var(--ease-enter),
             color var(--t-instant) var(--ease-enter)}
.btn,.noise,.car-btn,.ph-nav,a.proto{border-color:var(--stroke-ctrl)}
.say{background:none;box-shadow:inset 0 0 0 1px var(--stroke-ctrl)}
.bp-ico,.cw-ico{border-color:var(--stroke-ctrl)}
@media (hover:hover){
  .btn:not(.btn--on):not(.noise-btn):not([aria-pressed="true"]):hover,
  .noise:not([data-on="true"]):hover,
  .car-btn:hover:not(:disabled),
  .ph-nav:hover,a.proto:hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover));border-color:var(--stroke-hover);color:var(--ink)}
  .say:hover,.zoom:hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover));box-shadow:inset 0 0 0 1px var(--stroke-hover);color:var(--ink)}
  .zoom:hover{box-shadow:inset 0 0 0 1px var(--stroke-hover),var(--shadow-1)}
  .v10-tg button[aria-pressed="false"]:hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover));color:var(--ink)}
  .bp-toggle:hover .bp-ico,.bp-btn:hover .bp-ico,.cw-card:hover .cw-ico{background-image:linear-gradient(var(--fill-hover),var(--fill-hover));border-color:var(--stroke-hover)}
  .bp-btn:not(.bp-toggle):not([aria-expanded="true"]):hover::after{background-color:var(--fill-hover);box-shadow:inset 0 0 0 1px var(--stroke-hover)}
  .bp-toggle[aria-expanded="true"]:hover .bp-ico,.cw-card[aria-expanded="true"]:hover .cw-ico{background:var(--fill-on);border-color:var(--fill-on)}
}
/* on: the same everywhere, and it holds under the pointer */
.btn--on,.btn--on:hover,.btn[aria-pressed="true"]:not(.noise-btn),.noise[data-on="true"],.noise[data-on="true"]:hover{
  background:var(--fill-on);border-color:var(--fill-on);color:var(--text-on)}
.say:focus-visible{background:var(--fill-on);color:var(--text-on)}

/* ── tabs: the segment under the pointer takes the hover fill, drawn as the
   same pill as the active indicator (inset 3px top and bottom, full width of
   the segment) so hover and active are exactly the same size ── */
.tab::before{content:"";position:absolute;inset:3px 0;z-index:-1;border-radius:var(--r-pill);
  background:var(--fill-hover);opacity:0;transition:opacity var(--out) var(--ease)}
@media (hover:hover){
  .tab:not([aria-selected="true"]):hover{background:none;color:var(--ink)}
  .tab:not([aria-selected="true"]):hover::before{opacity:1;transition-duration:var(--in)}
}

/* ── cards that are links: the hairline steps up on hover ── */
@media (hover:hover){
  .card[data-href]:hover,.cw-card:hover,.lens-card:hover{box-shadow:inset 0 0 0 1px var(--stroke-ctrl)}
}

/* ── text links ── */
.cs-back,.cs-route a,.cs-toc ol a,.next a,.reslinks a,.stat-src a,.stat-upd a,.cert-view,.cw-link,.hero-cue,.dev-replay{
  transition:color var(--t-instant) var(--ease-enter)}
@media (hover:hover){
  .cs-back:hover,.cs-route a:hover,.cs-toc ol a:hover,.next a:hover,.reslinks a:hover,.stat-src a:hover,
  .stat-upd a:hover,.cert-view:hover,.cw-link:hover,.hero-cue:hover,.dev-replay:hover{color:var(--ink)}
}

/* ── focus: one ring for every control and link ── */
a:focus-visible,button:focus-visible,[role="link"]:focus-visible,[tabindex]:focus-visible,input:focus-visible,summary:focus-visible{
  outline:2px solid var(--ring);outline-offset:3px}


/* ═══ component tiers — emphasis follows role, never the page it is on ═══
   primary control    ink fill, page text                     .btn--on
   secondary control  1px --stroke-ctrl, ink text, 13px caps   .btn and every circle
   tertiary control   no stroke, ink text                      .btn-text .btn--plain .cs-back
   tag                1px --stroke, --body-c text, 12px caps,  28px, never hovers
   status             --surface-card fill, ink text            .avail
   eyebrow            12px caps, muted, no stroke
   A tag is always quieter than a control: lighter stroke, softer text, a step
   smaller. Only something that does something answers the pointer. */
:root{--ui-size:13px;--tag-size:12px;--tag-h:28px;--ui-track:.07em}

/* controls: ink text everywhere */
.btn:not(.btn--on):not([data-done]),.noise .noise-btn,.car-btn,.zoom,.say,a.proto,.cs-back,.btn-text{color:var(--ink)}
.noise[data-on="true"] .noise-btn{color:var(--page)}
.cs-back{font-size:var(--ui-size);letter-spacing:var(--ui-track)}
.say{font-size:var(--ui-size)}

/* tags */
.hero-labels li,.chip,.tag,.cmp-tag,.stat-upd-k,.gap-pill{
  display:inline-flex;align-items:center;min-height:var(--tag-h);height:auto;padding:0 .9em;border:0;border-radius:var(--r-pill);
  font-family:var(--sans);font-weight:500;font-size:calc(var(--tag-size) * var(--tscale,1));line-height:1.2;
  letter-spacing:var(--ui-track);text-transform:uppercase;color:var(--body-c);
  background:none;box-shadow:inset 0 0 0 1px var(--stroke);transition:none}
.hero-labels li:hover{background:none;box-shadow:inset 0 0 0 1px var(--stroke)}
/* a gap pill quotes a design response, so it keeps its own case */
.gap-pill{text-transform:none;letter-spacing:.01em}
/* tags that sit on a picture take the page colour so they stay legible */
.cmp-tag{background:var(--page)}
/* the one emphasised tag: what shipped, in ink, still not filled */
.tag--ship{color:var(--ink);background:none;box-shadow:inset 0 0 0 1px var(--ink)}

/* eyebrows */
.cw-eyebrow,.dec-lab,.card-eyebrow,.eyebrow-b,.cs-eyebrow,.rd-k,.cmp-side-k,.vs-e,.pushed-e,.cmp-h + .cmp-sub{
  font-size:calc(var(--tag-size) * var(--tscale,1));letter-spacing:var(--ui-track)}
.cert-view,.lens-go{font-size:var(--ui-size)}
.v10-tg button{font-size:var(--ui-size)}
.avail,.contact-loc.avail,.site-foot .contact-loc{font-size:var(--ui-size)}


/* ═══ calm technology: one surface, one shadow, one hover ═══
   surface   every content module sits on --surface-card, a shade lighter than
             the page, with the 1px --stroke hairline, 20px corners and
             --shadow-module. Device frames are hardware and keep their own.
   hover     controls and navigation darken by --fill-hover and outlined ones
             take an ink stroke; nothing turns white. Content cards that are
             links keep their slow lightening.
   Fewer values, each with a reason. */
:root{
  --surface-card:color-mix(in srgb, var(--page) 70%, var(--card));
  --shadow-module:0 1px 2px rgba(25,21,23,.04),0 10px 28px -14px rgba(25,21,23,.16);
}
.stack-cards .card,.cw-card,.lens-card,.panel,
.dgm,.gap-card,.ann,.carousel,.proto-frame,.rfig .tile,.rfig .st,.arch-side{
  background-color:var(--surface-card);border-radius:var(--r-lg);
  box-shadow:inset 0 0 0 1px var(--stroke),var(--shadow-module)}
.rfig .tile,.rfig .st{border-color:transparent}
/* link cards: the hairline steps up and the shadow deepens a little */
@media (hover:hover){
  .stack-cards .card[data-href]:hover,.cw-card:hover,.lens-card:hover{
    box-shadow:inset 0 0 0 1px var(--stroke-ctrl),0 1px 2px rgba(25,21,23,.05),0 14px 34px -14px rgba(25,21,23,.2)}
}
/* plain controls (no outline) take the tint on hover, like the tabs */
@media (hover:hover){
  .btn--plain:hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover)) !important}
  .btn:not(.btn--on):not([data-done]):not(.noise-btn):hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover))}
  .noise:not([data-on="true"]):hover{background-image:linear-gradient(var(--fill-hover),var(--fill-hover))}
}

/* ═══ loading: a quiet placeholder until an image arrives ═══
   site-motion.js marks every image that has not loaded yet with data-loading
   and clears it on load or error. The box is already reserved (width and
   height on the tag), so the placeholder holds the image's exact place: the
   recess tone, with one slow pass of the card tone across it. Still for
   reduced motion. */
img[data-loading]{color:transparent;background-color:var(--recess);
  background-image:linear-gradient(100deg,transparent 30%,var(--card) 50%,transparent 70%);
  background-size:220% 100%;background-repeat:no-repeat;animation:img-wait 1.8s ease-in-out infinite}
@keyframes img-wait{from{background-position:130% 0}to{background-position:-30% 0}}
[data-motion="reduced"] img[data-loading]{animation:none;background-image:none}
@media (prefers-reduced-motion:reduce){img[data-loading]{animation:none;background-image:none}}

/* ═══ high contrast: true black on true white ═══
   Each page's own [data-contrast="high"] block only pulls its greys up to the
   ink; this one, more specific, takes the whole palette to its extremes, so
   text, rules and icons are #000 on #FFF everywhere. The one saturated value
   (the availability dot) keeps its hue: it reports a state. */
:root[data-contrast="high"]{
  --page:#FFFFFF; --card:#FFFFFF; --recess:#F0F0F0;
  --ink:#000000; --body-c:#000000; --muted:#000000; --line:#000000; --line-soft:#4D4D4D;
  --edge:rgba(0,0,0,.55); --edge-ctrl:rgba(0,0,0,.8); --lattice:rgba(0,0,0,.16);
  --glass:rgba(255,255,255,.96);
  --surface-card:#FFFFFF; --fill-hover:rgba(0,0,0,.10)
}

/* ═══ case studies: every visual sits in a frame ═══
   The same recess the home cards put behind their diagrams: --recess, a fine
   square ruling in --lattice, a thin ink wash and a soft inner shadow. Every
   frame is as wide as Mindure's comparison sliders - the full content width
   less 3cm, centred - and goes edge to edge on a phone. A frame may hold
   several visuals that make one argument; a visual carrying a lot of text gets
   its own.
   On this ground --muted measures 4.3:1, under the 4.5:1 AA floor for body-size
   text, so inside a frame it steps down to #5E5356 (5.2:1), and --line-soft to
   #817775 (3.3:1, over the 3:1 floor for graphics). High contrast keeps its own
   values and drops the ruling for a single firm edge. */
.cs-body .sec > .vframe,.cs-head > .vframe{grid-column:1 / -1;justify-self:stretch;width:100%;min-width:0;
  margin:var(--section,64px) 0 0;position:relative;border-radius:var(--r-lg,16px);
  padding:clamp(28px,4.2vw,64px);
  --muted:#5E5356;--line-soft:#817775;
  background-color:var(--recess);
  background-image:
    linear-gradient(var(--lattice) 1px,transparent 1px),
    linear-gradient(90deg,var(--lattice) 1px,transparent 1px),
    linear-gradient(rgba(25,21,23,.03),rgba(25,21,23,.03));
  background-size:clamp(16px,1.5vw,24px) clamp(16px,1.5vw,24px),clamp(16px,1.5vw,24px) clamp(16px,1.5vw,24px),100% 100%;
  background-position:center center,center center,0 0;
  box-shadow:inset 0 0 14px rgba(25,21,23,.09),inset 0 0 0 1px var(--edge)}
.cs-body .sec > .vframe + .vframe{margin-top:var(--stack,24px)}
/* what sits inside keeps the size it was drawn at - the old 890px figure
   column - centred in the frame, so a wider frame adds air, not stretch.
   Labels and captions take the same measure, so their edges line up with
   the drawing they name. Only visuals made for the full frame span it. */
.vframe > *{grid-column:auto;max-width:min(100%,890px);margin-block:0;margin-inline:auto}
.vframe > .cmp,.vframe > .carousel,.vframe > .duo{max-width:none}
/* tables keep a reading measure */
.vframe > table{max-width:min(100%,760px)}
.vframe > * + *{margin-top:clamp(28px,3.4vw,56px)}
.vframe > .eyebrow-b + *,.vframe > .duo-h--label + *{margin-top:var(--stack,24px)}
.vframe > .tf-close{margin-top:var(--stack,24px)}
/* a figure that drew its own card now sits on the frame instead */
.vframe .dgm{background:none;box-shadow:none;padding:0}
.vframe .rfig{border-top:0;padding-top:0}
/* Mindure's paired visuals: one under the other, each with its label */
.vframe .duo,.vframe .duo-col,.vframe .duo-row{display:block}
.vframe .duo-col + .duo-col,.vframe .duo-row + .duo-row{margin-top:clamp(28px,3.4vw,56px)}
.vframe .duo-row > * + *{margin-top:var(--stack,24px)}
:root[data-contrast="high"] .cs-body .sec > .vframe,:root[data-contrast="high"] .cs-head > .vframe{--muted:var(--ink);--line-soft:var(--line);
  background-image:none;box-shadow:inset 0 0 0 1px var(--edge-ctrl)}
@media (max-width:767px){
  .cs-body .sec > .vframe,.cs-head > .vframe{width:100%;padding:20px}
}
/* Mindure's sliders: the frame is the size the slider was; the slider fills it */
.vframe .cmp{margin:0}
.vframe .cmp-stage{width:100%}
/* Mindure's resource links, now part of the text: a plain stack, not the old grid */
.sec-copy .duo-foot{display:flex;flex-direction:column;gap:12px}
.sec-copy .duo-foot > *{grid-column:auto}
.vframe .cmp-cols{width:100%}
/* Mindure: one frame, three readings of the problem. The Venn and the
   sense-making field side by side, the four domains in one line under them;
   one column on narrow screens */
@media (min-width:1024px){
  .vframe .duo{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:clamp(32px,4vw,64px);row-gap:clamp(36px,4vw,64px);align-items:start}
  .vframe .duo-col{display:contents}
  .vframe .duo-col + .duo-col,.vframe .duo-row + .duo-row{margin-top:0}
  .vframe .duo-row:has(.plane--venn){grid-column:1;grid-row:1}
  .vframe .duo-row:has(.sm){grid-column:2;grid-row:1}
  .vframe .duo-row:has(> .quad){grid-column:1 / -1;grid-row:2}
  .vframe .duo-row > .quad{grid-template-columns:repeat(4,minmax(0,1fr));max-width:none}
  /* the field and its key share half the frame, so both may shrink */
  .vframe .duo .sm{grid-template-columns:minmax(0,1.7fr) minmax(0,1fr);justify-content:stretch;column-gap:clamp(16px,2vw,32px);align-items:center}
  .vframe .duo .sm .plane--scatter{width:auto;min-width:0}
}

/* the closing line under a figure was --line-soft, a graphics colour that
   cannot carry text (3.1:1 here); inside a frame it takes the text grey */
.vframe .tf-close p{color:var(--muted)}
/* labels that sit on a connector line mask it with the ground they stand on */
.vframe .conn b{background:var(--recess)}
/* a thumbnail of an original image inside a frame: small, framed like a print,
   its open-full-size button always showing, the caption beside it */
.vframe .fig--thumb{position:relative;display:flex;align-items:center;gap:var(--stack,24px);
  width:fit-content;max-width:100%;margin-inline:max(0px,calc((100% - 890px) / 2)) 0}
.vframe .fig--thumb img{width:clamp(160px,22vw,280px);height:auto;margin:0;border-radius:10px;
  box-shadow:0 0 0 1px var(--edge-ctrl),0 6px 18px rgba(25,21,23,.10)}
.vframe .fig--thumb figcaption{margin:0;max-width:24ch;font-size:var(--micro);line-height:1.5;color:var(--muted)}
.vframe .fig--thumb .zoom{opacity:1;top:8px;right:auto;left:calc(clamp(160px,22vw,280px) - 8px);translate:-100% 0}
@media (max-width:600px){.vframe .fig--thumb{flex-direction:column;align-items:flex-start}}
/* a frame in the page header sits on its own line under the intro, centred */
.cs-head > .vframe{display:block;margin:calc(var(--stack,24px)*2) auto 0}
/* Tribe's stepper inside a frame: the frame is its card now, so its own card,
   stage and veils take the frame's ground instead of drawing a second one */
.vframe .carousel{background:none;box-shadow:none;border-radius:0;overflow:visible}
.vframe .car-stage{background:none}
/* the veil is the frame's own colour, wash included, so it dims the neighbours
   without drawing a lighter box over the empty slots */
.vframe .car-veil{background:color-mix(in srgb,var(--recess) 97%,#191517)}
/* a label above a full-frame visual spans with it */
.vframe > .eyebrow-b:has(+ .carousel),.vframe > .eyebrow-b:has(+ .cmp){max-width:none}
.vframe .car-top,.vframe .car-bottom{padding-inline:0}
/* lists of constraints, limits and failure modes inside a frame are numbered,
   so a row can be pointed to: 01, 02, 03 before each row's heading */
.vframe table.devsync tbody{counter-reset:row}
.vframe table.devsync tbody tr{counter-increment:row}
.vframe table.devsync tbody:has(tr + tr) th[scope="row"]::before{content:counter(row,decimal-leading-zero);
  display:block;margin-bottom:.3em;font-family:var(--sans);font-weight:500;font-size:var(--micro);
  letter-spacing:.07em;color:var(--muted)}
/* the answer in each numbered row starts level with the row's heading, not its number */
@media (min-width:768px){
  .vframe table.devsync tbody:has(tr + tr) td::before{content:"";display:block;
    height:calc(var(--micro) * 1.4 + var(--micro) * .3)}
}
/* a scale of levels is not a list: Signal's warning levels stay unnumbered */
.vframe .v5-levels table.devsync tbody th[scope="row"]::before,
.vframe .v5-levels table.devsync tbody td::before{content:none}
/* Tribe's stepper: its heading is a plain label, no rule under it */
/* set as the design system's ink pill - the "Gen 3 — Accepted" tag - because it
   names the same thing: the version that was accepted */
.vframe > .eyebrow-b:has(+ .carousel){width:fit-content;margin-inline:0;border:0;
  padding:.3em .9em;border-radius:var(--r-pill,999px);background:var(--ink);color:var(--page)}
/* Tribe's problem map: its box takes the drawing's own shape instead of the
   generic plane's, and inside a frame it keeps the frame's spacing, not the
   section-sized gap it was given on the page */
@media (min-width:901px){.vframe .plane--map{aspect-ratio:1280 / 500}}
/* at 900px and under the map scrolls sideways at a legible size, so its box
   takes the drawing's height; the desktop shape rule was outranking that */
@media (max-width:900px){.fig .plane--map{aspect-ratio:auto}}
.vframe > .fig.fig--map:first-child{margin-top:0}
.vframe > * + .fig.fig--map{margin-top:clamp(28px,3.4vw,56px)}

/* ═══ case studies: one vertical rhythm ═══
   Spacing says what belongs together (Gestalt proximity): the closer two
   things sit, the more they read as one. So the gaps form a scale on an 8px
   grid, each level about twice the one below, which is the step a reader needs
   to see a boundary at a glance instead of working it out:
     label to its visual         16      the label is part of the drawing
     visual to visual (in frame) 48      one argument, several pictures
     frame to frame              24      each frame's edge already groups it
     text to its visuals         48      the visuals belong to the text above
     section to section          192     a new topic: the page breathes
   On a phone the same steps scale down to 8 / 32 / 16 / 32 / 96. */
:root{
  --gap-label:clamp(8px,1.1vw,16px);
  --gap-visual:clamp(16px,1.7vw,24px);
  --gap-text-visual:clamp(32px,3.4vw,48px);
  --gap-section:clamp(96px,13.4vw,192px)
}
.cs-head{padding-bottom:var(--gap-section)}
.cs-head > .vframe{margin-top:var(--gap-text-visual)}
.cs-body .sec{row-gap:0;padding-bottom:var(--gap-section)}
.cs-body .sec:last-of-type{padding-bottom:0}
.cs-body .sec > .sec-copy + .vframe{margin-top:var(--gap-text-visual)}
.cs-body .sec > .vframe + .vframe{margin-top:var(--gap-visual)}
/* inside a frame: pictures a step apart, a label held to what it names */
.vframe > * + *{margin-top:var(--gap-text-visual)}
.vframe > .eyebrow-b + *,.vframe > .duo-h--label + *,.vframe .duo-row > * + *,.vframe > .tf-close{margin-top:var(--gap-label)}
.vframe .duo{row-gap:var(--gap-text-visual)}
/* the drive: its guidance is text, the road its visual */
.cs-body .road{row-gap:var(--gap-text-visual);margin-bottom:var(--gap-section)}
/* one column: the title sits on its text, as a heading sits on its paragraph */
@media (max-width:1023px){
  .cs-body .sec > .sec-label + .sec-copy{margin-top:var(--stack,24px)}
}
/* the gap is measured from the last line of text, so nothing trails after it */
.cs-body .sec-copy > :last-child{margin-bottom:0}
/* Inside a text block the same logic runs one level down. Paragraphs follow
   each other at the paragraph gap; a set-apart block - a decision, a pull
   quote - stands a clear step further off on both sides; the Read more links
   close the block one step after the text and sit together as one list. */
:root{--gap-block:clamp(24px,2.3vw,32px)}
.cs-body .sec-copy > :is(.dec,.dec-p,.hmw){margin-top:var(--gap-block);margin-bottom:0}
.cs-body .sec-copy > :is(.dec,.dec-p,.hmw) + *{margin-top:var(--gap-block)}
.cs-body .sec-copy > details.more{margin:var(--gap-label) 0 0}
.cs-body .sec-copy > :not(details.more) + details.more{margin-top:var(--gap-block)}
.cs-body .sec-copy > details.more + details.more{margin-top:0}
.cs-body .sec-copy > details.more[open]{margin-bottom:var(--gap-label)}
/* two columns: the title and its text share a first baseline, so they read as
   one line across the page rather than two blocks that happen to start close */
@media (min-width:1024px){
  .cs-body .sec > .sec-label,.cs-body .sec > .sec-copy{align-self:first baseline}
}
/* a sub-heading opens a new run of text: a clear step above, held close to
   what it introduces; a fold or a list of links is a set-apart block */
.cs-body .sec-copy > h3{margin-top:calc(var(--gap-block) * 1.5)}
.cs-body .sec-copy > :is(.blueprint,.duo-foot){margin-top:var(--gap-block)}

/* ═══ visual titles ═══
   Every visual is named by a small title: the same serif as the section titles
   one clear step down, so it reads as the heading of that picture and never as
   a new section. It sits on the visual's left edge, held close to it. Labels
   that were small capitals (Mindure's paired visuals, the slider names, the
   figure kickers) take the same style, so a visual is always named one way.
   Each frame is a group named by its first title (role="group",
   aria-labelledby), and the titles are h3s, so a screen reader can move from
   picture to picture. */
.vf-title,.vframe .rfig .ft,.vframe .duo-h--label,.vframe .cmp-h{
  font-family:var(--serif);font-weight:400;font-size:clamp(19px,1.55vw,22px);line-height:1.3;
  letter-spacing:-.01em;text-transform:none;color:var(--ink);margin:0;padding:0;border:0;max-width:44ch}
.vframe .rfig .ft{margin-bottom:var(--gap-label)}
.vframe > .vf-title + *{margin-top:var(--gap-label)}
.cs-body .rd-k.vf-title{margin:0}
.cs-head > .vframe .rfig .ft{font-family:var(--serif);font-weight:400;font-size:clamp(19px,1.55vw,22px);line-height:1.3;letter-spacing:-.01em;color:var(--ink);margin:0 0 var(--gap-label)}
/* a title takes the measure of the visual it names, so it starts on the same
   edge; a second title in a frame opens the next visual a full step down */
.vframe > .vf-title{width:100%;max-width:min(100%,890px);margin-inline:auto}
.vframe > * + .vf-title{margin-top:var(--gap-text-visual)}
.vframe > .vf-title:has(+ table){max-width:min(100%,760px)}
/* the carousel runs the frame's full width, so its title starts on that edge */
.vframe > .vf-title:has(~ .carousel){max-width:none}
/* Mindure's sliders: the title held a label step above the screens like every
   other visual, with a quiet pointer beside it - the account of each screen is
   under the picture, and without it the slider reads as the whole story */
.vframe .cmp-top{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  column-gap:var(--gap-visual);row-gap:4px;margin-bottom:var(--gap-label)}
.vframe .cmp-note{margin:0;font-family:var(--sans);font-weight:500;font-size:var(--micro);
  line-height:1.4;letter-spacing:.04em;color:var(--muted)}

/* ═══ a visual and its words ═══
   Every visual comes with its own short title and paragraph. Proximity does the
   grouping: the paragraph sits a short step above its frame, nearer to it than
   to anything else, and a gap three times as wide separates one pair from the
   next, so each paragraph is read as belonging to the picture under it. The
   title sits in the heading rail beside its paragraph, one size under the
   section title, so the rail reads down the page as its outline.
     paragraph to its visual   32   the words belong to the picture below
     pair to pair              96   a new point inside the same topic
     section to section       192   a new topic
   On a phone: 24 / 64 / 96, the title held 12 above its paragraph. */
:root{--gap-own:clamp(24px,2.3vw,32px);--gap-pair:clamp(64px,6.7vw,96px)}
.cs-body .sec > .sub-label{grid-column:1;margin:var(--gap-pair) 0 0;max-width:24ch;
  font-family:var(--serif);font-weight:500;font-size:calc(var(--body) * 1.3);line-height:1.3;
  letter-spacing:-.005em;color:var(--ink)}
.cs-body .sec > .sub-copy{margin-top:var(--gap-pair)}
.cs-body .sec > .sub-copy + .vframe{margin-top:var(--gap-own)}
@media (min-width:1024px){
  .cs-body .sec > .sub-label,.cs-body .sec > .sub-copy{align-self:first baseline}
}
@media (max-width:1023px){
  .cs-body .sec > .sub-label + .sub-copy{margin-top:calc(var(--gap-label) * 1.5)}
}
.vframe > .vf-title + .fig.fig--map{margin-top:var(--gap-label)}
