/* NEUON AI — Dr. KC visual direction. Brand rules: only these three
   colours (Brand Book) — blue/black/white — self-hosted Inter. */
@font-face { font-family:'Inter'; font-weight:400; font-display:swap; src:url('/assets/fonts/inter-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family:'Inter'; font-weight:700; font-display:swap; src:url('/assets/fonts/inter-latin-700-normal.woff2') format('woff2'); }

:root {
  --blue:#0078FF; --black:#0A0203; --white:#FFFFFF;
  --blue-deep:#004fa8; --blue-pale:#eaf3ff;
}
html { overflow-x:clip; }
* { margin:0; padding:0; box-sizing:border-box; }
html { scroll-behavior:smooth; }
body {
  font-family:'Inter',Helvetica,Arial,sans-serif; color:var(--black); background:var(--white);
  line-height:1.5; overflow-x:hidden;
  /* clip on the ELEMENT that scrolls, too. overflow-x:hidden on body alone
     still lets iOS pan the page when something inside is too wide, and a
     panned page looks like the left edge has been cut off. */
  overflow-x:clip;
}
h1,h2,h3,h4 { font-weight:700; line-height:1.15; }
button { font-family:inherit; }

/* ---- slideshow deck: each section fills the viewport, scroll snaps ----
   min-height (not fixed height) so a tall slide grows instead of pushing
   its own content up behind the fixed nav bar.

   ONE SCROLLBAR, and it is the page's.
   The deck used to be `height:100vh; overflow-y:auto` — a scroller of its own —
   while the Starter Pack, the arcade and the footer sat underneath it in the
   page's scrollbar. So a wheel that reached the bottom of NEUFAMILY simply
   stopped: the deck had run out, and the rest of the site was behind a second
   scrollbar you had to go and find with the mouse. Now the document scrolls
   everything and the snapping moves up to the root with it, which is also what
   lets a phone's address bar collapse the way it does on every other page. */
.deck-page { scroll-snap-type:y mandatory; }
.slide {
  scroll-snap-align:start; scroll-snap-stop:always;
  min-height:100vh; width:100%; position:relative; overflow:hidden;
  display:flex; flex-direction:column; justify-content:center;
  /* Clearance is measured from the DISC, not the bar. The disc hangs below the
     bar and is now half as big again as it was, so a section measured against
     var(--nav-h) had its heading running underneath it — which is exactly what
     happened to OUR COMMITMENT. */
  padding:calc(var(--nav-disc) + 1.6rem) clamp(1.6rem,6vw,6rem) 3rem;
}
/* What comes after the deck has to be snap stops too. Mandatory snapping will
   not come to rest anywhere that isn't one, so without these the page slides
   back up to NEUFAMILY the moment you let go of the wheel — the same dead end
   in a new disguise. The footer aligns to `end`, its bottom against the
   viewport's, because that is where the page stops scrolling anyway. */
.deck-page .pack, .deck-page .arcade { scroll-snap-align:start; }
.deck-page .pgfoot { scroll-snap-align:end; }

.slide--light { background:var(--white); color:var(--black); }
.slide--dark { background:var(--black); color:var(--white); }
.slide--blue { background:var(--blue); color:var(--white); }
.slide__title { font-size:clamp(2rem,4.4vw,3.4rem); letter-spacing:-0.01em; margin-bottom:1rem; }
.slide__title--hero { color:var(--white); font-size:clamp(2.1rem,5vw,3.8rem); max-width:44rem; position:relative; z-index:2; }
.kicker { font-weight:700; letter-spacing:0.14em; text-transform:uppercase; font-size:0.85rem; color:var(--blue); margin-bottom:0.6rem; }
.slide--dark .kicker, .slide--blue .kicker { color:var(--white); opacity:0.85; }
.lede { font-size:clamp(1.05rem,1.6vw,1.3rem); max-width:40rem; opacity:0.85; margin-bottom:1.4rem; }
.lede--light { color:var(--white); opacity:0.92; }
/* `text-decoration:none` because half of these are <a> elements and a link
   is underlined by default. Most of them happened to sit inside something
   that cleared it — the nav pill, an orbit node — and "Explore open roles" on
   NEUFamily did not, so it was the one button on the site wearing an
   underline. Cleared here, on the button itself, rather than by adding a
   fourth place that happens to clear it. */
.btn { border-radius:999px; padding:0.85rem 1.7rem; font-weight:700; font-size:0.95rem; cursor:pointer; border:2px solid var(--white); transition:transform 0.2s ease; text-decoration:none; }
.btn:hover { transform:translateY(-2px); }
.btn--white { background:var(--white); color:var(--blue); }
.btn--outline { background:transparent; color:var(--white); }

/* ---- nav: fixed floating capsule/pill ---- */
/* the bar is shorter than the circle that sits on it — see .nav__brand */
/* Dr. KC, on seeing it on the big screen: "Make this logo. Make it bigger."
   The disc is the one thing a room full of people looks at first, and at the
   old size the mark inside it was a smudge from four metres away. Everything
   in the credentials dome is sized off this number, so growing it grows the
   certificates with it. */
:root { --nav-h:3.9rem; --nav-disc:clamp(6.6rem,10.5vw,9.5rem); }
/* The capsule is centred on its own. The search button is taken out of the
   flow and hung off the pill's right edge, so adding it can never pull the
   logo off centre — the disc has to sit dead centre for the credentials
   circle to open symmetrically behind it. */
/* Dr. KC's bar: a full-width white bar with smooth bottom corners, not a
   floating capsule. The blue NEUON circle sits on it, centred, hanging below
   its lower edge, with radar waves rolling out of it. */
.nav { position:fixed; top:0; left:0; right:0; z-index:80; display:block; pointer-events:none; }
.nav__search {
  position:absolute; right:clamp(1.2rem,4vw,3rem); top:50%; transform:translateY(-50%);
  pointer-events:auto; width:2.8rem; height:2.8rem; border-radius:50%;
  background:var(--blue-pale); border:0; color:var(--blue); cursor:pointer;
  display:grid; place-items:center; padding:0;
  transition:background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.nav__search:hover { background:var(--blue); color:var(--white); transform:translateY(-50%) scale(1.06); }

/* NEU's face, at the right-hand edge of the bar.
   It used to sit one button's width in, to leave room for a magnifying glass
   beside it. That glass is gone — NEU is the search button now — so the
   inset went with it, and the face is back on the edge where the eye looks
   for it. Positioned off the edge rather than in the flow so it can never
   pull the logo disc off centre. */
.nav__neu {
  position:absolute; right:clamp(1.2rem,4vw,3rem); top:50%; transform:translateY(-50%);
  pointer-events:auto; width:2.8rem; height:2.8rem; border-radius:50%;
  background:var(--blue-pale); border:0; cursor:pointer; padding:0;
  display:grid; place-items:center; overflow:visible;
  transition:background 0.2s ease, transform 0.2s ease;
}
.nav__neu img {
  width:2.5rem; height:2.5rem; object-fit:contain; object-position:top center;
  border-radius:50%; display:block;
}
.nav__neu:hover { background:var(--blue); transform:translateY(-50%) scale(1.06); }
/* the little padlock, while it is still asleep */
.nav__neulock {
  position:absolute; right:-0.1rem; bottom:-0.1rem;
  width:1.05rem; height:1.05rem; border-radius:50%;
  background:var(--white); color:var(--blue);
  display:grid; place-items:center;
  box-shadow:0 1px 4px rgba(10,2,3,0.28);
}
/* a gentle look-at-me, once, so it is noticed without nagging */
.nav__neu.is-new { animation:navneu-hi 2.4s ease 1.2s 2; }
@keyframes navneu-hi {
  0%, 100% { transform:translateY(-50%) rotate(0); }
  8%  { transform:translateY(-50%) rotate(-9deg); }
  16% { transform:translateY(-50%) rotate(7deg); }
  24% { transform:translateY(-50%) rotate(0); }
}
@media (prefers-reduced-motion: reduce) { .nav__neu.is-new { animation:none; } }

/* On the main page the face in the bar IS the way in, so the floating bubble
   would be the same door twice. The panel it opens is the same one. */
body:has(.nav__neu) .nchat__bubble { display:none; }

/* No phone override. The first version shrank the face to 2.5rem while the
   search button stayed 2.8rem, which left them different sizes three pixels
   apart — a pair that reads as two of the same thing has to BE two of the same
   thing. There is room for both at full size even at 375px: the logo disc
   drops to its own minimum on a narrow screen and never reaches them. */
.nav__pill {
  position:relative;   /* anchors the search button to the bar's edge */
  pointer-events:auto; display:flex; align-items:center; justify-content:center;
  gap:clamp(1rem,2.6vw,2.4rem);
  background:var(--white); padding:0.85rem clamp(1.2rem,4vw,3rem);
  height:var(--nav-h);
  border-radius:0 0 clamp(1rem,2vw,1.8rem) clamp(1rem,2vw,1.8rem);
  box-shadow:0 10px 30px rgba(10,2,3,0.13);
  overflow:visible;    /* the circle hangs past the bottom edge */
}
/* equal-width sides keep the disc on the capsule's true centre */
.nav__side { display:flex; align-items:center; gap:clamp(1rem,2.6vw,2.4rem); flex:1 1 0; min-width:0; }
.nav__side--l { justify-content:flex-end; }
.nav__side--r { justify-content:flex-start; }
.nav__pill a:not(.nav__brand) { color:var(--blue); text-decoration:none; font-weight:700; font-size:0.95rem; opacity:0.6; transition:opacity 0.2s ease; white-space:nowrap; }
.nav__pill a:not(.nav__brand):hover, .nav__pill a.active { opacity:1; }
/* The circle is bigger than the bar and hangs below it. The button keeps a
   fixed width in the flow so it still reserves the centre slot, while the
   image itself is taken out of flow so it can overflow downward without
   stretching the bar. */
.nav__brand { width:var(--nav-disc); align-self:stretch; flex:none; }
/* solid blue circle carrying the white mark — Dr. KC's slide */
.nav__disc {
  position:absolute; left:50%; top:0.2rem; transform:translateX(-50%);
  width:var(--nav-disc); height:var(--nav-disc); border-radius:50%;
  background:var(--blue); display:grid; place-items:center; z-index:2;
  box-shadow:0 8px 22px -6px color-mix(in srgb, var(--blue) 65%, transparent);
  transition:transform 0.25s ease, box-shadow 0.25s ease;
}
.nav__disc img { width:70%; height:auto; display:block; }
.nav__brand--btn:hover .nav__disc {
  transform:translateX(-50%) scale(1.06);
  box-shadow:0 10px 30px -6px color-mix(in srgb, var(--blue) 85%, transparent);
}
.nav__brand--btn:active .nav__disc { transform:translateX(-50%) scale(0.97); }

/* radar waves rolling out of the circle, always running — the same language
   as the rings on "We Teach, It Learn", so it reads as a live control */
.nav__radar {
  position:absolute; left:50%; top:calc(0.2rem + var(--nav-disc) / 2);
  transform:translate(-50%,-50%); pointer-events:none; z-index:1;
}
.nav__radar i {
  position:absolute; left:50%; top:50%;
  width:var(--nav-disc); height:var(--nav-disc);
  margin-left:calc(var(--nav-disc) / -2);
  margin-top:calc(var(--nav-disc) / -2);
  border:2px solid var(--blue); border-radius:50%; opacity:0;
  animation:nav-radar 3s ease-out infinite;
}
.nav__radar i:nth-child(2) { animation-delay:1s; }
.nav__radar i:nth-child(3) { animation-delay:2s; }
@keyframes nav-radar {
  0%   { transform:scale(0.96); opacity:0.55; }
  100% { transform:scale(1.9);  opacity:0; }
}
@media (prefers-reduced-motion:reduce) { .nav__radar i { animation:none; } }

/* ---- mobile burger (hidden on desktop) ---- */
.nav__burger {
  display:none; background:none; border:0; cursor:pointer; padding:0.4rem;
  flex-direction:column; gap:5px; width:2.2rem;
}
.nav__burger span { display:block; height:2px; width:20px; background:var(--blue); border-radius:2px; transition:transform 0.25s ease, opacity 0.2s ease; }
.nav__burger.is-open span:nth-child(1) { transform:translateY(7px) rotate(45deg); }
.nav__burger.is-open span:nth-child(2) { opacity:0; }
.nav__burger.is-open span:nth-child(3) { transform:translateY(-7px) rotate(-45deg); }

/* ---- mobile drawer ---- */
.drawer {
  position:fixed; inset:0; z-index:75; background:var(--white);
  padding:calc(var(--nav-h) + 1.6rem) 1.6rem 2rem; overflow-y:auto;
  animation:drawer-in 0.25s ease;
}
@keyframes drawer-in { from { opacity:0; transform:translateY(-8px); } }
.drawer__label { font-size:0.72rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:var(--blue); margin:1.2rem 0 0.6rem; }
.drawer__links { display:grid; gap:0.1rem; }
.drawer__links a {
  display:block; padding:0.85rem 0.4rem; color:var(--black); text-decoration:none;
  font-weight:700; font-size:1.05rem; border-bottom:1px solid var(--line, #e8e8ea);
}
.drawer__links a:active { background:var(--blue-pale); }
.drawer__links--chapters a { font-size:0.98rem; color:var(--blue-deep); }
body.drawer-open, body.search-open { overflow:hidden; }

/* ---- search overlay ---- */
.search {
  position:fixed; inset:0; z-index:90; background:rgba(10,2,3,0.55);
  backdrop-filter:blur(4px); display:flex; justify-content:center;
  padding:calc(var(--nav-h) + 1.2rem) 1.2rem 2rem;
}
/* an explicit display value beats the `hidden` attribute, so restore it */
.search[hidden], .drawer[hidden] { display:none; }
.search__box { width:min(38rem,100%); background:var(--white); border-radius:18px; overflow:hidden; box-shadow:0 30px 70px rgba(0,0,0,0.4); max-height:100%; display:flex; flex-direction:column; }
.search__field { display:flex; align-items:center; gap:0.7rem; padding:1rem 1.2rem; border-bottom:1px solid #ececee; color:var(--blue); }
.search__field input { flex:1; border:0; outline:none; font:400 1.05rem 'Inter',sans-serif; color:var(--black); background:none; }
.search__close { background:none; border:0; cursor:pointer; font-size:1rem; color:var(--black); opacity:0.5; }
.search__close:hover { opacity:1; }
.search__results { list-style:none; overflow-y:auto; }
.search__results a { display:flex; align-items:baseline; gap:0.7rem; padding:0.85rem 1.2rem; text-decoration:none; color:var(--black); font-size:0.95rem; border-bottom:1px solid #f4f4f6; }
.search__results a:hover { background:var(--blue-pale); }
.search__kind { flex:none; font-size:0.66rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase; color:var(--blue); border:1px solid currentColor; border-radius:999px; padding:0.1rem 0.45rem; }
.search__empty { padding:1.2rem; opacity:0.6; font-size:0.92rem; }

/* ---- side rail: dots marking each slide, click to jump ---- */
.rail { position:fixed; right:1.4rem; top:50%; transform:translateY(-50%); z-index:70; display:flex; flex-direction:column; gap:0.6rem; }
/* P&G-style rail: quiet blue dots that glow, with the active one lit and
   labelled on hover so a presenter can see where they are in the running order */

/* IT NEEDS SOMETHING TO SIT ON.
   The dots are fixed, so they float over whatever the page happens to have
   under them at that height — a dark photograph in one section, a white panel
   in the next, and after the hero reel went to one big row, a bright still of
   a red car. Blue at 32% over a red car is not a control, it is a smudge, and
   the top dots were landing on a card's rounded corner and reading as damage.

   No arrangement of colours survives an arbitrary background. So the rail
   brings its own: a blurred capsule the dots always sit on, which means they
   look the same on every section of every page and nobody has to guess what
   is behind them. */
.rail {
  align-items:center;
  padding:0.8rem 0.72rem;
  border-radius:999px;
  background:color-mix(in srgb, #0a0a12 46%, transparent);
  -webkit-backdrop-filter:blur(10px) saturate(1.15);
          backdrop-filter:blur(10px) saturate(1.15);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--white) 16%, transparent),
             0 12px 32px -16px rgba(0,0,0,0.75);
}
.rail__dot {
  position:relative; width:9px; height:9px; border-radius:50%;
  /* white on the capsule, not blue: blue at low opacity on a dark blurred
     plate is nearly the plate. Blue is kept for the one that is lit. */
  background:color-mix(in srgb, var(--white) 45%, transparent);
  border:0; cursor:pointer; padding:0;
  transition:background 0.25s ease, transform 0.25s ease, box-shadow 0.3s ease;
}
.rail__dot:hover { background:var(--white); transform:scale(1.3); }
.rail__dot.active {
  background:var(--blue); transform:scale(1.5);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 22%, transparent),
             0 0 12px 2px color-mix(in srgb, var(--blue) 70%, transparent);
  animation:rail-glow 2.4s ease-in-out infinite;
}
@keyframes rail-glow {
  0%,100% { box-shadow:0 0 0 3px color-mix(in srgb, var(--blue) 20%, transparent),
                       0 0 10px 1px color-mix(in srgb, var(--blue) 55%, transparent); }
  50%     { box-shadow:0 0 0 6px color-mix(in srgb, var(--blue) 10%, transparent),
                       0 0 18px 4px color-mix(in srgb, var(--blue) 85%, transparent); }
}
/* the pill label, as on us.pg.com */
.rail__dot::after {
  /* clear of the capsule, not touching it: the offset is measured from the
     dot, and the capsule's own padding sits between the two */
  content:attr(data-label); position:absolute; right:calc(100% + 1.15rem); top:50%;
  transform:translateY(-50%) translateX(6px); white-space:nowrap;
  background:var(--white); color:var(--blue); font-weight:700; font-size:0.78rem;
  padding:0.35rem 0.85rem; border-radius:999px;
  /* the label lands on a photograph as often as not — the hairline is what
     keeps a white pill from bleeding into a bright still behind it */
  box-shadow:0 10px 26px -8px rgba(10,2,3,0.55),
             0 0 0 1px color-mix(in srgb, var(--black) 10%, transparent);
  opacity:0; pointer-events:none; transition:opacity 0.2s ease, transform 0.2s ease;
}
.rail__dot:hover::after, .rail__dot.active::after { opacity:1; transform:translateY(-50%) translateX(0); }
@media (prefers-reduced-motion:reduce) { .rail__dot.active { animation:none; } }

/* ---- hero + certified badges (shared abstract "aerial terrain" backdrop) ---- */
/* A still, not a film. The looping video made the section stutter on the
   laptops this is presented from — section 1 already animates a reel of cards
   over the top of it, and asking the same machine to decode video underneath
   that was one job too many. The picture is a frame of the same footage, so
   nothing was lost but the movement.

   The plate is bright silver and the headline over it is white, so it is
   dimmed and cooled toward the brand blue here rather than in the file — that
   way the same photograph can be reused somewhere brighter. */
.hero__bg {
  position:absolute; inset:0; z-index:0;
  background:url('/assets/video/hero-kid-globe.jpg') center center/cover no-repeat, #08131f;
  filter:brightness(0.62) contrast(1.06) saturate(1.15) hue-rotate(-6deg);
}
/* a phone does not need the 1920px plate */
@media (max-width:820px) {
  .hero__bg { background-image:url('/assets/video/hero-kid-globe-sm.jpg'); }
}

/* Two washes, not one: a dark band top and bottom so the kicker, the headline
   and the film reel all keep their contrast, and a blue cast through the
   middle so the plate belongs to the rest of the site. */
.hero__scrim {
  /* Decoration, and decoration must never take a click. This is a full-screen
     box sitting at z-index 1 over the whole of Section 1; without this line it
     quietly swallows every press on anything painted below it. Every other
     scrim on the site already says this — .teach__scrim does — and this one
     was the odd one out. */
  pointer-events:none;
  position:absolute; inset:0; z-index:1;
  background:
    linear-gradient(180deg,
      rgba(4,10,20,0.78) 0%,
      rgba(4,10,20,0.30) 26%,
      rgba(4,10,20,0.34) 62%,
      rgba(4,10,20,0.86) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--blue) 26%, transparent),
      color-mix(in srgb, var(--blue) 10%, transparent));
}
.slide--hero { align-items:flex-start; }
.slide--certified { align-items:center; }

/* ---- certified badge disc ---- */
.badge-disc {
  position:relative; z-index:2; background:var(--white); border-radius:50%;
  width:min(30rem,86vw); aspect-ratio:1; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:1rem; padding:3rem 2rem; box-shadow:0 30px 70px rgba(0,0,0,0.35);
}
.badge-disc__logo { height:64px; }
.badge-disc__list { list-style:none; display:flex; flex-wrap:wrap; gap:0.6rem 1rem; justify-content:center; max-width:22rem; }
/* ---- logo placeholders: plain blue shape + "L", real logos come later ---- */
.logo-ph {
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--blue); color:var(--white); font-weight:700;
  border-radius:8px; flex:none; line-height:1;
}
.badge-disc__list li { width:2.6rem; height:2.6rem; font-size:1.1rem; }

/* ---- We Teach, It Learn ---- */
.teach { position:relative; display:flex; flex-direction:column; align-items:center; text-align:center; gap:2.2rem; }
.teach__brain { width:min(20rem,60vw); aspect-ratio:1; display:flex; align-items:center; justify-content:center; }
.brain-ring { fill:none; stroke:var(--blue); stroke-width:1.4; opacity:0.5; }
.brain-ring--in { stroke-width:2.4; opacity:0.9; }
.teach__domains { list-style:none; display:flex; gap:1rem; flex-wrap:wrap; justify-content:center; }
.teach__domains li { border:1px solid color-mix(in srgb, var(--white) 30%, transparent); padding:0.5rem 1.1rem; border-radius:999px; font-size:0.85rem; font-weight:700; }

/* ---- Sovereign Applied AI (map hub + orbit; see orbit section below) ---- */
.slide#sovereign { align-items:center; text-align:center; }
.compact-quote { border-left:4px solid var(--blue); border-radius:14px; background:var(--blue-pale); padding:1.4rem 1.8rem; max-width:34rem; margin:0 auto; text-align:left; }
.compact-quote h4 { color:var(--blue); font-size:0.85rem; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:0.4rem; }

.slide--blue.slide#how-it-works { align-items:center; text-align:center; }

/* ---- Our Commitment ---- */
.commit__row { display:flex; align-items:center; justify-content:space-between; gap:2rem; flex-wrap:wrap; }
.thanks { font-weight:700; color:var(--blue); margin-top:1rem; }
.seal { flex:none; width:8rem; height:8rem; border-radius:50%; border:3px dashed var(--blue); display:flex; align-items:center; justify-content:center; text-align:center; font-weight:700; color:var(--blue); font-size:0.8rem; }
.logo-strip { list-style:none; display:flex; flex-wrap:wrap; gap:1rem; margin-top:2.6rem; }
.logo-strip li { width:4.4rem; height:2.8rem; font-size:1.2rem; border-radius:8px; }

/* ---- SDG ---- */
.sdg__title { color:var(--blue); }
.sdg__band { display:grid; grid-template-columns:repeat(3,1fr); gap:0.6rem; margin-top:1.6rem; max-width:60rem; }
.sdg__photo { aspect-ratio:4/3; border-radius:10px; }
.sdg__photo--sea { background:linear-gradient(160deg,#bcdcee,#5c8fae); }
.sdg__photo--earth { background:radial-gradient(circle at 40% 40%, #2f6fae, #06162b); }
.sdg__photo--forest { background:linear-gradient(160deg,#c9e6a8,#3f7a3f); }

/* ---- NEUGrowth / IPO 2030 ---- */
.slide#growth { align-items:center; text-align:center; }
.phase-row { list-style:none; display:grid; grid-template-columns:repeat(4,minmax(160px,1fr)); gap:1.2rem; margin-top:1.6rem; max-width:64rem; }
.phase-row li { border:1px solid color-mix(in srgb, var(--white) 25%, transparent); border-top:3px solid var(--blue); border-radius:12px; padding:1.2rem 1rem; text-align:left; }
.phase-row__period { color:var(--blue); font-weight:700; font-size:0.8rem; }
.phase-row h3 { font-size:1rem; margin:0.3rem 0; }
.phase-row p { font-size:0.8rem; opacity:0.75; }

/* ---- News grid ---- */
.news-grid { list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:1.2rem; margin-top:1.6rem; max-width:64rem; }
.news-card { border:1px solid color-mix(in srgb, var(--black) 12%, var(--white)); border-radius:14px; padding:1.4rem; }
.news-card__tag { color:var(--blue); font-weight:700; font-size:0.75rem; letter-spacing:0.08em; text-transform:uppercase; }
.news-card p { margin-top:0.5rem; font-size:0.92rem; }

/* ---- NEUAllies ---- */
.slide#allies { align-items:flex-start; }
.allies__card { border:1px solid color-mix(in srgb, var(--white) 25%, transparent); border-radius:14px; padding:1.4rem 1.8rem; margin-top:1rem; }
.allies__card h4 { color:var(--blue); }

/* ---- NEUFamily ---- */
.family__row { display:flex; align-items:center; justify-content:space-between; gap:3rem; flex-wrap:wrap; }
.family__photo { width:min(22rem,70vw); aspect-ratio:1; border-radius:50%; background:radial-gradient(circle at 35% 30%, #cfe0f7, #6f8fbf); flex:none; }
.family__ctas { display:flex; gap:1rem; margin-top:1.2rem; flex-wrap:wrap; }
.grain__heading { margin-top:2.6rem; }
.grain-row { list-style:none; display:grid; grid-template-columns:repeat(5,1fr); gap:1rem; margin-top:1rem; }
.grain-row li { background:rgba(255,255,255,0.12); border-radius:12px; padding:1rem 0.9rem; font-size:0.82rem; }
.grain-row__letter { display:block; font-size:1.3rem; font-weight:700; margin-bottom:0.3rem; }
.grain-row strong { display:block; margin-bottom:0.3rem; }

/* ---- footer ---- */
.footer { background:var(--black); color:var(--white); text-align:center; padding:4rem 1.4rem; }
.footer__logo { height:70px; margin-bottom:1rem; }
.footer__signature { font-weight:700; font-size:1.2rem; }
.footer__heritage { opacity:0.75; margin-top:0.4rem; }
.footer__links { display:flex; gap:1.6rem; justify-content:center; margin:1.6rem 0; flex-wrap:wrap; }
.footer__links a { color:var(--white); opacity:0.8; text-decoration:none; font-size:0.88rem; }
.footer__links a:hover { opacity:1; }
/* News lives here rather than in the top bar — give it a pill so it still
   reads as a destination, not just another footer link */
.footer__news {
  border:1.5px solid rgba(255,255,255,0.35); border-radius:999px;
  padding:0.4rem 1.1rem; font-weight:700; opacity:1 !important;
  transition:background 0.2s ease, border-color 0.2s ease;
}
.footer__news:hover { background:var(--blue); border-color:var(--blue); }
.footer__constellation-label { font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--blue); margin-top:2rem; }
.footer__links--constellation { margin-top:0.8rem; }
.footer__base { opacity:0.5; font-size:0.8rem; }

/* ================= radar-glow hover (round elements) ================= */
.radar { position:relative; cursor:pointer; transition:transform 0.3s ease; }
.radar:hover, .radar:focus-visible { transform:scale(1.02); }
.radar::before, .radar::after {
  content:''; position:absolute; inset:0; border-radius:50%;
  border:2px solid var(--blue); opacity:0; pointer-events:none;
}
.radar:hover::before, .radar:focus-visible::before { animation:radar-ping 1.6s ease-out infinite; }
.radar:hover::after, .radar:focus-visible::after { animation:radar-ping 1.6s ease-out 0.8s infinite; }
@keyframes radar-ping {
  0%   { transform:scale(1); opacity:0.65; }
  100% { transform:scale(1.55); opacity:0; }
}

/* ================= orbit: nodes revolving around a hub ================= */
.orbit {
  position:relative; width:100%; max-width:80rem; margin:0 auto;
  height:clamp(26rem,58vh,38rem); outline:none;
  --orbit-rx:440; --orbit-ry:138; --orbit-min-scale:0.38;
  touch-action:pan-y;
  /* A RING IS A ROOM, NOT A LADDER.
     orbit.js gives every card a z-index from how near the front of the turn
     it is — 0 at the back, 100 at the front — because that is how a ring in
     perspective has to be stacked. Those numbers were being read against the
     whole page, where the top bar sits at 80. So on the arcade band the front
     three game cards climbed straight over the bar: drawn across it, and
     swallowing the clicks on How, What, the logo and Trust, which is worse
     than the look of it.
     `isolation` makes the ring its own room. Inside it the depth numbers mean
     exactly what they meant before; outside it the whole ring is one layer,
     and no card can ever reach the bar again. */
  isolation:isolate;
}
.orbit__hub {
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  text-align:center; z-index:50; pointer-events:none; width:min(30rem,60vw);
}
.orbit__hub .slide__title { margin:0; }
.orbit__hint { font-size:0.78rem; opacity:0.7; margin-top:0.5rem; letter-spacing:0.04em; }
.orbit__ring { list-style:none; position:absolute; inset:0; }
.orbit__node {
  position:absolute; top:50%; left:50%; width:8rem;
  transition:opacity 0.2s linear; will-change:transform;
}
.orbit__node a, .orbit__node > .orbit__disc {
  display:flex; flex-direction:column; align-items:center; gap:0.7rem;
  text-decoration:none; color:inherit;
}
.orbit__node a { cursor:pointer; }
/* the round face — dark core with a lit rim, so it reads as a glowing
   orb against the blue slide instead of washing out into it */
.orbit__disc {
  width:8rem; height:8rem; border-radius:50%; position:relative;
  display:grid; place-items:center; flex:none;
  background:radial-gradient(circle at 36% 30%, #14385f 0%, #0a2036 52%, #050d16 100%);
  box-shadow:0 20px 44px -10px rgba(0,0,0,0.6), 0 0 0 2px rgba(255,255,255,0.9) inset,
             0 0 26px 2px rgba(255,255,255,0.28);
  transition:box-shadow 0.3s ease;
}
.orbit__disc-in {
  width:50%; height:50%; border-radius:50%;
  border:2px solid rgba(255,255,255,0.9);
  box-shadow:0 0 18px 2px rgba(0,120,255,0.9) inset, 0 0 12px rgba(255,255,255,0.5);
}
/* per-node accent variation, still blue/white/black only */
.orbit__disc--1 .orbit__disc-in { box-shadow:0 0 18px 3px rgba(0,120,255,1) inset, 0 0 14px rgba(255,255,255,0.6); }
.orbit__disc--2 { background:radial-gradient(circle at 62% 32%, #16406c 0%, #0a2036 54%, #050d16 100%); }
.orbit__disc--3 { background:radial-gradient(circle at 32% 64%, #0f2f52 0%, #081a2c 54%, #040a12 100%); }
.orbit__disc--4 { background:radial-gradient(circle at 50% 26%, #1a4778 0%, #0a2036 52%, #050d16 100%); }
.orbit__disc--5 { background:radial-gradient(circle at 40% 60%, #0d2a49 0%, #071626 54%, #040a12 100%); }
.orbit__disc--6 { background:radial-gradient(circle at 64% 58%, #16406c 0%, #0a2036 52%, #050d16 100%); }
.orbit__disc--7 { background:radial-gradient(circle at 28% 42%, #12365c 0%, #081a2c 54%, #040a12 100%); }
.orbit__label {
  font-weight:700; font-size:1.05rem; text-align:center; line-height:1.25;
  text-shadow:0 2px 10px rgba(0,0,0,0.45);
}
.orbit__label em { display:block; font-style:normal; font-weight:400; font-size:0.76rem; opacity:0.85; margin-top:0.25rem; }
/* front-most node gets the radar ping + full-strength face */
.orbit__node.is-front .orbit__disc { box-shadow:0 26px 60px -14px rgba(0,0,0,0.6), 0 0 0 3px rgba(255,255,255,0.7) inset, 0 0 34px 4px rgba(255,255,255,0.35); }
.orbit__node.is-front .orbit__disc::before,
.orbit__node.is-front .orbit__disc::after {
  content:''; position:absolute; inset:0; border-radius:50%;
  border:2px solid rgba(255,255,255,0.9); opacity:0; pointer-events:none;
  animation:radar-ping 2s ease-out infinite;
}
.orbit__node.is-front .orbit__disc::after { animation-delay:1s; }
.orbit__nav {
  position:absolute; top:50%; transform:translateY(-50%); z-index:60;
  width:3rem; height:3rem; border-radius:50%; border:0; cursor:pointer;
  background:var(--white); color:var(--blue); font-size:1.6rem; line-height:1;
  box-shadow:0 10px 26px rgba(0,0,0,0.3); transition:transform 0.2s ease;
}
.orbit__nav:hover { transform:translateY(-50%) scale(1.1); }
.orbit__nav--prev { left:0; }
.orbit__nav--next { right:0; }

/* ---- sovereign slide: map hub + 4 dimensions in orbit ---- */
.sov__head { text-align:center; max-width:44rem; margin:0 auto 0.6rem; }
.sov__head .lede { margin-left:auto; margin-right:auto; }
/* card rings need more room than disc rings — the cards are ~4x the area, so
   the ellipse widens and deepens and the back of the ring stays legible */
/* A card is ~240px tall, so the ring's vertical radius plus half a card must
   stay inside the container — otherwise the front card is clipped by the
   slide's overflow:hidden. Hence the flatter ellipse and the taller box. */
.orbit--sov { --orbit-rx:420; --orbit-ry:104; --orbit-min-scale:0.52; height:clamp(31rem,60vh,38rem); }
.orbit--how { --orbit-rx:470; --orbit-ry:118; --orbit-min-scale:0.44; height:clamp(33rem,66vh,42rem); }
.orbit--sov .orbit__hub, .orbit--how .orbit__hub { z-index:1; }  /* cards pass in front */
.orbit--sov .orbit__node--card { width:clamp(11rem,16vw,13.5rem); }
.orbit--sov .orbit__node { width:9rem; }
.orbit--sov .orbit__disc { width:6.2rem; height:6.2rem; }
/* only the front node shows its description, so labels never stack up */
.orbit--sov .orbit__label em { display:none; }
.orbit--sov .orbit__node.is-front .orbit__label em { display:block; }
.slide#sovereign .compact-quote { margin-top:1.6rem; }
.orbit__hub--map { width:min(46rem,86vw); }
.sarawak { width:100%; height:auto; display:block; overflow:visible; }
.sw-traces path {
  stroke-dasharray:26 200; stroke-dashoffset:0;
  animation:sw-flow 5.5s linear infinite;
}
.sw-traces path:nth-child(2) { animation-duration:6.8s; animation-delay:-1.2s; }
.sw-traces path:nth-child(3) { animation-duration:4.9s; animation-delay:-2.4s; }
.sw-traces path:nth-child(4) { animation-duration:7.6s; animation-delay:-0.6s; }
.sw-traces path:nth-child(5) { animation-duration:6.1s; animation-delay:-3.1s; }
@keyframes sw-flow { to { stroke-dashoffset:-1130; } }
.sw-pad { animation:sw-pad 2.6s ease-in-out infinite; animation-delay:var(--d,0s); transform-box:fill-box; transform-origin:center; }
@keyframes sw-pad {
  0%,100% { opacity:0.45; transform:scale(1); }
  50%     { opacity:1;    transform:scale(1.5); }
}
.sw-core { animation:sw-core 3.4s ease-in-out infinite; transform-box:fill-box; transform-origin:center; }
@keyframes sw-core { 0%,100% { opacity:0.9; } 50% { opacity:1; } }
.sw-label { fill:var(--white); font:700 13px/1 'Inter',sans-serif; letter-spacing:0.16em; opacity:0.9; }
.sw-place { fill:var(--blue); font:700 22px/1 'Inter',sans-serif; letter-spacing:0.3em; opacity:0.55; }
.compact-quote--dark { background:rgba(0,120,255,0.10); border-left-color:var(--blue); color:var(--white); }
.compact-quote--dark h4 { color:var(--blue); }
@media (prefers-reduced-motion:reduce) {
  .sw-traces path, .sw-pad, .sw-core { animation:none; }
  .orbit__node.is-front .orbit__disc::before, .orbit__node.is-front .orbit__disc::after { animation:none; }
}

/* ================= pillar detail pages (M3 subpages) ================= */
.pbar {
  position:fixed; top:0; left:0; right:0; z-index:80; display:flex; align-items:center;
  justify-content:space-between; padding:1rem clamp(1.2rem,4vw,2.6rem);
  background:color-mix(in srgb, var(--white) 88%, transparent); backdrop-filter:blur(10px);
}
.pbar__back { display:flex; align-items:center; gap:0.4rem; color:var(--black); text-decoration:none; font-weight:700; font-size:0.9rem; flex:1; }
/* the chapter's own NEU name, centred in the bar */
.pbar__name {
  flex:0 0 auto; font-weight:700; font-size:1.15rem; letter-spacing:0.02em;
  color:var(--blue); text-align:center;
}
/* The right-hand end of the bar: the mark, then NEU's face.
   The logo used to carry `flex:1` and do the pushing itself. It cannot now
   that it has company — the group does the pushing and the logo is just a
   logo, or NEU would be shoved off the edge on a narrow window. */
.pbar__right { flex:1; display:flex; align-items:center; justify-content:flex-end; gap:0.75rem; }
.pbar__logo { height:40px; flex:0 0 auto; object-fit:contain; object-position:right; }
/* Same face, same size, same idea as the front page's — this is the one way
   to search or ask on every page, so it looks the same on every page. */
.pbar__neu {
  flex:0 0 auto; width:2.6rem; height:2.6rem; border-radius:50%;
  background:var(--blue-pale); border:0; cursor:pointer; padding:0;
  display:grid; place-items:center; overflow:visible;
  transition:background 0.2s ease, transform 0.2s ease;
}
.pbar__neu img { width:2.3rem; height:2.3rem; object-fit:contain; object-position:top center; border-radius:50%; display:block; }
.pbar__neu:hover { background:var(--blue); transform:scale(1.06); }
/* the one-off wave on a first visit, the same as the front page's */
.pbar__neu.is-new { animation:navneu-hi 2.4s ease 1.2s 2; }
@media (prefers-reduced-motion: reduce) { .pbar__neu.is-new { animation:none; } }

/* -- footage pillars: reuse the cinematic frame-scrub three-beat scene -- */
.pintro { min-height:100vh; display:flex; flex-direction:column; justify-content:center; padding:7rem clamp(1.4rem,6vw,5rem) 3rem; background:var(--black); color:var(--white); }
.pintro--compact { min-height:auto; padding:6.4rem clamp(1.4rem,6vw,5rem) 1.6rem; }
.pintro__role { color:var(--blue); font-weight:700; letter-spacing:0.1em; text-transform:uppercase; font-size:0.82rem; margin-bottom:0.6rem; }
.pintro h1 { font-size:clamp(2.2rem,5vw,3.6rem); margin-bottom:1rem; }
.pintro__meta { display:flex; gap:2.4rem; flex-wrap:wrap; margin-top:2rem; font-size:0.88rem; opacity:0.8; max-width:44rem; }
.pintro__meta strong { display:block; color:var(--blue); font-size:0.75rem; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:0.3rem; opacity:1; }

.scene { position:relative; background:var(--black); color:var(--white); }
.scene__stage { position:relative; height:100vh; overflow:hidden; }
.seq-canvas { position:absolute; inset:0; width:100%; height:100%; display:block; }
.seq-loading { position:absolute; inset:0; display:grid; place-items:center; background:var(--black); z-index:3; transition:opacity 0.4s ease; }
.seq-loading.done { opacity:0; pointer-events:none; }
.seq-loading__label { font-size:0.8rem; letter-spacing:0.2em; color:var(--white); opacity:0.55; }
.scene__copy { position:absolute; left:0; right:0; bottom:0; z-index:4; padding:2rem clamp(1.4rem,6vw,4rem) 2.6rem; background:linear-gradient(transparent, rgba(10,2,3,0.72)); min-height:44vh; }
.scene__domain { font-weight:700; font-size:clamp(1.5rem,3.6vw,2.4rem); letter-spacing:-0.01em; margin-bottom:0.5rem; }
.scene__beats { position:relative; min-height:5.2rem; }
.scene__beat { position:absolute; inset:0 auto auto 0; max-width:40rem; font-size:clamp(0.92rem,1.9vw,1.06rem); opacity:0; }
.phandoff { background:var(--black); color:var(--white); text-align:center; padding:clamp(5.5rem,18vh,10rem) 1.4rem; }
.phandoff span { display:inline-block; max-width:34rem; font-size:clamp(1.15rem,2.6vw,1.6rem); line-height:1.45; font-weight:700; opacity:0.92; }

/* -- non-footage pillars: scroll-pinned text reveal -- */
.preveal { min-height:100vh; display:flex; flex-direction:column; justify-content:center; padding:clamp(7rem,16vh,10rem) clamp(1.4rem,6vw,6rem) clamp(4rem,10vh,6rem); }
.preveal__role { color:var(--blue); font-weight:700; letter-spacing:0.14em; text-transform:uppercase; font-size:0.82rem; margin-bottom:1.1rem; }
.preveal h1 { font-size:clamp(2.4rem,5.4vw,4rem); line-height:1.1; letter-spacing:-0.025em; margin-bottom:1.6rem; max-width:16ch; }
.preveal__body { font-size:clamp(1.05rem,1.7vw,1.3rem); line-height:1.7; max-width:38rem; opacity:0.78; margin-bottom:clamp(2.8rem,6vw,4.4rem); }
.preveal__points { list-style:none; display:grid; gap:1.6rem; max-width:40rem; }
.preveal__points li { border-left:3px solid var(--blue); padding:0.2rem 0 0.2rem 1.4rem; opacity:0; transform:translateY(16px); font-size:1rem; line-height:1.65; }
.preveal__meta { display:flex; gap:3rem; flex-wrap:wrap; margin-top:clamp(2.8rem,6vw,4rem); font-size:0.85rem; opacity:0.75; }
.preveal__meta strong { display:block; color:var(--blue); font-size:0.72rem; letter-spacing:0.08em; text-transform:uppercase; margin-bottom:0.3rem; opacity:1; }

/* ---- pillar content blocks (brochure-depth sections) ----
   Spacing scale follows the notebooklm.google rhythm: tall section padding,
   a short measure for reading, and one generous gap between the words and
   whatever the section is showing. White space is the layout, not a leftover. */
:root {
  --blk-y:clamp(4.6rem,13vh,8.5rem);      /* section top/bottom breathing room */
  --blk-x:clamp(1.4rem,6vw,4rem);
  --blk-gap:clamp(1.5rem,3vw,2.3rem);     /* between cards in a grid */
  --blk-rest:clamp(2.6rem,5.5vw,4.2rem);  /* between the lead and the content */
}
.blk { padding:var(--blk-y) var(--blk-x); background:var(--white); color:var(--black); }
.blk--dark { background:var(--black); color:var(--white); }
.blk--cta { background:var(--blue); color:var(--white); }
.blk__wrap { max-width:66rem; margin:0 auto; }
.blk__h { font-size:clamp(1.8rem,3.9vw,3rem); line-height:1.14; letter-spacing:-0.02em; margin-bottom:1.4rem; max-width:26ch; }
.blk__h em { font-style:normal; color:var(--blue); }
.blk--dark .blk__h em, .blk--cta .blk__h em { color:#8ec5ff; }
.blk__lead { font-size:clamp(1.02rem,1.7vw,1.2rem); line-height:1.72; opacity:0.72; max-width:38rem; margin-bottom:var(--blk-rest); }
.blk--dark .blk__lead, .blk--cta .blk__lead { opacity:0.8; }
.blk .kicker { margin-bottom:1.1rem; }
.blk--dark .kicker, .blk--cta .kicker { color:#8ec5ff; }
/* a heading with no lead under it still needs the same rest before content */
.blk__h + .probs, .blk__h + .steps, .blk__h + .uses, .blk__h + .caps, .blk__h + .statrow { margin-top:var(--blk-rest); }

/* a statement is a pause — centre it and let the margins do the talking */
.blk--statement { padding-top:calc(var(--blk-y) * 1.15); padding-bottom:calc(var(--blk-y) * 1.15); }
.blk--statement .blk__wrap { max-width:46rem; text-align:center; }
.blk--statement .blk__h { max-width:none; margin-left:auto; margin-right:auto; }
.blk--statement .blk__lead { max-width:36rem; margin-left:auto; margin-right:auto; margin-bottom:0; }

/* stat row — 3D / 0 / Real-Time */
.statrow { list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:var(--blk-gap); margin-top:var(--blk-rest); }
.statrow li { border:1px solid rgba(255,255,255,0.16); border-radius:22px; padding:2.4rem 1.4rem; text-align:center; background:rgba(255,255,255,0.04); }
.statrow__big { display:block; font-size:clamp(2.1rem,4.4vw,3.1rem); font-weight:700; color:#8ec5ff; line-height:1; letter-spacing:-0.02em; }
.statrow__label { display:block; margin-top:0.9rem; font-size:0.88rem; line-height:1.55; opacity:0.75; }

/* numbered problems */
.probs { list-style:none; display:grid; gap:var(--blk-gap); }
.prob { display:flex; gap:1.5rem; align-items:flex-start; border:1px solid color-mix(in srgb, var(--black) 10%, var(--white)); border-radius:22px; padding:1.9rem 2.1rem; background:var(--white); box-shadow:0 16px 34px -28px rgba(10,2,3,0.3); }
.prob__n { font-weight:700; font-size:1.1rem; color:var(--blue); flex:none; min-width:2rem; }
.prob h3 { font-size:1.06rem; margin-bottom:0.5rem; }
.prob p { font-size:0.94rem; line-height:1.68; opacity:0.7; }

/* capability pair */
.caps { display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr)); gap:var(--blk-gap); }
.cap { border:1px solid rgba(255,255,255,0.16); border-radius:22px; padding:2rem 1.9rem; background:rgba(255,255,255,0.04); }
.cap h3 { color:#8ec5ff; font-size:1.06rem; margin-bottom:0.6rem; }
.cap p { font-size:0.92rem; line-height:1.68; opacity:0.75; }
.caps__pill { display:inline-block; margin-top:var(--blk-rest); border:1px solid rgba(255,255,255,0.3); border-radius:999px; padding:0.6rem 1.4rem; font-weight:700; font-size:0.9rem; }

/* process steps */
.steps { list-style:none; display:grid; gap:1.1rem; counter-reset:s; }
.step { display:flex; gap:1.4rem; align-items:flex-start; border-left:3px solid var(--blue); border-radius:0 20px 20px 0; padding:1.6rem 1.9rem; background:var(--blue-pale); }
.step__n { font-weight:700; color:var(--white); background:var(--blue); width:1.9rem; height:1.9rem; border-radius:50%; display:grid; place-items:center; flex:none; font-size:0.9rem; }
.step h3 { font-size:1.02rem; margin-bottom:0.4rem; }
.step p { font-size:0.92rem; line-height:1.68; opacity:0.72; }

/* use-case grid */
.uses { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:var(--blk-gap); }
.use { border:1px solid color-mix(in srgb, var(--black) 10%, var(--white)); border-radius:24px; padding:2rem 1.9rem; background:var(--white); box-shadow:0 18px 38px -30px rgba(10,2,3,0.32); transition:transform 0.25s ease, box-shadow 0.25s ease; }
.use:hover { transform:translateY(-4px); box-shadow:0 24px 44px -26px rgba(10,2,3,0.34); }
.use h3 { color:var(--blue); font-size:1.08rem; margin-bottom:0.6rem; }
.use p { font-size:0.94rem; line-height:1.68; opacity:0.72; }

/* ---- hover glow on every box, site-wide ----
   A travelling neon ring (masked conic gradient) plus an ambient bloom —
   the same treatment the NEUStory cards use, applied consistently. */
.prob, .cap, .use, .step, .statrow li, .pillar-card, .news-card, .teamrow {
  position:relative;
  transition:transform 0.25s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}
.prob::after, .cap::after, .use::after, .step::after, .statrow li::after,
.pillar-card::after, .news-card::after {
  content:''; position:absolute; inset:0; border-radius:inherit;
  padding:2px; box-sizing:border-box; pointer-events:none; z-index:2;
  background:conic-gradient(from var(--laser-angle, 0deg),
    var(--blue), color-mix(in srgb, var(--blue) 55%, var(--white)) 25%,
    var(--white) 50%, color-mix(in srgb, var(--blue) 55%, var(--white)) 75%,
    var(--blue) 100%);
  filter:drop-shadow(0 0 5px var(--blue)) drop-shadow(0 0 10px color-mix(in srgb, var(--blue) 70%, transparent));
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity 0.3s ease;
}
.prob:hover::after, .cap:hover::after, .use:hover::after, .step:hover::after,
.statrow li:hover::after, .pillar-card:hover::after, .news-card:hover::after {
  opacity:1; animation:laser-spin 3s linear infinite;
}
.prob:hover, .cap:hover, .use:hover, .step:hover, .statrow li:hover,
.pillar-card:hover, .news-card:hover {
  transform:translateY(-3px);
  box-shadow:0 0 30px 5px color-mix(in srgb, var(--blue) 42%, transparent);
}
@media (prefers-reduced-motion:reduce) {
  .prob:hover::after, .cap:hover::after, .use:hover::after, .step:hover::after,
  .statrow li:hover::after, .pillar-card:hover::after, .news-card:hover::after { animation:none; }
}

/* ---- NEUStory news & gallery page ---- */
.newshead { padding-top:calc(4.5rem + var(--blk-y)); }  /* clears the fixed .pbar */
.newshead .blk__lead { margin-bottom:0; }
.newsgal { padding-top:clamp(3rem,7vh,4.5rem); }
.galfilter { display:flex; gap:0.7rem; flex-wrap:wrap; margin-bottom:var(--blk-rest); }
.galfilter a {
  display:inline-flex; align-items:center; gap:0.45rem; text-decoration:none;
  border:1.5px solid color-mix(in srgb, var(--black) 14%, var(--white));
  border-radius:999px; padding:0.45rem 1rem; font-size:0.86rem; font-weight:700;
  color:var(--black); transition:all 0.2s ease;
}
.galfilter a span { font-size:0.72rem; opacity:0.55; }
.galfilter a:hover { border-color:var(--blue); color:var(--blue); }
.galfilter a.is-on { background:var(--blue); border-color:var(--blue); color:var(--white); }
.galfilter a.is-on span { opacity:0.8; }

.gal { list-style:none; display:grid; grid-template-columns:repeat(auto-fill,minmax(290px,1fr)); gap:var(--blk-gap); }
.galcard {
  position:relative; border:1px solid color-mix(in srgb, var(--black) 9%, var(--white));
  border-radius:20px; overflow:hidden; background:var(--white);
  box-shadow:0 20px 40px -28px rgba(10,2,3,0.3);
  transition:transform 0.25s ease, box-shadow 0.3s ease, border-color 0.3s ease;
  display:flex; flex-direction:column;
}
.galcard::after {
  content:''; position:absolute; inset:0; border-radius:inherit; padding:3px;
  box-sizing:border-box; pointer-events:none; z-index:2;
  background:conic-gradient(from var(--laser-angle, 0deg),
    var(--blue), color-mix(in srgb, var(--blue) 55%, var(--white)) 25%,
    var(--white) 50%, color-mix(in srgb, var(--blue) 55%, var(--white)) 75%, var(--blue) 100%);
  filter:drop-shadow(0 0 5px var(--blue)) drop-shadow(0 0 10px color-mix(in srgb, var(--blue) 70%, transparent));
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity 0.3s ease;
}
.galcard:hover::after { opacity:1; animation:laser-spin 3s linear infinite; }
.galcard:hover { transform:translateY(-5px); box-shadow:0 0 32px 6px color-mix(in srgb, var(--blue) 42%, transparent); }
.galcard__media { aspect-ratio:4/3; overflow:hidden; background:var(--blue-pale); }
.galcard__media img { width:100%; height:100%; object-fit:cover; object-position:50% 20%; display:block; transition:transform 0.5s ease; }
.galcard:hover .galcard__media img { transform:scale(1.05); }
.galcard__media--none { display:grid; place-items:center; background:linear-gradient(140deg,#0a2036,#14385f); }
.galcard__media--none span { color:var(--white); font-weight:700; font-size:1.5rem; letter-spacing:0.1em; opacity:0.6; }
.galcard__body { padding:1.7rem 1.8rem 1.9rem; display:flex; flex-direction:column; gap:0.55rem; }
.galcard__meta { font-size:0.76rem; opacity:0.7; }
.galcard__pillar { color:var(--blue); font-weight:700; }
.galcard h3 { font-size:1.06rem; line-height:1.32; }
.galcard__text { font-size:0.92rem; line-height:1.68; opacity:0.72; }
/* "Read more" on a Story Card. A <details>, so it needs no script — the arrow
   the browser draws by itself is removed and the summary is dressed as the
   link it reads as. */
.galmore { margin-top:0.35rem; }
.galmore > summary {
  list-style:none; cursor:pointer; display:inline-block;
  font-size:0.84rem; font-weight:700; color:var(--blue);
  padding:0.1rem 0; border-bottom:1.5px solid transparent;
}
.galmore > summary::-webkit-details-marker { display:none; }
.galmore > summary:hover { border-bottom-color:var(--blue); }
.galmore > summary:focus-visible { outline:2px solid var(--blue); outline-offset:2px; }
.galmore__off { display:none; }
.galmore[open] .galmore__on { display:none; }
.galmore[open] .galmore__off { display:inline; }
/* the rest of the words read on from the paragraph above, not as a new block */
.galmore__rest { margin-top:0.35rem; }
/* OPEN, THE LINK MOVES TO THE BOTTOM. A <summary> is always the first child —
   it has to be, for the disclosure to mean anything — so while open it sat
   between the first thirty words and the rest of the sentence, cutting the
   paragraph in half with the words "Read less". Flex order puts it after the
   text it reveals, where it belongs, without touching the markup. */
.galmore[open] { display:flex; flex-direction:column; }
.galmore[open] > summary { order:2; margin-top:0.35rem; }
.galcard__partner { font-size:0.82rem; opacity:0.62; margin-top:0.2rem; }

/* ---- orbit cards (sections 3 and 4) ----
   Dr. KC asked for rounded squares instead of circles, each carrying a
   picture, a short title and a short explanation — so a staff member can
   pitch the card itself rather than having to know what a circle stands for. */
.orbit__node--card { width:clamp(11rem,17vw,14.5rem); }
.ocard {
  display:block; border-radius:22px; overflow:hidden; position:relative;
  background:var(--white); color:var(--black); text-align:left;
  box-shadow:0 22px 46px -26px rgba(10,2,3,0.5);
  transition:box-shadow 0.3s ease;
}
.ocard__media { display:block; aspect-ratio:4/3; overflow:hidden; background:var(--blue-pale); }
.ocard__media img { width:100%; height:100%; object-fit:cover; display:block; }
.ocard__body { display:block; padding:0.85rem 0.95rem 1rem; }
.ocard__body strong { display:block; font-size:0.98rem; letter-spacing:-0.01em; margin-bottom:0.2rem; }
.ocard__body em { display:block; font-style:normal; font-size:0.78rem; line-height:1.45; opacity:0.68; }

/* the pulse that says "this one can be pressed" — front card only, so the
   ring doesn't turn into a light show */
.ocard--pulse::after {
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  box-shadow:0 0 0 0 color-mix(in srgb, var(--white) 85%, transparent);
  opacity:0;
}
.orbit__node.is-front .ocard--pulse::after { opacity:1; animation:ocard-pulse 2.4s ease-out infinite; }
@keyframes ocard-pulse {
  0%   { box-shadow:0 0 0 0 color-mix(in srgb, var(--white) 80%, transparent); }
  70%  { box-shadow:0 0 0 14px color-mix(in srgb, var(--white) 0%, transparent); }
  100% { box-shadow:0 0 0 0 color-mix(in srgb, var(--white) 0%, transparent); }
}
.orbit__node.is-front .ocard { box-shadow:0 26px 60px -22px rgba(10,2,3,0.6); }
.orbit__node--card a { text-decoration:none; display:block; }
.orbit__node--card a:hover .ocard { box-shadow:0 0 34px 6px color-mix(in srgb, var(--white) 45%, transparent); }
@media (prefers-reduced-motion:reduce) { .ocard--pulse::after { animation:none !important; } }

/* a way out of the section for someone who wants the detail */
.sectmore {
  position:absolute; right:clamp(1.2rem,4vw,3rem); bottom:clamp(1.4rem,4vh,2.4rem); z-index:4;
  color:var(--white); text-decoration:none; font-weight:700; font-size:0.88rem;
  border:1px solid rgba(255,255,255,0.4); border-radius:999px; padding:0.55rem 1.2rem;
  transition:background 0.2s ease, border-color 0.2s ease;
}
.sectmore:hover { background:var(--blue); border-color:var(--blue); }

/* section 1's bottom-right button — the way out of the idle loop into proof */
.herowatch {
  position:absolute; right:clamp(1.2rem,4vw,3rem); bottom:clamp(1.6rem,5vh,3rem); z-index:3;
  display:flex; align-items:center; gap:0.9rem; text-decoration:none;
  padding:0.7rem 1.3rem 0.7rem 0.7rem; border-radius:999px;
  background:color-mix(in srgb, var(--white) 14%, transparent);
  border:1px solid rgba(255,255,255,0.4); color:var(--white);
  backdrop-filter:blur(10px);
  transition:background 0.25s ease, transform 0.25s ease, border-color 0.25s ease;
}
.herowatch:hover { background:var(--blue); border-color:var(--blue); transform:translateY(-3px); }
.herowatch__play {
  width:2.4rem; height:2.4rem; border-radius:50%; flex:none;
  background:var(--white); color:var(--blue); display:grid; place-items:center;
}
.herowatch__play svg { width:1.1rem; height:1.1rem; margin-left:2px; }
.herowatch__txt { display:flex; flex-direction:column; line-height:1.25; }
.herowatch__txt strong { font-size:0.92rem; }
.herowatch__txt em { font-style:normal; font-size:0.76rem; opacity:0.8; }

/* ---- the logo button + its credentials panel ----
   Dr. KC's idea: the round blue mark in the bar is a BUTTON. A slow pulse
   tells a first-time viewer that, without a label. Pressing it drops NEUON's
   certifications and awards, from anywhere on any page, at any point. */
.nav__brand--btn { border:none; background:none; padding:0; cursor:pointer; }
.nav__brand--btn.is-open .nav__radar i { animation:none; opacity:0; }

/* The panel is a CIRCLE that opens from behind the disc, exactly as Dr. KC
   drew it — capsule bar, blue disc riding on top of it, second round circle
   dropping out of it. The nav sits at z-index 80, so everything here stays
   below that and the disc keeps floating over the circle it opened. */
.certwrap { position:fixed; inset:0; z-index:70; }
.certwrap[hidden] { display:none; }
.certwrap__scrim {
  position:absolute; inset:0;
  background:color-mix(in srgb, var(--black) 55%, transparent);
  backdrop-filter:blur(6px);
  animation:cert-fade 0.3s ease;
}
@keyframes cert-fade { from { opacity:0; } }

/* radar waves rolling out of the disc — the button visibly "fires" */
.certwave {
  /* --cert-x / --cert-y are measured from the disc itself by certpanel.js,
     because the pill is not symmetrical and the disc is off viewport-centre */
  position:absolute; left:var(--cert-x,50%); top:var(--cert-y,3.5rem);
  transform:translate(-50%,-50%); pointer-events:none;
}
.certwave i {
  position:absolute; left:50%; top:50%; width:5.4rem; height:5.4rem; margin:-2.7rem 0 0 -2.7rem;
  border:2px solid var(--white); border-radius:50%; opacity:0;
  animation:cert-ping 2.4s ease-out infinite;
}
.certwave i:nth-child(2) { animation-delay:0.8s; }
.certwave i:nth-child(3) { animation-delay:1.6s; }
@keyframes cert-ping {
  0%   { transform:scale(0.7); opacity:0.75; }
  100% { transform:scale(5.5); opacity:0; }
}

/* The circle is centred ON the blue disc and grows out from behind it, so its
   top half disappears up behind the bar and only the bottom half is ever
   seen — the dome in Dr. KC's slide. All content therefore lives in the
   lower half. */
.certdisc {
  position:absolute; left:var(--cert-x,50%); top:var(--cert-y,3.5rem);
  transform:translate(-50%,-50%);
  width:min(58rem,96vw); aspect-ratio:1; border-radius:50%;
  background:var(--white); color:var(--black);
  box-shadow:0 40px 90px -20px rgba(10,2,3,0.45);
  transform-origin:center center;
  animation:cert-open 0.45s cubic-bezier(0.2,0.9,0.3,1.3);
}
@keyframes cert-open { from { opacity:0; transform:translate(-50%,-50%) scale(0.12); } }
@media (prefers-reduced-motion:reduce) {
  .certdisc, .certwrap__scrim { animation:none; }
  .certwave i { animation:none; opacity:0; }
}

/* Everything sits in the lower half — the part of the circle you can see.
   The kicker clears the blue disc, then the logos spin below it. */
/* starts below the blue disc — the disc hangs into the top of the circle, so
   anything above this line would be covered by it */
/* The dome is a full circle centred on the blue disc, so only its lower half
   is ever on screen: from 50% to 100% of its height. The middle of THAT is
   75%, which is where the ring belongs now the graph ball has gone. */
.certdisc__in {
  /* 72.5%, not 75%: the ring's depth scaling makes the front logo the tallest
     and it hangs lower than the ones behind, so the visual centre sits a little
     below the container's. Measured at 25px on a 928px dome. */
  position:absolute; top:72.5%; left:50%; transform:translate(-50%,-50%);
  width:86%; display:flex; flex-direction:column; align-items:center;
  gap:0.3rem; text-align:center;
}
.certdisc__kicker { font-weight:700; letter-spacing:0.13em; text-transform:uppercase; font-size:0.68rem; color:var(--blue); }

/* the credentials ring — same engine as sections 3 and 4, sized to sit
   inside the visible half of the dome */
.orbit--cert {
  --orbit-rx:150; --orbit-ry:30; --orbit-min-scale:0.55;
  width:100%; height:6.4rem; margin:0.1rem 0 0;
}
.orbit--cert .orbit__node { width:6.6rem; }
.orbit--cert .orbit__node a, .orbit--cert .clogo-wrap {
  display:flex; flex-direction:column; align-items:center; gap:0.4rem;
  text-decoration:none; color:var(--black);
}
.clogo {
  display:grid; place-items:center; width:6.4rem; height:4rem;
  border-radius:14px; background:var(--white);
  transition:transform 0.25s ease, box-shadow 0.25s ease;
}
.clogo img { max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; display:block; }
.orbit--cert .orbit__node a:hover .clogo {
  transform:translateY(-3px);
  box-shadow:0 10px 26px -10px color-mix(in srgb, var(--blue) 70%, transparent);
}
.clogo__name {
  font-size:0.66rem; font-weight:700; line-height:1.25; opacity:0;
  transition:opacity 0.25s ease; max-width:7.2rem;
}
.clogo__name em { display:block; font-style:normal; font-weight:400; font-size:0.6rem; opacity:0.65; margin-top:0.1rem; }
/* only the card at the front is legible enough to bother labelling */
.orbit--cert .orbit__node.is-front .clogo__name { opacity:1; }

/* a plain round play button — the wording lives on the arc instead */
.certdisc__more {
  display:grid; place-items:center; width:2.6rem; height:2.6rem; border-radius:50%;
  margin:0.4rem auto 0; border:2px solid var(--blue); color:var(--blue);
  text-decoration:none; transition:background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.certdisc__more svg { width:1.05rem; height:1.05rem; margin-left:2px; }
.certdisc__more:hover { background:var(--blue); color:var(--white); transform:scale(1.1); }

/* close sits just inside the dome's lower-right curve */
.certdisc__close {
  position:absolute; top:66%; right:13%; width:2rem; height:2rem;
  border:none; border-radius:50%; cursor:pointer; font-size:0.9rem;
  background:color-mix(in srgb, var(--black) 7%, var(--white)); color:var(--black);
}
.certdisc__close:hover { background:var(--blue); color:var(--white); }

/* the disc keeps pulsing while open so it still reads as the live control */
.nav__brand--btn.is-open { z-index:2; }

@media (max-width:640px) {
  .certdisc { width:150vw; }        /* wider than the screen — dome fills it */
  .certdisc__in { width:74%; gap:0.3rem; }
  .orbit--cert { --orbit-rx:112; --orbit-ry:26; height:9rem; }
  .orbit--cert .orbit__node { width:5.6rem; }
  .clogo { width:5rem; height:3.1rem; }
  .clogo__name { font-size:0.6rem; max-width:5.6rem; }
  .certdisc__close { right:26%; top:62%; }
}

/* ================= NEUON pack opening =================
   The mobile-game moment. Kept off the pitch path — it is the last thing
   before the footer, so a presenter never has to walk through it, but a
   visitor left alone with the screen has something to play with. */
/* THE DISC HANGS BELOW THE BAR.
   Every `.slide` measures its top clearance from var(--nav-disc) for that
   reason. The starter pack and the arcade are not slides, so they never got
   it, and the blue NEUON disc came to rest on top of "Young Innovator Starter
   Pack." — and on the arcade's heading under it. */
.pack {
  padding:calc(var(--nav-disc) + 1.6rem) var(--blk-x) var(--blk-y);
  background:var(--black); color:var(--white); text-align:center;
}
.pack__wrap { max-width:66rem; margin:0 auto; }
.pack .kicker { color:#8ec5ff; margin-bottom:1rem; }
.pack__h { font-size:clamp(1.7rem,3.6vw,2.7rem); letter-spacing:-0.02em; margin-bottom:0.9rem; }
.pack__h em { font-style:normal; color:#8ec5ff; }
.pack__lead { opacity:0.72; font-size:clamp(0.98rem,1.6vw,1.14rem); margin-bottom:var(--blk-rest); }
.pack__stage { position:relative; display:grid; place-items:center; min-height:26rem; }

/* ---- twelve packs on the shared ring ----
   Eight left visible gaps and read as loose cards; sixteen closes the ring
   into a proper carousel. */
.orbit--pack {
  --orbit-rx:340; --orbit-ry:58; --orbit-min-scale:0.34;
  height:24rem; cursor:grab; perspective:900px;
}
.orbit--pack:active { cursor:grabbing; }
.orbit--pack.is-gone { opacity:0; pointer-events:none; transition:opacity 0.35s ease; }
.orbit__node--pack { width:clamp(6.4rem,10vw,8.2rem); cursor:pointer; }

.ppack { display:block; transform-style:preserve-3d; }

/* ---- a pack that has been picked ----
   It comes to the centre, lifts, pulses, and can be turned by hand. The rest
   drop back and blur. The blur/opacity need !important because layout() in
   orbit.js writes those two properties inline on every frame. */
.orbit--pack.has-pick .orbit__node--pack:not(.is-picked) {
  filter:blur(3.5px) !important;
  opacity:0.32 !important;
}
.orbit__node--pack.is-picked { z-index:200 !important; }
.orbit__node--pack.is-picked .ppack {
  transform:scale(1.55) translateY(-10px) rotateY(var(--spin, 0deg));
  transition:transform 0.42s cubic-bezier(0.2,0.9,0.3,1.5);
  animation:pack-bounce 0.5s cubic-bezier(0.25,1.4,0.4,1);
}
/* while it is being turned by hand, follow the finger instead of easing */
.orbit__node--pack.is-picked .ppack.is-turning {
  transition:none; animation:none; cursor:grabbing;
}
@keyframes pack-bounce {
  0%   { transform:scale(1)    translateY(0)     rotateY(var(--spin, 0deg)); }
  55%  { transform:scale(1.68) translateY(-18px) rotateY(var(--spin, 0deg)); }
  100% { transform:scale(1.55) translateY(-10px) rotateY(var(--spin, 0deg)); }
}

/* the same radar language as the logo button, on the chosen pack */
.orbit__node--pack.is-picked .ppack__foil::after {
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  box-shadow:0 0 0 0 color-mix(in srgb, var(--blue) 85%, var(--white));
  animation:pack-pulse 1.9s ease-out infinite;
}
@keyframes pack-pulse {
  0%   { box-shadow:0 0 0 0 color-mix(in srgb, var(--blue) 80%, var(--white)); }
  70%  { box-shadow:0 0 0 18px color-mix(in srgb, var(--blue) 0%, transparent); }
  100% { box-shadow:0 0 0 0 color-mix(in srgb, var(--blue) 0%, transparent); }
}

/* the reverse face, so turning it by hand shows a real back */
/* the reverse face gets the same foil as the front, so turning a pack round
   never reveals a flat dead side */
.ppack__back {
  position:absolute; inset:0; border-radius:14px; overflow:hidden;
  backface-visibility:hidden; transform:rotateY(180deg);
  background:
    radial-gradient(120% 80% at 70% 12%, color-mix(in srgb, var(--white) 30%, transparent), transparent 55%),
    linear-gradient(205deg, #0a63d6 0%, var(--blue) 42%, #0b3f8f 100%);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,0.22);
  display:grid; place-items:center; padding:0.6rem;
}
.ppack__backword {
  color:var(--white); font-weight:700; text-align:center; line-height:1.35;
  font-size:clamp(0.58rem,1.05vw,0.74rem); letter-spacing:0.14em; text-transform:uppercase;
  opacity:0.92; text-shadow:0 2px 8px rgba(0,0,0,0.35);
}

.pack__pickhint { text-align:center; font-size:0.82rem; opacity:0.6; margin-top:0.4rem; letter-spacing:0.03em; }
.ppack__foil {
  backface-visibility:hidden;
  position:relative; width:100%; aspect-ratio:2/3; border-radius:14px; overflow:hidden;
  background:
    radial-gradient(120% 80% at 30% 12%, color-mix(in srgb, var(--white) 34%, transparent), transparent 55%),
    linear-gradient(155deg, #0a63d6 0%, var(--blue) 42%, #0b3f8f 100%);
  box-shadow:0 26px 54px -20px rgba(0,0,0,0.75), inset 0 0 0 1px rgba(255,255,255,0.22);
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:0.6rem;
}
.orbit__node--pack.is-picked .ppack__foil {
  box-shadow:0 32px 64px -18px rgba(0,0,0,0.8), 0 0 34px -4px color-mix(in srgb, var(--blue) 70%, transparent),
             inset 0 0 0 1px rgba(255,255,255,0.3);
}
.ppack__mark { width:48%; height:auto; opacity:0.95; }
.ppack__word { font-weight:700; letter-spacing:0.3em; font-size:0.6rem; opacity:0.7; padding-left:0.3em; }

.ppack__shine {
  position:absolute; inset:-40% -120%; pointer-events:none;
  background:linear-gradient(74deg, transparent 42%, rgba(255,255,255,0.45) 50%, transparent 58%);
  animation:pack-shine 4.5s ease-in-out infinite;
}
@keyframes pack-shine { 0%,100% { transform:translateX(-30%); } 50% { transform:translateX(30%); } }

/* ---- the glowing tear strip: the only way in ---- */
.ppack__tear {
  pointer-events:none;
  position:absolute; left:0; right:0; top:16%; height:1.7rem;
  display:grid; place-items:center; cursor:ew-resize; touch-action:none;
  opacity:0; transition:opacity 0.3s ease;
}
.orbit__node--pack.is-picked .ppack__tear { opacity:1; pointer-events:auto; }
.ppack__back .ppack__tear { top:16%; }
.ppack__glow {
  position:absolute; left:0; right:0; top:50%; height:3px; transform:translateY(-50%);
  border-radius:3px; overflow:hidden;
  background:color-mix(in srgb, var(--white) 45%, transparent);
  box-shadow:0 0 8px 1px color-mix(in srgb, var(--white) 60%, transparent),
             0 0 18px 3px color-mix(in srgb, var(--blue) 80%, var(--white));
  transform:translateY(-50%) scaleY(calc(1 + var(--pull, 0) * 2.6));
}
/* the highlight that runs along the line, showing which way to swipe */
.ppack__glow::after {
  content:''; position:absolute; inset:0;
  background:linear-gradient(90deg, transparent 0%, var(--white) 45%, var(--white) 55%, transparent 100%);
  background-size:55% 100%; background-repeat:no-repeat;
  animation:tear-sweep 2.1s linear infinite;
}
@keyframes tear-sweep {
  0%   { background-position:-60% 0; }
  100% { background-position:170% 0; }
}
.ppack__tear.is-pulling .ppack__glow::after { animation:none; }
@keyframes tear-nudge { 0%,100% { margin-left:-3px; } 50% { margin-left:3px; } }

.orbit__node--pack.is-torn .ppack__foil { animation:pack-rip 0.52s ease-in forwards; }
@keyframes pack-rip {
  0%   { transform:scale(1); opacity:1; }
  35%  { transform:scale(1.07) rotate(-1.5deg); }
  100% { transform:scale(0.66) translateY(34px); opacity:0; }
}

.pack__card { position:absolute; inset:0; display:grid; place-content:center; justify-items:center; gap:1.2rem; }
/* an explicit display beats the hidden attribute — without this the card
   wrapper stays laid over the whole stage while "hidden", and every click on
   a pack lands on its link instead of the pack */
.pack__card[hidden] { display:none; }

@media (prefers-reduced-motion:reduce) {
  .ppack__glow::after { animation:none; }
}

/* ---- the card that comes out ---- */
.pack__card { display:grid; place-items:center; gap:1.2rem; }
.pcardx {
  position:relative; display:block; width:clamp(13rem,24vw,17rem);
  border-radius:20px; overflow:hidden; text-decoration:none;
  background:var(--white); color:var(--black); text-align:left;
  box-shadow:0 34px 70px -26px rgba(0,0,0,0.8), 0 0 0 3px color-mix(in srgb, var(--blue) 55%, transparent);
  transform:translateY(26px) scale(0.86) rotateX(12deg); opacity:0;
  transition:transform 0.55s cubic-bezier(0.2,0.9,0.3,1.3), opacity 0.4s ease, box-shadow 0.3s ease;
}
.pack__card.is-in .pcardx { transform:none; opacity:1; }
.pcardx:hover { box-shadow:0 34px 70px -22px rgba(0,0,0,0.85), 0 0 34px 6px color-mix(in srgb, var(--blue) 55%, transparent); }
.pcardx__holo {
  position:absolute; inset:0; z-index:2; pointer-events:none; mix-blend-mode:overlay;
  background:linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.65) 45%, rgba(140,200,255,0.5) 55%, transparent 70%);
  background-size:250% 250%;
  animation:holo 3.4s linear infinite;
}
@keyframes holo { 0% { background-position:0% 0%; } 100% { background-position:250% 250%; } }
.pcardx__media { display:block; aspect-ratio:4/3; overflow:hidden; background:var(--blue-pale); }
.pcardx__media img { width:100%; height:100%; object-fit:cover; display:block; }
.pcardx__body { display:block; padding:1rem 1.1rem 0.4rem; }
.pcardx__kind { display:block; font-size:0.64rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:var(--blue); margin-bottom:0.3rem; }
.pcardx__body strong { display:block; font-size:1.04rem; margin-bottom:0.25rem; }
.pcardx__body em { display:block; font-style:normal; font-size:0.82rem; line-height:1.5; opacity:0.68; }
.pcardx__go { display:block; padding:0.6rem 1.1rem 1.1rem; font-size:0.8rem; font-weight:700; color:var(--blue); }
.pack__again {
  background:none; border:1px solid rgba(255,255,255,0.35); color:var(--white);
  border-radius:999px; padding:0.5rem 1.3rem; font-weight:700; font-size:0.85rem; cursor:pointer;
  transition:background 0.2s ease, border-color 0.2s ease;
}
.pack__again:hover { background:var(--blue); border-color:var(--blue); }

/* ---- the zoom-through when a card is opened ---- */
.zoomer { position:fixed; inset:0; z-index:200; pointer-events:none; }
.zoomer[hidden] { display:none; }
.zoomer span {
  position:absolute; left:var(--zx,50%); top:var(--zy,50%);
  width:14rem; height:14rem; margin:-7rem 0 0 -7rem; border-radius:50%;
  background:var(--blue); transform:scale(0); opacity:0.9;
  transition:transform 0.46s cubic-bezier(0.5,0,0.75,0), opacity 0.46s ease;
}
.zoomer.is-on span { transform:scale(14); opacity:1; }

@media (prefers-reduced-motion:reduce) {
  .ppack__shine, .pcardx__holo { animation:none; }
  .pcardx { transition:none; transform:none; opacity:1; }
}

/* ---- projects carousel ----
   A full-bleed horizontal rail: the first card lines up with the section
   text, the last one runs off the right edge so it reads as "there's more".
   Scroll-snap does the work; the arrows and dots are progressive enhancement. */
.projects { padding-bottom:calc(var(--blk-y) * 0.7); }
.projects .blk__lead { margin-bottom:0; }

/* break out of the section's horizontal padding so the rail is full-bleed */
.prow { position:relative; margin:var(--blk-rest) calc(var(--blk-x) * -1) 0; }
.prow__track {
  list-style:none; display:flex; gap:var(--blk-gap);
  overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory; scroll-behavior:smooth;
  padding:0.6rem max(var(--blk-x), calc((100% - 66rem) / 2)) 1.6rem;
  scrollbar-width:none;
}
.prow__track::-webkit-scrollbar { display:none; }

.pcard { flex:0 0 clamp(15rem,25vw,19.5rem); scroll-snap-align:start; }
.pcard__in {
  position:relative; display:flex; flex-direction:column; height:100%;
  border:1px solid color-mix(in srgb, var(--black) 9%, var(--white));
  border-radius:24px; overflow:hidden; background:var(--white); color:var(--black);
  text-decoration:none; box-shadow:0 20px 40px -30px rgba(10,2,3,0.3);
  transition:transform 0.25s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}
.pcard__in::after {
  content:''; position:absolute; inset:0; border-radius:inherit; padding:2px;
  box-sizing:border-box; pointer-events:none; z-index:3;
  background:conic-gradient(from var(--laser-angle, 0deg),
    var(--blue), color-mix(in srgb, var(--blue) 55%, var(--white)) 25%,
    var(--white) 50%, color-mix(in srgb, var(--blue) 55%, var(--white)) 75%, var(--blue) 100%);
  filter:drop-shadow(0 0 5px var(--blue)) drop-shadow(0 0 10px color-mix(in srgb, var(--blue) 70%, transparent));
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity 0.3s ease;
}
.pcard__in:hover::after { opacity:1; animation:laser-spin 3s linear infinite; }
.pcard__in:hover { transform:translateY(-5px); box-shadow:0 0 32px 6px color-mix(in srgb, var(--blue) 40%, transparent); }

.pcard__media { position:relative; aspect-ratio:4/3; overflow:hidden; background:var(--blue-pale); }
.pcard__media img { width:100%; height:100%; object-fit:cover; display:block; transition:transform 0.5s ease; }
.pcard__in:hover .pcard__media img { transform:scale(1.05); }

.pcard__status {
  position:absolute; top:0.9rem; left:0.9rem; z-index:2;
  border-radius:999px; padding:0.3rem 0.75rem; font-size:0.68rem; font-weight:700;
  letter-spacing:0.08em; text-transform:uppercase;
  background:color-mix(in srgb, var(--white) 88%, transparent); color:var(--black);
  backdrop-filter:blur(8px); border:1px solid rgba(255,255,255,0.6);
}
.pcard__status--live { background:var(--blue); color:var(--white); border-color:transparent; }
.pcard__status--pilot { background:var(--black); color:var(--white); border-color:transparent; }

.pcard__body { padding:1.4rem 1.5rem 1.6rem; display:flex; flex-direction:column; gap:0.5rem; flex:1; }
.pcard__body h3 { font-size:1.1rem; letter-spacing:-0.01em; }
.pcard__body p { font-size:0.9rem; line-height:1.65; opacity:0.7; flex:1; }
.pcard__go { font-size:0.82rem; font-weight:700; color:var(--blue); margin-top:0.4rem; }
.pcard__go em { font-style:normal; display:inline-block; transition:transform 0.2s ease; }
.pcard__in:hover .pcard__go em { transform:translate(2px,-2px); }
.pcard__note { font-size:0.78rem; font-weight:700; opacity:0.45; margin-top:0.4rem; }

/* arrows — hidden until they have somewhere to go */
.prow__nav {
  position:absolute; top:calc(50% - 3rem); z-index:4;
  width:2.9rem; height:2.9rem; border-radius:50%; border:none; cursor:pointer;
  background:var(--white); color:var(--black);
  box-shadow:0 6px 20px -6px rgba(10,2,3,0.35);
  display:grid; place-items:center;
  transition:opacity 0.25s ease, transform 0.2s ease, background 0.2s ease;
}
.prow__nav svg { width:1.3rem; height:1.3rem; }
.prow__nav:hover { background:var(--blue); color:var(--white); transform:scale(1.08); }
.prow__nav--prev { left:clamp(0.6rem,2vw,1.4rem); }
.prow__nav--next { right:clamp(0.6rem,2vw,1.4rem); }
.prow__nav[disabled] { opacity:0; pointer-events:none; }

.prow__foot { display:flex; justify-content:flex-end; }
.prow__dots { display:flex; gap:0.4rem; align-items:center; }
.prow__dots button {
  width:0.5rem; height:0.5rem; border-radius:999px; border:none; cursor:pointer;
  background:color-mix(in srgb, var(--black) 18%, var(--white));
  transition:width 0.25s ease, background 0.25s ease; padding:0;
}
.prow__dots button.is-on { width:1.5rem; background:var(--black); }

/* dark sections keep the same rail, inverted */
.blk--dark .prow__nav { background:var(--white); color:var(--black); }
.blk--dark .prow__dots button { background:rgba(255,255,255,0.28); }
.blk--dark .prow__dots button.is-on { background:var(--white); }

/* full-bleed image band — the eye's rest between text sections */
.imgband { position:relative; margin:0; background:var(--black); overflow:hidden; }
.imgband img { display:block; width:100%; height:clamp(19rem,46vh,30rem); object-fit:cover; }
.imgband--tall img { height:clamp(24rem,64vh,40rem); }
.imgband figcaption {
  position:absolute; left:0; right:0; bottom:0; padding:2.4rem clamp(1.4rem,6vw,4rem) 1.4rem;
  background:linear-gradient(transparent, rgba(10,2,3,0.72));
  color:var(--white); font-size:0.88rem; font-weight:700; letter-spacing:0.02em;
}

/* CTA */
.ctaline { display:flex; gap:1.8rem; flex-wrap:wrap; margin-bottom:1.8rem; font-size:1.05rem; }
.ctaline a { color:var(--white); font-weight:700; text-decoration:none; border-bottom:2px solid rgba(255,255,255,0.5); }
.ctaline a:hover { border-color:var(--white); }
.certrow { list-style:none; display:flex; gap:0.7rem; flex-wrap:wrap; }
.certrow li { border:1px solid rgba(255,255,255,0.42); border-radius:999px; padding:0.32rem 0.85rem; font-size:0.76rem; font-weight:700; letter-spacing:0.04em; }

/* -- shared: NEUStory mini-feed at the bottom of every pillar page
   (same card style as the 4600 build — rounded, hover-glow, backed by
   the same Backstage database, filtered to this pillar) -- */
.pnews { padding:var(--blk-y) var(--blk-x); background:var(--white); }
.pnews > * { max-width:66rem; margin-left:auto; margin-right:auto; }
.pnews .kicker { text-align:left; margin-bottom:1.1rem; }
.pnews .slide__title { color:var(--black); margin-bottom:0.4rem; letter-spacing:-0.02em; }
.feed { list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:var(--blk-gap); margin-top:var(--blk-rest); }
.feed__card {
  position:relative; border:1px solid color-mix(in srgb, var(--black) 8%, var(--white));
  border-radius:20px; overflow:hidden; padding:1.6rem; background:var(--white); color:var(--black);
  box-shadow:0 20px 40px -26px rgba(10,2,3,0.28);
  transition:transform 0.25s ease, box-shadow 0.25s ease, border-color 0.5s ease;
}
.feed__card:hover { transform:translateY(-4px); }
.feed__card.corner-lit {
  border-color:var(--blue);
  box-shadow:0 10px 30px -14px color-mix(in srgb, var(--blue) 35%, transparent), 0 0 22px color-mix(in srgb, var(--blue) 25%, transparent);
}
.feed__card::after {
  content:''; position:absolute; inset:0; border-radius:inherit; padding:4px; box-sizing:border-box;
  background:conic-gradient(from var(--laser-angle, 0deg), var(--blue), color-mix(in srgb, var(--blue) 55%, var(--white)) 25%,
    var(--white) 50%, color-mix(in srgb, var(--blue) 55%, var(--white)) 75%, var(--blue) 100%);
  filter:drop-shadow(0 0 5px var(--blue)) drop-shadow(0 0 10px color-mix(in srgb, var(--blue) 70%, transparent));
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity 0.3s ease; pointer-events:none; z-index:2;
}
.feed__card:hover::after { opacity:1; animation:laser-spin 3s linear infinite; }
.feed__card:hover { box-shadow:0 0 30px 6px color-mix(in srgb, var(--blue) 45%, transparent); }
@property --laser-angle { syntax:'<angle>'; inherits:false; initial-value:0deg; }
@keyframes laser-spin { to { --laser-angle:360deg; } }
.feed__photo { width:calc(100% + 3.2rem); max-width:none; margin:-1.6rem -1.6rem 1.1rem; display:block; aspect-ratio:4/3; object-fit:cover; object-position:50% 20%; }
.feed__meta { font-size:0.78rem; opacity:0.75; margin-bottom:0.6rem; }
.feed__card h3 { font-size:1.02rem; margin-bottom:0.5rem; }
.feed__card p:last-child { font-size:0.88rem; opacity:0.85; }

/* -- shared: prev/next chapter bar, pinned to the bottom of the viewport so
   the next chapter is always one tap away without scrolling to the end -- */
:root { --pfoot-h:4.1rem; }
.pillar-page { padding-bottom:var(--pfoot-h); }
.pfoot {
  position:fixed; bottom:0; left:0; right:0; z-index:70;
  display:flex; justify-content:space-between; align-items:center; gap:0.6rem;
  height:var(--pfoot-h); padding:0 clamp(0.9rem,4vw,2.4rem);
  background:color-mix(in srgb, var(--black) 88%, transparent);
  backdrop-filter:blur(12px); border-top:1px solid rgba(255,255,255,0.14);
  color:var(--white);
}
.pfoot a {
  color:var(--white); text-decoration:none; font-weight:700; opacity:0.9;
  font-size:0.9rem; display:flex; align-items:center; gap:0.4rem;
  padding:0.5rem 0.7rem; border-radius:999px; transition:background 0.2s ease, opacity 0.2s ease;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:38vw;
}
.pfoot a:hover { opacity:1; background:rgba(255,255,255,0.12); }
.pfoot__home {
  color:var(--white) !important; background:var(--blue);
  flex:none; padding:0.5rem 1.1rem !important;
}
.pfoot__home:hover { background:#0a67d6; }

/* ---- responsive ---- */
@media (max-width:900px) {
  .nav__side { display:none; }   /* burger takes over — see the drawer */
  .nav__pill { padding:0.5rem 1rem; gap:0.8rem; }
  /* pinned to the left edge, mirroring the search button, so neither one
     crowds the circle in the middle */
  .nav__burger {
    display:flex; position:absolute; left:clamp(1rem,4vw,1.6rem);
    top:50%; transform:translateY(-50%);
  }
  .pbar__name { font-size:1rem; }
  .pbar__back span { display:none; }
  .orbit { --orbit-rx:132; --orbit-ry:82; --orbit-min-scale:0.3; height:26rem; }
  .orbit--sov { --orbit-rx:140; --orbit-ry:86; --orbit-min-scale:0.36; height:26rem; }
  .orbit__node, .orbit__disc { width:5.4rem; }
  .orbit__disc { height:5.4rem; }
  .orbit--sov .orbit__node { width:5.4rem; }
  .orbit--sov .orbit__disc { width:4.4rem; height:4.4rem; }
  .orbit__label { font-size:0.85rem; }
  .orbit__label em { display:none; }
  .orbit__hub .slide__title { font-size:1.7rem; }
  .orbit__hub--map { width:92vw; }
  .phase-row, .grain-row { grid-template-columns:repeat(2,1fr); }
  .family__row { flex-direction:column; text-align:center; }
  .rail { display:none; }
}

/* ---- short viewports (most laptops are ~720-800px tall) ----
   These slides are scroll-snapped and clip their overflow, so anything that
   doesn't fit is simply lost. The card rings are the tallest thing on the
   page, so they shrink first, then the supporting text. */
@media (max-height:860px) {
  /* still measured from the disc — it is what overlaps, not the bar */
  .slide { padding-top:calc(var(--nav-disc) + 0.8rem); padding-bottom:1.6rem; }
  .orbit--sov { --orbit-ry:80; --orbit-rx:360; height:23rem; }
  .orbit--how { --orbit-ry:88; --orbit-rx:400; height:25rem; }
  .orbit__node--card,
  .orbit--sov .orbit__node--card { width:10.5rem; }
  .ocard__body { padding:0.6rem 0.75rem 0.7rem; }
  .ocard__body strong { font-size:0.88rem; }
  .ocard__body em { font-size:0.72rem; }
  .sov__head { margin-bottom:0.4rem; }
  .compact-quote { padding:0.7rem 1rem; }
  .compact-quote h4 { font-size:0.7rem; }
  .compact-quote p { font-size:0.82rem; }
}

/* phones: keep the airy rhythm but stop it turning into dead scrolling */
@media (max-width:600px) {
  :root { --blk-y:clamp(3.4rem,9vh,5.4rem); --blk-rest:2.2rem; --blk-gap:1.1rem; }
  .prob, .cap, .use { padding:1.5rem 1.4rem; }
  .step { padding:1.3rem 1.4rem; }
  .statrow li { padding:1.9rem 1.2rem; }
  .galcard__body { padding:1.4rem 1.4rem 1.6rem; }
  /* touch swipes the rail — arrows would just cover the cards */
  .prow__nav { display:none; }
  .orbit--pack { --orbit-rx:150; --orbit-ry:34; height:17rem; }
  .orbit__node--pack { width:5rem; }
  .prow__foot { justify-content:center; }
}

/* pointers that can't hover (touch) don't get the arrows either */
@media (hover:none) { .prow__nav { display:none; } }

/* a credential parked at the centre by a click */
.orbit--cert .orbit__node { cursor:pointer; }
.orbit--cert .orbit__node.is-held .clogo {
  transform:translateY(-4px) scale(1.12);
  box-shadow:0 12px 30px -8px color-mix(in srgb, var(--blue) 75%, transparent);
}
.orbit--cert .orbit__node.is-held .clogo__name { opacity:1; }

/* ---- section 2: Dr. KC's diagram, shown whole ---- */
.slide--teach { padding:0; align-items:center; justify-content:center; background:#0b1b2e; }

/* ── SECTION 2, ONE STAGE AT A TIME ──
   The section is taller than the screen and the stage inside it is pinned, so
   the extra height is scroll to spend walking through the three stages rather
   than a wall of three columns arriving at once. Same shape as Section 1. */
/* 340vh, not 290: three quarters of the travel is spent stepping through the
   three stages and the last quarter is the hold at the end, so the section has
   to be tall enough to give each stage a screenful of scroll AND still have a
   pause left over. */
.slide--teach { min-height:340vh; overflow:clip; justify-content:flex-start; }
/* Long-winded for the same reason the film band is: `.studio > :not(...)`
   forces position:relative on every child of this section, and a plain
   `.teach__pin` lost to it — leaving the stage un-pinned and the whole
   stepping dead, with --sp stuck at 1. */
.slide.slide--teach.studio > .teach__pin,
.slide.slide--teach > .teach__pin {
  position:sticky; top:0; height:100vh; width:100%;
  /* `safe center` because centred content that is TALLER than the box
     overflows equally both ways — and the top way is under the nav disc. Safe
     falls back to starting at the padding edge the moment it would not fit,
     so the heading can never ride up under the mark. */
  display:flex; flex-direction:column; justify-content:safe center; align-items:center;
  /* The bottom padding is not spare room — it is the strip the three stage
     dots stand in. At 2rem the dots printed straight over "Chapter · Happening
     now · Published" and the line under it. The stage is centred in whatever
     is left, so giving the dots their own space lifts the content clear. */
  padding:calc(var(--nav-disc) + 1rem) clamp(1rem,4vw,3rem) 6.4rem;
  overflow:hidden;
}

/* The three columns stop being a row and become one stack, each taking the
   middle of the screen in turn. They stay in the layout — the film's palm is
   lined up against the ball in stage 2, and an element that has been display
   :none has no position to line anything up with. */
.teach__pin .teach3 {
  position:relative; display:block; width:min(46rem,92vw); flex:none;
}
.teach__pin .teach3__col {
  position:absolute; left:50%; top:50%;
  width:100%; transform:translate(-50%,-50%) scale(0.96);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity 0.4s ease, transform 0.4s ease, visibility 0s linear 0.4s;
}
.teach__pin .teach3__col.is-on {
  opacity:1; visibility:visible; pointer-events:auto;
  transform:translate(-50%,-50%) scale(1);
  transition:opacity 0.4s ease, transform 0.4s ease, visibility 0s;
}
/* the stack needs a height of its own, since every column is out of flow */
.teach__pin .teach3 { min-height:clamp(20rem,52vh,30rem); }
/* A SHORT WINDOW has to give the stage less, or the heading plus the stage
   plus the dots' strip add up to more than the screen and the whole lot
   overflows the pin. The section used to have 160px of its own padding on top
   of the pin's, which hid this; that padding was what drew a line across the
   top of the section, so it is gone and the arithmetic has to work. */
@media (max-height:800px) { .teach__pin .teach3 { min-height:clamp(15rem,40vh,22rem); } }

/* the three inputs and the three results were columns; with a whole screen
   each they read better across */
.teach__pin .teach3__feeds { grid-template-columns:1fr 1fr; }
/* THREE PROMISES, THREE COLUMNS, ONE ROW.
   Two columns for three cards put Pillar 3 on a second row under Pillar 1,
   with a hole beside it — the eye read it as "two things, and an afterthought"
   rather than as Dr KC's three promises. They are equals, so they get equal
   room across one line. */
.teach__pin .teach3__results { grid-template-columns:repeat(3, 1fr); align-items:start; }
/* A smaller ball buys room at BOTH ends, because the stage is centred: the
   stage heading drops away from the subtitle, and AI CORE with the legend and
   the line under it lift clear of the three dots. Dr KC asked for both. */
.teach__pin .teach3__brain { width:min(19rem,44vw); margin:0 auto; }
/* A SHORT WINDOW. The ball is the tallest thing in stage 2 and it is sized
   from viewport WIDTH, so on a wide, short window it is the piece that pushes
   the legend down onto the three dots. This has to sit AFTER the rule above,
   not in an earlier media query — a media query buys no specificity. */
@media (max-height:800px) { .teach__pin .teach3__brain { width:min(15rem,30vh); } }
/* and the step heading sits lower, off the subtitle above it */
.teach__pin .teach3__step { margin-top:0.9rem; }
/* AI CORE hugs the ball rather than floating under it */
.teach__pin .teach3__chip { margin-top:0.2rem; }
.teach__pin .teach3__note { margin-top:0.5rem; }

/* "1 RAW INPUT SIGNALS" over the middle of its own stage. It was aligned left
   because it used to head a column in a row of three; alone on the screen,
   left-aligned over a centred grid just looks like a mistake. */
.teach__pin .teach3__step { justify-content:center; }

/* THE CALL TO ACTION GOES TO THE SIDE.
   It sits at the foot of the section, which is exactly where the three stage
   dots are — the button and the dots were printed on top of each other. The
   dots stay in the middle, because they are the thing being stepped; the
   button moves out to the right, where nothing else is. */
.teach__pin .ctarow {
  position:absolute; right:clamp(1rem,4vw,3rem); bottom:2rem;
  margin-top:0; z-index:6;
}

/* which stage is showing, and a way to jump */
.teachnav {
  position:absolute; left:50%; transform:translateX(-50%); bottom:2.2rem;
  display:flex; gap:0.5rem; z-index:6;
}
.teachdot {
  width:2.1rem; height:2.1rem; border-radius:50%; cursor:pointer;
  border:1.5px solid rgba(255,255,255,0.22); background:rgba(8,19,31,0.6);
  color:var(--white); font:800 0.78rem 'Inter',sans-serif; line-height:1;
  display:grid; place-items:center;
  transition:background 0.16s ease, border-color 0.16s ease, transform 0.12s ease;
}
.teachdot:hover { transform:scale(1.08); border-color:rgba(255,255,255,0.5); }
.teachdot.is-on { background:var(--blue); border-color:var(--blue); }

/* A phone gets the SAME stepped stage. It was left as a plain stack on the
   theory that a phone already reads top to bottom one thing at a time — but
   three columns stacked in one screen's worth of section simply ran into each
   other, which is what it actually looked like. One at a time, pinned, is the
   fix on a phone for the same reason it is on a desktop; only the sizes
   change. */
@media (max-width:820px) {
  .teach__pin { padding:calc(var(--nav-disc) + 0.3rem) 1rem 0.6rem; }
  .teach__pin .teach3 { width:min(30rem,94vw); min-height:clamp(18rem,46vh,26rem); }
  .teach__pin .teach3__feeds { grid-template-columns:1fr 1fr; gap:0.45rem; }
  .teach__pin .teach3__results { grid-template-columns:1fr; gap:0.5rem; }
  .teach__pin .teach3__brain { width:min(15rem,58vw); }
  .teach__pin .teach3__res { padding:0.4rem 0.6rem; }
  .teach__pin .teach3__res em { font-size:0.72rem; line-height:1.28; }
  /* the gap the stage heading gets on a desktop is room a phone does not
     have — stage 3 is the tallest and it met the button */
  .teach__pin .teach3__step { font-size:0.8rem; margin-top:0; }
  .teachdot { width:1.9rem; height:1.9rem; }
  /* On a phone the button and the dots come back into the flow and simply sit
     under the stage. Pinned to the bottom edge they were fine for stages 1 and
     2 and collided with stage 3, which is half as tall again as the others —
     and a phone has no width to put the button beside the dots. The stage box
     is given the height of the TALLEST stage so the two of them do not jump up
     and down as the stages change. */
  .teach__pin { justify-content:flex-start; }
  .teach__pin .teach3 { min-height:27.5rem; }
  .teach__pin .ctarow {
    position:static; transform:none; margin:0.6rem auto 0; justify-content:center;
  }
  .teachnav { position:static; transform:none; margin:0.5rem auto 0; }
}

/* ── the film behind "We Teach, It Learn" ──
   The same treatment as Section 1 and for the same reason: it is scrubbed by
   the scroll rather than played, so the boy and the robot move as far as
   somebody has moved and stop when they stop. Two things matter here and
   they pull against each other — the picture has to read, and three columns
   of text have to stay legible over it. Hence a dark plate and a heavy
   double scrim rather than a brighter film and a lighter wash. */
/* A FULL-WIDTH BAND, NOT A FILL. This section is taller than it is wide, so
   object-fit:cover cropped 60% of the frame's width away and took the boy —
   the whole point of the picture — with it. Full width shows the composition
   whole; scrollfilm.js sets --film-top so the robot's palm lands on the ball,
   and the edges are faded into the section's own colour so the band has no
   visible top and bottom. */
/* Written long, and it has to be. This section also carries `.studio`, and
   `.studio > :not(.studio__rig):not(.netball)` makes every child position
   :relative — three classes, so it beat a plain `.teach__bg` and left the
   band offset from its static position instead of placed against the
   section. These two selectors exist to outrank it. */
.slide.slide--teach.studio .teach__bg,
.slide.slide--teach .teach__bg {
  position:absolute; right:auto; z-index:0;
  left:var(--film-x, -160px); top:var(--film-top, 20%);
  /* wider than the section, so sliding it sideways to line the palm up on
     the ball can never pull a bare edge into view. scrollfilm.js knows this
     same number as PAD. */
  width:calc(100% + 320px); height:var(--film-h, auto); object-fit:cover;
  filter:brightness(0.52) contrast(1.05) saturate(1.1);
  pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, #000 16%, #000 82%, transparent 100%);
          mask-image:linear-gradient(180deg, transparent 0%, #000 16%, #000 82%, transparent 100%);
}
.slide.slide--teach.studio .teach__scrim,
.slide.slide--teach .teach__scrim {
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:
    /* THE DARK BAND HAS TO OUTLAST THE HEADING.
       It used to fade to 40% by 22%, and the heading runs from 19.6% to 30.2%
       — so the words were on the lit part of the film while every other
       heading on the site sits on near-black. That, not the letters, is why it
       read grey. The band now holds full strength to 32% and lets go by 40%,
       which is below the subtitle and above the first stage. */
    linear-gradient(180deg, rgba(11,27,46,0.94) 0%, rgba(11,27,46,0.9) 32%,
                            rgba(11,27,46,0.4) 40%,
                            rgba(11,27,46,0.4) 66%, rgba(11,27,46,0.95) 100%),
    /* and a wash left and right, which is where the two columns of text are */
    linear-gradient(90deg, rgba(11,27,46,0.9) 0%, rgba(11,27,46,0.18) 30%,
                           rgba(11,27,46,0.18) 70%, rgba(11,27,46,0.9) 100%);
}
/* Nothing is needed to lift the section's own contents in front of the film:
   this section also carries `.studio`, and that already gives every child
   position:relative and z-index:2 further down this file. */
.teach__art { width:100%; height:100%; object-fit:contain; display:block; }

/* home button, mirroring the search button on the other side of the bar */
.nav__home {
  position:absolute; left:clamp(1.2rem,4vw,3rem); top:50%; transform:translateY(-50%);
  pointer-events:auto; width:2.8rem; height:2.8rem; border-radius:50%;
  background:var(--blue-pale); color:var(--blue);
  display:grid; place-items:center; text-decoration:none;
  transition:background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.nav__home:hover { background:var(--blue); color:var(--white); transform:translateY(-50%) scale(1.06); }
@media (max-width:900px) { .nav__home { display:none; } }

/* the visible way back to the ring, under the swipe hint */
.pack__swap {
  display:block; margin:0.7rem auto 0;
  background:none; border:1px solid rgba(255,255,255,0.35); color:var(--white);
  border-radius:999px; padding:0.45rem 1.2rem; font-weight:700; font-size:0.82rem;
  cursor:pointer; transition:background 0.2s ease, border-color 0.2s ease;
}
.pack__swap:hover { background:var(--blue); border-color:var(--blue); }
.pack__swap[hidden] { display:none; }

/* ================= SECTION 1 · the reel =================
   3:4 cards tracking right and off the edge, over the hero background. The
   headline sits on top; hovering it opens a hole around the cursor. */
.slide--hero { justify-content:center; }
.reel {
  position:absolute; left:0; right:0; top:50%; transform:translateY(-50%);
  z-index:2; overflow:hidden; padding:2rem 0;
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image:linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.reel__track {
  display:flex; gap:clamp(1rem,2.4vw,2rem); width:max-content;
  animation:reel-run 48s linear infinite;
}
@keyframes reel-run { from { transform:translateX(-50%); } to { transform:translateX(0); } }
.reel.is-open .reel__track { animation-play-state:paused; }

.rcard {
  position:relative; flex:none; width:clamp(8rem,13vw,11.5rem); aspect-ratio:3/4;
  border:0; padding:0; cursor:pointer; border-radius:18px; overflow:hidden;
  background:color-mix(in srgb, var(--white) 8%, transparent);
  box-shadow:0 22px 46px -22px rgba(0,0,0,0.7), inset 0 0 0 1px rgba(255,255,255,0.18);
  transition:transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
}
.rcard img { width:100%; height:100%; object-fit:cover; display:block; }
.rcard__tag {
  position:absolute; left:0; right:0; bottom:0; padding:1.6rem 0.7rem 0.6rem;
  background:linear-gradient(transparent, rgba(10,2,3,0.8));
  color:var(--white); font-weight:700; font-size:0.76rem; letter-spacing:0.02em;
}
.rcard:hover { transform:translateY(-6px); box-shadow:0 28px 56px -18px rgba(0,0,0,0.8), 0 0 26px -4px color-mix(in srgb, var(--blue) 75%, transparent), inset 0 0 0 1px rgba(255,255,255,0.35); }
.reel.is-open .rcard:not(.is-open) { filter:blur(3px); opacity:0.35; }

/* ---- the headline, with a torch hole that follows the cursor ---- */
.herotitle {
  position:relative; z-index:4; text-align:center; text-transform:uppercase;
  letter-spacing:-0.01em; max-width:min(46rem,90vw); margin:0 auto;
  text-shadow:0 4px 30px rgba(0,0,0,0.55);
  cursor:default;
}
.herotitle.is-lit {
  -webkit-mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
          mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
}

/* ---- the landscape panel a card opens into ---- */
/* 250, not 150: Section 1's chapter fan sits at 200, and a film opened FROM
   that fan was rendering behind it. Above the fan, below the unlock box. */
.reelopen { position:fixed; inset:0; z-index:250; display:grid; place-items:center; padding:1.2rem;
  background:color-mix(in srgb, var(--black) 62%, transparent); backdrop-filter:blur(8px); }
.reelopen[hidden] { display:none; }
.reelopen__panel {
  position:relative; width:min(64rem,94vw); aspect-ratio:16/7;
  border-radius:26px; overflow:hidden; background:var(--black);
  box-shadow:0 50px 100px -30px rgba(0,0,0,0.8);
  animation:reel-open 0.45s cubic-bezier(0.2,0.9,0.3,1.3);
}
@keyframes reel-open { from { opacity:0; transform:scale(0.82); } }
.reelopen__panel img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.reelopen__body {
  position:absolute; inset:auto 0 0 0; padding:clamp(1.4rem,4vw,2.6rem);
  background:linear-gradient(transparent, rgba(10,2,3,0.88)); color:var(--white);
}
.reelopen__kind { font-size:0.76rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:#8ec5ff; margin-bottom:0.4rem; }
.reelopen__body h2 { font-size:clamp(1.5rem,3.4vw,2.6rem); letter-spacing:-0.02em; margin-bottom:0.9rem; }
.reelopen__go { color:var(--white); font-weight:700; text-decoration:none; border-bottom:2px solid var(--blue); }
.reelopen__close {
  position:absolute; top:1rem; right:1rem; width:2.4rem; height:2.4rem; border:0; cursor:pointer;
  border-radius:50%; background:rgba(255,255,255,0.9); color:var(--black); font-size:1rem;
}
.reelopen__close:hover { background:var(--blue); color:var(--white); }

@media (prefers-reduced-motion:reduce) {
  .reel__track { animation:none; }
  .reelopen__panel { animation:none; }
}
@media (max-width:640px) {
  .rcard { width:7rem; }
  .reelopen__panel { aspect-ratio:4/5; }
}

/* ---- the network core at the middle of "How It Works" ---- */
/* Section 4's title lives above the ring now — see index.ejs. Keeping it
   inside meant the back cards swung straight through it. */
.howhead { text-align:center; margin-bottom:0.4rem; }
.howhead .slide__title { margin-bottom:0.3rem; }
.netball {
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(19rem,42vw); aspect-ratio:1; z-index:2; pointer-events:none;
}
.netball__svg { width:100%; height:100%; display:block; overflow:visible; }
.netball__halo { animation:nb-breathe 4.5s ease-in-out infinite; transform-origin:center; }
@keyframes nb-breathe { 0%,100% { opacity:0.55; transform:scale(0.94); } 50% { opacity:1; transform:scale(1.06); } }
.netball__nodes circle { fill:var(--white); }
.netball__links line { stroke:var(--white); stroke-width:0.8; }

/* the bolt out to a chapter */
.netball__zaps {
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:var(--zw,100%); height:var(--zh,100%); overflow:visible; pointer-events:none;
}
.netball__bolt {
  fill:none; stroke:var(--white); stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
  filter:drop-shadow(0 0 6px var(--white)) drop-shadow(0 0 14px #8ec5ff);
  animation:nb-bolt 0.4s ease-out forwards;
}
@keyframes nb-bolt {
  0%   { opacity:0; stroke-width:3.5; }
  15%  { opacity:1; }
  60%  { opacity:0.85; stroke-width:1.6; }
  100% { opacity:0; stroke-width:1; }
}
/* the card lights up when it is struck */
.orbit__node--card.is-zapped .ocard {
  box-shadow:0 0 0 2px var(--white), 0 0 34px 8px color-mix(in srgb, var(--white) 70%, transparent);
  animation:nb-hit 0.62s ease-out;
}
@keyframes nb-hit {
  0%   { filter:brightness(2.1); }
  100% { filter:brightness(1); }
}
@media (prefers-reduced-motion:reduce) { .netball__halo { animation:none; } }

/* ================= MOBILE =================
   Touch has no cursor, so the hover tricks go — but the picture stays the
   same: the ball at the centre, cards drifting past, tap one to open it.
   Only the sizes change. */
@media (hover:none), (max-width:640px) {

  /* section 1 — solid headline above a swipeable strip */
  .slide--hero { justify-content:flex-start; }
  /* Full bleed. On a desktop the reel is absolutely positioned across the whole
     slide, so it already runs off both edges. Going static on a phone put it
     back inside the slide's side padding — the strip started an inch in from
     the left and the first card was permanently half-hidden behind the gutter.
     100vw plus a negative margin of exactly that padding (50% of the content
     box minus 50vw) pushes it back out to the glass on both sides, and the
     reel's own overflow:hidden does the clipping. */
  .reel { position:static; transform:none; padding:0; margin-top:1rem;
          width:100vw; margin-inline:calc(50% - 50vw);
          -webkit-mask-image:none; mask-image:none; }
  .reel__track { animation-duration:34s; gap:0.7rem; }
  .rcard { width:7.6rem; }
  .herotitle { position:static; order:-1; margin-top:calc(var(--nav-h) + 1.2rem);
               font-size:clamp(1.5rem,6.6vw,2rem); }
  /* no cursor, no torch — a stuck hole would just look like a broken title */
  .herotitle.is-lit, .overtitle.is-lit, .slide__title.is-lit,
  .grain__heading.is-lit, .pack__title.is-lit, .prow__title.is-lit,
  .blk__h.is-lit {
    -webkit-mask-image:none !important; mask-image:none !important;
  }
  .herowatch { position:static; margin:1rem auto 0; }

  /* the rings keep turning, just smaller */
  .orbit--sov  { --orbit-rx:118; --orbit-ry:44; --orbit-min-scale:0.42; height:19rem; }
  .orbit--how  { --orbit-rx:126; --orbit-ry:48; --orbit-min-scale:0.38; height:20rem; }
  .orbit--sdg  { --orbit-rx:120; --orbit-ry:44; --orbit-min-scale:0.42; height:19rem; }
  .orbit--wide { --orbit-rx:120; --orbit-ry:44; --orbit-min-scale:0.42; height:19rem; }
  .orbit--pack { --orbit-rx:112; --orbit-ry:38; --orbit-min-scale:0.34; height:16rem; }
  .orbit__node--card, .orbit--sov .orbit__node--card { width:8.4rem; }
  .orbit__node--pack { width:5.2rem; }
  .ocard__body { padding:0.55rem 0.6rem 0.7rem; }
  .ocard__body strong { font-size:0.8rem; }
  .ocard__body em { font-size:0.68rem; line-height:1.35; }
  .netball { width:min(11rem,42vw); }
  .netball--map { width:min(13rem,50vw); }

  /* the ball must not sit on top of the cards on a small screen */
  .netball--sect { opacity:0.75; }

  .prow__nav { display:none; }
  .galcard__body { padding:1.4rem 1.4rem 1.6rem; }
  .certdisc__close { right:26%; top:62%; }
}

/* ================= shared: centred see-through titles =================
   Every section title takes the same treatment as section 1 — the title sits
   over the moving cards and goes see-through around the cursor. One rule, so
   a new section gets it without anyone remembering to ask. */
.herotitle.is-lit, .overtitle.is-lit, .slide__title.is-lit,
.grain__heading.is-lit, .pack__title.is-lit, .prow__title.is-lit,
.blk__h.is-lit {
  -webkit-mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
          mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
}
.slide__title, .grain__heading, .pack__title, .prow__title, .blk__h { cursor:default; }

.overtitle {
  position:relative; z-index:4; text-align:center; text-transform:uppercase;
  letter-spacing:-0.01em; max-width:min(42rem,90vw); margin:0 auto;
  text-shadow:0 4px 30px rgba(0,0,0,0.55); cursor:default;
}
.overtitle--dark { color:var(--black); text-shadow:0 2px 24px rgba(255,255,255,0.85); }
.overtitle.is-lit {
  -webkit-mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
          mask-image:radial-gradient(circle var(--tr,110px) at var(--tx,50%) var(--ty,50%),
    transparent 0, transparent 55%, #000 100%);
}
.overtitle__sub {
  position:relative; z-index:4; text-align:center; margin:0.7rem auto 0;
  max-width:38rem; font-size:clamp(0.92rem,1.6vw,1.1rem); opacity:0.78;
}
.overtitle__sub--dark { color:var(--black); opacity:0.7; }

/* section 3 and 4 cards now match section 1's size */
.orbit__node--card,
.orbit--sov .orbit__node--card { width:clamp(8rem,13vw,11.5rem); }
.ocard { border:0; padding:0; cursor:pointer; text-align:left; width:100%; }

/* a card opened from a ring */
.ocard.is-open, .rcard.is-open, .lcard.is-open { outline:2px solid var(--white); outline-offset:3px; }

/* ---- Trust: the P&G-style logo drift ---- */
.reel--pale { --reel-mask:transparent; }
.reel__track--slow { animation-duration:70s; }
.lcard {
  flex:none; width:clamp(8rem,12vw,10.5rem); border:0; background:none; cursor:pointer;
  display:flex; flex-direction:column; align-items:center; gap:0.7rem; padding:0.4rem;
}
.lcard__disc {
  width:clamp(6.5rem,9vw,8.4rem); aspect-ratio:1; border-radius:50%;
  background:var(--white); display:grid; place-items:center; padding:1.1rem;
  box-shadow:0 16px 36px -20px rgba(10,2,3,0.4);
  transition:transform 0.28s ease, box-shadow 0.28s ease;
}
.lcard__disc img { max-width:100%; max-height:100%; object-fit:contain; display:block; }
.lcard:hover .lcard__disc {
  transform:translateY(-6px) scale(1.05);
  box-shadow:0 22px 46px -18px color-mix(in srgb, var(--blue) 70%, transparent);
}
.lcard__name { font-size:0.78rem; font-weight:700; color:var(--black); opacity:0.72; text-align:center; }
#commitment .thanks { position:relative; z-index:4; text-align:center; margin-top:1.2rem; }

/* ---- Impact: the SDG goals ---- */
.rcard--sdg {
  aspect-ratio:1; background:var(--white); padding:0.9rem;
  display:grid; place-items:center;
}
.rcard--sdg img { width:100%; height:100%; object-fit:contain; }
.rcard--sdg .rcard__tag { background:none; padding:0.4rem; color:var(--black); opacity:0.55; }

/* both new sections stack their reel behind a centred title */
#commitment, #sdg { position:relative; justify-content:center; }
#commitment .reel, #sdg .reel { position:absolute; left:0; right:0; top:50%; transform:translateY(-50%); }

/* ================= footer, Dr. KC's slide 10 =================
   A blue block with a curved top edge and a "Back to top" pill riding it. */
.pgfoot {
  position:relative; background:var(--blue); color:var(--white);
  padding:5.5rem clamp(1.4rem,5vw,4.5rem) 1.6rem; margin-top:4rem;
}
/* THE CHAPTER PAGES AND NEUSTORY CARRY A FIXED BAR at the very bottom — the
   dark "← NEUStudio · All chapters" strip. It floats over everything, so the
   last row of the footer (the locale, the copyright and the social marks) sat
   underneath it and could not be read or pressed. Give the footer the bar's
   own height back as padding on exactly the pages that have one. */
.pillar-page .pgfoot { padding-bottom:calc(var(--pfoot-h) + 1.6rem); }
.pgfoot__curve {
  position:absolute; left:0; right:0; top:-3.4rem; height:5rem;
  background:var(--blue); border-radius:50% 50% 0 0 / 100% 100% 0 0;
}
.pgfoot__top {
  position:absolute; top:1.4rem; left:50%; transform:translateX(-50%);
  border:1.5px solid rgba(255,255,255,0.85); border-radius:999px;
  padding:0.55rem 1.5rem; color:var(--white); text-decoration:none;
  font-weight:700; font-size:0.9rem; transition:background 0.2s ease, color 0.2s ease;
}
.pgfoot__top:hover { background:var(--white); color:var(--blue); }

.pgfoot__grid {
  display:grid; gap:2rem clamp(1.4rem,3vw,3rem); max-width:78rem; margin:0 auto;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
}
.pgfoot__contact { display:flex; flex-direction:column; gap:0.5rem; }
.pgfoot__col h3 {
  font-size:0.78rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase;
  margin-bottom:0.9rem;
}
.pgfoot__col a, .pgfoot__contact a {
  display:block; color:var(--white); text-decoration:none; opacity:0.86;
  font-size:0.86rem; margin-bottom:0.5rem; transition:opacity 0.2s ease;
}
.pgfoot__col a:hover, .pgfoot__contact a:hover { opacity:1; text-decoration:underline; }
.pgfoot__col a em, .pgfoot__contact a em { font-style:normal; font-size:0.72rem; opacity:0.75; }

.pgfoot__base {
  display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap;
  max-width:78rem; margin:2.6rem auto 0; padding-top:1.4rem;
  border-top:1px solid rgba(255,255,255,0.22);
}
.pgfoot__locale {
  border:1.5px solid rgba(255,255,255,0.75); border-radius:999px;
  padding:0.4rem 1.1rem; font-size:0.82rem; font-weight:700;
}
.pgfoot__copy { font-size:0.8rem; opacity:0.78; }
/* The registration number is required reading and nobody reads it. A shade
   quieter than the name it follows, and never broken across two lines —
   "(1308301-T)" landing on its own is how a number starts looking like a typo. */
.pgfoot__reg { opacity:0.75; white-space:nowrap; }
.pgfoot__social { display:flex; gap:1rem; }
.pgfoot__social a { color:var(--white); opacity:0.85; transition:opacity 0.2s ease, transform 0.2s ease; }
.pgfoot__social a:hover { opacity:1; transform:translateY(-2px); }
.pgfoot__social svg { width:1.3rem; height:1.3rem; display:block; }
@media (max-width:640px) {
  .pgfoot__base { justify-content:center; text-align:center; }
  .pgfoot__copy { order:3; width:100%; }
}

/* commitment: partner marks as rounded squares, section-1 size, under the thanks */
#commitment { justify-content:center; }
#commitment .reel { position:static; transform:none; margin-top:var(--blk-rest,2.6rem); overflow:hidden; }
.lcard {
  flex:none; width:clamp(8rem,13vw,11.5rem); aspect-ratio:3/4;
  border:0; cursor:pointer; border-radius:18px; overflow:hidden;
  background:var(--white); padding:1.2rem 1rem;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:0.9rem;
  box-shadow:0 18px 40px -24px rgba(10,2,3,0.4);
  transition:transform 0.28s ease, box-shadow 0.28s ease;
}
.lcard img { max-width:100%; max-height:52%; object-fit:contain; display:block; }
.lcard:hover { transform:translateY(-6px); box-shadow:0 24px 50px -20px color-mix(in srgb, var(--blue) 65%, transparent); }
.lcard__name { font-size:0.78rem; font-weight:700; color:var(--black); opacity:0.72; text-align:center; line-height:1.35; }

/* impact: the SDG ring, with the earth at its core */
.orbit--sdg { --orbit-rx:420; --orbit-ry:104; --orbit-min-scale:0.52; height:clamp(27rem,56vh,36rem); }
.ocard--sdg { background:var(--white); }
.ocard--sdg .ocard__media { aspect-ratio:1; padding:0.9rem; background:var(--white); display:grid; place-items:center; }
.ocard--sdg .ocard__media img { width:100%; height:100%; object-fit:contain; }
.netball--earth .netball__nodes circle { fill:#bdf7d8; }
.netball--earth .netball__links line { stroke:#7fe3c0; }
.netball__globe {
  fill:none; stroke:#4fd1a5; stroke-width:1.1; opacity:0.5;
  animation:nb-breathe 5.5s ease-in-out infinite;
}
/* the bolt takes the earth's colours in this section */
.netball--earth .netball__bolt {
  stroke:#8ef0c4;
  filter:drop-shadow(0 0 6px #8ef0c4) drop-shadow(0 0 14px #35a8e0);
}
.orbit--sdg .orbit__node--card.is-zapped .ocard {
  box-shadow:0 0 0 2px #8ef0c4, 0 0 34px 8px color-mix(in srgb, #35a8e0 70%, transparent);
}
@media (max-height:860px) { .orbit--sdg { --orbit-ry:80; --orbit-rx:360; height:23rem; } }

/* ================= the studio treatment =================
   Every section from 2 onwards is staged like a room: a light from above, a
   shadow on the floor, and information flowing between the floors. */
.studio { position:relative; isolation:isolate; }
.studio__rig { position:absolute; inset:0; pointer-events:none; z-index:1; overflow:hidden; }

/* ---- the room ----
   A studio: a lit strip across the ceiling, light slots down the side walls,
   a floor that catches the reflection. Dark rooms and white rooms use the
   same geometry so every section reads as the same space. */
.studio__ceil {
  position:absolute; left:8%; right:8%; top:0; height:4px; border-radius:0 0 4px 4px;
  background:linear-gradient(90deg, transparent, var(--white) 12%, var(--white) 88%, transparent);
  box-shadow:0 0 22px 5px color-mix(in srgb, var(--white) 60%, transparent),
             0 0 60px 18px color-mix(in srgb, var(--white) 26%, transparent);
  opacity:0.85;
}
.studio__strip {
  position:absolute; top:12%; width:3px; height:46%; border-radius:3px;
  background:linear-gradient(var(--white), color-mix(in srgb, var(--white) 30%, transparent));
  box-shadow:0 0 16px 3px color-mix(in srgb, var(--white) 45%, transparent);
  opacity:0.5;
}
.studio__strip--l { left:3.5%; transform:skewY(9deg); }
.studio__strip--r { right:3.5%; transform:skewY(-9deg); }

/* the light the ceiling strip throws into the room */
.studio__light {
  position:absolute; left:50%; top:0; transform:translateX(-50%);
  width:min(120rem,150%); height:82%;
  background:radial-gradient(ellipse 46% 100% at 50% 0%,
    color-mix(in srgb, var(--white) 20%, transparent) 0%,
    color-mix(in srgb, var(--white) 6%, transparent) 45%, transparent 74%);
}
/* the back wall meeting the floor, and the sheen on the floor itself */
.studio__floor {
  position:absolute; left:0; right:0; bottom:0; height:34%;
  background:linear-gradient(transparent, color-mix(in srgb, var(--black) 46%, transparent));
}
.studio__gloss {
  position:absolute; left:12%; right:12%; bottom:0; height:26%;
  background:radial-gradient(ellipse 60% 100% at 50% 100%,
    color-mix(in srgb, var(--white) 13%, transparent), transparent 70%);
  filter:blur(6px);
}

/* white rooms: the same space, lit rather than dark */
.slide--light .studio__ceil, .pack .studio__ceil {
  background:linear-gradient(90deg, transparent, var(--white) 12%, var(--white) 88%, transparent);
  box-shadow:0 0 22px 6px color-mix(in srgb, var(--blue) 22%, transparent),
             0 2px 30px 10px color-mix(in srgb, var(--black) 10%, transparent);
}
.slide--light .studio__strip, .pack .studio__strip {
  background:linear-gradient(var(--white), color-mix(in srgb, var(--white) 40%, transparent));
  box-shadow:0 0 18px 5px color-mix(in srgb, var(--black) 12%, transparent);
  opacity:0.9;
}
.slide--light .studio__light, .pack .studio__light {
  background:radial-gradient(ellipse 46% 100% at 50% 0%,
    color-mix(in srgb, var(--black) 6%, transparent) 0%, transparent 72%);
}
.slide--light .studio__floor, .pack .studio__floor {
  background:linear-gradient(transparent, color-mix(in srgb, var(--black) 12%, transparent));
}
.slide--light .studio__gloss, .pack .studio__gloss {
  background:radial-gradient(ellipse 60% 100% at 50% 100%,
    color-mix(in srgb, var(--white) 85%, transparent), transparent 70%);
}
/* the blue room keeps its colour, just gets the same lighting */
.slide--blue .studio__floor { background:linear-gradient(transparent, color-mix(in srgb, #003c8f 45%, transparent)); }

/* the section's own content must sit above the rig */
.studio > *:not(.studio__rig):not(.netball) { position:relative; z-index:2; }

/* ================= NEUON Starter Pack, in the light ================= */
.pack { background:var(--white); color:var(--black); }
.pack .kicker { color:var(--blue); }
.pack__h em { color:var(--blue); }
.pack__lead { opacity:0.66; }
.pack__pickhint { opacity:0.5; }
.pack__swap, .pack__again {
  border-color:color-mix(in srgb, var(--black) 22%, transparent); color:var(--black);
}
.pack__swap:hover, .pack__again:hover { background:var(--blue); border-color:var(--blue); color:var(--white); }
.pack .netball__nodes circle { fill:var(--blue); }
.pack .netball__links line { stroke:var(--blue); }
.pack .netball__bolt {
  stroke:var(--blue);
  filter:drop-shadow(0 0 5px var(--blue)) drop-shadow(0 0 14px color-mix(in srgb, var(--blue) 70%, transparent));
}
.orbit__node--pack.is-zapped .ppack__foil {
  box-shadow:0 0 0 2px var(--white), 0 0 34px 10px color-mix(in srgb, var(--blue) 75%, transparent);
  animation:nb-hit 0.62s ease-out;
}

/* ================= the dome's arc wording ================= */
.certarc { position:absolute; inset:0; width:100%; height:100%; pointer-events:none; }
.certarc__t {
  fill:var(--blue); font-family:'Inter',sans-serif; font-weight:700;
  font-size:21px; letter-spacing:6px; opacity:0.8;
}
.certdisc__kicker--sr {
  position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%);
}
.clogo-wrap { border:0; background:none; padding:0; cursor:pointer; }

/* ================= cards fly toward the viewer =================
   The panel arrives like a card thrown at the screen: angled away, spinning
   flat, growing as it comes. */
.reelopen { perspective:1400px; }
.reelopen__panel { animation:card-throw 0.62s cubic-bezier(0.16,0.84,0.28,1.06); }
@keyframes card-throw {
  0%   { opacity:0; transform:translate3d(28vw,14vh,-900px) rotateY(38deg) rotateZ(9deg) scale(0.42); }
  55%  { opacity:1; }
  100% { opacity:1; transform:none; }
}
@media (prefers-reduced-motion:reduce) {
  .reelopen__panel { animation:none; }
  .studio__rig { display:none; }
}


/* the dome's own core — same engine as every section */
.netball--dome { width:9rem; position:absolute; left:50%; top:46%; transform:translate(-50%,-50%); z-index:0; }
.netball--dome .netball__nodes circle { fill:var(--blue); }
.netball--dome .netball__links line { stroke:var(--blue); }
.netball--dome .netball__bolt {
  stroke:var(--blue); stroke-width:1.6;
  filter:drop-shadow(0 0 4px var(--blue)) drop-shadow(0 0 12px color-mix(in srgb, var(--blue) 60%, transparent));
}
.orbit--cert .orbit__node.is-zapped .clogo {
  box-shadow:0 0 0 2px var(--blue), 0 0 26px 6px color-mix(in srgb, var(--blue) 55%, transparent);
  animation:nb-hit 0.62s ease-out;
}
.orbit--cert { position:relative; z-index:1; }

/* ---- one core per section, at its centre ---- */
.netball--sect {
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(15rem,32vw); aspect-ratio:1; z-index:1; pointer-events:none;
}
.netball--sect .netball__halo { fill:none; }
.netball--sect .netball__nodes circle { fill:var(--white); opacity:0.5; }
.netball--sect .netball__links line { stroke:var(--white); }
.slide--light .netball--sect .netball__nodes circle,
.slide--light .netball--sect .netball__links line { fill:var(--blue); stroke:var(--blue); }
.netball--sect .netball__bolt { stroke-width:1.5; }
.slide--light .netball--sect .netball__bolt {
  stroke:var(--blue);
  filter:drop-shadow(0 0 4px var(--blue)) drop-shadow(0 0 12px color-mix(in srgb, var(--blue) 55%, transparent));
}
/* anything struck lights briefly, whatever kind of card it is */
.is-zapped { position:relative; }
.news-card.is-zapped, .allies__card.is-zapped, .feed__card.is-zapped, .lcard.is-zapped {
  box-shadow:0 0 0 2px color-mix(in srgb, var(--blue) 80%, var(--white)),
             0 0 30px 8px color-mix(in srgb, var(--blue) 55%, transparent);
  animation:nb-hit 0.62s ease-out;
}

/* every section from 2 on reads the same way: centred title above its content */
#news, #allies, #growth { text-align:center; }
#news > .slide__title, #allies > .slide__title, #growth > .slide__title,
#news > .lede, #allies > .lede, #growth > .lede { margin-left:auto; margin-right:auto; }
#news .news-grid, #growth .phase-row { text-align:left; }
.sovhead { position:relative; z-index:3; }

/* sovereign's core is the Sarawak map itself — the ball stays out of sight,
   only its bolts fire */
.netball--quiet .netball__svg { opacity:0; }
.netball--quiet { width:min(26rem,50vw); }

/* ================= the shared wide ring =================
   Trust, NEUStory, NEUFamily and Growth all use the same shape as How It
   Works: items circling a core. Adding a second batch later just means a
   second ring — the geometry already supports it. */
.orbit--wide { --orbit-rx:400; --orbit-ry:104; --orbit-min-scale:0.5; height:clamp(24rem,50vh,32rem); }
@media (max-height:860px) { .orbit--wide { --orbit-ry:78; --orbit-rx:340; height:21rem; } }

/* a card with no picture — the text carries it */
.ocard--plain { background:var(--white); color:var(--black); padding:1.2rem 1.1rem; }
.ocard--plain .ocard__body { padding:0; }
.slide--dark .ocard--plain, .slide--blue .ocard--plain { background:color-mix(in srgb, var(--white) 96%, var(--blue)); }
.ocard__period {
  display:block; font-size:0.68rem; font-weight:700; letter-spacing:0.1em;
  text-transform:uppercase; color:var(--blue); margin-bottom:0.35rem;
}
.ocard__letter {
  display:grid; place-items:center; width:2.4rem; height:2.4rem; margin-bottom:0.6rem;
  border-radius:50%; background:var(--blue); color:var(--white);
  font-weight:700; font-size:1.1rem;
}

/* ================= credential logos as small balls =================
   Small enough that nothing collides with the arc wording or the play button;
   click one to see the whole logo in the panel. */
.clogo {
  width:3.4rem; height:3.4rem; border-radius:50%; padding:0.5rem;
  background:var(--white); display:grid; place-items:center;
  box-shadow:0 6px 18px -8px rgba(10,2,3,0.35);
  transition:transform 0.25s ease, box-shadow 0.25s ease;
}
.clogo img { max-width:100%; max-height:100%; object-fit:contain; display:block; }
/* THE CERTIFICATES, AS BIG AS THE DOME ALLOWS
   These are the whole point of the panel — MD status, MySTI, SIRIM, the
   ministries — and they were being shown at a size where nobody past the front
   row could tell one from another. Each now sits in a disc slightly LARGER
   than the blue NEUON disc on the bar, with the ring pushed out to match so
   they still clear each other. */
.orbit--cert .clogo {
  width:calc(var(--nav-disc) * 1.06); height:calc(var(--nav-disc) * 1.06);
  padding:0.35rem;
}
.orbit--cert .orbit__node { width:calc(var(--nav-disc) * 1.06 + 0.8rem); }
/* Bigger discs need a wider ring or the front three sit on top of each other,
   and a deeper falloff so the ones round the back clearly read as behind
   rather than as a second row of half-size logos. */
.orbit--cert {
  height:calc(var(--nav-disc) * 1.55);
  --orbit-rx:300; --orbit-ry:46; --orbit-min-scale:0.40;
}
.orbit--cert .clogo-wrap:hover .clogo {
  transform:translateY(-3px) scale(1.12);
  box-shadow:0 10px 24px -8px color-mix(in srgb, var(--blue) 70%, transparent);
}
.orbit--cert .clogo__name { display:none; }
/* the Trust ring shows them larger, with their names */
.clogo--lg { width:6.4rem; height:6.4rem; padding:1rem; }
.orbit--wide .orbit__node--logo { width:8rem; }
.orbit--wide .clogo-wrap { display:flex; flex-direction:column; align-items:center; gap:0.5rem; }
.orbit--wide .clogo__name { opacity:0; transition:opacity 0.25s ease; font-size:0.7rem; }
.orbit--wide .orbit__node.is-front .clogo__name { opacity:1; }

/* the panel shows a logo whole, not cropped */
.reelopen__panel img[src$=".png"], .reelopen__panel img[src$=".svg"] {
  object-fit:contain; background:var(--white); padding:clamp(2rem,6vw,4rem);
}

/* NEUFamily carries a hero row and the G.R.A.I.N. ring; on a laptop the two
   together overrun the slide, so the row gives up its height first */
#family .family__row { align-items:center; }
@media (max-height:900px) {
  #family .family__row { display:block; }
  #family .family__row img, #family .family__row .family__photo { display:none; }
  #family .slide__title { font-size:clamp(1.4rem,3vw,1.9rem); margin-bottom:0.4rem; }
  #family .lede { font-size:0.95rem; margin-bottom:0.7rem; }
  #family .family__ctas { margin-bottom:0.4rem; }
  #family .grain__heading { margin:0.2rem 0; font-size:0.9rem; }
}

/* ---- every core is the same ball; Sovereign's simply has the map inside ---- */
.netball--map { width:min(22rem,44vw); }
.netball__map {
  position:absolute; inset:12%; display:grid; place-items:center;
  opacity:0.5; pointer-events:none;
}
.netball__map svg { width:100%; height:100%; }
.netball--map .netball__svg { position:relative; z-index:2; }
/* the SDG core drops its green — it is the same blue ball as the rest */
.netball--sect .netball__globe { display:none; }

/* ---- logos always inside their circle ---- */
.clogo, .clogo--lg { overflow:hidden; }
.clogo img, .clogo--lg img {
  max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain;
}

/* ---- NEUFamily and Partners centred, like every other section ---- */
#family, #allies { text-align:center; }
#family .family__row {
  display:flex; flex-direction:column; align-items:center; gap:0.6rem;
}
#family .family__row > div { max-width:46rem; margin:0 auto; }
#family .family__ctas { justify-content:center; }
#family .lede, #allies .lede { margin-left:auto; margin-right:auto; }
#allies .allies__card { margin-left:auto; margin-right:auto; max-width:26rem; }
#family .grain__heading { text-align:center; }

/* A tall logo (SFC is 2:3) was still overflowing its ball: max-height:100% on
   an auto-height grid item does not always bind. Filling the box and letting
   object-fit do the letterboxing always does. */
.clogo img, .clogo--lg img {
  width:100%; height:100%; object-fit:contain; display:block;
  max-width:100%; max-height:100%;
}

/* Percentage heights on a grid item resolve against an auto-sized row, so
   `height:100%` fell back to the image's intrinsic ratio and tall logos broke
   out. Pinning the image to the circle's inset makes the box definite. */
.clogo { position:relative; padding:0; }
/* The artwork fills its disc. At inset 22% a wide logo like Malaysia Digital
   was rendering at about a third of the circle it was sitting in — most of
   what you saw was white space. */
.clogo img, .clogo--lg img {
  position:absolute; inset:12%;
  width:auto; height:auto; max-width:none; max-height:none;
  object-fit:contain;
}
.clogo--lg img { inset:10%; }

/* ---- 4 · the top bar reads louder ---- */
.nav__pill a:not(.nav__brand) { font-size:1.02rem; font-weight:800; opacity:0.82; letter-spacing:-0.01em; }
.nav__pill a:not(.nav__brand):hover, .nav__pill a.active { opacity:1; }
.nav__pill a.active { text-decoration:underline; text-underline-offset:6px; text-decoration-thickness:3px; }

/* ---- 6 · the logo reads as a button by weight, not by pulsing ---- */
.nav__radar { display:none; }
.nav__disc {
  box-shadow:0 6px 0 -1px color-mix(in srgb, var(--blue) 55%, var(--black)),
             0 12px 26px -6px color-mix(in srgb, var(--blue) 70%, transparent),
             inset 0 2px 0 color-mix(in srgb, var(--white) 45%, transparent);
}
.nav__brand--btn:hover .nav__disc {
  transform:translateX(-50%) translateY(-2px) scale(1.04);
  box-shadow:0 8px 0 -1px color-mix(in srgb, var(--blue) 55%, var(--black)),
             0 16px 32px -6px color-mix(in srgb, var(--blue) 80%, transparent),
             inset 0 2px 0 color-mix(in srgb, var(--white) 45%, transparent);
}
.nav__brand--btn:active .nav__disc {
  transform:translateX(-50%) translateY(3px) scale(0.99);
  box-shadow:0 2px 0 -1px color-mix(in srgb, var(--blue) 55%, var(--black)),
             0 6px 16px -6px color-mix(in srgb, var(--blue) 70%, transparent);
}

/* ---- 3 · the core charges before it fires ---- */
.netball.is-charging .netball__nodes circle { animation:nb-charge 0.62s ease-in-out; }
.netball.is-charging .netball__links line { animation:nb-charge-line 0.62s ease-in-out; }
@keyframes nb-charge { 0%,100% { opacity:0.5; } 55% { opacity:1; } }
@keyframes nb-charge-line { 0%,100% { opacity:0.2; } 55% { opacity:0.95; } }
.netball.is-charging .netball__halo { animation:nb-charge-halo 0.62s ease-out; }
@keyframes nb-charge-halo { 0% { transform:scale(0.9); } 60% { transform:scale(1.25); } 100% { transform:scale(1); } }

/* ---- 5 · film cards ---- */
.rcard--film { aspect-ratio:16/9; width:clamp(11rem,19vw,16rem); }
.rcard--film .rcard__tag { font-size:0.7rem; line-height:1.3; padding:2rem 0.7rem 0.6rem; }

.reelopen__video { position:absolute; inset:0; background:var(--black); }
.reelopen__video[hidden] { display:none; }
.reelopen__video iframe { width:100%; height:100%; border:0; display:block; }
.reelopen__panel:has(.reelopen__video:not([hidden])) .reelopen__body {
  position:static; background:none; padding:0.9rem clamp(1rem,3vw,1.8rem) 1.1rem;
}
.reelopen__panel:has(.reelopen__video:not([hidden])) { aspect-ratio:auto; }

/* ---- 7 · NEUFamily drops the big round portrait ---- */
#family .family__photo, #family .family__row img, #family .family__round { display:none !important; }

/* ---- 2 · the credentials dome fills the screen on a phone ---- */
@media (hover:none), (max-width:640px) {
  .certdisc {
    width:100vw; height:100dvh; aspect-ratio:auto; border-radius:0;
    left:0; top:0; transform:none;
  }
  /* On a phone the dome is a full sheet, so the core and the ring stack and
     sit together in the middle of it. Left absolute, the core drifted to the
     bottom of an otherwise empty screen, nowhere near the logos it belongs to. */
  .certdisc { display:flex; flex-direction:column; align-items:center; justify-content:center; }
  .certdisc__in { position:static; width:86%; margin:0 auto; padding-top:0; transform:none; }
  .certarc { display:none; }
  .certdisc__close { position:absolute; top:calc(var(--nav-h) + 0.6rem); right:1rem; }
  .netball--dome { position:static; width:8rem; margin:0 auto 1rem; transform:none; }
  .orbit--cert { --orbit-rx:110; --orbit-ry:26; height:6rem; }
  .certwave { display:none; }
}

/* ================= 2 · We Teach, It Learn =================
   NO TOP PADDING ON THE SECTION. It used to hold the content clear of the
   nav disc, and the pinned stage inside now does that with padding of its
   own — so all this was still doing was holding the film and the scrim 160px
   down from the top of the section. Above that strip was flat section colour;
   below it, film under a 94% scrim, which is a few per cent lighter. That
   difference is the horizontal line across the top of the section. */
.slide--teach { padding-top:0; }
.teach3 {
  position:relative; z-index:2; display:grid; align-items:center;
  grid-template-columns:1fr 1.1fr 1fr; gap:clamp(1rem,3vw,3rem);
  width:min(78rem,94vw); margin:0 auto;
}
.teach3__step {
  display:flex; align-items:center; gap:0.6rem; margin-bottom:1rem;
  font-size:0.76rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:#8ec5ff;
}
.teach3__step span {
  display:grid; place-items:center; width:1.6rem; height:1.6rem; border-radius:50%;
  background:var(--blue); color:var(--white); font-size:0.78rem;
}
.teach3__feeds, .teach3__results { list-style:none; display:grid; gap:0.5rem; }
.teach3__feeds li {
  display:flex; align-items:center; gap:0.7rem; padding:0.55rem 0.85rem;
  border:1px solid rgba(255,255,255,0.16); border-radius:12px;
  background:rgba(255,255,255,0.05); font-size:0.85rem;
  animation:feed-send 3.6s ease-in-out infinite; animation-delay:var(--d,0s);
}
@keyframes feed-send {
  0%,70%,100% { transform:translateX(0); border-color:rgba(255,255,255,0.16); }
  20%         { transform:translateX(14px); border-color:#8ec5ff; }
}
.teach3__glyph { color:#8ec5ff; font-size:1rem; }

.teach3__brain { position:relative; display:grid; place-items:center; aspect-ratio:1; }
.teach3__brain svg { width:100%; height:100%; }
.teach3__mesh circle { fill:var(--white); }
.teach3__mesh line { stroke:#8ec5ff; stroke-width:0.7; }
.teach3__pulse {
  position:absolute; inset:14%; border-radius:50%; border:2px solid #8ec5ff;
  opacity:0; animation:teach-ping 3s ease-out infinite;
}
.teach3__pulse--2 { animation-delay:1.5s; }
@keyframes teach-ping { 0% { transform:scale(0.7); opacity:0.7; } 100% { transform:scale(1.5); opacity:0; } }
.teach3__chip {
  position:absolute; padding:0.4rem 0.9rem; border-radius:8px;
  background:var(--blue); color:var(--white); font-weight:700;
  font-size:0.7rem; letter-spacing:0.14em;
  box-shadow:0 0 24px 4px color-mix(in srgb, var(--blue) 70%, transparent);
}
.teach3__note { text-align:center; font-size:0.78rem; opacity:0.6; margin-top:0.8rem; }

.teach3__res {
  padding:0.7rem 0.9rem; border-radius:12px; font-size:0.85rem;
  border:1px solid rgba(255,255,255,0.16); background:rgba(255,255,255,0.05);
  animation:res-land 3.6s ease-in-out infinite; animation-delay:var(--d,0s);
}
.teach3__res strong { display:block; color:#8ec5ff; font-size:0.72rem; letter-spacing:0.1em; text-transform:uppercase; }
.teach3__res em { font-style:normal; opacity:0.85; }
@keyframes res-land {
  0%,40%   { opacity:0.25; transform:translateX(-10px); }
  60%,100% { opacity:1;    transform:translateX(0); }
}
/* the honest one: it gets corrected, then it lands */
.teach3__res--fix { border-color:#ffb84d55; }
.teach3__fix {
  display:block; margin-top:0.3rem; font-size:0.7rem; font-weight:700; color:#ffcf87;
  animation:res-fix 3.6s ease-in-out infinite;
}
@keyframes res-fix { 0%,55% { opacity:1; } 70%,100% { opacity:0.35; } }

/* ================= 1 · the reel fills the room =================
   The card WIDTH is not set here. Four rules further down claim it and the
   winner is `.reel--desk .rcard--film`, which is both more specific and
   !important — changing this one moves nothing, which cost me a round trip. */
.reel { padding:1.5rem 0; }


/* ================= 8 · the film plays big ================= */
.reelopen__panel:has(.reelopen__video:not([hidden])) {
  width:min(92rem,96vw); aspect-ratio:16/9; height:auto; max-height:88vh;
}
.reelopen__panel:has(.reelopen__video:not([hidden])) .reelopen__body {
  position:absolute; inset:auto 0 0 0;
  background:linear-gradient(transparent, rgba(10,2,3,0.9));
  padding:2.4rem clamp(1rem,3vw,2rem) 1rem; pointer-events:none;
}
.reelopen__panel:has(.reelopen__video:not([hidden])) .reelopen__body h2 { font-size:clamp(1rem,2vw,1.4rem); }
.reelopen__video { z-index:1; }
.reelopen__close { z-index:3; }

/* ================= 7 · picking a pack dims the room behind it ================= */
.pack.has-pick .pack__wrap, .pack.has-pick .pack__pickhint { filter:blur(2px); opacity:0.5; transition:0.3s ease; }
.pack.has-pick .studio__rig { filter:blur(3px); }

/* ================= 3 · moving between rooms ================= */
.studio { --enter:0; }
.studio.is-entering .studio__rig::after {
  content:''; position:absolute; inset:0;
  background:linear-gradient(105deg, transparent 35%,
    color-mix(in srgb, var(--white) 22%, transparent) 50%, transparent 65%);
  animation:room-sweep 0.9s ease-out forwards;
}
@keyframes room-sweep { from { transform:translateX(-100%); } to { transform:translateX(100%); } }
.studio.is-entering > *:not(.studio__rig) { animation:room-in 0.7s cubic-bezier(0.2,0.9,0.3,1) both; }
@keyframes room-in { from { opacity:0; transform:translateY(22px) scale(0.985); } }

/* ================= 4 · the room, with information floating in it ================= */
.studio__dust { position:absolute; inset:0; overflow:hidden; }
.studio__dust i {
  position:absolute; display:block; border-radius:50%;
  background:color-mix(in srgb, var(--white) 70%, transparent);
  animation:dust-rise linear infinite;
}
.slide--light .studio__dust i, .pack .studio__dust i { background:color-mix(in srgb, var(--blue) 40%, transparent); }
@keyframes dust-rise {
  from { transform:translateY(18vh) scale(0.6); opacity:0; }
  20%,80% { opacity:0.55; }
  to   { transform:translateY(-22vh) scale(1); opacity:0; }
}
@media (prefers-reduced-motion:reduce) {
  .studio__dust, .teach3__pulse { display:none; }
  .teach3__feeds li, .teach3__res, .teach3__fix { animation:none; }
  .studio.is-entering > *:not(.studio__rig) { animation:none; }
}

/* The film cards are sized from the section's height, not an arbitrary width,
   so the reel fills roughly two thirds of the room instead of floating in it. */
.rcard--film {
  width:auto; height:clamp(13rem,46vh,25rem); aspect-ratio:16/9;
}
.reel { padding:0; }
.rcard--film .rcard__tag { font-size:0.86rem; padding:2.6rem 1rem 0.9rem; }
@media (max-width:640px) { .rcard--film { height:clamp(9rem,26vh,13rem); } }

/* The film panel is flagged from JS rather than matched with :has(), which was
   not binding here and left the panel collapsed to the size of its buttons. */
.reelopen.is-film .reelopen__panel {
  width:min(92rem,96vw); height:auto; aspect-ratio:16/9; max-height:86vh;
}
.reelopen.is-film .reelopen__body {
  position:absolute; inset:auto 0 0 0; pointer-events:none;
  background:linear-gradient(transparent, rgba(10,2,3,0.9));
  padding:2.6rem clamp(1rem,3vw,2rem) 1rem;
}
.reelopen.is-film .reelopen__body h2 { font-size:clamp(1rem,2vw,1.5rem); margin-bottom:0.2rem; }
.reelopen.is-film .reelopen__go { display:none; }

/* the film cards: width driven by the room's height, so they scale together */
.rcard--film { width:clamp(19rem,72vh,42rem) !important; height:auto !important; aspect-ratio:16/9; }
@media (max-width:640px) { .rcard--film { width:clamp(13rem,44vh,20rem) !important; } }

/* ================= ONE GEOMETRY FOR EVERY RING =================
   The Starter Pack is the reference: same radius, same tilt, same card size,
   same distance from the core, same foil sheen. Every other ring matches it,
   so a presenter sees the identical picture in every room. */
:root {
  --ring-rx:340; --ring-ry:58; --ring-min:0.34; --ring-h:24rem;
  --ring-card:clamp(6.4rem,10vw,8.2rem);
}
.orbit--pack, .orbit--sov, .orbit--how, .orbit--sdg, .orbit--wide {
  --orbit-rx:var(--ring-rx); --orbit-ry:var(--ring-ry);
  --orbit-min-scale:var(--ring-min); height:var(--ring-h);
}
.orbit--sov .orbit__node--card, .orbit--how .orbit__node--card,
.orbit--sdg .orbit__node--card, .orbit--wide .orbit__node--card,
.orbit--wide .orbit__node--logo, .orbit__node--card { width:var(--ring-card); }

.ocard { position:relative; overflow:hidden; border-radius:14px; }
.ocard::before {
  content:''; position:absolute; inset:-40% -120%; pointer-events:none; z-index:3;
  background:linear-gradient(74deg, transparent 42%, rgba(255,255,255,0.45) 50%, transparent 58%);
  animation:pack-shine 4.5s ease-in-out infinite;
}
.ocard__media { aspect-ratio:1; }
.ocard__body { padding:0.6rem 0.65rem 0.75rem; }
.ocard__body strong { font-size:0.8rem; }
.ocard__body em { font-size:0.66rem; line-height:1.35; }
.clogo--lg { width:100%; aspect-ratio:1; height:auto; }

@media (max-height:860px) {
  :root { --ring-ry:44; --ring-rx:290; --ring-h:19rem; --ring-card:clamp(5.4rem,8vw,7rem); }
}
@media (max-width:640px) {
  :root { --ring-rx:112; --ring-ry:38; --ring-h:16rem; --ring-card:5.2rem; }
}

.netball--teach { position:relative; left:auto; top:auto; transform:none; width:min(13rem,26vw); margin:0 auto; }
.teach3__brain { position:relative; }
.teach3__chip { position:absolute; bottom:-0.4rem; }

/* ================= THE ROOM, IN 3D ================= */
#deck { perspective:1400px; perspective-origin:50% 42%; }
.studio { transform-style:preserve-3d; }

.studio__ceil {
  left:0; right:0; top:0; height:34%; border-radius:0 0 50% 50% / 0 0 18% 18%;
  background:
    radial-gradient(120% 150% at 20% -10%, color-mix(in srgb, var(--white) 22%, transparent), transparent 46%),
    radial-gradient(120% 150% at 80% -10%, color-mix(in srgb, var(--white) 22%, transparent), transparent 46%);
  box-shadow:none; opacity:1;
}
.slide--light .studio__ceil, .pack .studio__ceil {
  background:
    radial-gradient(120% 150% at 20% -10%, color-mix(in srgb, var(--white) 96%, transparent), transparent 48%),
    radial-gradient(120% 150% at 80% -10%, color-mix(in srgb, var(--white) 96%, transparent), transparent 48%);
}
.studio__strip {
  top:1.5%; width:clamp(2.4rem,5vw,4.4rem); height:0.5rem; border-radius:50%;
  background:var(--white); opacity:0.9; transform:none;
  box-shadow:0 0 26px 10px color-mix(in srgb, var(--white) 55%, transparent),
             0 14px 70px 30px color-mix(in srgb, var(--white) 22%, transparent);
}
.studio__strip--l { left:21%; }
.studio__strip--r { right:21%; left:auto; }

.studio__floor {
  left:-30%; right:-30%; bottom:0; height:42%;
  transform:perspective(700px) rotateX(62deg); transform-origin:50% 100%;
  background:linear-gradient(color-mix(in srgb, var(--white) 12%, transparent), transparent 60%),
             repeating-linear-gradient(90deg, transparent 0 11.5%, color-mix(in srgb, var(--white) 9%, transparent) 11.5% 11.6%);
}
.slide--light .studio__floor, .pack .studio__floor {
  background:linear-gradient(color-mix(in srgb, var(--white) 92%, transparent), color-mix(in srgb, var(--white) 55%, transparent) 60%),
             repeating-linear-gradient(90deg, transparent 0 11.5%, color-mix(in srgb, var(--black) 6%, transparent) 11.5% 11.6%);
}
.studio__gloss {
  left:0; right:0; bottom:40%; height:14%; filter:blur(10px);
  background:radial-gradient(ellipse 70% 100% at 50% 100%, color-mix(in srgb, var(--white) 18%, transparent), transparent 72%);
}
.slide--light .studio__gloss, .pack .studio__gloss {
  background:radial-gradient(ellipse 70% 100% at 50% 100%, color-mix(in srgb, var(--white) 95%, transparent), transparent 72%);
}

/* ---- walking into the next room ---- */
.studio.is-entering > *:not(.studio__rig) { animation:room-walk 0.75s cubic-bezier(0.22,0.9,0.28,1) both; }
.studio.is-up.is-entering > *:not(.studio__rig) { animation-name:room-walk-back; }
@keyframes room-walk {
  from { opacity:0; transform:translate3d(16vw,0,-320px) rotateY(-9deg); }
  to   { opacity:1; transform:none; }
}
@keyframes room-walk-back {
  from { opacity:0; transform:translate3d(-16vw,0,-320px) rotateY(9deg); }
  to   { opacity:1; transform:none; }
}
.studio.is-entering .studio__rig { animation:room-rig 0.75s ease-out both; }
.studio.is-up.is-entering .studio__rig { animation-name:room-rig-back; }
@keyframes room-rig { from { opacity:0.25; transform:translateX(7vw); } to { opacity:1; transform:none; } }
@keyframes room-rig-back { from { opacity:0.25; transform:translateX(-7vw); } to { opacity:1; transform:none; } }

@media (prefers-reduced-motion:reduce) {
  .studio.is-entering > *, .studio.is-entering .studio__rig { animation:none; }
  .ocard::before { animation:none; }
}

/* ================= 1 · plain rooms =================
   The drawn studio didn't read as a room, so it goes. Flat colour, a soft
   light from above, nothing else. */
.studio__ceil, .studio__strip, .studio__floor, .studio__gloss, .studio__dust { display:none !important; }
.studio__light {
  position:absolute; left:50%; top:0; transform:translateX(-50%);
  width:min(110rem,140%); height:70%;
  background:radial-gradient(ellipse 45% 100% at 50% 0%,
    color-mix(in srgb, var(--white) 9%, transparent), transparent 70%);
}
.slide--light .studio__light, .pack .studio__light {
  background:radial-gradient(ellipse 45% 100% at 50% 0%,
    color-mix(in srgb, var(--blue) 6%, transparent), transparent 70%);
}
#deck { perspective:none; }

/* ================= 9 · the flash, without the slide ================= */
.studio.is-entering > *:not(.studio__rig),
.studio.is-up.is-entering > *:not(.studio__rig) { animation:none !important; }
.studio.is-entering .studio__rig,
.studio.is-up.is-entering .studio__rig { animation:none !important; }
.studio.is-entering .studio__rig::after {
  content:''; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(105deg, transparent 35%,
    color-mix(in srgb, var(--white) 26%, transparent) 50%, transparent 65%);
  animation:room-sweep 0.85s ease-out forwards;
}
.slide--light .studio.is-entering .studio__rig::after,
.pack.is-entering .studio__rig::after {
  background:linear-gradient(105deg, transparent 35%,
    color-mix(in srgb, var(--blue) 14%, transparent) 50%, transparent 65%);
}

/* ================= 2 · We Teach, It Learn ================= */
.teach3 { align-items:start; }
.teach3__core { display:flex; flex-direction:column; align-items:center; }
/* the step label belongs above its own column, not floating over the header */
.teach3__core .teach3__step { order:-1; margin-bottom:0.8rem; }
.teach3__brain {
  position:relative; width:var(--ring-ball,17rem); aspect-ratio:1;
  display:grid; place-items:center; margin:0 auto;
}
.netball--teach {
  position:absolute; inset:0; left:50%; top:50%;
  transform:translate(-50%,-50%); width:100%; margin:0;
}
/* AI CORE sits under the ball, not on it */
.teach3__chip { position:static; margin-top:0.4rem; }
.teach3__note { margin-top:0.5rem; }

/* ================= 3 · Sovereign ================= */
#sovereign { align-items:center; }
#sovereign .sectmore {
  position:static; margin:1.2rem auto 0; display:inline-flex; align-self:center;
}

/* ================= 4 · Our Commitment ================= */
#commitment { display:flex; flex-direction:column; align-items:center; }
#commitment .thanks { order:3; margin-top:1.2rem; text-align:center; }
#commitment .orbit--wide { order:2; }
#commitment .overtitle, #commitment .overtitle__sub { order:1; }
/* This section lays its children out by `order`, so anything without one
   goes to the front — which is how the partner names and the certification
   line ended up printed ABOVE the headline. They belong with the thank-you,
   at the end. */
#commitment .partnerchips { order:4; margin-top:0.9rem; }
#commitment .certline { order:5; margin-top:0.35rem; }

/* ================= 5,6,7 · every card is the pack's card ================= */
.orbit--wide .orbit__node--logo { width:var(--ring-card); }
.orbit--wide .clogo-wrap {
  position:relative; display:block; width:100%; aspect-ratio:3/4;
  border-radius:14px; overflow:hidden; background:var(--white);
  box-shadow:0 20px 44px -24px rgba(10,2,3,0.5);
}
.orbit--wide .clogo-wrap::before {
  content:''; position:absolute; inset:-40% -120%; pointer-events:none; z-index:3;
  background:linear-gradient(74deg, transparent 42%, rgba(255,255,255,0.45) 50%, transparent 58%);
  animation:pack-shine 4.5s ease-in-out infinite;
}
.orbit--wide .clogo {
  position:absolute; inset:0 0 30% 0; width:auto; height:auto;
  border-radius:0; background:none; box-shadow:none;
}
.orbit--wide .clogo img { inset:16%; }
.orbit--wide .clogo__name {
  position:absolute; inset:auto 0 0 0; opacity:1; padding:0.5rem 0.55rem 0.7rem;
  font-size:0.66rem; line-height:1.3; color:var(--black); text-align:center;
}
/* the plain text cards match too */
.ocard--plain { aspect-ratio:3/4; display:flex; flex-direction:column; justify-content:center; }
.ocard--letter .ocard__letter { width:1.9rem; height:1.9rem; font-size:0.85rem; margin:0 auto 0.5rem; }
.ocard--plain .ocard__body { text-align:center; }
.ocard--sdg .ocard__media { aspect-ratio:1; }

/* ================= 8 · the pack room, plain white ================= */
.pack { background:var(--white); }
.pack .studio__light { background:radial-gradient(ellipse 45% 100% at 50% 0%,
  color-mix(in srgb, var(--blue) 5%, transparent), transparent 70%); }

/* the teach ball is exactly the pack ball */
.teach3__brain { width:min(19rem,42vw); }
.teach3__chip { display:inline-block; margin:0.6rem auto 0; }

/* the pack still carried its own width — one source of truth for all of them */
.orbit__node--pack { width:var(--ring-card) !important; }

/* ---- the cards were shrunk when the rings were unified; back to full size,
        driven from one variable so every ring stays in step ---- */
:root { --ring-card:clamp(8.5rem,11vw,12rem); --ring-rx:390; --ring-ry:64; --ring-h:27rem; }
@media (max-height:860px) { :root { --ring-card:clamp(7rem,9vw,9.5rem); --ring-rx:330; --ring-ry:50; --ring-h:22rem; } }
@media (max-width:640px)  { :root { --ring-card:6.6rem; --ring-rx:118; --ring-ry:40; --ring-h:17rem; } }

/* ---- the floating orbs come back; the rest of the room stays plain ---- */
.studio__dust { display:block !important; position:absolute; inset:0; overflow:hidden; }
.studio__dust i {
  position:absolute; display:block; border-radius:50%;
  background:radial-gradient(circle at 35% 35%,
    color-mix(in srgb, var(--white) 90%, transparent),
    color-mix(in srgb, var(--blue) 55%, transparent) 60%, transparent 72%);
  box-shadow:0 0 12px 3px color-mix(in srgb, var(--blue) 30%, transparent);
  animation:dust-rise linear infinite;
}
.slide--light .studio__dust i, .pack .studio__dust i {
  background:radial-gradient(circle at 35% 35%,
    color-mix(in srgb, var(--white) 95%, transparent),
    color-mix(in srgb, var(--blue) 40%, transparent) 60%, transparent 74%);
  box-shadow:0 0 14px 4px color-mix(in srgb, var(--blue) 16%, transparent);
}
@media (prefers-reduced-motion:reduce) { .studio__dust { display:none !important; } }

/* ================= 2 · every card the same size, text clipped ================
   A long role or detail used to stretch its card taller than its neighbours.
   Fixed proportions, and anything that doesn't fit ends in an ellipsis. */
.orbit__node--card .ocard, .orbit--wide .clogo-wrap {
  height:100%; display:flex; flex-direction:column;
}
.orbit__node--card, .orbit--wide .orbit__node--logo { aspect-ratio:3/4; }
.ocard__media { flex:0 0 58%; aspect-ratio:auto; }
.ocard__body { flex:1 1 auto; min-height:0; overflow:hidden; }
.ocard__body strong {
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; line-height:1.25;
}
.ocard__body em {
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical;
  overflow:hidden;
}
.ocard--plain { aspect-ratio:auto; height:100%; }

/* ================= 3 · logos sit properly in their card ================= */
.orbit--wide .clogo { position:relative; inset:auto; flex:0 0 62%; width:100%; }
.orbit--wide .clogo img {
  position:absolute; inset:14%; width:auto; height:auto;
  max-width:none; max-height:none; object-fit:contain;
}
.orbit--wide .clogo__name {
  position:relative; inset:auto; flex:1 1 auto; min-height:0; overflow:hidden;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  padding:0.35rem 0.5rem 0.6rem;
}

/* ================= 1 · mobile keeps its words ================= */
@media (max-width:640px) {
  .ocard__body { padding:0.45rem 0.5rem 0.55rem; }
  .ocard__body strong { font-size:0.72rem; -webkit-line-clamp:2; }
  .ocard__body em { font-size:0.6rem; line-height:1.3; -webkit-line-clamp:2; }
  .orbit--wide .clogo__name { font-size:0.58rem; -webkit-line-clamp:2; }
  /* the ring nodes were scaled so far down at the back that text vanished */
  :root { --ring-min:0.5; }
  .rcard__tag { font-size:0.66rem; padding:1.6rem 0.6rem 0.5rem; }
  .certdisc__in { width:82%; }
}

/* An absolutely-positioned <img> with width:auto keeps its intrinsic size —
   insets alone will not shrink it, so the logos were being cropped. Giving it
   a definite box lets object-fit letterbox them properly. */
.clogo img, .clogo--lg img, .orbit--wide .clogo img {
  /* inset:auto must come BEFORE left/top — as a shorthand it resets them */
  position:absolute; inset:auto; left:50%; top:50%; transform:translate(-50%,-50%);
  width:74%; height:74%; max-width:74%; max-height:74%;
  object-fit:contain; object-position:center;
}

/* the three stages stack on a phone — as a row they forced the page wider */
@media (max-width:820px) {
  .teach3 { grid-template-columns:1fr; gap:1.2rem; width:min(30rem,92vw); }
  .teach3__col { width:100%; }
  .teach3__brain { width:min(11rem,44vw); }
  .teach3__feeds, .teach3__results { grid-template-columns:1fr 1fr; display:grid; }
  .teach3__feeds li, .teach3__res { font-size:0.74rem; padding:0.45rem 0.6rem; }
  .teach3__res em { font-size:0.68rem; }
  /* same on a phone: the pin carries the nav clearance, the section carries
     none, or the film starts partway down and leaves a line */
  .slide--teach { padding-top:0; }
}


/* ================= hero film bands =================
   ONE on a laptop, four on a phone, five on a standing screen. Where there is
   more than one band they run opposite ways and carry different slices of the
   list, so the same film is never on screen twice at once. */
.reel--mob { display:none; }
.reel--desk .reel__track { animation:reel-run 52s linear infinite; }
.reel--desk .reel__track--b { animation-name:reel-run-back; animation-duration:61s; }
.reel--desk .rcard--film { width:clamp(12rem,30vh,20rem) !important; }

@keyframes reel-run-back { from { transform:translateX(0); } to { transform:translateX(-50%); } }

@media (max-width:820px) {
  .reel--desk { display:none; }
  .reel--mob  { display:block; }
  .reel--mob .reel__track { animation:reel-run 24s linear infinite; }
  .reel--mob .reel__track--b { animation-name:reel-run-back; animation-duration:29s; }
  .reel--mob .reel__track + .reel__track { margin-top:0.55rem; }
  .reel--mob .rcard--film { width:clamp(8rem,34vw,12rem) !important; }
  .reel--mob .rcard__tag { font-size:0.58rem; padding:1.2rem 0.5rem 0.4rem; }
}

/* ONE ROW, AND BIG — Taufik's mark-up, 12 Aug 2026.
   Two rows of ~288px cards read as a contact sheet: a wall of small pictures,
   none of which anybody actually looks at, and the titles under them were at
   the size you skim rather than the size you read. Both rows were crossed out.

   One row, sized from the height of the room: a card lands near 640px wide on
   a laptop, a bit over two across, each one plainly a film somebody could go
   and watch. The number of bands is decided in index.ejs — CSS cannot remove
   a row any more than it can add one — and this is only the size of them.

   Height is what is declared and the width follows, because 16:9 and a row
   that has to fit UNDER a headline means the height is the constrained side.
   Both properties need !important: `.rcard--film` further up pins width and
   height with !important of its own, and a plain rule here loses to it. */
/* THE SIZE ITSELF MOVED DOWN THE FILE, and this rule is gone rather than left
   to be overridden. Dr KC's mark-up put the card at about 18.6% of the screen
   WIDTH — five and a half across — where this made it 640px and two and a bit.
   Leaving a superseded size here would have made five rules that claim this
   card's width, and the comment forty lines up already warns about four. */

/* ---- the film panel: no black bars, and a way into the chapter ----
   max-height was capping the height while the width stayed fixed, so the box
   stopped being 16:9 and the video letterboxed inside it. Constraining the
   width by the available height instead keeps the box exactly 16:9. */
.reelopen.is-film .reelopen__panel {
  width:min(92rem, 96vw, 150vh);
  aspect-ratio:16/9; height:auto; max-height:none;
  background:#000;
}
.reelopen__video, .reelopen__video video, .reelopen__video iframe {
  position:absolute; inset:0; width:100%; height:100%; display:block; border:0;
}
.reelopen__video video { object-fit:contain; background:#000; }

/* the chapter button rides the bottom-right of the player */
.reelopen.is-film .reelopen__go {
  display:inline-block; pointer-events:auto;
  position:absolute; right:clamp(1rem,3vw,2rem); bottom:clamp(1rem,3vw,1.6rem);
  background:var(--blue); color:var(--white); border:0; border-radius:999px;
  padding:0.6rem 1.3rem; font-weight:700; font-size:0.88rem; text-decoration:none;
  box-shadow:0 10px 26px -8px rgba(0,0,0,0.6);
  transition:transform 0.2s ease, background 0.2s ease;
}
.reelopen.is-film .reelopen__go:hover { background:#0a67d6; transform:translateY(-2px); }
.reelopen.is-film .reelopen__body {
  padding:2.6rem clamp(1rem,3vw,2rem) 1rem;
  padding-right:clamp(10rem,22vw,16rem);      /* clear of the button */
}

/* The player had z-index:1 and the caption none, so the chapter button was
   painting underneath the video. It also has to clear the player's own
   controls along the bottom — so it sits top-left, opposite the close. */
.reelopen.is-film .reelopen__body { z-index:4; }
.reelopen.is-film .reelopen__go {
  z-index:5; top:clamp(0.9rem,2.5vw,1.4rem); left:clamp(0.9rem,2.5vw,1.4rem);
  right:auto; bottom:auto;
  background:color-mix(in srgb, var(--black) 55%, transparent);
  backdrop-filter:blur(8px);
  border:1.5px solid rgba(255,255,255,0.5);
}
.reelopen.is-film .reelopen__go:hover { background:var(--blue); border-color:var(--blue); }
/* the title strip drops to the bottom-left, clear of the controls */
.reelopen.is-film .reelopen__body {
  inset:auto 0 0 0; padding:2.4rem clamp(1rem,3vw,2rem) 3.4rem;
  padding-right:clamp(6rem,14vw,10rem);
}

/* ================= result states =================
   Each result carries its own colour, so the story reads at a glance:
   red found a problem, orange needs a person, yellow is still learning,
   green is closed out. */
.teach3__res {
  --st:#8ec5ff;                                  /* neutral, before JS runs */
  border-color:color-mix(in srgb, var(--st) 45%, transparent);
  background:color-mix(in srgb, var(--st) 9%, transparent);
  transition:border-color 0.45s ease, background 0.45s ease, box-shadow 0.45s ease;
}
.teach3__res.is-detected { --st:#ff5a5a; }
.teach3__res.is-alert    { --st:#ffa53d; }
.teach3__res.is-learning { --st:#ffd54a; }
.teach3__res.is-resolved { --st:#3ddc97; }

.teach3__res strong { color:var(--st); }
.teach3__state {
  display:block; margin-top:0.25rem; font-size:0.68rem; font-weight:700;
  letter-spacing:0.06em; text-transform:uppercase; color:var(--st); opacity:0.85;
}
/* the flash as a result changes */
.teach3__res.is-turning {
  animation:res-flip 0.55s ease-out;
  box-shadow:0 0 0 1px color-mix(in srgb, var(--st) 55%, transparent),
             0 0 22px -2px color-mix(in srgb, var(--st) 65%, transparent);
}
@keyframes res-flip {
  0%   { opacity:0.25; transform:translateX(-8px); }
  60%  { opacity:1;    transform:translateX(2px); }
  100% { opacity:1;    transform:none; }
}
/* the old fixed states are gone */
.teach3__res--ok, .teach3__res--fix, .teach3__fix { all:unset; }
.teach3__res { animation:none; }

/* ================= the knowledge graph =================
   Eight chapters wired together, with every published Story Card hanging off
   the chapters it touches. Colour carries the meaning — see .nglegend. */
.netgraph { position:relative; width:100%; height:100%; }
.netgraph__svg { width:100%; height:100%; display:block; overflow:visible; }
.netgraph__links line { stroke:#8ec5ff; stroke-width:0.5; transition:stroke 0.3s ease; }
.netgraph__links line.is-charged {
  stroke:#fff; stroke-width:1.3;
  filter:drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px #8ec5ff);
}
.netgraph__halo { animation:nb-breathe 5s ease-in-out infinite; transform-origin:center; }
.netgraph__links line.is-spoke { stroke:#0078FF; stroke-width:0.35; }

/* ---- the ball: a shell to sit inside, a core to sit at the centre ----
   A scatter of dots reads as a scatter. The same dots with a pulsing middle
   and a faint outer shell read as one object turning.
   A page that doesn't ship the graph data gets neither — an empty shell
   pinging away with nothing inside it just looks broken. */
.netgraph:not(.is-built) .netgraph__shell,
.netgraph:not(.is-built) .netgraph__halo,
.netgraph:not(.is-built) .netgraph__ping { display:none; }
.netgraph__shell {
  fill:none; stroke:#8ec5ff; stroke-width:0.35; stroke-dasharray:2 5; opacity:0.22;
  transform-box:fill-box; transform-origin:center;
  animation:ng-shell 26s linear infinite;
}
@keyframes ng-shell { to { transform:rotate(360deg); } }
.netgraph__ping {
  fill:none; stroke:#8ec5ff; stroke-width:0.8; opacity:0;
  transform-box:fill-box; transform-origin:center;
  animation:ng-ping 3.4s ease-out infinite;
}
.netgraph__ping--2 { animation-delay:1.7s; }
@keyframes ng-ping {
  0%   { transform:scale(0.35); opacity:0.55; }
  70%  { opacity:0.12; }
  100% { transform:scale(2.4);  opacity:0; }
}
/* ---- every dot is an orb, not a disc ----
   No outline anywhere: a wide coloured glow that fades to nothing, with a hot
   white middle. A flat circle that brightens reads as a light switching on; a
   glow that swells reads as the thing itself charging, which is what a signal
   arriving should look like.

   `color` carries the chapter hue, so one rule per state lights every chapter
   in its own colour instead of one rule per state per chapter. */
.ngnode {
  color:#8ec5ff;
  transform-box:fill-box; transform-origin:center;
}
/* the glow has no edge to aim at, so an invisible circle carries the click */
.ngdot { fill:none; pointer-events:all; }
.ngdot__glow {
  fill:url(#ngG-story); pointer-events:none;
  opacity:0.95; transition:opacity 0.35s ease;
}
.ngdot__hot {
  fill:url(#ngG-hot); pointer-events:none;
  opacity:0.45; transition:opacity 0.35s ease;
}

.ngnode.is-active { color:#3ddc97; }
.ngnode.is-active .ngdot__glow { fill:url(#ngG-nature); }
.ngnode.is-past   { color:#8ec5ff; }
.ngnode.is-active .ngdot__hot { opacity:0.6; }
.ngnode:not(.is-big) .ngdot__hot { opacity:0.3; }

/* the core: a white heart in a blue glow, and it breathes */
.ngnode.is-core { color:#2f9dff; animation:ng-core 2.6s ease-in-out infinite; }
.ngnode.is-core .ngdot__glow { fill:url(#ngG-core); }
.ngnode.is-core .ngdot__hot { opacity:0.95; }
@keyframes ng-core {
  0%,100% { transform:scale(1);    opacity:0.94; }
  50%     { transform:scale(1.14); opacity:1; }
}

/* a charge arriving: the orb swells and its middle goes white */
.ngnode.is-lit { animation:ng-hit 0.7s cubic-bezier(0.2,0.9,0.3,1.2); }
.ngnode.is-lit .ngdot__hot { opacity:1; transition:opacity 0.1s ease; }
.ngnode.is-lit .ngdot__glow { opacity:1; transition:opacity 0.1s ease; }
@keyframes ng-hit {
  0%   { transform:scale(1); }
  26%  { transform:scale(1.55); }
  100% { transform:scale(1); }
}

/* only the live copy invites a click */
.netgraph--live .ngnode { cursor:pointer; }
.netgraph--live .ngnode:hover .ngdot__hot,
.netgraph--live .ngnode:focus-visible .ngdot__hot { opacity:1; }
.netgraph--live .ngnode:focus-visible { outline:none; }

/* The label. It sits over a near-black panel, so it must set its own colour —
   inherited text colour here is the page's, which is nearly black too. */
.netgraph__tip {
  position:absolute; transform:translate(-50%,-100%); pointer-events:none; z-index:5;
  background:color-mix(in srgb, #06111f 94%, transparent);
  border:1px solid rgba(140,197,255,0.32); border-radius:10px;
  padding:0.5rem 0.75rem; max-width:15rem; text-align:center;
  color:var(--white);
  box-shadow:0 10px 26px -10px rgba(0,0,0,0.8), 0 0 24px -8px rgba(0,120,255,0.6);
}
.netgraph__tip.is-below { transform:translate(-50%,0); }
.netgraph__tip[hidden] { display:none; }
.netgraph__tip strong { display:block; font-size:0.78rem; line-height:1.3; color:var(--white); }
.netgraph__tip em {
  display:block; font-style:normal; font-size:0.66rem; line-height:1.35;
  opacity:0.7; margin-top:0.15rem; color:var(--white);
}
/* which chapters this thing belongs to, in each chapter's own colour */
.ngtip__tags {
  display:flex; flex-wrap:wrap; justify-content:center; gap:0.25rem; margin-top:0.4rem;
}
.ngtip__tags b {
  font-size:0.58rem; font-weight:700; letter-spacing:0.03em; padding:0.1rem 0.4rem;
  border-radius:999px; color:var(--c);
  background:color-mix(in srgb, var(--c) 18%, transparent);
  border:1px solid color-mix(in srgb, var(--c) 45%, transparent);
}
.ngtip__go {
  display:block; margin-top:0.35rem; font-size:0.56rem; letter-spacing:0.08em;
  text-transform:uppercase; color:#8ec5ff; opacity:0.8;
}

/* what the colours mean */
.nglegend {
  list-style:none; display:flex; justify-content:center; flex-wrap:wrap;
  gap:0.4rem 0.9rem; margin-top:0.7rem; font-size:0.64rem; letter-spacing:0.04em;
}
.nglegend li { display:flex; align-items:center; gap:0.35rem; opacity:0.75; }
.nglegend span { width:0.5rem; height:0.5rem; border-radius:50%; display:block; }
.nglegend .is-chapter span { background:#fff; }
.nglegend .is-active  span { background:#3ddc97; box-shadow:0 0 6px #3ddc97; }
.nglegend .is-past    span { background:#8ec5ff; }
@media (prefers-reduced-motion:reduce) { .netgraph__halo { animation:none; } }

/* the cost each trade removes, stated under its name */
.teach3__instead {
  display:block; font-size:0.66rem; line-height:1.35; opacity:0.5;
  margin:0.1rem 0 0.35rem;
}
/* the card's own parts now set their own sizes — see .teach3__layer below */
.teach3__res em { font-size:0.78rem; }

/* ================= the Golden Circle results =================
   Colour follows the chapter, not the state — learn the palette once and it
   reads the same everywhere on the site. */
.teach3__res {
  --neu:#8ec5ff;
  border-color:color-mix(in srgb, var(--neu) 48%, transparent);
  background:color-mix(in srgb, var(--neu) 9%, transparent);
  transition:border-color 0.5s ease, background 0.5s ease, box-shadow 0.5s ease;
}
.teach3__res .teach3__layer {
  display:block; font-size:0.6rem; font-weight:700; letter-spacing:0.14em;
  text-transform:uppercase; line-height:1.35;
  color:color-mix(in srgb, var(--neu) 70%, var(--white)); opacity:0.7;
}
/* the promise itself — fixed, and the biggest thing in the card */
.teach3__promise {
  display:block; font-size:1rem; font-weight:800; line-height:1.25;
  color:var(--white); margin-top:0.2rem;
}
.teach3__pline {
  display:block; font-size:0.7rem; line-height:1.4; opacity:0.62;
  margin:0.2rem 0 0.45rem;
}
/* and the chapter currently proving it */
.teach3__proof {
  display:flex; align-items:center; gap:0.45rem; flex-wrap:wrap;
  padding-top:0.45rem; border-top:1px solid color-mix(in srgb, var(--neu) 26%, transparent);
}
.teach3__neu {
  font-size:0.82rem; font-weight:700; color:var(--neu);
}
.teach3__trade {
  display:inline-block; margin:0; padding:0.12rem 0.5rem;
  border-radius:999px; font-size:0.66rem; font-weight:700; letter-spacing:0.06em;
  text-transform:uppercase;
  background:color-mix(in srgb, var(--neu) 20%, transparent);
  color:color-mix(in srgb, var(--neu) 80%, var(--white));
}
.teach3__res em { display:block; font-style:normal; font-size:0.74rem; line-height:1.45; opacity:0.78; margin-top:0.3rem; }
.teach3__res.is-turning {
  animation:res-flip 0.6s ease-out;
  box-shadow:0 0 0 1px color-mix(in srgb, var(--neu) 60%, transparent),
             0 0 26px -2px color-mix(in srgb, var(--neu) 65%, transparent);
}

/* ---- the lightning between the three columns ----
   Drawn in a single overlay that spans the whole row, so a bolt can start on
   a signal, cross into the ball, and leave for a result without any clipping. */
.teach3__bolts {
  position:absolute; inset:0; width:100%; height:100%;
  pointer-events:none; z-index:6; overflow:visible;
}
.bolt {
  fill:none; stroke:#8ec5ff; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round;
  filter:drop-shadow(0 0 4px currentColor);
  opacity:1; transition:opacity 0.3s ease;
}
.bolt--glow { stroke-width:6; opacity:0.22; filter:blur(3px); }
.bolt.is-gone { opacity:0; }
.bolt__head { filter:drop-shadow(0 0 6px #fff) drop-shadow(0 0 14px currentColor); }
@media (prefers-reduced-motion:reduce) { .teach3__bolts { display:none; } }

/* ---- the chain: a signal fires, the core takes it, a result lands ---- */
.teach3__feeds li.is-sending {
  border-color:#fff;
  box-shadow:0 0 0 1px rgba(255,255,255,0.5), 0 0 20px -2px #8ec5ff;
  animation:feed-fire 0.7s ease-out;
}
@keyframes feed-fire {
  0%   { transform:translateX(0); }
  35%  { transform:translateX(20px); }
  100% { transform:translateX(0); }
}
.netgraph.is-charging .netgraph__halo { opacity:1; transform:scale(1.16); }
.netgraph.is-charging .netgraph__links line { stroke:#fff; opacity:0.8 !important; }
.netgraph__halo, .netgraph__links line { transition:opacity 0.3s ease, transform 0.3s ease, stroke 0.3s ease; }

/* the graph's own nodes take the chapter palette too */
/* `color` is what the dome's solid dots use; the glow picks the matching
   gradient. Both come off the same class, so they can never disagree. */
.ngnode.is-neumotion  { color:#2f8fff; } .ngnode.is-neumotion  .ngdot__glow { fill:url(#ngG-motion); }
.ngnode.is-neunature  { color:#3ddc97; } .ngnode.is-neunature  .ngdot__glow { fill:url(#ngG-nature); }
.ngnode.is-neucity    { color:#ffa53d; } .ngnode.is-neucity    .ngdot__glow { fill:url(#ngG-city); }
.ngnode.is-neufactory { color:#b98cff; } .ngnode.is-neufactory .ngdot__glow { fill:url(#ngG-factory); }
.ngnode.is-neuslm     { color:#38d9d3; } .ngnode.is-neuslm     .ngdot__glow { fill:url(#ngG-slm); }
.ngnode.is-neutalents { color:#ff7ac8; } .ngnode.is-neutalents .ngdot__glow { fill:url(#ngG-talents); }
.ngnode.is-neustudio  { color:#ffd54a; } .ngnode.is-neustudio  .ngdot__glow { fill:url(#ngG-studio); }
.ngnode.is-neustory   { color:#8ec5ff; } .ngnode.is-neustory   .ngdot__glow { fill:url(#ngG-story); }

/* ================= the graph, opened =================
   The copy in the section is a doorway — the dots there are far too small to
   hit. Pressing it opens the same graph at full size, with zoom. */
.netgraph--door { cursor:zoom-in; transition:transform 0.3s ease, filter 0.3s ease; }
.netgraph--door:hover, .netgraph--door:focus-visible { transform:scale(1.04); filter:brightness(1.15); }
.netgraph--door:focus-visible { outline:2px solid #8ec5ff; outline-offset:6px; border-radius:50%; }

.gstage { position:fixed; inset:0; z-index:180; opacity:0; transition:opacity 0.26s ease; }
.gstage.is-open { opacity:1; }
.gstage[hidden] { display:none; }
.gstage__scrim {
  position:absolute; inset:0;
  background:color-mix(in srgb, var(--black) 78%, transparent); backdrop-filter:blur(10px);
}
.gstage__panel {
  position:absolute; inset:clamp(0.6rem,3vw,2.4rem);
  display:flex; flex-direction:column; border-radius:24px; overflow:hidden;
  background:color-mix(in srgb, #0b1b2e 92%, transparent);
  border:1px solid rgba(255,255,255,0.14);
  box-shadow:0 50px 100px -30px rgba(0,0,0,0.8);
  transform:scale(0.96); transition:transform 0.32s cubic-bezier(0.2,0.9,0.3,1.2);
}
.gstage.is-open .gstage__panel { transform:none; }
.gstage__bar {
  position:relative;
  display:flex; align-items:center; gap:1rem; flex-wrap:wrap;
  padding:0.8rem clamp(0.9rem,2.4vw,1.6rem);
  border-bottom:1px solid rgba(255,255,255,0.12); color:var(--white);
}
/* the line belongs in the middle of the bar, not tucked in the corner */
.gstage__title {
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  margin:0; white-space:nowrap; pointer-events:none;
}
.gstage__bar .nglegend { margin:0; margin-right:auto; }

/* ---- the title, shiny and a little bit digital ----
   One gradient sweeping across the letters, and two chromatic ghosts that
   twitch every few seconds — enough to read as a machine, not a poster. */
.gtitle {
  position:relative; display:inline-block;
  font-weight:800; font-size:clamp(0.78rem,1.5vw,1.02rem);
  letter-spacing:0.22em; text-transform:uppercase;
  background:linear-gradient(100deg,
    #6fb2ff 0%, #ffffff 16%, #cfe6ff 26%, #6fb2ff 40%, #6fb2ff 100%);
  background-size:260% 100%;
  -webkit-background-clip:text; background-clip:text; color:transparent;
  animation:gtitle-shine 6s linear infinite;
}
@keyframes gtitle-shine { to { background-position:-160% 0; } }
.gtitle::before, .gtitle::after {
  content:attr(data-text); position:absolute; left:0; top:0;
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  pointer-events:none;
}
.gtitle::before { color:#38d9d3; animation:gtitle-glitch-a 5.5s steps(1) infinite; }
.gtitle::after  { color:#ff7ac8; animation:gtitle-glitch-b 5.5s steps(1) infinite; }
@keyframes gtitle-glitch-a {
  0%,92%,100% { opacity:0; transform:none; clip-path:inset(0 0 100% 0); }
  93%         { opacity:0.55; transform:translate(-1.5px,-1px); clip-path:inset(0 0 58% 0); }
  95%         { opacity:0.4;  transform:translate(1.5px,1px);   clip-path:inset(46% 0 24% 0); }
  97%         { opacity:0;    clip-path:inset(0 0 100% 0); }
}
@keyframes gtitle-glitch-b {
  0%,92%,100% { opacity:0; transform:none; clip-path:inset(100% 0 0 0); }
  94%         { opacity:0.5; transform:translate(2px,1px);  clip-path:inset(52% 0 0 0); }
  96%         { opacity:0.35; transform:translate(-2px,0);  clip-path:inset(22% 0 44% 0); }
  98%         { opacity:0;   clip-path:inset(100% 0 0 0); }
}
@media (max-width:640px) {
  .gstage__title { position:static; transform:none; order:-1; width:100%; text-align:center; }
  .gtitle { letter-spacing:0.12em; }
}
@media (prefers-reduced-motion:reduce) {
  .gtitle { animation:none; }
  .gtitle::before, .gtitle::after { display:none; }
}
.gstage__zoom { display:flex; align-items:center; gap:0.4rem; }
.gstage__zoom button {
  width:1.9rem; height:1.9rem; border-radius:50%; cursor:pointer;
  border:1px solid rgba(255,255,255,0.3); background:none; color:var(--white);
  font-size:1rem; line-height:1;
}
.gstage__zoom button:hover { background:var(--blue); border-color:var(--blue); }
.gstage__zoom output { font-size:0.78rem; opacity:0.7; min-width:3rem; text-align:center; }
.gstage__close {
  width:2.2rem; height:2.2rem; border-radius:50%; cursor:pointer; font-size:1.2rem;
  border:0; background:rgba(255,255,255,0.12); color:var(--white);
}
.gstage__close:hover { background:var(--blue); }

/* ---- ask it out loud ----
   A pill rather than a bare icon, because a lone microphone glyph does not
   tell anybody what it wants them to SAY, and this one needs a set phrase. */
/* `hidden` is only a UA `display: none`, so the display below would have beaten
   it — and the microphone would have sat there looking pressable on every
   browser that cannot do speech at all, which is the one case the attribute
   exists to cover. */
.gmic[hidden] { display:none; }
.gmic {
  display:flex; align-items:center; gap:0.42rem; cursor:pointer;
  font:800 0.72rem 'Inter',sans-serif; letter-spacing:0.03em;
  padding:0.36rem 0.85rem 0.36rem 0.6rem; border-radius:999px;
  color:var(--white); border:1.5px solid rgba(255,255,255,0.28);
  background:rgba(255,255,255,0.08);
  transition:background 0.18s ease, border-color 0.18s ease, transform 0.12s ease;
}
.gmic:hover { background:var(--blue); border-color:var(--blue); transform:translateY(-1px); }
.gmic__ico { width:1.05rem; height:1.05rem; flex:none; }
.gmic__say { white-space:nowrap; }

/* live: red, and breathing, so there is never a doubt the room is being heard */
.gmic.is-live {
  background:#e0245e; border-color:#ff5a86; color:#fff;
  animation:gmic-live 1.5s ease-in-out infinite;
}
@keyframes gmic-live {
  0%, 100% { box-shadow:0 0 0 0 rgba(224,36,94,0.55); }
  70%      { box-shadow:0 0 0 0.5rem rgba(224,36,94,0); }
}
@media (prefers-reduced-motion: reduce) { .gmic.is-live { animation:none; } }

/* what it heard, and what it did about it */
.gvoice {
  margin:0.3rem clamp(0.9rem,2.4vw,1.6rem) 0; padding:0.5rem 0.85rem;
  border-radius:12px; font-size:0.8rem; line-height:1.45; text-align:center;
  border:1px solid rgba(255,255,255,0.14); background:rgba(255,255,255,0.05);
  color:rgba(255,255,255,0.92);
}
.gvoice span { opacity:0.62; }
.gvoice.is-live { border-color:rgba(255,90,134,0.5); background:rgba(224,36,94,0.14); }
.gvoice.is-ok   { border-color:rgba(120,220,170,0.45); background:rgba(60,200,140,0.12); }
.gvoice.is-miss { border-color:rgba(255,200,120,0.45); background:rgba(255,170,60,0.12); }
/* The ball must sit dead centre with room to zoom into. Sizing it from the
   HEIGHT is what does it: height is the scarce dimension on a laptop, and a
   square sized off the width overflowed the panel and pushed the graph down
   and out of the frame. */
/* ---- the chapter picker ---- */
.gpick {
  display:flex; flex-wrap:wrap; gap:0.4rem; justify-content:center;
  padding:0.7rem clamp(0.9rem,2.4vw,1.6rem) 0.2rem;
}
.gpick__btn, .gpick__all {
  --c:#8ec5ff;
  font:800 0.72rem 'Inter',sans-serif; letter-spacing:0.03em; cursor:pointer;
  padding:0.35rem 0.8rem; border-radius:999px; color:var(--white);
  border:1.5px solid color-mix(in srgb, var(--c) 55%, transparent);
  background:color-mix(in srgb, var(--c) 14%, transparent);
  transition:background 0.18s ease, border-color 0.18s ease, transform 0.12s ease, opacity 0.18s ease;
}
.gpick__btn::before {
  content:''; display:inline-block; width:0.5rem; height:0.5rem; border-radius:50%;
  background:var(--c); margin-right:0.4rem; vertical-align:middle;
  box-shadow:0 0 8px var(--c);
}
.gpick__btn:hover, .gpick__all:hover { transform:translateY(-1px); border-color:var(--c); }
.gpick__btn.is-on {
  background:var(--c); border-color:var(--c); color:#08111d;
  box-shadow:0 0 18px -4px var(--c);
}
.gpick__btn.is-on::before { background:#08111d; box-shadow:none; }
/* once something is picked, the unpicked buttons step back too */
.gpick.has-pick .gpick__btn:not(.is-on) { opacity:0.42; }
.gpick__all { --c:#ffffff; opacity:0.55; }
.gpick.has-pick .gpick__all { opacity:1; }

/* a dot outside the picked chapters: grey, quiet, and not clickable */
.ngnode.is-off { opacity:0.3; }
.ngnode.is-off .ngdot { pointer-events:none; }
.ngnode.is-off .ngdot__glow { fill:url(#ngG-mute); }
.ngnode.is-off .ngdot__hot { opacity:0; }
.netgraph__links line.is-off { stroke:#7c8ba1; opacity:0.05; }

.gstage__hint {
  text-align:center; font-size:0.7rem; letter-spacing:0.04em; color:var(--white);
  opacity:0.45; padding:0 1rem 0.8rem;
}
.gstage__view {
  flex:1; min-height:0; overflow:hidden; cursor:grab; touch-action:none;
  display:flex; align-items:center; justify-content:center;
}
.gstage__view.is-turning { cursor:grabbing; }
.netgraph--stage {
  height:94%; width:auto; max-width:94%; aspect-ratio:1; flex:none;
  transition:transform 0.22s ease; transform-origin:center;
}
@media (prefers-reduced-motion:reduce) {
  .gstage, .gstage__panel, .netgraph--stage { transition:none; }
}

/* =========================================================================
   THE BITS BACKSTAGE OWNS
   Every rule here styles something that comes out of the database rather than
   out of a template — a kicker, a row of chips, a certification line. They
   are collected together because they share one property: if a person clears
   the field in Backstage, the element is not rendered at all, so none of them
   may reserve space of their own.
   ========================================================================= */

/* the small line above the hero headline */
.herokick {
  position:relative; z-index:2; color:var(--white); opacity:0.86;
  font-weight:700; letter-spacing:0.16em; text-transform:uppercase;
  font-size:clamp(0.66rem,1.4vw,0.8rem); margin-bottom:0.7rem;
  text-shadow:0 2px 14px rgba(0,0,0,0.55);
}

/* the domain labels under "We Teach, It Learn", and the partners under the
   thank-you line — same chip, two colour schemes */
.domainchips, .partnerchips {
  list-style:none; display:flex; flex-wrap:wrap; justify-content:center;
  gap:0.4rem; margin:0.8rem 0 0; padding:0;
}
.domainchips li, .partnerchips li {
  font-size:0.72rem; font-weight:700; letter-spacing:0.06em;
  border-radius:999px; padding:0.3rem 0.8rem; white-space:nowrap;
}
.domainchips li { color:var(--white); background:rgba(255,255,255,0.12); border:1px solid rgba(255,255,255,0.24); }
.partnerchips { position:relative; z-index:4; }
.partnerchips li { color:var(--blue-deep); background:var(--blue-pale); border:1px solid color-mix(in srgb, var(--blue) 22%, transparent); }

.certline {
  position:relative; z-index:4; text-align:center; margin-top:0.7rem;
  font-size:0.74rem; letter-spacing:0.04em; opacity:0.62;
}

/* the closing note under the sovereign ring */
.sovcompact {
  position:relative; z-index:4; text-align:center;
  max-width:44rem; margin:1.1rem auto 0; font-size:0.86rem; line-height:1.55;
  color:var(--white); opacity:0.78;
}
.sovcompact b {
  display:block; font-size:0.7rem; letter-spacing:0.14em; text-transform:uppercase;
  opacity:0.75; margin-bottom:0.25rem;
}

/* the signature line above the footer's base row */
.pgfoot__sig {
  text-align:center; padding:0 clamp(1rem,4vw,3rem) 0.4rem;
  display:flex; flex-direction:column; gap:0.2rem;
}
.pgfoot__sig b { font-size:1.02rem; }
.pgfoot__sig span { font-size:0.8rem; opacity:0.72; }

/* =========================================================================
   THE BACKLIGHT
   Every film in section 1 is lit from behind in the colour of the chapter
   that made it. It is the same palette as the dots on the knowledge ball and
   the Golden Circle cards, so a person learns it once and it reads the same
   everywhere: orange is NEUCity, green is NEUNature, blue is NEUMotion.

   It has to be a box-shadow rather than a glowing layer: .rcard clips its
   children so it can round its corners over the thumbnail, and anything drawn
   inside would be cut off at the very edge the glow needs to escape from.
   A box-shadow is painted outside the border box and behind the element,
   which is exactly what a backlight is.
   ========================================================================= */
[data-neu="neucity"]    { --neu:#ffa53d; }
[data-neu="neunature"]  { --neu:#3ddc97; }
[data-neu="neumotion"]  { --neu:#2f8fff; }
[data-neu="neufactory"] { --neu:#b98cff; }
[data-neu="neuslm"]     { --neu:#38d9d3; }
[data-neu="neutalents"] { --neu:#ff7ac8; }
[data-neu="neustudio"]  { --neu:#ffd54a; }
[data-neu="neustory"]   { --neu:#8ec5ff; }

/* The reel needs more air than it had, in both directions.
   Sideways: with the old gap the haze from one card ran straight into its
   neighbour's and a row read as one smudge rather than as separate films.
   Vertically it was worse than tight — it was cut off. The reel clips its
   own overflow so the strip can run off the edges of the screen, and with no
   vertical padding the top and bottom rows sat flush against that clip, so
   the half of each glow that pointed out of the reel was simply not drawn.
   The padding is the room the light needs INSIDE the clipping box. */
.slide--hero .reel { padding-block: clamp(2.5rem, 5vh, 4.5rem); }
/* Tighter than it was, to go with the smaller cards — at a 41px gap the row
   looked sparser after the shrink than it had before it. */
.reel--desk .reel__track, .reel--mob .reel__track { gap: clamp(0.9rem, 1.9vw, 1.6rem); }
.reel--mob .reel__track + .reel__track { margin-top: 1.4rem; }

.rcard--film[data-neu] {
  --edge: color-mix(in srgb, var(--neu) 92%, transparent);
  box-shadow:
    0 18px 40px -20px rgba(0, 0, 0, 0.75),                      /* on the page  */
    0 0 0 2px var(--edge),                                      /* the filament */
    0 0 22px -1px color-mix(in srgb, var(--neu) 72%, transparent),   /* the bloom */
    0 0 58px 6px color-mix(in srgb, var(--neu) 38%, transparent),    /* the haze  */
    inset 0 0 0 1px color-mix(in srgb, var(--neu) 45%, transparent);
}

/* Hover lifts the card and turns the light up. Both stops are on the same
   transition the card already had, so it stays one movement. */
.rcard--film[data-neu]:hover,
.rcard--film[data-neu]:focus-visible {
  transform: translateY(-6px);
  box-shadow:
    0 26px 52px -18px rgba(0, 0, 0, 0.8),
    0 0 0 2px var(--neu),
    0 0 30px 1px color-mix(in srgb, var(--neu) 88%, transparent),
    0 0 88px 12px color-mix(in srgb, var(--neu) 52%, transparent),
    inset 0 0 0 1px color-mix(in srgb, var(--neu) 70%, transparent);
}

/* The chapter's name, small, in its own colour — the glow says "these
   two belong together", the word says which one it is. Colour alone is not
   an answer for anyone who cannot separate the greens from the blues. */
.rcard__neu {
  position: absolute; left: 0.6rem; top: 0.6rem; z-index: 2;
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; line-height: 1;
  padding: 0.26rem 0.5rem; border-radius: 999px;
  color: color-mix(in srgb, var(--neu, #8ec5ff) 88%, var(--white));
  background: color-mix(in srgb, #0A0203 62%, transparent);
  border: 1px solid color-mix(in srgb, var(--neu, #8ec5ff) 55%, transparent);
  backdrop-filter: blur(4px);
}
/* On a phone a film card is about 128px wide, and a 58px haze around it is
   most of the card again — it stops reading as a backlight and starts reading
   as fog. Same light, scaled to the object. */
@media (max-width: 640px) {
  .reel--mob .rcard__neu { font-size: 0.5rem; padding: 0.2rem 0.38rem; left: 0.4rem; top: 0.4rem; }
  .reel--mob .rcard--film[data-neu] {
    box-shadow:
      0 12px 26px -14px rgba(0, 0, 0, 0.75),
      0 0 0 1.5px var(--edge),
      0 0 12px -2px color-mix(in srgb, var(--neu) 70%, transparent),
      0 0 30px 2px color-mix(in srgb, var(--neu) 34%, transparent),
      inset 0 0 0 1px color-mix(in srgb, var(--neu) 45%, transparent);
  }
}

/* The panel a film opens into is lit by the same chapter, so pressing a card
   carries its colour with it instead of dropping into a neutral box. */
.reelopen__panel[data-neu] {
  box-shadow:
    0 40px 90px -20px rgba(0, 0, 0, 0.7),
    0 0 0 1px color-mix(in srgb, var(--neu) 50%, transparent),
    0 0 60px 2px color-mix(in srgb, var(--neu) 24%, transparent);
}
/* reelbox.css lands after this file and styles these two through
   `.reelopen .rbox--go .reelopen__go`, which is exactly as specific as the
   obvious selector here — so the later file won and the button stayed blue
   inside a green panel. Matching its shape and adding the panel puts this
   ahead of it on specificity rather than on load order. */
.reelopen .reelopen__panel[data-neu] .rbox--go .reelopen__go,
.reelopen.is-film .reelopen__panel[data-neu] .rbox--go .reelopen__go {
  background: var(--neu); color: #0A0203; border-color: transparent;
}
.reelopen .reelopen__panel[data-neu] .rbox--go .reelopen__go:hover {
  background: color-mix(in srgb, var(--neu) 82%, var(--white));
  border-color: transparent;
}
.reelopen .reelopen__panel[data-neu] .rbox--info .reelopen__kind { color: var(--neu); }

/* A reel that is not moving is not a reel, and a glow that big on a phone in
   a dark room is a lot. Reduced motion keeps the colour and drops the haze. */
@media (prefers-reduced-motion: reduce) {
  .rcard--film[data-neu] {
    box-shadow:
      0 18px 40px -20px rgba(0, 0, 0, 0.75),
      0 0 0 2px var(--edge),
      inset 0 0 0 1px color-mix(in srgb, var(--neu) 45%, transparent);
  }
}

/* =========================================================================
   THE BLUE GLOW ON THE BIG TITLES
   Only the section headlines — the one-per-screen ones a presenter reads out
   loud. Small print, card titles and body copy stay plain, because a glow on
   everything is a glow on nothing.

   It is layered: a tight core that thickens the letterform, then two wider
   blooms. Colour comes from the brand blue rather than plain white, so the
   headline sits in the same light as the disc in the bar and the backlight
   behind the films.
   ========================================================================= */
.slide__title {
  text-shadow:
    0 0 1px color-mix(in srgb, var(--blue) 60%, transparent),
    0 0 12px color-mix(in srgb, var(--blue) 55%, transparent),
    0 0 34px color-mix(in srgb, var(--blue) 40%, transparent),
    0 0 72px color-mix(in srgb, var(--blue) 24%, transparent);
}

/* The hero is the biggest one on the site and sits over moving pictures, so
   it carries a little more, plus a dark base so it stays legible when a pale
   film slides underneath it. */
.slide__title--hero {
  text-shadow:
    0 2px 18px rgba(4, 10, 26, 0.55),
    0 0 2px color-mix(in srgb, var(--blue) 70%, transparent),
    0 0 16px color-mix(in srgb, var(--blue) 68%, transparent),
    0 0 44px color-mix(in srgb, var(--blue) 48%, transparent),
    0 0 96px color-mix(in srgb, var(--blue) 30%, transparent);
}

/* ── THE BUTTON THAT FOLLOWS THE RING ──
   Under "How It Works". Two lines: the chapter it is offering, and that
   chapter's own one-liner underneath, so the button says what you get rather
   than "Learn more". It changes as the ring turns. */
.howmore {
  position: relative; z-index: 3; align-self: center;
  display: inline-grid; grid-template-columns: 1fr auto;
  align-items: center; gap: 0 0.9rem;
  margin-top: 1.2rem; padding: 0.7rem 1.4rem;
  border-radius: 999px; text-decoration: none;
  background: var(--white); color: var(--blue);
  box-shadow: 0 16px 34px -16px rgba(0,0,0,0.45);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
  max-width: min(26rem, 92vw);
}
.howmore:hover { transform: translateY(-2px); box-shadow: 0 22px 40px -16px rgba(0,0,0,0.55); }
.howmore b {
  grid-column: 1; font: 800 1rem 'Inter', sans-serif; line-height: 1.2;
}
.howmore em {
  grid-column: 1; font-style: normal;
  font: 600 0.78rem 'Inter', sans-serif; line-height: 1.3;
  color: color-mix(in srgb, var(--blue) 72%, var(--black));
  /* one line: a chapter with a long description must not make the button grow
     to two different heights as the ring turns */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.howmore__go { grid-column: 2; grid-row: 1 / span 2; font-size: 1.2rem; font-weight: 800; }

@media (max-width: 820px) {
  .howmore { padding: 0.6rem 1.1rem; }
  .howmore b { font-size: 0.92rem; }
  .howmore em { font-size: 0.72rem; }
}

/* THE HEADING WAS UNDER THE SCRIM. That is the whole answer, and it took
   three goes to find because I kept measuring the text instead of the paint
   order. Inside the pinned stage: the film is z-index 0, the scrim is 1, the
   three stages are 2 — and `.howhead` had no position and no z-index at all,
   so it was the one thing in the section painting UNDERNEATH the veil. White
   letters under a 40% wash read grey; and when the wash was strengthened to
   90% to give the heading a darker backing, it went nearly black instead.
   Everything else in here was already above it, which is why only the heading
   ever looked wrong. */
.slide--teach .howhead { position: relative; z-index: 2; }

/* SECTION 2's HEADING IS NOT DIM — IT IS ON THE WRONG BACKGROUND.
   Every heading on the site is white, 700, the same size, with the same blue
   bloom. This one measured identical to the others and still read grey, and
   the reason is what is behind it: the scrim's dark band had faded to 40% by
   22% of the section, and the heading starts at 19.6%. Every other heading on
   the site sits on near-black; this one was sitting on a lit patch of film.

   The first attempt at this put a heavy dark shadow behind the letters, which
   made it worse — a dark halo tight around white glyphs smothers the blue
   bloom and reads muddier still. The heading keeps the ordinary treatment, the
   same as Sovereign and How It Works; it is the BACKING that was fixed, in
   .teach__scrim below. */
.slide--teach .orbit__hint { color: var(--white); opacity: 0.92; }

/* On a white section the same glow reads as a smudge — dark text does not
   emit light. Keep the colour, drop the wide blooms, and let it read as a
   faint blue halo instead. */
.slide--light .slide__title,
.overtitle--dark {
  text-shadow:
    0 0 1px color-mix(in srgb, var(--blue) 22%, transparent),
    0 0 18px color-mix(in srgb, var(--blue) 22%, transparent),
    0 0 40px color-mix(in srgb, var(--blue) 12%, transparent);
}

/* The torch effect punches a hole through the headline to show the pictures
   behind it. A glow drawn round a transparent letter is just a blue blur
   floating in the hole, so it goes while the hole is open. */
.herotitle.is-lit, .overtitle.is-lit, .slide__title.is-lit { text-shadow: none; }

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

/* =========================================================================
   THE LIST BESIDE THE BALL
   The ball answers "how much is there and how does it connect". It cannot
   answer "what is it called" — a dot has no room for a title. So the same
   set is written out next to it, following whatever the picker has selected,
   and it scrolls, because one chapter can hold more than a column shows.
   ========================================================================= */
.gstage__body {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: 1fr minmax(260px, 340px); gap: 0.8rem;
  padding: 0 0.8rem;
}
.gstage__body .gstage__view { padding: 0; }

.glist {
  display: flex; flex-direction: column; min-height: 0;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px; overflow: hidden;
}
.glist__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
  padding: 0.7rem 0.85rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  flex: none;
}
.glist__head b { color: var(--white); font-size: 0.86rem; line-height: 1.3; }
.glist__head span {
  flex: none; font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--white); opacity: 0.5;
}

.glist__items { list-style: none; margin: 0; padding: 0.3rem; overflow-y: auto; flex: 1; min-height: 0; }
.glist__items::-webkit-scrollbar { width: 8px; }
.glist__items::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.16); border-radius: 99px; }

.glist__item {
  display: flex; align-items: flex-start; gap: 0.55rem;
  padding: 0.5rem 0.55rem; border-radius: 11px;
  text-decoration: none; color: var(--white);
  transition: background 0.16s ease, transform 0.2s cubic-bezier(0.2, 1.35, 0.35, 1);
}
.glist__item:hover, .glist__item:focus-visible {
  background: rgba(255, 255, 255, 0.09); transform: translateX(2px);
}

/* one pip per chapter — a thing shared between two chapters shows two */
.glist__pips { display: flex; flex: none; gap: 2px; padding-top: 0.28rem; }
.glist__pips i {
  width: 7px; height: 7px; border-radius: 50%; display: block;
  box-shadow: 0 0 7px -1px currentColor;
}

.glist__txt { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.glist__txt b { font-size: 0.8rem; font-weight: 700; line-height: 1.35; }
.glist__txt em {
  font-style: normal; font-size: 0.68rem; line-height: 1.35;
  color: var(--white); opacity: 0.5;
}

.glist__now {
  flex: none; margin-left: auto; align-self: center;
  font-size: 0.58rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: #0a2a18; background: #3ddc97; border-radius: 999px; padding: 0.12rem 0.4rem;
}
.glist__empty { padding: 1.2rem 0.9rem; font-size: 0.78rem; color: var(--white); opacity: 0.45; line-height: 1.5; }

/* the dot the pointer is currently over in the list */
.ngnode.is-hover .ngdot__hot { opacity: 1; }
.ngnode.is-hover .ngdot__glow { transform-box: fill-box; transform-origin: center; transform: scale(1.5); }

/* On a narrow screen the list goes underneath and takes a third of the
   height — the ball still has to be big enough to be worth turning. */
@media (max-width: 860px) {
  .gstage__body { grid-template-columns: 1fr; grid-template-rows: 1fr minmax(120px, 34%); }
}
@media (prefers-reduced-motion: reduce) {
  .glist__item { transition: none; }
}

/* THE STANDBY COPY.
   neuon.site exists for the days the office hosting neuon.ai has no internet.
   Somebody who arrives there should know within a second that it is the
   standby and where the real one lives — so this sits above the footer on
   every page, quiet but unmissable, and it is the only visible difference
   between the two sites. */
.standby {
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:0.5rem; padding:0.7rem 1.2rem; text-align:center;
  background:var(--black); color:var(--white); font-size:0.85rem;
}
.standby span { opacity:0.6; }
.standby a {
  color:var(--white); font-weight:800; text-decoration:none;
  border-bottom:2px solid color-mix(in srgb, var(--blue) 80%, transparent);
}
.standby a:hover { border-bottom-color:var(--white); }

/* THE CALL TO ACTION ON EVERY SECTION.
   One row, same shape everywhere, so a visitor learns once where the next
   step lives and then finds it on all eight. Inherits the section's own
   colours rather than carrying its own, because a blue button on the blue
   slide and a blue button on the white one are the same instruction. */
.ctarow {
  display:flex; align-items:center; gap:0.9rem; flex-wrap:wrap;
  margin-top:1.5rem; position:relative; z-index:3;
}
.ctarow__note { font-size:0.85rem; opacity:0.62; }
.btn.cta {
  display:inline-block; text-decoration:none;
  background:var(--blue); color:var(--white); border-color:var(--blue);
}
.btn.cta:hover { background:var(--blue-deep); border-color:var(--blue-deep); }
.slide--blue .btn.cta { background:var(--white); color:var(--blue); border-color:var(--white); }
.slide--blue .btn.cta:hover { background:var(--blue-pale); }
.slide--dark .btn.cta { background:var(--blue); color:var(--white); border-color:var(--blue); }
/* Section 1 carries no call-to-action row and no "See it working" any more —
   the chapter pill and its arrows are the only controls down there now. */

/* =========================================================================
   SECTION 1 — THE BOY AND THE BALL
   Two layers, one number. `--hp` runs 0 to 1 across the section's extra
   height and hero.js is the only thing that writes it.
   ========================================================================= */

/* The section is taller than the screen; that surplus IS the scroll the
   movement is spent over. `overflow: clip` and not `hidden`, because hidden
   creates a scroll container and a scroll container breaks the sticky stage
   inside it — the stage would simply never stick. */
.deck-page .slide--hero {
  /* 300vh, not 230. The extra 70 is ALL pause: hero.js's RUN was dropped from
     0.66 to 0.43 at the same time, which keeps the flight itself spending the
     same 86vh of scroll it always did. Raising the height without dropping RUN
     would have made the ball fly further per turn of the wheel, which is the
     opposite of what was asked. The two numbers only make sense together —
     change one and the other has to move. */
  min-height: 300vh;
  padding: 0;
  overflow: clip;
  display: block;
  scroll-snap-align: start;
}
.hero__pin {
  position: sticky; top: 0;
  height: 100vh; width: 100%;
  display: flex; flex-direction: column; justify-content: center;
  padding: calc(var(--nav-disc) + 1.6rem) clamp(1.6rem,6vw,6rem) 3rem;
  overflow: hidden;                    /* the reel still needs clipping */
}

/* The plate. It drifts left as the ball comes in — a small counter-move, so
   the ball reads as being PULLED across rather than the camera panning. */
.slide--hero .hero__bg {
  background-image: url('/assets/video/hero-kid-noglobe.jpg');
  /* THE PLATE MUST NEVER STOP COVERING.
     It drifts 2.5% left as the ball comes in. The first version also scaled
     it back down to 1.0 by the end — so at the end of the scroll it was
     exactly viewport-wide AND shifted left, which left a 32px strip of white
     page down the right-hand edge. The scale now bottoms out at 1.06, which
     is 76px of slack against a 32px drift: always more overhang than travel. */
  transform: translate3d(calc(var(--hp,0) * -2.5%), 0, 0) scale(calc(1.10 - var(--hp,0) * 0.04));
  transform-origin: 30% 55%;
  transition: transform 0.08s linear;
}
@media (max-width:820px) {
  .slide--hero .hero__bg { background-image: url('/assets/video/hero-kid-noglobe-sm.jpg'); }
}

/* THE BALL.
   Lands dead centre and full size — on the boy's eyeline, which is why `top`
   is 52% and not 50%.

   WHERE IT STARTS is set by the two offsets below, and it is a decision, not
   a leftover. It used to begin at 46vw out, which put more than half of it
   past the right-hand edge: the first thing anybody saw was a piece of
   something, and there was nothing to tell them a whole object was on its way
   in. It now starts high and to the right but FULLY on the screen — about 83%
   across and 38% down — so the thing that travels is visible from the first
   frame, and what the scroll does is bring it to the middle rather than
   introduce it. Change these two numbers to move the start; the landing is
   `top`/`left` and is not affected. */
.hero__ball {
  position: absolute; z-index: 1;
  top: 52%; left: 50%;
  width: clamp(19rem, 42vw, 40rem); aspect-ratio: 1;
  pointer-events: none;
  transform:
    translate3d(
      calc(-50% + (1 - var(--hp,0)) * 33vw),
      calc(-50% - (1 - var(--hp,0)) * 14vh),
      0)
    scale(calc(0.52 + var(--hp,0) * 0.48));
  opacity: calc(0.35 + var(--hp,0) * 0.65);
  transition: transform 0.08s linear, opacity 0.2s linear;
}
/* once it has landed it can be touched — not before, or it would swallow
   clicks on the reel while flying past */
.slide--hero.is-landed .hero__ball { pointer-events: auto; }
.hero__ballin { width: 100%; height: 100%; cursor: grab; }
.hero__ball.is-turning .hero__ballin { cursor: grabbing; }
.hero__ball .netgraph { width: 100%; height: 100%; }

/* Hovering halts it, as asked. The component's own drift is a CSS animation
   on the shell and pings; pausing them is what "stops moving" means to an eye
   — and the ball brightens slightly so it is obvious it answered. */
.hero__ball.is-held .netgraph__shell,
.hero__ball.is-held .netgraph__ping,
.hero__ball.is-held .netgraph__ping--2 { animation-play-state: paused; }
.hero__ball.is-held { filter: brightness(1.12) saturate(1.1); }

/* THE HEADLINE AT THE TOP, AND THE REEL UNDER IT — IN THAT ORDER, IN FLOW.
   The headline was pinned to the top and the reel was pinned to the middle,
   which meant nothing stopped them from meeting: at 1280x800 the first row of
   cards ran straight through "YOUR WORLD, UNDERSTOOD BY AI." Two absolutely
   positioned things cannot be made to avoid each other by choosing better
   numbers — the numbers are right until somebody opens a shorter window.

   So both come back into the flow and the pinned stage stacks them from the
   top. The reel is EARLIER in the markup than the headline (it has to be: the
   film cards are generated above it), which is what `order` is for. */
.hero__pin { justify-content: flex-start; }
.slide--hero .herokick,
.slide--hero .herotitle {
  position: static; transform: none;
  width: min(46rem, 90vw); margin-left: auto; margin-right: auto;
}
.slide--hero .herokick { order: -2; margin-top: 0.5rem; margin-bottom: 0.2rem; }
.slide--hero .herotitle { order: -1; margin-top: 0; margin-bottom: 0; }
/* and the reel takes what is left, under them.
   It has to break out of the stage's padding to keep running off both edges
   of the screen — in flow it would sit inside the padding and the cards would
   stop short of the edge, which loses the sense of a strip still going. */
.slide--hero .reel {
  position: static; transform: none; margin-top: clamp(0.8rem, 3vh, 2.2rem);
  width: 100vw; margin-inline: calc(50% - 50vw);
  flex: none;                 /* never squashed: it clips a row when it is */
}

/* A SHORT WINDOW. The cards are sized from viewport height so they come down
   on their own, but the headline is sized from viewport WIDTH and does not —
   so on a wide, short window there was no room left under it. It matters more
   now, not less: the headline is forced to two lines, so it cannot quietly
   save room by collapsing to one. The headline gives up the difference. */
@media (max-height: 780px) {
  .slide--hero .herotitle { font-size: clamp(1.6rem, 4.2vw, 2.6rem); }
  .slide--hero .reel { margin-top: 0.6rem; }
  .slide--hero .reel.reel--desk { padding: 0.8rem 0; }
}

/* The words step back as the ball arrives — one thing at a time. */
.slide--hero .herokick,
.slide--hero .herotitle,
.slide--hero .reel,
.slide--hero .herowatch { opacity: var(--hp-text, 1); transition: opacity 0.2s linear; }
.slide--hero .ctarow { opacity: var(--hp-text, 1); }

/* Anyone who asked for less movement gets the finished picture, no travel. */
.slide--hero.is-still { min-height: 100vh; }
.slide--hero.is-still .hero__ball { transition: none; }

@media (max-width:820px) {
  /* ONE SCREEN. This used to be 190vh — the travel the ball's flight was spent
     over. A phone does not fly the ball any more, so there is nothing to spend
     it on and it was just a wall of scrolling before anything could be read.
     The ball's own size and place are set further down, with everything else
     that hangs off it. */
  .deck-page .slide--hero { min-height: 100vh; }
}

/* ── Section 1, revised: Dr KC's footage, scrubbed not played ──
   The plate is a <video> now. It is never played: hero.js sets currentTime
   from the scroll position, so the boy moves exactly as far as somebody has
   scrolled and stops dead when they stop. That is the whole point — a picture
   that moves on its own has to be stopped and explained; this one answers the
   hand already on the mouse. */
.slide--hero video.hero__bg {
  width: 100%; height: 100%; object-fit: cover;
  background: #08131f;
}

/* ── the reel keeps tracking ──
   It was stopped for a round and Dr KC asked for it back: the drifting row is
   what tells a visitor at a glance that there is a lot to look at. What he
   did not want was the BACKGROUND playing on its own, and that is now scrubbed
   by scroll instead. This one still pauses while a film is open. */

/* ── the call to action sits centred on the sections that are centred ── */
.slide#sovereign .ctarow,
.slide#how-it-works .ctarow,
.slide#commitment .ctarow,
.slide#sdg .ctarow,
.slide#news .ctarow { justify-content: center; }

/* Our Commitment: at the BOTTOM, in the middle.
   The button was rendering ABOVE the heading even though it is last in the
   markup, because this section's children carry their own flex `order` — so
   position in the file decides nothing here. `order` is the only thing that
   does, and 99 puts it after everything. A call to action belongs after the
   things it is asking you to believe, not before them. */
.slide#commitment .ctarow {
  order: 99;
  width: 100%;
  margin-top: 1.6rem;
  justify-content: center;
}

/* the NEU the ball is currently showing, at the end of the scroll */
.heroneu {
  position: absolute; left: 50%; bottom: 12%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.45rem 1.1rem; border-radius: 999px;
  background: rgba(8, 19, 31, 0.72); backdrop-filter: blur(6px);
  border: 1.5px solid rgba(255, 255, 255, 0.18);
  font: 800 0.9rem 'Inter', sans-serif; color: var(--white);
  opacity: 0; transition: opacity 0.25s ease;
  pointer-events: none; white-space: nowrap; z-index: 2;
}
.slide--hero.is-landed .heroneu { opacity: 1; }
.heroneu i { width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--c, #8ec5ff); }
.heroneu em { font-style: normal; font-weight: 600; opacity: 0.6; font-size: 0.8rem; }

/* how long a film runs, on its card. Top-right so it never fights the title
   along the bottom, and always legible over whatever the still happens to be. */
.rcard__mins {
  position: absolute; top: 0.5rem; right: 0.5rem; z-index: 2;
  font: 800 0.7rem 'Inter', sans-serif; letter-spacing: 0.02em;
  padding: 0.15rem 0.45rem; border-radius: 6px;
  background: rgba(8, 19, 31, 0.78); color: var(--white);
}

/* ── the chapter name, and what it opens ── */
.heroneu { cursor: pointer; border: 1.5px solid rgba(255,255,255,0.18); }
.heroneu[hidden] { display: none; }
.slide--hero.is-landed .heroneu { pointer-events: auto; }
.heroneu:hover { background: rgba(8,19,31,0.9); border-color: rgba(255,255,255,0.4); }
.heroneu b { font-weight: 800; }
.heroneu::after { content: '▸'; opacity: 0.5; margin-left: 0.15rem; }

/* The panel is centred in the space BELOW the header, not in the viewport.
   The NEUON mark in the header is fixed and hangs about 8.5rem down the middle
   of the screen — dead centre of anything this size — so it was landing on the
   ball. Padding here rather than inside the panel, because the scrim is
   positioned against the padding box and still covers the whole screen. */
.neufan {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  padding: 9.5rem 0 1rem;
}
.neufan[hidden] { display: none; }
.neufan__scrim { position: absolute; inset: 0; background: rgba(4,10,20,0.82); backdrop-filter: blur(4px); }
.neufan__panel {
  position: relative; width: min(64rem, calc(100vw - 2rem)); max-height: 100%;
  background: linear-gradient(180deg, #0b1a2c, #071119);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 22px;
  padding: 1.4rem 1.2rem; overflow: hidden;
  display: flex; flex-direction: column;
}
.neufan__x {
  position: absolute; top: 1rem; right: 1rem; z-index: 70;
  width: 2.2rem; height: 2.2rem; border-radius: 50%; border: 0;
  background: rgba(255,255,255,0.12); color: var(--white); font-size: 1.2rem; cursor: pointer;
}
.neufan__x:hover { background: var(--blue); }

/* the ball and the chapter it is showing, with a way either side of it */
.neufan__head { display: flex; align-items: center; justify-content: center; gap: 1rem; }
.neufan__who { display: grid; justify-items: center; gap: 0.15rem; }
.neufan__core {
  width: clamp(5rem, 9vw, 7rem); aspect-ratio: 1;
  pointer-events: none;                 /* it is the badge for the list, not a control */
}
.neufan__core .netgraph { width: 100%; height: 100%; }
/* on a laptop screen the badge gives up its height to the list — the films
   are what somebody opened this for */
@media (max-height: 820px) { .neufan__core { width: 4.5rem; } }
/* the list takes whatever height is left and scrolls inside itself, so a
   chapter with twenty films never pushes the panel off the screen */
.neufan__stage { position: relative; margin-top: 1rem; overflow: auto; flex: 1 1 auto; min-height: 0; }
.neufan__head, .neufan__hint { flex: none; }

/* next and previous chapter, without closing and opening again */
.neufan__step {
  flex: none; width: 2.6rem; height: 2.6rem; border-radius: 50%; cursor: pointer;
  border: 1.5px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.08);
  color: var(--white); font-size: 1.2rem; line-height: 1; z-index: 60;
  display: grid; place-items: center;
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.12s ease;
}
.neufan__step:hover { background: var(--blue); border-color: var(--blue); transform: scale(1.08); }
.neufan__step:active { transform: scale(0.95); }

/* the chapter, named under its own ball */
.neufan__name {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  color: var(--white); text-align: center;
}
.neufan__dot {
  width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--c, #8ec5ff); flex: none;
}
.neufan__name b { font-size: clamp(1.15rem, 2.4vw, 1.5rem); font-weight: 800; }
.neufan__role { font-style: normal; font-size: 0.85rem; color: rgba(255,255,255,0.6); }
.neufan__hint { text-align: center; font-size: 0.8rem; color: rgba(255,255,255,0.5); margin-top: 0.7rem; }

/* ── the films, the way a video list looks everywhere else ──
   Still, running time in the corner of it, title underneath. Nobody has to be
   told how to read this one, which is the entire point of borrowing it. */
.ytgrid {
  display: grid; gap: 1.1rem 1rem; padding: 0.2rem 0.2rem 0.4rem;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}
.ytcard {
  display: grid; gap: 0.55rem; align-content: start;
  border: 0; padding: 0; background: none; cursor: pointer; text-align: left;
}
.ytcard__thumb {
  position: relative; display: block; aspect-ratio: 16/9; overflow: hidden;
  border-radius: 12px; background: #0b1a2c;
  box-shadow: 0 14px 30px -14px rgba(0,0,0,0.8);
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}
.ytcard__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ytcard:hover .ytcard__thumb { transform: translateY(-3px); box-shadow: 0 20px 36px -14px rgba(0,0,0,0.9); }
.ytcard:focus-visible { outline: none; }
.ytcard:focus-visible .ytcard__thumb { outline: 3px solid var(--c, #8ec5ff); outline-offset: 3px; }

/* how long it runs, bottom-right of the still */
.ytcard__dur {
  position: absolute; right: 0.4rem; bottom: 0.4rem; z-index: 2;
  font: 800 0.68rem 'Inter', sans-serif; letter-spacing: 0.02em;
  padding: 0.12rem 0.34rem; border-radius: 5px;
  background: rgba(0,0,0,0.78); color: #fff;
}
/* the play triangle, only once a pointer is on the card — a still with a
   permanent button over the middle of it hides the thing being chosen */
.ytcard__play {
  position: absolute; inset: 0; z-index: 1; display: grid; place-items: center;
  opacity: 0; transition: opacity 0.16s ease; background: rgba(4,10,20,0.28);
}
.ytcard__play::before {
  content: ''; width: 0; height: 0;
  border-left: 1.1rem solid #fff;
  border-top: 0.68rem solid transparent; border-bottom: 0.68rem solid transparent;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
}
.ytcard:hover .ytcard__play, .ytcard:focus-visible .ytcard__play { opacity: 1; }

.ytcard__meta { display: grid; gap: 0.15rem; }
.ytcard__meta strong {
  font: 700 0.86rem/1.35 'Inter', sans-serif; color: var(--white);
  /* two lines, then an ellipsis — a title is a label here, not the article */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ytcard__meta em { font-style: normal; font-size: 0.74rem; color: var(--c, #8ec5ff); font-weight: 700; }

@media (max-width: 700px) {
  /* the mark is smaller on a phone, so less room has to be left for it */
  .neufan { padding-top: 7.25rem; }
  .neufan__core { width: min(4.5rem, 18vw); }
  .neufan__step { width: 2.2rem; height: 2.2rem; }
  .ytgrid { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.9rem 0.7rem; }
  .ytcard__meta strong { font-size: 0.78rem; }
}

/* the film cards on the ring */
.fcard {
  display: block; width: clamp(8rem, 12vw, 11rem); aspect-ratio: 3/4;
  border: 0; padding: 0; cursor: pointer; border-radius: 14px; overflow: hidden;
  position: relative; background: #0b1a2c;
  box-shadow: 0 18px 40px -18px rgba(0,0,0,0.8);
}
.fcard img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.fcard__tag {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 1.6rem 0.6rem 0.55rem; text-align: left;
  font: 700 0.72rem 'Inter', sans-serif; color: var(--white); line-height: 1.3;
  background: linear-gradient(transparent, rgba(4,10,20,0.92));
}
.fcard__mins {
  position: absolute; top: 0.4rem; right: 0.4rem; z-index: 2;
  font: 800 0.66rem 'Inter', sans-serif; padding: 0.12rem 0.4rem; border-radius: 5px;
  background: rgba(8,19,31,0.8); color: var(--white);
}
.neufan__none { color: rgba(255,255,255,0.6); text-align: center; padding: 4rem 1rem; }

/* ── SIGN UP TO HEAR MORE ──
   The shape Dr KC marked up: one pill, the field on the left, the button on
   the right, joined rather than sitting apart. Under the chapter row so it is
   the last thing on the section, and it fades with the rest of the words as
   the ball takes the stage — a form floating over a landed ball would be
   asking for two things at once. */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* AT THE END OF THE SCROLL, NOT THE START.
   Dr KC's note, and it is about the order a pitch happens in: he talks the
   room through the chapters first and only then asks for the address. A form
   sitting there before anybody has heard anything is asking for a favour from
   a stranger. So it belongs to the landed state — the same moment the chapter
   row appears — and it is not on the screen before that.

   It also means the form no longer competes for room with the headline and
   the reel, which is why the headline is back to full size: at the start of
   the scroll this is not in the layout at all. */
.signup {
  position: absolute; left: 50%; bottom: 6.5%; transform: translateX(-50%);
  z-index: 5; display: flex; align-items: stretch; flex-wrap: wrap;
  justify-content: center; gap: 0;
  /* 100%, not 90vw: the stage has padding, and a width measured against the
     VIEWPORT overflowed the content box and sat off-centre on a phone. */
  width: min(30rem, 100%); max-width: calc(100vw - 2rem);
  background: rgba(255,255,255,0.96); backdrop-filter: blur(6px);
  border-radius: 12px; padding: 0.34rem;
  box-shadow: 0 20px 44px -18px rgba(0,0,0,0.7);
  /* hidden to the keyboard too, or tab lands in a form nobody can see */
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.28s ease, visibility 0s linear 0.28s;
}
.slide--hero.is-landed .signup {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity 0.28s ease, visibility 0s;
}

.signup__field { flex: 1 1 10rem; display: flex; min-width: 0; }
.signup__field input {
  width: 100%; border: 0; background: none; outline: none;
  padding: 0.6rem 0.8rem; min-width: 0;
  font: 500 0.95rem 'Inter', sans-serif; color: #0b1a2c;
}
.signup__field input::placeholder { color: rgba(11,26,44,0.5); }
.signup__field input:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; border-radius: 8px; }

.signup__go {
  flex: none; border: 0; cursor: pointer; border-radius: 9px;
  padding: 0.6rem 1.1rem; white-space: nowrap;
  background: var(--blue); color: var(--white);
  font: 800 0.9rem 'Inter', sans-serif;
  transition: background 0.16s ease, transform 0.12s ease;
}
.signup__go:hover { background: var(--blue-deep); }
.signup__go:active { transform: scale(0.97); }
.signup__go[disabled] { opacity: 0.6; cursor: default; }

/* the answer takes the whole width under the pill, so the row never jumps */
.signup__say {
  flex: 1 0 100%; text-align: center; margin: 0.35rem 0 0.1rem;
  font: 600 0.78rem 'Inter', sans-serif; color: #0b1a2c;
}
.signup__say:empty { display: none; }
.signup__say.is-bad { color: #b3261e; }
/* it has done its job — the field and the button go, the thank-you stays */
.signup.is-done .signup__field, .signup.is-done .signup__go { display: none; }
.signup.is-done .signup__say { margin: 0.35rem 0; }

@media (max-width: 820px) {
  /* lower than on a desktop: the chapter row sits at 6rem from the foot here,
     and at 6.5% the form was 5px into it */
  .signup { width: min(26rem, 100%); bottom: 2.4%; }
  .signup__go { padding: 0.6rem 0.8rem; font-size: 0.82rem; }
  /* a narrower basis, or the field claims 10rem and pushes the button onto a
     second line — one pill, one row, as marked up */
  .signup__field { flex-basis: 6rem; }
}

/* ── skipping through the chapters by hand ──
   The ball moves on by itself every couple of seconds. On a stage that is
   sometimes too slow and sometimes too fast, so there are arrows: press one
   and it goes straight to the next chapter and restarts the clock. */
/* Up off the floor of the screen. At 12% it sat right on the bottom edge of
   the pinned stage and was the first thing clipped on a short window. */
.heronav {
  position: absolute; left: 50%; bottom: 19%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 0.5rem; z-index: 3;
  opacity: 0; transition: opacity 0.25s ease; pointer-events: none;
}
.slide--hero.is-landed .heronav { opacity: 1; pointer-events: auto; }
.heronav .heroneu { position: static; transform: none; opacity: 1; }
.heroarrow {
  width: 2.2rem; height: 2.2rem; flex: none; border-radius: 50%; cursor: pointer;
  border: 1.5px solid rgba(255,255,255,0.2); background: rgba(8,19,31,0.72);
  color: var(--white); font-size: 1rem; line-height: 1;
  backdrop-filter: blur(6px);
  display: grid; place-items: center;
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.12s ease;
}
.heroarrow:hover { background: var(--blue); border-color: var(--blue); transform: scale(1.08); }
.heroarrow:active { transform: scale(0.95); }

/* the burst of charges before the ball hands over to the next chapter —
   the same signal Section 2 uses, so the two read as one machine */
.hero__ball.is-zapping .netgraph__shell { animation-duration: 0.5s; }
.hero__ball.is-zapping { filter: brightness(1.18) saturate(1.15); }

/* An aimed charge carries its chapter's colour, set inline by netgraph.js, so
   the stroke here must not overrule it — `is-charged` alone paints white. */
.netgraph__links line.is-charged.is-aimed { stroke-width: 1.6; }

/* On a phone "See it working" leaves the right-hand corner and centres itself
   at the foot of the section — straight underneath the call to action and the
   chapter nav, which both sit centred too. On a wide screen they miss each
   other by 165px; here they overlapped by 24. Both are lifted clear. */
@media (max-width: 820px) {
  /* JUST THE NAME.
     "NEUTalents The people chapter" is a comfortable phrase on a wide screen
     and half the width of a phone. The role is dropped here so the pill stays
     a pill — it is still read out to screen readers through the button's
     aria-label, so nothing is lost, only hidden. */
  .heroneu em { display: none; }

  /* nothing else is down here now, so the pill sits where the eye already is */
  .heronav { bottom: 6rem; }
}

/* ── the chapter, hung off the ball ──
   Dr KC's sketch: the name floating by the lit dot, and one of that chapter's
   films directly under it. It rides with the ball, so it grows and travels
   with it, and it freezes when the ball does. */
/* THE FILMS, ON THE BALL.
   A window hangs off each side, so the ball is flanked rather than lopsided.
   Positioned against the ball itself, which means they travel with it and land
   where it lands. The chapter is named once, at the foot — it used to be named
   here as well, which put a second copy of the same word directly under the
   NEUON mark in the header. */

/* the two windows */
.herofilm {
  position: absolute; z-index: 4; top: 26%;
  /* Big enough to actually watch a still, as asked — about a quarter of the
     window each, which is what fits either side of the ball at 1280 without
     the two of them touching it. */
  width: clamp(11rem, 25vw, 22rem); aspect-ratio: 16/10;
  border-radius: 16px; border: 1.5px solid rgba(255,255,255,0.16);
  background: #0b1a2c; padding: 0; overflow: hidden; cursor: pointer;
  box-shadow: 0 22px 44px -18px rgba(0,0,0,0.85);
  opacity: 0; pointer-events: none;
  transition: opacity 0.28s ease, transform 0.18s ease, border-color 0.18s ease;
}
.herofilm[hidden] { display: none; }
.slide--hero.is-landed .herofilm { opacity: 1; pointer-events: auto; }
/* Clear of the ball's LIT CIRCLE, which is smaller than its square box —
   at 82% the windows were sitting on the outermost dots. */
.herofilm--l { right: 87%; }
.herofilm--r { left: 87%; }
.herofilm:hover { transform: translateY(-3px); border-color: var(--c, #8ec5ff); }
.herofilm img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.herofilm__tag {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 1.8rem 0.7rem 0.6rem; text-align: left;
  font: 700 0.82rem 'Inter', sans-serif; color: var(--white); line-height: 1.3;
  background: linear-gradient(transparent, rgba(4,10,20,0.94));
}
.herofilm__more {
  position: absolute; top: 0.45rem; right: 0.45rem; z-index: 2;
  font: 800 0.68rem 'Inter', sans-serif; padding: 0.16rem 0.5rem; border-radius: 6px;
  background: rgba(8,19,31,0.82); color: var(--white);
}
.herofilm__more:empty { display: none; }

/* THE NAME ALONE, at the bottom.
   "NEUMotion First second-generation graduate (with Dr. Abel)" is a sentence,
   not a label — and the chapter is now named on the ball as well, so the row
   at the foot only has to say which one. */
.heroneu em { display: none; }

/* PAGING THROUGH THE REST OF THE CHAPTER.
   Two windows show two films, and most chapters have more than two. These step
   the pair on and back. They are for the phone, where there is no room to make
   the windows bigger AND show more of them — on a desktop the row at the foot
   already has arrows and a second pair beside them would just be a puzzle. */
.herofilmnav {
  position: absolute; z-index: 5; left: 50%; transform: translateX(-50%);
  top: 88%; display: none; gap: 0.8rem;
  opacity: 0; pointer-events: none; transition: opacity 0.28s ease;
}
.slide--hero.is-landed .herofilmnav { opacity: 1; pointer-events: auto; }
.herofilmnav[hidden] { display: none !important; }
.herofilmnav button {
  width: 2.4rem; height: 2.4rem; border-radius: 50%; cursor: pointer;
  border: 1.5px solid rgba(255,255,255,0.28); background: rgba(8,19,31,0.72);
  color: var(--white); font-size: 1rem; line-height: 1;
  backdrop-filter: blur(6px); display: grid; place-items: center;
}
.herofilmnav button:active { transform: scale(0.94); }

/* ── SECTION 1 ON A PHONE: ONE SCREEN, NO FLIGHT ──
   The ball flying in from the corner is worth a screen and a half of scroll
   on a laptop. On a phone it was a screen and a half before anything could be
   read, with the reel of films underneath as a second wall. So the section is
   one screen tall, hero.js marks it landed from the start, and what a phone
   gets is the finished arrangement Dr KC drew: headline, ball, two films with
   their arrows, the chapter row, the sign-up.

   Everything below is measured against the BALL, because on a phone the ball
   no longer moves — it is the fixed thing everything else can hang from. */
@media (max-width: 820px) {
  .slide--hero { min-height: 100vh; }
  /* the reel is the wall. Gone on a phone; the two windows on the ball are
     how films are reached here, and they page. */
  .reel--mob, .slide--hero .reel { display: none; }

  /* IN THE FLOW, under the headline. The third time today this is the answer:
     the ball's `top` is a percentage of viewport HEIGHT while its size is a
     percentage of viewport WIDTH, so on a shorter phone it rode up into the
     headline and no single percentage fixes both. In the flow it simply sits
     after the headline, whatever the headline turned out to be.
     The windows and their arrows stay hung off the ball and travel with it. */
  .slide--hero .hero__ball {
    /* relative, NOT static: the two windows and their arrows are positioned
       against the ball, and a static ball is not a containing block — they
       measured themselves against the whole pinned stage instead and landed
       hundreds of pixels down the page. */
    position: relative; transform: none; margin: 1.1rem auto 0;
    top: auto; left: auto; width: min(53vw, 13rem);
  }

  /* the two films, just under the ball */
  .herofilm { top: 106%; width: min(45vw, 11.5rem); }
  .herofilm--l { right: 51%; }
  .herofilm--r { left: 51%; }
  .herofilm__tag { font-size: 0.62rem; padding: 1rem 0.4rem 0.35rem; }
  /* and their arrows under those — 106% plus a window's own height */
  .herofilmnav { display: flex; top: 164%; }
}

/* ================= A STANDING SCREEN — the NEUONBOX =================
   A 1080x1920 panel is portrait, but it is not a phone: it is wider than a
   laptop is tall. It used to be given the desktop shape and left with the
   bottom half of the screen empty, so for a while it had five bands of its
   own and a third block of markup to make them.

   Dr KC's start frame replaced that brief: headline high, the picture open
   through the middle, the strip along the foot. A standing panel wants
   exactly what a phone wants — TWO lines at the foot — only bigger, because
   it is read from two metres rather than from a hand. So it shares the
   phone's markup and gets its own size, and the third variant is gone: one
   shape for a landscape screen, one for a portrait one.

   Deliberately NOT below 821px wide: under that the hero puts the ball in the
   flow under the headline and the phone's own rules already handle it. */
@media (orientation: portrait) and (min-width: 821px) and (min-height: 1000px) {
  .reel--desk, .slide--hero .reel--desk { display: none; }
  .slide--hero .reel--mob { display: block; }

  /* 22vw — about 238px on a 1080 panel, four and a half across. Bigger than
     the laptop's 18.6vw because of the viewing distance, and small enough
     that two lines are a band at the foot rather than a wall. */
  .reel--mob .rcard--film {
    width: clamp(12rem, 22vw, 26rem) !important;
    height: auto !important;
    aspect-ratio: 16 / 9;
  }
  .reel--mob .rcard__tag { font-size: 0.78rem; padding: 2rem 0.8rem 0.7rem; }
  .reel--mob .rcard__neu { font-size: 0.62rem; }
  .reel--mob .reel__track { gap: clamp(0.8rem, 1.5vw, 1.4rem); }
  .reel--mob .reel__track + .reel__track { margin-top: 0.7rem; }
  .slide--hero .reel--mob { padding-block: 0.8rem; margin-bottom: 0; }

  /* the pager keeps its own place on a screen this tall — there is room for
     it above the strip without putting it in the flow, which is the phone's
     fix for a phone's problem */
  .slide--hero .heronav { position: absolute; order: 0; margin: 0; bottom: 19%; }
}

/* ================= SECTION 2 — THREE STAGES THAT STAY PUT =================

   1 · IT LOOKED FROZEN.
   The section is pinned: for three screenfuls of scrolling the page does not
   move, and the film behind it is five seconds of a boy and a robot barely
   moving, at 52% brightness. So scrolling produced no visible change and the
   page read as stuck or broken — which is exactly what it was reported as.
   The film is fine and it really is being scrubbed; there is simply not
   enough IN five slow seconds to see. So the section says out loud how far
   through it you are, with a bar that fills as you scroll, and the picture
   brightens as you go. Both hang off --sp, the 0-to-1 the scroll driver was
   already publishing and nothing was reading.

   2 · THE STAGE HEADING MOVED, AND ON A SHORT SCREEN IT MOVED INTO THE CHIPS.
   The three stages were absolutely positioned and centred on the middle of
   the stack. Stage 2 is much the tallest — it carries the ball — so on a
   window under about 800px it was taller than the box it was centred in, and
   content centred in a box too small for it overflows BOTH ways. The top way
   is the heading: "2 IT LEARNS" landed on Computer Vision · Machine Learning
   · Internet of Things.
   They are stacked in one grid cell now and aligned to the TOP of it, so
   every stage heading lands in the same place whatever is under it, and no
   stage can ever ride up into the words above. It reads better as well: the
   frame holds still and the contents change, instead of three differently
   placed layouts taking turns.
   ========================================================================= */

.teach__pin .teach3 {
  display: grid;
  /* an explicit full-width track, not `auto`.
     The three stages carry width:100%, and a percentage width inside an `auto`
     track is circular — the browser breaks the tie by sizing the track to the
     stage's max-content instead, which came out at 336px of a 736px stack and
     left the three promises squeezed into the left half of the screen. */
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
}
.teach__pin .teach3__col {
  /* all three in the same cell, all three starting at its top edge */
  grid-area: 1 / 1;
  position: static;
  left: auto; top: auto;
  transform: scale(0.97);
  transform-origin: 50% 20%;
}
.teach__pin .teach3__col.is-on { transform: scale(1); }

/* On a short window the ball is what decides whether stage 2 fits at all, and
   30vh was still too much once the heading, the chip and the line under it
   were counted. */
@media (max-height: 800px) {
  .teach__pin .teach3__brain { width: min(13rem, 26vh); }
}

/* ---- AI CORE is a door, so it has to look like one ---- */
.teach3__chip--door {
  border: 0; cursor: pointer; font-family: inherit;
  transition: transform 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
}
.teach3__chip--door:hover,
.teach3__chip--door:focus-visible {
  transform: translateY(-1px) scale(1.04);
  background: #0a67d6;
  box-shadow: 0 0 30px 6px color-mix(in srgb, var(--blue) 85%, transparent);
}
.teach3__chip--door:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }

/* ---- how far through the section you are ----
   A rail behind the three stage dots. It is not decoration: it is the only
   thing on screen that moves CONTINUOUSLY while the stage is pinned, so a
   scroll that changes nothing else still visibly does something. */
.teachnav { align-items: center; }
.teachnav::before {
  content: ''; position: absolute; left: 50%; transform: translateX(-50%);
  top: 50%; margin-top: -1px;
  width: calc(100% + 2.4rem); height: 2px; border-radius: 2px;
  background: rgba(255, 255, 255, 0.16);
  pointer-events: none;
}
.teachnav::after {
  content: ''; position: absolute; left: calc(50% - 1.2rem - 50%);
  top: 50%; margin-top: -1px;
  width: calc((100% + 2.4rem) * var(--sp, 0)); height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--blue) 55%, transparent), var(--blue));
  box-shadow: 0 0 10px 1px color-mix(in srgb, var(--blue) 70%, transparent);
  pointer-events: none;
  transform: translateX(0);
}
.teachdot { position: relative; z-index: 1; }

/* ---- and the picture comes up as you go ----
   The scrim is an overlay, so this can be moved freely; the film itself must
   NOT be transformed, because scrollfilm.js positions it by hand so the
   robot's open palm lands exactly on the ball. */
.slide.slide--teach .teach__scrim { opacity: calc(1 - var(--sp, 0) * 0.28); }
.slide.slide--teach .teach__bg {
  filter: brightness(calc(0.46 + var(--sp, 0) * 0.26)) contrast(1.05) saturate(1.1);
}

@media (prefers-reduced-motion: reduce) {
  .teach__pin .teach3__col { transition: opacity 0.2s linear; transform: none; }
  .teach__pin .teach3__col.is-on { transform: none; }
}

/* A SHORT LAPTOP, STAGE 3.
   The three promises are the tallest stage, and their height comes from how
   much the text WRAPS — which is a function of width, while the room they
   have is a function of height. On a 1280x700 window the cards came to 444px
   in a space of 330 and ran over the three stage dots. So on a short window
   the cards give up the padding and a couple of points of type; they are
   still the same three promises, just set tighter. */
@media (max-height: 800px) {
  .teach__pin .teach3__res { padding: 0.55rem 0.7rem; }
  .teach__pin .teach3__res .teach3__layer { font-size: 0.55rem; letter-spacing: 0.1em; }
  .teach__pin .teach3__promise { font-size: 0.88rem; }
  .teach__pin .teach3__pline { font-size: 0.66rem; margin: 0.15rem 0 0.35rem; }
  .teach__pin .teach3__proof { padding-top: 0.35rem; gap: 0.35rem; }
  .teach__pin .teach3__res em { font-size: 0.7rem; line-height: 1.3; }
  /* and the heading gives up its gap, since every stage starts at the same
     line now and the saving applies to all three */
  .teach__pin .teach3__step { margin-top: 0.6rem; }
}

/* And the heading gives up some of its own room on a short window, because
   that room is what stage 3 is short of. Every pixel taken off the top is a
   pixel the three promises get; the title is still the biggest thing on the
   screen, it is simply not enormous. */
@media (max-height: 800px) {
  .slide--teach .howhead { margin-bottom: 0.1rem; }
  .slide--teach .howhead .slide__title { font-size: clamp(1.5rem, 3.2vw, 2.3rem); margin-bottom: 0.15rem; }
  .slide--teach .howhead .orbit__hint { font-size: 0.8rem; }
  .slide--teach .domainchips { margin-top: 0.45rem; }
  .slide--teach .domainchips li { font-size: 0.68rem; padding: 0.28rem 0.66rem; }
  .slide.slide--teach.studio > .teach__pin,
  .slide.slide--teach > .teach__pin { padding-top: calc(var(--nav-disc) + 0.3rem); padding-bottom: 5.2rem; }
}

/* THE PROMISES MUST NOT CHANGE HEIGHT WHILE YOU LOOK AT THEM.
   The chapter proving each promise rotates — teachresults.js swaps the name
   and the line under it every few seconds — and those lines are different
   lengths, so the card grew and shrank by a line as they came round. On a
   short window that was the difference between stage 3 clearing the three
   stage dots and running under them, and which one you got depended on when
   you happened to arrive. So the line gets a fixed three-line box: the card
   is the same height whichever chapter is in it. */
.teach3__res em {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
  min-height: calc(3 * 1.35em);
}
/* and the row above it, for the same reason: the chapter name and its badge
   sit on one line for NEUCity and wrap onto two for a longer one, which moved
   the bottom of the card by exactly the amount that decided whether it met
   the dots. Two lines are reserved whether they are used or not. */
.teach3__proof { min-height: calc(2 * 1.55em); align-content: flex-start; }

/* A 700px-TALL WINDOW is the tightest this section is ever asked to work in:
   a 1280x700 laptop with the browser's own bars taken off the top. One more
   notch off the three promises, and the stage dots move down into the padding
   they were already sitting in, so stage 3 finishes a clear margin above them
   rather than a pixel or two. */
@media (max-height: 730px) {
  .teach__pin .teach3__promise { font-size: 0.82rem; }
  .teach__pin .teach3__res em { font-size: 0.66rem; }
  .teach__pin .teach3__pline { font-size: 0.63rem; }
  .slide.slide--teach.studio > .teach__pin,
  .slide.slide--teach > .teach__pin { padding-bottom: 4.4rem; }
  .slide--teach .teachnav { bottom: 1.2rem; }
}

/* ── AND IT ZOOMS, so nobody can miss that the page is moving ──
   Brightening the picture was not enough: the film is five slow seconds and
   half a percent of change per scroll wheel notch is not change anybody sees.
   It pushes in by a fifth across the section now, which is unmistakable.

   THE ORIGIN IS THE PALM, and that is the whole trick. scrollfilm.js slides
   this band by hand so the robot's open palm lands exactly on the live NEUON
   ball — the two layers only look like one because of that. Scale about any
   other point and the palm walks off the ball as you scroll. Scale about the
   palm itself and the palm cannot move: everything else grows around the one
   point that has to stay still. The two numbers are the same two the markup
   gives the script (data-scrollfilm-palm-x / -palm), so if the film is ever
   replaced, all three change together.

   Safe to transform because align() measures the PIN and the ANCHOR and works
   from the video's natural size — it never reads this element's own box, so a
   transform here cannot feed back into the arithmetic that placed it. */
.slide.slide--teach .teach__bg {
  transform: scale(calc(1 + var(--sp, 0) * 0.2));
  transform-origin: 54% 78.5%;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .slide.slide--teach .teach__bg { transform: none; }
}

/* ================= ONE RING, EIGHT TIMES =================
   Taufik: "make all the cards the same size and view angle as the Starter
   Pack, because the spinning cards are all a different style."

   They were nearly all the same already — the six `ocard` rings share a
   radius, a tilt and a width. Two things were out:

     · the CARD SHAPE. A pack is 2:3 and an ocard came out 3:4, so the same
       128px-wide card was 192px tall on one ring and 171px on the next. Side
       by side in a deck that is a different card, not a different picture.
     · the ARCADE RING had its own everything — a wider card, a much rounder
       ellipse (118 against 50) and a higher minimum scale, so the eight games
       were seen from a different angle from every other ring on the site.

   The Starter Pack is the reference because it is the one Dr KC signed off,
   so the numbers here are its numbers. Written once, at the end, so the next
   person looking for "why do the rings look the same" finds one block rather
   than seven. */

/* THE SHAPE IS SET ON THE NODE, not the card — that is where the hour went.
   `.orbit__node--card` carries `aspect-ratio: 3/4` and the card fills it, so
   setting 2:3 on the card changed nothing at all: the node was still 171px
   tall and the card still filled 171px. The pack's node has no aspect-ratio
   and simply takes its card's height, which is why it was the odd one out. */
.orbit .orbit__node--card { aspect-ratio: 2 / 3; }

/* THE CARD IS 2:3, like a pack. The picture keeps its own 4:3 window inside
   it — cropping a photograph to 2:3 would cut heads off — and the words take
   what is left, which is what gives an ocard the extra height. */
.orbit .ocard { aspect-ratio: 2 / 3; display: flex; flex-direction: column; }
.orbit .ocard .ocard__media { flex: none; }
.orbit .ocard .ocard__body { flex: 1 1 auto; min-height: 0; overflow: hidden; }
/* a card with no picture gives the words the whole of it */
.orbit .ocard--plain .ocard__body,
.orbit .ocard--letter .ocard__body { display: flex; flex-direction: column; justify-content: center; }

/* THE ARCADE RING JOINS THE OTHERS: same ellipse, same tilt, same minimum
   scale, same card width. It had a rounder orbit and a bigger card so the
   eight games would cluster — which read as a different piece of furniture
   in the middle of a page of rings. */
.orbit--game { --orbit-rx: 330; --orbit-ry: 50; --orbit-min-scale: 0.34; }
@media (max-width: 820px) {
  .orbit--game { --orbit-rx: 112; --orbit-ry: 38; --orbit-min-scale: 0.34; }
}
.orbit__node--game { width: var(--ring-card) !important; }
.orbit--game .gcard { aspect-ratio: 2 / 3; height: auto; }

/* ── G.R.A.I.N.: the letter rides ON the picture ──
   The letter used to be the whole card, centred over nothing. Now there is a
   picture under it, so the letter becomes a badge in the corner of it — still
   the thing that says which value this is, no longer the only thing there. */
.ocard--letter .ocard__media { position: relative; }
.ocard--letter .ocard__media img { object-fit: cover; }
.ocard--letter .ocard__letter {
  position: absolute; top: 0.5rem; left: 0.5rem; margin: 0; z-index: 2;
  box-shadow: 0 6px 16px -6px rgba(10, 2, 3, 0.55);
}

/* where a story was published, at the foot of its card in the gallery */
.galcard__source { margin: 0.5rem 0 0; font-size: 0.82rem; }
.galcard__source a { color: var(--blue); font-weight: 700; text-decoration: none; }
.galcard__source a:hover { text-decoration: underline; }

/* ── THE START FRAME: the strip sits along the BOTTOM ──
   Dr KC's mark-up of Section 1: headline high, film strip flush along the
   foot of the screen, the picture between them. It was floating in the middle
   of the frame with a hundred pixels of nothing under it, which reads as a
   row that stopped somewhere rather than a strip running along the bottom.

   `margin-top: auto` in the pinned column is what does it — the headline and
   the kicker keep their place at the top and every pixel that is left over
   goes above the strip instead of being shared out. The pin's own bottom
   padding comes down to a hair, because the strip is meant to touch. */
.slide--hero .reel {
  margin-top: auto;
  margin-bottom: 0;
}
.hero__pin { padding-bottom: 0.9rem; }

/* THE STRIP AND THE SIGN-UP BAR SHARE THE FOOT OF THE FRAME, and it was worth
   checking before assuming they fight. They do not: the bar is hidden and
   untabbable until the ball lands (`.is-landed`), by which time the strip has
   faded, and the bar sits two layers above it anyway. Measured with
   elementFromPoint at three points through the flight — the input is what
   answers a press every time it is meant to. No rule needed here; this note
   is so nobody adds one. */

/* ================= DR KC'S STRIP: SMALLER BOXES, ROOM IN THE MIDDLE =========
   His mark-up measures: the cards are about 18.6% of the screen's width — five
   and a half across — not two and a bit. At 640px they filled the middle of
   the frame; the picture between the headline and the strip, which is the
   whole point of the start frame, had nowhere to be.

   SIZED FROM THE WIDTH, not the height, because that is what the mark-up is a
   proportion of: 18.6vw is five and a half across at 1440 and five and a half
   across at 1920. The card stays 16:9 — these are film stills and cropping
   them square would cut the subject out of every one.

   The speed does not need touching: reel.js works the duration out from the
   track's own width, so smaller cards mean a shorter lap and the strip carries
   on at the same 85 pixels a second. That is what a constant is for. */
.reel--desk .rcard--film {
  width: clamp(11rem, 18.6vw, 22rem) !important;
  height: auto !important;
  aspect-ratio: 16 / 9;
}
/* the caption comes down with the card — 1rem on a 268px still was a headline */
.reel--desk .rcard__tag { font-size: 0.74rem; padding: 1.9rem 0.7rem 0.6rem; }

/* ---- A PHONE DOES NOT GET THE STRIP. REVERTED, ON PURPOSE. ----
   The strip was put on a phone here — two lines of 40vw cards along the foot,
   with the chapter pager moved into the flow above them to make room. It was
   wrong on a phone and it is gone: below 821px the rule about seven hundred
   lines up stands unopposed again —

       .reel--mob, .slide--hero .reel { display: none; }

   and Section 1 on a phone is back to the arrangement it had: headline, ball,
   the ball's two film windows with their arrows, the chapter pager where it
   has always been, the sign-up bar. That arrangement is Dr KC's own, and the
   two windows are how a film is reached on a phone — the strip was a second
   way to the same seven films, at a size where the caption on a 156px card is
   most of the card.

   The pager goes back to being absolutely positioned with it. It was only put
   into the flow because the strip had landed across it; with the strip gone,
   the reason goes too, and leaving the pager in the flow would be a fix for a
   problem that no longer exists.

   NOTHING HERE TOUCHES THE OTHER TWO SCREENS. The laptop's one line of 18.6vw
   cards is unchanged, and so is the NEUONBOX's two lines of 22vw — that panel
   is picked out by `min-width: 821px`, so it never saw a rule in this block.
   The `.reel--mob` markup stays two bands because the NEUONBOX renders it. */

/* ================= ONE LINE ACROSS A LANDSCAPE SCREEN =====================
   "YOUR WORLD, UNDERSTOOD BY AI." is the sentence the company leads with, and
   across a wide screen it should read as one sentence rather than as two
   stacked halves. On a phone and on the NEUONBOX it still breaks after the
   comma — a portrait screen has no room for it whole.

   THE BREAK IS SUPPRESSED, NOT REMOVED. `twoLines()` in index.ejs puts a real
   <br> in the markup, and it stays there for every screen; this hides it on a
   landscape one. Doing it in CSS rather than in the template is what lets the
   same page serve all three shapes, and it leaves the hand-escaping in that
   function — the reason the tag exists at all — completely alone.

   THE SIZE IS THE WHOLE PROBLEM, and it is arithmetic rather than taste.
   Measured in the browser: this sentence, uppercase, Inter 700 at -0.01em, is
   17.27 times the font size wide on one line. So

       one line fits  <=>  font-size x 17.27  <=  the room there is

   The pin gives its children 88vw at the widths where its padding is 6vw,
   which is the tightest case. 4.9vw x 17.27 = 84.6vw, so the sentence fits
   with about 3vw to spare at every width in that band — and 4.9vw is under
   the 5vw it was already being sized at, so the headline does not shrink
   noticeably to buy the line. The 3.8rem ceiling is unchanged; at the width
   where the ceiling takes over (~1240px) the line is 1050px against 1091px of
   room, the tightest point on the curve, and it clears.

   NO `white-space: nowrap`, deliberately. If a fallback font is ever wider
   than Inter — Arial Bold is — nowrap would push the sentence out of a pin
   that clips its overflow, and the end of it would be cut off. Left to wrap,
   the worst that can happen is the two lines it has today. A fallback that
   degrades to the previous design beats one that loses words.

   Not below 821px, and not in portrait: a phone keeps its two lines, and so
   does the standing panel, which is 1080 wide and would have to set this
   sentence at 62px to fill itself on one line. */
@media (orientation: landscape) and (min-width: 821px) {
  .slide--hero .herotitle br { display: none; }
  .slide--hero .herotitle {
    /* 100%, not a vw figure: the pin's padding is already taken out of it, so
       the box can never be wider than the room it is standing in.

       BOTH width AND max-width, and the second one is the one that mattered.
       `.herotitle` carries `max-width: min(46rem, 90vw)` — 736px — which held
       the box at two-line size no matter what width was asked for, so hiding
       the break alone turned the sentence into THREE ragged lines instead of
       one. A width that a max-width is quietly overruling looks exactly like
       a rule that did not apply. */
    width: min(70rem, 100%);
    max-width: min(70rem, 100%);
    font-size: clamp(2.1rem, 4.9vw, 3.8rem);
  }
}

/* A SHORT LANDSCAPE WINDOW STILL GETS THE SMALLER HEADLINE.
   There is a rule seven hundred lines up that takes the headline down on a
   window under 780px tall, so the film strip has somewhere to be. The block
   above is later in the file and the same specificity, so without this it
   would quietly undo that rule and the strip would lose its room — the exact
   fault the earlier rule was written to fix.

   It is repeated here rather than moved, because the two are answers to
   different questions: that one is about height, this one is about a
   sentence. One line is still comfortable at this size — 4.2vw x 17.27 is
   72.5vw against 88vw of room. */
@media (orientation: landscape) and (min-width: 821px) and (max-height: 780px) {
  .slide--hero .herotitle { font-size: clamp(1.6rem, 4.2vw, 2.6rem); }
}
