/* ==========================================================================
   CREDOSOFT.AI — Credo AI Integrity Management platform
   Primary marketing site · static HTML/CSS/JS · zero dependencies

   Design language: spacex.com "endless scroll" style —
   full-viewport pages stacked with razor-sharp boundaries, expansive
   full-bleed background images, white text, DIN-style uppercase type,
   hairline rules. Teal is reserved for tiny accents only (dots, focus).
   See Website_Design_Guide.md for the full system.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Surfaces */
  --bg:            #000000;
  --bg-soft:       #05070a;
  --bg-card:       rgba(10, 13, 17, 0.66);

  /* Lines — white hairlines, never teal */
  --line:          rgba(255, 255, 255, 0.14);
  --line-soft:     rgba(255, 255, 255, 0.08);
  --line-strong:   rgba(255, 255, 255, 0.35);

  /* Text — white first, greys only for hierarchy */
  --text:          #ffffff;
  --muted:         rgba(255, 255, 255, 0.72);
  --faint:         rgba(255, 255, 255, 0.45);

  /* Brand accents — used sparingly (dots, focus, tiny badges) */
  --teal:          #2ee6c8;
  --blue:          #4da3ff;
  --amber:         #ffc14d;
  --corrosion:     #ff7a90;

  /* Type */
  --font: "D-DIN", "DIN Alternate", Bahnschrift, "Segoe UI", "Helvetica Neue", Arial, sans-serif;

  /* Metrics */
  --header-h:      72px;
  --radius:        14px;
  --radius-sm:     2px;
  --gutter:        clamp(24px, 5vw, 72px);
  --content-max:   1240px;

  /* Motion */
  --ease:          cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur:           0.5s;
}

/* --------------------------------------------------------------------------
   2. Reset / base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

::selection { background: #ffffff; color: #000; }

:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}

/* Skip-to-content link — visually hidden until focused */
.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 300;
  padding: 11px 20px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #000;
  background: var(--text);
  border-radius: var(--radius-sm);
  transition: top 0.2s var(--ease);
}

.skip-link:focus { top: 16px; }

/* --------------------------------------------------------------------------
   3. Typography — uppercase, wide-tracked, DIN-flavoured, WHITE
   -------------------------------------------------------------------------- */
.kicker {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 20px;
}

h1, h2, h3 {
  margin: 0 0 16px;
  line-height: 1.06;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--text);
}

h1 { font-size: clamp(36px, 6.4vw, 76px); }
h2 { font-size: clamp(30px, 4.2vw, 52px); }
h3 { font-size: clamp(20px, 2.4vw, 26px); }

/* SpaceX-style hairline rule — white, not teal */
.rule {
  width: 56px;
  height: 1px;
  background: var(--text);
  border: 0;
  margin: 0 0 26px;
}

p { margin: 0 0 16px; color: var(--muted); }
p.lead { color: var(--text); font-size: clamp(18px, 2vw, 22px); }

/* Orange emphasis — used sparingly to highlight key concepts (warm accent, not teal) */
.accent { color: var(--amber); }

/* --------------------------------------------------------------------------
   4. Full-page "window" sections — the endless scroll topography
   Each section is a full viewport, image-backed, stacked with a razor-sharp
   hairline boundary. Content sits bottom-left, SpaceX style.
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.page {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;              /* content anchored bottom-left */
  overflow: hidden;
  background: var(--bg);
  border-top: 1px solid var(--line-soft);   /* razor-sharp boundary */
}

.page:first-of-type { border-top: 0; }

.page__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.page__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  filter: saturate(0.9) contrast(1.02);
}

/* Legibility gradient — darkens from the bottom-left where copy sits */
.page__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.10) 38%, rgba(0, 0, 0, 0.62) 72%, var(--bg) 100%),
    radial-gradient(90% 70% at 18% 90%, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 70%);
}

/* Dark-surface variant (module grid needs a clean reading surface) */
.page--plain { background: var(--bg); }
.page--plain .page__bg::after {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.55) 40%, rgba(0, 0, 0, 0.72) 72%, var(--bg) 100%),
    radial-gradient(90% 70% at 18% 90%, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 70%);
}

.page__content {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: calc(var(--header-h) + 24px) var(--gutter) clamp(72px, 13vh, 132px);
}

.page__head { max-width: 780px; }

/* Hero — first page, tallest headline moment */
.page--hero .page__content { padding-bottom: clamp(88px, 15vh, 150px); }

@keyframes hero-rise {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}

.hero-panel { animation: hero-rise 1s var(--ease) both; }

.hero-sub {
  font-size: clamp(18px, 2.2vw, 26px);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 8px;
}

.page .tagline {
  max-width: 560px;
  font-size: clamp(16px, 1.8vw, 19px);
  color: var(--muted);
  margin-bottom: 34px;
}

/* Hero bullet points — replaces the tagline paragraph when copy is a list */
.hero-list {
  list-style: none;
  margin: 0 0 34px;
  padding: 0;
  max-width: 620px;
}

.hero-list li {
  position: relative;
  padding: 10px 0 10px 26px;
  font-size: clamp(16px, 1.8vw, 19px);
  color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}

.hero-list li:first-child { border-top: 1px solid var(--line-soft); }

.hero-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 19px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--line-strong);
}

/* Frosted cyan glass panel — hero copy and section heads */
.hero-panel,
.page__head {
  padding: clamp(28px, 4vw, 44px);
  border: 1px solid rgba(46, 230, 200, 0.16);
  border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(46, 230, 200, 0.08) 0%, rgba(46, 230, 200, 0.025) 100%);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  backdrop-filter: blur(18px) saturate(1.25);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

.hero-panel { max-width: 720px; }

/* How to obtain 3D models — the four capture/import sources */
.sources {
  margin-top: clamp(32px, 5vw, 56px);
}

.sources__head {
  max-width: 780px;
}

.sources__head .kicker {
  margin-bottom: 12px;
}

.sources__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin-top: 28px;
}

.source-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  min-height: 200px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.source-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.source-card__index {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--faint);
  margin-bottom: 14px;
}

.source-card h3 { font-size: 20px; margin-bottom: 10px; }
.source-card p { font-size: 15px; flex: 1 1 auto; }

/* Scroll cue */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  z-index: 2;
  width: 1px;
  height: 56px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), transparent);
  animation: cue 2.2s ease-in-out infinite;
}

@keyframes cue {
  0%, 100% { opacity: 0.25; transform: translateX(-50%) translateY(0); }
  50%      { opacity: 1;    transform: translateX(-50%) translateY(6px); }
}

/* --------------------------------------------------------------------------
   5. Header / navigation — fixed, transparent → solid on scroll
   -------------------------------------------------------------------------- */
.header {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--header-h);
  z-index: 100;
  display: flex;
  align-items: center;
  background: transparent;
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease);
  border-bottom: 1px solid transparent;
}

.header.is-scrolled {
  background: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--line-soft);
}

.header__inner {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 700;
  font-size: 15px;
  color: var(--text);
  /* The brand shares its row with the language menu, and below 1021px with the
     burger. Without this the logo is the first thing the flex algorithm
     squeezes, and it renders squashed/short. */
  flex: 0 0 auto;
}

.brand img { height: 32px; width: auto; }

.nav__links {
  display: flex;
  align-items: center;
  /* Twelve labels, not ten: the gap below is sized from measurements rather
     than taste. At 1021px — the tightest desktop row — French is the widest
     language and tools/measure-header.mjs reports nav=890.5/919 with the
     values in this file; at the 11px / 0.09em pair the wider bands use, the
     same row is 993.1px, i.e. 74px over. */
  gap: clamp(10px, 1.1vw, 18px);
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__links a {
  position: relative;
  /* 11px / 0.09em keeps all twelve translated labels on one line in the
     desktop rows, in all six languages — French is the widest and the numbers
     below are measured at exact viewports by tools/measure-header.mjs:
     nav=1043.8px of 1096px at 1440px, 1067.7px of 1096px at 1920px.
     nowrap keeps "Digital Twin" / "Damage Modeller" / "Monte Carlo" from
     breaking in half. Below 1120px the size and tracking step down again —
     see the 1021–1120px block in §16. */
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  white-space: nowrap;
  text-transform: uppercase;
  color: var(--text);
  padding: 6px 0;
  transition: color 0.2s var(--ease);
}

.nav__links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--text);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s var(--ease);
}

.nav__links a:hover { color: var(--text); }
.nav__links a:hover::after { transform: scaleX(1); }

/* CTA link in the nav */
.nav__cta {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 9px 18px !important;
  letter-spacing: 0.16em !important;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease) !important;
}
.nav__cta::after { display: none; }
.nav__cta:hover { border-color: var(--text); background: rgba(255, 255, 255, 0.08); }

/* Burger (mobile) */
.burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  width: 44px;
  height: 44px;
  padding: 10px;
  background: none;
  border: 0;
  cursor: pointer;
  z-index: 120;
}

.burger span {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--text);
  transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
}

body.menu-open .burger span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
body.menu-open .burger span:nth-child(2) { opacity: 0; }
body.menu-open .burger span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* Full-screen mobile overlay menu */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 110;
  background: rgba(0, 0, 0, 0.97);
  display: flex;
  flex-direction: column;
  /* The picker, eleven link rows and the footer note total ~880-1050px, which is
     more than a phone has. A centred column overflows off BOTH edges — the first
     rows above the top (clipped, unreachable) and the language picker below the
     bottom (ditto), with nothing to scroll because a fixed element does not
     scroll its own overflow. So: start-aligned and scrollable, and the auto
     margins on the first/last child below re-centre the block whenever it *does*
     fit (§2.3, tools/measure-mobile.mjs measures both states). */
  justify-content: flex-start;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: calc(var(--header-h) + 24px) var(--gutter) 48px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s var(--ease), visibility 0.35s var(--ease);
}

body.menu-open .mobile-menu { opacity: 1; visibility: visible; }

/* Auto margins centre the block while there is room and collapse to 0 when
   there is not — the one centring trick that cannot push content off the top of
   a scroll container. */
.mobile-menu__lang { margin-top: auto; }
.mobile-menu .menu-foot { margin-bottom: auto; }

.mobile-menu ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.mobile-menu li { border-bottom: 1px solid var(--line-soft); }

.mobile-menu a {
  display: block;
  /* Fluid, not the old flat 26px/18px: at 26px and line-height 1.6 every row was
     77.6px, so the eleven of them were ~854px — taller than the phone, which is
     what pushed the picker off screen. A phone now gets ~18px type in ~48px
     rows (a 44px touch target, §2.2) and a tablet still gets the full 26px.
     min(…, 5vh) is what keeps a landscape phone sane: there the width is 844px
     but the height is only ~390px. */
  padding: clamp(12px, 1.6vh, 18px) 0;
  font-size: min(clamp(17px, 4.6vw, 26px), 5vh);
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text);
  transition: color 0.2s var(--ease), padding-left 0.25s var(--ease);
}

.mobile-menu a:hover { color: var(--text); padding-left: 12px; }

.mobile-menu .menu-foot {
  margin-top: clamp(20px, 3.5vh, 48px);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}

/* --------------------------------------------------------------------------
   5b. Language menu — dropdown in the header, button list in the mobile
       overlay. Language names stay in their own language, so the whole
       block is marked [data-i18n-skip] and js/i18n.js never touches it.
       Behaviour (open/close, roving focus, persistence) lives in js/i18n.js.
   -------------------------------------------------------------------------- */
.header__actions {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 28px);
  margin-left: auto;
}

.lang { position: relative; }

.lang__toggle {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.lang__toggle:hover,
.lang.is-open .lang__toggle {
  border-color: var(--line);
  background: rgba(255, 255, 255, 0.06);
}

.lang__globe {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
  opacity: 0.85;
}

.lang__caret {
  width: 5px;
  height: 5px;
  border-right: 1px solid var(--text);
  border-bottom: 1px solid var(--text);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.25s var(--ease);
}

.lang.is-open .lang__caret { transform: translateY(1px) rotate(-135deg); }

.lang__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 178px;
  padding: 6px;
  background: rgba(0, 0, 0, 0.94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  /* visibility is delayed only while closing, so the fade-out still plays. */
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease),
              visibility 0s linear 0.22s;
}

/* Opening flips visibility with no delay: a menu that still computes to
   `visibility: hidden` cannot take keyboard focus on the frame it opens, so
   the first option would be skipped for keyboard users. */
.lang.is-open .lang__menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease),
              visibility 0s;
}

.lang__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: 100%;
  padding: 10px 12px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-align: left;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color 0.2s var(--ease), background 0.2s var(--ease);
}

.lang__option:hover { color: var(--text); background: rgba(255, 255, 255, 0.07); }
.lang__option.is-active { color: var(--text); }

.lang__code {
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--faint);
}

.lang__option.is-active .lang__code { color: var(--teal); }

/* Language picker inside the mobile overlay (touch). It sits at the TOP of the
   overlay, under the fixed header: the link list below it scrolls, the picker
   does not have to be scrolled to (it used to end 100-300px below the fold). */
.mobile-menu__lang {
  padding-bottom: clamp(14px, 2.4vh, 26px);
  margin-bottom: clamp(20px, 3vh, 36px);
  border-bottom: 1px solid var(--line-soft);
}

.lang-list__title {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--faint);
}

.lang-list {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 1.4vh, 10px);
  margin-top: clamp(10px, 1.6vh, 16px);
}

.lang-list__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* A guaranteed touch target (§2.2) and one step down from the 12px/0.16em it
     was: six uppercase names have to wrap into two or three rows on a phone,
     not five. */
  min-height: 44px;
  padding: clamp(8px, 1.2vh, 12px) clamp(12px, 3vw, 18px);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease),
              background 0.2s var(--ease);
}

.lang-list__btn:hover { color: var(--text); border-color: var(--line-strong); }

.lang-list__btn.is-active {
  color: #000;
  background: var(--text);
  border-color: var(--text);
}

@media (prefers-reduced-motion: reduce) {
  .lang__menu, .lang__caret { transition: none; }
}

/* --------------------------------------------------------------------------
   6. Buttons — white-first, SpaceX text-link style
   -------------------------------------------------------------------------- */
.actions { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 28px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  background: transparent;
  cursor: pointer;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
}

.btn:hover { border-color: var(--text); background: rgba(255, 255, 255, 0.08); transform: translateY(-1px); }

.btn--primary {
  background: var(--text);
  border-color: var(--text);
  color: #000;
}
.btn--primary:hover { background: rgba(255, 255, 255, 0.85); border-color: var(--text); color: #000; }

.btn .arrow { transition: transform 0.25s var(--ease); }
.btn:hover .arrow { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   7. Deployment grid — SaaS vs On-Premise (the Platform page)
   -------------------------------------------------------------------------- */
.deploy {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  margin-top: clamp(40px, 6vw, 64px);
}

.deploy-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 36px 32px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  flex-direction: column;
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.deploy-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.deploy-card .deploy-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--faint);
  margin-bottom: 18px;
}

.deploy-card h3 { font-size: 26px; margin-bottom: 12px; }
.deploy-card p { font-size: 15px; }

.deploy-list {
  list-style: none;
  margin: 20px 0 24px;
  padding: 0;
  border-top: 1px solid var(--line-soft);
}

.deploy-list li {
  padding: 12px 0 12px 22px;
  position: relative;
  font-size: 14px;
  color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}

.deploy-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 19px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--line-strong);
}

.deploy-card .status { margin-top: auto; }

/* --------------------------------------------------------------------------
   7.5 Web technology — the modern browser stack (Platform page)
   The five cards ride the shared carousel (§17) as a card band
   (`.carousel--cards`): one card centred at a time, its neighbours peeking in,
   arrows flanking one dot per card. The live probe sits in the closing content
   column under the band (§17, `.page__content--tail`).
   -------------------------------------------------------------------------- */
.tech__head {
  margin-top: clamp(40px, 6vw, 64px);
  max-width: 780px;
}

.tech__head .kicker {
  margin-bottom: 12px;
}

/* One slide of the band — the card itself. */
.tech-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 30px 28px;
  display: flex;
  flex-direction: column;
  min-height: 220px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.tech-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.tech-card__index {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--faint);
  margin-bottom: 16px;
}

.tech-card h3 { font-size: 21px; margin-bottom: 10px; }
.tech-card p { font-size: 15px; flex: 1 1 auto; }

/* Live capability probe — reports the visitor's actual browser GPU */
.tech__probe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  margin-top: 24px;
  padding: 18px 22px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: rgba(8, 11, 15, 0.5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.tech__probe-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--faint);
}

.tech__probe-chip {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}

.tech__probe-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--faint);
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.tech__probe-chip.is-webgpu .tech__probe-dot {
  background: var(--teal);
  box-shadow: 0 0 10px rgba(46, 230, 200, 0.7);
}

.tech__probe-chip.is-webgl2 .tech__probe-dot {
  background: var(--text);
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.35);
}

.tech__probe-chip.is-fallback .tech__probe-dot {
  background: var(--faint);
}

/* --------------------------------------------------------------------------
   7.6 Path to Deployment — three key principles from the white paper
   -------------------------------------------------------------------------- */
.deploy-points {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  margin-top: clamp(32px, 5vw, 56px);
}

.deploy-point {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 30px 28px;
  display: flex;
  flex-direction: column;
  min-height: 220px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.deploy-point:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.deploy-point__num {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--faint);
  margin-bottom: 16px;
}

.deploy-point h3 { font-size: 21px; margin-bottom: 10px; }
.deploy-point p { font-size: 15px; margin: 0; }

.deploy-cta {
  margin-top: clamp(28px, 4vw, 40px);
}

/* --------------------------------------------------------------------------
   7.7 About — company identity
   -------------------------------------------------------------------------- */
.about-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  margin-top: clamp(28px, 4vw, 40px);
}

.about-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 30px 28px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.about-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.about-card h3 { font-size: 21px; margin-bottom: 10px; }
.about-card p { font-size: 15px; margin: 0; }

/* --------------------------------------------------------------------------
   8. Module cards — the suite of tools under Credo AI (#modules)
   The ten cards ride the shared carousel (§17) as a card band
   (`.carousel--cards`): one card per slide, stepped through with the arrows
   or the dots.
   -------------------------------------------------------------------------- */

.module-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 32px 30px;
  display: flex;
  flex-direction: column;
  min-height: 320px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.module-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.module-card .module-index {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  color: var(--faint);
  margin-bottom: 18px;
}

.module-card h3 { font-size: 22px; margin-bottom: 12px; }
.module-card p { font-size: 15px; flex: 1 1 auto; }

.module-card .status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 20px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
}
.module-card .status::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--faint);
}
.module-card .status--live { color: var(--text); }
.module-card .status--live::before {
  background: var(--teal);
  box-shadow: 0 0 8px rgba(46, 230, 200, 0.6);
}

.module-card .card-link {
  margin-top: 22px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: opacity 0.2s var(--ease);
}
.module-card .card-link .arrow { transition: transform 0.25s var(--ease); }
.module-card .card-link:hover { opacity: 0.7; }
.module-card .card-link:hover .arrow { transform: translateX(4px); }

/* "View Movie" button on the Credo3D module card */
.movie-btn {
  margin-top: auto;
  align-self: flex-start;
  padding: 11px 20px;
  font-size: 12px;
}

/* Action row on a module card (e.g. View Movie + Business Case) */
.card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  margin-top: auto;
  padding-top: 22px;
}
.card-actions .movie-btn { margin-top: 0; }
.card-actions .card-link { margin-top: 0; }

/* --------------------------------------------------------------------------
   8.5 AI review — the design-data validation proof point + the Report Assistant
       (#ai)
   All five cards ride the shared carousel (§17) as a card band — the same
   component as the modules (#modules), the Monte Carlo blocks (#monte-carlo)
   and the browser-stack cards (#platform). The Report Assistant opens the band
   on a showcase-width slide (`.carousel__item--wide`, `.ai-card--lead` below);
   the report it writes follows as the band's one picture card (`.ai-shot`
   below), then the three "Example" cards, one card slide each. The `#ai` page
   is therefore a `.page--carousel` page, like every other band on the site.
   -------------------------------------------------------------------------- */
.ai-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 32px 30px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  flex-direction: column;
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.ai-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

.ai-card .ai-index {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 18px;
}

.ai-card h3 { font-size: 22px; margin-bottom: 12px; }
.ai-card p { font-size: 15px; flex: 1 1 auto; }

.ai-card .ai-tag {
  margin-top: 20px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The Report Assistant — the band's opening slide, and the section's flagship
   claim. It takes the showcase width (`.carousel__item--wide`) and it is copy
   only: the shot it used to carry is the slide after it now (`.ai-shot` below).
   Two things keep the double width from reading as an empty box — the copy is
   given a measure (at 15px a line across 880px runs to ~110 characters), and
   the title steps up one notch, because four lines of card copy do not fill a
   showcase-width card. */
.ai-card--lead h3 { font-size: clamp(24px, 2.1vw, 30px); }

.ai-card--lead p {
  font-size: 16px;
  max-width: 58ch;
}

/* The report the assistant writes — the band's one picture card, and the slide
   that gives the band its height: a card's glass (§8.5) holding one square shot,
   `assets/Report_Data_Merge_Small.png` (512 × 512). The frame fills the card's
   content box and carries the shot's own ratio, so a `contain` fit fills it edge
   to edge — no black pillars — and the box is reserved before the lazy image has
   loaded (a frame sized off the image instead collapsed to its border until the
   file arrived). Width in percent keeps the square as large as the slide allows
   at every viewport: 380² on a 440px slide, 244² on a phone's 304px one, with no
   media query. */
.ai-shot .carousel__frame {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
}

.ai-findings {
  margin-top: clamp(36px, 5vw, 56px);
  border-top: 1px solid var(--line);
  padding-top: 20px;
}

.ai-findings__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 16px;
}

.ai-findings__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

.ai-finding {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.02);
  padding: 16px 18px;
}

.ai-finding__tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.ai-finding__tag::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--amber);
}

.ai-finding p {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   9. Chips — micro tags, white borders, tiny coloured dots only
   -------------------------------------------------------------------------- */
.chips { display: flex; flex-wrap: wrap; gap: 10px; margin: 26px 0 30px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.03);
}
.chip .dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.dot--teal  { background: var(--teal); }
.dot--blue  { background: var(--blue); }
.dot--amber { background: var(--amber); }
.dot--corrosion { background: var(--corrosion); }

/* --------------------------------------------------------------------------
   10. Client grid — the Clients page (imported from credosoft.com)
   -------------------------------------------------------------------------- */
.client-grid {
  list-style: none;
  /* The standard block gap under a page head card — the same rhythm as
     .deploy (§7) and the page content that closes a carousel band (§17) —
     so the matrix never sits flush against the head's hairline. */
  margin: clamp(40px, 6vw, 64px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}

.client-grid li {
  padding: 22px 24px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.02);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: background 0.25s var(--ease);
}

.client-grid li:hover { background: rgba(255, 255, 255, 0.05); }

.client-grid .client-name {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}

.client-grid .client-sector {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}

/* --------------------------------------------------------------------------
   11. Stat strip — white numbers, hairline frame
   -------------------------------------------------------------------------- */
.stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 20px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 40px 0;
  margin: clamp(48px, 7vw, 88px) 0 0;
}

.stat .stat-num {
  font-size: clamp(34px, 4vw, 52px);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
  line-height: 1;
  margin-bottom: 10px;
}
.stat .stat-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
}

/* --------------------------------------------------------------------------
   12. CTA page — closing full-viewport window
   -------------------------------------------------------------------------- */
.page--cta { align-items: center; }
.page--cta .page__content { text-align: center; }
.page--cta .page__head { margin: 0 auto; }
.page--cta .actions { justify-content: center; margin-top: 32px; }

/* --------------------------------------------------------------------------
   13. Footer — SpaceX-style multi-column
   -------------------------------------------------------------------------- */
.footer {
  border-top: 1px solid var(--line);
  background: var(--bg-soft);
  padding: clamp(48px, 6vw, 80px) 0 32px;
}

.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, 1fr);
  gap: clamp(24px, 4vw, 56px);
  margin-bottom: clamp(40px, 5vw, 64px);
}

.footer h4 {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 18px;
}

.footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer li { margin-bottom: 10px; }
.footer a {
  font-size: 14px;
  color: var(--faint);
  transition: color 0.2s var(--ease);
}
.footer a:hover { color: var(--text); }

.footer .footer-brand p { font-size: 14px; max-width: 300px; margin-top: 14px; }
.footer .footer-brand img { height: 24px; margin-bottom: 16px; }

.footer__legal {
  border-top: 1px solid var(--line-soft);
  padding-top: 22px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}
.footer__legal a { color: var(--faint); }
.footer__legal a:hover { color: var(--text); }

/* --------------------------------------------------------------------------
   14. Reveal-on-scroll
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.reveal.is-visible { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .hero-panel { animation: none; }
  .scroll-cue { animation: none; }
}

/* --------------------------------------------------------------------------
   15. Video modal — plays the Credo3D module movie in a window
   -------------------------------------------------------------------------- */
.video-modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.video-modal[hidden] { display: none; }

.video-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  animation: fade-in 0.25s var(--ease);
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.video-modal__window {
  position: relative;
  z-index: 1;
  width: min(1080px, 100%);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: #000;
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.8);
  animation: modal-rise 0.35s var(--ease);
  overflow: hidden;
}

@keyframes modal-rise {
  from { opacity: 0; transform: translateY(24px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.video-modal__close {
  position: absolute;
  top: 14px; right: 16px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: rgba(0, 0, 0, 0.6);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.video-modal__close:hover { border-color: var(--text); background: rgba(255, 255, 255, 0.12); }

.video-modal__player {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}

.video-modal__caption {
  padding: 12px 18px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  border-top: 1px solid var(--line-soft);
}

/* --------------------------------------------------------------------------
   16. Responsive
   -------------------------------------------------------------------------- */
/* Desktop header is two rows: brand + language menu, then the nav on its own
   full-width row. Twelve uppercase labels plus the language control do not fit
   beside the brand in every language — the FR nav alone measures 1014.8px at
   1200px, against a 1080px content column the brand and the language menu must
   share. tools/measure-header.mjs reports each row's need per language. Below
   1021px the mobile overlay owns the nav, so the 72px header height still
   applies there. */
@media (min-width: 1021px) {
  /* Brand row (~32px logo) + nav row (~30px) + 10px between them, plus the
     original 20px of breathing room the 72px one-line header had. */
  :root { --header-h: 112px; }

  .header__inner {
    /* The nav has flex-basis 100%, so it wraps onto row two; brand and the
       language menu stay on row one, pushed to the edges by space-between. */
    flex-wrap: wrap;
    row-gap: 10px;
  }

  .nav {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* Row two is full width, so the links line up under the logo instead of
     floating on the right edge as they did in the one-line header. */
  .nav__links { justify-content: flex-start; }

  /* The overlay belongs to the phone band: up here the burger is hidden, so it
     can never be opened — hide it outright instead of leaving a full-screen
     layer sitting at opacity 0 over the page. */
  .mobile-menu { display: none; }
}

/* Twelve labels in the tightest desktop band. The nav is a single flex row
   from 1021px, and between 1021px and 1120px the content column (90% of the
   viewport) is narrower than French needs at 11px / 0.09em — measured, not
   guessed: at 1100px FR needs 1002.8px of 990px, at 1021px 993.1px of 919px.
   One step down in size and tracking clears it with ~29px to spare at the
   tightest width, and nothing else changes. */
@media (min-width: 1021px) and (max-width: 1120px) {
  .nav__links a {
    font-size: 10px;
    letter-spacing: 0.07em;
  }
}

@media (max-width: 1020px) {
  .nav__links { display: none; }
  .burger { display: flex; }
  /* The overlay carries its own language list (.lang-list) at this size. */
  .lang { display: none; }

  /* The overlay scrolls its own content now, so keep the page behind it from
     scrolling along with a drag (scroll chaining — and a jumping scrollbar on a
     narrow desktop window). */
  body.menu-open { overflow: hidden; }

  .footer__grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 560px) {
  .footer__grid { grid-template-columns: 1fr; }
  .actions .btn { width: 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   17. Carousel — horizontal physics-scroll band
   Full-bleed glass band, one item centred at a time, arrows flanking a dot
   sequence underneath. Native momentum on touch/trackpad, drag-with-inertia
   on the mouse (js/main.js → initCarousel). The feature showcase, the module
   cards, the Monte Carlo blocks, the browser-stack cards, the digital-twin
   cards, the damage catalogue and the AI assistant cards (#ai) ride the same
   component — only the slide content differs.
   The band carries a load lead (§17.1): the first slide's resting position —
   the one the band loads on — sits a little left of centre, so the slides
   after it are already in view when the band arrives rather than off the
   right edge.
   -------------------------------------------------------------------------- */
/* A page that carries a carousel stacks its head above the band, so the band
   spans the viewport rather than the content column. */
.page--carousel {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-end;
}

.page--carousel .page__content {
  flex: 0 0 auto;
  padding-bottom: clamp(24px, 4vh, 48px);
}

/* A content column that closes a carousel page — the digital-twin label, note
   and download under the two bands. The band above already carries the vertical
   gap, so the head's top padding is dropped and the blocks keep their own
   margins. */
.page--carousel .page__content--tail {
  padding-top: 0;
}

/* 17.1 Load lead. The track's leading padding is this much shorter than its
   trailing padding, which moves the first slide's resting position — the one
   the band loads on, because the scroll cannot go below 0 — `--carousel-lead`
   left of centre. The band therefore arrives with the first slide *and* a real
   slice of the slides after it on screen, instead of one slide parked in the
   middle of an empty half-window. Every other snap position is still the
   centred one (an arrow, dot or drag lands where it always did, and stepping
   back to the first slide returns to this load position), because those
   targets sit right of 0 and are reached exactly.
   A fifth of the bare width the slide leaves over (two fifths of one side's
   worth), capped at 200px: that stays under half the slide pitch (§17.2,
   440px + gap), which is what keeps the *first* slide the snap target the band
   loads on rather than the second. A phone has almost no bare width to give
   up, so the lead fades to nothing there and the first slide is never clipped.
   js/main.js needs to know nothing: both the CSS snap and centerOn measure
   from the items' own offsetLeft, and a negative target simply clamps to 0. */
.carousel {
  /* Slide width for this band. The module band halves it (§8); the track's
     centring padding is derived from it, so the two always agree. */
  --carousel-slide: min(880px, 86vw);
  --carousel-lead: min(calc((100vw - var(--carousel-slide)) / 5), 200px);

  position: relative;
  z-index: 1;
  width: 100%;
  margin: clamp(12px, 2vw, 20px) 0 clamp(20px, 3vw, 32px);
  padding: clamp(24px, 4vw, 40px) 0 clamp(28px, 5vw, 48px);
  /* A quarter of the 0.5 the band was built with — halved, then halved again:
     at 0.5 the full-bleed glass read as a panel laid over the page rather than
     a window onto it, and one halving still read that way. The frost is what is
     left carrying the effect, so it is untouched — `blur(18px) saturate(1.2)`. */
  background: rgba(8, 11, 15, 0.125);
  -webkit-backdrop-filter: blur(18px) saturate(1.2);
  backdrop-filter: blur(18px) saturate(1.2);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}

/* 17.2 Card band — half a showcase slide, because a card's few lines would
   leave a full-width slide mostly empty. Two or three cards then share the row
   with the active one centred, and the track's centring padding follows the
   variable. Doubled up on the band's class so it out-ranks the default above
   whatever order the two rules are written in. Used by #modules and
   #damage-modeller. */
.carousel.carousel--cards {
  --carousel-slide: min(440px, 78vw);
}

/* The side padding is half a slide, so the active item sits in the middle of
   the window and its neighbours peek in at both edges. The leading side gives
   up the load lead (§17.1) — the one asymmetry in the component, and the
   reason the first slide (the position the band loads on) sits left of
   centre. */
.carousel__track {
  position: relative;
  display: flex;
  gap: clamp(16px, 2.5vw, 32px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  padding: 0 calc((100vw - var(--carousel-slide)) / 2);
  padding-left: calc((100vw - var(--carousel-slide)) / 2 - var(--carousel-lead));
}

.carousel__track::-webkit-scrollbar { display: none; }

.carousel__track.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
}

/* One slide per item, centred in the track. */
.carousel__item {
  flex: 0 0 var(--carousel-slide);
  scroll-snap-align: center;
}

/* A single slide that needs the showcase width inside a card band — the Monte
   Carlo comparison table, whose three columns cannot fold into a card. */
.carousel.carousel--cards .carousel__item--wide {
  flex: 0 0 min(880px, 86vw);
}

/* A media slide stacks its frame above the caption; a card slide is the card. */
.carousel__item--media {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.carousel__frame {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #000;
  overflow: hidden;
  height: clamp(300px, 52vh, 560px);
}

.carousel__frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  -webkit-user-drag: none;
  user-drag: none;
}

.carousel__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
  background: #000;
}

.carousel__item figcaption .kicker { margin-bottom: 8px; }

.carousel__item figcaption h3 {
  font-size: clamp(18px, 2vw, 24px);
  margin: 0 0 6px;
}

.carousel__item figcaption .lead {
  margin: 0;
  font-size: clamp(14px, 1.5vw, 17px);
  max-width: 46ch;
}

/* Control bar under the band — large arrows flanking the dot sequence */
.carousel__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(18px, 4vw, 44px);
  margin-top: clamp(20px, 3vw, 32px);
  padding: 0 var(--gutter);
}

.carousel__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: clamp(52px, 6vw, 64px);
  height: clamp(52px, 6vw, 64px);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: transparent;
  color: var(--text);
  font-size: clamp(20px, 2.4vw, 28px);
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease);
}
.carousel__arrow:hover { border-color: var(--text); background: rgba(255, 255, 255, 0.08); }
.carousel__arrow:active { transform: scale(0.94); }

/* Dot sequence — one dot per item; the active dot is filled white */
.carousel__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.carousel__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.carousel__dot::before {
  content: "";
  width: 9px;
  height: 9px;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: transparent;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease), transform 0.2s var(--ease);
}

.carousel__dot:hover::before { border-color: var(--text); }

.carousel__dot.is-active::before,
.carousel__dot[aria-current="true"]::before {
  background: var(--text);
  border-color: var(--text);
  transform: scale(1.35);
}

@media (max-width: 560px) {
  .carousel__frame { height: clamp(200px, 34vh, 320px); }

  /* Ten or eleven dots plus two arrows cannot share a phone's width inside
     the gutter, so the sequence takes its own row above the arrows: nothing
     is clipped off the screen edge and the arrows keep their touch target. */
  .carousel__nav {
    flex-wrap: wrap;
    gap: 16px 20px;
  }

  .carousel__dots {
    order: -1;
    flex: 1 0 100%;
    flex-wrap: wrap;
    gap: 3px;
  }

  .carousel__dot { width: 24px; height: 28px; }
  .carousel__arrow { width: 44px; height: 44px; }
}

/* --------------------------------------------------------------------------
   18. Digital twin — ISO alignment checklist (#digital-twin)
   -------------------------------------------------------------------------- */
/* Panel wrapping the checklist and the download CTA. Neutral dark glass — the
   same treatment as the other sub-section cards (.module-card / .ai-card /
   .deploy-card / .roadmap-card). */
.twin-panel {
  margin-top: clamp(28px, 4vw, 44px);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 32px 30px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* The ISO framing, split into square cards that ride the shared carousel
   (§17). Square: the band's slide width doubles as the height, and
   `align-self: flex-start` keeps the flex row from stretching them taller. */
.twin-card {
  align-self: flex-start;
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 30px 28px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.twin-card .kicker { margin-bottom: 14px; }

.twin-card p {
  margin: 0;
  font-size: 15px;
}

.twin-checklist {
  margin-top: clamp(24px, 3vw, 36px);
  border-top: 1px solid var(--line);
}

.twin-checklist__label {
  padding: 18px 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  border-bottom: 1px solid var(--line);
}

/* The eleven ISO-alignment rows are one card each on the shared carousel
   (§17): natural height (not square, unlike the framing cards), stretched to
   the tallest card in the row, with the badge pinned to the bottom. */
.twin-card--row {
  align-self: stretch;
  aspect-ratio: auto;
  padding: 26px 28px;
}

.twin-card__concept {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}

.twin-card__std {
  display: block;
  margin-top: 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

.twin-card__desc {
  margin: 14px 0 0;
  font-size: 14px;
  color: var(--muted);
}

.twin-status {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;   /* its own width, not the card's */
  gap: 8px;
  margin-top: auto;         /* pinned to the bottom of the card */
  padding-top: 20px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}

.twin-status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--faint);
}

.twin-status--live { color: var(--text); }
.twin-status--live::before {
  background: var(--teal);
  box-shadow: 0 0 8px rgba(46, 230, 200, 0.6);
}

.twin-status--specified { color: var(--text); }
.twin-status--specified::before {
  background: var(--amber);
  box-shadow: 0 0 8px rgba(255, 193, 77, 0.5);
}

.twin-status--scope { color: var(--faint); }
.twin-status--scope::before { background: var(--faint); }

.twin-checklist__note {
  margin: 18px 0 0;
  font-size: 13px;
  color: var(--faint);
}

.twin-cta {
  margin-top: clamp(28px, 4vw, 40px);
}

/* --------------------------------------------------------------------------
   19. Damage Modeller — API 581 mechanism catalogue (#damage-modeller)
   The four mechanism groups ride the shared carousel as a card band
   (`.carousel--cards`, §17) — the same treatment as the module cards.
   -------------------------------------------------------------------------- */

.damage-group {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 26px 28px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.damage-group__name {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 4px;
}

.damage-group__std {
  display: block;
  margin-top: 5px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

.damage-group__list {
  list-style: none;
  margin: 14px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--line-soft);
}

.damage-group__list li {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 4px 0;
  font-size: 13px;
  line-height: 1.4;
  color: var(--muted);
}

.dmg-ref {
  flex: 0 0 auto;
  min-width: 46px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--faint);
}

/* --------------------------------------------------------------------------
   20. Monte Carlo — probabilistic simulation over API 581 (#monte-carlo)

   The comparison table and the closing line come from the white paper the
   button serves (assets/monte-carlo-api-581-white-paper.pdf). The section's
   blocks are one card each on the shared carousel (§17, `.carousel--cards`):
   the framing, the method, the three cases, the table and the closing line —
   the same component as §18 (Digital Twin), §19 (Damage Modeller) and the
   browser-stack cards on the Platform page: neutral dark glass, white
   hairlines, no accent colour. `.mc-panel`, `.mc-cards` and `.mc-rule` retired
   with the panel (git history).
   -------------------------------------------------------------------------- */
/* The simulator shot that opens the band — the section's first slide, and the
   other `.carousel__item--wide` media slide (the comparison table is the
   second). Frame and caption sit side by side rather than stacked: the
   screenshot is landscape, and stacked it would set the height of the whole
   card row — every `.mc-card` stretches to the tallest item in the line. Side
   by side the slide stays inside the card row's height, so the band keeps its
   rhythm and the graphic stays as large as it can be. On a phone it stacks
   like any other media slide (§17). */
.mc-shot {
  /* The UA `figure` margin (1em 40px) sits outside the slide's basis: 40px of
     dead space down the leading edge and 32px on its height — the one thing
     this slide must not spend, since the frame height is what the card row
     would follow. The #showcase slides keep theirs; their inset is that band's
     look. */
  margin: 0;
  flex-direction: row;
  align-items: center;
  gap: clamp(20px, 3vw, 36px);
}

/* The frame carries the shot's own ratio (2224 × 1750 = 1.271), so a `contain`
   fit fills it edge to edge — no black pillars — and the box is reserved
   before the lazy image has loaded (sizing the frame off the image instead
   collapsed it to its border until the file arrived). Re-export the asset at
   another ratio and this line moves with it. */
.mc-shot .carousel__frame {
  flex: 0 0 auto;
  width: clamp(240px, 42vh, 440px);
  height: auto;
  aspect-ratio: 2224 / 1750;
}

.mc-shot figcaption { flex: 1 1 auto; }

/* On a phone the slide is too narrow for a side caption — stack it, frame
   filling the slide, caption underneath like any other media slide. */
@media (max-width: 560px) {
  .mc-shot { flex-direction: column; align-items: stretch; }

  .mc-shot .carousel__frame { width: 100%; }
}

/* The card that keeps the white paper's "governing rule" look — a heavier left
   edge and a slightly lifted background. */
.mc-card--rule {
  border-left: 2px solid var(--line-strong);
  background: rgba(255, 255, 255, 0.03);
}

/* The card that carries the comparison table. The card is the frame, so the
   wrap drops the section spacing and its top hairline; it still scrolls the
   three columns sideways on a phone. */
.mc-card .mc-table-wrap {
  margin-top: 0;
  border-top: 0;
}

/* The closing line's own top margin belongs to the section, not to a card. */
.mc-card .mc-oneline { margin: 0; }

.mc-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 30px 28px;
  display: flex;
  flex-direction: column;
  min-height: 220px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.mc-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

/* The card's kicker line — a number on the three API 581 cards ("01"), a name
   on the cards that carry a named block instead ("At a glance", "The method")
   — the same voice either way. */
.mc-card__num,
.mc-card__label {
  margin-bottom: 16px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
}

.mc-card h3 { font-size: 21px; margin-bottom: 10px; }
.mc-card p { flex: 1 1 auto; margin: 0; font-size: 15px; }

/* The white paper's comparison table — what the standard reports, and what the
   simulation reports instead. Kept as a real <table>: it is tabular data, and
   a screen reader should be told which column it is hearing. */
.mc-table-wrap {
  margin-top: clamp(28px, 4vw, 40px);
  overflow-x: auto;
  border-top: 1px solid var(--line);
}

.mc-table {
  width: 100%;
  min-width: 620px;              /* three columns never fold on a phone */
  border-collapse: collapse;
}

.mc-table__caption {
  padding: 18px 0 14px;
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  border-bottom: 1px solid var(--line);
}

.mc-table th,
.mc-table td {
  padding: 14px 20px 14px 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
  font-size: 14px;
}

.mc-table thead th {
  padding-top: 16px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}

.mc-table tbody th { font-weight: 700; color: var(--text); }
.mc-table td { color: var(--muted); }

/* "In one line" — the paper's closing line, set as the section's takeaway */
.mc-oneline {
  max-width: 780px;
  margin: clamp(22px, 3vw, 28px) 0 0;
  font-size: 15px;
  color: var(--text);
}

.mc-oneline__label {
  display: block;
  margin-bottom: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
}

/* Download row — the white-paper button that closes the section. A flex row,
   so a status badge (the §18 "Specified" dot) can sit beside the button if the
   section ever needs one. */
.mc-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: clamp(28px, 4vw, 40px);
}

@media (max-width: 560px) {
  .mc-panel { padding: 26px 22px; }
  .mc-table th,
  .mc-table td { padding-right: 14px; }
}

/* --------------------------------------------------------------------------
   21. Roadmap — Credo8 stays the core, Credo3D + AI builds on top
   -------------------------------------------------------------------------- */
.roadmap-stack {
  max-width: 640px;
  margin-top: clamp(40px, 6vw, 64px);
}

.roadmap-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 32px 30px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  flex-direction: column;
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease);
}

.roadmap-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }

/* The new visualisation + AI layer reads as the emphasised top card */
.roadmap-card--add { border-color: var(--line-strong); }

/* The Credo8 core platform is the foundation the additive layer runs on */
.roadmap-card--core { border-color: rgba(46, 230, 200, 0.28); }

.roadmap-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 18px;
}

.roadmap-card h3 { font-size: 24px; margin-bottom: 12px; }
.roadmap-card p { font-size: 15px; color: var(--muted); }

.roadmap-list {
  list-style: none;
  margin: 20px 0 24px;
  padding: 0;
  border-top: 1px solid var(--line-soft);
}

.roadmap-list li {
  padding: 12px 0 12px 22px;
  position: relative;
  font-size: 14px;
  color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}

.roadmap-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 19px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--line-strong);
}

.roadmap-card .status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
}

.roadmap-card .status::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--faint);
}

.roadmap-card .status--live { color: var(--text); }
.roadmap-card .status--live::before {
  background: var(--teal);
  box-shadow: 0 0 8px rgba(46, 230, 200, 0.6);
}

/* Connector between the additive layer and the core platform */
.roadmap-join {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 18px 0;
}

.roadmap-join::before,
.roadmap-join::after {
  content: "";
  height: 1px;
  width: 40px;
  background: var(--line);
}

.roadmap-join span {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--faint);
}

/* Parallel-deployment note */
.roadmap-deploy {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  max-width: 640px;
  margin-top: 24px;
  padding: 20px 24px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-card);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.roadmap-deploy__label {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text);
  white-space: nowrap;
}

.roadmap-deploy p {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

@media (max-width: 560px) {
  .roadmap-deploy { flex-direction: column; gap: 8px; }
}

/* --------------------------------------------------------------------------
   22. Molecule — Credo8 core with Credo AI + Credo3D in orbit (top of page)

   Inline SVG + CSS, plus a little vanilla JS (see js/main.js). The core
   swings its three satellites ±20° about the centre — clockwise, then back
   anticlockwise — so the band reads as a molecule flexing. Each satellite
   sits in its own group (bond + node + label travel together) and the labels
   carry an exact inverse rotation so they stay upright. The band sits on a
   starfield and hovering a circle lights a red pointer flare.

   ↳ .page--molecule is the one sanctioned exception to the full-viewport
     rule: 75svh, full browser width (see Website_Design_Guide.md §11).
   -------------------------------------------------------------------------- */
.page--molecule {
  min-height: 75svh;
  align-items: center;
  justify-content: center;
  padding-top: var(--header-h);
  /* a deep-space black plus a whisper of brand teal — the starfield above it
     (see .molecule__stars) carries the scene */
  background:
    radial-gradient(58% 62% at 50% 50%, rgba(46, 230, 200, 0.04) 0%, rgba(46, 230, 200, 0) 62%),
    var(--bg);
}

/* The 16:9 scene fills the browser width, capped by the 75svh height budget */
.molecule {
  position: relative;
  z-index: 1;
  display: block;
  width: min(100%, calc((75svh - var(--header-h)) * 1.7778));
  height: auto;
  font-family: var(--font);
}

/* --------------------------------------------------------------------------
   Starfield — the molecule band's backdrop.
   Three star layers are SVG data URIs built once in js/main.js (random
   positions, no visible tiling and an even scatter). Drift and twinkle are
   CSS only — never per-frame JS. Falls back silently to nothing if JS is off.
   -------------------------------------------------------------------------- */
.molecule__stars {
  position: absolute;
  inset: -6%;                    /* oversized, so the drift never shows an edge */
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;                    /* lit once the field has been drawn */
  background-repeat: no-repeat;
  background-size: 100% 100%;
  transition: opacity 1.4s var(--ease);
  animation: molecule-drift-far 260s ease-in-out infinite alternate;
}

.molecule__stars.is-lit { opacity: 1; }

.molecule__stars::before,
.molecule__stars::after {
  content: "";
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* Near field — drifts against the far field, for parallax */
.molecule__stars::before {
  background-image: var(--stars-near);
  animation: molecule-drift-near 170s ease-in-out infinite alternate;
}

/* The brightest stars shimmer */
.molecule__stars::after {
  background-image: var(--stars-bright);
  animation: molecule-twinkle 6.5s ease-in-out infinite alternate;
}

@keyframes molecule-drift-far {
  from { transform: translate3d(-1.2%, -0.6%, 0); }
  to   { transform: translate3d(1.2%, 0.6%, 0); }
}

@keyframes molecule-drift-near {
  from { transform: translate3d(2%, 1%, 0); }
  to   { transform: translate3d(-2%, -1%, 0); }
}

@keyframes molecule-twinkle {
  from { opacity: 0.45; }
  to   { opacity: 1; }
}

/* Orbit track the satellites slide along */
.molecule__ring {
  fill: none;
  stroke: rgba(255, 255, 255, 0.16);
  stroke-width: 1;
  stroke-dasharray: 2 10;
}

/* Bonds — hairline white, never teal (Design Guide §11) */
.molecule__link {
  stroke: rgba(255, 255, 255, 0.28);
  stroke-width: 1.5;
}

/* A second, brighter dashed bond running core edge → node edge */
.molecule__link--flow {
  stroke: rgba(255, 255, 255, 0.6);
  stroke-linecap: round;
  stroke-dasharray: 3 15;
  animation: molecule-flow 2.4s linear infinite;
}

/* One dash period (3 + 15) per cycle = a seamless loop */
@keyframes molecule-flow {
  to { stroke-dashoffset: -18; }
}

.molecule__node {
  fill: url(#mol-node);
  stroke: rgba(255, 255, 255, 0.42);
  stroke-width: 1.5;
}

/* Roadmap node (Credo RBI — not shipped): a dimmer solid rim, plus a muted
   label and no data-flow bond, keep it reading as planned (Design Guide §1).
   Must follow .molecule__node. */
.molecule__node--roadmap {
  stroke: rgba(255, 255, 255, 0.3);
}

/* Core — teal glass hairline, the same treatment as .hero-panel.
   The bonds now meet the core at its outer edge, so the core must never
   move: its pulse animates stroke opacity only — a transform would tear
   the joint open. */
.molecule__core {
  fill: url(#mol-core);
  stroke: rgba(46, 230, 200, 0.3);
  stroke-width: 1.5;
  animation: molecule-pulse 7s ease-in-out infinite;
}

@keyframes molecule-pulse {
  0%, 100% { stroke-opacity: 0.55; }
  50%      { stroke-opacity: 1; }
}

/* Satellites: clockwise 20°, then back anticlockwise 20° */
.molecule__sat {
  transform-box: view-box;
  transform-origin: 600px 337.5px;
  animation: molecule-swing 14s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}

/* One 14s cycle split into thirds, so the three nodes travel as a wave:
   Credo AI at 0%, Credo3D at 33%, Credo RBI at 67% */
.molecule__sat--3d  { animation-delay: -4.67s; }
.molecule__sat--rbi { animation-delay: -9.33s; }

@keyframes molecule-swing {
  0%   { transform: rotate(0deg); }
  50%  { transform: rotate(20deg); }
  100% { transform: rotate(0deg); }
}

/* Node labels: exact inverse of the parent swing, so they stay upright */
.molecule__label {
  fill: #fff;
  font-size: 32px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-anchor: middle;
  dominant-baseline: central;
  text-transform: uppercase;
  transform-box: view-box;
  animation: molecule-swing-rev 14s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}

.molecule__sat--ai .molecule__label { transform-origin: 354.3px 165.4px; }
.molecule__sat--3d .molecule__label { transform-origin: 871.9px 464.3px; animation-delay: -4.67s; }
.molecule__sat--rbi .molecule__label { transform-origin: 340.2px 487.5px; animation-delay: -9.33s; }

/* The roadmap node's label is muted to match its dashed rim */
.molecule__label--roadmap { fill: var(--muted); }

/* --------------------------------------------------------------------------
   Pointer flare — a red light that follows the cursor inside a circle.
   JS sets only a translate on .molecule__glow-dot; each dot is ~0.6 of its
   circle's radius (so it reads as a flare, not a wash) and the circle's own
   clipPath keeps every pixel of it inside the rim.
   -------------------------------------------------------------------------- */
.molecule__node,
.molecule__core { pointer-events: all; cursor: crosshair; }

.molecule__glow {
  opacity: 0;
  pointer-events: none;              /* never steal the hit test from the circle */
  transition: opacity 0.25s var(--ease);
}

.molecule__glow.is-active { opacity: 1; }

.molecule__glow-dot { fill: url(#mol-flare); }

@keyframes molecule-swing-rev {
  0%   { transform: rotate(0deg); }
  50%  { transform: rotate(-20deg); }
  100% { transform: rotate(0deg); }
}

.molecule__core-label {
  fill: #fff;
  font-size: 54px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-anchor: middle;
  dominant-baseline: central;
  text-transform: uppercase;
}

@media (max-width: 720px) {
  .page--molecule { min-height: 68svh; }
}

@media (prefers-reduced-motion: reduce) {
  .molecule__sat,
  .molecule__label,
  .molecule__core,
  .molecule__link--flow { animation: none; }

  /* the pointer flare still works, it just snaps instead of easing in */
  .molecule__glow { transition: none; }

  /* the starfield still appears — it just stops drifting and shimmering */
  .molecule__stars { animation: none; transition: none; }
  .molecule__stars::before,
  .molecule__stars::after { animation: none; }
}
