/* ==============================================================================================
   studio.solaryxdev.com — Solaryx Studio.

   Built on the design SYSTEM measured out of fantasy.co with sites/scripts/measure-design.mjs,
   under the precedent the operator set in S15: measured, never mirrored. What the tool returned,
   and what is implemented below:

     9 sections / 16.5 screens      -> 8 sections, and the page is short on purpose
     one sans, weight 400 dominant  -> 400 everywhere, 500 only on labels and small UI
     42px display vs 13-18px body   -> a huge jump and almost nothing between the two
     white on #000, #fff   -> exactly that; true black, not a soft near-black
     1440px container               -> --shell
     8px radius                     -> --r
     0.35s ease-out on interaction  -> --t-fast
     cubic-bezier(.19,1,.22,1) 0.75-1s on reveals -> --ease-expo, --t-reveal
     one fixed "Menu" control       -> .bar__menu + .menu overlay
     media-led sections             -> .media everywhere, video-ready

   NOT taken: their typeface, their markup, their copy, and their red. S15 settled that an agency's
   accent colour stays theirs; ours is brass, used as sparsely as they use red.
   ============================================================================================== */

/* POPPINS, SIL OFL 1.1, self-hosted. Measured off the reference the operator named: its headline
   computes to Poppins 600, and it self-hosts the same four latin woff2 files rather than linking a
   CDN. That is also our rule (LG Muenchen I, 3 O 17493/20) and the OFL ships with a client build
   with nobody doing anything — which is why this also retires the Cairoli Now purchase.

   Four static cuts, not a variable file: Poppins has no variable release, and the latin subsets are
   ~8KB each, so all four together cost less than one variable face. OFL.txt ships beside them. */
@font-face {
  font-family: Poppins;
  src: url('fonts/Poppins-400.e20c304d49.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Poppins;
  src: url('fonts/Poppins-500.0593996c5b.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Poppins;
  src: url('fonts/Poppins-600.b3f3a0bb95.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Poppins;
  src: url('fonts/Poppins-700.b9f853f622.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

:root {
  /* BLACK AND WHITE, and nothing else — operator, 2026-09-04. Supersedes "Mist Over Concrete"
     and the brass before it.

     The only derivation permitted is BLACK OR WHITE AT AN ALPHA, for rule-lines, scrims and the
     one step of type hierarchy below full strength. No third hue exists in this file. This also
     ends the contrast problem the previous palette had: #000 on #fff is 21:1, so every size and
     weight on the page clears WCAG AAA with room to spare. */
  --bg: #000;
  --bg-2: #000;
  --ink: #fff;
  --ink-dim: rgba(255, 255, 255, .78);
  --ink-faint: rgba(255, 255, 255, .55);
  --line: rgba(255, 255, 255, .26);
  --line-soft: rgba(255, 255, 255, .13);
  --accent: #fff;
  --accent-ink: #000;

  --shell: min(1440px, calc(100vw - 8vw));
  --r: 8px;
  --r-sm: 2px;
  /* THE BOX RADIUS. Operator, 2026-09-04: "every box should be like that curved, no pills". 22px
     on anything that reads as a panel — media frames, the black showcase, the stepper, the cards.
     Never a pill: a fully rounded end (999px) is banned on this page, which is why no rule below
     sets one and why the nav CTA is a rounded rectangle and not a lozenge. */
  --r-box: 22px;

  /* The measured jump: a display size with nothing between it and the body. Resisting the urge to
     add a 24px and a 32px is most of what makes this look like the reference rather than like a
     bootstrap page wearing its colours. */
  /* MEASURED OFF THE LIVE, MOUNTED PAGE — 90px x58, 40px x83, 18px x24, 12px x57, at weight 500.
     The first measurement was taken at domcontentloaded, before the app rendered, and returned
     "42px" and a font-family of "sans"; both were artefacts of measuring an unmounted page. Four
     sizes, a hard jump between each, and nothing in between: that gap is the look. */
  --fs-xl: clamp(3.2rem, 9vw, 5.625rem);    /* 90px */
  --fs-lg: clamp(2.1rem, 4.4vw, 2.5rem);    /* 40px */
  --fs-display: var(--fs-lg);               /* the same step as --fs-lg, named for its job */
  --fs-sm-display: clamp(1.35rem, 2vw, 1.6rem);  /* 25.6px — section sub, card title, question */
  --fs-lead: clamp(1.125rem, 1.5vw, 1.35rem);    /* 21.6px — the lead paragraph and a card name */
  --fs-body: 1.125rem;                      /* 18px */
  --fs-small: .875rem;
  --fs-label: .75rem;                       /* 12px */

  --t-fast: .35s;
  --t-reveal: .9s;
  --ease-out: cubic-bezier(.33, 1, .68, 1);
  --ease-expo: cubic-bezier(.19, 1, .22, 1);

  /* One family, the reference's own stack order behind it. */
  --font-display: Poppins, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-text: Poppins, "Helvetica Neue", Helvetica, Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; }
h1, h2, h3, p, ol, ul, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, video { max-width: 100%; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

::selection { background: var(--accent); color: var(--accent-ink); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-sm); }

/* The loading veil. Removed once the hero image has decoded, so the first frame is never a
   half-painted page. */
body.loading { overflow: hidden; }
body.loading::before {
  content: ''; position: fixed; inset: 0; z-index: 9999; background: var(--bg);
}

/* --- type ------------------------------------------------------------------------------------ */
.display {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-display);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -.03em;
  text-wrap: balance;
}
h2.display, h2.display--lg, h2.display--sm { margin: 0; font-weight: inherit; }
.display--xl { font-size: var(--fs-xl); line-height: 1; letter-spacing: -.035em; }
.display--lg { font-size: var(--fs-lg); line-height: 1; letter-spacing: -.04em; }
.display--sm { font-size: var(--fs-sm-display); }

.label {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.prose { color: var(--ink-dim); max-width: 58ch; }
.prose + .prose { margin-top: 1.1rem; }
.prose strong { color: var(--ink); font-weight: 500; }
.prose--lg { font-size: var(--fs-lead); max-width: 46ch; }

.link { color: var(--accent); text-underline-offset: .22em; }
.link:hover { color: var(--ink); }

.btn {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .85rem 1.4rem;
  border-radius: var(--r);
  font-size: var(--fs-small); font-weight: 500;
  text-decoration: none; white-space: nowrap;
  border: 1px solid transparent;
  transition: background var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out);
}
.btn--ghost { border-color: var(--line); color: var(--ink); }
.btn--ghost:hover { border-color: var(--ink); }
.btn--solid { background: var(--accent); color: var(--accent-ink); }
.btn--solid:hover { background: #fff; }

/* --- the bar and the one menu ---------------------------------------------------------------
   A single fixed control. The reference carries the word "Menu" and nothing else, which is the
   point: no link list arguing with the page. Rendered outside any transformed ancestor — that is
   what broke the previous fixed header, and Lenis (unlike ScrollSmoother) transforms nothing. */
.bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1000;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.4rem clamp(1.1rem, 4vw, 2.6rem);
  mix-blend-mode: difference;
  pointer-events: none;
}
.bar > * { pointer-events: auto; }
.bar__mark {
  font-family: var(--font-display);
  font-size: var(--fs-small); font-weight: 500; letter-spacing: -.02em;
  text-decoration: none; white-space: nowrap;
}
.bar__mark span { opacity: .55; font-weight: 400; }

.bar__menu {
  display: inline-flex; align-items: center; gap: .7rem;
  font-size: var(--fs-small); font-weight: 500; letter-spacing: .01em;
  padding: .2rem 0;
}
.bar__menu-glyph { display: inline-block; width: 22px; }
.bar__menu-glyph i {
  display: block; height: 1px; background: currentColor;
  transition: transform var(--t-fast) var(--ease-out);
}
.bar__menu-glyph i + i { margin-top: 6px; }
.bar__menu[aria-expanded="true"] .bar__menu-glyph i:first-child { transform: translateY(3.5px) rotate(45deg); }
.bar__menu[aria-expanded="true"] .bar__menu-glyph i + i { transform: translateY(-3.5px) rotate(-45deg); }

.menu {
  position: fixed; inset: 0; z-index: 999;
  background: var(--bg);
  display: grid; align-items: center;
  padding: 6rem clamp(1.1rem, 4vw, 2.6rem) 3rem;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease-out);
}
.menu[hidden] { display: none; }
.menu.is-open { opacity: 1; }
.menu__inner { width: var(--shell); margin: 0 auto; }
.menu__list li + li { border-top: 1px solid var(--line-soft); }
.menu__list a {
  display: flex; align-items: baseline; gap: 1.4rem;
  padding: clamp(.9rem, 2.4vw, 1.6rem) 0;
  font-family: var(--font-display);
  font-size: var(--fs-lg); font-weight: 400; letter-spacing: -.04em;
  text-decoration: none;
  opacity: .55;
  transition: opacity var(--t-fast) var(--ease-out);
}
.menu__list a:hover { opacity: 1; }
.menu__n { font-family: var(--font-text); font-size: var(--fs-label); letter-spacing: .1em; color: var(--accent); }
.menu__meta {
  margin-top: 3rem; display: flex; flex-wrap: wrap; gap: 1.5rem;
  font-size: var(--fs-small); color: var(--ink-faint);
}
.menu__meta a { color: var(--ink-dim); text-decoration: none; }
.menu__meta a:hover { color: var(--ink); }

/* --- media ------------------------------------------------------------------------------------
   One rule for every media slot, so a video dropped in later needs no new CSS. */
.media { position: relative; overflow: hidden; border-radius: var(--r); background: var(--bg-2); }
.media__el { width: 100%; height: 100%; object-fit: cover; }

/* --- sections --------------------------------------------------------------------------------- */
/* The lede wraps the first two sections to give them the light ground, so they are grandchildren
   of <main> and this rule stopped matching them: the label "WHAT WE RUN" sat hard against the
   viewport edge at x=0 while every section below it was correctly inset. */
main > section,
main > .lede > section {
  width: var(--shell);
  margin: 0 auto;
  padding: clamp(5rem, 14vh, 11rem) 0;
}

/* --- media ------------------------------------------------------------------------------------
   One rule for every media slot, so a video dropped in later needs no new CSS. */
.media { position: relative; overflow: hidden; border-radius: var(--r); background: var(--bg-2); }
.media__el { width: 100%; height: 100%; object-fit: cover; }

/* --- sections --------------------------------------------------------------------------------- */
/* The lede wraps the first two sections to give them the light ground, so they are grandchildren
   of <main> and this rule stopped matching them: the label "WHAT WE RUN" sat hard against the
   viewport edge at x=0 while every section below it was correctly inset. */
main > section,
main > .lede > section {
  width: var(--shell);
  margin: 0 auto;
  padding: clamp(5rem, 14vh, 11rem) 0;
}

/* Statement rows: text and media side by side, alternating. */
.say { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.say__media { aspect-ratio: 4 / 3; }
@media (min-width: 56em) {
  .say { grid-template-columns: 1.15fr 1fr; }
  .say--flip .say__media { order: -1; }
}

/* The quiet stretch: three numbered rows, no media, small type. Measured at 18px on the reference
   and it is the contrast with the display sizes either side that does the work. */
.caps { display: grid; gap: 0; border-top: 1px solid var(--line-soft); }
.cap {
  display: grid; gap: .6rem 3rem;
  padding: clamp(1.8rem, 4vw, 3rem) 0;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 56em) {
  .cap { grid-template-columns: 5rem 16rem minmax(0, 1fr); align-items: start; gap: 0 3rem; }
}
.cap__n { font-size: var(--fs-label); font-weight: 500; letter-spacing: .1em; color: var(--accent); padding-top: .35em; }
.cap__title { font-family: var(--font-display); font-size: var(--fs-body); font-weight: 500; letter-spacing: -.02em; }
.cap__body { color: var(--ink-dim); max-width: 52ch; }

.proof { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.proof__media { aspect-ratio: 3 / 4; }
@media (min-width: 56em) { .proof { grid-template-columns: 1fr 1.2fr; } }
.proof__text .label { display: block; margin-bottom: 1.2rem; }
.proof__text .display { margin-bottom: 1.6rem; }

/* --- pricing ---------------------------------------------------------------------------------- */
.pricing__head { max-width: 58ch; }
.pricing__head .label { display: block; margin-bottom: 1.2rem; }
.pricing__head .display { margin-bottom: 1.2rem; }
.tiers { margin-top: clamp(2.5rem, 6vw, 4rem); display: grid; gap: 1rem; }
@media (min-width: 56em) { .tiers { grid-template-columns: 1fr 1fr; gap: 1.25rem; } }

.tier {
  border: 1px solid var(--line-soft);
  border-radius: var(--r);
  padding: clamp(1.6rem, 3vw, 2.5rem);
  background: var(--bg-2);
  display: flex; flex-direction: column; gap: 1.4rem;
}
.tier--lead { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.tier__top { display: flex; align-items: center; gap: .8rem; }
.tier__name { font-family: var(--font-display); font-size: var(--fs-lead); font-weight: 500; letter-spacing: -.02em; }
.tier__flag {
  font-size: var(--fs-label); font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
  background: var(--accent); color: var(--accent-ink);
  padding: .3rem .55rem; border-radius: var(--r-sm);
}
.tier__price { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.tier__amount {
  font-family: var(--font-display);
  font-size: var(--fs-lg); font-weight: 400; letter-spacing: -.045em; line-height: 1;
}
.tier__per { font-size: var(--fs-small); font-weight: 500; color: var(--ink-dim); }
.tier__vat { flex-basis: 100%; font-size: var(--fs-label); font-weight: 500; color: var(--ink-faint); }
.tier__for { color: var(--ink-dim); }
.tier__list { display: grid; gap: .6rem; font-size: var(--fs-small); color: var(--ink-dim); }
.tier__list li { display: flex; gap: .7rem; }
.tier__list li::before { content: '—'; color: var(--accent); }
.tier__list li.is-off { color: var(--ink-faint); }
.tier__list li.is-off::before { content: '×'; color: var(--ink-faint); }
.tier__list strong { color: var(--ink); font-weight: 500; }
.tier__cta { margin-top: auto; }

.setup-note {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.6rem, 3vw, 2.2rem);
  border-top: 1px solid var(--line-soft);
  font-size: var(--fs-small); color: var(--ink-faint);
  display: grid; gap: .9rem; max-width: 76ch;
}
.setup-note strong { color: var(--ink-dim); font-weight: 500; }

/* --- closing + ai ----------------------------------------------------------------------------- */

.ai { display: grid; gap: 1.4rem 3rem; }
@media (min-width: 56em) { .ai { grid-template-columns: 12rem minmax(0, 1fr); } }
.ai .display { margin-bottom: 1.4rem; }

/* --- footer ------------------------------------------------------------------------------------ */
.foot { border-top: 1px solid var(--line-soft); }
.foot__top {
  width: var(--shell); margin: 0 auto;
  padding: clamp(4rem, 12vh, 9rem) 0 clamp(2.5rem, 6vh, 4rem);
}
.foot__lead {
  font-family: var(--font-display);
  font-size: var(--fs-lg); font-weight: 400; letter-spacing: -.04em; line-height: 1;
}
.foot__prose { margin-top: 1.2rem; color: var(--ink-dim); max-width: 46ch; }
.foot__cta {
  display: inline-block; margin-top: 2rem;
  font-family: var(--font-display);
  font-size: var(--fs-sm-display); font-weight: 400; letter-spacing: -.03em;
  color: var(--accent); text-underline-offset: .2em;
}
.foot__bar {
  width: var(--shell); margin: 0 auto;
  padding: 1.6rem 0 2.4rem;
  border-top: 1px solid var(--line-soft);
  display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between;
  font-size: var(--fs-label); font-weight: 500; color: var(--ink-faint);
}
.foot__links { display: flex; flex-wrap: wrap; gap: 1.4rem; }
.foot__links a { text-decoration: none; }
.foot__links a:hover { color: var(--ink); }

/* --- legal pages -------------------------------------------------------------------------------- */
.legal {
  width: min(72ch, calc(100vw - 8vw));
  margin: 0 auto;
  padding: clamp(7rem, 18vh, 11rem) 0 clamp(4rem, 10vh, 7rem);
  color: var(--ink-dim);
}
.legal h1 {
  font-family: var(--font-display);
  font-size: var(--fs-lg); font-weight: 400; letter-spacing: -.04em;
  color: var(--ink); margin-bottom: 1.6rem;
}
.legal h2 {
  font-family: var(--font-display);
  font-size: var(--fs-body); font-weight: 500; color: var(--ink);
  margin: 2.4rem 0 .7rem;
}
.legal p { margin-bottom: .9rem; }
.legal a { color: var(--accent); }
.legal table { width: 100%; border-collapse: collapse; margin: 1rem 0 1.5rem; font-size: var(--fs-small); }
.legal th, .legal td { text-align: left; padding: .6rem .8rem; border: 1px solid var(--line-soft); vertical-align: top; }
.legal th { color: var(--ink); font-weight: 500; white-space: nowrap; }

/* --- reveal --------------------------------------------------------------------------------------
   The rest state is applied by JS (`.is-armed`), never by static CSS. If the script never runs, the
   page is simply visible — a reveal that can hide your own copy on failure is not a reveal, it is a
   bug with a timeline. Learned the hard way; see ui/LESSONS.md L13. */
[data-reveal].is-armed > *,
[data-reveal].is-armed .media { opacity: 0; }
.rv-word { display: inline-block; will-change: transform, opacity; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal].is-armed > *,
  [data-reveal].is-armed .media { opacity: 1 !important; transform: none !important; }
  .rv-word { opacity: 1 !important; transform: none !important; }
}


/* ==============================================================================================
   PINNED SECTIONS — the scroll mechanic, 2026-09-03.

   This is the thing the first rebuild missed. The reference does not scroll sections past the
   viewport; it PINS them. Measured on the mounted page: `position: sticky; top: 0` children at
   1229px (one viewport) inside parents of 4,422px and 6,924px — 3.6 and 5.6 viewports. The section
   holds the screen while its contents move through it, which is what "each part opens" means.

   Done with sticky rather than a GSAP pin on purpose: sticky needs no JavaScript, cannot desync
   from the scroll position, and leaves nothing broken if the engine fails to load. GSAP only drives
   what happens INSIDE the frame.
   ============================================================================================== */
.pin {
  width: 100% !important;
  max-width: none;
  padding: 0 !important;
  position: relative;
}
/* Tuned against the reference, which measures 13.5 screens end to end. At 300/400 ours came out at
   20.6 — the mechanic was right and the dwell was too long, which reads as the page not moving. */
.pin--3 { height: 175vh; }
.pin--4 { height: 225vh; }

.pin__inner {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 32rem;
  display: grid;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 4rem);
  width: var(--shell);
  margin: 0 auto;
  padding: clamp(4rem, 10vh, 7rem) 0;
  overflow: hidden;
}
@media (min-width: 56em) {
  .pin__inner { grid-template-columns: 1fr 1fr; }
  .caps .pin__inner, .closing .pin__inner { grid-template-columns: 1fr; }
}

.pin__media { height: min(62vh, 34rem); }
.pin__text { display: grid; gap: 1.2rem; align-content: center; }
.pin__text .label { margin-bottom: .4rem; }

/* ==============================================================================================
   THE PRELOADER, THE TRANSITION MASK, AND THE BACKGROUND LAYER — 2026-09-03

   All three measured on the reference's mounted page:
     .fixed.inset-0.bg-black.z-[9999]  holding an SVG logo whose letters GSAP moves into place,
                                       present ~215ms to ~1,800ms
     .js-t-mask  fixed inset-0 z-[11] bg-black invisible   — the route-change wipe
     .site-background  fixed inset-x-0 -inset-y-200        — a full-bleed layer behind everything
   ============================================================================================== */

.preload {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--bg);
  display: grid;
  place-items: center;
  gap: 1.6rem;
  align-content: center;

  /* THE EXIT IS GUARANTEED BY CSS, NOT BY JAVASCRIPT. If app.js never runs — blocked, failed,
     errored — this still clears itself at 2.6s. A preloader that depends on a script to leave is a
     black page for anyone whose script did not arrive, and that is the worst failure a site has. */
  animation: preload-out .7s var(--ease-expo) 2.6s forwards;
}
/* The script removes it sooner, the moment the hero image has actually decoded. */
.preload.is-done { animation: preload-out .7s var(--ease-expo) forwards; }

@keyframes preload-out {
  to { opacity: 0; visibility: hidden; transform: translate3d(0, -1.5rem, 0); }
}

.preload__mark {
  font-family: var(--font-display);
  font-size: var(--fs-sm-display);
  font-weight: 500;
  letter-spacing: -.03em;
  display: flex;
  gap: .4em;
}
/* One wordmark, one weight — "Solaryx Studio is together written". It was set as two words with the
   second dimmed, which reads as two things. */
/* Each letter is wrapped by the script and staggered in. Without the script the words are simply
   there, which is why the resting state is visible rather than hidden. */
.preload__ltr { display: inline-block; }

.preload__bar {
  width: min(11rem, 40vw);
  height: 1px;
  background: var(--line);
  overflow: hidden;
}
.preload__bar i {
  display: block; height: 100%; width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  /* 1.5s, matched to the minimum hold in app.js. It was 2.4s against a 1.5s exit, so the bar was
     yanked off screen at 82% — a progress indicator that never reaches the end is worse than none,
     because it reads as the page having given up. */
  animation: preload-bar 1.5s var(--ease-out) forwards;
}
@keyframes preload-bar { to { transform: scaleX(1); } }

/* The route-change wipe. Invisible until a navigation asks for it. */
.t-mask {
  position: fixed; inset: 0; z-index: 900;
  background: var(--bg);
  transform: translate3d(0, 100%, 0);
  pointer-events: none;
  visibility: hidden;
}
.t-mask.is-on { visibility: visible; }

/* A full-bleed layer behind everything, moved against the scroll so the page has depth even where
   there is no media. Deliberately subtle — it is a ground, not an effect. */
.site-bg {
  position: fixed;
  inset: -20vh 0;
  z-index: -1;
  background:
    radial-gradient(60% 40% at 70% 10%, rgba(255, 255, 255, .08), transparent 70%),
    radial-gradient(50% 40% at 15% 85%, rgba(255, 255, 255, .05), transparent 70%);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .preload { animation: preload-out .01s linear .2s forwards; }
  .preload__bar i { animation: none; transform: scaleX(1); }
  .site-bg { display: none; }
}

/* ==============================================================================================
   THE SHOT SEQUENCE — 2026-09-03, to the operator's order:
     strip -> big picture that shrinks -> grid with text that arrives, leaves and darkens ->
     scrolling text over pictures changing behind on a fade
   ============================================================================================== */

/* --- the tool bar, and the components from our own library --------------------------------------
   react-bits/LogoLoop and react-bits/TextType. The <ul> inside the mount point is the complete
   list, so the section is finished before the island loads and stays finished if it never does. */
.tt { font-variant-numeric: tabular-nums; }
.tt__cursor { color: var(--accent); font-weight: 400; }

/* react-bits/LogoLoop carries the strip. Its own markup is absolutely positioned, so the mount
   point needs a height; the <ul> inside it is the fallback and is replaced when the island mounts. */
.tools__loop { min-height: 3.2rem; }
.tools__loop .mark {
  font-family: var(--font-display);
  font-size: var(--fs-sm-display);
  font-weight: 500;
  letter-spacing: -.02em;
  color: var(--ink-faint);
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease-out);
}
.tools__loop .mark:hover { color: var(--ink); }


/* ==============================================================================================
   THE 2026-09-04 LANDING — the operator's beat order:
     opener that expands on scroll with the line arriving on it -> a moving bar of the tools we run
     -> a black panel at half width that opens to full with a fanned gallery inside -> text and a
     sliced strip -> text and a mosaic -> pricing -> a stepper.

   Every panel on this page is CURVED and none of them is a pill. `--r-box` is the only radius that
   belongs on a box; `border-radius: 999px` appears nowhere by rule.
   ============================================================================================== */

/* --- the display scale, one step larger than the 2026-09-03 page ---------------------------------
   The reference grammar runs the headline far past a normal h1 — measured near 9rem on a 1440
   viewport against 18px body, with almost nothing in between. `--fs-xl` topped out at 90px, which
   read as a large heading rather than as the page's one loud element. */
:root { --fs-hero: clamp(3.2rem, 10.5vw, 8.25rem); }
.display--hero {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  line-height: .9;          /* measured on the reference: 80px type on a 72px line */
  letter-spacing: -.035em;  /* Poppins is geometric and wide; -.045em closed it up too far */
  font-weight: 600;
  text-wrap: balance;
}

/* --- 1 + 2 · the lede, on a light ground -----------------------------------------------------
   The page opens light and turns dark at the showcase. That is not decoration: the grammar this
   beat is measured from puts a very large near-black headline on a near-white ground with the
   media as the only saturated object in the frame, and the contrast IS the mechanism. Inverted onto
   our black ground it disappeared — the werkstatt photographs are deliberately dark, so a dark
   panel on black is a black rectangle.

   Tokens are redefined on `.lede` rather than swapped globally, so every rule written against
   --ink / --line / --bg-2 keeps working inside it with no per-rule light variant. */
/* The fixed bar needs no light variant: it already carries `mix-blend-mode: difference`, so it
   inverts against whatever is under it and renders near-black over the lede on its own. */
.lede {
  --bg: #fff;
  --bg-2: #fff;
  --ink: #000;
  --ink-dim: rgba(0, 0, 0, .74);
  --ink-faint: rgba(0, 0, 0, .56);   /* .52 measured 4.27:1 at 12px — under AA. This is 4.94:1. */
  --line: rgba(0, 0, 0, .22);
  --line-soft: rgba(0, 0, 0, .11);
  --accent: #000;
  --accent-ink: #fff;
  background: var(--bg);
  color: var(--ink);
  position: relative;
  z-index: 1;
}

.opener { position: relative; padding-top: clamp(7rem, 16vh, 11rem); }
.opener__text {
  display: grid; gap: 1.1rem; justify-items: center; text-align: center;
  max-width: 60rem; margin-inline: auto;
  padding-bottom: clamp(2.5rem, 7vh, 5rem);
}
.opener__text .label { color: var(--ink-faint); }
.opener__text .display--hero { margin: 0; color: var(--ink); }
.opener__sub { color: var(--ink-dim); max-width: 38rem; margin: 0; }

/* The media block below the headline. Curved hard — this is the box the operator's no-pill rule is
   really about — and it is what ScrollExpand takes over and opens on scroll. */
.opener__stage { position: relative; width: 100%; }
.opener__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-box);
  overflow: hidden;
  background: var(--bg-2);
}
/* The line that rides the opening panel. Hidden until the island moves it into the overlay: on the
   no-JavaScript page it would otherwise sit under the picture with nothing to explain it. */
.opener__over {
  position: absolute; inset: auto 0 2rem; margin: 0; padding: 0 6%;
  text-align: center; color: #fff; text-shadow: 0 2px 30px rgba(0, 0, 0, .7);
}
.sx-se__overlay .opener__over { position: static; padding: 0; }
.opener__hint {
  display: block; text-align: center; margin-top: 1.4rem;
  font-size: var(--fs-label); font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint);
}

/* --- 2 · the tool bar -------------------------------------------------------------------------- */
.tools { display: grid; gap: 1.6rem; }
.tools__row {
  display: flex; flex-wrap: wrap; gap: .9rem 2.6rem; align-items: baseline;
  list-style: none; margin: 0; padding: 0;
}
.tools__row li { display: grid; gap: .1rem; }
.tools__name {
  font-family: var(--font-display); font-size: var(--fs-lead);
  font-weight: 500; letter-spacing: -.02em; color: var(--ink-dim);
}
.tools__note { font-size: var(--fs-label); font-weight: 500; color: var(--ink-faint); }
/* LogoLoop's own markup is absolutely positioned, so the mount point needs a height of its own. */
.tools__loop {
  min-height: 4.2rem;
  /* min-width:0 is load-bearing. `.tools` is a grid and a grid item defaults to min-width:auto,
     which lets it size to its content instead of its track. LogoLoop's track is `width:max-content`,
     so the item grew to fit the track, the component measured the grown item as its container,
     asked for enough copies to fill it, and the track grew again — a runaway that settled at
     1,686,445px wide and 8,250 <li> in the DOM. Measured, not theorised. */
  min-width: 0;
}
.tools__loop .mark {
  display: block;
  font-family: var(--font-display); font-size: var(--fs-lead);
  font-weight: 500; letter-spacing: -.02em; color: var(--ink-dim); white-space: nowrap;
  transition: color var(--t-fast) var(--ease-out);
}
.tools__loop .mark__note {
  display: block; font-family: var(--font-text); font-size: var(--fs-label);
  font-weight: 500; color: var(--ink-faint); white-space: nowrap;
}
.tools__loop a:hover .mark, .tools__loop .mark:hover { color: var(--ink); }

/* --- 3 · the black panel, and the fan inside it -------------------------------------------------- */
.showcase {
  position: relative; display: grid; gap: clamp(2rem, 5vw, 3.5rem);
  /* Where the page turns dark. The curve at the top edge is the seam between the light lede and
     the black half — the operator's "box of black windows" arriving as a panel over the lede. */
  background: var(--bg);
  border-radius: var(--r-box) var(--r-box) 0 0;
  margin-top: clamp(3rem, 8vh, 6rem);
  padding-top: clamp(3rem, 9vh, 7rem);
}
.showcase__head { display: grid; gap: .9rem; max-width: 34rem; }
/* Half the screen wide and low in its section BEFORE any script runs, so the beat is already
   what the operator asked for when the island is only enhancing it. */
.showcase__panel {
  position: relative;
  width: min(50%, 46rem);
  margin-inline: auto;
  margin-top: clamp(2rem, 8vh, 6rem);
  aspect-ratio: 16 / 10;
  border-radius: var(--r-box);
  overflow: hidden;
  background: var(--bg-2);
}
.showcase__blur { position: relative; height: 0; }
/* Once the island owns the panel it must be free to open to full bleed; the width above is the
   no-script size. `aspect-ratio` goes too — ScrollExpand's sticky stage sets its own height. */
/* The panel's own ground. Concrete, the darkest of the five. */
.showcase__panel.is-live .sx-se__frame { background: #000; }
.showcase__panel.is-live {
  width: 100%; max-width: none; margin-top: 0;
  aspect-ratio: auto; background: none; border-radius: 0; overflow: visible;
}

/* THE FAN. Five photographs, centre largest, falling away by distance from the middle. Sized in
   percentages of the panel so it holds its proportions while the panel is expanding — a fan sized
   in pixels would jump the moment the frame changed width. */
.fan {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(.5rem, 1.4vw, 1.1rem);
  width: 100%; height: 100%; padding: 0 4%;
}
.fan__item { margin: 0; border-radius: var(--r); overflow: hidden; flex: 0 0 auto; }
.fan__item img, .fan__item .media__el { width: 100%; height: 100%; object-fit: cover; display: block; }
.fan__item--0, .fan__item--4 { width: 11%; aspect-ratio: 2 / 3; opacity: .55; }
.fan__item--1, .fan__item--3 { width: 20%; aspect-ratio: 3 / 4; opacity: .8; }
.fan__item--2 { width: 30%; aspect-ratio: 4 / 5; opacity: 1; }

/* --- 4 · text, then the sliced strip -------------------------------------------------------------
   Six panels closed as one band, drawing apart on scrub. The transform is driven from app.js and
   every panel moves on `transform` only — DOCTRINE 347. */
.slices { display: grid; gap: clamp(2.5rem, 6vw, 4.5rem); }
.slices__text { display: grid; gap: 1rem; max-width: 46rem; }
.slices__text .prose { color: var(--ink-dim); }
.strip6 {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  /* Static. The panels move over these; nothing animates a gap or a radius, so the whole beat is
     transform-only and never costs a layout (DOCTRINE 347). */
  gap: clamp(.4rem, 1vw, .9rem);
  height: min(62vh, 34rem);
  border-radius: var(--r-box);
  overflow: hidden;
}
.strip6__panel {
  position: relative; height: 100%; overflow: hidden;
  border-radius: var(--r);
  will-change: transform;
}
.strip6__panel .media__el { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- 5 · text, then the mosaic -------------------------------------------------------------------- */
.mosaic-sec { display: grid; gap: clamp(2.5rem, 6vw, 4.5rem); }
.mosaic-sec__text { display: grid; gap: 1.4rem; max-width: 52rem; }
.points { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 1.6rem; }
.point { display: grid; gap: .35rem; grid-template-columns: 3rem minmax(0, 1fr); align-items: start; }
.point__n { font-size: var(--fs-label); font-weight: 500; color: var(--accent); grid-row: span 2; }
.point__title { font-family: var(--font-display); font-size: var(--fs-lead); font-weight: 500; margin: 0; }
.point__body { color: var(--ink-dim); margin: 0; }

.mosaic {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(.5rem, 1.2vw, 1rem);
}
.mosaic__cell {
  position: relative; overflow: hidden;
  border-radius: var(--r-box);
  aspect-ratio: 4 / 3;
  will-change: transform;
}
.mosaic__cell .media__el { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The centre cell is taller and is the one react-bits/PixelSwap takes over. */
.mosaic__cell--mid { grid-row: span 2; aspect-ratio: 3 / 4; }
/* The second photograph is emitted so the build can credit it, then hidden: PixelSwap renders its
   own copy. Hidden with `display: none` rather than removed, because with no JavaScript there is
   no swap and a second picture stacked under the first would be a layout bug. */
.mosaic__swap { display: none; }

/* --- 8 · the stepper ---------------------------------------------------------------------------- */
.signup { display: grid; gap: clamp(2rem, 5vw, 3rem); }
.signup__head { display: grid; gap: .9rem; max-width: 40rem; }
.signup__box {
  border: 1px solid var(--line);
  border-radius: var(--r-box);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  background: var(--bg-2);
  max-width: 44rem;
}
/* The no-JavaScript path: the four questions as a list, and the mailto the rest of the page uses.
   Never a dead end. */
.signup__fallback { list-style: none; margin: 0 0 1.5rem; padding: 0; display: grid; gap: 1rem; }
.signup__fallback li { display: grid; gap: .2rem; }
.signup__fallback strong { font-weight: 500; }
.signup__fallback span { color: var(--ink-faint); font-size: var(--fs-small); }
.signup__cta { color: var(--accent); text-decoration: none; border-bottom: 1px solid currentColor; }

.stepper { display: grid; gap: 1.6rem; }
.stepper__rail { display: flex; align-items: center; list-style: none; margin: 0; padding: 0; }
.stepper__railitem { display: flex; align-items: center; flex: 0 0 auto; }
.stepper__railitem:not(:last-child) { flex: 1 1 auto; }
/* Rounded squares, never circles — the page's no-pill rule holds down to a 2rem control. */
.stepper__dot {
  width: 2rem; height: 2rem; flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: var(--r);
  border: 1px solid var(--line);
  background: transparent; color: var(--ink-faint);
  font-size: var(--fs-label); font-weight: 500; font-variant-numeric: tabular-nums;
  transition: color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out),
              background-color var(--t-fast) var(--ease-out);
}
.stepper__dot--current { color: var(--bg); background: var(--accent); border-color: var(--accent); }
.stepper__dot--complete { color: var(--accent); border-color: var(--accent); }
.stepper__line { flex: 1 1 auto; height: 1px; background: var(--line); margin: 0 .6rem; position: relative; }
.stepper__line::after {
  content: ''; position: absolute; inset: 0; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-reveal) var(--ease-expo);
}
.stepper__line.is-done::after { transform: scaleX(1); }

/* The height move. `height` is transitioned here rather than transform, deliberately and as the
   documented exception to DOCTRINE 347: the footer sits below this box in flow and a transform
   would slide it over the buttons instead of moving them. One property, one element, measured. */
.stepper__pane { overflow: hidden; transition: height .4s var(--ease-out); }
.stepper__step {
  transition: transform .4s var(--ease-out), opacity .4s var(--ease-out);
  will-change: transform, opacity;
}
.stepper__step.is-from-right { transform: translate3d(2.5rem, 0, 0); opacity: 0; }
.stepper__step.is-from-left  { transform: translate3d(-2.5rem, 0, 0); opacity: 0; }

.stepper__foot { display: flex; gap: .8rem; align-items: center; }
.stepper__back, .stepper__next {
  font: inherit; font-size: var(--fs-small); font-weight: 500;
  border-radius: var(--r); padding: .7rem 1.3rem; cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out),
              border-color var(--t-fast) var(--ease-out);
}
.stepper__back { background: transparent; border: 1px solid var(--line); color: var(--ink-dim); }
.stepper__back:hover { border-color: var(--ink-faint); color: var(--ink); }
.stepper__next { background: var(--accent); border: 1px solid var(--accent); color: var(--accent-ink); margin-left: auto; }
.stepper__next:hover { background: var(--ink); border-color: var(--ink); }

.q { display: grid; gap: .5rem; padding-bottom: .4rem; }
.q__label { font-family: var(--font-display); font-size: var(--fs-sm-display); font-weight: 500; letter-spacing: -.02em; }
.q__hint { color: var(--ink-faint); font-size: var(--fs-small); margin: 0; }
.q__input {
  font: inherit; color: var(--ink); background: transparent;
  border: 1px solid var(--line); border-radius: var(--r);
  padding: .8rem 1rem; margin-top: .4rem; width: 100%;
}
.q__input::placeholder { color: var(--ink-faint); }
.q__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: transparent; }
.q__opts { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .4rem; }
.q__opt {
  font: inherit; font-size: var(--fs-small); font-weight: 500;
  background: transparent; color: var(--ink-dim);
  border: 1px solid var(--line); border-radius: var(--r);
  padding: .6rem 1.1rem; cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.q__opt:hover { color: var(--ink); border-color: var(--ink-faint); }
.q__opt[data-on="true"] { color: var(--accent); border-color: var(--accent); }
.signup__sent { color: var(--ink-dim); margin-top: 1rem; }

/* ==============================================================================================
   THE PORTED COMPONENT CLASSES.

   react-bits ships these components styled in Tailwind. This page has no Tailwind and is not going
   to acquire one for seven components, so their layout classes were replaced at the source with the
   `sx-*` names below. Every rule here is a straight translation of the utility string it replaced —
   nothing was redesigned in the move, so a future version of a component can be re-ported by
   reading its diff rather than by re-deriving what these do.
   ============================================================================================== */

/* react-bits/ScrollExpand */
.sx-se { position: relative; width: 100%; height: 100%; }
.sx-se--scroll { overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-width: none; -ms-overflow-style: none; }
.sx-se--scroll::-webkit-scrollbar { display: none; }
.sx-se__track { position: relative; width: 100%; }
.sx-se__stage { position: sticky; top: 0; width: 100%; overflow: hidden; }
.sx-se__frame { position: absolute; inset: 0; clip-path: inset(21% 29% 21% 29% round 24px); will-change: clip-path; }
.sx-se__media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transform-origin: center; user-select: none; will-change: transform; }
.sx-se__scrim {
  position: absolute; inset: 0; opacity: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(0, 0, 0, .75), rgba(0, 0, 0, .1) 45%, rgba(0, 0, 0, .35));
}
.sx-se__overlay {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  padding: 6%; opacity: 0; will-change: opacity, transform;
}
.sx-se__title {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  margin: 0; padding-inline: 6%; text-align: center;
  font-weight: 700; line-height: 1; letter-spacing: -.03em; color: #fff;
  font-size: var(--fs-hero); text-shadow: 0 2px 24px rgba(0, 0, 0, .45);
  pointer-events: none; will-change: opacity, transform;
}
.sx-se__hint {
  position: absolute; left: 0; right: 0; bottom: 1.25rem; text-align: center;
  font-size: var(--fs-small); letter-spacing: .02em; color: rgba(255, 255, 255, .55);
  pointer-events: none; will-change: opacity, transform;
}

/* react-bits/PixelSwap */
.sx-px { position: relative; isolation: isolate; width: 100%; overflow: hidden; outline: none; border-radius: inherit; }
.sx-px__layer { position: absolute; inset: 0; height: 100%; width: 100%; }
.sx-px__layer[data-visible="false"] { visibility: hidden; }
.sx-px__grid { pointer-events: none; position: absolute; inset: 0; z-index: 3; }
.sx-px__cell { position: absolute; overflow: hidden; opacity: 0; contain: paint; }

/* react-bits/GradualBlur */
.sx-gb { position: relative; isolation: isolate; }
.sx-gb--parent { position: absolute; }
.sx-gb--page { position: fixed; }
.sx-gb__layer { position: absolute; inset: 0; }
.sx-gb__inner { position: relative; width: 100%; height: 100%; }
.sx-gb__content { position: relative; }

/* The host div `wrap` and the opener mount put adopted nodes into. It must not introduce a box. */
.adopt { display: contents; }

/* Visually hidden, still read aloud. The stepper's indicators carry a "Step 3" label for screen
   readers beside the digit sighted users see; with no rule for this class the label rendered on
   top of the digit and every indicator read "Step 3 3". The clip-rect form is used rather than
   `display: none`, which would remove it from the accessibility tree and defeat the point. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==============================================================================================
   NARROW AND REDUCED-MOTION ANSWERS.

   Reduced motion REDUCES, it does not delete (DOCTRINE 387). Every beat below still shows every
   photograph and every word; what goes is the movement between them. The islands do not mount at
   all under reduce, so these rules describe the page as the build emitted it.
   ============================================================================================== */
@media (max-width: 47.99em) {
  .showcase__panel { width: min(84%, 30rem); }
  .strip6 { grid-auto-flow: row; grid-auto-columns: auto; height: auto; }
  .strip6__panel { aspect-ratio: 16 / 10; }
  .mosaic { grid-template-columns: repeat(2, 1fr); }
  .mosaic__cell--mid { grid-row: span 1; aspect-ratio: 4 / 3; }
  .fan { padding: 0 3%; }
  .fan__item--0, .fan__item--4 { display: none; }
  .fan__item--1, .fan__item--3 { width: 26%; }
  .fan__item--2 { width: 38%; }
  .point { grid-template-columns: 2.2rem minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .opener__frame { aspect-ratio: 16 / 9; }
  .showcase__panel { width: 100%; margin-top: 0; aspect-ratio: 16 / 9; }
  .strip6 { grid-auto-flow: row; grid-auto-columns: auto; height: auto; gap: .5rem; }
  .strip6__panel { aspect-ratio: 16 / 9; border-radius: var(--r); transform: none !important; }
  .mosaic__cell { transform: none !important; }
  .stepper__pane, .stepper__step, .stepper__line::after { transition: none; }
  .stepper__step.is-from-right, .stepper__step.is-from-left { transform: none; opacity: 1; }
}

/* --- react-bits/LogoLoop ------------------------------------------------------------------------
   Ported off Tailwind like the rest. This one was missed in the first pass because it predated the
   2026-09-04 rebuild and was assumed to be working — it was not. With no Tailwind, `overflow-x-hidden`
   and `w-max` were inert, the track never became a row, and the thirty marks stacked into a 1,584px
   column of near-empty page between the hero and the showcase. That was the empty region.

   Measured after the port: the mount point is ~4rem and the marks run as one line. */
.sx-ll {
  position: relative;
  --logoloop-gap: 32px;
  --logoloop-logoHeight: 28px;
  --logoloop-fadeColorAuto: var(--bg);
}
.sx-ll--h { overflow-x: hidden; }
.sx-ll--v { overflow: hidden; height: 100%; display: inline-block; }
.sx-ll--scale { padding-block: calc(var(--logoloop-logoHeight) * .1); }

.sx-ll__list { display: flex; align-items: center; margin: 0; padding: 0; list-style: none; }
.sx-ll__list--v { flex-direction: column; }

.sx-ll__item { flex: none; font-size: var(--logoloop-logoHeight); line-height: 1; }
.sx-ll__item--h { margin-right: var(--logoloop-gap); }
.sx-ll__item--v { margin-bottom: var(--logoloop-gap); }
.sx-ll__item--scale { overflow: visible; }

.sx-ll__node { display: inline-flex; align-items: center; }
.sx-ll__link { display: inline-flex; align-items: center; text-decoration: none; border-radius: var(--r-sm); }
.sx-ll__img {
  height: var(--logoloop-logoHeight); width: auto; display: block; object-fit: contain;
  -webkit-user-drag: none; pointer-events: none;
}

.sx-ll__track { display: flex; will-change: transform; user-select: none; position: relative; z-index: 0; }
.sx-ll__track--h { flex-direction: row; width: max-content; }
.sx-ll__track--v { flex-direction: column; height: max-content; width: 100%; }

.sx-ll__fade { pointer-events: none; position: absolute; z-index: 10; }
.sx-ll__fade--l, .sx-ll__fade--r { top: 0; bottom: 0; width: clamp(24px, 8%, 120px); }
.sx-ll__fade--t, .sx-ll__fade--b { left: 0; right: 0; height: clamp(24px, 8%, 120px); }
.sx-ll__fade--l { left: 0;  background: linear-gradient(to right,  var(--logoloop-fadeColor, var(--logoloop-fadeColorAuto)) 0%, transparent 100%); }
.sx-ll__fade--r { right: 0; background: linear-gradient(to left,   var(--logoloop-fadeColor, var(--logoloop-fadeColorAuto)) 0%, transparent 100%); }
.sx-ll__fade--t { top: 0;   background: linear-gradient(to bottom, var(--logoloop-fadeColor, var(--logoloop-fadeColorAuto)) 0%, transparent 100%); }
.sx-ll__fade--b { bottom: 0; background: linear-gradient(to top,   var(--logoloop-fadeColor, var(--logoloop-fadeColorAuto)) 0%, transparent 100%); }

@media (prefers-reduced-motion: reduce) {
  .sx-ll__track { transform: none !important; }
}

/* Each mark on this page is a name over its note, so the node stacks instead of sitting in a row,
   and the item's font-size is not the logo height — the two spans set their own. */
.tools__loop .sx-ll__item { font-size: inherit; }
.tools__loop .sx-ll__node { flex-direction: column; align-items: flex-start; gap: .1rem; }

/* --- the market selector ---------------------------------------------------------------- */
.market { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; margin: 0 0 1.6rem;
  font-size: var(--fs-small); color: var(--ink-dim); }
.market__label { font-weight: 500; }
.market__select { font: inherit; color: var(--ink); background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .4rem 2rem .4rem .7rem; appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 55%, calc(100% - .7rem) 55%; background-size: .35rem .35rem; background-repeat: no-repeat; }
.market__select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.market__note { color: var(--ink-faint); }
