/* ============================================================
   LAUNCH7 — UTILITY LAYER COMPONENTS
   Added 2026-08-22 with the repositioning. This file is the new
   design language: centred chrome, lit backdrops, glass bento.

   It does NOT extend components.css. The retired pages (agents,
   services, work, teardown) keep loading that file until they are
   deleted; new pages load tokens → base → l7 and nothing else.
   Two component layers exist on purpose, for exactly as long as
   the old pages do.

   Same rule as everywhere else: semantic tokens only, no raw
   values. The one exception is geometry that is not a token —
   blur radii, gradient stops, mask percentages.
   ============================================================ */

/* ============================================================
   ELEMENTS
   ============================================================ */

/* ---------- buttons ----------
   Three levels, one primary per view. The primary is the brand
   blue fill in BOTH themes: white on #2424FF is 7.58:1 either
   way, which is the whole reason the fill survives the theme
   switch untouched. */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  min-height:48px;padding:0 var(--space-6);
  border:1px solid transparent;border-radius:var(--radius-pill);
  font-size:var(--step-sm);font-weight:600;line-height:1;
  text-decoration:none;cursor:pointer;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease);
}
.btn:hover{text-decoration:none}
.btn-primary{background:var(--btn-primary-bg);color:var(--btn-primary-text)}
.btn-primary:hover{background:var(--btn-primary-hover);filter:brightness(.88)}
.btn-secondary{background:var(--btn-secondary-bg);color:var(--btn-secondary-text)}
.btn-secondary:hover{background:var(--btn-secondary-hover)}
.btn-ghost{background:transparent;color:var(--btn-ghost-text);border-color:var(--btn-ghost-border)}
.btn-ghost:hover{background:var(--btn-ghost-hover-bg)}
/* 44px, not 38px. The old value cleared WCAG 2.2's 24px minimum but missed the
   44px tap-target floor this project sets for itself, and .btn-sm is on the nav
   CTA and both panel-foot buttons — the most-tapped controls on a phone. It also
   now matches .menu-btn and .theme-btn, so the navbar row is a consistent height. */
.btn-sm{min-height:44px;padding:0 var(--space-4);font-size:var(--step-xs)}

/* ---------- mono label ----------
   The bracketed technical label the reference boards use for
   every piece of secondary information. Bracket characters are
   in ::before/::after so they are decoration and never reach a
   screen reader as "left square bracket". */
.tag{
  display:inline-flex;align-items:center;gap:.5em;
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--text-muted);
}
.tag-accent{color:var(--text-accent)}
/* The brackets carry no information, but they are still glyphs and a strict
   audit reads them as text. They used to sit at opacity:.55, which multiplies
   against the label's own colour alpha — measured that put them at 2.15:1
   (light body tag), 2.20:1 (accent footer tile), 2.46:1 (dark .tag-accent) and
   2.60–2.66:1 (footer tiles). Five of seven contexts fell under even the 3:1
   non-text floor. They inherit the label colour now, so their ratio is exactly
   the label's and passes wherever the label does. This also matches the
   references, where the brackets read at the same weight as the word. [REF]
   A text-node walker cannot catch this — generated content is not a text node. */
.tag-brack::before{content:'['}
.tag-brack::after{content:']'}

/* ---------- knock-out word ----------
   The treatment from the reference hero: one word in the
   headline sitting on a solid brand fill rather than being
   coloured text. This is deliberate and it is the reason the
   brand blue needs no lightening for headlines — the text on it
   is white at 7.58:1 in both themes.

   box-decoration-break keeps the fill intact if the headline
   wraps mid-phrase. */
.knock{
  background:var(--l7-blue);color:var(--l7-white);
  padding:0 .16em;border-radius:8px;
  box-decoration-break:clone;-webkit-box-decoration-break:clone;
}

/* ---------- meta row ----------
   The dot-separated mono strip under the hero. */
.meta-row{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:var(--space-3) var(--space-4);
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--text-muted);
}
/* The separator is the first flex child of the item that follows it, so the
   list gap sits on its left and the item gap on its right. Both are 16px,
   which is what keeps the dots optically centred between the words — an
   earlier version pulled them left with a negative margin and they sat
   hard against the preceding word. */
.meta-row li{display:flex;align-items:center;gap:var(--space-4)}
.meta-row li+li::before{content:'';flex:none;width:4px;height:4px;border-radius:50%;background:var(--text-accent)}

/* ---------- hairline rule ---------- */
.rule{height:1px;background:var(--border);border:0;margin-block:var(--space-7)}

/* ============================================================
   BACKDROP — glow + wireframe
   Every one of these is decoration. All are aria-hidden in the
   markup and none carries content. Strip this whole block and
   every page still reads.
   ============================================================ */

.lit{position:relative;isolation:isolate}
.lit>*{position:relative;z-index:1}

/* The ambient radial. Sits behind everything, clipped to its
   section, and is the single element carrying the "lit from
   above" feel of the reference boards. In light theme the token
   drops it to .16 alpha, where it reads as a faint wash rather
   than a smudge. */
.glow{
  position:absolute;inset:-40% -20% auto -20%;height:170%;
  z-index:0;pointer-events:none;
  background:radial-gradient(58% 52% at 50% 4%, var(--glow-core) 0%, var(--glow-edge) 68%);
}
.glow-b{inset:auto -20% -45% -20%;height:140%;background:radial-gradient(52% 48% at 50% 96%, var(--glow-core) 0%, var(--glow-edge) 66%)}

/* Perspective wireframe — the dome from the first board. Pure
   SVG, stroked with --grid-line, masked so it fades out before
   it reaches the text. */
.wire{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  width:100%;height:100%;
  color:var(--grid-line-strong);
  /* Centred low and wide. The dome's arcs sit in the bottom two-thirds of
     the viewBox, so a mask centred at 40% erased most of them — the earlier
     values left one faint band under the navbar and nothing else. */
  -webkit-mask-image:radial-gradient(92% 88% at 50% 66%,#000 0%,#000 34%,transparent 88%);
  mask-image:radial-gradient(92% 88% at 50% 66%,#000 0%,#000 34%,transparent 88%);
}
.wire path,.wire line,.wire ellipse{fill:none;stroke:currentColor;vector-effect:non-scaling-stroke}

/* Square grid — the quieter backdrop used behind the bento. */
.mesh{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),
                   linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:radial-gradient(80% 70% at 50% 50%,#000 0%,transparent 80%);
  mask-image:radial-gradient(80% 70% at 50% 50%,#000 0%,transparent 80%);
}

/* ---------- glass ----------
   backdrop-filter is progressive: without it the card is still a
   legible bordered panel on a solid token background, which is
   why --glass-bg is an alpha over a real surface rather than the
   surface itself. */
.glass{
  background:var(--bg-surface);
  border:1px solid var(--glass-border);
  border-radius:var(--radius-card);
  box-shadow:inset 0 1px 0 var(--glass-hi),var(--shadow-ambient);
}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{
    background:var(--glass-bg);
    -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(140%);
    backdrop-filter:blur(var(--glass-blur)) saturate(140%);
  }
}

/* ============================================================
   CHROME — navbar
   Placement copied from the pulsr reference: menu left, wordmark
   dead centre, actions right. Three grid columns with the outer
   two equal, so the wordmark is centred on the VIEWPORT rather
   than on whatever happens to be beside it.
   ============================================================ */

/* The bar itself carries nothing — no fill, no rule. What floats on it
   is three self-contained pills: menu, wordmark, and one capsule
   holding the theme toggle and the CTA together.

   Because the bar is see-through and the pills have no outline, each
   pill's fill is the only thing guaranteeing its own legibility. The
   worst case is a pill sitting over the brightest thing that can
   scroll beneath it: white in dark theme lands at 6.69:1 for white
   text, and the brand blue in light theme at 11.46:1 for ink text.
   Both measured against --pill-bg at .72, which is where that alpha
   came from — see the note on the token. */
/* FIXED, not sticky — and that is the whole reason this reads as
   transparent now. Sticky keeps the bar in normal flow, so it owns a
   strip of the document and the only thing behind it is the body
   colour. Setting background:transparent on that just shows the page
   background, which on dark is near-black: a black bar by another
   route. Taking it out of flow lets the hero — glow, wireframe and all
   — run underneath, which is what there is to see through to. */
.l7nav{
  position:fixed;top:0;left:0;right:0;z-index:200;
  background:none;
  border-bottom:0;
  pointer-events:none;   /* the gaps between pills must not eat clicks */
}
/* Re-armed on the pills themselves, NOT on .l7nav-in. That row is a
   1fr/auto/1fr grid, so its outer columns are empty space — handing it
   pointer-events would put an invisible 1040px-wide bar across the top
   of the hero, swallowing every click and hover underneath it. */
.menu-btn,.l7nav-brand,.l7nav-right,.l7panel{pointer-events:auto}
.l7nav-in{
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  /* Narrower than --wrap (1180). The pills pull in off the edges so the
     row reads as a floating cluster rather than a full-width bar. */
  max-width:1040px;
  min-height:72px;gap:var(--space-4);
}

/* One shared skin for all three groups, so they read as a set.

   No border. The fill, a one-pixel top highlight and a soft ambient
   shadow draw the control instead — an outline on a transparent bar
   reads as a wireframe box, which is the opposite of premium. Losing
   the border means the fill alone has to carry legibility, so it is
   set from measurement rather than taste: see --pill-bg in tokens. */
.menu-btn,
.l7nav-brand,
.l7nav-right{
  background:var(--pill-bg);
  border:0;
  border-radius:var(--radius-pill);
  box-shadow:inset 0 1px 0 var(--pill-hi),var(--pill-shadow);
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .menu-btn,.l7nav-brand,.l7nav-right{
    -webkit-backdrop-filter:blur(20px) saturate(1.4);backdrop-filter:blur(20px) saturate(1.4);
  }
}
/* The lift on hover. 1px is enough to feel responsive without the row
   appearing to wobble as the pointer crosses it. */
.menu-btn:hover,.l7nav-brand:hover{transform:translateY(-1px)}

.l7nav-brand{
  grid-column:2;justify-self:center;
  display:inline-flex;align-items:center;gap:var(--space-3);
  min-height:52px;padding:0 var(--space-5);
  color:var(--text-primary);text-decoration:none;
  font-weight:700;font-size:1.125rem;letter-spacing:-.02em;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.l7nav-brand:hover{text-decoration:none}
.l7nav-brand svg{width:26px;height:26px;border-radius:7px}
.l7nav-brand i{font-style:normal;color:var(--text-accent)}

.l7nav-left{grid-column:1;justify-self:start;display:flex;align-items:center;gap:var(--space-2)}

/* The grouped capsule. 4px of padding around 44px controls keeps every
   tap target at the floor while the capsule reads as one object. */
.l7nav-right{
  grid-column:3;justify-self:end;
  display:flex;align-items:center;gap:var(--space-2);
  padding:4px;
}

/* The CTA. A vertical gradient between the brand blue and the deep blue
   that already exists for hover, plus a coloured shadow so it lifts off
   the glass. Both ends are darker-or-equal to #2424FF, so the white
   label's floor stays 7.58:1 and only improves down the gradient —
   a lighter top would have been the prettier choice and the wrong one. */
.l7nav-cta{
  background:var(--l7-blue);
  box-shadow:0 4px 14px rgba(36,36,255,.34),inset 0 1px 0 rgba(255,255,255,.22);
  border:0;
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease),filter var(--dur-fast) var(--ease);
}
.l7nav-cta:hover{
  transform:translateY(-1px);
  filter:saturate(1.08);
  box-shadow:0 6px 20px rgba(36,36,255,.46),inset 0 1px 0 rgba(255,255,255,.26);
}
.l7nav-cta:active{transform:translateY(0)}
@media(prefers-reduced-motion:reduce){
  .menu-btn:hover,.l7nav-brand:hover,.l7nav-cta:hover,.l7nav-cta:active{transform:none}
}
/* Inside the capsule the toggle drops its own outline — the capsule is
   already drawing one, and two nested borders looks like a mistake.

   Scoped to the width where the capsule actually exists rather than
   being written flat and un-done below it. Flat rules here read as
   .l7nav-right .theme-btn:hover, which outranks anything a max-width
   query can say about .theme-btn — so on a phone, where the group is
   not a group, hovering the toggle wiped out the outline it needs. */
@media(min-width:720px){
  .l7nav-right .theme-btn{border:0;background:transparent}
  .l7nav-right .theme-btn:hover{background:var(--btn-ghost-hover-bg)}
}

/* Under 720px the CTA is hidden, which would otherwise leave a capsule
   drawn around a single round button. Below that the group stops being
   a group and the toggle stands on its own as a third pill. */
@media(max-width:719px){
  .l7nav-right{
    background:none;box-shadow:none;padding:0;
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
  .l7nav-right .theme-btn{
    border:0;background:var(--pill-bg);
    box-shadow:inset 0 1px 0 var(--pill-hi),var(--pill-shadow);
  }
}

/* Menu button. Label is hidden under 560px, where the icon alone
   has to carry it — aria-label covers that case. */
/* Background, border and radius come from the shared pill skin above.
   Setting them again here would win on source order and knock the
   glass back out. */
.menu-btn{
  display:inline-flex;align-items:center;gap:var(--space-3);
  min-height:52px;padding:0 var(--space-5);
  color:var(--text-primary);cursor:pointer;
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.menu-btn:hover{background:var(--pill-bg)}
.menu-btn .bars{display:grid;gap:3px;width:14px}
.menu-btn .bars i{display:block;height:1.5px;background:currentColor;transition:transform var(--dur-base) var(--ease),opacity var(--dur-fast) var(--ease)}
.menu-btn[aria-expanded="true"] .bars i:nth-child(1){transform:translateY(4.5px) rotate(45deg)}
.menu-btn[aria-expanded="true"] .bars i:nth-child(2){opacity:0}
.menu-btn[aria-expanded="true"] .bars i:nth-child(3){transform:translateY(-4.5px) rotate(-45deg)}
@media(max-width:559px){.menu-btn .lbl{display:none}.menu-btn{padding:0 var(--space-3)}}

/* Theme toggle. Two icons in one button; CSS shows the one for
   the theme you would switch TO, and the accessible name is kept
   in step by site.js. */
.theme-btn{
  display:inline-grid;place-items:center;
  width:44px;height:44px;
  background:transparent;border:1px solid var(--border);border-radius:50%;
  color:var(--text-primary);cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.theme-btn:hover{background:var(--btn-ghost-hover-bg);border-color:var(--border-strong)}
.theme-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;grid-area:1/1}
.theme-btn .i-moon{display:block}
.theme-btn .i-sun{display:none}
:root[data-theme="dark"] .theme-btn .i-moon{display:none}
:root[data-theme="dark"] .theme-btn .i-sun{display:block}
@media(prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .theme-btn .i-moon{display:none}
  :root:not([data-theme="light"]) .theme-btn .i-sun{display:block}
}

@media(max-width:719px){.l7nav-cta{display:none}}

/* ---------- menu panel ----------
   Grid-rows 0fr→1fr so it animates without a hard-coded height
   and collapses to genuinely zero when closed. */
/* The fill and the rule belong to the OPEN panel only. Closed, this
   element is a 0fr grid row — but a background paints across the whole
   border box regardless of content height, so background:var(--bg-page)
   plus a 1px border drew a full-width strip of page colour under the
   floating pills. On dark that is the black line under the navbar. */
.l7panel{
  display:grid;grid-template-rows:0fr;
  background:none;border-bottom:0;
  transition:grid-template-rows var(--dur-base) var(--ease);
}
.l7panel.open{background:var(--bg-page);border-bottom:1px solid var(--border)}
/* visibility, not just overflow. Clipping to a 0fr row hides the panel but
   leaves its five links and two buttons focusable — a keyboard user tabbing off
   the Menu button dropped into seven invisible controls (WCAG 2.4.3 / 2.4.7).
   visibility:hidden takes them out of the tab order and the accessibility tree.
   The 0s-linear delay holds it visible for the length of the collapse so the
   row animation still plays; opening clears the delay so it shows immediately. */
.l7panel>div{overflow:hidden;visibility:hidden;transition:visibility 0s linear var(--dur-base)}
.l7panel.open>div{visibility:visible;transition-delay:0s}
.l7panel.open{grid-template-rows:1fr}
.l7panel-in{padding-block:var(--space-5) var(--space-7);display:grid;gap:var(--space-1)}
/* Direct children only. Scoped this way because the panel also contains
   .btn elements in its foot, and a descendant selector here outranks
   .btn-primary — which silently repainted the panel's CTA as ink text on
   the blue fill at 2.56:1. Caught by the rendered contrast audit. */
.l7panel-in > a{
  display:flex;align-items:baseline;gap:var(--space-4);
  padding:var(--space-3) 0;
  color:var(--text-primary);text-decoration:none;
  font-size:var(--step-h3);font-weight:700;letter-spacing:-.015em;
  border-bottom:1px solid var(--border);
}
.l7panel-in > a:hover{color:var(--text-accent);text-decoration:none}
.l7panel-in > a span{font-family:var(--font-mono);font-size:var(--step-eyebrow);color:var(--text-muted);letter-spacing:.16em}
.l7panel .l7panel-foot{margin-top:var(--space-5);display:flex;flex-wrap:wrap;gap:var(--space-3)}
/* The visibility delay exists only to outlast the row animation. With no
   animation to outlast, it would just be a pause before the menu disappears. */
@media(prefers-reduced-motion:reduce){.l7panel{transition:none}.l7panel>div{transition:none}}

/* ============================================================
   HERO
   ============================================================ */

/* The top figure clears the fixed navbar. It used to be 80px, which was
   set when the bar still occupied its own strip of the document; now the
   bar floats over this section, so the hero has to reserve that height
   itself or the eyebrow sits behind the pills. 72px bar + breathing room. */
.hero{position:relative;overflow:hidden;padding-block:clamp(132px,15vw,200px) clamp(72px,10vw,128px);text-align:center}

/* Same reason: an in-page jump lands at the top of the target, which is
   now underneath the floating bar. Scoped to main so the chrome's own
   ids are unaffected. */
main [id]{scroll-margin-top:104px}
.hero-in{max-width:900px;margin-inline:auto;display:grid;gap:var(--space-6);justify-items:center}
/* No max-width. The two beats are split by an explicit <br> in the markup —
   the reference headlines are hand-broken, not measure-wrapped, and a ch
   limit fights the break and produces a third orphan line. */
.hero h1{font-size:var(--step-display);letter-spacing:-.038em;line-height:1.04}
.hero .lede{font-size:var(--step-body-lg);color:var(--text-secondary);max-width:56ch;margin-inline:auto;text-align:center}
.hero-actions{display:flex;flex-wrap:wrap;gap:var(--space-3);justify-content:center}
.hero-note{font-size:var(--step-xs);color:var(--text-muted)}

/* ============================================================
   STATEMENT
   The section the design doc singled out: one large two-line
   assertion, then a row of short declaratives underneath. The
   cards are deliberately plain — the sentence is the design.
   ============================================================ */

.statement{position:relative;overflow:hidden;padding-block:var(--section-y);background:var(--bg-tint)}
.statement h2{font-size:var(--step-display);letter-spacing:-.034em;line-height:1.04;max-width:18ch;margin-bottom:var(--space-8)}
.claims{display:grid;gap:var(--space-4)}
@media(min-width:640px){.claims{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.claims{grid-template-columns:repeat(4,1fr)}}
.claim{padding:var(--space-6);border-radius:var(--radius-card);border:1px solid var(--border);background:var(--bg-surface)}
.claim h3{font-size:var(--step-h3);margin-block:var(--space-4) var(--space-3)}
.claim p{font-size:var(--step-sm);color:var(--text-secondary)}

/* ============================================================
   BENTO — the five things Launch7 supplies
   Six-column grid at desktop: two wide cards on the top row,
   three equal cards beneath. Two columns at tablet with the
   first card spanning both. One column on a phone.
   ============================================================ */

.bento-wrap{position:relative;overflow:hidden;padding-block:var(--section-y)}
.bento{display:grid;gap:var(--space-4)}
@media(min-width:700px){
  .bento{grid-template-columns:repeat(2,1fr)}
  .bento-a{grid-column:span 2}
}
@media(min-width:1000px){
  .bento{grid-template-columns:repeat(6,1fr)}
  .bento-a{grid-column:span 3}
  .bento-b{grid-column:span 3}
  .bento-c,.bento-d,.bento-e{grid-column:span 2}
}

.bcard{
  position:relative;display:flex;flex-direction:column;gap:var(--space-5);
  padding:var(--space-6);min-height:260px;
  border-radius:var(--radius-card);overflow:hidden;
}
.bcard-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-4)}
.bcard h3{font-size:var(--step-h3);margin-bottom:var(--space-3)}
.bcard p{font-size:var(--step-sm);color:var(--text-secondary);max-width:44ch}
.bcard-fig{margin-top:auto;position:relative}

/* The one solid card. Sits on the brand fill in both themes, so
   everything inside it is white at 7.58:1 or better. */
.bcard-solid{background:var(--l7-blue);color:var(--l7-white)}
.bcard-solid p{color:rgba(255,255,255,.82)}
.bcard-solid .tag{color:rgba(255,255,255,.72)}

/* ---------- card figures ----------
   All five are DOM + CSS. No images, no canvas, nothing to
   load, and every one is aria-hidden in the markup. */

.fig{font-family:var(--font-mono);font-size:var(--step-eyebrow);line-height:1.5}

/* Skills — a stack of file rows */
.fig-files{display:grid;gap:var(--space-2)}
.fig-files div{
  display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-3) var(--space-4);
  border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--bg-surface);color:var(--text-secondary);
}
.fig-files div::before{content:'';width:8px;height:8px;border-radius:2px;background:var(--text-accent);flex:none}
.fig-files div:nth-child(2)::before{opacity:.6}
.fig-files div:nth-child(3)::before{opacity:.3}

/* Tools — a terminal line */
.fig-term{
  padding:var(--space-4);border-radius:var(--radius-sm);
  background:var(--l7-ink);color:var(--l7-gray-300);
  border:1px solid rgba(255,255,255,.10);
  display:grid;gap:var(--space-2);
}
.fig-term span{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fig-term .p{color:var(--text-accent)}
.fig-term .c{color:var(--l7-white)}

/* MCP — a hub with three spokes */
.fig-hub{display:flex;align-items:center;gap:var(--space-3)}
.fig-hub .hub{
  flex:none;width:46px;height:46px;border-radius:12px;
  display:grid;place-items:center;
  background:var(--l7-blue);color:var(--l7-white);
  font-weight:700;
}
.fig-hub .spokes{display:grid;gap:var(--space-2);flex:1;min-width:0}
.fig-hub .spokes i{
  display:block;height:1px;background:var(--border-strong);position:relative;
}
.fig-hub .spokes i::after{
  content:'';position:absolute;right:0;top:-3px;
  width:7px;height:7px;border-radius:50%;
  background:var(--bg-surface);border:1px solid var(--border-strong);
}
.fig-hub .spokes i:nth-child(2){width:78%}
.fig-hub .spokes i:nth-child(3){width:56%}

/* Workflows — three connected nodes */
.fig-flow{display:flex;align-items:center;gap:var(--space-2)}
.fig-flow b{
  flex:none;padding:var(--space-2) var(--space-3);
  border-radius:10px;border:1px solid var(--border);
  background:var(--bg-surface);color:var(--text-secondary);
  font-weight:700;
}
.fig-flow i{flex:1;height:1px;background:var(--border-strong);min-width:10px}
.bcard-solid .fig-flow b{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.28);color:var(--l7-white)}
.bcard-solid .fig-flow i{background:rgba(255,255,255,.34)}

/* Resources — link rows */
.fig-links{display:grid;gap:var(--space-2)}
.fig-links div{
  display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-2) 0;border-bottom:1px solid var(--border);
  color:var(--text-secondary);
}
.fig-links div::before{
  content:'';flex:none;width:16px;height:12px;border-radius:3px;
  border:1px solid var(--border-strong);
}
.fig-links div:first-child::before{background:var(--text-accent);border-color:transparent}

/* ============================================================
   LOOP — the six-step differentiator
   ============================================================ */

.loop{display:grid;gap:var(--space-4);counter-reset:l}
@media(min-width:640px){.loop{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.loop{grid-template-columns:repeat(3,1fr)}}
.loop li{
  counter-increment:l;
  padding:var(--space-5) var(--space-5) var(--space-5) var(--space-8);
  border-top:1px solid var(--border);position:relative;
}
.loop li::before{
  content:counter(l,decimal-leading-zero);
  position:absolute;left:0;top:var(--space-5);
  font-family:var(--font-mono);font-size:var(--step-eyebrow);font-weight:700;
  letter-spacing:.1em;color:var(--text-accent);
}
.loop h3{font-size:var(--step-sm);margin-bottom:var(--space-2)}
.loop p{font-size:var(--step-xs);color:var(--text-secondary)}
.loop li.key{border-top-color:var(--border-accent)}
.loop li.key h3{color:var(--text-accent)}

/* ============================================================
   CTA
   ============================================================ */

.cta{position:relative;overflow:hidden;padding-block:clamp(72px,10vw,132px);text-align:center}
.cta-in{max-width:900px;margin-inline:auto;display:grid;gap:var(--space-6);justify-items:center}
.cta h2{font-size:var(--step-display);letter-spacing:-.034em;line-height:1.04}
.cta p{color:var(--text-secondary);font-size:var(--step-body-lg);max-width:52ch}

/* ============================================================
   FAQ — native <details>, keyboard behaviour comes free
   ============================================================ */

.faq{display:grid}
.faq details{border-top:1px solid var(--border)}
.faq details:last-of-type{border-bottom:1px solid var(--border)}
.faq summary{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-5);
  padding:var(--space-5) 0;cursor:pointer;list-style:none;
  font-size:var(--step-h3);font-weight:700;letter-spacing:-.014em;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:'';flex:none;width:12px;height:12px;
  border-right:2px solid var(--text-accent);border-bottom:2px solid var(--text-accent);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--dur-base) var(--ease);
}
.faq details[open] summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.faq p{padding-bottom:var(--space-6);color:var(--text-secondary);max-width:70ch}
@media(prefers-reduced-motion:reduce){.faq summary::after{transition:none}}

/* ============================================================
   FOOTER — bento
   Structure taken from the Umano reference: a solid accent tile,
   two link columns, and the email address set as the largest
   thing on the page.
   ============================================================ */

.l7foot{background:var(--bg-footer);color:var(--l7-white);padding-block:var(--space-8) var(--space-6)}
/* :not(.btn) matters. Without it this rule outranks .btn-secondary and
   painted the footer CTA white-on-white in dark theme, where
   --btn-secondary-bg is also white. Caught by the rendered contrast audit. */
.l7foot a:not(.btn){color:inherit}

/* The footer is near-black in BOTH themes, so its buttons cannot read the
   theme-dependent button tokens — in light theme --btn-secondary-bg is ink,
   which would put a near-black button on a near-black footer. Pinned to the
   base ramp instead: white fill, ink label, 19.38:1, identical either way. */
.l7foot .btn-secondary{background:var(--l7-white);color:var(--l7-ink)}
.l7foot .btn-secondary:hover{background:var(--l7-gray-200)}
.l7foot .btn-ghost{color:var(--l7-white);border-color:rgba(255,255,255,.22)}
.l7foot .btn-ghost:hover{background:rgba(255,255,255,.07)}
.l7foot-grid{display:grid;gap:var(--space-4)}
@media(min-width:760px){.l7foot-grid{grid-template-columns:repeat(4,1fr)}}

.ftile{
  border-radius:var(--radius-card);padding:var(--space-6);
  border:1px solid rgba(255,255,255,.12);
  background:rgba(255,255,255,.04);
  display:flex;flex-direction:column;gap:var(--space-4);
}
.ftile .tag{color:rgba(255,255,255,.55)}
@media(min-width:760px){.ftile-wide{grid-column:span 2}}

/* the one filled tile */
.ftile-accent{background:var(--l7-blue);border-color:transparent}
.ftile-accent h3{font-size:var(--step-h3);max-width:18ch}
/* .74, not .72. On the #2424FF fill, .72 composites to 4.48:1 — an AA miss by
   0.02 on small bold mono, which does not qualify as large text. .74 is 4.67:1.
   Same in both themes, because the footer does not invert. */
.ftile-accent .tag{color:rgba(255,255,255,.74)}

.flinks{display:grid;gap:var(--space-3);margin-top:var(--space-2)}
.flinks a{color:rgba(255,255,255,.72);text-decoration:none;font-size:var(--step-sm)}
.flinks a:hover{color:var(--l7-white);text-decoration:underline}

/* the oversized address */
.fmail{
  display:block;margin-top:var(--space-6);
  font-size:clamp(1.75rem,1rem + 3.4vw,4rem);
  font-weight:700;letter-spacing:-.035em;line-height:1;
  color:var(--l7-white);text-decoration:none;
  word-break:break-word;
}
.fmail:hover{color:var(--l7-white);text-decoration:none}

.l7foot-base{
  margin-top:var(--space-7);padding-top:var(--space-5);
  border-top:1px solid rgba(255,255,255,.12);
  display:flex;flex-wrap:wrap;gap:var(--space-4);
  align-items:center;justify-content:space-between;
  font-size:var(--step-xs);color:rgba(255,255,255,.55);
}
.l7foot-social{display:flex;gap:var(--space-2)}
.l7foot-social a{
  display:grid;place-items:center;width:40px;height:40px;border-radius:50%;
  border:1px solid rgba(255,255,255,.16);color:rgba(255,255,255,.75);
}
.l7foot-social a:hover{background:rgba(255,255,255,.08);color:var(--l7-white)}
.l7foot-social svg{width:17px;height:17px;fill:currentColor}

/* Focus rings inside the footer sit on near-black in both themes,
   so they take the lifted blue rather than the page token. */
.l7foot :focus-visible{outline-color:var(--l7-white)}

/* ============================================================
   THE RIG — assembly diagram with a draggable core
   Ported from Mayank's reference file, rebuilt on our own tokens.
   Three things had to change and each was forced, not preferred:

   1. The reference is Tailwind + hard-coded #2B4BFF / #38BDF8.
      No Tailwind here and the brand is fixed, so this is plain CSS
      on our tokens.
   2. The connector lines are the CONTENT of the diagram, so they owe
      3:1 under WCAG 1.4.11. Brand #2424FF on this panel measures
      2.64:1 and fails. With one blue in the system there is no lighter
      blue to fall back to, so the lines are white — 19.99:1 on it.
   3. The reference marks "connected" in emerald. There is no green
      in this palette and inventing one is not on. White carries the
      state, and the ✓ glyph carries it too, so colour is never the
      only signal.

   The panel is near-black in BOTH themes. [OPINION] It reads as a
   device rather than a page section, which is what the design doc's
   references do. [REF] .l7foot already sets that precedent — it is
   the other surface here that does not invert. Nothing inside reads a
   theme text token, for the same reason the footer does not.
   ============================================================ */

.rig-wrap{padding-block:var(--section-y)}

/* The five categories, sitting under the diagram. Reuses .loop, so the
   01–05 numbering is the counter rather than five hand-typed numbers
   that would drift the moment one is added or removed. */
.rig-cats{margin-top:var(--space-8)}
.rig-note{
  margin-top:var(--space-6);
  font-size:var(--step-sm);color:var(--text-secondary);
  max-width:var(--wrap-narrow);
}
.rig-note strong{color:var(--text-primary);font-weight:600}

.rig{
  position:relative;
  height:clamp(420px,58vw,520px);
  border-radius:var(--radius-card);
  background:var(--l7-ink-sunk);
  border:1px solid rgba(255,255,255,.09);
  overflow:hidden;
  isolation:isolate;
  contain:layout paint;
  /* touch-action stays OFF the stage on purpose. Setting it here would
     mean a finger landing anywhere on the panel could not scroll the
     page past it. Only .rig-core opts out of touch scrolling. */
}

/* Faint technical grid, straight from the reference. Decorative only. */
.rig::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(to right, rgba(255,255,255,.025) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,.025) 1px, transparent 1px);
  background-size:32px 32px;
}
/* Ambient glow behind the core. Follows the core via --gx/--gy. */
.rig::after{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(circle at var(--gx,50%) var(--gy,58%),
    rgba(36,36,255,.30) 0%, rgba(36,36,255,.10) 34%, transparent 62%);
  transition:background var(--dur-fast) linear;
}

.rig-lines{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;overflow:visible}
/* No viewBox on purpose: without one, SVG user units are CSS pixels,
   so the JS can work in the same coordinate space as the DOM rects. */
.rig-line{
  stroke:var(--l7-white);              /* 19.99:1 on the panel */
  stroke-width:2;stroke-linecap:round;fill:none;
  stroke-dasharray:var(--len,600);
  stroke-dashoffset:var(--len,600);
}
.rig.is-wired .rig-line{
  stroke-dashoffset:0;
  transition:stroke-dashoffset .8s cubic-bezier(.4,0,.2,1);
}
/* While the core is being dragged the geometry changes every frame,
   so the draw transition has to stop or every line re-animates. */
.rig.is-dragging .rig-line{transition:none}

/* ---------- the six labels ---------- */
.rig-node{
  position:absolute;z-index:2;
  left:var(--nx);top:var(--ny);
  transform:translate(-50%,-50%) scale(.6);
  padding:var(--space-2) var(--space-4);
  border-radius:var(--radius-chip);
  background:var(--l7-ink-raised);
  border:1px solid rgba(255,255,255,.18);
  color:var(--l7-gray-300);            /* 12.42:1 on the badge */
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  letter-spacing:.08em;white-space:nowrap;
  opacity:1;
}
/* Hidden only when scripting is on, exactly like .rv — with JS blocked
   the diagram is a complete static picture rather than an empty box. */
.js .rig-node{opacity:0}
.js .rig-node.is-in{
  opacity:1;transform:translate(-50%,-50%) scale(1);
  transition:opacity .5s cubic-bezier(.16,1,.3,1),transform .5s cubic-bezier(.16,1,.3,1),border-color .3s var(--ease),box-shadow .3s var(--ease);
}
.rig-node.is-wired{border-color:rgba(255,255,255,.40);box-shadow:0 0 16px rgba(36,36,255,.22)}

/* ---------- the core ---------- */
.rig-core{
  position:absolute;z-index:3;top:0;left:0;
  width:var(--core,112px);height:var(--core,112px);
  display:grid;place-items:center;
  border-radius:50%;
  background:var(--l7-blue);
  padding:3px;
  box-shadow:0 0 60px 18px rgba(36,36,255,.42);
  cursor:grab;
  touch-action:none;
  -webkit-user-select:none;user-select:none;
  transform:translate3d(0,0,0) scale(.6);
  opacity:1;
}
.js .rig-core{opacity:0}
.js .rig-core.is-in{opacity:1;transition:opacity .5s cubic-bezier(.16,1,.3,1),transform .5s cubic-bezier(.16,1,.3,1)}
.rig-core:active,.rig.is-dragging .rig-core{cursor:grabbing}
/* No transition while dragging or the core lags a frame behind the pointer. */
.rig.is-dragging .rig-core{transition:none}
.rig-core span{
  display:grid;place-items:center;width:100%;height:100%;
  border-radius:50%;background:var(--l7-white);
  color:var(--l7-blue);                 /* 7.58:1 on white */
  font-weight:800;font-size:var(--step-sm);letter-spacing:-.02em;
}
.rig-core:focus-visible{outline:3px solid var(--l7-white);outline-offset:4px}

/* ---------- status + replay ---------- */
.rig-foot{
  position:absolute;z-index:4;left:50%;bottom:var(--space-4);
  transform:translateX(-50%);
  display:flex;align-items:center;gap:var(--space-3);
  flex-wrap:wrap;justify-content:center;width:max-content;max-width:92%;
}
.rig-status{
  margin:0;padding:var(--space-2) var(--space-4);
  border-radius:var(--radius-pill);
  background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.12);
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  color:var(--l7-gray-400);             /* 8.00:1 on the panel */
  letter-spacing:.06em;
}
.rig.is-wired .rig-status{
  color:var(--l7-white);border-color:rgba(255,255,255,.35);
  background:rgba(36,36,255,.12);
}
.rig-replay{
  min-height:44px;padding:0 var(--space-4);
  border-radius:var(--radius-pill);
  background:transparent;border:1px solid rgba(255,255,255,.22);
  color:var(--l7-white);cursor:pointer;
  font-family:var(--font-mono);font-size:var(--step-eyebrow);
  letter-spacing:.12em;text-transform:uppercase;
}
.rig-replay:hover{background:rgba(255,255,255,.08)}
.rig-replay:focus-visible{outline:3px solid var(--l7-white);outline-offset:3px}
.rig-hint{
  position:absolute;z-index:4;left:50%;top:var(--space-4);transform:translateX(-50%);
  margin:0;font-family:var(--font-mono);font-size:var(--step-eyebrow);
  color:var(--l7-gray-400);letter-spacing:.06em;text-align:center;
  opacity:0;transition:opacity var(--dur-base) var(--ease);pointer-events:none;
}
.rig.is-wired .rig-hint{opacity:1}

@media(max-width:559px){
  .rig{height:clamp(380px,92vw,460px)}
  .rig-core{--core:84px}
  .rig-node{padding:var(--space-1) var(--space-3);letter-spacing:.04em}
  .rig-hint{display:none}
}

/* Reduced motion: the diagram still assembles, it just arrives already
   assembled. Dragging keeps working — that is input, not motion. */
@media(prefers-reduced-motion:reduce){
  .js .rig-node,.js .rig-core{opacity:1;transform:translate(-50%,-50%) scale(1)}
  .js .rig-core{transform:translate3d(0,0,0)}
  .js .rig-node.is-in,.js .rig-core.is-in{transition:none}
  .rig.is-wired .rig-line{transition:none}
  .rig::after{transition:none}
}
