/* ============================================================
   LUMEN design system — v3
   Loaded AFTER lumen-overrides.css. That file maps upstream's utility
   classes onto our palette; this one builds an actual visual system on
   top: a real type scale, the cyan+amber palette, card treatment,
   section rhythm and motion.

   Background (2026-09-22 audit): the page had no type hierarchy at all.
   The hero's display slot — <h2 class="typo-h1">, which is 48->80px —
   is EMPTY in upstream's own markup, and the tagline renders in
   <p class="typo-paragraph-lg"> at 18->20px. So the biggest type on the
   page was a decorative section heading while the actual brand statement
   was paragraph-sized. That single inversion is most of why the page read
   as flat. Fix 1 below addresses it.

   Palette note: the site was monotone cyan. Comparing probe 03 (cyan
   chrome + warm ring) against probe 06 (pure cyan) showed the warm accent
   is what creates depth. Amber is now a first-class colour, not a
   decoration.
   ============================================================ */

:root {
  --lum-bg:        #080b10;
  --lum-surface:   #0e141c;
  --lum-surface-2: #131b25;
  --lum-line:      rgba(148, 180, 204, 0.14);
  --lum-line-hi:   rgba(0, 229, 255, 0.34);
  --lum-ink:       #eaf0f7;
  --lum-dim:       #8595a8;
  --lum-cyan:      #00e5ff;
  --lum-cyan-dk:   #04222a;
  --lum-amber:     #ff8a3d;

  --lum-display: var(--font-abc-monument), ui-sans-serif, system-ui, sans-serif;
  --lum-body:    var(--font-roobert), ui-sans-serif, system-ui, sans-serif;

  --lum-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --lum-cut: 18px;   /* corner cut size */
}

/* ============================================================
   1. TYPE SCALE
   ============================================================ */

/* Promote the hero tagline out of the paragraph slot and into display type.
   .block-hero-intersection is the hero wrapper; the tagline is the only
   typo-paragraph-lg directly inside it. */
.block-hero-intersection p.typo-paragraph-lg {
  font-family: var(--lum-display) !important;
  font-size: clamp(2.6rem, 6.4vw, 5.5rem) !important;
  line-height: 1.03 !important;
  font-weight: 500 !important;
  letter-spacing: -0.032em !important;
  max-width: 15ch;
  margin-inline: auto;
  text-wrap: balance;
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.65);
}

/* The hero headline sits over video; a scrim keeps it legible no matter
   which frame is showing. */
.block-hero-intersection::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(8, 11, 16, 0.72) 0%,
    rgba(8, 11, 16, 0.18) 32%,
    rgba(8, 11, 16, 0.42) 72%,
    rgba(8, 11, 16, 0.92) 100%
  );
  z-index: 1;
}
.block-hero-intersection > * { position: relative; z-index: 2; }

/* Section headings: the source jumps 36 -> 72px between h2 usages with
   nothing in between. Give every section heading one size. */
[id^="block-"] .typo-h2,
[id^="block-"] h2.typo-h2 {
  font-size: clamp(2rem, 3.6vw, 3.25rem) !important;
  line-height: 1.08 !important;
  letter-spacing: -0.024em !important;
  font-weight: 500 !important;
  text-wrap: balance;
}

/* Card-level headings stay clearly subordinate. */
[id^="block-"] .typo-h5,
[id^="block-"] .typo-h6,
[id^="block-"] .typo-h7 {
  font-size: 1.375rem !important;
  line-height: 1.3 !important;
  letter-spacing: -0.012em !important;
}

/* Body copy: the source runs 18px at 1.44 line-height, which is cramped for
   the long paragraphs this site is full of. */
[id^="block-"] .typo-paragraph-lg,
[id^="block-"] .typo-paragraph-md {
  line-height: 1.68 !important;
  color: #c3cfdc !important;
}
[id^="block-"] p { text-wrap: pretty; }

/* Eyebrow / label treatment, used for the small caps labels above headings. */
[id^="block-"] .typo-label,
[id^="block-"] .typo-label-md,
[id^="block-"] .typo-label-lg { letter-spacing: 0.14em; text-transform: uppercase; }

/* ============================================================
   2. SURFACE + RHYTHM
   ============================================================ */

body {
  background-color: var(--lum-bg) !important;
  background-image:
    radial-gradient(1200px 620px at 78% -10%, rgba(0, 229, 255, 0.085), transparent 62%),
    radial-gradient(900px 520px at -10% 12%,  rgba(255, 138, 61, 0.045), transparent 58%) !important;
}

/* The source leaves a very tall void between a section heading and its cards.
   Cap it without flattening the deliberate full-bleed hero spacing.

   Scoped away from the anchor spans: upstream emits <span id="block-…"
   class="invisible relative -top-5 block"> as empty scroll targets, and a bare
   [id^="block-"] selector gave each of them 86.4px of padding top and bottom —
   three invisible 173px blocks, 519px of dead space the page never had. */
/* Section-to-section spacing was padding + margin + padding stacking to ~200px, which
   is a full screen-third of nothing between every pair of sections. Collapse the
   margins and let the padding own the rhythm. */
[id^="block-"]:not(span) { margin-bottom: 0 !important; }
[id^="block-"]:not(span) { padding-top: clamp(36px, 3.6vw, 64px) !important; padding-bottom: clamp(36px, 3.6vw, 64px) !important; }
/* The section blocks are `flex flex-col` with a 48px row-gap, which on top of the block
   padding opened ~240px between a section heading and its own cards — the copy and the
   thing it describes read as unrelated. */
[id^="block-"]:not(span) { row-gap: 20px !important; }
span[id^="block-"] { padding: 0 !important; height: 0 !important; }
span[id^="block-"]:empty { display: block; }

/* Content measure: paragraphs were running the full 1080px container. */
[id^="block-"] p { max-width: 68ch; }

/* ============================================================
   3. CARDS — cut corner + glow
   ============================================================ */

/* Only applied to figure-like cards (an image plus a caption), never to a
   wrapper that could contain a dropdown: clip-path clips every descendant,
   including absolutely-positioned popups. See the popup trap in memory. */
[id^="block-"] a:has(> img),
[id^="block-"] a:has(img[src*="/cdn/lumen/"]) {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 0 !important;
  clip-path: polygon(
    0 0,
    calc(100% - var(--lum-cut)) 0,
    100% var(--lum-cut),
    100% 100%,
    var(--lum-cut) 100%,
    0 calc(100% - var(--lum-cut))
  );
  transition: box-shadow 0.4s var(--lum-ease), transform 0.4s var(--lum-ease);
}
[id^="block-"] a:has(img[src*="/cdn/lumen/"]):hover {
  box-shadow: 0 0 0 1px var(--lum-line-hi), 0 26px 70px -34px rgba(0, 229, 255, 0.55);
  transform: translateY(-3px);
}

/* Our generated art gets a subtle scale on hover, and never the upstream
   hue-rotate safety net. */
img[src*="/cdn/lumen/"] {
  filter: none !important;
  transition: transform 0.7s var(--lum-ease);
}
a:hover > img[src*="/cdn/lumen/"] { transform: scale(1.035); }

/* ============================================================
   4. ACCENT — amber as the second colour
   ============================================================ */

/* Warm accent on hover for text links, so the page has a warm register
   instead of the single cyan note it had before. */
a.text-red-01:hover, [id^="block-"] a:hover { color: var(--lum-amber) !important; }

/* Keep the primary CTA cyan — it is the one place the brand colour must
   dominate — but give the secondary buttons a warm outline instead. */
a[class*="border-"][class*="rounded-"]:hover {
  border-color: var(--lum-amber) !important;
  box-shadow: 0 0 24px -6px rgba(255, 138, 61, 0.5);
}

/* ============================================================
   5. NAV — wallet button
   ============================================================ */

/* Styled in wallet.js, which owns the element. It injects its stylesheet into <head>
   AFTER this file, so at equal specificity its rules win — duplicating the button's look
   here would be a specificity war that silently loses. wallet.js now also inserts the
   pill into the navbar's right-hand cluster (next to Login), so alignment comes from the
   existing flex row rather than from fixed-position arithmetic. */

/* ============================================================
   16. NAVIGATION — one row, one height
   ============================================================ */

/* The harvested bar is 168px tall and spreads its contents across three different
   vertical positions: the wallet pill at 26px, the links and Login at 59px, and the
   logo centred at 70px. Nothing lines up with anything, which is the loudest
   "unfinished" signal on the page. Collapse it to a single 76px row and let flex centre
   everything on one axis. */
div[class*="top-0"]:has(nav) {
  height: auto !important;
  background: rgba(8, 11, 16, 0.82) !important;
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  border-bottom: 1px solid var(--lum-line) !important;
}
div[class*="top-0"]:has(nav) > div {
  height: 76px !important;
  min-height: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  align-items: center !important;
}

/* The logo was rendering ~85px tall — nearly the height of the entire bar. */
div[class*="top-0"] img[src*="logo"],
div[class*="top-0"] img[src*="logotest"] {
  height: 34px !important;
  width: auto !important;
  max-height: 34px !important;
}

/* Top-level links: a real hit area and a hover state, instead of bare underlined text. */
div[class*="top-0"] nav a {
  font-size: 14.5px !important;
  letter-spacing: 0.005em;
  padding: 8px 11px !important;
  border-radius: 7px;
  text-decoration: none !important;
  transition: color 0.18s var(--lum-ease), background-color 0.18s var(--lum-ease);
}
div[class*="top-0"] nav a:hover {
  color: var(--lum-cyan) !important;
  background: rgba(0, 229, 255, 0.08);
}
div[class*="top-0"] nav svg {
  opacity: 0.5;
  transition: opacity 0.18s var(--lum-ease);
}
div[class*="top-0"] nav li:hover svg { opacity: 1; }

/* Login sits at the right edge of the same container as the logo, so give it the same
   height as the wallet pill or the pair reads as two different controls. */
div[class*="top-0"] a[class*="rounded-\[120px\]"] {
  height: 38px !important;
  padding: 0 18px !important;
  font-size: 13.5px !important;
  font-weight: 600 !important;
}

/* ---- dropdown panel ----
   Upstream ships it at 48px padding with 20px items — larger type than the top-level
   links it hangs from, so the hierarchy is inverted — a near-invisible 1px border and a
   barely-there shadow. Rebuild it as a proper panel. */
div[class*="top-0"] nav div:has(> ul) {
  background: #0e141c !important;
  border: 1px solid var(--lum-line) !important;
  border-radius: 12px !important;
  padding: 14px !important;
  box-shadow:
    0 28px 70px -24px rgba(0, 0, 0, 0.85),
    0 0 0 1px rgba(0, 229, 255, 0.05),
    inset 0 1px 0 rgba(255, 255, 255, 0.03) !important;
  overflow: hidden;
  z-index: 70 !important;
}

/* A decorative pseudo-element on the panel renders as a large pale wedge across its top
   edge. Whatever its intent, it reads as a broken arrow. */
div[class*="top-0"] nav div:has(> ul)::before,
div[class*="top-0"] nav div:has(> ul)::after,
div[class*="top-0"] nav ul ul::before,
div[class*="top-0"] nav ul ul::after { content: none !important; display: none !important; }

div[class*="top-0"] nav ul ul { row-gap: 3px !important; }

/* The panel puts a 32px flex gap between its group label and the list, which reads as
   the menu having lost its first item. */
div[class*="top-0"] nav div:has(> ul) { gap: 2px !important; }
div[class*="top-0"] nav div:has(> ul) > h6,
div[class*="top-0"] nav div:has(> ul) [class*="typo-label"] {
  margin: 0 0 2px !important;
  padding: 2px 12px 6px !important;
}
/* and the same gap again between the last item and the panel's bottom edge */
div[class*="top-0"] nav div:has(> ul) > *:last-child { margin-bottom: 0 !important; }

/* Items: below the top-level size, with a hover that matches the rest of the site. */
div[class*="top-0"] nav ul ul a {
  display: block;
  font-size: 14px !important;
  line-height: 1.35 !important;
  padding: 9px 12px !important;
  border-radius: 7px;
  color: #c3cfdc !important;
}
div[class*="top-0"] nav ul ul a:hover {
  color: var(--lum-ink) !important;
  background: rgba(0, 229, 255, 0.1);
}
/* The group label ("FEATURES", "ABOUT") above each list. */
div[class*="top-0"] nav ul ul span[class*="label"],
div[class*="top-0"] nav ul ul [class*="typo-label"] {
  display: block;
  padding: 4px 12px 8px;
  font-size: 11px !important;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--lum-dim) !important;
}

/* ============================================================
   6. COOKIE BANNER
   ============================================================ */

/* Termly's consent bar is the highest-contrast object on every screenful — a pure white
   plate that also covers the footer copyright line entirely.
   Two things made the first attempt at restyling it fail silently:
     1. the white is painted by the ROOT element, and a `[class*="termly-"] > div` rule
        only reaches its children, leaving a white frame around the dark inner panel;
     2. the buttons carry hashed classes (termly-styles-module-root-ecf4eb), so matching
        on "accept"/"decline" never hit anything — the Accept button stayed white-on-white
        and Preferences stayed black-on-dark.
   Its class names are all hashed, so match on the stable prefixes and on position. */
[class*="termly-styles-root-"] {
  background: transparent !important;
  box-shadow: none !important;
  border: none !important;
}
[class*="termly-styles-main-"],
[class*="termly-styles-content-"],
[class*="termly-styles-message-"] {
  background: rgba(14, 20, 28, 0.96) !important;
  border: 1px solid var(--lum-line) !important;
  border-radius: 12px !important;
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  color: var(--lum-ink) !important;
}
[class*="termly-styles-message-"] p,
[class*="termly-styles-message-"] span,
[class*="termly-styles-message-"] a { color: var(--lum-ink) !important; }
[class*="termly-styles-message-"] a { color: var(--lum-cyan) !important; }

/* Button row: the row itself paints a white plate behind the buttons, so restyling only
   the buttons still left a white rectangle showing. Everything ghosted, the last button
   (Accept) filled as the primary. */
[class*="termly-styles-buttons-"] {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
}
[class*="termly-styles-buttons-"] button {
  background: transparent !important;
  color: var(--lum-ink) !important;
  border: 1px solid var(--lum-line-hi) !important;
  border-radius: 2px !important;
  font-weight: 600 !important;
  transition: filter 0.2s var(--lum-ease), background-color 0.2s var(--lum-ease);
}
[class*="termly-styles-buttons-"] button:hover { background: rgba(0, 229, 255, 0.1) !important; }
[class*="termly-styles-buttons-"] button:last-child {
  background: var(--lum-cyan) !important;
  border-color: var(--lum-cyan) !important;
  color: var(--lum-cyan-dk) !important;
}
[class*="termly-styles-buttons-"] button:last-child:hover { filter: brightness(1.08); }

/* Footer re-open link, which Termly injects into the page. */
a.termly-display-preferences { color: var(--lum-dim) !important; }
a.termly-display-preferences:hover { color: var(--lum-cyan) !important; }

/* ============================================================
   7. FOOTER
   ============================================================ */

footer, [class*="footer"] { border-top: 1px solid var(--lum-line); }

/* ============================================================
   8. MOTION — reveal on scroll
   ============================================================ */

/* lumen-visuals.js adds .lumen-reveal. Anchor on the markup this site
   actually uses ([id^="block-"]), not <section>, which matched nothing. */
.lumen-reveal { opacity: 0; transform: translateY(28px); }
.lumen-reveal.lumen-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.8s var(--lum-ease), transform 0.8s var(--lum-ease);
}

/* Stagger children so a row of cards arrives in sequence. */
.lumen-reveal.lumen-in > * { animation: none; }
.lumen-stagger { opacity: 0; transform: translateY(22px); }
.lumen-stagger.lumen-in {
  opacity: 1; transform: none;
  transition: opacity 0.7s var(--lum-ease), transform 0.7s var(--lum-ease);
}

/* ============================================================
   9. HERO STAGE — the render frontier, animated
   ============================================================ */

/* lumen-anim.js replaces the harvested hero video with this stage. The still's own
   subject is a wireframe half resolving into a finished chrome half, so a slow
   push-in plus a sweeping band reads as "the render is still running" rather than
   as a static backdrop. */
.lumen-hero-stage {
  position: relative;
  overflow: hidden;
  background: #05080c;
}
.lumen-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  animation: lumenHeroPush 26s ease-in-out infinite alternate;
}
@keyframes lumenHeroPush {
  from { transform: scale(1.015); }
  to   { transform: scale(1.085); }
}

/* The band is mostly a soft cyan wash with a thin bright core. Kept translucent so it
   reads as light passing over the frame, not as a second render frontier competing
   with the one already drawn into the artwork. */
.lumen-hero-scan {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 26%;
  pointer-events: none;
  mix-blend-mode: screen;
  background: linear-gradient(90deg,
    rgba(0, 229, 255, 0.00) 0%,
    rgba(0, 229, 255, 0.07) 38%,
    rgba(0, 229, 255, 0.20) 48%,
    rgba(226, 252, 255, 0.52) 50%,
    rgba(0, 229, 255, 0.20) 52%,
    rgba(0, 229, 255, 0.07) 62%,
    rgba(0, 229, 255, 0.00) 100%);
  animation: lumenScan 8.5s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
/* Travel is expressed in the band's own width: crossing the container needs
   100/26 of its width plus its own, so -110% -> 500%. */
@keyframes lumenScan {
  0%   { transform: translateX(-115%); opacity: 0; }
  10%  { opacity: 1; }
  86%  { opacity: 1; }
  100% { transform: translateX(500%); opacity: 0; }
}

/* ============================================================
   10. CTA — the primary action must be the brand colour
   ============================================================ */

/* Upstream draws this button as a white outline, which is correct on its own light
   hero and nearly invisible on ours: the single most important control on the page
   was the faintest element on it. Fill it with the brand cyan and invert the label. */
.block-hero-intersection a[class*="rounded-"],
.block-hero-intersection button[class*="rounded-"] {
  background-color: var(--lum-cyan) !important;
  border-color: var(--lum-cyan) !important;
  color: var(--lum-cyan-dk) !important;
  font-weight: 600 !important;
  box-shadow: 0 0 34px -8px rgba(0, 229, 255, 0.65);
}
.block-hero-intersection a[class*="rounded-"]:hover,
.block-hero-intersection button[class*="rounded-"]:hover {
  background-color: #4df0ff !important;
  border-color: #4df0ff !important;
  box-shadow: 0 0 48px -6px rgba(0, 229, 255, 0.85);
  transform: translateY(-2px);
}

/* ============================================================
   11. BENEFIT CARDS
   ============================================================ */

/* The Fast / Elastic / Affordable cards are swiper slides, so they never matched the
   :has(img) card rule above — they had a hairline and nothing else. Give them the same
   cut-corner language as the image cards so the page reads as one system. */
[id^="block-"] .swiper-slide > *,
[id^="block-"] .swiper-slide {
  position: relative;
}
[id^="block-"] .swiper-slide > * {
  background: linear-gradient(180deg, rgba(19, 27, 37, 0.9), rgba(11, 16, 23, 0.9));
  border: 1px solid var(--lum-line);
  border-radius: 0 !important;
  clip-path: polygon(
    0 0,
    calc(100% - var(--lum-cut)) 0,
    100% var(--lum-cut),
    100% 100%,
    var(--lum-cut) 100%,
    0 calc(100% - var(--lum-cut))
  );
  transition: border-color 0.35s var(--lum-ease), box-shadow 0.35s var(--lum-ease), transform 0.35s var(--lum-ease);
}
[id^="block-"] .swiper-slide > *:hover {
  border-color: var(--lum-line-hi);
  box-shadow: 0 20px 60px -34px rgba(0, 229, 255, 0.6);
  transform: translateY(-3px);
}

/* Benefit icons: upstream ships a 64px box around a much smaller glyph, which left the
   icons looking lost at the top of a tall card. */
[id^="block-"] img[src*="/cdn/lumen/icon-"] {
  width: 44px !important;
  height: 44px !important;
  margin-bottom: 6px;
}

/* ============================================================
   12. SECTION RHYTHM — final pass
   ============================================================ */

/* Cap the void the carousel opens between a section heading and its cards. */
[id^="block-"] .swiper { margin-top: 0 !important; padding-top: 0 !important; }

/* ============================================================
   13. ARTIST CREDITS — remove
   ============================================================ */

/* Upstream draws a credit over every showcase image with an ::after pseudo-element
   reading data-artist-name (31 of them site-wide: Beeple, Emiliano Iozzo, Raoul Marks,
   Annibale Siconolfi, …). Every one of those images has been replaced by our own
   generated art, so the credit is now simply false — and it is exactly the kind of
   wrong attribution that carries legal risk, since these are named real artists.

   Two of them are also rebuild damage: the RENDER -> LUMEN text pass hit names
   containing "Render", turning FVCKRENDER into "FVCKLUMEN" and "Render X on iPad"
   into "Lumen X on iPad". Removing the layer fixes those too.

   A pseudo-element cannot be removed by editing the DOM text, only by overriding the
   content — which is what this does. */
[data-artist-name]::after { content: none !important; display: none !important; }

/* ============================================================
   14. SECONDARY CTA
   ============================================================ */

/* Section-level buttons ("Getting Started", "Try LUMEN") ship as a white outline built
   for upstream's light page. On our dark surface they read as disabled. Give them the
   brand outline instead, distinct from the filled hero CTA so the two do not compete. */
[id^="block-"] a[class*="rounded-"],
footer a[class*="rounded-"] {
  border-color: var(--lum-line-hi) !important;
  color: var(--lum-cyan) !important;
}
[id^="block-"] a[class*="rounded-"]:hover,
footer a[class*="rounded-"]:hover {
  background-color: var(--lum-cyan) !important;
  border-color: var(--lum-cyan) !important;
  color: var(--lum-cyan-dk) !important;
  box-shadow: 0 0 34px -8px rgba(0, 229, 255, 0.7);
}

/* ============================================================
   15. INJECTED SECTIONS
   ============================================================ */

/* lumen-sections.js appends three sections the harvested page never had. They are added
   to the DOM after hydration, so React never sees them — appending is safe in a way that
   editing the RSC payload is not (see the note on trimming payload rows). */

.lum-section {
  position: relative;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: clamp(36px, 3.6vw, 64px) clamp(12px, 2vw, 24px);
}
.lum-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--lum-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--lum-cyan);
  margin-bottom: 16px;
}
.lum-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--lum-cyan);
  box-shadow: 0 0 12px var(--lum-cyan);
  animation: lumPulse 1.9s ease-in-out infinite;
}
@keyframes lumPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

.lum-h2 {
  font-family: var(--lum-display);
  font-size: clamp(1.9rem, 3.4vw, 3rem);
  line-height: 1.08;
  letter-spacing: -0.024em;
  font-weight: 500;
  margin: 0 0 14px;
  text-wrap: balance;
}
.lum-lede {
  font-family: var(--lum-body);
  font-size: 1.05rem;
  line-height: 1.65;
  color: #c3cfdc;
  max-width: 62ch;
  margin: 0 0 40px;
}

/* ---- live network stats ---- */

.lum-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 1px;
  background: var(--lum-line);
  border: 1px solid var(--lum-line);
  margin-bottom: 34px;
}
.lum-stat {
  background: #0b1017;
  padding: 26px 22px 22px;
}
.lum-stat b {
  display: block;
  font-family: var(--lum-display);
  font-size: clamp(1.9rem, 3.2vw, 2.7rem);
  line-height: 1;
  font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--lum-cyan);
  font-variant-numeric: tabular-nums;
}
.lum-stat.is-warm b { color: var(--lum-amber); }
.lum-stat span {
  display: block;
  margin-top: 9px;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  color: var(--lum-dim);
}
/* The API reports these live; a failed fetch must not leave a dash sitting there as if
   it were a real figure, so failures render "—" and the row dims. */
.lum-stats.is-stale { opacity: 0.45; }

.lum-nodes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.lum-node {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 14px;
  border: 1px solid var(--lum-line);
  background: #0b1017;
  font-family: var(--lum-body);
  font-size: 13px;
  color: #c3cfdc;
}
.lum-node i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--lum-cyan);
  flex: none;
}
.lum-node i.is-busy { background: var(--lum-amber); }
.lum-node em { font-style: normal; color: var(--lum-dim); }
.lum-node b { color: var(--lum-ink); font-weight: 600; }

/* ---- workflow ---- */

.lum-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}
.lum-step { position: relative; }
.lum-step-img {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #05080c;
  clip-path: polygon(
    0 0,
    calc(100% - var(--lum-cut)) 0,
    100% var(--lum-cut),
    100% 100%,
    var(--lum-cut) 100%,
    0 calc(100% - var(--lum-cut))
  );
}
.lum-step-img img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.7s var(--lum-ease); }
.lum-step:hover .lum-step-img img { transform: scale(1.05); }
.lum-step-n {
  position: absolute;
  top: 14px; left: 14px;
  font-family: var(--lum-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--lum-cyan-dk);
  background: var(--lum-cyan);
  padding: 5px 10px;
  z-index: 2;
}
.lum-step h3 {
  font-family: var(--lum-display);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: -0.012em;
  margin: 18px 0 7px;
}
.lum-step p {
  font-family: var(--lum-body);
  font-size: 0.95rem;
  line-height: 1.6;
  color: #9fadbd;
  margin: 0;
}

/* ---- gallery ---- */

.lum-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
  gap: 14px;
}
.lum-gallery figure {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #05080c;
  clip-path: polygon(
    0 0,
    calc(100% - var(--lum-cut)) 0,
    100% var(--lum-cut),
    100% 100%,
    var(--lum-cut) 100%,
    0 calc(100% - var(--lum-cut))
  );
}
.lum-gallery img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.75s var(--lum-ease); }
.lum-gallery figure:hover img { transform: scale(1.06); }
.lum-gallery figcaption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 40px 16px 14px;
  font-family: var(--lum-body);
  font-size: 12.5px;
  letter-spacing: 0.05em;
  color: #dbe4ee;
  background: linear-gradient(to top, rgba(5, 8, 12, 0.92), transparent);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.35s var(--lum-ease), transform 0.35s var(--lum-ease);
}
.lum-gallery figure:hover figcaption { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .lum-dot { animation: none; }
  .lum-gallery img, .lum-step-img img { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .lumen-reveal, .lumen-stagger { opacity: 1 !important; transform: none !important; transition: none !important; }
  .lumen-hero-img, .lumen-hero-scan { animation: none !important; }
  .lumen-hero-scan { opacity: 0; }
  .block-hero-intersection img, img[src*="/cdn/lumen/"] { transition: none !important; }
}
