/* ==========================================================================
   Organization Presentation Portal — design system
   Palette is fixed: shared neutrals below, brand values injected per org
   by src/utils/theme.js. No colour outside the two approved palettes.
   ========================================================================== */

/* --------------------------------------------------------------- typography
   Oswald for display, Open Sans for text - the pairing the user supplied. Both are
   bundled as woff2 under /fonts and neither is fetched at run time: there is no
   network at presentation time, so a webfont link would leave the deck in a fallback
   face on the night.

   font-display is block rather than swap. A projector deck is read once, from the back
   of a room; a flash of Arial reflowing into Oswald mid-sentence is worse than a few
   hundred milliseconds of nothing, and these are 12-19kB each off local disk.

   Oswald is condensed with real weights, so unlike the face this replaced it can carry
   a genuine 600 - no synthesised bold, and headings can go back to a heavier setting
   where the design wants one.

   Agency FB is declared below, used by exactly one thing: the CEO's name on the
   Leadership hero, drawn as outlined SVG letterforms tuned to that face. */
@font-face {
  font-family: 'Oswald';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('/fonts/Oswald-400.woff2') format('woff2');
}

@font-face {
  font-family: 'Oswald';
  font-style: normal;
  font-weight: 500 700;
  font-display: block;
  src: url('/fonts/Oswald-600.woff2') format('woff2');
}

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('/fonts/OpenSans-400.woff2') format('woff2');
}

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url('/fonts/OpenSans-500.woff2') format('woff2');
}

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 600;
  font-display: block;
  src: url('/fonts/OpenSans-600.woff2') format('woff2');
}

@font-face {
  font-family: 'Open Sans';
  font-style: normal;
  font-weight: 700 900;
  font-display: block;
  src: url('/fonts/OpenSans-700.woff2') format('woff2');
}

/* Kept for the CEO's name only — see .leader-hero__draw text. */
@font-face {
  font-family: 'Agency FB';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: local('Agency FB'), url('/fonts/AgencyFB-Regular.ttf') format('truetype');
}

@font-face {
  font-family: 'Agency FB';
  font-style: normal;
  font-weight: 700 900;
  font-display: block;
  src: local('Agency FB Bold'), local('Agency FB'), url('/fonts/AgencyFB-Bold.ttf') format('truetype');
}

:root {
  /* Shared neutrals */
  --page-bg: #f8fafc;
  --surface: #ffffff;
  --text: #111827;
  --muted: #4b5563;
  --border: #e5e7eb;
  --success: #16a34a;
  --error: #dc2626;

  /* Brand tokens — overwritten at runtime per organization */
  --brand-primary: #000000;
  --brand-secondary: #e95a22;
  --brand-accent: #e95a22;
  --brand-highlight: #f45c23;
  --brand-on: #ffffff;
  /* The accent darkened enough to read as text on white — see theme.js. */
  --accent-ink: #ba481b;
  --nav-bg: #000000;
  --nav-text: #ffffff;
  --on-accent: #ffffff;
  --on-primary: #ffffff;
  --accent-soft: rgba(233, 90, 34, 0.12);
  --accent-line: rgba(233, 90, 34, 0.32);
  --primary-soft: rgba(0, 0, 0, 0.06);
  --nav-hover: rgba(255, 255, 255, 0.1);
  --nav-line: rgba(255, 255, 255, 0.14);
  --nav-dim: rgba(255, 255, 255, 0.68);

  /* Shape & depth — kept deliberately light for a calm, professional surface */
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;
  --shadow-sm: 0 1px 3px rgba(17, 24, 39, 0.04), 0 1px 2px rgba(17, 24, 39, 0.02);
  --shadow-md: 0 4px 14px rgba(17, 24, 39, 0.06), 0 1px 4px rgba(17, 24, 39, 0.03);
  --shadow-lg: 0 18px 40px rgba(17, 24, 39, 0.1), 0 4px 12px rgba(17, 24, 39, 0.04);
  --gap: 18px;
  /* Text and display, the two faces the whole deck uses. Fallbacks are the nearest
     shapes available without a download, so a machine missing the woff2 still lays
     out at roughly the same width. */
  /* What the player bar takes out of the bottom-right corner: its own width, the
     inset it sits at, and the gap between the two bars. The presenter bar subtracts
     this when it centres, so the two sit side by side rather than on each other. */
  --player-reserve: 246px;
  --font: 'Open Sans', 'Segoe UI', Arial, sans-serif;
  --display: 'Oswald', 'Arial Narrow', 'Segoe UI', sans-serif;
  /* Montserrat is wider and taller in the x-height than the condensed face this
     replaced, so body copy no longer needs the nudge it used to get. */
  --text-scale: 1;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

/* Headings take the display face, body copy the text face — the pairing as supplied.
   Twenty-six rules already named the display face directly; everything else inherited
   from body, which meant most slide headings were setting in the text face. A semantic
   heading is display type by definition, so they are claimed here rather than one
   component at a time.

   Bebas Neue is caps-only and tightly set, so it needs the tracking opened up and the
   line height pulled in; at its default both a two-line title and a wide one look
   wrong. It also has no bold, hence the flat weight: asking for 700 gets a synthesised
   smear on some machines. */
h1, h2, h3 {
  font-family: var(--display);
  /* Oswald has real weights, so this is a drawn 600 rather than the synthesised bold
     the previous face was being asked for. */
  font-weight: 600;
  letter-spacing: 0.008em;
  line-height: 1.08;
}

body {
  font-family: var(--font);
  background: var(--page-bg);
  color: var(--text);
  font-size: 17.5px;
  line-height: 1.6;
  letter-spacing: 0.006em;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

button {
  font: inherit;
  cursor: pointer;
}

a {
  color: var(--accent-ink, var(--brand-accent));
}

.boot {
  display: grid;
  place-items: center;
  min-height: 100vh;
  color: var(--muted);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.2;
  text-decoration: none;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
  white-space: nowrap;
}

.btn:active {
  transform: translateY(1px);
}

.btn--primary {
  background: var(--brand-accent);
  color: var(--on-accent);
  box-shadow: var(--shadow-sm);
}

.btn--primary:hover {
  box-shadow: var(--shadow-md);
}

.btn--dark {
  background: var(--brand-primary);
  color: var(--on-primary);
}

.btn--ghost {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
}

.btn--ghost:hover {
  border-color: var(--brand-accent);
  color: var(--accent-ink, var(--brand-accent));
}

.btn--quiet {
  background: transparent;
  color: var(--muted);
  padding: 8px 12px;
}

.btn--quiet:hover {
  color: var(--accent-ink, var(--brand-accent));
}

.btn--danger {
  background: var(--surface);
  border-color: var(--border);
  color: var(--error);
}

.btn--danger:hover {
  background: var(--error);
  color: #ffffff;
  border-color: var(--error);
}

.btn--sm {
  padding: 7px 13px;
  font-size: 13px;
}

.btn--block {
  width: 100%;
}

.btn[disabled] {
  opacity: 0.55;
  pointer-events: none;
}

.btn--on-dark {
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.28);
}

.btn--on-dark:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* ---------------------------------------------------------------- inputs */
.field {
  display: grid;
  gap: 7px;
  margin-bottom: 14px;
}

.field__label {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.01em;
}

.input,
.textarea,
.select {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.textarea {
  min-height: 96px;
  resize: vertical;
  line-height: 1.6;
}

.input:focus,
.textarea:focus,
.select:focus {
  outline: none;
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 14px;
  color: var(--muted);
}

/* ---------------------------------------------------------------- login */
.login {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  min-height: 100vh;
}

.login__brand {
  background: var(--text);
  color: #ffffff;
  padding: 56px 60px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
  overflow: hidden;
}

.login__brand::after {
  content: "";
  position: absolute;
  right: -140px;
  bottom: -160px;
  width: 460px;
  height: 460px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
}

.login__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 12px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.72);
}

.login__headline {
  font-size: clamp(30px, 3.4vw, 46px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 20px 0 14px;
  max-width: 16em;
}

.login__sub {
  color: rgba(255, 255, 255, 0.76);
  max-width: 34em;
  font-size: 17px;
}

.login__orgs {
  display: grid;
  gap: 12px;
  position: relative;
  z-index: 1;
}

.login__org {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.14);
}

.login__swatch {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 15px;
}

.login__org-name {
  font-weight: 650;
}

.login__org-tag {
  font-size: 12.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}

.login__panel {
  display: grid;
  place-items: center;
  padding: 40px 32px;
  background: var(--page-bg);
}

.login__card {
  width: min(430px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 34px 32px;
}

.login__card h2 {
  margin: 0 0 6px;
  font-size: 25px;
  letter-spacing: -0.01em;
}

.login__card p.login__hint-text {
  margin: 0 0 22px;
  color: var(--muted);
  font-size: 14.5px;
}

.role-toggle {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 5px;
  background: var(--page-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  margin-bottom: 20px;
}

.role-toggle__btn {
  border: none;
  background: transparent;
  border-radius: 999px;
  padding: 9px 10px;
  font-weight: 600;
  font-size: 14px;
  color: var(--muted);
  transition: background 0.18s ease, color 0.18s ease;
}

.role-toggle__btn.is-active {
  background: var(--text);
  color: #ffffff;
}

.form-error {
  margin: 0 0 14px;
  padding: 10px 13px;
  border-radius: var(--radius-sm);
  background: rgba(220, 38, 38, 0.08);
  border: 1px solid rgba(220, 38, 38, 0.28);
  color: var(--error);
  font-size: 14px;
}

.login__creds {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px dashed var(--border);
  font-size: 13px;
  color: var(--muted);
}

.login__creds code {
  background: var(--page-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1px 6px;
  font-size: 12.5px;
}






/* Corner wedge only — kept clear of the logo and tagline so brand-coloured
   text never lands on a brand-coloured fill. */

/* The card is a brand fill, so a wordmark drawn for paper gets a white plate
   rather than being asked to survive on it. */





/* ==========================================================================
   App shell + navigation pane
   White is the surface. A row is a slide; the deck is flat. Selected turns the
   title and its mark brand green and shows a bar at the left edge — no fill, and
   nothing here uses a gradient. Hover is the only tint.
   Collapsed, the pane becomes an icon rail in the deepened brand colour: white
   glyphs, and the selected one in gold.
   ========================================================================== */
:root {
  --nav-w: 300px;
  --nav-rail-w: 78px;
  --nav-surface: #ffffff;
  --nav-sunken: #f4f2ee;
  --nav-line: #e6e2db;
  --nav-ink: #14161a;
  --nav-ink-soft: #3d434c;
  /* 4.6:1 on white — the counted labels are small caps, so they cannot be pale. */
  --nav-ink-quiet: #6f7583;
  --nav-rail-bg: #000000;
}

.shell {
  display: grid;
  grid-template-columns: var(--nav-w) minmax(0, 1fr);
  min-height: 100vh;
  transition: grid-template-columns 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

:root[data-nav-rail='1'] .shell {
  grid-template-columns: var(--nav-rail-w) minmax(0, 1fr);
}

/* Shared icon sizing — every glyph in the pane comes from utils/icons.js. */
.ic {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}

.ic--sm {
  width: 16px;
  height: 16px;
}

.ic--xs {
  width: 13px;
  height: 13px;
}

/* ------------------------------------------------------------ the panel */
.sidenav {
  position: sticky;
  top: 0;
  z-index: 30;
  height: 100vh;
  display: flex;
  flex-direction: column;
  /* Visible so the rail's subsection card can sit outside the 78px column. */
  overflow: visible;
  border-right: 1px solid var(--nav-line);
  background: var(--nav-surface);
  box-shadow: 6px 0 22px rgba(20, 22, 26, 0.05);
  color: var(--nav-ink);
}

/* -------------------------------------------------------------- brand row */
.sidenav__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 14px 10px;
}

.sidenav__logo {
  height: 38px;
  width: auto;
  max-width: 178px;
  object-fit: contain;
  flex: 0 0 auto;
}

.sidenav__name {
  flex: 1;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* Rail-only square mark: a wordmark is unreadable at 44px.

   No plate behind it. The artwork is a 512px PNG that is transparent everywhere
   except its strokes — nothing was ever sitting on white — so the white square
   was the plate alone, and on the black rail it read as a sticker rather than a
   logo. The dark green measures 4.1:1 against the rail. */
.sidenav__mark {
  display: none;
  width: 46px;
  height: 46px;
  object-fit: contain;
  background: transparent;
}

.sidenav__mark--text {
  place-items: center;
  border-radius: 13px;
  background: var(--nav-accent-ink, var(--accent-ink));
  color: #ffffff;
  font-size: 18px;
  font-weight: 800;
}

.sidenav__head-act,
.sidenav__collapse {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--nav-sunken);
  color: var(--nav-ink);
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

/* One auto margin, not two: with it on both, the free space split between them
   and the magnifier drifted into the middle of the head. */
.sidenav__head-act {
  margin-left: auto;
}

.sidenav__head-act:hover,
.sidenav__collapse:hover {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 18%, #ffffff);
  color: var(--nav-accent-ink, var(--accent-ink));
}

/* Signing out reads as an exit, not as another navigation control. */
.sidenav__head-act--exit {
  color: var(--nav-ink-soft);
}

.sidenav__head-act--exit:hover {
  background: color-mix(in srgb, var(--error) 12%, #ffffff);
  color: var(--error);
}

/* The rail keeps it. Leaving is the one thing still worth reaching there, and the
   pane it used to hide is no longer where it lives. */
.sidenav--rail .sidenav__head-act--exit {
  background: rgba(255, 255, 255, 0.1);
  color: #ffffff;
}

.sidenav--rail .sidenav__head-act--exit:hover {
  background: var(--error);
  color: #ffffff;
}


/* -------------------------------------------------------- who is signed in */






/* -------------------------------------------------------- counted labels */
.snav-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 18px 7px;
  color: var(--nav-ink-quiet);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.snav-label__text {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
  white-space: nowrap;
}

.snav-label__word {
  overflow: hidden;
  text-overflow: ellipsis;
}

.snav-label__count {
  color: var(--nav-ink);
  font-weight: 800;
}

.snav-label__act {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--nav-ink-quiet);
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.snav-label__act:hover {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 16%, #ffffff);
  color: var(--nav-accent-ink, var(--accent-ink));
}

/* ----------------------------------------------------------- scroll body */
.sidenav__scroll {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 10px 12px 14px;
  /* Invisible scroll: it still scrolls, it just does not draw a bar down the
     side of the pane. A projector deck should not show furniture. */
  scrollbar-width: none;
}

.sidenav__scroll::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.snav-search {
  display: none;
  grid-template-columns: 34px minmax(0, 1fr);
  align-items: center;
  margin: 0 0 8px;
  border-radius: 999px;
  background: var(--nav-sunken);
}

.snav-search.is-open {
  display: grid;
}

.snav-search__icon {
  width: 15px;
  height: 15px;
  justify-self: center;
  color: var(--nav-ink-quiet);
}

.snav-search__input {
  min-width: 0;
  padding: 9px 14px 9px 0;
  border: 0;
  background: transparent;
  color: var(--nav-ink);
  font: inherit;
  font-size: 13px;
  outline: none;
}

.snav-search__input::placeholder {
  color: var(--nav-ink-quiet);
}

/* ------------------------------------------------------ organization guide */
.nav-tree {
  display: flex;
  flex-direction: column;
  gap: 5px;
  /* Never shorter than the rows inside it. With `min-height: 0` the flex parent was
     free to squeeze this box below its content: the rows spilled out of it without
     scrolling anything, and the workspace card was then laid out over the top of
     them. Below 800px of viewport the card sat 51px above the last row and as much
     as 199px on a short window. min-content keeps the box honest, so the pane
     scrolls instead - invisibly, as everything here does. */
  min-height: min-content;
}

/* With nothing open the ten rows shared out the height rather than stacking at
   the top over a third of a pane of nothing. `:has` is what keeps this to the
   closed state: the moment a group opens, the tree goes back to sizing itself to
   its content and the pane scrolls, which is the behaviour the open state wants.

   Capped, because four sections would otherwise each be 180px tall; whatever the
   cap leaves over is dealt out as gaps rather than pooling at the bottom. */
.nav-tree:not(:has(.nav-tree__group.is-open)) {
  /* Grows to fill a tall pane, never shrinks on a short one. */
  flex: 1 0 auto;
  justify-content: space-between;
}

/* A column, emphatically. As a flex row the group laid its button out beside its
   own (closed, zero-height) subsection panel, so the two groups that have
   subsections stopped filling the width and their titles ellipsised to "Or..."
   and "Lea...". */
.nav-tree:not(:has(.nav-tree__group.is-open)) > .nav-tree__group {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 44px;
  max-height: 66px;
}

.nav-tree:not(:has(.nav-tree__group.is-open)) > .nav-tree__group > .nav-tree__parent {
  flex: 1 1 auto;
}

.nav-tree__group.is-filtered,
.nav-tree__child.is-filtered {
  display: none;
}

.nav-tree__parent,
.nav-tree__child {
  width: 100%;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* Pill rows: the shape carries the state, so nothing needs a border. */
.nav-tree__parent {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto;
  align-items: center;
  gap: 11px;
  min-height: 44px;
  padding: 9px 14px 9px 16px;
  border-radius: 999px;
  color: var(--nav-ink);
  font-size: 14px;
  font-weight: 600;
  transition: background 0.16s ease, color 0.16s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
}

.nav-tree__badge {
  display: grid;
  place-items: center;
  width: 23px;
  height: 23px;
  color: inherit;
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}

/* The supplied artwork, painted rather than drawn.

   `background: currentColor` through a mask is what makes one file serve every
   state: the same glyph is ink on a normal row, brand green on the selected one and
   white on the rail, with no second copy and no filter hacks. `contain` keeps the
   mixed viewBoxes (some are 24, some 32) at one visual size. */
.nav-glyph {
  display: block;
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}

/* One weight for the whole family. These fourteen carry real detail — a clasp, a
   lens, a flag — and 2.1 closed the gaps inside them into solid blobs; 1.7 is the
   library's own stroke, so the pane matches every other icon in the app. 21px
   rather than 19 because the rows are 47px tall and the extra two pixels are what
   let the busier glyphs hold together. */
.nav-tree__icon {
  width: 21px;
  height: 21px;
  stroke-width: 1.7;
}

.sec-icon {
  width: 19px;
  height: 19px;
}

.sec-icon--asset {
  width: 21px;
  height: 21px;
  object-fit: contain;
  border-radius: 5px;
}

.nav-tree__parent:hover {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 9%, #ffffff);
  transform: translateX(2px);
}

.nav-tree__parent:hover .nav-tree__badge {
  transform: scale(1.12);
}

.nav-tree__parent:active {
  transform: translateX(2px) scale(0.99);
}

/* :not() keeps the open-group tint from outranking the active fill. */
.nav-tree__group.is-open > .nav-tree__parent:not(.is-active) {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 10%, #ffffff);
}

/* Selected reads as colour, not as a fill: the title and the mark turn brand
   green and a short bar appears at the left edge. A solid pill on whichever row
   you were last on made the pane look like a list of buttons with one pressed,
   and it fought the row's own icon for attention. The bar is the quieter signal
   and it lines up down the whole pane. */
.nav-tree__parent.is-active {
  background: transparent;
  color: var(--nav-accent-ink, var(--accent-ink));
  font-weight: 700;
}

.nav-tree__parent.is-active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 3px;
  height: 56%;
  border-radius: 999px;
  background: var(--nav-accent, var(--brand-accent));
  transform: translateY(-50%);
}

/* The mark follows the title rather than staying ink-black, and grows a touch —
   at 19px a colour change alone is easy to miss. */
.nav-tree__parent.is-active .nav-tree__badge {
  transform: scale(1.08);
}

.nav-tree__title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-tree__flag {
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--nav-sunken);
  color: var(--nav-ink-quiet);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* No fill behind it any more, so the draft chip keeps its ordinary colours. */
.nav-tree__parent.is-active .nav-tree__flag {
  background: var(--nav-sunken);
  color: var(--nav-ink-quiet);
}

.nav-tree__chevron {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  color: var(--nav-ink-quiet);
  transition: transform 0.18s ease;
}

.nav-tree__parent.is-active .nav-tree__chevron {
  color: var(--nav-accent-ink, var(--accent-ink));
}

.nav-tree__group.is-open .nav-tree__chevron {
  transform: rotate(90deg);
}

/* Subsections read as one block under their parent, and they open by growing
   into place: a 0fr → 1fr grid row interpolates, where `display: none` cannot. */
.nav-tree__children {
  display: grid;
  grid-template-rows: 0fr;
  margin: 0;
  border-radius: 18px;
  background: var(--nav-sunken);
  opacity: 0;
  overflow: hidden;
  transition:
    grid-template-rows 0.3s cubic-bezier(0.32, 0.72, 0, 1),
    opacity 0.2s ease,
    margin 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}

/* The vertical padding belongs to the open state only. A 0fr track zeroes the
   rows inside it but not this element's own padding, so a closed panel still
   measured 14px and every group with subsections sat 14px lower than one
   without — the pitch down the pane visibly wandered. */
.nav-tree__children-inner {
  display: grid;
  gap: 2px;
  min-height: 0;
  padding: 0 8px 0 34px;
  transition: padding 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}

.nav-tree__group.is-open > .nav-tree__children {
  grid-template-rows: 1fr;
  margin: 3px 0 8px;
  opacity: 1;
}

.nav-tree__group.is-open > .nav-tree__children > .nav-tree__children-inner {
  padding-top: 6px;
  padding-bottom: 8px;
}

/* Each row arrives just after the one above it. */
.nav-tree__group.is-open > .nav-tree__children .nav-tree__child,
.nav-tree__group.is-open > .nav-tree__children .nav-tree__caption {
  animation: nav-row-in 0.34s cubic-bezier(0.32, 0.72, 0, 1) both;
  animation-delay: calc(var(--i, 0) * 26ms);
}

@keyframes nav-row-in {
  from {
    opacity: 0;
    transform: translateX(-8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.nav-tree__caption {
  padding: 8px 12px 3px;
  color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.nav-tree__child {
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr);
  align-items: center;
  gap: 9px;
  min-height: 30px;
  padding: 6px 12px;
  border-radius: 999px;
  color: var(--nav-ink-soft);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  transition: background 0.16s ease, color 0.16s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
}

.nav-tree__child:hover {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 12%, #ffffff);
  color: var(--nav-accent-ink, var(--accent-ink));
  transform: translateX(3px);
}

.nav-tree__child.is-active {
  background: var(--nav-accent-ink, var(--accent-ink));
  color: #ffffff;
  font-weight: 600;
}

/* A third level is rare; the rule keeps it legible without another indent step. */
.nav-tree__child--nested {
  margin-left: 12px;
  padding-left: 12px;
  border-left: 2px solid color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 34%, transparent);
  border-radius: 0 999px 999px 0;
  font-size: 13px;
}

.nav-tree__child-icon {
  width: 13px;
  height: 13px;
  stroke-width: 2.6;
}

.nav-tree__child-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* -------------------------------------------------- workspace destinations */
/* The tree above stretches to fill the pane, so without a margin here the card's
   ground sits flush against the last row and the two read as one overlapping
   thing. The tree gives the space back by shrinking, not by scrolling. */
.snav-card {
  display: grid;
  gap: 2px;
  flex: 0 0 auto;
  margin-top: 14px;
  padding: 6px;
  border-radius: 18px;
  background: var(--nav-sunken);
}

.snav-card__row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  align-items: center;
  gap: 11px;
  min-height: 38px;
  padding: 4px 8px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--nav-ink);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.snav-card__row:hover {
  background: #ffffff;
}

.snav-card__mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: #ffffff;
  color: var(--nav-accent-ink, var(--accent-ink));
}

.snav-card__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.snav-card__row--exit {
  color: var(--nav-ink-soft);
}

.snav-card__row--exit .snav-card__mark {
  color: var(--nav-ink-soft);
}

.snav-card__row--exit:hover {
  background: color-mix(in srgb, var(--error) 10%, #ffffff);
  color: var(--error);
}

.snav-card__row--exit:hover .snav-card__mark {
  color: var(--error);
}

.snav-card__row--add {
  margin-top: 3px;
  padding-top: 7px;
  border-top: 1px dashed var(--nav-line);
  border-radius: 999px;
}

.snav-card__mark--add {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 14%, #ffffff);
}

.snav-card__row--add:hover .snav-card__mark--add {
  background: var(--nav-accent, var(--brand-accent));
  color: var(--nav-on-accent, var(--on-accent));
}

/* ================================================================= the rail
   Black, 78px, icons only: the brand mark and every section within one click.
   Labels, counts and cards all belong to the open pane. */
.snav-rail__list {
  display: none;
}

.snav-flyout {
  position: absolute;
  left: calc(100% + 10px);
  z-index: 45;
  width: 236px;
  padding: 8px;
  border-radius: 16px;
  background: var(--nav-surface);
  box-shadow: 0 18px 44px rgba(20, 22, 26, 0.22);
}

/* The rail's label: brand-coloured, arrowed, and never the grey OS tooltip. */
.snav-tip {
  position: absolute;
  left: calc(100% + 10px);
  z-index: 46;
  padding: 7px 12px;
  border-radius: 10px;
  background: var(--nav-accent-ink, var(--accent-ink));
  box-shadow: 0 10px 26px rgba(20, 22, 26, 0.24);
  color: #ffffff;
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: 0.01em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-6px, -50%);
  transition: opacity 0.16s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
}

.snav-tip::before {
  content: '';
  position: absolute;
  top: 50%;
  right: 100%;
  width: 8px;
  height: 8px;
  margin-top: -4px;
  border-radius: 2px 0 0 0;
  background: inherit;
  transform: translateX(4px) rotate(45deg);
}

.snav-tip.is-on {
  opacity: 1;
  transform: translate(0, -50%);
}

.snav-tip[hidden] {
  display: none;
}

.snav-flyout[hidden] {
  display: none;
}

.snav-flyout:not([hidden]) {
  animation: nav-flyout-in 0.24s cubic-bezier(0.32, 0.72, 0, 1) both;
}

@keyframes nav-flyout-in {
  from {
    opacity: 0;
    transform: translateX(-10px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.snav-flyout__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 4px 6px 12px;
}

.snav-flyout__title {
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--nav-ink);
  font: inherit;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-align: left;
  text-transform: uppercase;
  cursor: pointer;
}

.snav-flyout__title:hover {
  color: var(--nav-accent-ink, var(--accent-ink));
}

.snav-flyout__close {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--nav-sunken);
  color: var(--nav-ink);
  cursor: pointer;
}

.snav-flyout__close:hover {
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 18%, #ffffff);
  color: var(--nav-accent-ink, var(--accent-ink));
}

.snav-flyout__list {
  display: grid;
  gap: 2px;
  max-height: min(420px, 60vh);
  overflow-y: auto;
}

.sidenav--rail {
  align-items: center;
  padding: 0;
  border-right-color: transparent;
  background: var(--nav-rail-bg);
  box-shadow: none;
}

.sidenav--rail .sidenav__head {
  flex-direction: column;
  gap: 12px;
  padding: 14px 0 10px;
}

.sidenav--rail .sidenav__logo,
.sidenav--rail .sidenav__name,
.sidenav--rail .snav-label,
.sidenav--rail .sidenav__scroll {
  display: none;
}

.sidenav--rail .sidenav__mark {
  display: grid;
}

.sidenav--rail .sidenav__collapse {
  margin: 0;
  background: rgba(255, 255, 255, 0.1);
  color: #ffffff;
}

.sidenav--rail .sidenav__collapse:hover {
  background: var(--nav-rail-active, var(--nav-second, var(--brand-accent)));
  color: #14161a;
}

.sidenav--rail .snav-rail__list {
  display: flex;
  flex: 1;
  min-height: 0;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 6px 0 10px;
  overflow-y: auto;
  scrollbar-width: none;
}

.sidenav--rail .snav-rail__list::-webkit-scrollbar {
  width: 0;
  display: none;
}

.snav-rail__btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: #ffffff;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
}

.snav-rail__btn:hover {
  background: rgba(255, 255, 255, 0.16);
  transform: scale(1.08);
}

/* Gold, not a filled disc. The rail ground is already the brand green, so a green
   pill on it disappeared; the selected glyph changing colour is the whole signal,
   with the same left bar the open pane uses so the two states agree. */
.snav-rail__btn.is-active {
  background: transparent;
  color: var(--nav-rail-active, var(--nav-second, var(--brand-accent)));
}

.snav-rail__btn.is-active::before {
  content: '';
  position: absolute;
  left: -13px;
  top: 50%;
  width: 3px;
  height: 22px;
  border-radius: 999px;
  background: var(--nav-rail-active, var(--nav-second, var(--brand-accent)));
  transform: translateY(-50%);
}

.snav-rail__icon {
  width: 21px;
  height: 21px;
  stroke-width: 1.7;
}

.snav-rail__icon.sec-icon--asset {
  width: 21px;
  height: 21px;
  padding: 2px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.92);
}

/* ------------------------------------------------------------- themed tooltip
   A pill in the brand colour with white type and a caret, to the design supplied.
   One node, parented to <body> by utils/tooltip.js and positioned per hover, which is
   what lets it work over a scrolling pane, over FitSlide's transform, and on elements
   that cannot carry a pseudo-element at all.

   Two motions, as asked: it pops in - scale with a little overshoot, so it arrives
   rather than fades - and then floats, a 2px rise and fall that keeps it feeling live
   without pulling the eye off the thing it is labelling. Both off under
   prefers-reduced-motion.

   Fill is `--nav-accent` with `--nav-on-accent` on it, the pair the theme computes for
   exactly this: green with white here, orange with white for Torii. Never the raw
   accent as text, which is the rule the rest of this file keeps. */
.tipx {
  position: fixed;
  z-index: 90;
  max-width: 280px;
  padding: 8px 15px;
  border-radius: 999px;
  background: var(--nav-accent, var(--brand-accent));
  box-shadow: 0 10px 26px rgba(20, 22, 26, 0.24);
  color: var(--nav-on-accent, var(--on-accent));
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: 0.01em;
  line-height: 1.2;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.92);
  transform-origin: var(--tip-caret, 50%) 100%;
}

.tipx[hidden] {
  display: none;
}

/* The caret, sitting on whichever edge faces the control, at the control's centre
   rather than the pill's - the pill slides sideways to stay on screen. */
.tipx::after {
  content: '';
  position: absolute;
  left: var(--tip-caret, 50%);
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: inherit;
  transform: translateX(-50%) rotate(45deg);
}

.tipx[data-side='top']::after {
  bottom: -4px;
}

.tipx[data-side='bottom']::after {
  top: -4px;
}

.tipx[data-side='bottom'] {
  transform-origin: var(--tip-caret, 50%) 0;
}

.tipx.is-on {
  animation:
    tip-pop 0.24s cubic-bezier(0.34, 1.56, 0.64, 1) both,
    tip-float 2.8s 0.24s ease-in-out infinite;
}

@keyframes tip-pop {
  from {
    opacity: 0;
    transform: scale(0.88);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes tip-float {
  0%, 100% {
    transform: translateY(0) scale(1);
  }
  50% {
    transform: translateY(-2.5px) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .tipx.is-on {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* The rail had a tooltip of its own before this one existed. Same pill, so the product
   has one tooltip design rather than two that nearly match. */
.snav-tip {
  border-radius: 999px;
  background: var(--nav-accent, var(--brand-accent));
  color: var(--nav-on-accent, var(--on-accent));
}

/* An icon on its own needs a square, not a pill sized for a word. */
.btn--icon {
  display: grid;
  place-items: center;
  width: 30px;
  padding: 0;
}

/* --------------------------------------------------------------- topbar */
.main {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 32px;
  background: rgba(248, 250, 252, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

/* Collapsed, every row this bar takes comes off the slide. The pane is put on
   its rail to get out of the way, so the bar gets out of the way with it:
   shallower, smaller type, smaller controls — and `nowrap`, because the whole
   point is a bar that cannot grow a second line. Measured, it goes from 64px
   to 40px, and those 24 rows go to the picture. The pane open is the working
   view and the bar keeps its full size there. */
:root[data-nav-rail='1'] .topbar {
  gap: 10px;
  padding: 6px 16px;
  flex-wrap: nowrap;
}

/* Collapsed, the bar FLOATS over the slide instead of taking a row of its own,
   and that is what actually removes the bars down the sides — not the smaller
   type, which only helps a little.

   The arithmetic, measured at 1920x1000: the stage is 1842 wide, and a slide
   fills only when its 860 nominal rows fit `1600 x height / width`. With a
   40px bar in the flow the stage is 960 tall, which allows 834 rows — 26 short,
   so FitSlide declined to fill and left 61px down each side rather than lose
   the bottom of the slide. Floating the bar gives the stage the whole 1000,
   which allows 869, and the slide fills edge to edge with nothing cropped.

   It is over the picture, so it carries its own scrim and lifts on hover, the
   way the film players in this deck do. The pane open is the working view and
   the bar stays in the flow there, where those rows cost nothing. */
:root[data-nav-rail='1'] .shell--fit .main {
  position: relative;
}

:root[data-nav-rail='1'] .shell--fit .topbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  border-bottom: 0;
  /* NO ground at rest. The scrim is the deck's own near-white, which is
     invisible over a white slide and a grey band across a black one — and the
     first slide of this deck is a black film. The controls carry their own
     backgrounds, so they stay readable on either; only the breadcrumb fades
     into a dark slide, and it is the one thing here nobody needs to read. */
  background: transparent;
  backdrop-filter: none;
  opacity: 0.5;
  transition: opacity 0.22s ease, background 0.22s ease;
}

/* Reached for: the ground comes up with it, so the crumbs can be read. */
:root[data-nav-rail='1'] .shell--fit .topbar:hover,
:root[data-nav-rail='1'] .shell--fit .topbar:focus-within {
  opacity: 1;
  background: linear-gradient(
    to bottom,
    rgba(248, 250, 252, 0.94),
    rgba(248, 250, 252, 0.6) 64%,
    rgba(248, 250, 252, 0)
  );
  backdrop-filter: blur(6px);
}

@media (prefers-reduced-motion: reduce) {
  :root[data-nav-rail='1'] .shell--fit .topbar { transition: none; }
}

:root[data-nav-rail='1'] .topbar__crumbs {
  font-size: 12px;
}

:root[data-nav-rail='1'] .topbar .btn {
  padding: 5px 11px;
  font-size: 12.5px;
}

:root[data-nav-rail='1'] .topbar .badge,
:root[data-nav-rail='1'] .topbar .tag {
  font-size: 10.5px;
  padding: 2px 7px;
}

.topbar__crumbs {
  font-size: 13.5px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.topbar__crumbs strong {
  color: var(--text);
}

.topbar__spacer {
  flex: 1;
}

.topbar__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--muted);
}

.badge--accent {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent-ink, var(--brand-accent));
}

.badge--draft {
  background: rgba(75, 85, 99, 0.08);
  color: var(--muted);
}

.badge--hidden {
  background: rgba(220, 38, 38, 0.08);
  border-color: rgba(220, 38, 38, 0.24);
  color: var(--error);
}

.badge--live {
  background: rgba(22, 163, 74, 0.1);
  border-color: rgba(22, 163, 74, 0.3);
  color: var(--success);
}

/* ---------------------------------------------------------------- slide */
.stage {
  padding: 28px 32px 80px;
  flex: 1;
}

.slide {
  max-width: 1260px;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow:
    0 1px 3px rgba(17, 24, 39, 0.04),
    0 8px 24px rgba(17, 24, 39, 0.06),
    0 20px 48px -12px rgba(17, 24, 39, 0.06);
  padding: 48px 52px 56px;
  animation: slide-in 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  position: relative;
  overflow: hidden;
}

/* Top accent line — refined and minimal */
.slide::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(
    90deg,
    var(--brand-primary) 0%,
    var(--brand-accent) 50%,
    var(--brand-primary) 100%
  );
}

@keyframes slide-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.slide__head {
  margin-bottom: 36px;
  position: relative;
}

/* The head opens the slide: kicker, title, then the rule drawing itself in. */
.slide__kicker {
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--brand-accent);
  animation: fade-up 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* Kicker accent line */
.slide__kicker::before {
  content: "";
  width: 28px;
  height: 2px;
  background: var(--brand-accent);
  border-radius: 99px;
  animation: scale-x-in 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) 0.1s both;
  transform-origin: left;
  flex-shrink: 0;
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@keyframes scale-x-in {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.slide__title {
  font-size: clamp(32px, 3.2vw, 48px);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-weight: 700;
  margin: 10px 0 0;
  color: var(--text);
  animation: fade-up 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) 0.06s both;
}

.slide__rule {
  width: 56px;
  height: 4px;
  border-radius: 99px;
  background: var(--brand-accent);
  margin-top: 18px;
  transform-origin: left center;
  animation: scale-x-in 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) 0.14s both;
}

.slide__body {
  display: grid;
  gap: 26px;
}

.block {
  animation: fade-up 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}

@keyframes block-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

.block-heading {
  font-size: clamp(22px, 1.8vw, 30px);
  letter-spacing: -0.02em;
  font-weight: 700;
  margin: 4px 0 0;
  line-height: 1.2;
  color: var(--text);
}

.block-heading--3 {
  font-size: clamp(18px, 1.3vw, 22px);
  font-weight: 600;
  color: var(--text);
}

.prose {
  font-size: clamp(15.5px, 1.02vw, 18px);
  line-height: 1.75;
  color: var(--muted);
  margin: 0;
  max-width: 78ch;
}

.prose strong {
  color: var(--text);
  font-weight: 700;
}

.bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
  max-width: 86ch;
}

.bullets li {
  position: relative;
  padding-left: 30px;
  font-size: clamp(15.5px, 1vw, 17.5px);
  line-height: 1.65;
  color: var(--muted);
}

.bullets li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 0.62em;
  width: 9px;
  height: 9px;
  border-radius: 3px;
  background: linear-gradient(
    135deg,
    var(--brand-accent),
    var(--brand-highlight, var(--brand-accent))
  );
  box-shadow: 0 1px 4px rgba(233, 90, 34, 0.2);
}

.divider {
  height: 1px;
  background: var(--border);
  position: relative;
}

.divider::after {
  content: "";
  position: absolute;
  left: 0;
  top: -1px;
  width: 64px;
  height: 3px;
  border-radius: 99px;
  background: linear-gradient(
    90deg,
    var(--brand-accent),
    var(--brand-highlight, var(--brand-accent))
  );
  box-shadow: 0 1px 6px rgba(233, 90, 34, 0.2);
}

/* ---------------------------------------------------------------- stats */
.stat-band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: 16px;
}

.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 22px 20px;
  box-shadow: 0 4px 16px -2px rgba(17, 24, 39, 0.04);
  position: relative;
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.stat:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -4px rgba(17, 24, 39, 0.08);
}

.stat::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--brand-accent);
}

.stat__value {
  font-size: clamp(30px, 2.8vw, 44px);
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--brand-primary);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.stat__label {
  margin-top: 6px;
  font-size: 13px;
  color: var(--muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---------------------------------------------------------------- cards */
.card-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
}

.card-grid--team {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 218px), 1fr));
}

.card-grid--placement {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
}

.card-grid--partner {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px -2px rgba(17, 24, 39, 0.04);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}

.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px -4px rgba(17, 24, 39, 0.09);
  border-color: rgba(0, 0, 0, 0.15);
}

.card__media {
  background: var(--page-bg);
  overflow: hidden;
}

.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.35s ease;
}

.card:hover .card__media img {
  transform: scale(1.03);
}

.card__media--square {
  aspect-ratio: 1 / 1;
}

.card__media--wide {
  aspect-ratio: 16 / 9;
}

.card__media--logo {
  aspect-ratio: 12 / 5;
  display: grid;
  place-items: center;
  padding: 14px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.card__media--logo img {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

.card__body {
  padding: 16px 18px 18px;
  display: grid;
  gap: 7px;
  align-content: start;
  flex: 1;
}

.card__title {
  font-size: 16.5px;
  font-weight: 680;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin: 0;
}

.card__subtitle {
  font-size: 13.5px;
  color: var(--accent-ink, var(--brand-accent));
  font-weight: 650;
}

.card__meta {
  font-size: 12.5px;
  color: var(--muted);
  letter-spacing: 0.01em;
}

.card__text {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--muted);
  margin: 2px 0 0;
}

.card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.chip {
  font-size: 11.5px;
  font-weight: 650;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink, var(--brand-accent));
  border: 1px solid var(--accent-line);
}

.card--certification {
  border-left: 4px solid var(--brand-accent);
}

.card--plain .card__body {
  padding: 20px;
}

.card--placement {
  flex-direction: row;
  align-items: stretch;
}

.card--placement .card__media--logo {
  width: 108px;
  aspect-ratio: auto;
  border-bottom: none;
  border-right: 1px solid var(--border);
  flex: 0 0 auto;
  padding: 12px;
}

.card--placement .card__body {
  padding: 14px 16px;
}

.card__index {
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 12px;
  color: var(--muted);
}

/* ---------------------------------------------------------------- quote */
.quote {
  background: linear-gradient(
    135deg,
    var(--brand-primary) 0%,
    color-mix(in srgb, var(--brand-primary) 85%, var(--brand-accent)) 100%
  );
  color: var(--on-primary);
  border-radius: var(--radius-lg);
  padding: 36px 40px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 28px;
  align-items: center;
  box-shadow:
    var(--shadow-md),
    0 0 0 1px rgba(233, 90, 34, 0.08);
  position: relative;
  overflow: hidden;
}

/* Floating decorative background element */
.quote::before {
  content: "";
  position: absolute;
  top: -40px;
  right: -40px;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.04);
  pointer-events: none;
}

.quote__portrait {
  width: 118px;
  height: 118px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid var(--brand-accent);
  flex: 0 0 auto;
  box-shadow: 0 0 0 4px rgba(233, 90, 34, 0.15);
}

.quote__mark {
  font-size: 64px;
  line-height: 0.6;
  font-weight: 700;
  background: linear-gradient(
    135deg,
    var(--brand-accent),
    var(--brand-highlight, var(--brand-accent))
  );
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.quote__text {
  font-size: clamp(17px, 1.5vw, 24px);
  line-height: 1.58;
  font-weight: 500;
  margin: 10px 0 14px;
  max-width: 62ch;
}

.quote__author {
  font-weight: 700;
  font-size: 15.5px;
  letter-spacing: 0.01em;
}

.quote__role {
  font-size: 13.5px;
  color: rgba(255, 255, 255, 0.68);
  letter-spacing: 0.03em;
}

/* -------------------------------------------------------------- gallery
   Smart alignment: up to 5 per row; because the row is centred and full
   rows fill the width exactly, only a trailing partial row appears centred. */
.gallery {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--gap);
}

.gallery__item {
  flex: 0 0 auto;
  width: calc((100% - 4 * var(--gap)) / 5);
  border: none;
  padding: 0;
  background: none;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  cursor: zoom-in;
  position: relative;
}

.gallery__item:hover {
  transform: translateY(-3px) scale(1.012);
  box-shadow: var(--shadow-md);
}

/* 16:9 matches almost every photograph these decks are built from, so a tile
   holds a whole picture with no crop and no wasted band around it. */
.gallery__item img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--page-bg);
}

/* Cropping to a perfectly uniform grid stays available, but it is opt-in. */
.gallery--cover .gallery__item img {
  object-fit: cover;
  background: none;
}

.gallery__caption {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  margin-top: 14px;
}

.gallery--count-1 .gallery__item,
.gallery--count-2 .gallery__item {
  width: calc((100% - var(--gap)) / 2);
}

.gallery--count-3 .gallery__item,
.gallery--count-4 .gallery__item {
  width: calc((100% - 2 * var(--gap)) / 3);
}

.gallery__meta {
  position: absolute;
  inset: auto 0 0 0;
  padding: 8px 10px;
  font-size: 11.5px;
  color: #ffffff;
  background: rgba(17, 24, 39, 0.55);
  opacity: 0;
  transition: opacity 0.2s ease;
  text-align: left;
}

.gallery__item:hover .gallery__meta {
  opacity: 1;
}

/* ------------------------------------------------------------- lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(17, 24, 39, 0.94);
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 18px;
  animation: fade-in 0.18s ease both;
}

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

.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: rgba(255, 255, 255, 0.82);
  font-size: 14px;
  gap: 12px;
}

.lightbox__stage {
  display: grid;
  place-items: center;
  min-height: 0;
  padding: 12px 0;
}

.lightbox__stage img {
  max-width: min(1400px, 92vw);
  max-height: 78vh;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  object-fit: contain;
}

.lightbox__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.icon-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  display: grid;
  place-items: center;
  font-size: 18px;
  transition: background 0.15s ease, transform 0.15s ease;
}

.icon-btn:hover {
  background: rgba(255, 255, 255, 0.22);
}

/* --------------------------------------------------- presentation mode */
.deck-bar {
  position: fixed;
  bottom: 22px;
  /* Centred in the space left over once the player bar's corner is reserved, not in
     the whole window. Measured while presenting at 1600x900: this bar ran 210..1390
     and the player bar 1370..1586, so dead centre laid 20px of one over the other.
     The reserve is the player bar's width plus its inset plus the gap between them. */
  left: calc(50% - (var(--player-reserve) / 2));
  transform: translateX(-50%);
  /* Never pushed off the left edge on a narrow window, whatever the reserve costs. */
  max-width: calc(100vw - var(--player-reserve) - 24px);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 999px;
  background: rgba(17, 24, 39, 0.9);
  color: #ffffff;
  box-shadow: var(--shadow-lg);
  backdrop-filter: blur(6px);
  opacity: 0.28;
  transition: opacity 0.25s ease;
}

.deck-bar:hover,
.deck-bar:focus-within {
  opacity: 1;
}

.deck-bar__count {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  padding: 0 8px;
  color: rgba(255, 255, 255, 0.82);
  white-space: nowrap;
}

.deck-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  background: var(--brand-accent);
  z-index: 45;
  transition: width 0.3s ease;
}

body.is-presenting .sidenav,
body.is-presenting .topbar {
  display: none;
}

body.is-presenting .stage {
  padding: 0;
}

/* `:root` on the front is not decoration: it takes this to (0,3,1), which beats
   the (0,3,0) of `:root[data-nav-rail='1'] .shell` further up. Without it that
   rule won while the pane was collapsed, so the grid went on reserving the
   rail's 78px column with the pane itself `display: none` — `.main` fell into
   that column and the whole slide was drawn 78px wide for the length of the
   switch into presenting. That was the flicker: it only ever happened when
   presenting was entered FROM the collapsed pane, which is exactly how a
   presenter does it. */
:root body.is-presenting .shell {
  grid-template-columns: minmax(0, 1fr);
}

/* Presenting: the slide is the display. It fills the nominal box FitSlide sized
   to the screen's shape, and the canvas takes whatever height is left over.
   The gutter is deliberately left alone — see the note on
   `body.is-presenting .fit-slide__inner .slide`. */
body.is-presenting .slide {
  max-width: none;
  min-height: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.is-presenting .slide::before {
  display: none;
}

/* `stretch` hands the spare height to the auto-sized rows, so blocks grow to
   fill the slide instead of huddling at the top over a band of empty white.
   Growing only — `flex-shrink: 0` — because a shrinking canvas would squeeze the
   rows below their content and clip it. */
body.is-presenting .canvas {
  flex: 1 0 auto;
  align-content: stretch;
}

body.is-presenting .slide__title {
  font-size: clamp(38px, 4.4vw, 68px);
}

body.is-presenting .prose {
  font-size: clamp(17px, 1.35vw, 24px);
}

body.is-presenting .bullets li {
  font-size: clamp(16px, 1.25vw, 22px);
}

body.is-presenting .stat__value {
  font-size: clamp(34px, 3.6vw, 58px);
}

body.is-presenting .gallery {
  --gap: 22px;
}

/* ---------------------------------------------------------------- editor */
.editor {
  max-width: 1080px;
  margin: 0 auto;
  display: grid;
  gap: 20px;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 24px 26px;
}

.panel__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}

.panel__title {
  font-size: 18px;
  font-weight: 700;
  margin: 0;
  letter-spacing: -0.01em;
}

.panel__hint {
  font-size: 13.5px;
  color: var(--muted);
  margin: -8px 0 16px;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 14px;
}

.block-list {
  display: grid;
  gap: 14px;
}

.block-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.block-card.is-dragging {
  opacity: 0.45;
}

.block-card.is-dropzone {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.block-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  background: var(--page-bg);
  border-bottom: 1px solid var(--border);
}

.block-card__handle {
  cursor: grab;
  color: var(--muted);
  font-size: 17px;
  padding: 2px 4px;
  border-radius: 6px;
  user-select: none;
}

.block-card__handle:active {
  cursor: grabbing;
}

.block-card__type {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent-ink, var(--brand-accent));
}

.block-card__body {
  padding: 16px;
  display: grid;
  gap: 12px;
}

.subcard {
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: 13px;
  display: grid;
  gap: 10px;
  background: var(--page-bg);
}

.subcard__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.row-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}

.add-menu {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 4px;
}

.thumb-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.thumb {
  position: relative;
  width: 104px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface);
}

.thumb.is-dragging {
  opacity: 0.4;
}

.thumb.is-dropzone {
  box-shadow: 0 0 0 3px var(--accent-soft);
  border-color: var(--brand-accent);
}

.thumb img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  cursor: grab;
}

.thumb__remove {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: rgba(17, 24, 39, 0.72);
  color: #ffffff;
  font-size: 13px;
  line-height: 1;
  display: grid;
  place-items: center;
}

.thumb__remove:hover {
  background: var(--error);
}

.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 22px;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  background: var(--page-bg);
  transition: border-color 0.15s ease, background 0.15s ease;
}

.dropzone.is-over {
  border-color: var(--brand-accent);
  background: var(--accent-soft);
  color: var(--accent-ink, var(--brand-accent));
}

.sticky-actions {
  position: sticky;
  bottom: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 14px 18px;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}

.tab-manager {
  display: grid;
  gap: 8px;
}

.tab-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  flex-wrap: wrap;
}

.tab-row.is-dragging {
  opacity: 0.45;
}

.tab-row.is-dropzone {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.tab-row__title {
  flex: 1;
  min-width: 160px;
  font-weight: 600;
}

.empty-note {
  padding: 28px;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--page-bg);
}

/* --------------------------------------------------------------- modal */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(17, 24, 39, 0.55);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 80;
  animation: fade-in 0.15s ease both;
}

.modal {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  width: min(560px, 100%);
  max-height: 86vh;
  overflow: auto;
  padding: 26px 26px 22px;
}

.modal__title {
  margin: 0 0 6px;
  font-size: 20px;
  letter-spacing: -0.01em;
}

.modal__text {
  color: var(--muted);
  font-size: 14.5px;
  margin: 0 0 18px;
}

.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
  flex-wrap: wrap;
}

/* -------------------------------------------------------------- toasts */
.toast-host {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 100;
  display: grid;
  gap: 10px;
  max-width: min(380px, calc(100vw - 40px));
}

.toast {
  background: var(--text);
  color: #ffffff;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  font-size: 14px;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  animation: toast-in 0.22s ease both;
  border-left: 4px solid var(--brand-accent);
}

.toast--success {
  border-left-color: var(--success);
}

.toast--error {
  border-left-color: var(--error);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --------------------------------------------------------- responsive */
@media (max-width: 1280px) {
  .gallery__item {
    width: calc((100% - 3 * var(--gap)) / 4);
  }
  .gallery--count-3 .gallery__item,
  .gallery--count-4 .gallery__item {
    width: calc((100% - 2 * var(--gap)) / 3);
  }
}

@media (max-width: 1024px) {
  .shell {
    grid-template-columns: 240px minmax(0, 1fr);
  }
  .slide {
    padding: 34px 30px 40px;
  }
  .stage {
    padding: 22px 20px 70px;
  }
  .topbar {
    padding: 12px 20px;
  }
  .gallery__item {
    width: calc((100% - 2 * var(--gap)) / 3);
  }
}

@media (max-width: 860px) {
  .login {
    grid-template-columns: 1fr;
  }
  .login__brand {
    padding: 34px 26px;
    gap: 26px;
  }
  /* Stacked layout: the pane becomes a full-width card above the deck. The rail
     is meaningless at this width, so every rail rule is undone rather than
     overridden piecemeal — a pane collapsed on the desktop still opens here. */
  .shell,
  :root[data-nav-rail='1'] .shell {
    grid-template-columns: minmax(0, 1fr);
  }
  .sidenav,
  .sidenav--rail {
    position: static;
    align-items: stretch;
    height: auto;
    max-height: 78vh;
    border-right: 0;
    border-bottom: 1px solid var(--nav-line);
    background: var(--nav-surface);
  }
  .sidenav__collapse,
  .sidenav--rail .sidenav__mark,
  .sidenav--rail .snav-rail__list,
  .snav-flyout {
    display: none;
  }
  .sidenav--rail .sidenav__head {
    flex-direction: row;
    padding: 16px 14px 10px;
  }
  .sidenav--rail .sidenav__logo {
    display: block;
  }
  .sidenav--rail .snav-label {
    display: flex;
  }
  .sidenav--rail .sidenav__scroll {
    display: block;
    max-height: 46vh;
  }
  .sidenav__scroll {
    max-height: 46vh;
  }
  .quote {
    grid-template-columns: 1fr;
    padding: 26px 22px;
  }
  .card--placement {
    flex-direction: column;
  }
  .card--placement .card__media--logo {
    width: 100%;
    aspect-ratio: 12 / 5;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
}

@media (max-width: 640px) {
  .gallery__item,
  .gallery--count-3 .gallery__item,
  .gallery--count-4 .gallery__item {
    width: calc((100% - var(--gap)) / 2);
  }
  .slide {
    padding: 26px 20px 32px;
    border-radius: var(--radius);
  }
  .stat-band {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ==========================================================================
   Block canvas — the section builder surface (viewer + editor share this)
   ========================================================================== */
.canvas {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: minmax(var(--row-height, 28px), auto);
  gap: var(--canvas-gap, 16px);
  align-items: stretch;
  align-content: start;
}

.canvas-block {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Blocks arrive in reading order when a slide opens. The editor is exempt:
   an element being dragged must never animate under the pointer. */
.canvas:not(.canvas--editing) > .canvas-block {
  animation: block-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--i, 0) * 100ms);
}

.canvas-block > * {
  min-height: 0;
  flex: 1;
}

.canvas-block--divider,
.canvas-block--heading,
.canvas-block--stats {
  justify-content: center;
}

/* ------------------------------------------------------------- text box */
.text-box {
  display: flex;
  flex-direction: column;
  gap: 14px;
  justify-content: flex-start;
  position: relative;
}

.text-box--center {
  text-align: center;
  align-items: center;
}

.text-box--center .bullets li {
  text-align: left;
}

.media-empty__hint {
  color: var(--muted);
  font-style: italic;
}

/* ------------------------------------------------------ image / video box */
.image-box,
.video-box,
.profile-box {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
}

.image-box__frame {
  flex: 1;
  min-height: 140px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--page-bg);
  cursor: zoom-in;
  display: block;
  position: relative;
  transition: box-shadow 0.35s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

.image-box__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 0 rgba(233, 90, 34, 0);
  transition: box-shadow 0.35s ease;
  pointer-events: none;
  z-index: 1;
}

.image-box__frame:hover::after {
  box-shadow: inset 0 0 24px rgba(233, 90, 34, 0.08);
}

.image-box__frame img {
  width: 100%;
  height: 100%;
  min-height: 140px;
  object-fit: cover;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.image-box__frame:hover {
  box-shadow: 0 8px 28px rgba(17, 24, 39, 0.12);
}

.image-box__frame:hover img {
  transform: scale(1.04);
}

.media-title {
  font-size: 0.9em;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.01em;
}

.media-caption {
  font-size: 0.85em;
  color: var(--muted);
  text-align: center;
  position: relative;
  padding-top: 6px;
}

.media-caption::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 32px;
  height: 2px;
  border-radius: 99px;
  background: var(--brand-accent);
  opacity: 0.4;
}

.media-note {
  font-size: 0.8em;
  color: var(--muted);
  background: var(--page-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--brand-accent);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin: 0;
}

.media-empty {
  flex: 1;
  min-height: 130px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--page-bg);
  color: var(--muted);
  font-size: 0.9em;
  text-align: center;
  padding: 16px;
}

.media-empty__icon {
  font-size: 26px;
  color: var(--accent-ink, var(--brand-accent));
}

/* ----------------------------------------------------------- video box */
.video-box__frame {
  position: relative;
  flex: 1;
  min-height: 160px;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--text);
  border: 1px solid var(--border);
}

.video-box__player {
  width: 100%;
  height: 100%;
  min-height: 160px;
  display: block;
  object-fit: contain;
  background: var(--text);
}

.video-box__fs {
  position: absolute;
  right: 10px;
  bottom: 52px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(17, 24, 39, 0.72);
  color: #ffffff;
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 0.78em;
  font-weight: 700;
  opacity: 0;
  transition: opacity 0.2s ease, background 0.2s ease;
}

.video-box__frame:hover .video-box__fs,
.video-box__frame:focus-within .video-box__fs {
  opacity: 1;
}

.video-box__fs:hover {
  background: var(--brand-accent);
  color: var(--on-accent);
  border-color: transparent;
}

/* The Back control only exists while the video owns the screen. */
.video-box__back {
  position: absolute;
  top: 18px;
  left: 18px;
  z-index: 5;
  display: none;
  align-items: center;
  gap: 8px;
  border: 1px solid rgba(255, 255, 255, 0.32);
  background: rgba(17, 24, 39, 0.8);
  color: #ffffff;
  border-radius: 999px;
  padding: 10px 18px;
  font-size: 15px;
  font-weight: 700;
}

.video-box__back:hover {
  background: var(--brand-accent);
  color: var(--on-accent);
  border-color: transparent;
}

.video-box__frame.is-fullscreen {
  border: none;
  border-radius: 0;
}

.video-box__frame.is-fullscreen .video-box__back {
  display: flex;
}

.video-box__frame.is-fullscreen .video-box__fs {
  display: none;
}

.video-box__frame:fullscreen .video-box__player {
  width: 100vw;
  height: 100vh;
}

/* --------------------------------------------------------- profile slot
   Fixed frame + cover crop, so any upload lands well composed. */
.profile-box {
  align-items: center;
  text-align: center;
}

.profile-box__zoom {
  border: none;
  background: none;
  padding: 0;
  cursor: zoom-in;
  width: 100%;
  display: flex;
  justify-content: center;
}

.profile-box__frame {
  width: 100%;
  max-width: 320px;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--page-bg);
  border: 1px solid var(--border);
  box-shadow: 0 4px 16px -2px rgba(17, 24, 39, 0.05);
  position: relative;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.profile-box__frame:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px -4px rgba(17, 24, 39, 0.1);
  border-color: var(--brand-accent);
}

.profile-box__frame::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--brand-accent);
}

.profile-box__frame--portrait {
  aspect-ratio: 4 / 5;
}

.profile-box__frame--square {
  aspect-ratio: 1 / 1;
}

.profile-box__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

.profile-box__frame:hover img {
  transform: scale(1.02);
}

.profile-box__placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--muted);
  background: var(--page-bg);
}

.profile-box__placeholder span {
  font-size: 34px;
  font-weight: 700;
  color: var(--brand-accent);
}

.profile-box__placeholder small {
  font-size: 12px;
}

.profile-box__caption {
  display: grid;
  gap: 4px;
  margin-top: 12px;
}

.profile-box__name {
  font-weight: 750;
  font-size: 1.1em;
  color: var(--text);
  letter-spacing: -0.01em;
}

.profile-box__role {
  color: var(--brand-accent);
  font-weight: 700;
  font-size: 0.82em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.profile-box__blurb {
  margin: 6px 0 0;
  color: var(--muted);
  font-size: 0.88em;
  line-height: 1.65;
}

/* ==========================================================================
   Canvas editor chrome
   ========================================================================== */
.canvas-editor {
  display: grid;
  gap: 18px;
}

.canvas--editing {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 14px;
  min-height: 320px;
  background-attachment: local;
  background-image: none;
  transition: background-image 0.12s ease;
}

/*
 * Column guides are a placement aid, not decoration: they appear while an
 * element is being dragged or dropped and are invisible the rest of the time,
 * so the canvas reads as the page rather than as graph paper.
 */
body.is-block-dragging .canvas--editing,
body.is-placing-block .canvas--editing,
body.is-block-dragging .canvas--editing-nested,
body.is-placing-block .canvas--editing-nested {
  background-image:
    repeating-linear-gradient(to right, transparent 0, transparent calc(8.3333% - 1px), var(--accent-line) calc(8.3333% - 1px), var(--accent-line) 8.3333%);
}

.canvas--editing .canvas-block {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 34px 14px 14px;
  overflow: hidden;
  animation: none;
}

.canvas--editing .canvas-block:hover {
  border-color: var(--accent-line);
}

.canvas--editing .canvas-block.is-selected {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.canvas--editing .canvas-block.is-moving,
.canvas--editing .canvas-block.is-resizing {
  z-index: 6;
  box-shadow: var(--shadow-lg);
  border-color: var(--brand-accent);
  opacity: 0.96;
}

.canvas-block__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.canvas-block__body > * {
  flex: 1;
  min-height: 0;
}

.block-toolbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 30px;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 6px;
  background: var(--page-bg);
  border-bottom: 1px solid var(--border);
}

.block-toolbar__label {
  flex: 1;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.block-toolbar__btn {
  border: none;
  background: transparent;
  color: var(--muted);
  width: 24px;
  height: 24px;
  border-radius: 6px;
  display: grid;
  place-items: center;
  font-size: 13px;
  line-height: 1;
}

.block-toolbar__btn:hover {
  background: var(--accent-soft);
  color: var(--accent-ink, var(--brand-accent));
}

.block-toolbar__btn--danger:hover {
  background: rgba(220, 38, 38, 0.1);
  color: var(--error);
}

.block-toolbar__grip {
  cursor: grab;
  touch-action: none;
}

.block-toolbar__grip:active {
  cursor: grabbing;
}

.canvas-block__size {
  position: absolute;
  right: 6px;
  bottom: 6px;
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  background: var(--page-bg);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 1px 5px;
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.canvas--editing .canvas-block:hover .canvas-block__size,
.canvas--editing .canvas-block.is-selected .canvas-block__size,
.canvas--editing .canvas-block.is-resizing .canvas-block__size {
  opacity: 1;
}

.resize-handle {
  position: absolute;
  background: transparent;
  touch-action: none;
  z-index: 4;
}

.resize-handle--e {
  top: 30px;
  right: 0;
  bottom: 12px;
  width: 10px;
  cursor: ew-resize;
}

.resize-handle--s {
  left: 12px;
  right: 12px;
  bottom: 0;
  height: 10px;
  cursor: ns-resize;
}

.resize-handle--se {
  right: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  cursor: nwse-resize;
  border-right: 3px solid var(--border);
  border-bottom: 3px solid var(--border);
  border-bottom-right-radius: var(--radius);
}

.canvas-block:hover .resize-handle--se,
.canvas-block.is-selected .resize-handle--se {
  border-color: var(--brand-accent);
}

body.is-block-dragging {
  user-select: none;
  cursor: grabbing;
}

.canvas-empty {
  grid-column: 1 / -1;
  grid-row: 1 / span 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--muted);
  text-align: center;
  padding: 24px;
}

.canvas-empty strong {
  color: var(--text);
  font-size: 1.1em;
}

.add-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.add-bar__label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-right: 4px;
}

.select--inline {
  width: auto;
  padding: 7px 10px;
  font-size: 14px;
}

/* ========================================================== icon chooser
   Section identity: a library mark or an uploaded PNG/SVG. The preview is the
   real navigation row, so the choice is judged where it will be seen.
   ========================================================== */
.section-form {
  display: grid;
  gap: 4px;
}

.icon-chooser {
  display: grid;
  gap: 12px;
}

.icon-chooser__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--page-bg);
}

.icon-chooser__preview {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* The chip repeats the pane's own treatment: light fill, accent glyph. */
.icon-chooser__preview-chip {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: #ffffff;
  color: var(--accent-ink, var(--brand-accent));
}

.icon-chooser__preview-glyph {
  display: grid;
  place-items: center;
}

.icon-chooser__preview-glyph .sec-icon {
  width: 21px;
  height: 21px;
}

.icon-chooser__preview-glyph .sec-icon--asset {
  width: 26px;
  height: 26px;
}

.icon-chooser__preview-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.icon-chooser__preview-title {
  overflow: hidden;
  font-size: 15px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.icon-chooser__preview-note {
  color: var(--muted);
  font-size: 12px;
}

.icon-chooser__tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.icon-chooser__hint {
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
}

.input--sm {
  padding: 9px 12px;
  font-size: 14px;
}

.icon-chooser__grid {
  display: grid;
  gap: 14px;
  max-height: 236px;
  overflow-y: auto;
  padding: 4px 2px 2px;
}

.icon-chooser__group-label {
  margin-bottom: 7px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.icon-chooser__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 7px;
}

.icon-tile {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.icon-tile:hover {
  border-color: var(--brand-accent);
  color: var(--accent-ink, var(--brand-accent));
  transform: translateY(-1px);
}

.icon-tile__glyph {
  width: 20px;
  height: 20px;
}

.icon-tile.is-active {
  border-color: transparent;
  background: linear-gradient(130deg, var(--brand-accent), var(--brand-highlight));
  box-shadow: 0 6px 16px color-mix(in srgb, var(--brand-accent) 34%, transparent);
  color: var(--on-accent);
}

.icon-chooser__empty {
  margin: 0;
  padding: 18px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 13.5px;
  text-align: center;
}

.btn__icon {
  width: 16px;
  height: 16px;
}

/* The editor's nav-icon control: shows the current mark, opens the chooser. */
.icon-field {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 12px 7px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.icon-field:hover {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.icon-field__chip {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--page-bg);
  color: var(--accent-ink, var(--brand-accent));
}

.icon-field__chip .sec-icon {
  width: 18px;
  height: 18px;
}

.icon-field__text {
  flex: 1;
  min-width: 0;
  color: var(--muted);
  font-size: 13.5px;
}

.icon-field__act {
  color: var(--accent-ink, var(--brand-accent));
  font-size: 13px;
  font-weight: 600;
}

/* -------------------------------------------------- block edit dialog bits */
.modal--wide {
  width: min(720px, 100%);
}

.block-fields {
  display: grid;
  gap: 14px;
}

.field__hint {
  font-size: 12.5px;
  color: var(--muted);
}

.checkbox-grid {
  display: grid;
  gap: 8px;
}

.asset-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--page-bg);
}

.asset-row__preview {
  width: 132px;
  height: 92px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  flex: 0 0 auto;
}

.asset-row__preview--round {
  width: 84px;
  height: 84px;
  border-radius: 50%;
}

.asset-row__empty {
  width: 132px;
  height: 92px;
  display: grid;
  place-items: center;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  color: var(--accent-ink, var(--brand-accent));
  font-size: 26px;
  flex: 0 0 auto;
}

.asset-row__actions {
  display: grid;
  gap: 8px;
  align-content: start;
  min-width: 0;
}

.dropzone.is-busy {
  border-color: var(--brand-accent);
  color: var(--accent-ink, var(--brand-accent));
}

/* Section identity in the manager and the template list: the same marks the
   navigation pane draws, on a light surface. */
.row-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--page-bg);
  color: var(--accent-ink, var(--brand-accent));
}

.row-icon .sec-icon,
.row-icon .ic {
  width: 17px;
  height: 17px;
}

.row-icon--action {
  padding: 0;
  cursor: pointer;
  transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
}

.row-icon--action:hover {
  border-color: var(--brand-accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ==========================================================================
   Responsive: below the stacking breakpoint every block goes full width in
   reading order, so the canvas still reads correctly on a tablet.
   ========================================================================== */
@media (max-width: 900px) {
  .canvas {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: auto;
  }

  /* The fitted slide is excluded here rather than corrected further down: this
     !important beats the per-block inline grid placement, and CSS cannot put an
     inline value back once it has lost. Below the 861px cutoff the fit is off
     and the slide does stack, so it is added back at the end of this file. */
  .canvas-block:not(.fit-slide__inner .canvas-block) {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
  }

  .image-box__frame,
  .video-box__frame {
    min-height: 220px;
  }
}

/* Presentation mode: a touch more air, larger media. */
body.is-presenting .canvas {
  --canvas-gap: 22px;
}

body.is-presenting .profile-box__frame {
  max-width: 380px;
}

/* Stat cards size to their content instead of stretching to the block box. */
.stat-band {
  align-content: start;
}

.stat {
  align-self: start;
}

/* Text-ish blocks keep their content top-aligned inside the block box. */
.canvas-block--heading > *,
.canvas-block--paragraph > *,
.canvas-block--bullets > *,
.canvas-block--stats > * {
  flex: 0 0 auto;
}

/* ==========================================================================
   Admin tools on the slide itself — so adding a block never needs a hunt
   ========================================================================== */
.slide-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 28px;
  padding: 14px 16px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--page-bg);
}

.slide-tools__label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-right: 4px;
}

.slide-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 56px 24px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--page-bg);
  color: var(--muted);
}

.slide-empty strong {
  color: var(--text);
  font-size: 1.25em;
}

.slide-empty span {
  max-width: 48ch;
}

/* The editor's add bar follows you down a long canvas. */
.add-bar {
  position: sticky;
  bottom: 76px;
  z-index: 12;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(8px);
  box-shadow: var(--shadow-md);
}

/* Make the drag grip unmistakable while editing. */
.block-toolbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.canvas--editing .canvas-block:hover .block-toolbar,
.canvas--editing .canvas-block.is-selected .block-toolbar {
  background: var(--accent-soft);
  border-bottom-color: var(--accent-line);
}

.block-toolbar__grip {
  color: var(--accent-ink, var(--brand-accent));
  font-size: 15px;
}

.block-toolbar__grip:hover {
  background: var(--brand-accent);
  color: var(--on-accent);
}

/* Visible resize affordance on the right and bottom edges, not just the corner. */
.canvas--editing .canvas-block:hover .resize-handle--e::after,
.canvas--editing .canvas-block.is-selected .resize-handle--e::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 2px;
  transform: translateY(-50%);
  width: 4px;
  height: 34px;
  border-radius: 99px;
  background: var(--brand-accent);
  opacity: 0.6;
}

.canvas--editing .canvas-block:hover .resize-handle--s::after,
.canvas--editing .canvas-block.is-selected .resize-handle--s::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 2px;
  transform: translateX(-50%);
  width: 34px;
  height: 4px;
  border-radius: 99px;
  background: var(--brand-accent);
  opacity: 0.6;
}

@media (max-width: 900px) {
  .add-bar {
    position: static;
  }
}
/* Embedded providers (YouTube / Vimeo / Drive) fill the same frame as <video>. */
.video-box__player--embed {
  width: 100%;
  height: 100%;
  min-height: 160px;
  border: 0;
  display: block;
  background: var(--text);
}

.video-box__frame.is-fullscreen .video-box__player--embed {
  width: 100vw;
  height: 100vh;
}

.video-box__source {
  margin-left: 8px;
  vertical-align: middle;
}

/* Keep the Back control clickable above an embedded player. */
.video-box__back {
  z-index: 10;
}
/* ==========================================================================
   Page-builder elements
   Every element below draws only on the brand tokens, so a section built from
   them cannot drift off-palette however it is arranged.
   ========================================================================== */

/* ------------------------------------------------------------------ hero */
.slide--hero {
  padding: 0 !important;
  border: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  overflow: hidden !important;
  min-height: 860px !important;
  height: 100% !important;
  display: flex !important;
  flex-direction: column !important;
}

.slide--hero .canvas {
  height: 100% !important;
  min-height: 860px !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-template-rows: 1fr !important;
}

.slide--hero .canvas-block--hero {
  height: 100% !important;
  min-height: 860px !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .hero {
  border-radius: 0 !important;
  height: 100% !important;
  min-height: 860px !important;
  width: 100% !important;
  padding: 64px 80px;
  flex: 1 1 auto !important;
}

body.is-presenting .fit-slide__inner .slide--hero {
  padding: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  height: 100% !important;
}

/* Color tag helpers for hero text elements */
.text-brand-green {
  color: #71BD1F !important;
  font-weight: 800;
}

.text-brand-gold {
  color: #FFBB00 !important;
  font-weight: 800;
}

.text-brand-green-line {
  color: #71BD1F !important;
  font-weight: 800;
  position: relative;
  display: inline-block;
  margin-left: 8px;
}

.text-brand-green-line::after {
  content: "";
  position: absolute;
  bottom: -8px;
  left: 0;
  right: 0;
  height: 5px;
  border-radius: 99px;
  background: linear-gradient(90deg, #71BD1F 0%, #FFBB00 100%);
}

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--brand-primary);
  color: var(--on-primary);
  padding: 44px 48px;
  isolation: isolate;
  min-height: 100%;
}

.hero--sm { min-height: 220px; }
.hero--md { min-height: 340px; }
.hero--lg { min-height: 460px; }
.hero--full { min-height: 100%; }

.hero--plain {
  background: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-highlight) 190%);
}

.hero__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
  border: 0;
}

/* 18s, 4% — enough that the frame breathes, not enough to notice as motion.
   The editor is exempt so a drifting background never fights the pointer. */
.hero:not(.hero--editing) img.hero__media {
  animation: hero-drift 18s ease-in-out infinite alternate both;
}

/* Pan, do not zoom: on an architectural elevation a growing scale eats the
   roofline, which reads as a bad crop rather than as motion. */
@keyframes hero-drift {
  from {
    transform: scale(1.03) translate3d(-0.7%, 0, 0);
  }
  to {
    transform: scale(1.03) translate3d(0.7%, 0, 0);
  }
}

/* NGI's brand film is a 2.34:1 picture exported inside a 16:9 frame, so it
   carries 130px of black at the top and 131px at the bottom as real pixels.
   object-fit cannot crop what is part of the image, so the element is scaled
   past the frame until those rows sit outside the hero. 1.325 = 1080/819 plus a
   rounding margin.
   It is the default rather than the rule: Torii's film is honestly 1920x1080
   with no bars anywhere in it — measured across five frames — and this scale
   simply threw a third of the picture away. A block that knows its own film
   sets `--hero-zoom`; see `hero.zoom`. */
.hero video.hero__media {
  transform: scale(var(--hero-zoom, 1.325));
}

.hero__media--embed {
  pointer-events: none !important;
  user-select: none !important;
  position: absolute !important;
  top: 50% !important;
  left: 50% !important;
  width: 125% !important;
  min-width: 125% !important;
  height: 145% !important;
  min-height: 145% !important;
  border: 0 !important;
  transform: translate(-50%, -50%) scale(1.22) !important;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(90deg, rgba(8, 14, 26, 0.94) 0%, rgba(8, 14, 26, 0.82) 42%, rgba(8, 14, 26, 0.3) 75%, rgba(8, 14, 26, 0.08) 100%);
}

.hero--center .hero__scrim {
  background: rgba(0, 0, 0, 0.9);
}

.hero--right .hero__scrim {
  background: linear-gradient(-90deg, rgba(6, 12, 22, 0.96) 0%, rgba(6, 12, 22, 0.85) 45%, rgba(6, 12, 22, 0.3) 75%, rgba(6, 12, 22, 0.02) 100%) !important;
}

.hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 48rem;
}

.hero:not(.hero--editing) .hero__kicker {
  animation: block-in 0.55s cubic-bezier(0.22, 0.68, 0, 1) 0.18s both;
}

.hero:not(.hero--editing) .hero__heading {
  animation: block-in 0.68s cubic-bezier(0.22, 0.68, 0, 1) 0.26s both;
}

.hero:not(.hero--editing) .hero__subheading {
  animation: block-in 0.68s cubic-bezier(0.22, 0.68, 0, 1) 0.36s both;
}

.hero--center {
  align-items: center;
  text-align: center;
}

.hero--center .hero__content {
  align-items: center;
}

.hero--right {
  align-items: flex-end;
  text-align: right;
}

.hero--right .hero__content {
  align-items: flex-end;
  text-align: right;
  margin-left: auto;
}

.hero__kicker {
  font-size: clamp(38px, 4.2vw, 64px);
  font-weight: 900;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #ffffff;
  margin-bottom: 24px;
  line-height: 1.1;
  display: inline-block;
}

.hero--media .hero__kicker {
  color: #ffffff;
  opacity: 1;
}

.hero__heading {
  margin: 0 0 16px 0;
  font-size: clamp(28px, 2.6vw, 44px);
  line-height: 1.15;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.hero__subheading {
  margin: 0;
  font-size: clamp(16px, 1.25vw, 21px);
  line-height: 1.65;
  opacity: 0.95;
  max-width: 48rem;
}

.hero__hint {
  position: absolute;
  right: 14px;
  bottom: 12px;
  font-size: 12px;
  padding: 3px 9px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
}

/* ------------------------------------------------------------- KPI cards */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: 14px;
  align-content: start;
}

/* On a slide the block is given a box to fill, so the cards share that height
   evenly instead of sitting at the top over a band of empty space — and the
   number sits in the middle of the card it was given, not at its top. */
.canvas-block--kpi .kpi-grid {
  height: 100%;
  align-content: stretch;
  grid-auto-rows: 1fr;
}

.canvas-block--kpi .kpi {
  justify-content: center;
}

/* Same for a card deck: on a slide the block is a box to fill, so the cards
   share the height and their copy sits in the middle of the card rather than
   stranded at the top of a tall empty one. */
.canvas-block--cards .card-grid {
  height: 100%;
  align-content: stretch;
  grid-auto-rows: 1fr;
}

/* The body is a grid, so centring is align-content, not justify-content. */
.canvas-block--cards .card__body {
  align-content: center;
}

.kpi-grid--fixed {
  grid-template-columns: repeat(var(--kpi-columns, 3), minmax(0, 1fr));
}

.kpi {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 24px 26px;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 4px 16px -2px rgba(17, 24, 39, 0.05);
  position: relative;
  overflow: hidden;
  min-width: 0;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.kpi:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px -4px rgba(17, 24, 39, 0.08);
  border-color: var(--brand-accent);
}

.kpi::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 4px;
  height: 100%;
  background: var(--brand-accent);
}

.kpi-grid--outline .kpi {
  background: transparent;
  box-shadow: none;
  border-color: var(--border);
}

.kpi-grid--plain .kpi {
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 12px 0;
}

.kpi__icon {
  font-size: 26px;
  line-height: 1;
  margin-bottom: 4px;
  color: var(--brand-accent);
}

.kpi__value {
  font-size: clamp(32px, 3vw, 48px);
  font-weight: 800;
  line-height: 1.05;
  color: var(--brand-primary);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.kpi__label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1.45;
  margin-top: 2px;
}

.kpi__note {
  font-size: 12.5px;
  color: var(--muted);
  opacity: 0.85;
  line-height: 1.5;
}

/* ------------------------------------------------------------ icon block */
.icon-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
  min-width: 0;
}

.icon-box__glyph {
  display: grid;
  place-items: center;
  line-height: 1;
  flex: none;
}

.icon-box__glyph--circle,
.icon-box__glyph--square {
  background: var(--accent-soft);
  color: var(--accent-ink, var(--brand-accent));
}

.icon-box__glyph--circle { border-radius: 99px; }
.icon-box__glyph--square { border-radius: var(--radius); }

.icon-box--sm .icon-box__glyph { width: 38px; height: 38px; font-size: 18px; }
.icon-box--md .icon-box__glyph { width: 50px; height: 50px; font-size: 24px; }
.icon-box--lg .icon-box__glyph { width: 66px; height: 66px; font-size: 32px; }

.icon-box--primary .icon-box__glyph--circle,
.icon-box--primary .icon-box__glyph--square {
  background: var(--primary-soft);
  color: var(--brand-primary);
}

.icon-box--muted .icon-box__glyph--circle,
.icon-box--muted .icon-box__glyph--square {
  background: var(--page-bg);
  color: var(--muted);
  border: 1px solid var(--border);
}

.icon-box__glyph--none {
  background: none;
  width: auto;
  height: auto;
}

.icon-box__label {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
}

.icon-box__note {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
  line-height: 1.5;
}

/* -------------------------------------------------------- buttons / links */
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  align-content: flex-start;
}

.button-row--center { justify-content: center; }
.button-row--right { justify-content: flex-end; }

.btn--cta {
  padding: 11px 22px;
  font-size: 16px;
}

.btn--outline {
  background: transparent;
  color: var(--accent-ink, var(--brand-accent));
  border: 2px solid currentColor;
}

.btn--outline:hover {
  background: var(--accent-soft);
}

/* On a hero the outline and quiet buttons sit on a photo, so they go white. */
.hero--media .btn--outline,
.hero--plain .btn--outline {
  color: #ffffff;
}

.hero--media .btn--ghost,
.hero--plain .btn--ghost {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.5);
  background: rgba(255, 255, 255, 0.08);
}

.btn.is-linkless {
  opacity: 0.85;
  cursor: default;
}

/* ------------------------------------------------------------- logo tile */
.logo-box {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
  height: 100%;
}

.logo-box__link {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  flex: 1;
  min-height: 0;
}

.logo-box__frame {
  display: grid;
  place-items: center;
  height: 100%;
  min-height: 70px;
  border-radius: var(--radius);
  overflow: hidden;
}

.logo-box__frame--surface {
  background: var(--surface);
  border: 1px solid var(--border);
}

.logo-box__frame--soft {
  background: var(--accent-soft);
}

.logo-box__frame--dark {
  background: var(--brand-primary);
}

.logo-box__frame--none {
  background: transparent;
}

.logo-box__frame--pad-none { padding: 0; }
.logo-box__frame--pad-sm { padding: 8px; }
.logo-box__frame--pad-md { padding: 16px; }
.logo-box__frame--pad-lg { padding: 26px; }

.logo-box__frame img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  /* Never crop a logo â€” a wordmark and an emblem must both survive the tile. */
  object-fit: contain;
}

.logo-box__placeholder {
  display: grid;
  gap: 4px;
  place-items: center;
  color: var(--muted);
  font-size: 26px;
}

.logo-box__placeholder small {
  font-size: 12px;
}

.logo-box__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}

/* ----------------------------------------------------------- layout box */
.layout-box {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
  overflow: hidden;
}

.layout-box--bordered {
  border: 1px solid var(--border);
}

.layout-box--bg-surface { background: var(--surface); }
.layout-box--bg-soft { background: var(--accent-soft); }
.layout-box--bg-brand { background: var(--brand-primary); color: var(--on-primary); }
.layout-box--bg-dark { background: var(--text); color: #ffffff; }
.layout-box--bg-none { background: transparent; }

.layout-box--bg-brand .prose,
.layout-box--bg-dark .prose,
.layout-box--bg-brand .icon-box__note,
.layout-box--bg-dark .icon-box__note,
.layout-box--bg-brand .icon-box__label,
.layout-box--bg-dark .icon-box__label,
.layout-box--bg-brand .block-heading,
.layout-box--bg-dark .block-heading {
  color: inherit;
}

.layout-box--pad-none { padding: 0; }
.layout-box--pad-sm { padding: 10px; }
.layout-box--pad-md { padding: 18px; }
.layout-box--pad-lg { padding: 28px; }

.layout-box--radius-none { border-radius: 0; }
.layout-box--radius-sm { border-radius: var(--radius-sm); }
.layout-box--radius-md { border-radius: var(--radius); }
.layout-box--radius-lg { border-radius: var(--radius-lg); }

.layout-box--gap-none > .canvas { --canvas-gap: 0px; }
.layout-box--gap-sm > .canvas { --canvas-gap: 8px; }
.layout-box--gap-lg > .canvas { --canvas-gap: 22px; }

.layout-box__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
  flex: none;
}

.layout-box > .canvas {
  flex: 1;
  min-height: 0;
}

.layout-box__empty {
  display: grid;
  place-items: center;
  min-height: 80px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: 14px;
}

/* --------------------------------------------------------- image titles */
.media-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 8px;
  flex: none;
}

.image-box--radius-none .image-box__frame { border-radius: 0; }
.image-box--radius-sm .image-box__frame { border-radius: var(--radius-sm); }
.image-box--radius-lg .image-box__frame { border-radius: var(--radius-lg); }
.image-box--radius-pill .image-box__frame { border-radius: 999px; }

/* ==========================================================================
   Page-builder editor chrome
   ========================================================================== */
.history-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.history-bar__label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-right: 4px;
}

/* ------------------------------------------------------- element library */
.element-library {
  display: grid;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.element-library__group {
  display: grid;
  gap: 7px;
}

.element-library__label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}

.element-library__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.element-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 13px 7px 9px;
  border-radius: 99px;
  border: 1px solid var(--border);
  background: var(--page-bg);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: grab;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.1s ease;
}

.element-chip:hover {
  border-color: var(--brand-accent);
  background: var(--surface);
}

.element-chip:active {
  cursor: grabbing;
  transform: scale(0.97);
}

.element-chip.is-suggested {
  border-color: var(--accent-line);
  background: var(--accent-soft);
}

.element-chip__icon {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 99px;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 12px;
}

.element-chip.is-suggested .element-chip__icon {
  border-color: var(--accent-line);
  color: var(--accent-ink, var(--brand-accent));
}

/* A canvas accepting a dragged element lights up its whole grid. */
.canvas--editing.is-drop-target,
.canvas--editing-nested.is-drop-target {
  outline: 2px dashed var(--brand-accent);
  outline-offset: -2px;
  background-color: var(--accent-soft);
}

body.is-placing-block .canvas--editing {
  border-color: var(--brand-accent);
}

/* ------------------------------------------------------- nested canvases */
.canvas--editing-nested {
  min-height: 90px;
  border-radius: var(--radius-sm);
}

.canvas--editing-nested > .canvas-block {
  position: relative;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: 26px 10px 10px;
  overflow: hidden;
  animation: none;
}

.canvas--editing-nested > .canvas-block:hover {
  border-color: var(--accent-line);
}

.canvas--editing-nested > .canvas-block.is-selected {
  border-style: solid;
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

.canvas--editing-nested .block-toolbar {
  height: 24px;
  font-size: 11px;
}

.layout-box--editing {
  min-height: 100%;
}

.canvas-empty--nested {
  grid-column: 1 / -1;
  display: grid;
  place-items: center;
  min-height: 80px;
  color: var(--muted);
  font-size: 13px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
}

.canvas-empty {
  grid-column: 1 / -1;
}

.block-toolbar__btn--wide {
  width: auto;
  padding: 0 8px;
  font-size: 11px;
  font-weight: 700;
}

/* -------------------------------------------------------- edit / preview */
.mode-toggle {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: 99px;
  background: var(--page-bg);
  border: 1px solid var(--border);
}

.mode-toggle__btn {
  border: 0;
  background: none;
  padding: 5px 15px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
}

.mode-toggle__btn.is-active {
  background: var(--brand-accent);
  color: var(--on-accent);
}

.mode-pane.is-hidden {
  display: none;
}

/* ----------------------------------------------------------- icon picker */
.icon-picker {
  display: grid;
  gap: 8px;
}

.icon-picker__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.icon-chip {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 16px;
}

.icon-chip:hover {
  border-color: var(--brand-accent);
}

.icon-chip.is-active {
  border-color: var(--brand-accent);
  background: var(--accent-soft);
}

.input--icon {
  max-width: 260px;
}

.range {
  width: 100%;
  accent-color: var(--brand-accent);
}

/* -------------------------------------------------------- gallery titles */
.thumb--titled {
  width: 132px;
}

.thumb__title {
  width: 100%;
  border: 0;
  border-top: 1px solid var(--border);
  padding: 5px 7px;
  font: inherit;
  font-size: 12px;
  background: var(--surface);
  color: var(--text);
}

.thumb__title:focus {
  outline: 2px solid var(--brand-accent);
  outline-offset: -2px;
}

/* ==========================================================================
   Sample-layout picker
   ========================================================================== */
.template-picker__lead {
  margin: 0 0 16px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.55;
}

.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: 14px;
}

.template-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.12s ease;
}

.template-card:hover {
  border-color: var(--brand-accent);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.template-card.is-suggested {
  border-color: var(--accent-line);
}

.template-card__frame {
  height: 170px;
  overflow: hidden;
  background: var(--page-bg);
  border-bottom: 1px solid var(--border);
  position: relative;
}

/* The preview is the real renderer at 1200px, scaled down â€” so a sample
   layout cannot look different from what choosing it produces. */
.template-card__preview {
  width: 1200px;
  transform: scale(0.208);
  transform-origin: top left;
  padding: 18px;
  pointer-events: none;
}

.template-card__blank {
  color: var(--muted);
  font-size: 22px;
}

.template-card__meta {
  display: grid;
  gap: 3px;
  padding: 12px 14px 14px;
}

.template-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.template-card__name {
  font-weight: 700;
  font-size: 16px;
}

.template-card__desc {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.45;
}

.template-picker__foot {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

/* --------------------------------------------------------- brand controls */
.swatch-field__row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.swatch-field__picker {
  width: 46px;
  height: 38px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}

.swatch-field__hex {
  max-width: 130px;
  text-transform: uppercase;
}

/* ==========================================================================
   Responsive behaviour for the new elements
   ========================================================================== */
@media (max-width: 900px) {
  /* Nested canvases stack with their parent, keeping reading order. */
  .canvas--nested {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: auto;
  }

  /* Excluded for the same reason as the top-level blocks above. */
  .canvas--nested > .canvas-block:not(.fit-slide__inner .canvas-block) {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
  }

  .hero {
    padding: 32px 26px;
  }

  .hero--full {
    min-height: 420px;
  }

  .kpi-grid--fixed {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
  }
}

@media (max-width: 640px) {
  .hero {
    padding: 26px 20px;
    border-radius: var(--radius);
  }

  .hero--sm,
  .hero--md { min-height: 200px; }

  .hero--lg,
  .hero--full { min-height: 300px; }

  .kpi-grid,
  .kpi-grid--fixed {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));
  }

  .kpi {
    padding: 14px 15px;
  }

  .button-row {
    width: 100%;
  }

  .btn--cta {
    flex: 1 1 auto;
    justify-content: center;
  }

  .template-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   Small elements
   A logo or an icon may be one column wide and one row tall. At that size the
   block chrome has to get out of the way, or the toolbar is the whole element.
   ========================================================================== */
.canvas--editing .canvas-block.is-compact,
.canvas--editing-nested > .canvas-block.is-compact {
  padding: 6px;
  overflow: visible;
}

/* The toolbar floats above a compact block and only on hover, so a small logo
   shows as a small logo instead of as a title bar with a picture under it. */
.canvas-block.is-compact .block-toolbar {
  top: auto;
  bottom: calc(100% + 3px);
  height: 26px;
  width: max-content;
  min-width: 100%;
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 8;
}

.canvas-block.is-compact:hover .block-toolbar,
.canvas-block.is-compact:focus-within .block-toolbar,
.canvas-block.is-compact.is-selected .block-toolbar {
  opacity: 1;
  pointer-events: auto;
}

/* Its label is noise at this size â€” the icon and the buttons are enough. */
.canvas-block.is-compact .block-toolbar__label {
  display: none;
}

.canvas-block.is-compact .canvas-block__size {
  display: none;
}

/* A logo tile scales with its block rather than holding a floor of its own,
   so shrinking the block genuinely shrinks the logo. */
.logo-box__frame {
  min-height: 28px;
}

.logo-box__frame--pad-md { padding: 12px; }
.logo-box__frame--pad-lg { padding: 20px; }

.logo-box__placeholder {
  font-size: 20px;
  line-height: 1.1;
}

/* Below this the "upload a logo" caption cannot fit; the glyph carries it. */
.canvas-block.is-compact .logo-box__placeholder small,
.canvas-block.is-compact .logo-box__title {
  display: none;
}

.canvas-block.is-compact .icon-box {
  align-items: center;
  justify-content: center;
  height: 100%;
}

/* ==========================================================================
   Gallery: posters and titled images
   ========================================================================== */

/* Posters and certificates carry text to the edge â€” fit them whole instead of
   cropping them to a uniform grid. */
.gallery--contain .gallery__item img {
  object-fit: contain;
  background: var(--page-bg);
}

/*
 * A filename stays a hover affordance, but a title the admin typed is content:
 * it sits under its image permanently, because a projector audience has no
 * pointer to hover with.
 */
.gallery--titled .gallery__meta {
  position: static;
  opacity: 1;
  background: none;
  color: var(--muted);
  text-align: center;
  padding: 7px 4px 0;
  font-size: 12.5px;
  line-height: 1.35;
  display: block;
}

.gallery--titled .gallery__item {
  display: flex;
  flex-direction: column;
}

/* The right-aligned hero needs its gradient mirrored; a flat wash over the
   whole photograph was dimming the picture instead of backing the words. */
.hero--right .hero__scrim {
  background: linear-gradient(270deg, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.55) 60%, rgba(0, 0, 0, 0.3) 100%);
}

.hero--center .hero__scrim {
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.86) 0%, rgba(0, 0, 0, 0.62) 100%);
}

/* ==========================================================================
   Fit to screen â€” a deck is paged, never scrolled
   The slide is laid out at a fixed nominal width and scaled to the space
   available, so a section looks identical on a laptop and on a projector and
   the presenter never has to reach for a scrollbar.
   ========================================================================== */
.shell--fit {
  height: 100vh;
  min-height: 0;
  overflow: hidden;
}

.shell--fit .main {
  min-height: 0;
  height: 100vh;
  overflow: hidden;
}

.stage--fit {
  position: relative;
  flex: 1;
  min-height: 0;
  padding: 0;
  overflow: hidden;
}

.fit-slide {
  position: absolute;
  inset: 0;
  display: grid;
  /* The slide is laid out at its full nominal size, which is wider than the
     frame, so there is no free space here for alignment to distribute — the
     centring is done by the translate in FitSlide instead. Anchoring to the
     content corner is what that translate measures from. */
  place-items: start;
  overflow: hidden;
  padding: 18px 20px;
}

.fit-slide__inner {
  transform-origin: top left;
  will-change: transform;
}

/* Inside the frame the slide takes the full nominal width â€” the frame, not the
   slide, is what limits the measure now. */
.fit-slide__inner .slide {
  width: 100%;
  max-width: none;
  margin: 0;
  min-height: 0;
}

/* The quick-add bar sits below the fitted slide at its own scale. */
.slide-tools--bar {
  flex: none;
  margin: 0;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
  background: var(--surface);
}

/* Presentation mode: the slide owns the whole screen, edge to edge. */
body.is-presenting .fit-slide {
  padding: 0;
}

/*
 * The padding must match the normal slide exactly. The layout pass measures
 * how many rows each block of text needs at one specific canvas width, and a
 * wider gutter here would rewrap that text onto an extra line with no row left
 * to put it on.
 */
body.is-presenting .fit-slide__inner .slide {
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 44px 48px 52px;
}

body.is-presenting .stage--fit {
  padding: 0;
}

/*
 * Collapsed, the editing view IS the presenting view (2026-09-18, on request:
 * "even though if I collapse the tabs it should look like full screen"). These
 * repeat the four rules above for the rail rather than widening their
 * selectors, so that presenting itself cannot be changed by accident here.
 *
 * The slide's own padding has to be the presenting 44/48/52 and not the
 * editor's 48/52/56. It is not a cosmetic difference: every block's row count
 * is measured at one exact canvas width, so a gutter 4px wider rewraps a line
 * of text onto a row that does not exist. Matching it is also the point — what
 * the pane collapsed shows is then what the room will see, to the pixel.
 */
:root[data-nav-rail='1'] .shell--fit .fit-slide {
  padding: 0;
}

:root[data-nav-rail='1'] .shell--fit .stage--fit {
  padding: 0;
}

:root[data-nav-rail='1'] .shell--fit .fit-slide__inner .slide {
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 44px 48px 52px;
}

:root[data-nav-rail='1'] .shell--fit .fit-slide__inner .slide--hero {
  padding: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  height: 100% !important;
}

/* A projector is 16:9; leave room for the deck controls at the bottom. */
body.is-presenting .shell--fit .main {
  height: 100vh;
}

@media (max-width: 860px) {
  /* Below the stacking breakpoint the nav becomes a strip and the slide needs
     to scroll again â€” scaling a full deck onto a phone would be unreadable. */
  .shell--fit,
  .shell--fit .main {
    height: auto;
    overflow: visible;
  }

  .stage--fit {
    position: static;
    overflow: visible;
    min-height: 60vh;
  }

  .fit-slide {
    position: static;
    padding: 16px;
  }

  .fit-slide__inner {
    width: 100% !important;
    transform: none !important;
  }
}

/*
 * Inside the fit frame the grid rows are a fixed height rather than a minimum.
 *
 * With `auto` tracks a photograph sizes itself to its natural aspect ratio,
 * because `height: 100%` has nothing definite to resolve against â€” so the row
 * silently grows, the slide ends up hundreds of pixels taller than the layout
 * says it is, and the scale that was calculated for it no longer fits. Fixed
 * tracks make a section's height exactly what its rows declare, which is what
 * lets the whole slide be scaled onto one screen with confidence.
 *
 *
 * Fixed tracks mean a block's reserved rows are the whole story, so anything
 * that reflows its content taller spills outside the slide instead of pushing
 * the card down. That is why the viewport breakpoints are kept out of the frame
 * further down: the slide is this one width whatever the window is.
 *
 * Fixed only while the slide is actually being scaled, though — below the 861px
 * cutoff the fit is switched off and the slide becomes a scrolling responsive
 * page, where a fixed row would clip content that is no longer scaled to fit.
 */
@media (min-width: 861px) {
  .fit-slide__inner .canvas {
    grid-auto-rows: var(--row-height, 28px);
  }
}

.fit-slide__inner .canvas-block {
  min-height: 0;
}

/* Media is clipped to its box — that is what "fill the frame" means for a
   photograph. Text is left visible: if a measurement is ever a pixel short,
   the words should still be readable rather than sliced. */
.fit-slide__inner .canvas-block--image,
.fit-slide__inner .canvas-block--video,
.fit-slide__inner .canvas-block--profile,
.fit-slide__inner .canvas-block--logo,
.fit-slide__inner .canvas-block--hero,
.fit-slide__inner .canvas-block--box {
  overflow: hidden;
}

/* Media fills the box it is given rather than dictating it. */
.fit-slide__inner .image-box__frame,
.fit-slide__inner .image-box__frame img,
.fit-slide__inner .logo-box__frame,
.fit-slide__inner .video-box__frame {
  min-height: 0;
}

.fit-slide__inner .image-box,
.fit-slide__inner .logo-box,
.fit-slide__inner .video-box,
.fit-slide__inner .profile-box {
  height: 100%;
}

/* ==========================================================================
   Type inside the fit frame is fixed, not viewport-relative
   ==========================================================================
   The slide is a fixed 1600px design that gets scaled as one piece, so a font
   sized in `vw` would change with the browser window *inside* a canvas whose
   row heights were measured once. Text that grows after its rows were measured
   spills over the element below it â€” which is exactly what happened between a
   1400px measuring pass and a 1920px projector.
   Each value below is what its clamp() resolves to at the 1600px design width,
   so nothing moves between the editor, the laptop and the projector; only the
   scale factor changes.
   ========================================================================== */
.fit-slide__inner .slide__title {
  font-size: 46px;
}

.fit-slide__inner .hero__heading {
  font-size: 52px;
}

.fit-slide__inner .hero__subheading {
  font-size: 21px;
}

.fit-slide__inner .kpi__value {
  font-size: 44px;
}

.fit-slide__inner .stat__value {
  font-size: 44px;
}

/*
 * Presentation mode must not resize anything either. Those rules date from when
 * the slide was a scrolling page; now that it is scaled to fit, they would
 * enlarge the type a second time.
 */
body.is-presenting .fit-slide__inner .slide__title {
  font-size: 46px;
}

body.is-presenting .fit-slide__inner .prose,
body.is-presenting .fit-slide__inner .bullets li {
  font-size: 1em;
}

body.is-presenting .fit-slide__inner .stat__value {
  font-size: 44px;
}

body.is-presenting .fit-slide__inner .canvas {
  --canvas-gap: 16px;
}

body.is-presenting .fit-slide__inner .gallery {
  --gap: 18px;
}

body.is-presenting .fit-slide__inner .profile-box__frame {
  max-width: none;
}

/* ==========================================================================
   The whole block is a drag handle
   Reaching for the little grip is a learned behaviour; grabbing the thing you
   want to move is the instinct. The block itself now drags, so the cursor has
   to say so.
   ========================================================================== */
.canvas--editing .canvas-block,
.canvas--editing-nested > .canvas-block {
  cursor: grab;
}

.canvas--editing .canvas-block.is-moving,
.canvas--editing-nested > .canvas-block.is-moving {
  cursor: grabbing;
}

/* Controls inside a block keep their own cursor â€” they are still clickable,
   because a press only becomes a drag once the pointer has travelled. */
.canvas--editing .canvas-block button,
.canvas--editing .canvas-block a,
.canvas--editing-nested > .canvas-block button {
  cursor: pointer;
}

.canvas--editing .resize-handle {
  cursor: nwse-resize;
}

.canvas--editing .resize-handle--e { cursor: ew-resize; }
.canvas--editing .resize-handle--s { cursor: ns-resize; }

/* A newly placed element announces itself, so adding to a full section does
   not feel like nothing happened. */
@keyframes block-landed {
  0% { box-shadow: 0 0 0 3px var(--brand-accent); }
  100% { box-shadow: 0 0 0 3px transparent; }
}

.canvas-block.is-landed {
  animation: block-landed 1.2s ease-out 1;
}

/* ==========================================================================
   The fitted slide is one fixed width, so the window's breakpoints stop here

   Every breakpoint in this file answers the size of the window. The fitted
   slide does not: above the 861px cutoff it is laid out at the nominal width
   and scaled to the frame, so it is 1600px wide on a 1000px laptop just as it
   is on a projector. Left alone, a narrow window switched the slide's own
   interior to the narrow rules — a gallery dropped from five across to three,
   blocks stacked into one column — while the slide stayed 1600px wide. The
   reflowed content then outgrew the rows its blocks reserve, and because those
   rows are fixed here it painted outside the card rather than pushing it taller.

   So the wide layout is restored for the slide's interior, at the one width the
   slide is ever laid out at. Below the cutoff none of this applies: the fit is
   off, the slide is a normal responsive page, and the breakpoints above are
   exactly right for it again.
   ========================================================================== */
@media (min-width: 861px) {
  .fit-slide__inner .slide {
    padding: 44px 48px 52px;
  }

  .fit-slide__inner .canvas,
  .fit-slide__inner .canvas--nested {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .fit-slide__inner .gallery__item {
    width: calc((100% - 4 * var(--gap)) / 5);
  }

  .fit-slide__inner .gallery--count-1 .gallery__item,
  .fit-slide__inner .gallery--count-2 .gallery__item {
    width: calc((100% - var(--gap)) / 2);
  }

  .fit-slide__inner .gallery--count-3 .gallery__item,
  .fit-slide__inner .gallery--count-4 .gallery__item {
    width: calc((100% - 2 * var(--gap)) / 3);
  }

  .fit-slide__inner .hero {
    padding: 44px 48px;
  }

  .fit-slide__inner .hero--full {
    min-height: min(72vh, 620px);
  }

  .fit-slide__inner .kpi-grid--fixed {
    grid-template-columns: repeat(var(--kpi-columns, 3), minmax(0, 1fr));
  }

  .fit-slide__inner .image-box__frame {
    min-height: 140px;
  }

  .fit-slide__inner .video-box__frame {
    min-height: 160px;
  }
}

/* Below the cutoff the fit is off, so the slide stacks like any other page. The
   two rules this restores had to exclude the frame by selector rather than be
   overridden here, because their !important outranks the inline grid placement
   the canvas puts on each block. */
@media (max-width: 860px) {
  .fit-slide__inner .canvas-block {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
  }
}

/* ==========================================================================
   Leader hero — the editorial opening spread for a founder or CEO.
   Type on the left, portrait on the right with the surname ghosted behind it
   and pill tags floating over the shoulders. Brand tokens throughout, so it
   reads correctly in either organization's palette.
   ========================================================================== */
.leader-hero {
  /* The block owns an editorial ground of its own rather than the page white,
     because the portrait is a cutout that has to sit *in* something. Accent
     comes from the colour the organization leads with — the same token the nav
     uses — so this reads green for Technical Hub and orange for Torii. */
  --leader-ground: #f5f2ec;
  --leader-ink: #201e1d;
  --leader-accent: var(--nav-accent, var(--brand-accent));
  --leader-accent-ink: var(--nav-accent-ink, var(--accent-ink));
  /* Translucent derivatives of whatever the lead colour turned out to be. */
  --leader-rim: color-mix(in srgb, var(--leader-accent) 55%, transparent);
  --leader-glow: color-mix(in srgb, var(--leader-accent) 22%, transparent);
  --leader-tail: color-mix(in srgb, var(--leader-accent) 55%, transparent);

  position: relative;
  display: flex;
  align-items: center;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--leader-ground);
  color: var(--leader-ink);
}

/* The composition, held to a shape the portrait can be laid out against.
   Presenting stretches the slide to the screen, and the cutout is `contain`-fitted
   into a column that does not widen with it — so past roughly 1.43 the column
   outgrows the cutout's own ratio, `contain` flips to fitting by width, and the
   figure shrinks and slides down the column while the pills, anchored to the
   column, stay where they were.

   1000px is the 16:10 floor written as a length: the slide is laid out at a
   nominal 1600 wide whatever the screen (FitSlide scales the whole thing
   afterwards), so 1600 ÷ 1.6 is a constant here. It has to be a length and not
   `aspect-ratio` — an aspect against the definite 1600 width would also become
   the stage's *intrinsic* height, and FitSlide measures exactly that to decide
   whether the slide can fill the display. A 1000px intrinsic against a 900px
   16:9 screen reads as overflow, and the slide letterboxes itself on the one
   shape it was drawn for. min-height keeps that measurement where it was.

   The floor is inert at 16:9 and at 16:10 — the two shapes a deck is shown at —
   and on anything taller the stage centres itself while the block's cream
   carries on past it, so there is no band to see. */
.leader-hero__stage {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  height: 100%;
  min-height: 860px;
  max-height: 1000px;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
}

.leader-hero__pane {
  position: relative;
  z-index: 2;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 20px;
  padding: 48px 24px 48px 56px;
  min-width: 0;
}

/* ------------------------------------------------------------- eyebrow */
.leader-hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 14px;
  animation: block-in 0.6s cubic-bezier(0.22, 0.68, 0, 1) 0.05s both;
}

/* 0.4 alpha measured 2.4:1 on the cream ground — far under 4.5:1 for a label
   this small. 0.66 lands at 5.1:1 and still reads as the quiet tier. */
.leader-hero__index {
  color: rgba(32, 30, 29, 0.66);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.34em;
}

.leader-hero__rule {
  width: 44px;
  height: 1.5px;
  background: rgba(32, 30, 29, 0.25);
}

/* 11.5px uppercase is small text, so this takes the ink derivative — the raw
   accent measures 3.2:1 on the cream ground and fails. */
.leader-hero__kicker {
  color: var(--leader-accent-ink);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- name
   The name signs itself: the outline strokes on, then the solid letterform
   fades in over it. Each line is an SVG whose viewBox is fitted to its glyphs
   at runtime (see LeaderHero.js), so the two lines share one cap height and a
   substituted display face cannot crop the surname. Height drives the size;
   width follows from the viewBox ratio. */
.leader-hero__name {
  display: grid;
  gap: 2px;
  justify-items: start;
}

.leader-hero__name-line {
  max-width: 100%;
  line-height: 0;
}

/* A flat size, not a viewport clamp. The slide is laid out at a nominal 1600 and
   then scaled as a whole, so `vw` here reads the real window rather than the
   canvas and quietly resized the name between screens — 58px in a small window,
   86px on a projector, against spacing that never moved. 90px is the old ceiling
   plus the 4px asked for, and now it is the size everywhere. */
.leader-hero__draw {
  display: block;
  height: 90px;
  width: auto;
  max-width: 100%;
  overflow: visible;
}

.leader-hero__draw text {
  font-family: 'Agency FB', 'Oswald', 'Saira Condensed', 'Arial Narrow', sans-serif;
  font-weight: 700;
}

/* Dashing the outline shorter than the glyph run makes several strokes travel
   at once, which is what gives it the handwritten rather than plotted feel. */
.leader-hero__draw-stroke {
  stroke-dasharray: 1400;
  stroke-dashoffset: 1400;
  stroke-linejoin: round;
  stroke-linecap: round;
  animation: leader-draw 1.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}

.leader-hero__draw-fill {
  opacity: 0;
  animation: leader-draw-fill 0.9s ease both;
}

@keyframes leader-draw {
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes leader-draw-fill {
  to {
    opacity: 1;
  }
}

/* ------------------------------------------------------------- the copy */
.leader-hero__tagline {
  margin: 6px 0 0;
  max-width: 32ch;
  color: rgba(32, 30, 29, 0.85);
  font-size: clamp(20px, 1.8vw, 26px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  animation: block-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) 0.42s both;
}

.leader-hero__body {
  margin: 0;
  max-width: 46ch;
  color: rgba(32, 30, 29, 0.65);
  font-size: 15.5px;
  line-height: 1.75;
  animation: block-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) 0.52s both;
}

/* ---------------------------------------------------- the career in figures */
/* Under the social row. A hairline rule above sets them apart from the links
   without drawing a box, the figure takes the deck's accent ink and the line
   under it stays in the body's grey — so the pair reads as two facts, not as a
   badge. Arrives after the socials, on the same easing as everything else in
   the pane. */
.leader-hero__marks {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 34px;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid rgba(32, 30, 29, 0.14);
  max-width: 46ch;
  animation: block-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) 0.74s both;
}

.leader-hero__mark {
  display: grid;
  gap: 3px;
  position: relative;
  padding-left: 14px;
}

/* The accent, as a rule rather than as a fill: the deck reserves filled accent
   for what is active, and these are facts. */
.leader-hero__mark::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  border-radius: 3px;
  background: var(--nav-accent, var(--brand-accent, #E95A22));
}

.leader-hero__mark-value {
  color: var(--accent-ink, #B4451A);
  font-family: var(--display, inherit);
  font-size: 25px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.01em;
}

.leader-hero__mark-label {
  color: rgba(32, 30, 29, 0.66);
  font-size: 13px;
  line-height: 1.35;
}

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

/* ------------------------------------------------------------- socials */
.leader-hero__follow {
  display: grid;
  gap: 12px;
  margin-top: 8px;
  animation: block-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) 0.62s both;
}

.leader-hero__follow-label {
  color: rgba(32, 30, 29, 0.66);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.leader-hero__socials {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Yellow, as asked. The glyph carries the brand gold on a white disc rather than the
   gold becoming the disc: gold behind a white glyph measures 1.7:1, which is the one
   thing the palette rule in this file forbids. On white the same gold reads as the
   brand and the ring gives it an edge. */
.leader-hero__social {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 1.5px solid color-mix(in srgb, var(--brand-accent) 42%, rgba(32, 30, 29, 0.12));
  border-radius: 999px;
  background: #ffffff;
  color: var(--brand-accent);
  font: inherit;
  cursor: pointer;
  overflow: hidden;
  transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

/* Hover lifts and rings it in gold rather than filling the disc: a gold fill behind a
   full-colour brand logo muddies every one of them. */
.leader-hero__social:hover {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-accent) 26%, transparent);
  transform: translateY(-2px);
}

/* Supplied artwork, fitted to the disc. `contain` because these arrive at five
   different aspect ratios - LinkedIn and Facebook are circular badges, YouTube is a
   rounded oblong, Oracle is a wide wordmark - and a crop would clip the ones that are
   not square. */
/* Nearly filling the disc. At 24px in a 40px disc these read as specks on a slide seen
   from the back of a room - the logo is the thing being recognised, so it gets the
   space and the ring is just an edge. */
.lh-logo {
  display: block;
  width: 38px;
  height: 38px;
  object-fit: contain;
}

/* The wordmark gets the width it needs. At 24px square ORACLE is a red smudge; on the
   oblong it is legible at the same height as the glyphs beside it. */
.leader-hero__social--word .lh-logo {
  width: 92px;
  height: 24px;
}

/* Oracle is set as type, so its disc is a rounded oblong rather than a circle -
   a six-letter word cannot be squeezed into 40px round and stay legible. */
.leader-hero__social--word {
  width: auto;
  padding: 0 15px;
  border-radius: 999px;
}

.lh-oracle {
  font-family: var(--display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.12em;
  line-height: 1;
}

/* ------------------------------------------------------- the in-place viewer
   Covers the hero rather than replacing the slide, so the deck does not navigate and
   the back button is the only way out - which is what makes it feel like a panel
   rather than a page. Absolute inside .leader-hero, which is the positioned ancestor.

   It holds a card about the destination rather than the destination itself. All five
   of these sites refuse to be framed - Instagram and Facebook answer
   X-Frame-Options: DENY, YouTube SAMEORIGIN, LinkedIn blocks bots outright and
   academy.oracle.com 403s - so an iframe here would be a blank rectangle. The deck
   also presents with no network, which settles it. */
.lh-view {
  position: absolute;
  inset: 0;
  z-index: 12;
  display: grid;
  /* Bar, then the frame, then the strip that explains a blank one. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  background: rgba(247, 246, 243, 0.97);
  backdrop-filter: blur(10px);
  opacity: 0;
  transform: translateY(10px) scale(0.995);
  transition: opacity 0.22s ease, transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}

.lh-view[hidden] {
  display: none;
}

.lh-view.is-open {
  opacity: 1;
  transform: none;
}

.lh-view__bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 26px;
  border-bottom: 1px solid rgba(32, 30, 29, 0.1);
}

.lh-view__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px 8px 11px;
  border: 0;
  border-radius: 999px;
  background: #201e1d;
  color: #ffffff;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s ease, transform 0.18s ease;
}

.lh-view__back:hover {
  background: var(--brand-accent);
  color: #201e1d;
  transform: translateX(-2px);
}

.lh-view__crumb {
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.lh-view__host {
  color: var(--muted);
  font-family: var(--mono);
  font-size: 12px;
}

.lh-view__out {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 8px 14px;
  border-radius: 999px;
  background: #201e1d;
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.18s ease;
}

.lh-view__out:hover {
  background: var(--brand-accent);
  color: #201e1d;
}

/* The frame's own ground. White rather than the panel's warm grey, so a site that does
   render is not tinted by the deck, and a site that refuses reads as plainly empty
   instead of looking like a styling mistake. */
.lh-view__stage {
  min-height: 0;
  margin: 0 18px;
  border: 1px solid rgba(32, 30, 29, 0.12);
  border-radius: 14px;
  background: #ffffff;
  overflow: hidden;
}

.lh-view__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Shown when the frame is refused. The site is not coming, so this says which site,
   why, and gives the one control that does work - rather than leaving a white
   rectangle that looks like a bug in the deck. */
.lh-view__blocked {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 15px;
  height: 100%;
  padding: 30px;
  text-align: center;
}

.lh-view__blocked .lh-logo {
  width: 64px;
  height: 64px;
}

.lh-view__blocked-word .lh-logo {
  width: 150px;
  height: 40px;
}

.lh-view__blocked h3 {
  margin: 0;
  font-size: 30px;
}

.lh-view__blocked p {
  max-width: 56ch;
  margin: 0;
  color: var(--muted);
  font-size: 16px;
  line-height: 1.55;
}

.lh-view__blocked .lh-view__out {
  margin-left: 0;
  padding: 12px 22px;
  font-size: 15px;
}

.lh-view__url {
  margin: 0;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 12.5px;
  word-break: break-all;
}

.lh-view__hint {
  margin: 0;
  padding: 11px 22px 15px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
  text-align: center;
}

.lh-view__mark {
  display: grid;
  place-items: center;
  min-width: 62px;
  height: 62px;
  padding: 0 16px;
  border-radius: 999px;
  background: var(--brand-accent);
  color: #201e1d;
}

/* In the bar it is a badge beside the crumb, not the hero of a card. White, because a
   brand logo on the accent gold fights it. */
.lh-view__mark--sm {
  min-width: 30px;
  height: 30px;
  padding: 0 7px;
  border: 1px solid rgba(32, 30, 29, 0.12);
  background: #ffffff;
}

.lh-view__mark--sm .lh-logo {
  width: 18px;
  height: 18px;
}

.lh-view__mark--sm.lh-view__mark--word {
  padding: 0 10px;
}

.lh-view__mark--sm.lh-view__mark--word .lh-logo {
  width: 48px;
  height: 13px;
}

.lh-view__mark .ic {
  width: 26px;
  height: 26px;
}

.lh-view__mark--sm .ic {
  width: 15px;
  height: 15px;
}

.lh-view__mark--sm .lh-oracle {
  font-size: 10px;
}

@media (prefers-reduced-motion: reduce) {
  .lh-view {
    transition: none;
  }
}

.leader-hero__social:hover {
  transform: translateY(-2px);
  border-color: var(--leader-accent);
  background: var(--leader-accent);
  color: var(--nav-on-accent, #ffffff);
}

/* ------------------------------------------------------------- portrait */
/* No overflow clip here: the pills sit deliberately outside the column and the
   figure stands taller than it, and .leader-hero already bounds the block.
   The right gutter is what buys the pills their overhang — they anchor to this
   box, and .leader-hero clips, so without it the ones at `right: -1%` lose
   their rounded end to the block edge. Margin rather than padding: an
   absolutely positioned child measures from the padding box, which padding
   would not move. Kept off the grid so the left pane never rewraps. */
.leader-hero__figure {
  position: relative;
  z-index: 1;
  min-width: 0;
  min-height: 0;
  margin-right: 48px;
}

/* Ghosted surname watermark — spans the block edge to edge and sits behind both
   columns. The size is fitted to the block in LeaderHero.js rather than clamped
   to the viewport, which is what lets it reach both edges at any slide width. */
/* Centred both ways by flex on a full-bleed overlay, not by a translate —
   block-in animates transform to `none` and would wipe a centring transform. */
.leader-hero__watermark {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 0.78;
  pointer-events: none;
  animation: block-in 1.1s ease 0.3s both;
}

.leader-hero__watermark-text {
  color: transparent;
  background: linear-gradient(180deg, rgba(32, 30, 29, 0.07), rgba(32, 30, 29, 0.025));
  -webkit-background-clip: text;
  background-clip: text;
  font-size: 200px; /* probe value; replaced once the block is measured */
  font-weight: 800;
  line-height: 0.78;
  font-family: var(--display);
  white-space: nowrap;
}

/* The figure and everything shaped by it. `contain` + `bottom center` must match
   the mask sizing on every layer below, or the rim light slides off the shoulder. */
.leader-hero__portrait-wrapper {
  position: absolute;
  left: 50%;
  bottom: -3%;
  transform: translateX(-50%);
  /* Height drives the size, deliberately. A width-based `min()` flips between
     its two branches as the slide narrows, which resizes the figure and drops
     the head to a different line on every screen. Sizing off the stage's own
     height keeps him at one place in the frame everywhere.
     100.5% puts the crown of the head at 3.96% of the stage: the crown sits
     1.43% into the asset, so crown = 103% (the bottom bleed) − 100.5% × 98.57%.
     Raised from 96.2%, which sat him 2.9% lower — a head's worth of dead cream
     above him that the eye read as the figure sagging out of the frame.
     Width is 138% purely so it can never be the binding dimension — at this
     height the photo is as wide as the column, and a 100% box would sit exactly
     on the boundary where `contain` switches to fitting by width. It has to
     clear 130% because the stage's 16:10 floor is where the column is at its
     tallest relative to its width, and there the photo wants 129.5%. */
  width: 138%;
  height: 100.5%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: leader-portrait-in 1s cubic-bezier(0.22, 0.68, 0, 1) 0.3s both;
}

.leader-hero__portrait {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
  display: block;
  filter: contrast(1.04) saturate(1.03);
}

/* A cutout dropped straight onto a flat ground reads as a sticker. These layers
   reuse the portrait's own alpha as a mask so each one is clipped to the
   silhouette. */
.leader-hero__layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-image: var(--portrait);
  mask-image: var(--portrait);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: bottom center;
  mask-position: bottom center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Two ground-coloured halos, scaled a hair past the figure and blurred, so the
   cut edge dissolves into the page instead of ending on a hard line. */
.leader-hero__feather--tight {
  transform: scale(1.016);
  background: var(--leader-ground);
  filter: blur(2.5px);
  opacity: 0.95;
}

.leader-hero__feather--soft {
  transform: scale(1.03);
  background: var(--leader-ground);
  filter: blur(14px);
  opacity: 0.6;
}

/* Rim light: accent raking across one shoulder, warm bounce on the other.
   Screen-blended so it lifts the highlights and leaves the midtones alone. */
.leader-hero__rim {
  mix-blend-mode: screen;
  opacity: 0.5;
  background:
    linear-gradient(105deg, var(--leader-rim) 0%, transparent 38%),
    linear-gradient(255deg, rgba(255, 214, 170, 0.4) 0%, rgba(255, 214, 170, 0) 34%);
}

/* Grounds the figure — without it the suit floats off the bottom edge. */
.leader-hero__shade {
  mix-blend-mode: multiply;
  opacity: 0.35;
  background: linear-gradient(180deg, rgba(32, 30, 29, 0) 55%, rgba(32, 30, 29, 0.5) 100%);
}

.leader-hero__glow {
  position: absolute;
  left: 50%;
  top: 46%;
  transform: translate(-50%, -50%);
  width: 78%;
  height: 62%;
  border-radius: 999px;
  background: radial-gradient(closest-side, var(--leader-glow), transparent 70%);
  filter: blur(28px);
  pointer-events: none;
}

@keyframes leader-portrait-in {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(20px) scale(1.02);
  }
  to {
    opacity: 1;
    transform: translateX(-50%);
  }
}

.leader-hero__portrait-empty {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--muted);
  font-size: 13px;
}

/* ----------------------------------------------------------------- tags */
.leader-hero__tag {
  position: absolute;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 18px;
  border: 1px solid rgba(32, 30, 29, 0.12);
  border-radius: 999px;
  background: #ffffff;
  box-shadow: 0 10px 30px rgba(32, 30, 29, 0.12);
  color: #201e1d;
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
  animation:
    leader-tag-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) var(--tag-delay, 1s) both,
    leader-float 5s ease-in-out calc(var(--tag-delay, 1s) + 0.7s) infinite;
}

.leader-hero__tag .ic {
  color: var(--leader-accent-ink);
}

.leader-hero__tag--solid {
  border-color: var(--leader-accent);
  background: var(--leader-accent);
  color: var(--nav-on-accent, #ffffff);
}

.leader-hero__tag--solid .ic {
  color: currentColor;
}

/* The tail fades away from the pill and toward the figure it points at, so its
   direction flips with the side the pill sits on. */
.leader-hero__tag-tail {
  flex: none;
  width: 26px;
  height: 1.5px;
  background: linear-gradient(90deg, var(--leader-tail), transparent);
}

.leader-hero__tag--right .leader-hero__tag-tail {
  background: linear-gradient(270deg, var(--leader-tail), transparent);
}

.leader-hero__tag--solid .leader-hero__tag-tail {
  background: linear-gradient(270deg, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0));
}

@keyframes leader-tag-in {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes leader-float {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-9px);
  }
}

/* The global reduce rule collapses durations but leaves delays intact, and this
   block stages itself out to 1.9s — long enough that the name would sit blank
   while someone waited on it. Land everything at once instead. */
@media (prefers-reduced-motion: reduce) {
  .leader-hero *,
  .leader-hero *::before,
  .leader-hero *::after {
    animation-delay: 0s !important;
  }

  .leader-hero__draw-stroke {
    stroke-dashoffset: 0;
  }

  .leader-hero__draw-fill {
    opacity: 1;
  }

  .leader-hero__tag {
    animation: none !important;
  }
}

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--leader-hero {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .leader-hero {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

/* Stacked, off the slide canvas — the aspect floor is a presenting concern, so
   the stage gives it up here and takes its height from the stacked content. */
@media (max-width: 900px) {
  .leader-hero:not(.fit-slide__inner .leader-hero) .leader-hero__stage {
    grid-template-columns: minmax(0, 1fr);
    aspect-ratio: auto;
    height: 100%;
  }
  .leader-hero:not(.fit-slide__inner .leader-hero) .leader-hero__pane {
    padding: 30px 24px;
  }
  .leader-hero:not(.fit-slide__inner .leader-hero) .leader-hero__figure {
    min-height: 380px;
  }
}

/* ==========================================================================
   Milestone timeline — a history the presenter walks one stop at a time.
   The year is an odometer: only the characters that change roll (see
   MilestoneTimeline.js). Every size here is a flat nominal value, never a
   viewport unit — the slide is laid out at 1600 and scaled as a whole, so a
   `vw` would read the window and resize the type between screens.
   ========================================================================== */
.milestones {
  --ms-lead: var(--nav-accent, var(--brand-accent));
  --ms-ink: var(--nav-accent-ink, var(--accent-ink));
  --ms-line: color-mix(in srgb, var(--ms-lead) 22%, transparent);
  /* The year is the slide. At 148px it read as a caption sitting in a white
     field; the content on a stop is only ever a title and a few lines, so if
     the odometer does not carry the page nothing does. */
  --od-cell: 300px;

  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
}

/* Same floor as the leader hero, and for the same reason: a length rather than
   an aspect, because an aspect against the definite 1600 width also becomes the
   intrinsic height FitSlide measures to decide whether the slide may fill the
   display — and a 1000px intrinsic on a 900px screen letterboxes the one shape
   the deck was drawn for. */
/* A column rather than three even rows. A stop carries a title and a handful of
   lines, so there is always slack on a 16:9 slide — centring the middle row
   split that slack in two and hung the composition in the middle of nowhere.
   Anchoring the content near the top and letting `margin-top: auto` push the
   rail to the floor puts all the air in one place, below the reading, where it
   reads as margin instead of as a gap. */
.milestones__inner {
  flex: 0 0 auto;
  width: 100%;
  height: 100%;
  min-height: 860px;
  max-height: 1000px;
  display: flex;
  flex-direction: column;
  padding: 44px 64px 40px;
}

/* ------------------------------------------------------------------- head */
.milestones__head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 6px 16px;
  animation: block-in 0.6s cubic-bezier(0.22, 0.68, 0, 1) 0.05s both;
}

.milestones__kicker {
  grid-column: 1;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ms-ink);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.milestones__heading {
  grid-column: 1;
  margin: 6px 0 0;
  font-size: 40px;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

/* Tabular, so the counter does not jitter sideways as the stop number changes. */
.milestones__counter {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  color: var(--muted);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ stage */
.milestones__stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 56px;
  margin-top: 46px;
  min-height: 0;
}

/* --------------------------------------------------------------- odometer */
.milestones__odometer {
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

/* The window a character rolls through. The clip is what makes it a counter
   rather than a list. */
.milestones__cell {
  position: relative;
  display: block;
  height: var(--od-cell);
  overflow: hidden;
}

.milestones__strip {
  display: block;
  will-change: transform;
}

.milestones__glyph {
  display: block;
  height: var(--od-cell);
  line-height: var(--od-cell);
  /* A fixed advance, so a 1 occupies the same cell as an 8 and the year does
     not breathe in and out as it counts. */
  min-width: 0.58em;
  text-align: center;
  color: var(--ms-ink);
  font-family: var(--display);
  font-size: 340px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* ----------------------------------------------------------------- detail */
.milestones__detail {
  min-width: 0;
  border-left: 2px solid var(--ms-line);
  padding-left: 40px;
}

/* The panel arrives when the year rolls. It used to share the deck's generic 16px
   rise, which read as the same fade every other block does; the odometer beside it is
   travelling sideways, so the text now comes in from the same direction and settles
   slightly late, as though the year pushed it into place.

   The rule down the left edge draws itself at the same time, which is what ties the
   two halves together rather than leaving the text sliding against a static line. */
.milestones__detail.is-in .milestones__title {
  animation: ms-text-in 0.62s cubic-bezier(0.22, 0.9, 0.16, 1) both;
}

@keyframes ms-text-in {
  from {
    opacity: 0;
    transform: translate3d(34px, 6px, 0);
    filter: blur(4px);
  }
  60% {
    filter: blur(0);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

/* Each bullet swings in a touch further than the heading and a touch later, so the
   list reads top to bottom rather than arriving as one slab. */
@keyframes ms-bullet-in {
  from {
    opacity: 0;
    transform: translate3d(26px, 0, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The left rule wipes down from the top as the panel lands. `scaleY` from the top
   edge rather than a height animation: a transform does not touch layout, so the
   text beside it is not reflowed mid-flight. */
.milestones__detail.is-in {
  animation: ms-rule-draw 0.66s cubic-bezier(0.22, 0.9, 0.16, 1) both;
}

@keyframes ms-rule-draw {
  from { border-left-color: transparent; }
  to { border-left-color: var(--ms-line); }
}

/* The dash beside each bullet draws outward from the text, after its line has landed. */
.milestones__detail.is-in .milestones__bullets li::before {
  animation: ms-dash 0.4s ease-out var(--bullet-delay, 0.12s) both;
}

@keyframes ms-dash {
  from { transform: scaleX(0); opacity: 0; }
  to { transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .milestones__detail.is-in,
  .milestones__detail.is-in .milestones__title,
  .milestones__detail.is-in .milestones__bullets li,
  .milestones__detail.is-in .milestones__bullets li::before {
    animation: none;
  }
}

.milestones__title {
  margin: 0 0 20px;
  font-size: 34px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.015em;
}

/* No ch cap: the column is already bounded by the odometer beside it, and a
   46ch cap on top of that left the longest line stopping two-thirds across an
   otherwise empty half of the slide. */
.milestones__bullets {
  display: grid;
  gap: 13px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.milestones__bullets li {
  position: relative;
  padding-left: 28px;
  color: var(--muted);
  font-size: 18.5px;
  line-height: 1.5;
}

.milestones__detail.is-in .milestones__bullets li {
  animation: ms-bullet-in 0.54s cubic-bezier(0.22, 0.9, 0.16, 1) var(--bullet-delay, 0.12s) both;
}

/* A drawn rule rather than a disc: the accent is reserved for what is active,
   and a row of accent discs spends it on punctuation. */
.milestones__bullets li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 12px;
  height: 2px;
  /* Grows away from the margin, not out from its own middle. */
  transform-origin: left center;
  background: var(--ms-lead);
}

/* ------------------------------------------------------------------- rail */
.milestones__foot {
  display: grid;
  gap: 10px;
  margin-top: auto;
  animation: block-in 0.7s cubic-bezier(0.22, 0.68, 0, 1) 0.5s both;
}

/* The deck's own control pill is fixed to the bottom of the *viewport* and lands
   squarely on the middle of the rail, over dots that are meant to be clickable.
   Every other block can be sat on — this one cannot. Clearance is generous
   because the pill is measured in screen pixels while this is measured in slide
   pixels: the slide is scaled, so the overlap grows as the scale shrinks, and
   100px covers it down to the smallest scale the deck is shown at. */
body.is-presenting .milestones__inner {
  padding-bottom: 100px;
}

.milestones__rail {
  position: relative;
  display: flex;
  align-items: center;
  height: 22px;
}

.milestones__rail-track {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--border);
}

.milestones__rail-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--ms-lead);
  transition: width 0.55s cubic-bezier(0.22, 0.68, 0, 1);
}

/* space-between rather than a fixed step, so the first and last dot land
   exactly on the ends of the track whatever the stop count. */
.milestones__dots {
  position: relative;
  display: flex;
  justify-content: space-between;
  width: 100%;
}

.milestones__dot {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  cursor: pointer;
}

.milestones__dot-tick {
  display: block;
  width: 10px;
  height: 10px;
  border: 2px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  transition: transform 0.35s cubic-bezier(0.22, 0.68, 0, 1), background 0.35s ease,
    border-color 0.35s ease;
}

.milestones__dot:hover .milestones__dot-tick {
  border-color: var(--ms-lead);
}

.milestones__dot.is-past .milestones__dot-tick {
  border-color: var(--ms-lead);
  background: var(--ms-lead);
}

.milestones__dot.is-active .milestones__dot-tick {
  border-color: var(--ms-lead);
  background: var(--ms-lead);
  transform: scale(1.85);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--ms-lead) 16%, transparent);
}

.milestones__rail-labels {
  display: flex;
  justify-content: space-between;
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.16em;
}

.milestones--empty {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 13px;
}

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--milestone-timeline {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .milestones {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

/* The staged entrance runs out to ~1s; landing it at once beats holding a blank
   panel in front of someone who asked for less motion. The odometer reads the
   same query in JS and snaps rather than rolling. */
@media (prefers-reduced-motion: reduce) {
  .milestones *,
  .milestones *::before,
  .milestones *::after {
    animation-delay: 0s !important;
    transition: none !important;
  }
}

@media (max-width: 900px) {
  .milestones:not(.fit-slide__inner .milestones) .milestones__inner {
    max-height: none;
    padding: 28px 24px;
  }
  .milestones:not(.fit-slide__inner .milestones) .milestones__stage {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }
  .milestones:not(.fit-slide__inner .milestones) .milestones__detail {
    border-left: 0;
    padding-left: 0;
  }
}

/* ==========================================================================
   Milestone timeline — dark variant.

   A deliberate departure from the house rules, at the user's direction: this
   block carries a photographic ground, a scrim and a drifting sky, where the
   rest of the portal is white surfaces and flat colour. Everything below is
   scoped under `.milestones--dark` so none of it can leak into another block.
   ========================================================================== */
.milestones--dark {
  --ms-ink: #ffffff;
  --ms-line: rgba(255, 255, 255, 0.22);
  /* Smaller than the light variant's 300px. Boxed digits carry their own border,
     padding and shadow, so the same cell height reads a great deal heavier —
     at 300 the plates ran into the heading above them. */
  --od-cell: 216px;
  background: #05070d;
  color: #ffffff;
  isolation: isolate;
}

/* -------------------------------------------------------------- the ground */
.milestones__photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.milestones__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 38%;
  /* Pushed down and desaturated so it reads as a ground rather than as a
     picture competing with the year sitting on top of it. */
  filter: saturate(0.62) brightness(0.52) contrast(1.05);
}

/* The scrim is the one gradient in the product. A photograph has no contract
   with the type over it — this one runs from near-solid at the edges, where the
   kicker and the rail sit, to merely dark at the centre, so the crowd still
   reads while every piece of text clears 4.5:1. */
/* Close to flat, and dark. An earlier pass lightened the middle to let the crowd
   through, which put the brightest, busiest part of the photograph directly
   behind the reading and left the top reading as plain black — the picture
   fought the type and lost the balance of the field. A near-even veil keeps the
   crowd legible as texture everywhere and gives every piece of text the same
   ground to sit on. The ends are lifted only enough to seat the kicker and the
   rail. */
.milestones__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg, rgba(5, 7, 13, 0.9) 0%, rgba(5, 7, 13, 0.74) 22%,
      rgba(5, 7, 13, 0.72) 70%, rgba(5, 7, 13, 0.92) 100%);
}

/* ----------------------------------------------------------------- the sky */
.milestones__sky {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}

/* Two slow clouds of brand colour. Long, co-prime durations so the pair never
   settles into a visible loop over the length of a talk. */
.milestones__nebula {
  position: absolute;
  border-radius: 999px;
  filter: blur(90px);
  opacity: 0.5;
}

.milestones__nebula--a {
  top: -18%;
  left: -10%;
  width: 780px;
  height: 620px;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--ms-lead) 62%, transparent), transparent 72%);
  animation: ms-drift-a 47s ease-in-out infinite;
}

.milestones__nebula--b {
  right: -14%;
  bottom: -22%;
  width: 900px;
  height: 700px;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--brand-secondary, #71bd1f) 46%, transparent), transparent 72%);
  animation: ms-drift-b 61s ease-in-out infinite;
}

@keyframes ms-drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(90px, 60px, 0) scale(1.14); }
}

@keyframes ms-drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.08); }
  50%      { transform: translate3d(-110px, -70px, 0) scale(1); }
}

/* Each field holds one seeded dot whose box-shadow paints the rest. The tile is
   drawn twice, one span apart, so translating by exactly one span and starting
   over is seamless — a plain loop with no jump to hide. */
.milestones__star-field {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.milestones__stars {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  border-radius: 999px;
  background: transparent;
}

.milestones__star-field--far .milestones__stars {
  animation: ms-stars 150s linear infinite;
  opacity: 0.55;
}

.milestones__star-field--mid .milestones__stars {
  animation: ms-stars 96s linear infinite;
  opacity: 0.75;
}

/* The near layer twinkles as well as drifts; the far ones do not, because a
   whole sky pulsing at once looks like a rendering fault rather than weather. */
.milestones__star-field--near .milestones__stars {
  animation: ms-stars 62s linear infinite, ms-twinkle 5.5s ease-in-out infinite;
}

@keyframes ms-stars {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, calc(-1 * var(--star-span)), 0); }
}

@keyframes ms-twinkle {
  0%, 100% { opacity: 0.9; }
  50%      { opacity: 0.5; }
}

/* ------------------------------------------------------ content over it all */
.milestones--dark .milestones__inner {
  position: relative;
  z-index: 3;
}

/* Centred, as asked. `margin: auto 0` on a flex item splits the free space
   evenly above and below, so the stage sits exactly between the head and the
   rail whatever the slide's height. On the white variant this left a void at
   both ends; over a photograph there is no void to leave — the ground carries
   the whole field, so the composition belongs in the middle of it. */
.milestones--dark .milestones__stage {
  margin: auto 0;
  align-content: center;
}

.milestones--dark .milestones__foot {
  margin-top: 0;
}

.milestones--dark .milestones__kicker {
  color: #ffffff;
}

.milestones--dark .milestones__kicker .ic {
  color: var(--brand-accent);
}

.milestones--dark .milestones__heading,
.milestones--dark .milestones__title {
  color: #ffffff;
}

.milestones--dark .milestones__counter,
.milestones--dark .milestones__rail-labels {
  color: rgba(255, 255, 255, 0.72);
}

.milestones--dark .milestones__bullets li {
  color: rgba(255, 255, 255, 0.86);
}

.milestones--dark .milestones__bullets li::before {
  background: var(--brand-accent);
}

.milestones--dark .milestones__detail {
  border-left-color: rgba(255, 255, 255, 0.22);
}

/* --------------------------------------------------------- boxed odometer */
/* The year in cells, after the "SINCE 2016" plate in the photograph. The box is
   the cell that already clips the roll, so it costs nothing structurally — it
   just becomes visible. */
.milestones--dark .milestones__odometer {
  gap: 14px;
}

.milestones--dark .milestones__cell {
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 22px 48px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  padding: 0 10px;
}

.milestones--dark .milestones__glyph {
  color: #ffffff;
  /* 246px of Oswald inside a 216px plate left 17px of air above the digit and
     overhung the bottom edge by 7px - measured, which is why the digits looked
     dropped in their boxes. 186px brings the whole numeral inside the plate with
     room to spare on both sides. */
  font-size: 210px;
  /* Half-leading hangs the baseline from the middle of the line box, and a numeral
     has no descender to fill the space below it, so a line box the height of the
     plate sits the ink low. Pulling the line box in lifts the baseline; the residual
     is trimmed by the nudge below. */
  line-height: 170px;
  min-width: 0.66em;
  text-shadow: 0 4px 26px rgba(0, 0, 0, 0.5);
}

/* The last few pixels, solved rather than guessed. At 210px the numeral's ink is
   176px tall inside a 216px plate, so 20px belongs above it and 20px below; measured
   against the line box the ink was sitting 37px down and 3px up, so it comes up 17px.
   Applied to the glyph rather than the cell, because the roll translates the stack the
   cells live in and a nudge there would ride along with it. */
.milestones--dark .milestones__glyph {
  position: relative;
  top: 13px;
}

/* ------------------------------------------------------------- rail, dark */
.milestones--dark .milestones__rail-track {
  background: rgba(255, 255, 255, 0.2);
}

.milestones--dark .milestones__rail-fill {
  background: var(--brand-accent);
}

.milestones--dark .milestones__dot-tick {
  border-color: rgba(255, 255, 255, 0.38);
  background: transparent;
}

.milestones--dark .milestones__dot.is-past .milestones__dot-tick,
.milestones--dark .milestones__dot.is-active .milestones__dot-tick {
  border-color: var(--brand-accent);
  background: var(--brand-accent);
}

.milestones--dark .milestones__dot.is-active .milestones__dot-tick {
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--brand-accent) 26%, transparent);
}

.milestones--dark .milestones__dot:hover .milestones__dot-tick {
  border-color: var(--brand-accent);
}

/* Focus has to be visible: the block takes a tabindex so it can receive the
   wheel gesture, which makes it a stop on the keyboard path. */
.milestones:focus-visible {
  outline: 2px solid var(--brand-accent);
  outline-offset: -4px;
}

/* A drifting sky is exactly the motion someone disabling animation is asking to
   be rid of. The ground and the scrim stay; only the movement stops. */
@media (prefers-reduced-motion: reduce) {
  .milestones__nebula,
  .milestones__stars {
    animation: none !important;
  }
}

/* ==========================================================================
   Leadership panels — a wall of chapters that open one at a time.

   Ported from a React sketch. Sizes are flat nominal values: the slide is laid
   out at 1600 and scaled as a whole, so the original's `vh`/`vw` would have
   measured the window instead of the canvas. The remote font import is gone too
   — nothing is fetched at presentation time.
   ========================================================================== */
.lj-root {
  --lj-ink: #0e1512;
  --lj-paper: #f7f8f4;
  --lj-line: #e4e7e0;

  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--lj-ink);
  color: var(--lj-ink);
}

/* ------------------------------------------------------------ hero overlay */
/* Sits over the unopened wall and steps aside the moment a chapter is chosen —
   the panels are the content; this is only the way in. */
.lj-overlay {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  width: min(72%, 720px);
  padding: 44px;
  color: #ffffff;
  pointer-events: none;
  transition: opacity 0.45s ease, transform 0.45s ease;
}

.lj-root.has-open .lj-overlay {
  opacity: 0;
  transform: translateY(-8px);
}

.lj-warp {
  width: 100%;
  height: 200px;
}

.lj-warp-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.lj-warp-text {
  font-family: var(--display);
  font-weight: 700;
  font-size: 74px;
  letter-spacing: -1px;
  fill: #ffffff;
}

.lj-split {
  display: inline-block;
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 2px solid rgba(255, 255, 255, 0.55);
  color: #ffffff;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}

.lj-char {
  display: inline-block;
  white-space: pre;
  opacity: 0;
  transform: translateY(26px);
  animation: lj-char 0.85s cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
}

@keyframes lj-char {
  to { opacity: 1; transform: none; }
}

/* -------------------------------------------------------------- the wall */
.lj-rail {
  display: flex;
  height: 100%;
  width: 100%;
}

.lj-card {
  position: relative;
  display: flex;
  flex: 1 1 0;
  min-width: 66px;
  overflow: hidden;
  background: var(--accent);
  transition: flex-grow 0.55s cubic-bezier(0.6, 0.05, 0.2, 1);
}

.lj-card.is-open { flex-grow: 16; }
.lj-card.is-closed { flex-grow: 1; cursor: pointer; }
.lj-card.is-closed:hover { filter: brightness(1.07); }

/* ------------------------------------------------------------------ spine */
.lj-spine {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 22px;
  padding: 24px 0;
  border: 0;
  background: transparent;
  color: var(--on);
  cursor: pointer;
}

.lj-root.all-closed .lj-spine { padding-bottom: 36px; }
.lj-card.is-closed .lj-spine { flex: 1 1 auto; width: 100%; }
.lj-card.is-open .lj-spine { flex: 0 0 84px; justify-content: space-between; }

/* 19px bold, not 15/16. The spine sits directly on the panel's own colour, and
   the lighter board swatches leave neither white nor ink above 4.5:1 on it —
   the light blue tops out at 4.33. At 18.66px bold the text clears the bar as
   large type instead, which every swatch passes with room. Full opacity for the
   same reason: 0.85 was quietly spending a fifth of the contrast. */
.lj-num {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: 1px;
}

.lj-spine-title {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.6px;
  line-height: 1;
  white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.22);
}

.lj-spine-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--on) 16%, transparent);
}

.lj-spine-icon .ic { color: var(--on); }

/* ------------------------------------------------------------------- body */
.lj-body {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  background: var(--lj-paper);
  opacity: 0;
  transform: translateX(14px);
  transition: opacity 0.5s 0.1s, transform 0.5s 0.1s;
  pointer-events: none;
}

.lj-card.is-open .lj-body { opacity: 1; transform: none; pointer-events: auto; }
.lj-card.is-closed .lj-body { display: none; }

.lj-media {
  position: relative;
  flex: 0 0 42%;
  min-width: 0;
  overflow: hidden;
}

.lj-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A panel with no photograph yet is a chapter plate, not an empty frame: the
   number set as a watermark with the chapter's mark over it. Both are drawn
   from the panel's own colour so it still reads as part of the wall. */
.lj-photo--empty {
  position: relative;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, #ffffff);
}

.lj-plate-num {
  grid-area: 1 / 1;
  color: color-mix(in srgb, var(--accent) 26%, #ffffff);
  font-family: var(--display);
  font-size: 260px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  user-select: none;
}

.lj-plate-mark {
  grid-area: 1 / 1;
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  border-radius: 999px;
  background: #ffffff;
  box-shadow: 0 10px 30px -12px rgba(14, 21, 18, 0.4);
}

.lj-plate-mark .ic {
  width: 34px;
  height: 34px;
  color: color-mix(in srgb, var(--accent) 72%, #000000);
}

/* The year badge is small type and cannot be enlarged into the large-text bar
   the way the spine was, so it takes a darkened derivative of the panel colour
   instead — the same one the chapter line uses. White clears 4.9:1 on every
   swatch there, against 2.59:1 on the raw tangerine. */
.lj-chip {
  position: absolute;
  top: 14px;
  left: 14px;
  padding: 6px 12px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 72%, #000000);
  color: #ffffff;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 1px;
  box-shadow: 0 4px 10px -4px rgba(0, 0, 0, 0.4);
}

.lj-content {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-width: 0;
  padding: 36px;
  overflow: auto;
}

/* The chapter line takes a darkened derivative of the panel colour rather than
   the colour itself: at 12px the raw accent fails against paper for four of the
   seven panels. */
.lj-chapter {
  margin: 0 0 8px;
  color: color-mix(in srgb, var(--accent) 72%, #000000);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
}

.lj-role {
  margin: 0 0 14px;
  font-size: 32px;
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.5px;
}

.lj-summary {
  margin: 0 0 20px;
  max-width: 52ch;
  color: #3d453e;
  font-size: 15.5px;
  line-height: 1.65;
}

.lj-list {
  display: grid;
  gap: 11px;
  margin: 0 0 auto;
  padding: 0;
  list-style: none;
}

.lj-list li {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  color: #20261f;
  font-size: 15px;
  font-weight: 500;
  opacity: 0;
  transform: translateY(8px);
  animation: lj-in 0.5s var(--d, 0.15s) forwards;
}

.lj-list .ic {
  flex: 0 0 auto;
  margin-top: 2px;
  color: color-mix(in srgb, var(--accent) 72%, #000000);
}

@keyframes lj-in {
  to { opacity: 1; transform: none; }
}

.lj-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 26px;
  padding-top: 16px;
  border-top: 1px solid var(--lj-line);
}

.lj-step {
  color: #6b746c;
  font-size: 14px;
  font-weight: 800;
}

.lj-step i {
  padding: 0 4px;
  color: var(--lj-line);
  font-style: normal;
}

/* Same reasoning as the chip: 13.5px on a raw swatch cannot pass. */
.lj-next {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px;
  border: 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 72%, #000000);
  color: #ffffff;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
}

.lj-next .ic { color: #ffffff; }

.lj-next:hover {
  transform: translateX(3px);
  box-shadow: 0 8px 18px -8px var(--accent);
}

.lj-spine:focus-visible,
.lj-next:focus-visible {
  outline: 3px solid #ffffff;
  outline-offset: -3px;
  border-radius: 8px;
}

.lj-next:focus-visible { outline-color: var(--lj-ink); }

/* ------------------------------------------------- the photographs, in the theme
   Each panel's picture is rendered in that panel's own green rather than sitting in
   the rail as a full-colour rectangle. `mix-blend-mode: luminosity` keeps only the
   lightness of the photograph and takes its hue from the ground behind it, which is
   the card's accent - so the same treatment tracks the ramp automatically and there
   is no second palette to keep in step.

   Grayscale first, because luminosity alone still lets a saturated shirt or a lanyard
   pull the blend off-hue. Contrast is nudged up to hold the faces open after the
   channel is flattened.

   `isolation: isolate` matters: without it the blend reaches past the card and mixes
   with whatever the rail is sitting on. */
.lj-media {
  isolation: isolate;
  background: var(--deep, #003C19);
}

.lj-photo--whole,
.lj-media .lj-photo {
  filter: grayscale(1) contrast(1.08) brightness(1.04);
  mix-blend-mode: luminosity;
}

/* A wash over the picture in the same green, heaviest where the text sits beside it, so
   the join between photograph and panel is a gradient rather than a hard edge. */
.lj-media::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(90deg, transparent 55%, color-mix(in srgb, var(--deep, #003C19) 62%, transparent) 100%),
    linear-gradient(0deg, color-mix(in srgb, var(--deep, #003C19) 38%, transparent), transparent 58%);
  pointer-events: none;
}

/* A photograph reduced to one hue loses the thing that told you where it ended, so the
   edge is drawn back in. */
.lj-media {
  border-right: 1px solid color-mix(in srgb, #ffffff 16%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .lj-photo--whole,
  .lj-media .lj-photo {
    transition: none;
  }
}

.lj-root--empty {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 13px;
}

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--leadership-panels {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .lj-root {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

@media (prefers-reduced-motion: reduce) {
  .lj-card,
  .lj-body,
  .lj-list li,
  .lj-next,
  .lj-char,
  .lj-overlay {
    transition: none !important;
    animation: none !important;
    opacity: 1;
    transform: none;
  }
  .lj-card.is-closed .lj-body { display: none; }
}

/* Stacked, off the slide canvas. */
@media (max-width: 900px) {
  .lj-root:not(.fit-slide__inner .lj-root) .lj-overlay {
    position: static;
    width: auto;
    opacity: 1;
    transform: none;
    padding: 26px 22px 20px;
  }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-rail {
    flex-direction: column;
    height: auto;
  }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-card,
  .lj-root:not(.fit-slide__inner .lj-root) .lj-card.is-open,
  .lj-root:not(.fit-slide__inner .lj-root) .lj-card.is-closed {
    flex: 1 1 auto;
    flex-direction: column;
    transition: none;
  }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-spine {
    flex-direction: row;
    justify-content: flex-start;
    width: 100%;
    gap: 14px;
    padding: 18px 20px;
  }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-spine-title {
    writing-mode: horizontal-tb;
    transform: none;
  }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-spine-icon { margin-left: auto; }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-body { flex-direction: column; }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-media { flex: 0 0 auto; height: 220px; }
  .lj-root:not(.fit-slide__inner .lj-root) .lj-content { overflow: visible; }
}

/* ==========================================================================
   Gallery wall — a wall of moments behind a hollow title, then the
   anniversary frame. Everything is namespaced under .th-root.

   Ported from a React sketch: the remote font import is gone (nothing is
   fetched at presentation time) and every `vh`/`vw` became a flat nominal
   size, because the slide is laid out at 1600 and scaled as a whole.
   ========================================================================== */
.th-root {
  --gw-ink: #0c140f;
  --gw-ink-soft: #16281d;
  --gw-paper: #eaf1ec;
  --gw-muted: #9fb3a6;
  --gw-green: #4cc072;
  --gw-green-deep: #2e9c4a;

  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--gw-ink);
  color: var(--gw-paper);
}

/* ------------------------------------------------------------- the frames */
.th-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  transition: opacity 0.6s ease;
}

.th-frame--wall { background: var(--gw-ink); }

.th-root.is-decade .th-frame--wall { opacity: 0; pointer-events: none; }

.th-frame--decade {
  background: var(--gw-ink);
  opacity: 0;
  pointer-events: none;
}

.th-root.is-decade .th-frame--decade { opacity: 1; pointer-events: auto; }

/* --------------------------------------------------------------- the wall */
/* CSS columns, not grid: a masonry wall wants tiles of their own height
   flowing down a column, which is exactly what multicol already does. */
/* CSS columns, not grid: a masonry wall wants tiles of their own height flowing
   down a column, which is what multicol already does.

   `column-fill: auto` is the load-bearing line. The default, `balance`, tries to
   equalise the columns — with a fixed height and more content than fits, that
   left one column 556px short of the floor and a black hole in the wall.
   `auto` fills each column to the bottom before starting the next, so every
   column that is visible is full and the only ragged one is off the edge.

   The width is set from the tile count at runtime (see GalleryWall.js); the
   value here is only what applies before that first measurement. */
.th-wall {
  height: calc(100% + 340px);
  padding: 16px;
  columns: 200px;
  column-gap: 16px;
  column-fill: auto;
  overflow: hidden;
}

.th-tile {
  position: relative;
  display: block;
  width: 100%;
  margin: 0 0 16px 0;
  break-inside: avoid-column;
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  border-radius: 12px;
  overflow: hidden;
  background: var(--gw-ink-soft);
  cursor: grab;
  opacity: 0;
  transform: scale(0.96);
  contain: layout paint;
  isolation: isolate;
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.th-tile.is-visible { opacity: 1; transform: scale(1); }
.th-tile.is-dragging { opacity: 0.4; cursor: grabbing; }
.th-tile:active { cursor: grabbing; }

.th-tile:focus-visible {
  outline: 2px solid var(--gw-green);
  outline-offset: 2px;
}

.th-tile img {
  display: block;
  width: 100%;
  height: auto;
  min-height: 64px;
  max-height: 250px;
  object-fit: cover;
  object-position: center;
  pointer-events: none;
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.th-tile:hover img { transform: scale(1.06); }

/* ------------------------------------------------------------- the pill */
.th-pill {
  position: absolute;
  left: 9px;
  bottom: 9px;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: 999px;
  background: #ffffff;
  box-shadow: 0 10px 26px -10px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transform: translateY(9px);
  transition: opacity 0.28s ease, transform 0.28s ease;
  pointer-events: none;
}

.th-tile:hover .th-pill,
.th-tile:focus-visible .th-pill { opacity: 1; transform: none; }

.th-pill__rule {
  width: 14px;
  height: 2px;
  border-radius: 2px;
  background: var(--gw-green-deep);
}

.th-pill__label {
  color: #16241c;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ------------------------------------------------------- the hollow title */
/* The radial ground is what makes the title read as a hole punched in the
   wall rather than a card laid on top of it. */
.th-hero {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 38px 64px;
  text-align: center;
  pointer-events: none;
  z-index: 10;
  background: rgba(12, 20, 15, 0.95);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(76, 192, 114, 0.4);
  border-radius: 24px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.th-hero__eyebrow {
  margin: 0 0 14px;
  color: var(--gw-green);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 3px;
  text-transform: uppercase;
}

.th-hero__title {
  margin: 0;
  color: #ffffff;
  font-family: var(--display);
  font-size: 84px;
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: 1px;
  text-transform: uppercase;
}

/* #9fb3a6 measured 6.6:1 on the ink, but the title's ground fades to the wall
   behind it — so the subtitle takes a lighter tone that still clears the bar
   over a photograph if the scrim runs thin at the edges. */
.th-hero__sub {
  margin: 12px 0 0;
  color: #c2d3c8;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
}

/* ---------------------------------------------------------- frame two */
.th-decade__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.th-root.is-decade .th-decade__img {
  animation: gw-fade-up 1s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.th-decade__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(rgba(12, 20, 15, 0.34), rgba(12, 20, 15, 0.34)),
    radial-gradient(90% 90% at 50% 120%, rgba(12, 20, 15, 0.92), transparent 62%);
}

.th-decade__caption {
  position: absolute;
  left: 84px;
  bottom: 78px;
  display: flex;
  align-items: center;
  gap: 24px;
}

.th-decade__mark {
  color: #ffffff;
  font-family: var(--display);
  font-size: 168px;
  font-weight: 700;
  line-height: 0.8;
  -webkit-text-stroke: 2px rgba(76, 192, 114, 0.55);
}

.th-decade__years {
  margin: 0;
  color: #ffffff;
  font-family: var(--display);
  font-size: 64px;
  font-weight: 700;
  line-height: 0.9;
  text-transform: uppercase;
}

.th-decade__line {
  margin: 8px 0 0;
  color: #8fe0a8;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
}

@keyframes gw-fade-up {
  from { opacity: 0; transform: translateY(24px) scale(0.96); }
  to { opacity: 1; transform: none; }
}

.th-root--empty {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 13px;
}

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--gallery-wall {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .th-root {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

@media (prefers-reduced-motion: reduce) {
  .th-tile,
  .th-tile img,
  .th-pill,
  .th-frame {
    transition: none !important;
  }
  .th-decade__img { animation: none !important; }
}

@media (max-width: 900px) {
  .th-root:not(.fit-slide__inner .th-root) .th-wall { columns: 120px; }
  .th-root:not(.fit-slide__inner .th-root) .th-hero { padding: 24px 28px; }
  .th-root:not(.fit-slide__inner .th-root) .th-hero__title { font-size: 40px; }
  .th-root:not(.fit-slide__inner .th-root) .th-decade__caption { left: 24px; bottom: 32px; gap: 14px; }
  .th-root:not(.fit-slide__inner .th-root) .th-decade__mark { font-size: 78px; }
  .th-root:not(.fit-slide__inner .th-root) .th-decade__years { font-size: 32px; }
}

/* ==========================================================================
   Leadership Journey — 7-panel full screen editorial spread
   ========================================================================== */
.lj-root {
  --ink: #16181A; --card: #FBFAF6; --line: #E2E0D6;
  --gray: #6B7069; --brand: #12A150; --brand-lt: #3DDC84;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-family: var(--font);
  color: var(--ink); background: var(--ink);
  position: relative; overflow: hidden; height: 100%; width: 100%;
  min-height: 860px;
  border-radius: var(--radius-lg);
  -webkit-font-smoothing: antialiased;
}
.lj-root * { box-sizing: border-box; }

.lj-overlay {
  position: absolute; top: 0; left: 0; z-index: 5;
  padding: clamp(26px, 4.5vw, 56px);
  width: min(94vw, 960px); color: #fff; pointer-events: none;
  transition: opacity .45s ease, transform .45s ease;
}
.has-open .lj-overlay { opacity: 0; transform: translateY(-10px); }
.lj-name { margin: 0; font-family: var(--display); line-height: 1.06; letter-spacing: -.5px; white-space: nowrap; text-shadow: 0 2px 18px rgba(0,0,0,.4); }
.lj-name span { display: inline; }
.lj-name-top {
  font-weight: 700; font-size: clamp(26px, 5.2vw, 74px); color: #fff;
  opacity: 0; animation: ljUp .7s .1s cubic-bezier(.2,.7,.2,1) forwards;
}
.lj-sep {
  font-weight: 700; font-size: clamp(26px, 5.2vw, 74px); color: #fff; opacity: .7; margin: 0 .18em;
  opacity: 0; animation: ljUp .7s .1s cubic-bezier(.2,.7,.2,1) forwards;
}
.lj-name-bottom {
  font-weight: 700; font-size: clamp(26px, 5.2vw, 74px); color: var(--brand-lt);
  opacity: 0; animation: ljUp .7s .16s cubic-bezier(.2,.7,.2,1) forwards;
}
.lj-rule {
  display: block; height: 2px; width: min(420px, 66%); background: var(--brand-lt);
  margin: clamp(18px, 2.6vw, 30px) 0 clamp(14px, 1.8vw, 20px);
  transform-origin: left; transform: scaleX(0); animation: ljRule .7s .36s cubic-bezier(.6,.05,.2,1) forwards;
}
.lj-tagline {
  margin: 0; font-weight: 700; font-size: clamp(16px, 2vw, 24px); letter-spacing: -.2px; color: #fff;
  text-shadow: 0 2px 14px rgba(0,0,0,.4);
  opacity: 0; animation: ljUp .6s .46s cubic-bezier(.2,.7,.2,1) forwards;
}
@keyframes ljUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
@keyframes ljRule { to { transform: scaleX(1); } }

.lj-rail { display: flex; gap: 0; height: 100%; width: 100%; min-height: 860px; }
.lj-card {
  position: relative; display: flex; overflow: hidden; min-width: 60px;
  background: var(--accent);
  transition: flex-grow .58s cubic-bezier(.6,.05,.2,1);
  flex: 1 1 0; border-right: 1px solid rgba(255,255,255,.06);
}
.lj-card:last-child { border-right: 0; }
.lj-card.is-open { flex-grow: 18; }
.lj-card.is-closed { flex-grow: 1; cursor: pointer; }
.lj-card.is-closed::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: var(--brand-lt);
  transform: scaleX(0); transform-origin: left; transition: transform .4s;
}
.lj-card.is-closed:hover { filter: brightness(1.12); }
.lj-card.is-closed:hover::after { transform: scaleX(1); }

.lj-spine {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  gap: 22px; padding: 26px 0 34px; border: 0; background: transparent; cursor: pointer;
  color: var(--on); position: relative; z-index: 2;
}
.is-closed .lj-spine { flex: 1 1 auto; width: 100%; }
.is-open .lj-spine { flex: 0 0 clamp(58px, 5.5vw, 80px); justify-content: space-between; padding: 24px 0; }
.lj-num { font-family: var(--mono); font-weight: 600; font-size: 13px; letter-spacing: 1px; opacity: .75; }
.lj-spine-title {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-family: var(--display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(16px, 1.8vw, 22px); letter-spacing: 1.5px; white-space: nowrap; line-height: 1;
}
.lj-spine-icon {
  width: 33px; height: 33px; display: grid; place-items: center; border-radius: 999px;
  background: rgba(255,255,255,0.15);
}

.lj-body {
  flex: 1 1 auto; min-width: 0; display: flex; background: var(--card);
  opacity: 0; transform: translateX(16px);
  transition: opacity .5s .12s, transform .5s .12s; pointer-events: none;
}
.is-open .lj-body { opacity: 1; transform: none; pointer-events: auto; }
.is-closed .lj-body { display: none; }

.lj-media { position: relative; flex: 0 0 40%; min-width: 0; overflow: hidden; }
.lj-photo { width: 100%; height: 100%; object-fit: cover; display: block; }
.lj-photo--empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  color: var(--deep); height: 100%;
  background:
    radial-gradient(130% 130% at 20% 0%, var(--tint), transparent 62%),
    repeating-linear-gradient(135deg,#fff 0 24px,var(--tint) 24px 48px);
}
.lj-photo-hint {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  color: var(--deep); background: #fff; padding: 6px 12px; border-radius: 20px; border: 1px solid var(--line);
}
.lj-chip {
  position: absolute; top: 16px; left: 16px; display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-weight: 600; font-size: 13px; letter-spacing: 1px;
  color: #fff; background: var(--deep); padding: 7px 13px; border-radius: 8px;
}

.lj-content {
  position: relative; flex: 1 1 auto; min-width: 0;
  padding: clamp(24px, 3.2vw, 44px); display: flex; flex-direction: column;
  align-items: center; text-align: center; justify-content: center; overflow: auto;
}
.lj-ghost {
  position: absolute; top: -10px; right: 8px; z-index: 0; pointer-events: none;
  font-family: var(--display); font-weight: 800; font-size: clamp(120px, 17vw, 220px); line-height: 1;
  color: var(--ink); opacity: .05; letter-spacing: -2px;
}
.lj-content > *:not(.lj-ghost) { position: relative; z-index: 1; }

.lj-close {
  position: absolute; top: 16px; right: 16px; z-index: 10;
  width: 36px; height: 36px; display: grid; place-items: center; cursor: pointer;
  color: var(--ink); background: #fff; border: 1px solid var(--line); border-radius: 50%;
  transition: transform .2s, background .2s;
}
.lj-close:hover { transform: rotate(90deg); background: var(--tint); }

.lj-eyebrow {
  margin: 16px 0 10px; font-weight: 600; font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; color: var(--brand); text-align: center;
}
.lj-role {
  margin: 0 auto 16px; font-family: var(--display); font-weight: 600;
  font-size: clamp(26px, 3vw, 42px); line-height: 1.02; letter-spacing: -.5px; color: var(--ink);
  max-width: 24ch; text-align: center;
}
.lj-summary {
  margin: 0 auto 20px; font-size: 15.5px; line-height: 1.65; color: var(--gray);
  max-width: 54ch; text-align: center;
}

.lj-quote {
  margin: 0 auto 22px; padding: 12px 24px;
  border-left: 0; border-top: 2px solid var(--brand); border-bottom: 2px solid var(--brand);
  font-family: var(--display); font-weight: 500; font-size: clamp(16px, 1.7vw, 21px); line-height: 1.35;
  color: var(--ink); max-width: 48ch; text-align: center;
}
.lj-quote-mark { color: var(--brand); vertical-align: -3px; margin-right: 6px; }

.lj-list { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: 12px; justify-content: center; width: 100%; max-width: 50ch; }
.lj-list li {
  display: flex; gap: 11px; align-items: center; justify-content: center; font-size: 14.5px; color: var(--ink); font-weight: 500;
  text-align: center; opacity: 0; transform: translateY(8px); animation: ljIn .5s var(--d, .15s) forwards;
}
.lj-list svg { color: var(--brand); flex: 0 0 auto; margin-top: 2px; }

.lj-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line);
}
.lj-step { font-family: var(--mono); font-weight: 600; color: var(--gray); font-size: 13px; letter-spacing: 1px; white-space: nowrap; }
.lj-step i { font-style: normal; color: var(--line); padding: 0 3px; }
.lj-nav {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer; max-width: 44%;
  font-weight: 600; font-size: 13px; color: var(--ink); background: transparent; border: 0; padding: 6px 2px;
  overflow: hidden; white-space: nowrap; transition: color .2s, transform .2s;
}
.lj-nav span { overflow: hidden; text-overflow: ellipsis; }
.lj-nav:hover { color: var(--brand); }
.lj-nav--next { justify-content: flex-end; text-align: right; }
.lj-nav:hover.lj-nav--next { transform: translateX(3px); }
.lj-nav:hover:not(.lj-nav--next) { transform: translateX(-3px); }

.lj-spine:focus-visible, .lj-nav:focus-visible, .lj-close:focus-visible { outline: 3px solid var(--brand-lt); outline-offset: 2px; border-radius: 8px; }

@media (max-width: 760px) {
  .lj-root { height: auto; overflow: visible; }
  .lj-overlay { position: static; width: auto; opacity: 1 !important; transform: none; padding: 28px 22px 20px; background: var(--ink); }
  .lj-name { white-space: normal; }
  .lj-rail { flex-direction: column; height: auto; gap: 0; width: 100%; }
  .lj-card { transition: none; border-right: 0; border-bottom: 1px solid rgba(255,255,255,.08); }
  .lj-card.is-open, .lj-card.is-closed { flex: 1 1 auto; }
  .lj-card.is-closed::after { display: none; }
  .lj-spine { flex-direction: row; flex-basis: auto; width: 100%; gap: 14px; justify-content: flex-start; padding: 18px 20px; }
  .is-open .lj-spine { flex-basis: auto; padding: 18px 20px; }
  .lj-spine-title { writing-mode: horizontal-tb; transform: none; font-size: 21px; letter-spacing: 1px; }
  .lj-num { font-size: 14px; }
  .lj-spine-icon { margin-left: auto; }
  .lj-body { flex-direction: column; max-height: 0; overflow: hidden; transform: none; opacity: 1; transition: max-height .5s ease; }
  .is-open .lj-body { max-height: 2000px; }
  .lj-media { flex: 0 0 auto; height: 220px; }
  .lj-content { overflow: visible; }
}

.canvas-block--leadership-panels {
  min-height: 860px !important;
  height: 100% !important;
}

/* ==========================================================================
   AI Ready Engineer Showcase — Neatly aligned PDF showcase
   ========================================================================== */
.are-root {
  --are-bg: #090e0b;
  --are-card: #111a14;
  --are-card-border: rgba(76, 192, 114, 0.25);
  --are-green: #4cc072;
  --are-green-bright: #3ddc84;
  --are-text: #eaf1ec;
  --are-muted: #9fb3a6;

  position: relative;
  height: 100%;
  width: 100%;
  min-height: 860px;
  background: var(--are-bg);
  color: var(--are-text);
  padding: 32px 40px;
  overflow-y: auto;
  border-radius: var(--radius-lg);
  font-family: var(--font);
  box-sizing: border-box;
}

.are-hero {
  text-align: center;
  margin-bottom: 36px;
  padding: 28px 32px;
  background: linear-gradient(180deg, rgba(17, 26, 20, 0.8) 0%, rgba(9, 14, 11, 0.95) 100%);
  border: 1px solid var(--are-card-border);
  border-radius: 20px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}

.are-hero__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.are-kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--are-green-bright);
}

.are-partners-chips {
  display: flex;
  gap: 10px;
}

.are-partner-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  background: rgba(76, 192, 114, 0.12);
  border: 1px solid rgba(76, 192, 114, 0.3);
  color: #ffffff;
  padding: 4px 12px;
  border-radius: 999px;
}

.are-hero__title {
  margin: 10px 0 12px;
  font-family: var(--display);
  font-size: clamp(38px, 4vw, 56px);
  font-weight: 800;
  letter-spacing: 2px;
  color: #ffffff;
  text-transform: uppercase;
  line-height: 1;
}

.are-title-highlight {
  color: var(--are-green-bright);
  text-shadow: 0 0 30px rgba(61, 220, 132, 0.4);
}

.are-hero__sub {
  max-width: 800px;
  margin: 0 auto 24px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--are-muted);
}

.are-stats-bar {
  display: flex;
  justify-content: center;
  gap: 24px;
  flex-wrap: wrap;
}

.are-stat-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 20px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  min-width: 140px;
}

.are-stat-badge strong {
  font-family: var(--display);
  font-size: 24px;
  font-weight: 700;
  color: var(--are-green-bright);
}

.are-stat-badge span {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1px;
  color: #a0b5a8;
  margin-top: 2px;
}

.are-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-bottom: 40px;
}

@media (max-width: 1100px) {
  .are-grid {
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  }
}

.are-card {
  display: flex;
  flex-direction: column;
  background: var(--are-card);
  border: 1px solid var(--are-card-border);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  transition: transform 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
}

.are-card:hover {
  transform: translateY(-6px);
  border-color: rgba(76, 192, 114, 0.6);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.75), 0 0 20px rgba(76, 192, 114, 0.2);
}

.are-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  background: rgba(0, 0, 0, 0.3);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.are-card__title-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.are-card__badge {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--are-green-bright);
}

.are-card__title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: #ffffff;
}

.are-card__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 9 / 16;
  background: #000000;
  overflow: hidden;
  cursor: pointer;
}

.are-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.are-card__frame:hover .are-card__img {
  transform: scale(1.04);
}

.are-card__overlay {
  position: absolute;
  inset: 0;
  background: rgba(9, 14, 11, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.are-card__frame:hover .are-card__overlay {
  opacity: 1;
}

.are-zoom-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  background: #ffffff;
  color: #090e0b;
  font-size: 13px;
  font-weight: 700;
  border-radius: 999px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
  transform: translateY(10px);
  transition: transform 0.3s ease;
}

.are-card__frame:hover .are-zoom-btn {
  transform: translateY(0);
}

.are-card__footer {
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: rgba(0, 0, 0, 0.2);
}

.are-card__caption {
  margin: 0;
  font-size: 13px;
  color: var(--are-muted);
  line-height: 1.4;
}

.are-card__actions {
  display: flex;
  gap: 10px;
}

.are-btn-expand,
.are-btn-pdf,
.are-btn-download-sm {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 700;
  border-radius: 8px;
  padding: 8px 14px;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.are-btn-expand {
  flex: 1;
  justify-content: center;
  background: rgba(76, 192, 114, 0.15);
  color: var(--are-green-bright);
  border: 1px solid rgba(76, 192, 114, 0.3);
}

.are-btn-expand:hover {
  background: rgba(76, 192, 114, 0.28);
  border-color: var(--are-green-bright);
}

.are-btn-pdf {
  background: rgba(255, 255, 255, 0.08);
  color: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.15);
}

.are-btn-pdf:hover {
  background: rgba(255, 255, 255, 0.18);
}

.are-btn-download-sm {
  background: rgba(255, 255, 255, 0.06);
  color: var(--are-muted);
  border: 1px solid rgba(255, 255, 255, 0.12);
  padding: 4px 10px;
}

.are-btn-download-sm:hover {
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.3);
}

.canvas-block--ai-ready-engineer {
  min-height: 860px !important;
  height: 100% !important;
}

/* ==========================================================================
   Course deck — the AI Ready Engineer prospectus, paged.
   The flyer's own identity: near-black green ground, lime for what is live,
   gold for a number. Flat nominal sizes throughout — the slide is laid out at
   1600 and scaled whole, so a `vw` here would measure the window.
   ========================================================================== */
.cd-root {
  --cd-ink: #071109;
  --cd-ink-2: #0d1c12;
  --cd-line: rgba(255, 255, 255, 0.12);
  --cd-lime: #7ED957;
  --cd-gold: #FFC42E;
  --cd-body: rgba(255, 255, 255, 0.76);

  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 80% at 78% 8%, rgba(24, 92, 48, 0.55), transparent 62%),
    radial-gradient(90% 70% at 10% 96%, rgba(18, 70, 38, 0.5), transparent 60%),
    var(--cd-ink);
  color: #ffffff;
}

/* Frames are stacked and cross-faded rather than laid side by side: each is a
   full slide in its own right and only one is ever on. */
.cd-frame {
  position: absolute;
  inset: 0;
  display: grid;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s ease;
}

.cd-frame.is-on { opacity: 1; visibility: visible; }

.cd-inner {
  align-self: center;
  width: 100%;
  padding: 56px 88px 92px;
}

/* -------------------------------------------------------------------- hero */
.cd-eyebrow {
  margin: 0 0 18px;
  color: var(--cd-lime);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 3px;
  text-transform: uppercase;
}

/* The programme mark at the head of the opening frame. Sized by height so a
   wordmark of any proportion sits on the same baseline as the eyebrow under it,
   and capped in width so a wide one cannot push past the measure.

   On a plate, because the artwork is drawn for paper: its "READY ENGINEER" is
   near-black and this frame is near-black too, so on the ground alone half the
   logo disappears. The same answer the card marks already use for supplied
   artwork — `.cd-card__mark.has-art` is a white disc for exactly this reason. */
.cd-mark {
  box-sizing: border-box;
  /* Out of the column and into the empty half on the right, where it sits clear
     of the title and the standfirst instead of pushing them down. Anchored to
     the frame's own padding, so it lines up with the gutter the rest of the
     page is set to. */
  position: absolute;
  top: 56px;
  right: 88px;
  z-index: 2;
  height: 104px;
  width: auto;
  max-width: 520px;
  padding: 14px 22px;
  border-radius: 16px;
  /* Translucent, not solid: the frame's green reads through the plate while the
     logo's near-black wordmark still has something light to sit on. */
  background: rgba(255, 255, 255, 0.9);
  object-fit: contain;
  object-position: center;
}

.cd-title {
  margin: 0;
  display: grid;
  font-family: var(--display);
  font-size: 116px;
  font-weight: 700;
  line-height: 0.92;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.cd-standfirst {
  margin: 26px 0 0;
  max-width: 62ch;
  color: var(--cd-body);
  font-size: 22px;
  line-height: 1.5;
}

.cd-stats {
  display: flex;
  gap: 18px;
  margin-top: 40px;
}

.cd-stat {
  flex: 1 1 0;
  display: grid;
  gap: 6px;
  padding: 24px 28px;
  border: 1px solid var(--cd-line);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.04);
}

.cd-stat__value {
  color: var(--cd-gold);
  font-family: var(--display);
  font-size: 54px;
  font-weight: 700;
  line-height: 1;
}

.cd-stat__label {
  color: var(--cd-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1.6px;
  text-transform: uppercase;
}

/* Typographic partner marks. See CourseDeck.js — no vendor logo is drawn. */
.cd-partners { margin-top: 34px; }

.cd-partners__label {
  display: block;
  margin-bottom: 12px;
  color: var(--cd-lime);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 3px;
  text-transform: uppercase;
}

.cd-partners__row { display: flex; gap: 14px; flex-wrap: wrap; }

.cd-partner {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  padding: 13px 20px;
  border: 1px solid var(--cd-line);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
}

/* A partner that has supplied its own lockup is drawn rather than set. Both
   files are opaque and carry their own ground — one cream, one white — so each
   takes a light plate instead of the deck's dark card: dropped straight onto
   the slide they would be two bright rectangles with nothing framing them. */
.cd-partner.has-art {
  align-items: center;
  padding: 11px 15px;
  background: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.24);
}

/* Sized by HEIGHT and nothing else — the rule the showcase's logo plates
   learned the hard way: a percentage max-width resolves against the plate, the
   width wins, the height follows the ratio out of the bottom of the plate and
   `overflow` cuts it. Equal heights and each mark's own width is what makes a
   row of different lockups read as one set. */
.cd-partner__art {
  display: block;
  height: 62px;
  width: auto;
  max-width: none;
}

/* Where a row mixes drawn marks with a name in type, every chip takes the
   height of the tallest so the row reads as one set rather than as two logos
   with a label parked beside them. Scoped with `:has` to a row that actually
   has artwork — a deck whose partners are all typographic keeps its own
   proportions, which is what NGI's still is. */
.cd-partners__row:has(.has-art) .cd-partner {
  min-height: 86px;
  align-items: center;
}

/* The certification a frame is about, level with the frame's own title. The
   modules list is sixteen rows in two columns, so there is no corner of the
   body a mark could sit in without landing on a line of it. */
.cd-head.has-badge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 30px;
}

.cd-head__text { min-width: 0; }

.cd-head__badge {
  flex: 0 0 auto;
  width: 138px;
  height: 138px;
  object-fit: contain;
  border-radius: 50%;
  box-shadow: 0 18px 44px -16px rgb(0 0 0 / 0.55);
}

@media (max-width: 900px) {
  .cd-head.has-badge { gap: 18px; }
  .cd-head__badge { width: 96px; height: 96px; }
  .cd-partner__art { height: 44px; }
  .cd-partners__row:has(.has-art) .cd-partner { min-height: 68px; }
}

.cd-partner__name { font-size: 18px; font-weight: 700; }

.cd-partner__note {
  color: var(--cd-lime);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.6px;
  text-transform: uppercase;
}

/* ------------------------------------------------------------- frame heads */
.cd-head { margin-bottom: 34px; }

.cd-heading {
  margin: 0;
  font-size: 52px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.cd-sub {
  margin: 12px 0 0;
  color: var(--cd-body);
  font-size: 18px;
}

/* ------------------------------------------------------------------- cards */
.cd-cards {
  display: grid;
  grid-template-columns: repeat(var(--cd-cols, 2), minmax(0, 1fr));
  gap: 18px;
}

.cd-card {
  display: flex;
  gap: 18px;
  padding: 24px 26px;
  border: 1px solid var(--cd-line);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.04);
}

/* A card carrying its own artwork (the Claude Certified badge): the disc holds
   the picture instead of a line icon. Same 46px, same ring; the picture fills it. */
.cd-card__mark.has-art { overflow: hidden; padding: 0; background: #fff; }
.cd-card__mark.has-art img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 999px; }
.cd-card__mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border: 1.5px solid rgba(126, 217, 87, 0.5);
  border-radius: 999px;
  color: var(--cd-lime);
}

.cd-card__title { margin: 0 0 7px; font-size: 20px; font-weight: 800; line-height: 1.2; }

.cd-card__body { margin: 0; color: var(--cd-body); font-size: 15.5px; line-height: 1.5; }

/* The certification card: the whole row, the seal at a size the back of the
   room can read, and the points set beside it in two columns so the card is as
   wide as it is and not as tall. `contain`, not `cover` — the badge is a disc
   on a transparent square and cover would shave its rim. */
.cd-card--wide { grid-column: 1 / -1; align-items: center; gap: 30px; padding: 24px 30px; }
.cd-card--wide .cd-card__mark { width: 124px; height: 124px; border-width: 0; }
.cd-card--wide .cd-card__mark.has-art { background: none; }
.cd-card--wide .cd-card__mark.has-art img { object-fit: contain; border-radius: 0; }
.cd-card__text { min-width: 0; }
.cd-card__list {
  margin: 10px 0 0;
  padding: 0 0 0 20px;
  display: grid;
  gap: 7px;
  color: var(--cd-body);
  font-size: 15.5px;
  line-height: 1.45;
}
.cd-card__list li::marker { color: var(--cd-lime); }
.cd-card--wide .cd-card__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 34px; }

/* ------------------------------------------------- certification and points */
/* Two halves of one frame: the seal on the left, what it is worth on the right.
   `1fr 1fr` is the 50/50 the slide is meant to read as, and the picture is
   contained rather than cropped — a certification mark with its rim shaved is a
   mark nobody recognises. */
.cd-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 56px;
}
.cd-split__art { margin: 0; display: grid; place-items: center; }
.cd-split__art img {
  display: block;
  width: 100%;
  max-width: 100%;
  max-height: 540px;
  height: auto;
  object-fit: contain;
}
.cd-split__list { margin: 0; padding: 0 0 0 26px; list-style: disc; display: grid; gap: 22px; }
.cd-split__list li::marker { color: var(--cd-lime); }
.cd-point__title { margin: 0 0 6px; font-size: 21px; font-weight: 800; line-height: 1.2; }
.cd-point__body { margin: 0; color: var(--cd-body); font-size: 16px; line-height: 1.5; }

/* ------------------------------------------------------------------ people */
/* Ten portraits, one slide. A fixed square shot per person so a row of faces
   lines up whatever shape the original photograph was, and the name under it
   rather than over it — a caption across a face is unreadable on a projector. */
.cd-people {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--cd-cols, 5), minmax(0, 1fr));
  gap: 22px 18px;
}
.cd-person { display: grid; justify-items: center; gap: 10px; text-align: center; }
.cd-person__shot {
  display: grid;
  place-items: center;
  width: 100%;
  max-width: 148px;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border: 1.5px solid rgba(126, 217, 87, 0.45);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
}
.cd-person__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cd-person__mono { color: var(--cd-lime); font-size: 38px; font-weight: 800; }
.cd-person__name { font-size: 17px; font-weight: 800; line-height: 1.15; }
.cd-person__role { color: var(--cd-body); font-size: 13px; line-height: 1.3; }

/* ----------------------------------------------------------------- modules */
/* Fills down the first column and then across, not row by row. The flyer runs
   01–08 down the left and 09–16 down the right; row-major grid order gave
   01, 02 / 03, 04 and broke the reading the printed page teaches. */
.cd-modules {
  display: grid;
  grid-template-columns: repeat(var(--cd-cols, 2), minmax(0, 1fr));
  grid-template-rows: repeat(var(--cd-rows, 8), auto);
  grid-auto-flow: column;
  gap: 0 56px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.cd-module {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding: 15px 2px;
  border-bottom: 1px solid var(--cd-line);
}

.cd-module__n {
  color: var(--cd-lime);
  font-size: 17px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.cd-module__title { font-size: 19px; font-weight: 700; }

/* ------------------------------------------------------------------- close */
.cd-close { display: grid; gap: 34px; justify-items: center; text-align: center; }

.cd-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }

.cd-chip {
  padding: 9px 17px;
  border: 1px solid var(--cd-line);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  font-size: 14px;
  font-weight: 700;
}

.cd-stats--close { width: 100%; max-width: 720px; }

.cd-lines { display: grid; gap: 10px; }

.cd-line { margin: 0; color: var(--cd-body); font-size: 24px; }

.cd-line--last {
  color: #ffffff;
  font-family: var(--display);
  font-size: 64px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.cd-contact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 28px;
  padding-top: 22px;
  border-top: 1px solid var(--cd-line);
  width: 100%;
}

.cd-contact__item {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--cd-body);
  font-size: 15px;
  font-weight: 600;
}

.cd-contact__item .ic { color: var(--cd-lime); }

/* ---------------------------------------------------------------- progress */
/* Sits clear of the deck's own control pill, which is fixed to the bottom of
   the viewport and would otherwise land on it. */
.cd-progress {
  position: absolute;
  left: 88px;
  bottom: 42px;
  display: flex;
  gap: 8px;
}

.cd-progress__dot {
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.22);
  transition: background 0.3s ease;
}

.cd-progress__dot.is-on { background: var(--cd-lime); }

.cd-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--course-deck {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .cd-root {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

@media (prefers-reduced-motion: reduce) {
  .cd-frame, .cd-progress__dot { transition: none !important; }
}

@media (max-width: 900px) {
  .cd-root:not(.fit-slide__inner .cd-root) .cd-inner { padding: 28px 24px 70px; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-title { font-size: 44px; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-heading { font-size: 30px; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-cards,
  .cd-root:not(.fit-slide__inner .cd-root) .cd-modules { grid-template-columns: minmax(0, 1fr); }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-people { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-split { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-mark { position: static; height: 64px; padding: 8px 14px; margin: 0 0 20px; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-card--wide { align-items: flex-start; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-card--wide .cd-card__list { grid-template-columns: minmax(0, 1fr); }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-stats { flex-wrap: wrap; }
  .cd-root:not(.fit-slide__inner .cd-root) .cd-progress { left: 24px; bottom: 24px; }
}

/* The credential tile in the hero stat row. A ruled disc rather than a copy of
   the partner's seal — see CourseDeck.js. */
.cd-seal {
  align-content: center;
  justify-items: center;
  text-align: center;
  gap: 4px;
  border-color: rgba(233, 122, 74, 0.45);
  background: rgba(233, 122, 74, 0.1);
}

.cd-seal__ring {
  color: #F0A882;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 4px;
  text-transform: uppercase;
}

.cd-seal__name {
  color: #ffffff;
  font-size: 22px;
  font-weight: 800;
  line-height: 1.15;
}

.cd-seal__note {
  color: #F0A882;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
}

/* ==========================================================================
   Drift wall — tiles drifting up and down a tilted 3D plane, title punched
   through the middle. Ported from DriftWall.css; the remote font import is
   gone and every size is a flat nominal value.
   ========================================================================== */
.dw-root {
  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  /* NGI's deep green unless the block names its own plate; the ground and the
     scrim behind the centre are the same colour, so a deck sets it once. */
  --dw-plate: #0c140f;
  background: var(--dw-plate);
  color: #eaf1ec;
}

.dw-wall {
  position: absolute;
  inset: 0;
  overflow: hidden;
  perspective: var(--dw-perspective, 1200px);
  perspective-origin: 50% 50%;
  /* Feathers the top and bottom edges so columns arrive and leave rather than
     being sliced off by the frame. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 6%, #000 94%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 6%, #000 94%, transparent 100%);
}

.dw-plane {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  transform-origin: 50% 50%;
  transform-style: preserve-3d;
  will-change: transform;
  cursor: pointer;
}

.dw-col {
  position: relative;
  width: calc(var(--dw-tile-w) + var(--dw-gap));
  transform-style: preserve-3d;
}

.dw-track {
  display: flex;
  flex-direction: column;
  transform-style: preserve-3d;
  will-change: transform;
}

/* The tile takes the picture's shape rather than the picture being cut to the
   tile's. A fixed 230×150 landscape box with `object-fit: cover` took the top
   off every portrait in the wall — including the faces. Height is `auto` on the
   image, so the tile is exactly as tall as the photograph is, nothing is
   cropped, and no letterbox bars appear either. Column loops are re-measured
   from the real content height once the images land (see DriftWall.js), so the
   varying heights stay seamless. */
/* NOT `transform-style: preserve-3d`, and this is the wall's whole performance
   story (measured 2026-09-17). Torii's snapshot draws 1,149 tiles, and giving
   each one its own 3D context costs more than everything else on the slide put
   together: idle frame time 48.7ms with it and 20.9ms without — 20fps against
   48. Nothing inside a tile is at a different depth from the tile itself; the
   only Z was the hover lift, which is a scale now (see below). The wall's depth
   is unchanged: `.dw-wall` still holds the perspective and `.dw-plane`,
   `.dw-col` and `.dw-track` still keep their 3D contexts, so the columns still
   stand in space and still turn with the pointer.
   Measured and found NOT to matter, so all three stay: the `saturate(0.92)` on
   every image (48.5 without it), the 0.42s transitions (48.6), and the hover
   box-shadow (48.7). Ablate before optimising — two of the three obvious
   suspects here cost nothing at all. */
.dw-tile {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  padding: calc(var(--dw-gap) / 2);
  outline: none;
}

/* Skip the tiles that are off screen — about 720 of 766 at any moment. Set by
   DriftWall.js once it has pinned every tile's measured height, never from
   here: a tile is sized by its own photograph, and skipping the layout of one
   whose height had not been pinned would shorten the column the wrap length is
   measured from. See the note on `cull` in that file. */
.dw-wall.is-culled .dw-tile {
  content-visibility: auto;
}

.dw-tile__inner {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--dw-radius);
  background: #0b0b12;
  opacity: var(--dw-dim);
  transform: translateZ(0);
  transition:
    transform 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

.dw-tile img {
  display: block;
  width: 100%;
  height: auto;
  /* A floor and a ceiling only — an image that has not decoded yet has no
     height at all, and one very tall portrait should not own a whole column. */
  min-height: 90px;
  max-height: 340px;
  object-fit: contain;
  filter: saturate(0.92);
  transition: filter 0.42s cubic-bezier(0.22, 1, 0.36, 1);
  user-select: none;
  -webkit-user-drag: none;
}

.dw-tile__scrim {
  position: absolute;
  inset: 0;
  background: #08100b;
  opacity: 0.25;
  pointer-events: none;
  transition: opacity 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The lift is a scale, not a translateZ. With the tile flat (see above) a Z
   translation renders as nothing at all, so `DriftWall.js` converts the block's
   `lift` — a distance in px toward the viewer — into the scale that distance
   would have produced at this wall's own perspective, and publishes it as
   `--dw-lift-scale`. The block field keeps its meaning and the tile keeps its
   look; the difference is that a scale composites and a 3D context does not. */
.dw-tile:hover .dw-tile__inner,
.dw-tile:focus-visible .dw-tile__inner {
  opacity: 1;
  transform: scale(var(--dw-lift-scale, 1.043));
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.7);
}

.dw-tile:hover img,
.dw-tile:focus-visible img { filter: saturate(1.05); }

.dw-tile:hover .dw-tile__scrim,
.dw-tile:focus-visible .dw-tile__scrim { opacity: 0.05; }

.dw-tile:focus-visible .dw-tile__inner { outline: 2px solid #7ED957; outline-offset: 2px; }

/* ------------------------------------------------------------------- pill */
.dw-pill {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: 999px;
  background: #ffffff;
  box-shadow: 0 10px 26px -10px rgba(0, 0, 0, 0.6);
  opacity: 0;
  transform: translateY(9px);
  transition: opacity 0.28s ease, transform 0.28s ease;
  pointer-events: none;
}

.dw-tile:hover .dw-pill,
.dw-tile:focus-visible .dw-pill { opacity: 1; transform: none; }

.dw-pill__rule { width: 14px; height: 2px; border-radius: 2px; background: #2e9c4a; }

.dw-pill__text {
  color: #16241c;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ------------------------------------------------------------- the title */
/* The radial ground is what punches the title through the wall rather than
   laying a card on top of it. */
.dw-hero {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 6;
  transform: translate(-50%, -50%);
  /* The scrim behind the title is sized by this padding, so it comes down with the
     type. At 54/96 around a 104px line the plate covered 43% of the slide width and
     29% of its height, which buried the photographs it is supposed to sit over. */
  padding: 34px 62px;
  text-align: center;
  pointer-events: none;
  background: radial-gradient(ellipse 116% 132% at 50% 50%,
    var(--dw-plate) 34%, color-mix(in srgb, var(--dw-plate) 86%, transparent) 56%, transparent 76%);
}
/* The mark instead of the name. Sized so it leads the wall the way the 64px
   title did — about six rows of tiles — and no more. */
.dw-hero--logo { padding: 26px 74px 30px; }
.dw-hero__logo {
  display: block;
  width: 210px;
  height: 210px;
  margin: 0 auto;
  object-fit: contain;
}
.dw-hero--logo .dw-hero__tagline { margin-top: 2px; color: rgba(255, 255, 255, 0.74); }

.dw-hero__title {
  margin: 0;
  font-family: var(--display);
  /* Medium: 104px was the height of six rows of the wall behind it. 64 still leads the
     slide without being the only thing on it. */
  font-size: 64px;
  /* A drawn weight now, not a synthesised one. */
  font-weight: 600;
  line-height: 0.98;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}

.dw-hero__top { color: #4cc072; }
.dw-hero__bottom { color: #FFC42E; }

.dw-hero__tagline {
  margin: 11px 0 0;
  color: #c2d3c8;
  /* Scaled with the title so the pair keeps its proportion. */
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 3.6px;
  text-transform: uppercase;
}

/* --------------------------------------------------------------- lightbox */
.dw-modal {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 40px;
  background: rgba(4, 8, 6, 0.82);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.dw-modal.is-in { opacity: 1; }

.dw-modal__card {
  position: relative;
  display: grid;
  max-width: min(1100px, 92vw);
  max-height: 88vh;
  overflow: hidden;
  border-radius: 18px;
  background: #101a14;
  box-shadow: 0 40px 100px -30px rgba(0, 0, 0, 0.8);
}

.dw-modal__media { display: grid; place-items: center; min-height: 0; background: #06100a; }

.dw-modal__media img { max-width: 100%; max-height: 70vh; display: block; object-fit: contain; }

.dw-modal__meta { display: grid; gap: 6px; padding: 20px 26px 24px; }

.dw-modal__tag {
  color: #7ED957;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 2.4px;
  text-transform: uppercase;
}

.dw-modal__title { margin: 0; color: #ffffff; font-size: 24px; font-weight: 800; }

.dw-modal__close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #ffffff;
  cursor: pointer;
}

.dw-modal__close:hover { background: rgba(0, 0, 0, 0.8); }

.dw-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

/* The block is the whole page, so it owns the canvas cell outright. */
.canvas-block--drift-wall {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.slide--hero .dw-root {
  min-height: 860px !important;
  height: 100% !important;
  flex: 1 1 auto !important;
  border-radius: 0 !important;
}

/* Drifting columns are exactly the motion someone disabling animation is asking
   to be rid of. The wall stays, still. */
@media (prefers-reduced-motion: reduce) {
  .dw-tile__inner,
  .dw-tile img,
  .dw-tile__scrim,
  .dw-pill,
  .dw-modal { transition: none !important; }
}

@media (max-width: 900px) {
  .dw-root:not(.fit-slide__inner .dw-root) .dw-hero { padding: 26px 30px; }
  .dw-root:not(.fit-slide__inner .dw-root) .dw-hero__title { font-size: 42px; }
  .dw-root:not(.fit-slide__inner .dw-root) .dw-hero__tagline { font-size: 12px; letter-spacing: 3px; }
}

/* A photograph the column would butcher is shown whole instead of cropped.
   Set by LeadershipPanels.js from the picture's own ratio — see fitToPanel. */
.lj-photo--whole {
  object-fit: contain;
  background: color-mix(in srgb, var(--accent, #16181a) 14%, #ffffff);
  padding: 18px;
}



/* ==========================================================================
   Leadership panels — horizontal arrangement.

   The spines were vertical, which made the open panel a tall, narrow column:
   0.55 aspect, in which a 16:9 card can only be cropped or stranded in the
   middle with dead space above and below. Turning the rail on its side makes
   the open panel wide and short, which is the shape a photograph is, so the
   picture fills it instead of floating in it.
   ========================================================================== */
.lj-rail { flex-direction: column; }

.lj-card {
  flex-direction: column;
  min-width: 0;
  min-height: 52px;
  transition: flex-grow 0.55s cubic-bezier(0.6, 0.05, 0.2, 1);
}

.lj-card.is-open { flex: 1 1 0; flex-grow: 16; }
/* 52, not 62. Four collapsed bars at 62px cost the open panel 248px of the
   900 available, which was the last 28px the longest chapter needed. */
.lj-card.is-closed { flex: 0 0 52px; }

/* The spine is a bar now: number, title and mark reading left to right. */
.lj-card .lj-spine {
  flex: 0 0 52px;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 20px;
  width: 100%;
  padding: 0 28px;
}

.lj-card .lj-spine-title {
  writing-mode: horizontal-tb;
  transform: none;
  letter-spacing: 2px;
}

.lj-card .lj-spine-icon { margin-left: auto; }

.lj-card .lj-body { flex: 1 1 auto; flex-direction: row; min-height: 0; }

/* Wide enough that a landscape photograph is close to filling it, with the
   copy taking the rest. */
/* 50%, not 58%. At 58% the copy column was 742px and the two longest chapters
   ran 33-46px past it; the picture loses little at this width and the text
   gains a whole line's worth of room. */
.lj-card .lj-media { flex: 0 0 50%; min-width: 0; height: auto; }

/* The last 8px. Ecosystem Builder is the longest chapter and was still
   clipping its final line; a slightly tighter measure clears it without
   touching the type size. */
.lj-card .lj-content { flex: 1 1 auto; min-width: 0; overflow: auto; padding: 22px 30px; }
.lj-card .lj-summary { margin-bottom: 14px; }
.lj-card .lj-role { margin-bottom: 10px; }

/* The hero overlay sat over vertical spines; it has the top bar's height to
   clear now, not a column's width. */
.lj-root.all-closed .lj-overlay { padding: 34px 44px; }

/* --- horizontal rail: corrections ------------------------------------- */

/* Collapsed, the five bars share the whole screen instead of stacking 52px
   deep and leaving the rest of the slide empty. The 52px floor is only for
   the bars that stand aside once a chapter is open. */
.lj-root.all-closed .lj-card,
.lj-root.all-closed .lj-card.is-closed {
  flex: 1 1 0;
  min-height: 0;
}

.lj-root.all-closed .lj-card .lj-spine {
  flex: 1 1 auto;
  padding: 0 34px;
}

/* The chapter number is already carried by the order of the bars and by the
   ghosted figure on the open panel; a third copy on every spine was noise. */
.lj-card .lj-num { display: none; }

/* The prev / next / count line at the foot of each chapter duplicated the
   rail sitting directly above it — every one of those destinations is one
   click away on screen. */
.lj-card .lj-foot { display: none; }

/* The title sat directly on the bars and collided with them. It gets its own
   plate now, centred, so it reads as a cover over the rail rather than text
   printed across it. */
.lj-root.all-closed .lj-overlay {
  top: 50%;
  left: 50%;
  width: auto;
  max-width: 78%;
  transform: translate(-50%, -50%);
  padding: 40px 56px;
  text-align: center;
  border-radius: 20px;
  background: rgba(8, 14, 10, 0.82);
  backdrop-filter: blur(3px);
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.7);
}

.lj-root.all-closed .lj-overlay .lj-rule { margin-left: auto; margin-right: auto; }

/* No title card over the rail. It was there when the spines were vertical and
   the middle of the slide was empty; with the bars filling the screen it only
   covers three of them, and each bar already names its own chapter. */
.lj-root .lj-overlay { display: none; }

/* The push-to-presenter switch on a navigation row. Deliberately quiet: it sits
   in a list read many times a day and acted on rarely, so it holds its colour
   only when the section is live. */
.snav-release {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-left: 6px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}

.snav-release:hover { border-color: var(--nav-accent, var(--brand-accent)); color: var(--nav-accent-ink, var(--accent-ink)); }

.snav-release.is-live {
  border-color: var(--nav-accent, var(--brand-accent));
  background: color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 14%, #ffffff);
  color: var(--nav-accent-ink, var(--accent-ink));
}

.snav-release:disabled { opacity: 0.5; cursor: progress; }

.snav-release:focus-visible { outline: 2px solid var(--nav-accent, var(--brand-accent)); outline-offset: 1px; }

/* The rows are buttons; the toggle sits inside one, so it needs its own layer
   to receive the click rather than the row swallowing it. */
.nav-tree__parent, .nav-tree__child { position: relative; }
.snav-release { position: relative; z-index: 2; }

/* --- navigation rows: keep them one line tall ------------------------------
   The Draft flag and the release switch were wrapping onto a second line, so
   every unreleased row stood twice as tall as a released one and the list of
   seventeen no longer fitted the pane. Both are trailing marks on the same
   line; the title takes the space that is left. */
.nav-tree__parent,
.nav-tree__child {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 8px;
  min-height: 0;
}

.nav-tree__title,
.nav-tree__child-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-tree__badge,
.nav-tree__child-icon,
.nav-tree__chevron,
.nav-tree__flag,
.snav-release { flex: none; }

/* "DRAFT" beside a 24px switch was as wide as some of the titles. It is a
   state marker, not a label — the switch already says the same thing. */
.nav-tree__flag {
  padding: 2px 6px;
  font-size: 9.5px;
  letter-spacing: 0.06em;
}

.snav-release { margin-left: 0; }

/* --- sidebar: nothing scrolls sideways ------------------------------------
   The rows were wider than the pane, so the whole sidebar carried a horizontal
   scrollbar along the bottom. Every row now lives inside the pane's width and
   the title is what gives way, by ellipsis. */
.nav-tree,
.nav-tree__group { min-width: 0; max-width: 100%; overflow-x: clip; }

.nav-tree__parent,
.nav-tree__child {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* The chevron on a group with no subsections was still taking its column. */
.nav-tree__parent .nav-tree__chevron { margin-left: 0; }

/* The subsection list was 4px wider than the group holding it — enough to put
   a scrollbar under the entire sidebar. The 34px left indent was being added
   to a full-width box rather than taken out of it. */
.nav-tree__children,
.nav-tree__children-inner {
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  overflow-x: clip;
}

.nav-tree__children-inner { padding-right: 10px; }

.nav-tree__child { max-width: 100%; }

/* The guide scrolls, it just does not advertise it. A track down the side of a
   sixteen-row list is a permanent piece of furniture for something the wheel
   already does; the list still scrolls exactly as before. */
.sidenav__scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
  scroll-behavior: smooth;
}

.sidenav__scroll::-webkit-scrollbar { width: 0; height: 0; }

/* The switch reads as a line icon rather than a hairline: at 14px the default
   1.7 stroke thinned out against the row's text. */
.snav-release .ic { width: 15px; height: 15px; }
/* ==========================================================================
   Platform wall — a rotating 3D stack of browser windows; clicking one runs
   the real product inside the slide.

   The swap is the CardSwap motion rebuilt on CSS transforms: GSAP is not
   available (zero runtime dependencies, presented offline), so the timeline
   lives in Platforms.js and this file supplies only the stage and the skin.

   Credentials are never rendered as text. The stage offers the role name and
   two copy buttons, because a password on a projector is a password given away.
   ========================================================================== */
.pf-root {
  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.pf-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

/* ------------------------------------------------------------- the wall ---- */
.pf-wall {
  display: grid;
  /* Absolute, not height:100%. The root's height comes from its min-height, so
     its computed `height` is auto and a percentage height on a child never
     resolves — the wall collapsed to its content and left a band of dead slide
     under the deck. Pinning to the root's box is the only reliable fill. */
  position: absolute;
  inset: 0;
  grid-template-columns: minmax(260px, 0.36fr) minmax(0, 1.64fr);
  align-items: center;
  gap: 16px;
  padding: 40px 16px 40px 40px;
}
.pf-root.is-open .pf-wall { display: none; }

.pf-intro { max-width: 520px; }
.pf-eyebrow {
  margin: 0 0 12px; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase;
}
.pf-title { margin: 0; font-size: 46px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.04; }
.pf-sub { margin: 14px 0 0; color: var(--muted); font-size: 16.5px; line-height: 1.55; }

.pf-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 28px; }
.pf-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text);
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
  transition: border-color .18s ease, transform .18s ease;
}
.pf-chip:hover { border-color: var(--nav-accent, var(--brand-accent)); transform: translateY(-1px); }

/* Once the rail is a dock, the transform belongs to the animation loop. Leaving
   it in the transition made every frame chase a 180ms tween of the frame before,
   which reads as lag; and the hover lift would fight the scale for the same
   property. Both are dropped for docked chips — the dock supplies its own lift. */
.pf-chips.is-dock .pf-chip {
  transition: border-color .18s ease, box-shadow .18s ease;
  will-change: transform;
}
.pf-chips.is-dock .pf-chip:hover { transform: none; }
/* Grown chips overlap their neighbours instead of shoving them, so the one under
   the pointer needs to read as the top one. */
.pf-chips.is-dock .pf-chip.is-near {
  box-shadow: 0 10px 24px -10px rgb(15 23 42 / 0.35);
  border-color: var(--nav-accent, var(--brand-accent));
}
.pf-chip .ic { color: var(--nav-accent-ink, var(--accent-ink)); }
.pf-chip em {
  font-style: normal; font-size: 10.5px; font-weight: 800; color: var(--muted);
  padding: 2px 6px; border-radius: 999px; background: var(--page-bg);
}
.pf-hint { margin: 20px 0 0; color: var(--muted); font-size: 13px; }

/* ----------------------------------------------------- the rotating deck ---- */
.pf-stagearea {
  position: relative;
  height: 100%;
  min-height: 600px;
  perspective: 1500px;
  perspective-origin: 55% 50%;
}
/* The fan steps right and up, so the deck origin sits low and left of centre —
   otherwise the tail of the stack runs past the slide edge and gets clipped. */
/* Origin chosen so the fan — which steps right and up — sits inside the slide
   at the largest window size that still clears both edges. Measured, not
   guessed: at 990x570 with a 32/20 step, and allowing for the ±35px of vertical
   extent the 4deg skew adds to a 990px-wide card, the stack spans 1182x759 of
   the 1207x780 the column gives it. */
.pf-deck { position: absolute; top: 50%; left: 50%; width: 0; height: 0; transform-style: preserve-3d; }

.pf-win {
  position: absolute; top: 0; left: 0;
  display: flex; flex-direction: column;
  width: 980px; height: 566px;
  padding: 0; margin: 0;
  border: 1px solid var(--border); border-radius: 16px;
  background: var(--surface);
  box-shadow: 0 30px 70px rgba(15, 23, 42, 0.18), 0 4px 14px rgba(15, 23, 42, 0.08);
  overflow: hidden; cursor: pointer;
  font: inherit; text-align: left; color: inherit;
  transform-style: preserve-3d; backface-visibility: hidden; will-change: transform;
}
.pf-win:hover { border-color: var(--nav-accent, var(--brand-accent)); }
.pf-win:focus-visible { outline: 3px solid var(--nav-accent, var(--brand-accent)); outline-offset: 3px; }

.pf-win__bar {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  padding: 7px 14px; border-bottom: 1px solid var(--border); background: var(--page-bg);
}
.pf-win__dots { display: inline-flex; gap: 6px; }
.pf-win__dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--border); }
.pf-win__dots i:first-child { background: #ff5f57; }
.pf-win__dots i:nth-child(2) { background: #febc2e; }
.pf-win__dots i:nth-child(3) { background: #28c840; }
.pf-win__host {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 1px 10px; border-radius: 999px; background: var(--surface);
  color: var(--muted); font-size: 11px; line-height: 1.5; font-weight: 600; text-align: center;
}

.pf-win__shell { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; background: var(--page-bg); }
.pf-win__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
/* The recording is scenery inside a button — clicks belong to the window. */
.pf-win__video { pointer-events: none; background: var(--page-bg); }

.pf-win__blank {
  position: absolute; inset: 0; display: grid; align-content: center; justify-items: center;
  gap: 10px; padding: 24px; text-align: center;
}
.pf-win__mark {
  display: grid; place-items: center; width: 56px; height: 56px; border-radius: 16px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--nav-accent-ink, var(--accent-ink));
}
.pf-win__blankname { font-size: 22px; font-weight: 800; }
.pf-win__blankblurb { max-width: 340px; color: var(--muted); font-size: 14px; line-height: 1.5; }

.pf-win__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex: 0 0 auto;
  padding: 13px 18px; border-top: 1px solid var(--border); background: var(--surface);
}
.pf-win__name { font-size: 18px; font-weight: 800; letter-spacing: -0.01em; }
/* Which face of the platform this window is — portal or admin. Outlined, not
   filled: a solid accent pill on all seven windows turned the fan into blobs. */
.pf-win__sub {
  margin-right: auto; padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--muted);
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
}
.pf-win__open {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--nav-accent-ink, var(--accent-ink)); font-size: 12.5px; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.08em;
}

/* --------------------------------------------------------------- the stage -- */
.pf-stage { position: absolute; inset: 0; display: none; grid-template-rows: auto minmax(0, 1fr); background: var(--surface); }
.pf-root.is-open .pf-stage { display: grid; }

.pf-stage__bar { display: flex; align-items: center; gap: 16px; padding: 12px 18px; border-bottom: 1px solid var(--border); }
.pf-stage__back, .pf-stage__out, .pf-stage__keys {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text);
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; text-decoration: none;
  transition: border-color .18s ease;
}
.pf-stage__back:hover, .pf-stage__out:hover, .pf-stage__keys:hover { border-color: var(--nav-accent, var(--brand-accent)); }
.pf-stage__id { display: grid; min-width: 0; }
.pf-stage__name { font-size: 14.5px; font-weight: 800; }
.pf-stage__url { color: var(--muted); font-size: 12px; }
.pf-stage__view {
  margin-left: 8px; padding: 2px 8px; border-radius: 999px; font-style: normal;
  background: var(--nav-accent, var(--brand-accent)); color: #ffffff;
  font-size: 10px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
}

/* Portal / Admin — the two faces of the same platform, one click apart. */
.pf-switch { display: inline-flex; padding: 3px; border: 1px solid var(--border); border-radius: 999px; background: var(--page-bg); }
.pf-switch[hidden] { display: none; }
.pf-switch__btn {
  padding: 6px 15px; border: 0; border-radius: 999px; background: transparent;
  color: var(--muted); font: inherit; font-size: 12.5px; font-weight: 800; cursor: pointer;
  transition: background .18s ease, color .18s ease;
}
.pf-switch__btn:hover { color: var(--text); }
.pf-switch__btn.is-on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

.pf-chip b {
  padding: 2px 7px; border-radius: 999px;
  background: var(--nav-accent, var(--brand-accent)); color: #ffffff;
  font-size: 9.5px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
}

.pf-stage__tools { position: relative; margin-left: auto; display: flex; align-items: center; gap: 10px; }
.pf-stage__keys.is-on {
  border-color: var(--nav-accent, var(--brand-accent));
  background: var(--nav-accent, var(--brand-accent)); color: #ffffff;
}
.pf-stage__keys.is-on .ic { color: #ffffff; }

.pf-stage__body { display: grid; min-height: 0; }
.pf-stage__frame { width: 100%; height: 100%; border: 0; background: #ffffff; }

/* ------------------------------------------------- credentials, on request -- */
.pf-keys {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 30;
  width: 340px; max-height: 560px; overflow: auto;
  padding: 16px; border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);
}
.pf-keys[hidden] { display: none; }
.pf-keys__head {
  margin: 0 0 12px; color: var(--muted);
  font-size: 11px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
}
.pf-key { padding: 12px 0; border-top: 1px solid var(--border); }
.pf-key:first-of-type { border-top: 0; padding-top: 0; }
.pf-key__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 9px; }
.pf-key__role { font-size: 14px; font-weight: 800; }
.pf-key__jump {
  padding: 3px 9px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface);
  color: var(--muted); font: inherit; font-size: 10.5px; font-weight: 800; cursor: pointer;
}
.pf-key__jump:hover { border-color: var(--nav-accent, var(--brand-accent)); color: var(--text); }
.pf-key__acts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pf-key__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 10px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--page-bg); color: var(--text);
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.pf-key__btn:hover { border-color: var(--nav-accent, var(--brand-accent)); background: var(--surface); }
.pf-keys__note { margin: 14px 0 0; color: var(--muted); font-size: 11.5px; line-height: 1.5; }

/* --------------------------------------------------------- full-bleed slide -- */
.canvas-block--platforms {
  min-height: 860px !important; height: 100% !important; flex: 1 1 auto !important;
  display: flex !important; flex-direction: column !important; grid-row: 1 / -1 !important;
}
.slide--hero .pf-root { min-height: 860px !important; height: 100% !important; flex: 1 1 auto !important; border-radius: 0 !important; }

@media (prefers-reduced-motion: reduce) {
  .pf-win { transition: none !important; }
}

/* ==========================================================================
   Centers of Excellence — a wall of partner academies; opening one shows its
   photos and videos without leaving the slide.

   The source design leans on Google Fonts and drifting radial gradients.
   Neither travels: the deck is presented offline, and white is the major
   surface here. Each centre's brand colour is kept for the card you are
   pointing at — its edge, its mark, its rule on the detail page.
   ========================================================================== */
.coe-root {
  position: relative;
  height: 100%;
  min-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.coe-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

/* The scrollbar is gone, not the scrolling. A grey trough down the edge of a
   projected slide is furniture the room does not need; the rows still move,
   smoothly, and each one animates in as it arrives. */
.coe-wall {
  position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden;
  padding: 34px 46px 30px;
  scroll-behavior: smooth; scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior: contain;
}
.coe-wall::-webkit-scrollbar { width: 0; height: 0; display: none; }
.coe-root.is-open .coe-wall { display: none; }

.coe-head { margin-bottom: 22px; }
.coe-eyebrow {
  margin: 0 0 10px; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
/* The deck's own display face, self-hosted, so it survives being presented
   with no internet — the source design asked for Space Grotesk from Google
   Fonts, which would simply not arrive. Agency FB is condensed, so it carries
   a longer line at a larger size without wrapping. */
.coe-title {
  margin: 0;
  font-family: var(--display);
  font-size: 58px; font-weight: 700; letter-spacing: 0.005em; line-height: 1.02;
  text-transform: uppercase;
}
.coe-sub { margin: 9px 0 0; max-width: 62ch; color: var(--muted); font-size: 16.5px; line-height: 1.55; }

.coe-stats { display: flex; align-items: stretch; gap: 22px; margin-top: 12px; }
.coe-stats > div { display: grid; }
.coe-stats b { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; }
.coe-stats span { color: var(--muted); font-size: 12px; letter-spacing: 0.06em; }
.coe-stats i { width: 1px; background: var(--border); }

/* auto-fit, never auto-fill: auto-fill leaves a phantom column and the last
   row of centres ends up floating in dead space. */
/* Four equal rows that share whatever height is left, rather than rows sized
   by their content with the remainder pooling at the bottom of the slide. */
.coe-grid {
  display: grid; flex: 1 1 auto;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-template-rows: repeat(4, minmax(0, 1fr));
  gap: 14px;
}

.coe-card {
  position: relative; display: flex; flex-direction: column; gap: 8px;
  min-height: 0; overflow: hidden;
  padding: 14px 14px 11px; border: 1px solid var(--border); border-radius: 16px;
  background: var(--surface); color: inherit; font: inherit; text-align: left; cursor: pointer;
  box-shadow: 0 1px 3px rgba(20, 25, 35, 0.05);
  transition: transform .32s cubic-bezier(.2,.8,.2,1), box-shadow .32s ease, border-color .32s ease;
}
.coe-card::before {
  content: ''; position: absolute; inset: 0; border-radius: 18px; pointer-events: none;
  background: var(--coe-wash); opacity: 0; transition: opacity .32s ease;
}
.coe-card:hover, .coe-card:focus-visible {
  transform: translateY(-5px);
  border-color: var(--coe-edge);
  box-shadow: 0 20px 44px -20px var(--coe-glow);
}
.coe-card:hover::before, .coe-card:focus-visible::before { opacity: 1; }
.coe-card:focus-visible { outline: 3px solid var(--coe-edge); outline-offset: 3px; }
.coe-card > * { position: relative; }

.coe-card__top { display: flex; align-items: center; }
/* Several of the supplied "square" files are actually wordmarks. A fixed 46px
   box shrank them to a smudge, so the mark holds a constant height and takes
   whatever width its artwork needs, up to a limit the card can carry. */
.coe-logo { display: block; width: 130px; height: 44px; padding: 0; }
/* An explicit height, not max-height: the supplied files vary from 40px to
   800px intrinsic, and a cap alone left the small ones as specks. Height is
   fixed so every mark reads at the same optical weight; width follows the
   artwork. */
/* One box, contain, left-aligned. Neither a fixed height nor a fixed width
   works alone: the supplied artwork runs from 1.00 aspect (Microsoft and
   Splunk are square canvases with the mark floating in padding) to 4.66
   (Claude). A constant box lets each one take the dimension it needs and
   keeps every card's mark on the same baseline. */
.coe-logo img { width: 100%; height: 100%; object-fit: contain; object-position: left center; }
.coe-mono {
  display: grid; place-items: center; width: 46px; height: 46px; border-radius: 13px;
  font-size: 16px; font-weight: 800; letter-spacing: -0.02em;
}
.coe-card__name { font-size: 15px; font-weight: 700; line-height: 1.2; }
/* Two lines at most, and allowed to shrink. A 1fr grid row does not stop a
   card growing past it — the card spills, and on a short display that spill
   went under the deck controls. Clamping the one elastic line is what makes
   the row height mean something. */
.coe-card__tag {
  color: var(--muted); font-size: 11.5px; line-height: 1.38;
  min-height: 0; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.coe-card__foot {
  display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 12px;
  border-top: 1px solid var(--border); color: var(--muted); font-size: 11.5px;
}
/* The design paints this label in each brand's own colour. Measured against
   white, 16 of the 19 fall below 4.5:1 — MongoDB's green is 1.58:1, which is
   barely a mark on the page at projector distance. The brand still owns the
   card: its mark, its tint on hover, its edge, its rule on the detail page.
   The one thing it does not get is the running text. */
.coe-card__go {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto;
  color: var(--text); font-weight: 800;
}
.coe-card__go .ic { color: var(--coe-color); }

/* ------------------------------------------------------------- the detail */
.coe-detail {
  position: absolute; inset: 0; display: none; overflow-y: auto; overflow-x: hidden;
  padding: 32px 52px 40px; background: var(--surface);
  scroll-behavior: smooth; scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior: contain;
}
.coe-detail::-webkit-scrollbar { width: 0; height: 0; display: none; }
.coe-root.is-open .coe-detail { display: block; }

.coe-back {
  display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--page-bg);
  color: var(--text); font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
  transition: border-color .2s ease, transform .2s ease;
}
.coe-back:hover { border-color: var(--nav-accent, var(--brand-accent)); transform: translateX(-3px); }

.coe-hero { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; margin: 30px 0 0; }
.coe-logo--lg { display: block; width: 320px; height: 128px; padding: 0; border: 0; background: none; }
.coe-logo--lg img { width: 100%; height: 100%; }
.coe-mono--lg { width: 96px; height: 96px; border-radius: 24px; font-size: 34px; }
.coe-hero__name { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -0.02em; }
.coe-hero__tag { margin: 6px 0 0; color: var(--muted); font-size: 16px; }

.coe-rule { height: 2px; margin: 24px 0 28px; border-radius: 2px; }

.coe-gal__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 16px; }
.coe-gal__head h4 { margin: 0; font-size: 19px; font-weight: 800; }
.coe-gal__head span { color: var(--muted); font-size: 12.5px; }
.coe-gal__none { color: var(--muted); font-size: 13.5px; }

/* A masonry, not a grid of boxes. Every photo here arrives at a different
   shape — a wide event banner, a portrait of a handshake, a square poster —
   and a fixed-height cell with object-fit: cover simply cut them: the Oracle
   poster showed one strip of a bullet list. Columns let each image keep its
   own proportions and take the height it needs, so nothing is cropped and
   nothing is stretched. */
.coe-gal { column-gap: 14px; column-fill: balance; }
.coe-tile {
  position: relative; margin: 0 0 14px; overflow: hidden;
  break-inside: avoid; -webkit-column-break-inside: avoid;
  border: 1px solid var(--border); border-radius: 14px; background: var(--page-bg);
}
/* Width-constrained, height free. With height:auto the intrinsic ratio does
   the work and object-fit never comes into it — the browser draws the picture
   the shape it actually is. */
.coe-tile__media { display: block; width: 100%; height: auto; }
.coe-tile figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 8px 12px;
  background: rgba(9, 12, 20, 0.72); color: #ffffff; font-size: 12px; font-weight: 600;
}

.canvas-block--coe-wall {
  min-height: 860px !important; height: 100% !important; flex: 1 1 auto !important;
  display: flex !important; flex-direction: column !important; grid-row: 1 / -1 !important;
}
.slide--hero .coe-root { min-height: 860px !important; height: 100% !important; flex: 1 1 auto !important; border-radius: 0 !important; }

@media (prefers-reduced-motion: reduce) {
  .coe-card { transition: none !important; }
}

/* --- Centers of Excellence: motion ---------------------------------------
   Cards arrive in reading order, scatter out of the way when one is opened,
   and the detail rises into the space they leave. All of it is off under
   prefers-reduced-motion, where the component skips the sequence entirely. */
.coe-card.is-waiting { opacity: 0; transform: translateY(20px) scale(.97); }
.coe-card { transition: transform .42s cubic-bezier(.2,.8,.2,1), opacity .34s ease, box-shadow .32s ease, border-color .32s ease; }

.coe-detail { animation: coeDetailIn .5s cubic-bezier(.2,.8,.2,1) both; }
.coe-hero { animation: coeRise .45s .06s cubic-bezier(.2,.8,.2,1) both; }
.coe-rule { animation: coeRule .5s .16s cubic-bezier(.2,.8,.2,1) both; transform-origin: left center; }
.coe-gal__head { animation: coeRise .45s .18s cubic-bezier(.2,.8,.2,1) both; }
.coe-gal, .coe-gal__none { animation: coeRise .5s .24s cubic-bezier(.2,.8,.2,1) both; }
.coe-logo--lg, .coe-mono--lg { animation: coeMark .55s .1s cubic-bezier(.2,.9,.2,1) both; }

@keyframes coeDetailIn { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes coeRise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes coeRule { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes coeMark { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .coe-card, .coe-card.is-waiting { transition: none !important; opacity: 1 !important; transform: none !important; }
  .coe-detail, .coe-hero, .coe-rule, .coe-gal__head, .coe-gal, .coe-gal__none,
  .coe-logo--lg, .coe-mono--lg { animation: none !important; }
}

/* --- Centers of Excellence: the orbit the page opens on -------------------
   Twenty marks circling the organization's own. Each ring spins as a single
   element and every logo counter-spins at the same rate, so the marks stay
   upright while the ring turns — two animations, not twenty. */
/* Two rows, so the cue can never sit on top of the ring: the wheel centres in
   the space that is left after the line has taken what it needs. */
.coe-orbit {
  position: absolute; inset: 0;
  display: grid; grid-template-rows: auto minmax(0, 1fr); justify-items: center;
  padding: 26px 20px 30px; cursor: pointer; background: var(--surface);
}
.coe-root.is-wall .coe-orbit, .coe-root.is-open .coe-orbit { display: none; }
.coe-orbit:focus-visible { outline: 3px solid var(--nav-accent, var(--brand-accent)); outline-offset: -6px; }

.coe-orbit__field { position: relative; align-self: center; width: 640px; height: 640px; display: grid; place-items: center; }

.coe-ring {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  animation: coeSpin linear infinite;
}
.coe-ring--rev { animation-direction: reverse; }
/* Still a hairline — the glow comes from the shadow, not from thickening the
   stroke. Tinted by the organization's accent through currentColor, so Torii
   lights orange and Technical Hub green without a second rule. */
.coe-ring__path {
  position: absolute; inset: 0; border-radius: 50%;
  color: var(--nav-accent, var(--brand-accent));
  border: 1px solid currentColor;
  box-shadow: 0 0 18px 0 currentColor, inset 0 0 22px 0 currentColor;
  opacity: .26;
  animation: coeRingGlow 5.5s ease-in-out infinite;
}
.coe-ring--rev .coe-ring__path { animation-delay: -2.75s; }
@keyframes coeRingGlow { 0%, 100% { opacity: .16; } 50% { opacity: .42; } }
.coe-seat { position: absolute; top: 50%; left: 50%; width: 0; height: 0; }
.coe-seat__fix { position: absolute; top: 0; left: 0; width: 0; height: 0; }
/* No tile behind the mark. A white chip on a white page is a rectangle the
   eye has to ignore twenty times over; the logos sit on the page. */
.coe-seat__spin {
  position: absolute; top: 50%; left: 50%; display: grid; place-items: center;
  width: 104px; height: 66px; margin: -33px 0 0 -52px;
  animation: coeSpinSeat linear infinite;
}
.coe-seat__spin--rev { animation-direction: reverse; }
.coe-seat__spin .coe-logo { width: 92px; height: 52px; }

.coe-hub {
  position: relative; z-index: 2; display: grid; place-items: center;
  width: 190px; height: 190px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 18px 60px -22px rgba(15, 23, 42, 0.45);
}
.coe-hub img { max-width: 132px; max-height: 116px; object-fit: contain; }
.coe-hub b {
  font-family: var(--display); font-size: 54px;
  color: var(--nav-accent-ink, var(--accent-ink));
}

.coe-orbit__cue {
  margin: 0; padding-bottom: 4px; text-align: center; max-width: none; white-space: nowrap;
  font-family: var(--display);
  font-size: 36px; font-weight: 700; letter-spacing: 0.012em; text-transform: uppercase;
  color: var(--text);
}

@keyframes coeSpin { from { transform: translate(-50%, -50%) rotate(0deg); } to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes coeSpinSeat { from { transform: rotate(0deg); } to { transform: rotate(-360deg); } }

/* Collapsing: the rings draw in to the mark and fade before the wall shows. */
.coe-root.is-collapsing .coe-orbit__field { animation: coeCollapse .62s cubic-bezier(.5,0,.75,0) both; }
.coe-root.is-collapsing .coe-orbit__cue { animation: coeCueOut .3s ease both; }
@keyframes coeCollapse { to { transform: scale(.24); opacity: 0; } }
@keyframes coeCueOut { to { opacity: 0; transform: translateY(10px); } }

/* The wall waits behind the wheel until it is handed over. */
.coe-wall { display: none; }
.coe-root.is-wall .coe-wall { display: flex; flex-direction: column; }
.coe-root.is-open .coe-wall { display: none; }

@media (prefers-reduced-motion: reduce) {
  .coe-ring, .coe-seat__spin { animation: none !important; }
  .coe-root.is-collapsing .coe-orbit__field,
  .coe-root.is-collapsing .coe-orbit__cue { animation: none !important; }
}

/* --- Centers of Excellence: the headline ---------------------------------
   Not black. The organization's readable accent carries it, with a light
   sweeping across the letters.

   This is the one place the deck's no-gradient rule is set aside, and only
   inside the glyphs: the sweep is the effect, and it was asked for by name.
   The base colour is set on `color` as well, so if background-clip ever fails
   the headline is still a readable line rather than an invisible one. */
.coe-shine {
  color: var(--nav-accent-ink, var(--accent-ink));
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .coe-shine {
    background-image: linear-gradient(
      100deg,
      var(--nav-accent-ink, var(--accent-ink)) 0 42%,
      color-mix(in srgb, var(--nav-accent-ink, var(--accent-ink)) 32%, #ffffff) 50%,
      var(--nav-accent-ink, var(--accent-ink)) 58% 100%);
    background-size: 260% 100%;
    /* repeat, not no-repeat. The gradient is wider than the box and its
       position animates past both edges; with no-repeat the uncovered part of
       a multi-line heading painted as nothing at all and words vanished. */
    background-repeat: repeat;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: coeShine 6.5s linear infinite;
  }
}
@keyframes coeShine { from { background-position: 130% 0; } to { background-position: -130% 0; } }

@media (prefers-reduced-motion: reduce) {
  .coe-shine { animation: none !important; }
  .coe-ring__path { animation: none !important; opacity: .26 !important; }
}

/* --- Centers of Excellence: clear the deck controls -----------------------
   The Prev / count / Exit pill is fixed 22px off the bottom of the viewport
   and stands about 50px tall, so the last row of cards was running underneath
   it. These paddings are in slide units and the slide is scaled to the
   display, so 96 here is ~82px on a 1366-wide screen and ~115px on a 1920 —
   comfortably clear at both ends without stealing height when not presenting. */
/* The wall and the detail scroll, so shortening them costs nothing — content
   simply continues under the pill. The orbit does not: it is a fixed composition
   with the ring centred well clear of the pill, so the reservation was a 104px
   dead strip along the bottom of the page in presentation mode and nothing else. */
body.is-presenting .coe-wall,
body.is-presenting .coe-detail { bottom: 104px; }

/* ==========================================================================
   Success Stories — a wall of faces that keeps moving, and any one of them
   opens into the story beside it.

   The wall is columns, not a grid: each sits on an arc so the run reads as a
   wave, and each rises and falls on its own clock like the bars of an
   equaliser. The arc lives on the column and the bobbing on a wrapper inside
   it, because two transforms on one element means the animation wins and the
   wave flattens the moment it starts playing.
   ========================================================================== */
/* max-height, not just min-height. height:100% here resolves against a parent
   with no definite height, so it computes to auto and the flex children are
   free to grow — the portrait grew the stage, the stage grew the root to
   1625px, and the wall was pushed off the bottom of the slide. A definite
   ceiling is what lets flex shrink the stage instead. */
/* A moving wash in the organization's own colours behind the wall.
   This deck's rule is no gradients, and this is a deliberate exception: it was
   asked for by name. It is kept to a tint that never competes with a
   photograph — colour-mixed against the surface rather than laid over it — and
   it stops entirely under prefers-reduced-motion. */
.sw-root::before {
  content: ''; position: absolute; inset: -20%; z-index: 0; pointer-events: none;
  background:
    radial-gradient(38% 42% at 18% 22%,
      color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 12%, transparent), transparent 62%),
    radial-gradient(34% 38% at 82% 12%,
      color-mix(in srgb, var(--nav-accent-ink, var(--accent-ink)) 10%, transparent), transparent 62%),
    radial-gradient(44% 40% at 60% 96%,
      color-mix(in srgb, var(--nav-accent, var(--brand-accent)) 9%, transparent), transparent 64%);
  animation: swDrift 26s ease-in-out infinite;
}
.sw-root > * { position: relative; z-index: 1; }
@keyframes swDrift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(-3%, 2%, 0) scale(1.05); }
}


/* -------------------------------------------------------------- the intro */
.sw-eyebrow {
  margin: 0 0 10px; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
.sw-sub { margin: 14px 0 0; max-width: 56ch; color: var(--muted); font-size: 17px; line-height: 1.55; }

/* -------------------------------------------------------------- the stage */
/* Two caps, and the tighter one wins.
   CSS caps it to the frame. StoryWall then caps it to twice its own pixels,
   inline, once the file has loaded and its real size is known. Neither cap
   alone is enough: `width: 100%` blew a 206px source up ~5x into a ~1000px
   frame, which is softness no source survives and which `object-fit: contain`
   does not help with, because contain still scales up. Capping at 1x instead
   left a postage stamp in a large empty box. Twice is the point where the
   enlargement is still tolerable at projector distance.

   The border and radius live on the image, not on the frame, so a small file
   reads as a small photograph rather than as a big box mostly full of nothing. */
.sw-close:hover { border-color: var(--nav-accent, var(--brand-accent)); }

/* --------------------------------------------------------------- the wall */
/* Flush to the floor of the slide, and to both edges. The columns rise from
   here rather than rocking about a midpoint, so their resting position is the
   bottom of the screen and nothing needs clearance underneath to bob into. */
/* Edge to edge, open or closed. Centring left a margin either side once the
   tiles shrank for the open state; space-between pins the first tile to the
   left edge and the last to the right whatever size they are. */
.slide--hero .sw-root { min-height: 860px !important; max-height: 860px !important; height: 100% !important; flex: 1 1 auto !important; border-radius: 0 !important; }


@media (prefers-reduced-motion: reduce) {
  .sw-root::before { animation: none !important; }
  .sw-c, .sw-c__body { transition: none !important; }
  .sw-c__body { animation: none !important; }
}

/* ==========================================================================
   Programs — one card opens the whole set, a card opens its films, a film
   opens the screen.
   ========================================================================== */
.pg-root {
  position: relative; height: 100%; min-height: 860px; max-height: 860px;
  overflow: hidden; border-radius: var(--radius-lg); background: var(--surface);
}
.pg-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

.pg-wall { position: absolute; inset: 0; display: grid; place-items: center; padding: 40px 32px; }
.pg-root.is-gallery .pg-wall, .pg-root.is-playing .pg-wall { display: none; }

/* --------------------------------------------------------------- the cover */
.pg-cover {
  position: absolute; z-index: 20;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  width: 520px; padding: 44px 44px 38px;
  border: 1px solid var(--border); border-radius: 26px; background: var(--surface);
  box-shadow: 0 40px 90px -34px rgba(15, 23, 42, 0.34);
  cursor: pointer; font: inherit; text-align: left; color: inherit;
  transition: transform .55s cubic-bezier(.5,0,.75,0), opacity .4s ease;
}
/* The stack is drawn by the cover, not by the real cards standing behind it —
   a full-height card behind a short cover pokes out top and bottom. Two leaves,
   tucked and rotated, and they lean out a little further on hover. */
.pg-cover::before, .pg-cover::after {
  content: ''; position: absolute; inset: 14px 0 auto; z-index: -1;
  height: 100%; border: 1px solid var(--border); border-radius: 26px;
  background: var(--surface); box-shadow: 0 22px 50px -30px rgba(15, 23, 42, 0.3);
  transition: transform .45s cubic-bezier(.22,1,.36,1);
}
.pg-cover::before { transform: translateY(16px) rotate(-2.4deg) scale(.965); }
.pg-cover::after { transform: translateY(9px) rotate(1.7deg) scale(.985); }
.pg-cover:hover::before { transform: translateY(22px) rotate(-4deg) scale(.955); }
.pg-cover:hover::after { transform: translateY(13px) rotate(3deg) scale(.978); }
.pg-cover {
  isolation: isolate;
}
.pg-cover:hover { border-color: var(--nav-accent, var(--brand-accent)); transform: translateY(-4px); }
.pg-cover:hover .pg-cover__go { transform: translateX(4px); }
.pg-cover__go { transition: transform .3s cubic-bezier(.22,1,.36,1); }
.pg-root.is-fanned .pg-cover { transform: scale(.86) translateY(-26px); opacity: 0; pointer-events: none; }
.pg-cover__eyebrow {
  color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
.pg-cover__title {
  margin: 0; font-family: var(--display);
  font-size: 62px; font-weight: 700; line-height: 1.02; text-transform: uppercase;
  color: var(--nav-accent-ink, var(--accent-ink));
}
.pg-cover__count { color: var(--muted); font-size: 15px; }
.pg-cover__go {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 14px;
  padding: 11px 20px; border-radius: 999px;
  background: var(--nav-accent, var(--brand-accent)); color: #ffffff;
  font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
}

/* ----------------------------------------------------------------- the row */
.pg-row {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; width: 100%; height: 100%; max-height: 620px;
}
/* These caps are deliberate and were tried the other way. Raised to 700 the cards
   did reach further down the block, but a 163px-wide card 630px tall is a column
   of white with a logo floating in the middle of it — the emptiness moved inside
   the card instead of going away. Eight narrow cards cannot fill an 860px block;
   what they can do is sit as a compact row with equal air above and below, which
   is a margin rather than a dead band. */
.pg-card {
  flex: 1 1 0; min-width: 0; max-width: 190px; height: 100%; max-height: 560px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 22px 12px 18px;
  border: 1px solid var(--border); border-radius: 18px; background: var(--surface);
  box-shadow: 0 20px 46px -24px rgba(15, 23, 42, 0.26);
  cursor: pointer; font: inherit; color: inherit; text-align: center;
  will-change: transform;
}
.pg-root:not(.is-fanned) .pg-card { pointer-events: none; }
/* The cards answer the pointer: the whole card lifts, its mark grows a little,
   and the film count turns to the accent. Cheap transforms only — ten of these
   animating at once on a projector is not the place for anything else. */
.pg-card {
  transition: transform .34s cubic-bezier(.22,1,.36,1), box-shadow .34s ease, border-color .3s ease;
}
.pg-root.is-fanned .pg-card:hover {
  transform: translateY(-14px) scale(1.04) !important;
  border-color: var(--nav-accent, var(--brand-accent));
  box-shadow: 0 34px 60px -26px rgba(15, 23, 42, 0.4);
  z-index: 5;
}
.pg-card__mark img, .pg-card__mark b { transition: transform .34s cubic-bezier(.22,1,.36,1); }
.pg-root.is-fanned .pg-card:hover .pg-card__mark img,
.pg-root.is-fanned .pg-card:hover .pg-card__mark b { transform: scale(1.1); }
.pg-card:focus-visible { outline: 3px solid var(--nav-accent, var(--brand-accent)); outline-offset: 3px; }
.pg-card__mark { display: grid; place-items: center; flex: 1 1 auto; min-height: 0; width: 100%; }
.pg-card__mark img { max-width: 100%; max-height: 100%; object-fit: contain; }
.pg-card__mark b { font-size: 40px; font-weight: 800; color: var(--nav-accent-ink, var(--accent-ink)); }
.pg-card__name { flex: 0 0 auto; font-size: 14px; font-weight: 800; line-height: 1.25; }

/* ------------------------------------------------------- gallery & player */
.pg-gallery, .pg-player {
  position: absolute; inset: 0; display: none; background: #000000;
}
.pg-root.is-gallery .pg-gallery { display: grid; grid-template-rows: auto minmax(0, 1fr); }
/* The gallery's way back is not floated and not conditional. Only the *player's*
   bar had a reason to hide — it sits on the footage — and the shared rule left
   this one at zero opacity with nothing to reveal it, so the panel had a back
   button that never appeared. */
.pg-gallery .pg-bar {
  position: relative; opacity: 1;
  background: none;
  border-bottom: 1px solid rgb(255 255 255 / 0.08);
}
.pg-root.is-playing .pg-gallery { display: none; }
.pg-root.is-playing .pg-player { display: grid; }

/* Floated over the film, not a row above it. A bar in the layout took height
   off the player, and a 16:9 film in a shortened box letterboxes; given the
   whole 16:9 slide it fits to the pixel. */
.pg-bar {
  position: absolute; z-index: 5; top: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: 16px; padding: 16px 24px;
  background: linear-gradient(rgba(0,0,0,.55), transparent);
  opacity: 0; transition: opacity .25s ease;
}
/* Lit on open, and again on any movement over the film — see `lightBar`. */
.pg-player:hover .pg-bar,
.pg-player.is-lit .pg-bar,
.pg-bar:focus-within { opacity: 1; }
.pg-back {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--page-bg); color: var(--text);
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.pg-back:hover { border-color: var(--nav-accent, var(--brand-accent)); }
/* Both bars sit on black — the gallery panel and the film behind it — so these
   cannot inherit the page ink or they are a dark label on a dark ground. */
.pg-gal__title, .pg-player__title {
  font-size: 19px; font-weight: 800;
  color: #FFFFFF;
}

/* The films fill what is left of the slide. Two films are two half-screens,
   three are three — not a strip of thumbnails along the top. */
.pg-gal__grid {
  display: grid; min-height: 0;
  grid-template-columns: repeat(var(--pg-cols, 3), minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: 16px; padding: 20px 28px 24px;
}
.pg-gal__none { color: var(--muted); font-size: 14px; }

.pg-vid {
  display: block; min-height: 0; padding: 0;
  border: 0; background: none; cursor: pointer; font: inherit; color: inherit;
}
.pg-shot {
  position: relative; display: grid; place-items: center;
  width: 100%; height: 100%; overflow: hidden;
  border: 1px solid var(--border); border-radius: 16px; background: #0b0f18;
  transition: transform .25s ease, border-color .25s ease;
}
.pg-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pg-shot__play {
  position: relative; display: grid; place-items: center;
  width: 74px; height: 74px; border-radius: 50%;
  background: rgba(9, 12, 20, 0.6); color: #ffffff;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7);
}
.pg-shot__name {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 26px 18px 14px;
  background: linear-gradient(transparent, rgba(6, 9, 15, 0.86));
  color: #ffffff; font-size: 15px; font-weight: 700; text-align: left;
}
.pg-vid:hover .pg-shot { transform: translateY(-3px); border-color: var(--nav-accent, var(--brand-accent)); }

/* --- opening a card ------------------------------------------------------- */
/* The panel used to be there the instant the class landed, which read as a cut
   rather than as a card opening. It rises off the row and its tiles arrive in
   reading order behind it, so the eye is led from the card that was clicked to
   the thing it contained. `display` flips from none to grid on the same frame,
   which is what starts these without anything to trigger them in script. */
@keyframes pg-panel-in {
  from { opacity: 0; transform: scale(0.975) translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.pg-root.is-gallery .pg-gallery {
  animation: pg-panel-in .38s cubic-bezier(.2,.75,.3,1) both;
  transform-origin: 50% 55%;
}
@keyframes pg-tile-in {
  from { opacity: 0; transform: translateY(16px) scale(0.965); }
  to   { opacity: 1; transform: none; }
}
.pg-root.is-gallery .pg-gal__grid > .pg-vid {
  animation: pg-tile-in .5s cubic-bezier(.2,.75,.3,1) both;
  /* Held back past the panel's own arrival, or the tiles race the frame they
     are supposed to be arriving inside. */
  animation-delay: calc(var(--i, 0) * 55ms + 120ms);
}
/* The film or photograph fades up out of black rather than cutting to it. */
@keyframes pg-viewer-in { from { opacity: 0; } to { opacity: 1; } }
.pg-root.is-playing .pg-player,
.pg-player.is-portal { animation: pg-viewer-in .3s ease both; }

.pg-player__frame { position: absolute; inset: 0; background: #000000; }
/* Swallows clicks on the film. Clicking an embed pauses it, and a paused
   YouTube player draws its title, its channel and a grid of More videos over
   the top — which is exactly what was showing. Nothing to click, nothing to
   pause, none of that ever appears. */
.pg-player__frame::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.pg-player__yt, .pg-player__file { width: 100%; height: 100%; border: 0; display: block; }
/* A photograph is the content, so it is shown whole on the black ground rather
   than cropped to the shape of the screen. */
/* The box fills the frame, the picture is contained inside it, and script caps
   the box at twice the file's own pixels. `width: auto` was the bug: max-width
   only ever limits an intrinsically-sized image, it never stretches one, so an
   800px file sat at 800px in the middle of a 1600px display however generous the
   cap was. */
.pg-player__img {
  position: absolute; inset: 0; margin: auto;
  width: 100%; height: 100%;
  max-width: 100%; max-height: 100%;
  object-fit: contain;
}
/* Nothing to pause, so nothing to swallow — and the click-catcher would take the
   pointer events the bar needs to re-light itself. */
.pg-player.is-still .pg-player__frame::after { content: none; }
.pg-shot__play--still { width: 58px; height: 58px; }

.canvas-block--program-deck {
  min-height: 860px !important; max-height: 860px !important; height: 100% !important;
  flex: 1 1 auto !important; display: flex !important; flex-direction: column !important;
  grid-row: 1 / -1 !important;
}
.slide--hero .pg-root { min-height: 860px !important; max-height: 860px !important; height: 100% !important; flex: 1 1 auto !important; border-radius: 0 !important; }

/* No reserved band for the presenter pill. It is `position: fixed` at 0.28
   opacity until hovered, it is centred rather than full width, and every other
   section in the deck lets it float over content. Reserving 100px here put a dead
   strip under the film gallery on every programme — which is the empty bottom
   this page was reported for. The exception stays where the pill lands on small
   click targets: see `.milestones__inner`, whose rail dots it covers exactly. */

@media (prefers-reduced-motion: reduce) {
  .pg-card, .pg-shot, .pg-cover { transition: none !important; }
  .pg-root.is-gallery .pg-gallery,
  .pg-root.is-gallery .pg-gal__grid > .pg-vid,
  .pg-root.is-playing .pg-player,
  .pg-player.is-portal { animation: none !important; }
}

/* ==========================================================================
   Testimonials — a row of arched portraits; a student plays across the screen.

   The arch is the shape from the reference: a tall rounded top on a straight
   base. Each sits on its own flat wash, light enough that the face stays the
   brightest thing in the frame.
   ========================================================================== */
.tw-root {
  position: relative; height: 100%; min-height: 860px; max-height: 860px;
  overflow: hidden; border-radius: var(--radius-lg); background: var(--surface);
}
.tw-root--empty { display: grid; place-items: center; color: var(--muted); font-size: 13px; }

.tw-wall {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  padding: 44px 40px 40px;
}
.tw-root.is-playing .tw-wall { display: none; }

.tw-head { flex: 0 0 auto; }
.tw-eyebrow {
  margin: 0 0 10px; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
.tw-title {
  margin: 0; font-family: var(--display);
  font-size: 58px; font-weight: 700; line-height: 1.02; text-transform: uppercase;
  color: var(--nav-accent-ink, var(--accent-ink));
}

/* The row fills what is left and every arch shares it equally. */
/* Three across, two down, and every card shows the whole frame.

   The arch was the wrong container for this material. These are wide studio
   frames and each student stands somewhere different in theirs, so filling a
   tall shape meant cropping to a guessed window — which landed on empty wall
   for half of them, twice. A card at the frame's own proportions needs no
   guess: the picture is shown as it was shot. */
.tw-row {
  flex: 1 1 auto; min-height: 0;
  box-sizing: border-box; width: 100%;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: 18px 20px; padding: 26px 0 0;
}
.tw-card {
  position: relative; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; gap: 10px;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: center;
  transition: transform .3s cubic-bezier(.22,1,.36,1);
  animation: twRise .6s cubic-bezier(.22,1,.36,1) both;
}
.tw-arch {
  position: relative; flex: 1 1 auto; min-height: 0; width: 100%; overflow: hidden;
  border-radius: 18px;
  transition: box-shadow .3s ease, transform .3s cubic-bezier(.22,1,.36,1);
}
.tw-arch img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* contain, not cover: the whole frame, never a slice of it. */
  object-fit: contain; object-position: center;
}
.tw-card:hover { transform: translateY(-6px); }
.tw-card:hover .tw-arch { box-shadow: 0 26px 50px -24px rgba(15, 23, 42, 0.4); }
.tw-card:focus-visible .tw-arch { outline: 3px solid var(--nav-accent, var(--brand-accent)); outline-offset: 3px; }
.tw-card__name { flex: 0 0 auto; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; }
.tw-card__note { flex: 0 0 auto; color: var(--muted); font-size: 12.5px; }

.tw-card__play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(.86);
  display: grid; place-items: center; width: 66px; height: 66px; border-radius: 50%;
  background: rgba(9, 12, 20, 0.52); color: #ffffff;
  opacity: 0; transition: opacity .28s ease, transform .28s cubic-bezier(.22,1,.36,1);
  pointer-events: none;
}
.tw-card { position: relative; }
.tw-card:hover .tw-card__play, .tw-card:focus-visible .tw-card__play {
  opacity: 1; transform: translate(-50%, -50%) scale(1);
}
.tw-card.is-active .tw-arch { outline: 3px solid var(--nav-accent, var(--brand-accent)); outline-offset: 3px; }

/* ------------------------------------------------------------- the player */
.tw-player {
  position: absolute; inset: 0; display: none; background: #000000;
}
.tw-root.is-playing .tw-player { display: grid; animation: twFade .35s ease both; }
@keyframes twFade { from { opacity: 0; } to { opacity: 1; } }

/* Floated over the film, not a row above it. A bar in the layout took height
   off the player, and a 16:9 film in a shortened box letterboxes; given the
   whole 16:9 slide it fits to the pixel. */
.tw-bar {
  position: absolute; z-index: 5; top: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: 16px; padding: 16px 24px;
  background: linear-gradient(rgba(0,0,0,.55), transparent);
  opacity: 0; transition: opacity .25s ease;
}
.tw-player:hover .tw-bar, .tw-bar:focus-within { opacity: 1; }
.tw-back {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--page-bg); color: var(--text);
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.tw-back:hover { border-color: var(--nav-accent, var(--brand-accent)); }
.tw-player__name { font-size: 19px; font-weight: 800; }
.tw-player__frame { position: absolute; inset: 0; background: #000000; }
/* Swallows clicks on the film. Clicking an embed pauses it, and a paused
   YouTube player draws its title, its channel and a grid of More videos over
   the top — which is exactly what was showing. Nothing to click, nothing to
   pause, none of that ever appears. */
.tw-player__frame::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.tw-player__yt, .tw-player__file { width: 100%; height: 100%; border: 0; display: block; }

.canvas-block--testimonial-wall {
  min-height: 860px !important; max-height: 860px !important; height: 100% !important;
  flex: 1 1 auto !important; display: flex !important; flex-direction: column !important;
  grid-row: 1 / -1 !important;
}
.slide--hero .tw-root { min-height: 860px !important; max-height: 860px !important; height: 100% !important; flex: 1 1 auto !important; border-radius: 0 !important; }

body.is-presenting .tw-wall { padding-bottom: 104px; }

@media (prefers-reduced-motion: reduce) {
  .tw-card, .tw-arch, .tw-card__play { animation: none !important; transition: none !important; }
  .tw-card { opacity: 1 !important; transform: none !important; }
}

/* --- Films fill the frame, the way the Executive Summary hero does ----------
   A 16:9 embed showing a 2.39:1 master letterboxes it — the bars are YouTube's,
   inside the player, so sizing the iframe to the box cannot remove them. The
   hero solves it by over-sizing the iframe and centring it, so the film's own
   picture covers the frame and the bars fall outside it. Same numbers here.

   1.34 is 2.39 divided by 1.78: exactly the zoom that takes a 2.39:1 picture
   letterboxed in a 16:9 player and makes its height fill the frame. */
/* The iframe must BE 16:9 and be sized to cover, not stretched to the window.
   Stretch it to a window that is not 16:9 and YouTube letterboxes the film
   inside the box you gave it — then zooming to hide those bars crops the
   picture, which is exactly what went wrong: bars still showing and the speaker
   cut off at the edge.

   max() gives the smallest 16:9 box that still covers the viewport, so the film
   fills it with no letterbox and only the necessary overflow is cropped. The
   1.04 is for the hairline the encode leaves at the very top and bottom. */
.pg-player__yt, .tw-player__yt {
  position: absolute !important;
  top: 50% !important; left: 50% !important;
  width: max(100%, calc(100% * 16 / 9)) !important;
  height: max(100%, calc(100% * 9 / 16)) !important;
  transform: translate(-50%, -50%) scale(1.04) !important;
  border: 0 !important;
}
/* Portalled, the box is the window — and the sum has to reckon with the film,
   not just the player.

   These masters are 2.39:1 sitting inside a 16:9 upload, so YouTube letterboxes
   them within the player. Covering the window with the *player* still leaves
   those bars: the picture only occupies 1.78/2.39 = 0.744 of the player's
   height. To make the picture itself reach the top and bottom the player has to
   stand 1/0.744 = 1.344 times the viewport height, and 16:9 gives the width.

   100vh * 2.39 is that height times 16/9, written out. */
.tw-player.is-portal .tw-player__yt,
.pg-player.is-portal .pg-player__yt {
  width: max(100vw, calc(100vh * 2.39)) !important;
  height: max(calc(100vh * 1.344), calc(100vw * 0.5625)) !important;
  transform: translate(-50%, -50%) scale(1.02) !important;
}
.tw-player.is-portal, .pg-player.is-portal { overflow: hidden; }
/* Files are ours and are not letterboxed, so they cover honestly. */
.pg-player__file, .tw-player__file { width: 100%; height: 100%; object-fit: cover; }

/* --- A playing film owns the whole display --------------------------------
   The block's canvas is 860 tall inside a 900 slide, so a player pinned to the
   section stopped 40px short and left a white band with the deck controls
   floating in it. While presenting the player leaves the section's box and
   pins to the viewport instead: nothing is between it and the edges.

   z-index 30 sits under the deck bar's 40, so Prev / Next / Exit stay on top
   and clickable rather than being buried by the film. */
/* position:fixed does not help: FitSlide scales the slide with a transform, and
   a transformed ancestor becomes the containing block for fixed descendants —
   fixed and absolute resolve to the same 860px box. The gap is the box itself,
   so while presenting the section takes the slide's full 900 instead. */
body.is-presenting .canvas-block--testimonial-wall,
body.is-presenting .canvas-block--program-deck,
body.is-presenting .tw-root,
body.is-presenting .pg-root {
  min-height: 900px !important;
  max-height: 900px !important;
}
body.is-presenting .tw-player,
body.is-presenting .pg-player { z-index: 30; }

/* --- A playing film covers the display, not just the slide -----------------
   The player is moved to <body> while it plays, so it is outside FitSlide's
   transform and answers to the viewport. `display` has to be set here: the
   `.is-playing .tw-player` rule stops matching the moment the element leaves
   the section.

   z-index 35 keeps it under the deck bar's 40, so Prev / Next / Exit stay on
   top of the film and clickable — the film may cover them, the controls still
   win the click. */
.tw-player.is-portal,
.pg-player.is-portal {
  display: block !important;
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  z-index: 35;
  background: #000000;
}

/* ==========================================================================
   Placements — justified-row galleries that never crop or stretch a source
   ========================================================================== */

.pw-root {
  display: flex;
  flex-direction: column;
  /* A definite height, not a minimum.
     `.canvas-block > *` sets `flex: 1`, and its `flex-basis: 0%` beats `height`
     on the main axis — so a root with only a `min-height` grows to its content
     instead of letting the stage inside it scroll, and FitSlide then scales the
     whole slide down to fit. With 43 photographs on Campus Events this root
     measured 3068px and the slide was drawn at 0.294 — a postage stamp in the
     middle of the screen. It only looked right while presenting, because the
     presenting rule below pins the height with `--slide-h`.
     `flex: 0 0 auto` is what gives it a basis of its own. Same arrangement as
     `.ev-root`, and for the same reason.
     A percentage cannot stand in: the root's parent is `auto`, so a percentage
     resolves to auto and the child's scroller never gets a box to scroll in. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.pw-empty { display: grid; place-items: center; min-height: 400px; gap: 10px; text-align: center; }
.pw-hint { margin: 0; color: var(--muted); font-size: 13px; }

/* --------------------------------------------------------------- the head -- */
/* One centred column: title, then the chapter pill, then the company chips. The
   left-aligned version stacked three things in a corner and left the gallery
   with no centre line to sit on. */
.pw-head { flex: 0 0 auto; padding: 14px 20px 8px; text-align: center; }
.pw-head__top { margin-bottom: 16px; }
.pw-eyebrow {
  margin: 0 0 8px; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 11px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
.pw-title { margin: 0; font-size: 36px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.05; }
.pw-rule {
  display: block; height: 3px; width: 52px; margin: 12px auto 0; border-radius: 2px;
  background: var(--nav-accent-ink, var(--accent-ink));
  animation: pw-rule 620ms cubic-bezier(.2,.7,.3,1) both;
}
@keyframes pw-rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.pw-lead {
  margin: 12px auto 0; max-width: 640px; color: var(--muted);
  font-size: 14px; line-height: 1.55;
}

/* ------------------------------------------------- the chapter pill (top) -- */
/* One floating pill holding every chapter, the active one filled. Modelled on
   the bar in the reference: a single rounded surface rather than four separate
   buttons, so it reads as one control with a current position. */
.pw-navwrap { display: flex; justify-content: center; }
.pw-rail {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 10px 30px -18px rgb(15 23 42 / 0.45), 0 1px 0 rgb(255 255 255 / 0.6) inset;
}
.pw-tab {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 22px;
  border: 0; border-radius: 999px;
  background: transparent;
  font: inherit; color: var(--muted); cursor: pointer;
  transition: color 180ms ease, background 220ms ease;
  animation: pw-tab-in 480ms cubic-bezier(.2,.7,.3,1) both;
}
@keyframes pw-tab-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.pw-tab .ic { flex: 0 0 auto; }
.pw-tab__name {
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 14.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.pw-tab:hover { color: var(--ink, #111827); background: var(--page-bg); }
.pw-tab.is-on {
  color: #fff;
  background: var(--nav-accent, var(--brand-primary, #111827));
  box-shadow: 0 8px 20px -10px rgb(15 23 42 / 0.55);
}
.pw-tab:focus-visible { outline: 2px solid var(--nav-accent-ink, var(--accent-ink)); outline-offset: 2px; }

/* ------------------------------------------------------ the company chips -- */
.pw-chips { display: flex; justify-content: center; gap: 7px; flex-wrap: wrap; margin-top: 14px; }
.pw-chips[hidden] { display: none; }
.pw-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); font: inherit; font-size: 12px; font-weight: 650;
  color: var(--muted); cursor: pointer;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}
.pw-chip em { font-style: normal; font-size: 10px; opacity: 0.7; }
.pw-chip:hover { color: var(--ink, #111827); border-color: #d1d5db; }
.pw-chip.is-on {
  color: #fff; border-color: var(--nav-accent-ink, var(--accent-ink));
  background: var(--nav-accent-ink, var(--accent-ink));
}

/* ------------------------------------------------------------- the stage -- */
/* Rows are centred when they fit and top-aligned when they scroll — a centred
   scroll container hides its own first row above the fold. */
.pw-stage {
  flex: 1 1 auto;
  min-height: 0;
  max-height: 100%;
  padding: 18px 20px 22px;
  overflow-y: auto !important;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(0, 0, 0, 0.3) transparent;
  /* Deliberately NOT `scroll-behavior: smooth`.
     The wheel handler in PlacementWall scrolls this element by hand
     (`stage.scrollTop += e.deltaY`), and with smooth scrolling on, reading
     `scrollTop` back returns the *current animated position* rather than the
     target — so every tick of a trackpad flick added its delta to a value that
     was still catching up, and each new assignment restarted the animation.
     Measured: a flick asking for 800px moved 138px of the 350 available, which
     is what "I can't scroll" looked like from the outside. Instant assignment
     accumulates exactly. */
  overscroll-behavior: contain;
  pointer-events: auto;
  touch-action: pan-y;
}
.pw-stage:not(.is-short) {
  display: block !important;
}
.pw-stage.is-short {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.pw-stage.is-short .pw-row:last-child { margin-bottom: 0; }
.pw-stage::-webkit-scrollbar { width: 6px; }
.pw-stage::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.25); border-radius: 4px; }

/* The solver hands every tile an exact width derived from that image's own
   aspect ratio, and the row's gaps are set inline — so the row only has to lay
   them out left to right. `space-between` was harmless while the tiles could
   stretch; it is not now. */
.pw-row { display: flex; align-items: stretch; justify-content: flex-start; margin-bottom: 12px; width: 100%; }
/* A tail row does not reach the margin. Centred it reads as composition; pushed
   apart by `space-between` it read as two images that had fallen to the edges. */
.pw-row--short { justify-content: center; }

/* ==========================================================================
   Never stretch a placement card
   ==========================================================================
   `flex: 1 1 auto` let a tile grow past the width the row solver gave it, to
   fill whatever space was left over. On a full row that is invisible; on the
   leftover tail it is not — one wide card alone on a row was being pulled from
   1048px to the full 1560 while its height stayed put, and `object-fit: cover`
   then cropped 40% of the picture away to cover the box. Measured at 229% off
   its true aspect ratio on the company-wise wall.

   These are designed cards with a student's name, their branch and their
   package set into them. A crop takes a name off. The solved width is final. */
/* The cards arrive with depth rather than just fading up: each one starts tipped
   back and a little below its place, then stands up into the row. The perspective
   has to live on the row, not the card — on the card itself every tile gets its
   own vanishing point and the ones at the edges of a wide row visibly skew. */
.pw-row { perspective: 1400px; perspective-origin: 50% 40%; }

.pw-tile {
  position: relative; flex: 0 0 auto; padding: 0;
  border: 0; border-radius: var(--radius-sm); overflow: hidden;
  background: #f3f4f6; cursor: zoom-in;
  opacity: 0; transform: translateY(26px) scale(0.94) rotateX(12deg);
  transform-origin: 50% 100%;
  transition: opacity 620ms cubic-bezier(.2,.7,.3,1),
              transform 620ms cubic-bezier(.2,.7,.3,1),
              box-shadow 200ms ease;
}
.pw-tile.is-in { opacity: 1; transform: none; }

/* A sheen that crosses the card as it lands, and again when it is pointed at.
   It is a gradient on a pseudo-element rather than a filter, so it costs one
   composited layer and not a repaint of the picture — which matters at fifty
   cards on a wall that is already animating all of them. */
.pw-tile::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%, rgb(255 255 255 / 0.42) 50%, transparent 62%);
  transform: translateX(-120%);
  pointer-events: none;
  /* Under the company tag, which has to stay legible over it. */
  z-index: 1;
}
.pw-tile.is-in::after { animation: pw-sheen 900ms cubic-bezier(.2,.7,.3,1) 160ms 1; }
.pw-tile:hover::after { animation: pw-sheen 720ms cubic-bezier(.2,.7,.3,1) 1; }
@keyframes pw-sheen {
  from { transform: translateX(-120%); }
  to   { transform: translateX(120%); }
}

.pw-tile:hover {
  box-shadow: 0 18px 38px rgb(0 0 0 / 0.22); z-index: 2;
  transform: translateY(-4px) scale(1.012);
}
.pw-tile img {
  display: block; width: 100%; height: 100%;
  /* The box was solved from this image's own width and height, so cover has
     nothing to crop beyond a rounding hairline — and unlike `contain` it cannot
     leave a letterbox line where the rounding fell the other way. */
  object-fit: cover;
  transition: transform 420ms cubic-bezier(.2,.7,.3,1);
}
.pw-tile:hover img { transform: scale(1.035); }
.pw-tile__tag {
  position: absolute; inset: auto 0 0 0; padding: 16px 10px 8px;
  background: linear-gradient(to top, rgb(0 0 0 / 0.72), transparent);
  color: #fff; font-size: 11.5px; font-weight: 700; letter-spacing: 0.01em;
  text-align: left; opacity: 0; transition: opacity 200ms ease;
  /* Above the sheen, or the company name flashes white as it passes. */
  z-index: 2;
}
.pw-tile:hover .pw-tile__tag, .pw-tile:focus-visible .pw-tile__tag { opacity: 1; }

/* ---------------------------------------------------------- the lightbox -- */
/* On the body, never inside the slide: FitSlide's transform would otherwise be
   the containing block and `inset: 0` would mean the slide, not the screen. */
.pw-light {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 3vh 3vw;
  background: rgb(9 11 14 / 0.9);
  opacity: 0; transition: opacity 220ms ease;
}
.pw-light.is-on { opacity: 1; }
.pw-light[hidden] { display: none; }
.pw-light__frame {
  margin: 0; display: flex; flex-direction: column; gap: 12px;
  max-width: 100%; max-height: 100%;
  transform: scale(0.97); transition: transform 260ms cubic-bezier(.2,.7,.3,1);
}
.pw-light.is-on .pw-light__frame { transform: none; }
.pw-light__img {
  /* Bounded by the viewport and nothing else, so the file is shown at its own
     resolution wherever it fits. */
  display: block; max-width: 100%; max-height: calc(100vh - 12vh);
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--radius); background: #111;
}
.pw-light__foot { display: flex; align-items: baseline; gap: 12px; color: #e5e7eb; }
.pw-light__cap { margin: 0; font-size: 15px; font-weight: 700; }
.pw-light__meta { color: #9ca3af; font-size: 12px; font-variant-numeric: tabular-nums; }
.pw-light__close {
  position: absolute; top: 18px; right: 20px;
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 0; border-radius: 999px; background: rgb(255 255 255 / 0.12);
  color: #fff; cursor: pointer; transition: background 160ms ease;
}
.pw-light__close:hover { background: rgb(255 255 255 / 0.22); }

@media (prefers-reduced-motion: reduce) {
  .pw-rule, .pw-tab { animation: none; }
  .pw-tile { opacity: 1; transform: none; transition: none; }
  .pw-tile:hover { transform: none; }
  .pw-tile:hover img { transform: none; }
  /* The sheen is decoration only — nothing is communicated by it, so it goes
     entirely rather than being slowed down. */
  .pw-tile::after { display: none; }
  .pw-light, .pw-light__frame { transition: none; }
}

/* --- lightbox: step between images without closing it --------------------- */
.pw-light__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 56px; height: 56px;
  border: 0; border-radius: 999px;
  background: rgb(255 255 255 / 0.12); color: #fff; cursor: pointer;
  transition: background 160ms ease, transform 160ms ease;
}
.pw-light__nav:hover { background: rgb(255 255 255 / 0.24); }
.pw-light__nav:active { transform: translateY(-50%) scale(0.94); }
.pw-light__nav[hidden] { display: none; }
.pw-light__nav .ic { width: 28px; height: 28px; }
.pw-light__nav--prev { left: 22px; }
.pw-light__nav--next { right: 22px; }
/* The arrows sit over the image, so the frame keeps clear of them. */
.pw-light .pw-light__frame { max-width: calc(100% - 160px); }
.pw-light__count {
  margin-left: auto; color: #9ca3af; font-size: 12px;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.pw-light__foot { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .pw-light__nav { transition: none; }
}

/* ==========================================================================
   The presenter bar — named neighbours, and one dot per slide to jump with
   ========================================================================== */

/* The dock holds two controls, so it is sized by them rather than widened.
   It used to carry a rail of every section between them — sixteen icons, only
   the one in hand named — and that went on request (2026-09-17) along with the
   hover cards above it. What is left is the pair of named tabs, the position,
   and the way out. `position` is deliberately NOT re-declared: the base rule is
   `fixed`, and restating it as `relative` here once dropped the bar 39px below
   the fold and grew the page a scrollbar. */
.deck-bar--pair {
  gap: 10px;
  padding: 9px 11px;
  /* Dead centre, which the wide bar could not be. The base rule shifts the bar
     left by half `--player-reserve` so a 1180px bar and the film player's
     controls in the bottom-right corner sat side by side rather than on each
     other. This bar is about 480px, so that shift is 123px of visible offset
     bought to avoid a collision that can no longer happen — it reads as a dock
     someone nudged. Centred instead, with the width capped so that half the bar
     plus the whole reserve still clears the corner at any window size: at
     1600px the bar's right edge is 1039 against a player starting at 1370. */
  left: 50%;
  max-width: min(980px, calc(100vw - (2 * var(--player-reserve)) - 48px));
}

/* Each button names where it lands, so it is a label with a chevron rather than
   a chevron with a label: the name is 15.5px, up from the 12.5px it was when a
   rail of sixteen dots had to fit beside it. That is the size a presenter reads
   from the desk without leaning in, which is the whole reason the dock exists. */
.deck-step {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 16px; border: 0; border-radius: 999px;
  background: rgb(255 255 255 / 0.10); color: #fff;
  font: inherit; cursor: pointer;
  transition: background 160ms ease, transform 160ms ease;
}
.deck-step:hover { background: rgb(255 255 255 / 0.22); }
.deck-step:active { transform: scale(0.97); }
.deck-step:focus-visible { outline: 2px solid rgb(255 255 255 / 0.7); outline-offset: 2px; }
.deck-step .ic { flex: 0 0 auto; opacity: 0.75; }
.deck-step__text {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 0; max-width: 260px;
}
.deck-step__text em {
  font-style: normal; font-size: 10px; font-weight: 800;
  letter-spacing: 0.13em; text-transform: uppercase; opacity: 0.55;
}
.deck-step__text span {
  font-size: 15.5px; font-weight: 650; line-height: 1.15;
  max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The next tab's name sits against its own chevron, so the two buttons mirror. */
.deck-step--next .deck-step__text { align-items: flex-end; }

@media (max-width: 900px) {
  /* The names are the first thing to go; the chevrons and the count still say
     which way is forward and where the presenter is. */
  .deck-step__text { display: none; }
  .deck-step { padding: 9px 11px; }
}

@media (prefers-reduced-motion: reduce) {
  .deck-step { transition: none; }
}
}

/* --- the digits that moved, in the logo's two colours -------------------- */
/* Only the changed cells are tinted, so the colour reads as "this is what
   rolled" rather than as decoration. One digit changing takes the yellow; when
   two change the upper one takes the green and the lower keeps the yellow.
   The odometer sits on the night sky, and CLAUDE.md allows the raw accent on a
   dark ground — it is the white surface where it would fail.

   Targeted at the glyph, not the cell: `.milestones--dark .milestones__glyph`
   sets an explicit white, so a colour on the cell is inherited into a child that
   immediately overrides it and the digits stay white. */
.milestones__cell.is-rolled-minor .milestones__glyph { color: var(--brand-accent); }
.milestones__cell.is-rolled-major .milestones__glyph { color: var(--brand-primary); }

/* Torii's primary is black, invisible against the sky, so on the dark variant
   the green is lifted toward white and both get a faint bloom — which also
   keeps them distinct from the plain digits at projector distance. */
.milestones--dark .milestones__cell.is-rolled-minor .milestones__glyph {
  color: var(--brand-accent);
  text-shadow: 0 0 26px color-mix(in srgb, var(--brand-accent) 55%, transparent);
}
.milestones--dark .milestones__cell.is-rolled-major .milestones__glyph {
  color: color-mix(in srgb, var(--brand-primary) 62%, #ffffff);
  text-shadow: 0 0 22px color-mix(in srgb, var(--brand-primary) 45%, transparent);
}

/* The tint arrives with the roll rather than snapping on ahead of it. */
.milestones__glyph { transition: color 380ms ease; }
@media (prefers-reduced-motion: reduce) {
  .milestones__glyph { transition: none; }
}

/* --- the last stop is a word: its final letter wears the logo ------------- */
/* NEXT has no digit to tint, so the T carries both brand colours at once. A
   hard two-stop split, not a blend: CLAUDE.md rules out gradients as surface
   decoration, and this is a two-colour mark rather than a wash.

   background-clip: text needs transparent glyph colour, which also kills
   text-shadow — the bloom the tinted digits get comes from a drop-shadow filter
   here instead. Guarded, because a browser without background-clip would paint
   the letter as nothing at all. */
.milestones__cell.is-logo-mark .milestones__glyph { color: var(--brand-accent); }

@supports (background-clip: text) or (-webkit-background-clip: text) {
  .milestones__cell.is-logo-mark .milestones__glyph {
    background-image: linear-gradient(
      160deg,
      var(--brand-primary) 0%, var(--brand-primary) 48%,
      var(--brand-accent) 52%, var(--brand-accent) 100%
    );
    /* Covers the glyph box exactly and does not travel — an oversized repeating
       gradient is what once animated past its box and left letters unpainted. */
    background-size: 100% 100%;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
  /* Torii's primary is black, which reads as a hole on the night sky, so the
     dark variant lifts the darker half toward white. */
  .milestones--dark .milestones__cell.is-logo-mark .milestones__glyph {
    background-image: linear-gradient(
      160deg,
      color-mix(in srgb, var(--brand-primary) 62%, #ffffff) 0%,
      color-mix(in srgb, var(--brand-primary) 62%, #ffffff) 48%,
      var(--brand-accent) 52%, var(--brand-accent) 100%
    );
    filter: drop-shadow(0 0 18px color-mix(in srgb, var(--brand-accent) 40%, transparent));
  }
}

/* ==========================================================================
   Platforms — one horizontal pill of the products' own marks
   ========================================================================== */

/* Horizontal, not the vertical tray it was. Modelled on the bar in the
   reference: a single rounded surface, the current item a filled pill carrying
   its name, the rest plain marks. A column of seven small tiles down the left
   read as a toolbar; one bar under the title reads as navigation. */
/* Its own row across the foot of the slide, centred. */
/* In the flow, not floating. Absolute at the foot it sat over the front
   window's own footer, covering the product name and its Open link. */
.pf-railrow {
  grid-area: rail;
  display: flex; justify-content: center; align-items: flex-end;
  padding: 4px 0 2px;
}

/* stylelint-disable-next-line no-descending-specificity */
.pf-chips {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: calc(100% - 60px);
  overflow-x: auto;
  scrollbar-width: none;
  padding: 7px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 12px 34px -20px rgb(15 23 42 / 0.5);
}

.pf-row {
  position: relative;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 6px 8px;
  border: 0; border-radius: 999px;
  background: transparent;
  font: inherit; color: var(--muted); cursor: pointer;
  transition: background 220ms ease, color 180ms ease;
  /* The dock leans the item toward the pointer; the mark inside it grows. */
  transform: translateY(calc(-4px * var(--dock-k, 0)));
}

/* --- the mark ------------------------------------------------------------- */
.pf-row__tile {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 40px; height: 40px;
  border-radius: 12px;
  overflow: hidden;
  /* One ground for every tile. The marks that only existed white-on-dark were
     luma-inverted at crop time — luma only, so the orange in t@g survives where
     a plain negate would have flipped it — and their grounds clipped to white. */
  background: #ffffff;
  border: 1px solid var(--border);
  /* This is the dock: the mark scales, the bar does not move. */
  transform: scale(calc(1 + 0.3 * var(--dock-k, 0)));
  transition: transform 90ms linear, box-shadow .18s ease, border-color .18s ease;
}
.pf-row__tile img {
  display: block; width: 100%; height: 100%;
  /* contain, never cover: these are wordmarks at three different aspect ratios
     and cover would slice the ends off the wide ones. */
  object-fit: contain;
  padding: 5px;
  box-sizing: border-box;
}
.pf-row__tile .ic { color: var(--nav-accent-ink, var(--accent-ink)); }
.pf-row.is-near .pf-row__tile,
.pf-row:hover .pf-row__tile {
  border-color: var(--nav-accent, var(--brand-accent));
  box-shadow: 0 10px 22px -10px rgb(15 23 42 / 0.4);
}

/* --- the name, on the current one and whatever is hovered ---------------- */
.pf-row__tip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0;
  max-width: 0; overflow: hidden; white-space: nowrap;
  opacity: 0;
  transition: max-width .3s cubic-bezier(.2,.7,.3,1), opacity .2s ease, padding .3s ease;
}
.pf-row.is-on .pf-row__tip,
.pf-row:hover .pf-row__tip,
.pf-row:focus-visible .pf-row__tip {
  max-width: 220px; opacity: 1; padding-right: 8px;
}
.pf-row__tip b { font-size: 13.5px; font-weight: 700; letter-spacing: -0.005em; line-height: 1.2; }
.pf-row__tip i {
  font-style: normal; font-size: 9px; font-weight: 800;
  letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--nav-accent-ink, var(--accent-ink));
}
.pf-row__tip em { display: none; }

/* The current view is a filled pill, as in the reference. */
.pf-row.is-on {
  background: var(--nav-accent, var(--brand-primary, #111827));
  color: #fff;
}
.pf-row.is-on .pf-row__tip i { color: rgb(255 255 255 / 0.72); }
.pf-row:hover:not(.is-on) { background: var(--page-bg); color: var(--ink, #111827); }
.pf-row:focus-visible {
  outline: 2px solid var(--nav-accent, var(--brand-accent));
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .pf-row, .pf-row__tile, .pf-row__tip { transition: none; transform: none; }
}

/* ==========================================================================
   Platforms — title above, the stack in the middle, the bar below
   ========================================================================== */

/* One column, three bands. The two-column version put a left-hand text rail
   beside the stack, which left the title stranded in a corner and gave the bar
   nowhere to go but on top of the windows. */
.pf-wall {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: 'intro' 'stage' 'rail';
  gap: 0;
  align-items: stretch;
}
.pf-intro {
  grid-area: intro;
  max-width: none;
  padding: 26px 40px 0;
  text-align: center;
}
.pf-title {
  margin: 0 auto;
  max-width: 20ch;
  font-size: 40px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.06;
}
.pf-eyebrow { margin: 0 0 8px; }
.pf-sub { margin: 12px auto 0; max-width: 60ch; }
.pf-stagearea {
  grid-area: stage;
  min-height: 0;
  display: grid; place-items: center;
}

/* --- the marks: no border, and bigger ------------------------------------ */
/* The border drew a box round every mark and the set read as a grid of frames.
   Without it the marks sit on the bar's own surface, which is what the
   reference does, and the extra room goes into the mark itself. */
.pf-chips {
  gap: 6px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  box-shadow: 0 14px 38px -22px rgb(15 23 42 / 0.55);
}
.pf-row { padding: 5px 7px; gap: 10px; }
.pf-row__tile {
  width: 52px; height: 52px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  box-shadow: none;
}
.pf-row__tile img { padding: 2px; }
.pf-row__tile .ic { width: 26px; height: 26px; }
.pf-row.is-near .pf-row__tile,
.pf-row:hover .pf-row__tile { border: 0; box-shadow: none; }
/* The current pill is the only frame on the bar, which is what makes it read as
   the current one. */
.pf-row.is-on .pf-row__tile { background: rgb(255 255 255 / 0.16); }
.pf-row__tip b { font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.pf-row__tip i { font-size: 9.5px; letter-spacing: 0.14em; }
.pf-row.is-on .pf-row__tip { max-width: 240px; }

/* The stack's back cards are offset upward by slotFor, so they rise out of the
   stage band and painted straight over the title — which measured 42px tall, in
   the right place and the right colour, and was simply behind them. The title and
   the bar both sit above the stack now. */
.pf-intro { position: relative; z-index: 4; }
.pf-railrow { position: relative; z-index: 4; }
/* And the stack is given room to lean up into without reaching the title. */
.pf-stagearea { padding-top: 26px; }

/* ==========================================================================
   Platforms — the navigation back on the side, and the title clear of the stack
   ========================================================================== */

/* Two columns. Stacking the title over the stack could never work: slotFor
   offsets the back cards upward, so the fan always reaches into whatever band
   sits above it, and lifting the title's z-index only made it legible *on top of*
   the windows rather than clear of them. Side by side, the title is in a
   different column and nothing has to overlap at all. */
.pf-wall {
  grid-template-columns: minmax(280px, 30%) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    'intro stage'
    'rail  stage';
  column-gap: 24px;
  align-items: start;
}
.pf-intro {
  grid-area: intro;
  padding: 46px 0 0 44px;
  text-align: left;
  z-index: 4;
}
.pf-title {
  margin: 0;
  max-width: 14ch;
  font-size: 44px; font-weight: 800; letter-spacing: -0.035em; line-height: 1.02;
}
.pf-sub { margin: 14px 0 0; max-width: 34ch; text-align: left; }

/* The rail under the title, down the side, and vertical again — one column of
   marks reads as a list of products beside the thing they open. */
.pf-railrow {
  grid-area: rail;
  display: flex; justify-content: flex-start; align-items: flex-start;
  padding: 22px 0 0 44px;
  z-index: 4;
}
.pf-chips {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  max-width: none;
  overflow: visible;
  padding: 10px;
  border-radius: 24px;
  background: #f6f7f9;
  border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  box-shadow: none;
}
.pf-row { justify-content: flex-start; border-radius: 16px; }
/* Vertical again, so the name grows sideways out of the column rather than
   widening the bar it sits in. */
.pf-row__tip { max-width: 0; }
.pf-row.is-on .pf-row__tip,
.pf-row:hover .pf-row__tip,
.pf-row:focus-visible .pf-row__tip { max-width: 200px; }

.pf-stagearea {
  grid-area: stage;
  align-self: stretch;
  padding-top: 0;
  place-items: center;
}

/* ==========================================================================
   Platforms — a rail of named rows, the open one a card in its own colour
   ========================================================================== */

/* Oswald first. It was already third in the stack behind Agency FB; naming it
   first uses it wherever it is installed, and the condensed fallbacks still
   catch a machine that has neither. */
.pf-title {
  font-family: var(--display);
  font-size: 46px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.04;
  text-transform: none;
}

/* --- the rail ------------------------------------------------------------- */
.pf-chips {
  width: 100%;
  padding: 8px;
  gap: 8px;
  background: transparent;
  border: 0;
}
/* A row is its name. The mark only appears on the open one, so the rail reads as
   a list until something is chosen. */
.pf-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  border-radius: 16px;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
  color: var(--ink, #111827);
  text-align: left;
  transform: none;
  transition: background .28s ease, border-color .2s ease, color .2s ease,
              box-shadow .28s ease, padding .28s ease;
}
.pf-row__tip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  max-width: none; overflow: hidden; opacity: 1; padding: 0;
  min-width: 0;
}
.pf-row__tip b {
  font-size: 14.5px; font-weight: 700; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.pf-row__tip i { color: var(--muted); }
.pf-row__tip em {
  display: block; font-style: normal;
  font-size: 11px; font-weight: 600; color: var(--muted);
}

/* The mark: hidden until this row is the open one, then it grows in beside the
   name — a snapshot of the product on its own coloured card. */
.pf-row__tile {
  width: 0; height: 44px;
  border: 0; border-radius: 12px;
  background: rgb(255 255 255 / 0.92);
  opacity: 0;
  transform: none;
  transition: width .3s cubic-bezier(.2,.7,.3,1), opacity .24s ease;
}
.pf-row.is-on .pf-row__tile { width: 62px; opacity: 1; }
.pf-row__tile img { padding: 5px; }

/* The open row, in the product's own colour. */
.pf-row.is-on {
  padding: 16px 14px;
  background: var(--tint, var(--brand-primary, #111827));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 16px 34px -18px color-mix(in srgb, var(--tint, #111827) 70%, transparent);
}
.pf-row.is-on .pf-row__tip b { font-size: 16px; white-space: normal; }
.pf-row.is-on .pf-row__tip i,
.pf-row.is-on .pf-row__tip em { color: rgb(255 255 255 / 0.8); }
.pf-row:hover:not(.is-on) {
  background: var(--page-bg);
  border-color: color-mix(in srgb, var(--tint, var(--border)) 45%, transparent);
}

/* --- the stack: off the right edge --------------------------------------- */
/* The fan was running past the slide and getting clipped. A little breathing
   room on the right, and the whole thing pulled left to use it. */
.pf-wall { grid-template-columns: minmax(300px, 28%) minmax(0, 1fr); column-gap: 18px; }
.pf-stagearea { padding-right: 46px; transform: translateX(-26px); }

@media (prefers-reduced-motion: reduce) {
  .pf-row, .pf-row__tile { transition: none; }
}


/* ==========================================================================
   The landing page — white ground, Technical Hub's palette, partner orbits
   ========================================================================== */

.lp {
  --lp-green: #008638;
  --lp-gold: #FFBB00;
  --lp-ink: #0B1220;

  position: relative;
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 5vh 24px 0;
  overflow: hidden;
  background: #ffffff;
  color: var(--lp-ink);
  isolation: isolate;
}
/* Two faint washes of the brand, so the white is lit rather than flat. */
.lp::before {
  content: '';
  position: absolute; inset: -20% -10% auto -10%; height: 70%;
  background:
    radial-gradient(60% 60% at 22% 20%, color-mix(in srgb, var(--lp-green) 14%, transparent) 0%, transparent 70%),
    radial-gradient(55% 55% at 82% 12%, color-mix(in srgb, var(--lp-gold) 18%, transparent) 0%, transparent 70%);
  pointer-events: none;
  z-index: -2;
}

/* --- the orbits ---------------------------------------------------------- */
.lp__field {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
  z-index: -1;
}
.lp__ring {
  position: absolute;
  border-radius: 50%;
  border: 1px solid rgb(11 18 32 / 0.06);
}
/* Sized off the viewport height so the rings stay circular and stay behind the
   copy at any shape of screen. */
.lp__ring--1 { width: 84vh; height: 84vh; }
.lp__ring--2 { width: 116vh; height: 116vh; }
.lp__ring--3 { width: 148vh; height: 148vh; border-color: rgb(11 18 32 / 0.04); }

/* A mark riding an orbit. The element declares its angle; the stylesheet does
   the trigonometry — rotate out to the radius, then rotate back so the logo
   itself stays upright rather than lying on the curve. */
.lp-orb {
  position: absolute;
  display: grid; place-items: center;
  width: 58px; height: 58px;
  border-radius: 18px;
  background: #fff;
  border: 1px solid rgb(11 18 32 / 0.07);
  box-shadow: 0 14px 30px -18px rgb(11 18 32 / 0.4);
  animation-name: lp-bob;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
.lp-orb--r1 { transform: rotate(var(--deg)) translateY(-42vh) rotate(calc(-1 * var(--deg))); }
.lp-orb--r2 { transform: rotate(var(--deg)) translateY(-58vh) rotate(calc(-1 * var(--deg))); }
.lp-orb img { width: 34px; height: 34px; object-fit: contain; }
@keyframes lp-bob { from { margin-top: -7px; } to { margin-top: 7px; } }

/* --- the copy ------------------------------------------------------------ */
.lp__inner {
  position: relative;
  width: 100%; max-width: 900px;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}

.lp-badge {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px 7px 11px;
  border: 1px solid rgb(11 18 32 / 0.1);
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 8px 20px -14px rgb(11 18 32 / 0.5);
  color: #4b5563;
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  animation: lp-rise 620ms cubic-bezier(.2,.7,.3,1) both;
}
.lp-badge__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--lp-green);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--lp-green) 18%, transparent);
}

.lp-title {
  margin: 24px 0 0;
  max-width: 20ch;
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(38px, 6vw, 78px);
  line-height: 1.06;
  letter-spacing: -0.02em;
  /* The line the eye passes over. */
  color: rgb(11 18 32 / 0.34);
}
.lp-word { display: inline-block; margin-right: 0.26em; animation: lp-rise 720ms cubic-bezier(.2,.7,.3,1) both; }
.lp-word--lit { color: var(--lp-ink); display: inline-flex; align-items: center; gap: 0.2em; }
.lp-word--plus { color: rgb(11 18 32 / 0.25); }
.lp-line {
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: center;
  width: 100%;
  font-size: clamp(32px, 5.2vw, 70px);
}
.lp-line .lp-word { white-space: nowrap; }

@keyframes lp-rise {
  from { opacity: 0; transform: translateY(18px); filter: blur(5px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* The two lit glyphs, one in each brand colour. */
.lp-glyph {
  position: relative;
  display: inline-grid; place-items: center;
  width: 1.06em; height: 1.06em;
  flex: 0 0 auto;
  border-radius: 0.26em;
  background: #fff;
  box-shadow: 0 0 0 1px rgb(11 18 32 / 0.07);
}
.lp-glyph .ic { width: 62%; height: 62%; }
.lp-glyph--green .ic { color: var(--lp-green); }
.lp-glyph--gold .ic { color: #B27B00; }   /* the gold's readable derivative on white */
.lp-glyph__halo {
  position: absolute; inset: -36%;
  border-radius: 50%;
  filter: blur(12px);
  z-index: -1;
  animation: lp-breathe 3.8s ease-in-out infinite alternate;
}
.lp-glyph--green .lp-glyph__halo { background: radial-gradient(circle, var(--lp-green) 0%, transparent 70%); }
.lp-glyph--gold .lp-glyph__halo { background: radial-gradient(circle, var(--lp-gold) 0%, transparent 70%); }
@keyframes lp-breathe { from { opacity: 0.26; transform: scale(0.92); } to { opacity: 0.6; transform: scale(1.1); } }

.lp-sub {
  margin: 22px 0 0;
  max-width: 54ch;
  color: #4b5563;
  font-size: clamp(14px, 1.15vw, 16.5px);
  line-height: 1.62;
  animation: lp-rise 720ms 540ms cubic-bezier(.2,.7,.3,1) both;
}

/* --- the action ---------------------------------------------------------- */
.lp-actions {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: center;
  margin-top: 30px;
  animation: lp-rise 760ms 660ms cubic-bezier(.2,.7,.3,1) both;
}
.lp-cta {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 14px 24px;
  border: 0; border-radius: 999px;
  background: var(--lp-green);
  color: #fff;
  font: inherit; font-size: 14.5px; font-weight: 700;
  cursor: pointer;
  box-shadow: 0 16px 34px -16px color-mix(in srgb, var(--lp-green) 75%, transparent);
  transition: transform .25s cubic-bezier(.2,.7,.3,1), box-shadow .25s ease, background .2s ease;
}
.lp-cta .ic { transition: transform .25s ease; }
.lp-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 22px 44px -16px color-mix(in srgb, var(--lp-green) 85%, transparent);
}
.lp-cta:hover .ic { transform: translateX(4px); }
.lp-cta:focus-visible { outline: 2px solid var(--lp-ink); outline-offset: 3px; }

.lp-ghost {
  padding: 13px 20px;
  border: 1px solid rgb(11 18 32 / 0.12);
  border-radius: 999px;
  background: #fff;
  color: #4b5563;
  font: inherit; font-size: 13.5px; font-weight: 650;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.lp-ghost:hover { color: var(--lp-ink); border-color: rgb(11 18 32 / 0.25); background: #f8fafc; }

/* --- the partners along the foot ----------------------------------------- */
.lp-foot {
  position: relative;
  width: 100%;
  margin-top: auto;
  padding: 34px 0 26px;
  animation: lp-rise 800ms 820ms cubic-bezier(.2,.7,.3,1) both;
}
.lp-foot__lead {
  margin: 0 0 16px;
  text-align: center;
  color: #6b7280;
  font-size: 11px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
}
.lp-track {
  display: flex;
  overflow: hidden;
  /* Faded at both ends so the marks arrive and leave rather than being cut. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
}
.lp-track__run {
  display: flex; align-items: center; gap: 42px;
  padding-right: 42px;
  flex: 0 0 auto;
  animation: lp-marquee 46s linear infinite;
}
@keyframes lp-marquee { from { transform: translateX(0); } to { transform: translateX(-100%); } }
.lp-track:hover .lp-track__run { animation-play-state: paused; }

.lp-chip { display: grid; place-items: center; flex: 0 0 auto; }
.lp-chip img {
  width: 40px; height: 40px; object-fit: contain;
  /* Grey until looked at, so eighteen marks read as one band rather than
     eighteen competing colours. */
  filter: grayscale(1);
  opacity: 0.55;
  transition: filter .3s ease, opacity .3s ease, transform .3s ease;
}
.lp-chip:hover img { filter: none; opacity: 1; transform: scale(1.12); }

@media (max-width: 900px) {
  .lp__field { display: none; }
  .lp-title { max-width: 18ch; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-orb, .lp-glyph__halo, .lp-track__run { animation: none; }
  .lp-badge, .lp-word, .lp-sub, .lp-actions, .lp-foot { animation: none; opacity: 1; transform: none; filter: none; }
  .lp-cta, .lp-chip img, .lp-ghost { transition: none; }
  .lp-cta:hover { transform: none; }
}



/* ==========================================================================
   Success Stories — a card carousel that arrives folded, hung on a wire
   ========================================================================== */

.sw-root {
  /* Sized from one place, because the card height is what decides whether the
     section fills the slide: the deck band is what is left after the head and
     the controls, and a card shorter than that band leaves a hole under it. */
  --sw-card-w: 396px;
  --sw-shot-h: 388px;
  /* Where the lowest point of the hanging line sits inside the deck. Both the
     wire and the cards are placed off it, and it has to clear the climb at the
     wire's ends — otherwise the line runs up out of the deck and into the head. */
  --sw-trough: 96px;
  /* The anchor is the trough, pushed down to centre the hanging composition in
     the band the deck actually got. Presenting sizes the slide off the screen, so
     that band is 538 rows on one display and 659 on the next, and a composition
     pinned to the top of it reads as hanging in the upper half of the page.
     Floored at the trough: on a band too short to centre in, the extra goes under
     the cards rather than pulling the wire's ends up out of sight. */
  --sw-anchor: max(var(--sw-trough), calc(50% - (var(--sw-shot-h) + 78px) / 2));
  --sw-green: #008638;
  --sw-ink: #0B1220;

  position: relative;
  height: 100%; min-height: 860px; max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  /* A white gradient, not a flat white: the cards are white too, so the ground
     needs to fall away underneath them or they have nothing to sit against. */
  background:
    radial-gradient(80% 60% at 18% 0%, rgb(0 134 56 / 0.07) 0%, transparent 62%),
    radial-gradient(70% 55% at 88% 4%, rgb(255 187 0 / 0.10) 0%, transparent 60%),
    linear-gradient(180deg, #FFFFFF 0%, #F4F7F9 52%, #E8EEF2 100%);
  color: var(--sw-ink);
  font-family: var(--display);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}
.sw-empty {
  grid-row: 1 / -1;
  display: grid; place-items: center; align-content: center; gap: 12px;
  color: #5B6570; text-align: center;
}
.sw-hint { margin: 0; font-size: 13px; }

/* --- the header ---------------------------------------------------------- */
.sw-head { padding: 34px 56px 0; text-align: center; }
/* Set as a poster line: one weight, all caps, tracked in tight and stacked two
   deep with the subject's name on the second line. Oswald's 600 is the heaviest
   real cut in the build — 700 and up would be the browser smearing the 600, which
   at this size is visible as a furred edge. */
.sw-title {
  margin: 0;
  display: flex; align-items: baseline; justify-content: center;
  /* A real row gap, not a squeezed line-height: the name is its own line and the
     two want measured air between them, not the leading of one paragraph. */
  flex-wrap: wrap;
  column-gap: 0.3em; row-gap: 10px;
  line-height: 1;
}
.sw-title__lead,
.sw-title__accent,
.sw-title__name {
  font-family: var(--display);
  font-style: normal; font-weight: 600;
  text-transform: uppercase;
  font-size: clamp(38px, 4.5vw, 70px);
  letter-spacing: -0.008em;
  animation: sw-in 780ms cubic-bezier(.2,.7,.3,1) both;
}
.sw-title__accent {
  /* The brand green, not the lime: on white the lime measures far too pale to
     carry a headline. */
  color: var(--sw-green);
  animation-delay: 90ms;
}
/* The name takes the whole row, which is what puts it on a line of its own, and
   is set a size down so the two lines read as headline and subject rather than
   as one run of caps. */
.sw-title__name {
  flex: 0 0 100%;
  font-size: clamp(21px, 2.4vw, 36px);
  /* Tracked out, where the line above is tracked in: a name read as a byline
     wants the letters apart, and the contrast is what separates the two lines
     into headline and subject rather than one long run of caps. */
  letter-spacing: 0.11em;
  /* The tracking adds its space to the right of the last letter too, so the line
     hangs left of centre unless that much is given back. */
  text-indent: 0.11em;
  color: rgb(11 18 32 / 0.58);
  animation-delay: 180ms;
}
@keyframes sw-in {
  from { opacity: 0; transform: translateY(16px); filter: blur(5px); }
  to   { opacity: 1; transform: none; filter: none; }
}
.sw-open {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 14px;
  padding: 10px 20px;
  border: 1px solid rgb(11 18 32 / 0.14);
  border-radius: 999px;
  background: #fff; color: var(--sw-ink);
  font: inherit; font-size: 12.5px; font-weight: 600; letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow: 0 8px 20px -14px rgb(11 18 32 / 0.4);
  animation: sw-in 780ms 400ms cubic-bezier(.2,.7,.3,1) both;
  transition: background .22s ease, border-color .22s ease, color .22s ease;
}
.sw-open:hover { background: var(--sw-green); border-color: var(--sw-green); color: #fff; }
.sw-root.is-open .sw-open { display: none; }

/* --- the wire and the deck ------------------------------------------------ */
/* A definite height and auto block margins, so the deck sits in the middle of
   whatever band it is given. Presenting hands the section a taller slide than the
   admin canvas does, and without this the whole composition stays pinned to the
   top of the band and the extra height lands as a dead strip under the cards.
   The height is derived from the card rather than typed: trough, the clip's bite,
   the picture, and the foot with room for the sway to swing into. */
.sw-deck { position: relative; min-height: 0; }

/* Sized in its own pixels and centred, not stretched to the deck: a
   `preserveAspectRatio` fit would scale the stroke with the box and the line
   would thicken or vanish with the slide's width. The box is lifted by the height
   of its own climb, which puts the trough of the curve exactly on the cards'
   anchor line — the figure comes from the component, which is the only place the
   bend is defined. */
.sw-wire {
  position: absolute;
  left: 50%; top: var(--sw-anchor);
  translate: -50% calc(-1 * var(--wire-lift, 64px));
  opacity: 0;
  transition: opacity .5s ease .16s;
  pointer-events: none;
  overflow: visible;
  /* The ends are far off the slide, where a line that simply stopped would read
     as a cut. */
  /* The fade has to be narrow. Held at 26% the mask was hiding the whole steep
     part of the curve and showing only its flattest half, which read as a slack
     straight line however deep the bend underneath it actually was. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 11%, #000 89%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0%, #000 11%, #000 89%, transparent 100%);
}
.sw-wire path { stroke: rgb(11 18 32 / 0.22); stroke-width: 1.5; }
.sw-root.is-open .sw-wire { opacity: 1; }

.sw-c {
  position: absolute;
  /* Top-anchored with the origin on the top edge, so every card's top lands on
     the same line and a scaled-down neighbour shrinks downward from its pin
     rather than lifting away from the wire. */
  top: var(--sw-anchor); left: 50%;
  transform-origin: 50% 0;
  width: var(--sw-card-w);
  /* Enough for the clip to bite: the wire runs across the top of this box and
     the card starts just below it, so the clip can hold both. */
  padding-top: 14px;
  cursor: pointer;
  will-change: transform, opacity;
  transition: transform .72s cubic-bezier(.22,.68,0,1), opacity .46s ease;
}

/* A bulldog clip, not a dot: a green tab that straddles the wire above and the
   card's top edge below, so the line visibly carries the picture. Its top corners
   are rounder than its bottom ones, which is the whole of the read — a rectangle
   with four equal corners looks like a swatch. */
.sw-c__clip {
  position: absolute; top: -14px; left: 50%;
  width: 19px; height: 40px; margin-left: -9.5px;
  border-radius: 7px 7px 4px 4px;
  background: var(--sw-green);
  box-shadow: 0 3px 7px -1px rgb(11 18 32 / 0.34);
  opacity: 0; transition: opacity .4s ease .2s;
  z-index: 3;
}
/* The spring: one pale line across the throat of the clip, at the height where
   the two halves meet. */
.sw-c__clip::before {
  content: ''; position: absolute;
  left: 3px; right: 3px; top: 12px; height: 2px;
  border-radius: 1px;
  background: rgb(255 255 255 / 0.42);
}
/* The jaw, a shade deeper than the body, so the bottom third reads as the part
   that is actually gripping. */
.sw-c__clip::after {
  content: ''; position: absolute;
  left: 0; right: 0; bottom: 0; height: 15px;
  border-radius: 3px 3px 4px 4px;
  background: rgb(0 0 0 / 0.18);
}
.sw-root.is-open .sw-c__clip { opacity: 1; }

/* The card swings from the pin. The sway is on this body and not on .sw-c,
   because .sw-c's transform is rewritten by script on every move and an
   animation on the same property would win and freeze the layout. */
.sw-c__body {
  display: flex; flex-direction: column;
  border-radius: 20px;
  overflow: hidden;
  background: #fff;
  /* A hairline, because a white card on a near-white ground needs an edge. */
  border: 1px solid rgb(11 18 32 / 0.07);
  transform-origin: 50% 0;
  box-shadow: 0 26px 50px -26px rgb(11 18 32 / 0.34);
  transition: box-shadow .4s ease;
}
.sw-root.is-open .sw-c__body {
  animation: sw-sway var(--sway-dur, 6s) var(--sway-delay, 0s) ease-in-out infinite alternate;
}
@keyframes sw-sway {
  from { transform: rotate(-1.15deg); }
  to   { transform: rotate(1.15deg); }
}
/* The card being read hangs nearly still — a picture someone is looking at should
   not be the most restless thing on the slide. */
.sw-root.is-open .sw-c.is-centre .sw-c__body { animation-duration: calc(var(--sway-dur, 6s) * 1.6); }
.sw-c.is-centre .sw-c__body { box-shadow: 0 36px 70px -28px rgb(11 18 32 / 0.42); }
.sw-c:hover .sw-c__body { animation-play-state: paused; }

.sw-c__shot {
  display: grid; place-items: center;
  height: var(--sw-shot-h);
  padding: 18px;
  background: #F5F7F9;
}
/* Contained, never cropped: the sources run from 206px squares to 2000px posters
   and a fixed crop would slice the ends off the wide ones. */
.sw-c__img { display: block; width: auto; height: auto; max-width: 100%; max-height: 100%; object-fit: contain; }

/* --- cards that hold a film --------------------------------------------- */
/* Contained rather than cropped, like every other card here, so the speaker is
   never the part that gets cut. The padding goes and the ground turns dark: a
   film sits in its own frame the way it would in a player, and the letterbox a
   16:9 clip leaves in a squarer card should read as black bars, not as a card
   with grey margins. */
.sw-c__shot--film { position: relative; padding: 0; background: #0D1117; }
.sw-c__film { width: 100%; height: 100%; max-width: 100%; max-height: 100%; }
/* Sits over the still frame, not over the controls — there are none on the card;
   the film only plays in the viewer. Pointer events off, so the click passes
   through to the card, which is what opens it. */
.sw-c__play {
  position: absolute; inset: 0; margin: auto;
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.9); color: #111827;
  box-shadow: 0 6px 18px -6px rgb(11 18 32 / 0.6);
  pointer-events: none;
  transition: transform .2s ease, background .2s ease;
}
.sw-c:hover .sw-c__play, .sw-c.is-centre .sw-c__play { transform: scale(1.08); background: #fff; }

.sw-c__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 18px 18px;
}
.sw-c__name {
  margin: 0; min-width: 0;
  font-size: 14px; font-weight: 600; letter-spacing: -0.005em; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sw-c__view {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px;
  border: 1px solid rgb(11 18 32 / 0.16);
  border-radius: 999px;
  background: #fff; color: var(--sw-ink);
  font: inherit; font-size: 11.5px; font-weight: 650;
  cursor: pointer;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.sw-c__view:hover { background: var(--sw-ink); border-color: var(--sw-ink); color: #fff; }
/* Only the card in hand offers it: the neighbours are one click from becoming the
   card in hand, and two live buttons on one card confuses that. */
.sw-c:not(.is-centre) .sw-c__view { opacity: 0; pointer-events: none; }

/* --- the controls -------------------------------------------------------- */
.sw-nav {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 0 0 30px;
  opacity: 0; pointer-events: none;
  transform: translateY(10px);
  transition: opacity .34s ease, transform .44s cubic-bezier(.2,.7,.3,1);
}
.sw-root.is-open .sw-nav { opacity: 1; pointer-events: auto; transform: none; }
.sw-nav__btn {
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border: 1px solid rgb(11 18 32 / 0.16);
  border-radius: 50%;
  background: #fff; color: var(--sw-ink); cursor: pointer;
  box-shadow: 0 6px 16px -12px rgb(11 18 32 / 0.5);
  transition: background .2s ease, border-color .2s ease, transform .2s ease;
}
.sw-nav__btn:hover:not(:disabled) { background: #F1F4F6; border-color: rgb(11 18 32 / 0.3); }
.sw-nav__btn:active:not(:disabled) { transform: scale(0.94); }
.sw-nav__btn:disabled { opacity: 0.32; cursor: default; }
.sw-nav__count {
  min-width: 68px; text-align: center;
  color: #5B6570;
  font-size: 12px; font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: 0.08em;
}
.sw-nav__fold {
  margin-left: 8px;
  padding: 11px 20px;
  border: 0; border-radius: 999px;
  background: var(--sw-green); color: #fff;
  font: inherit; font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow: 0 12px 26px -14px rgb(0 134 56 / 0.7);
  transition: filter .2s ease, transform .2s ease;
}
.sw-nav__fold:hover { filter: brightness(1.08); transform: translateY(-1px); }

/* --- the full-size viewer ------------------------------------------------ */
/* Still dark: it is a viewer, and a photograph shows best against something that
   is not competing with it. */
.sw-view {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 5vh 5vw;
  background: rgb(6 7 9 / 0.9);
  opacity: 0; transition: opacity .24s ease;
}
.sw-view.is-on { opacity: 1; }
.sw-view[hidden] { display: none; }
.sw-view__frame {
  margin: 0; display: flex; flex-direction: column; align-items: center; gap: 14px;
  max-width: 100%; max-height: 100%;
  transform: scale(0.97);
  transition: transform .28s cubic-bezier(.2,.7,.3,1);
}
.sw-view.is-on .sw-view__frame { transform: none; }
.sw-view__img {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: calc(100vh - 18vh);
  border-radius: 14px; background: #111;
}
/* A film fills the frame, where a photograph is left at its own pixels. The
   sources here are 873px and 1280px wide, and held to that a testimonial plays
   as a small window in the middle of a projector screen. Enlarging a film is not
   the same bargain as enlarging a photograph: the motion carries it, and the
   audience is across a hall. The width is whatever a 16:9 clip needs to use the
   height available, so anything wider than that is limited by the frame instead
   and nothing can overflow either way. */
.sw-view__vid {
  width: min(100%, calc((100vh - 18vh) * 16 / 9));
  height: auto;
  max-height: calc(100vh - 18vh);
  background: #000;
}
/* Both carry `display: block` from .sw-view__img, which outranks a bare [hidden]
   and would leave the one that is off still drawn. Said here at two classes of
   specificity so whichever is hidden actually goes. */
.sw-view__img[hidden], .sw-view__vid[hidden] { display: none; }

.sw-view__cap { margin: 0; color: #fff; font-size: 14px; font-weight: 600; }
.sw-view__close {
  position: absolute; top: 22px; right: 24px;
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 0; border-radius: 50%;
  background: rgb(255 255 255 / 0.14); color: #fff; cursor: pointer;
  transition: background .18s ease;
}
.sw-view__close:hover { background: rgb(255 255 255 / 0.26); }

/* --- filling the display ------------------------------------------------- */
/* Presenting has no head above the section, so the pictures can be larger than
   they are in the admin canvas. The height itself is handled by the shared rule
   for every fixed-height root — see "sections must not be shorter than their
   slide" at the end of this file. */
/* The pictures stay the size they are in the admin canvas. Presenting is taller
   on a 16:10 screen but no taller on 16:9, and the hanging deck has to fit the
   band on both — the extra height on a taller screen goes to the air around the
   cards, which is what `.sw-deck`'s auto margins are for. */

@media (prefers-reduced-motion: reduce) {
  .sw-title__lead, .sw-title__accent, .sw-title__name, .sw-open { animation: none; }
  .sw-c, .sw-c__body, .sw-nav, .sw-view, .sw-view__frame, .sw-nav__btn { transition: none; }
  .sw-root.is-open .sw-c__body { animation: none; }
}

/* ==========================================================================
   Hover controls for every player — play/pause and skip
   ========================================================================== */

/* One bar, six call sites. It sits over whatever it drives and appears on hover
   of that surface, so it never covers a picture nobody is pointing at. */
.vc {
  position: absolute;
  /* Bottom right, and always on show.

     It was centred once because pinning it along the foot put it under the
     presenter bar and dropped the skip buttons onto the section icons. The corner
     avoids both: the presenter bar is fixed at bottom *centre*, and the section
     rail is on the left. Centred, it also sat over the middle of the picture,
     which on the hero is exactly where the headline is. */
  right: 14px; bottom: 14px;
  transform: scale(1);
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px;
  border-radius: 999px;
  background: rgb(8 10 13 / 0.72);
  border: 1px solid rgb(255 255 255 / 0.14);
  backdrop-filter: blur(10px);
  /* Visible without hovering: a presenter should be able to see that the film can
     be paused before they need to pause it. Hover only brightens it. */
  opacity: 0.88;
  pointer-events: auto;
  z-index: 6;
  transition: opacity .2s ease, transform .28s cubic-bezier(.2,.7,.3,1);
}
@supports not (backdrop-filter: blur(10px)) { .vc { background: rgb(8 10 13 / 0.9); } }

/* Full strength when the player's surface or the bar itself is under the pointer.
   The translate these rules used to restore went with the centring; leaving it in
   would have thrown the bar half its own size off the corner on hover. */
.has-vc:hover > .vc,
.has-vc:focus-within > .vc,
.pf-win:hover .vc,
.pg-player__frame:hover .vc,
.tw-player__frame:hover .vc,
.hero__vc:hover .vc,
.hero:hover .hero__vc .vc,
.vc:hover, .vc:focus-within {
  opacity: 1;
  pointer-events: auto;
  transform: scale(1.03);
}

.vc__btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-width: 34px; height: 34px;
  padding: 0 9px;
  border: 0; border-radius: 999px;
  background: rgb(255 255 255 / 0.1);
  color: #fff;
  font: inherit; font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background .16s ease, transform .16s ease;
}
.vc__btn:hover { background: rgb(255 255 255 / 0.22); }
.vc__btn:active { transform: scale(0.94); }
.vc__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Play/pause reads as the primary of the three. */
.vc__btn--main {
  min-width: 42px; height: 42px; padding: 0;
  background: #fff; color: #0A0B0D;
}
.vc__btn--main:hover { background: #fff; transform: scale(1.06); }

/* The hero's film is a backdrop under the headline, so its controls need their
   own layer above the copy rather than sitting on an element nobody can reach. */
.hero__vc {
  /* The whole frame, so the bar centres in the hero rather than in a strip along
     its foot. */
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 4;
}
.hero__vc .vc { pointer-events: auto; }

/* While presenting, the slide fills the display, so the player bar lines up with the
   presenter bar by taking the same offset. Off it, the bar belongs to its own frame
   and keeps the tighter inset. Without this the two sat 8px apart vertically. */
body.is-presenting .vc {
  bottom: 22px;
}
.hero:hover .hero__vc .vc { opacity: 1; }

/* Nothing to offset any more: a centred bar clears the presenter bar by the
   height of half the film. */

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

/* The sound button lights when audio is actually on, so a presenter can see at a
   glance whether the room is about to hear anything. */
.vc__btn.is-live {
  background: var(--nav-accent, var(--brand-accent, #B8F14A));
  color: #0A0B0D;
}
.vc__btn.is-live:hover { background: var(--nav-accent, var(--brand-accent, #B8F14A)); filter: brightness(1.08); }

/* --- a centre's YouTube film ---------------------------------------------- */
/* The vendor academies publish to YouTube, so a centre's strip holds frames as
   well as files. Mounted on click: four Pega films that all start the moment a
   centre opens is four soundtracks at once. */
/* The aspect ratio is what gives this box a height at all: the poster, the play
   button and the mounted frame are every one of them absolutely positioned, so
   without it the tile collapsed to 2px and the film was invisible behind the
   photographs. Sixteen by nine because that is what the films are. */
.coe-tile__yt {
  position: relative; display: block; aspect-ratio: 16 / 9;
  background: #10141a; overflow: hidden;
}
.coe-tile__poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0.72;
}
.coe-tile__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.coe-tile__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  border: 0; background: transparent; color: #fff; cursor: pointer;
}
.coe-tile__play .ic {
  width: 56px; height: 56px; padding: 14px; border-radius: 999px;
  background: var(--nav-accent, #008638); color: #fff;
  box-shadow: 0 10px 26px rgb(0 0 0 / 0.34);
  transition: transform 200ms cubic-bezier(.2,.7,.3,1), background 180ms ease;
}
.coe-tile__play:hover .ic { transform: scale(1.08); }
.coe-tile__yt.is-live .coe-tile__play, .coe-tile__yt.is-live .coe-tile__poster { display: none; }
/* Nothing to hover before the frame exists, so the bar only arms once it does. */
.coe-tile--yt .vc { opacity: 0; transition: opacity 180ms ease; }
.coe-tile__yt.is-live:hover ~ figcaption, .coe-tile--yt:hover .vc,
.coe-tile--yt:focus-within .vc { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .coe-tile__play .ic { transition: none; }
  .coe-tile__play:hover .ic { transform: none; }
}

/* ==========================================================================
   The full-screen film stage — shared by Events and Video Resumes
   ==========================================================================
   On the body, never inside a slide: FitSlide scales the slide with a transform,
   and a transformed ancestor becomes the containing block for fixed descendants,
   so `inset: 0` in a slide means the slide and not the screen.
   ========================================================================== */

.fs-root {
  position: fixed; inset: 0; z-index: 70;
  display: grid; grid-template-rows: minmax(0, 1fr) auto;
  padding: 3vh 3vw 0;
  background: rgb(6 8 10 / 0.96);
  opacity: 0; transition: opacity 220ms ease;
}
.fs-root.is-on { opacity: 1; }
.fs-root[hidden] { display: none; }

.fs-box {
  /* Width solved against both limits, so the ratio is exact. `max-height` fought
     the aspect ratio instead: the box came out 1408x776, a ratio of 1.815, and the
     player letterboxed itself inside it. Taking the smaller of the width the
     viewport allows and the width the remaining height allows leaves 16:9 intact. */
  place-self: center;
  width: min(calc(100vw - 12vw), calc((100vh - 15vh) * 16 / 9));
  aspect-ratio: 16 / 9;
  border-radius: var(--radius); overflow: hidden; background: #000;
  box-shadow: 0 30px 80px rgb(0 0 0 / 0.6);
  transform: scale(0.975); transition: transform 260ms cubic-bezier(.2,.7,.3,1);
}
.fs-root.is-on .fs-box { transform: none; }
.fs-frame { position: relative; width: 100%; height: 100%; }
.fs-yt, .fs-file { display: block; width: 100%; height: 100%; border: 0; object-fit: contain; }
/* The bar is centred over the film and arms on hover, like every other player. */
.fs-frame .vc { opacity: 0; transition: opacity 180ms ease; }
.fs-box:hover .vc, .fs-box:focus-within .vc { opacity: 1; }

.fs-bar {
  display: flex; align-items: baseline; gap: 20px;
  padding: 16px 4px 18px; color: #e8eaed;
}
.fs-bar__text { min-width: 0; }
.fs-title {
  margin: 0; font-size: 24px; font-weight: 800; letter-spacing: -0.02em; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fs-sub { margin: 4px 0 0; color: #9aa1a9; font-size: 13px; font-weight: 650; }
.fs-sub[hidden] { display: none; }
.fs-count {
  margin-left: auto; flex: 0 0 auto;
  color: var(--brand-accent, #FFBB00);
  font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums;
}

.fs-close {
  position: absolute; top: 22px; left: 28px; z-index: 99;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px;
  border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 999px;
  background: rgba(15, 23, 42, 0.85); backdrop-filter: blur(8px);
  color: #fff; font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 13.5px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  cursor: pointer; transition: background 180ms ease, border-color 180ms ease, transform 180ms ease;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
}
.fs-close:hover {
  background: var(--nav-accent, #008638);
  border-color: var(--nav-accent, #008638);
  transform: translateY(-2px);
}
.fs-nav {
  position: absolute; top: 50%; z-index: 2; transform: translateY(-50%);
  display: grid; place-items: center; width: 58px; height: 58px;
  border: 0; border-radius: 999px; background: rgb(255 255 255 / 0.12);
  color: #fff; cursor: pointer; transition: background 160ms ease, transform 160ms ease;
}
.fs-nav:hover { background: color-mix(in srgb, var(--nav-accent, #008638) 70%, transparent); }
.fs-nav:active { transform: translateY(-50%) scale(0.94); }
.fs-nav[hidden] { display: none; }
.fs-nav .ic { width: 28px; height: 28px; }
.fs-nav--prev { left: 18px; }
.fs-nav--next { right: 18px; }

.fs-poster { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (prefers-reduced-motion: reduce) {
  .fs-root, .fs-box, .fs-nav, .fs-close { transition: none; }
}

/* ==========================================================================
   Events — one chapter at a time, read from across a room
   ==========================================================================
   The previous version put a player beside a list of every entry, and it was too
   much at once: 23 rows in a 496px column at 12.5px. This is one thing instead —
   a grid of entries at a size that can be read — and the film plays full screen.
   ========================================================================== */

.ev-root {
  display: flex; flex-direction: column;
  /* `.canvas-block > *` sets `flex: 1`, whose `flex-basis: 0%` overrides `height`
     on the main axis. Without a basis of its own this root grows to its content
     and FitSlide scales the whole slide down to fit; with it, the grid scrolls. */
  flex: 0 0 auto; height: 860px; max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.ev-empty { display: grid; place-items: center; min-height: 400px; gap: 10px; text-align: center; }
.ev-hint { margin: 0; color: var(--muted); font-size: 13px; }

.ev-head {
  flex: 0 0 auto;
  display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
  align-items: end; column-gap: 30px; padding: 24px 30px 16px;
}
/* --------------------------------------------------------------- the head -- */
.ev-head {
  flex: 0 0 auto;
  padding: 20px 30px 12px;
  text-align: center;
}
.ev-head--clean {
  display: flex;
  justify-content: center;
  align-items: center;
}
.ev-title--center {
  margin: 0;
  color: var(--nav-accent-ink, #008638);
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 34px;
  font-weight: 850;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
}

/* --------------------------------------------------------------- the tabs -- */
.ev-tabs {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  gap: 10px;
  padding: 0 30px 14px;
}
.ev-tab {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 22px;
  border: 1px solid var(--hairline, #e5e7eb);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 14.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: color 160ms ease, background 160ms ease, border-color 160ms ease;
}
.ev-tab:hover {
  color: var(--ink, #111827);
  border-color: #d1d5db;
  background: var(--page-bg);
}
.ev-tab.is-on {
  color: #fff;
  background: var(--nav-accent-ink, #008638);
  border-color: var(--nav-accent-ink, #008638);
  box-shadow: 0 8px 20px -10px rgba(0, 134, 56, 0.45);
}

/* --------------------------------------------------------------- the grid -- */
.ev-grid {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* `safe` is what makes this usable with both a full chapter and a short one:
     a chapter of two entries is centred in the band instead of clinging to the
     top under a screen of white, and one of twenty-three — which overflows and
     scrolls — falls back to flex-start rather than centring its overflow and
     putting the first row out of reach above the scroll origin. */
  justify-content: safe center;
  gap: 20px;
  padding: 14px 30px 28px;
  overflow-y: auto !important;
  scrollbar-width: none !important;
  -ms-overflow-style: none;
  touch-action: pan-y;
}
.ev-grid::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
}

.ev-row {
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 20px;
  width: 100%;
}

.ev-card {
  flex: 0 0 calc((100% - 3 * 20px) / 4);
  width: calc((100% - 3 * 20px) / 4);
  max-width: calc((100% - 3 * 20px) / 4);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hairline, #e5e7eb);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.05);
  transition: border-color 220ms ease, box-shadow 220ms ease, transform 220ms ease;
}
.ev-card:hover {
  border-color: var(--nav-accent-ink, #008638);
  transform: translateY(-6px) scale(1.02);
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.16);
  z-index: 2;
}

.ev-card__hit {
  display: block; width: 100%; padding: 0; border: 0; background: none; cursor: pointer;
}
.ev-card__shot {
  position: relative; display: block; width: 100%; aspect-ratio: 16 / 9;
  background: #10141a; overflow: hidden;
}
.ev-card__shot .fs-poster { transition: transform 420ms cubic-bezier(.2,.7,.3,1); }
.ev-card:hover .ev-card__shot .fs-poster { transform: scale(1.05); }
.ev-card__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: #fff;
}
.ev-card__play .ic {
  width: 46px; height: 46px; padding: 11px; border-radius: 999px;
  background: rgba(8, 10, 13, 0.55); color: #fff;
  transition: background 180ms ease, transform 180ms ease;
}
.ev-card:hover .ev-card__play .ic {
  background: var(--nav-accent, #008638); transform: scale(1.12);
}

/* --- entries that are photographs, not film ------------------------------ */
/* The card keeps the 16:9 poster shape the grid solves its columns from, so a
   folder of square photographs sits in the same row as the film posters without
   the row changing height. The first picture is cropped to that frame here and
   only here — the viewer it opens shows every one of them whole. */
.ev-card__shot--photos .fs-poster { object-fit: cover; }
/* The count sits in the corner rather than under the title, because the title
   row is already solved for two lines of event name and a run of part chips. */
.ev-card__count {
  position: absolute; right: 8px; bottom: 8px;
  min-width: 22px; padding: 2px 7px; border-radius: 999px;
  background: rgba(8, 10, 13, 0.62); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1.5; text-align: center;
  letter-spacing: 0.02em;
}
.ev-card__foot {
  flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; gap: 6px;
  padding: 14px 16px; min-width: 0; text-align: left;
}
.ev-card__title {
  margin: 0; font-size: 16.5px; font-weight: 800; line-height: 1.35;
  color: var(--ink, #111827);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* One title, several films. */
.ev-flow {
  margin-top: auto; display: flex; align-items: center; flex-wrap: wrap; gap: 3px;
}
.ev-flow__part {
  min-width: 30px; padding: 4px 7px;
  border: 1px solid var(--hairline, #e5e7eb); border-radius: 7px;
  background: var(--surface); color: var(--muted);
  font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: color 160ms ease, background 160ms ease, border-color 160ms ease;
}
.ev-flow__part:hover {
  color: #1a1a1a; background: var(--brand-accent, #FFBB00);
  border-color: var(--brand-accent, #FFBB00);
}
.ev-flow__arrow { display: grid; place-items: center; color: #c3c8ce; }
.ev-flow__arrow .ic { width: 12px; height: 12px; }
  border-color: var(--brand-accent, #FFBB00);
}
.ev-flow__arrow { display: grid; place-items: center; color: #c3c8ce; }
.ev-flow__arrow .ic { width: 12px; height: 12px; }

@media (prefers-reduced-motion: reduce) {
  .ev-rule, .ev-tab, .ev-card, .ev-flow__part { animation: none; }
  .ev-card, .ev-card__play .ic, .ev-flow__part { transition: none; }
  .ev-card:hover .ev-card__shot .fs-poster { transform: none; }
}

/* ==========================================================================
   Video Resumes — a hundred and sixteen students, by name
   ==========================================================================
   Finding a name is the job, so the search box and the initial strip come before
   the wall. No card depends on its thumbnail arriving: the initials plate sits
   under the poster and carries the tile when the host is unreachable, which is
   every presentation.
   ========================================================================== */

/* ==========================================================================
   Video Resumes — a hundred and sixteen students, by name
   ========================================================================== */

.vr-root {
  display: flex; flex-direction: column;
  flex: 0 0 auto; height: 860px; max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.vr-empty { display: grid; place-items: center; min-height: 400px; gap: 10px; text-align: center; }
.vr-hint { margin: 0; color: var(--muted); font-size: 13px; }

.vr-head {
  flex: 0 0 auto;
  padding: 20px 30px 10px;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
}
.vr-title--center {
  margin: 0;
  color: var(--nav-accent-ink, #008638);
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 34px;
  font-weight: 850;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
}

/* -------------------------------------------------------------- the tools -- */
.vr-tools {
  flex: 0 0 auto; display: flex; align-items: center; gap: 16px;
  padding: 0 30px 14px; border-bottom: 1px solid var(--hairline, #e5e7eb);
}
.vr-search {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 8px 14px; border: 1px solid var(--hairline, #e5e7eb);
  border-radius: 999px; background: var(--surface); color: var(--muted);
}
.vr-search:focus-within {
  border-color: var(--nav-accent-ink, #008638);
  color: var(--nav-accent-ink, #008638);
}
.vr-search__input {
  width: 170px; border: 0; background: none; outline: none;
  color: var(--ink, #111827); font: inherit; font-size: 14px;
}
.vr-search__input::-webkit-search-cancel-button { filter: grayscale(1); }

.vr-strip { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.vr-letter {
  min-width: 26px; padding: 4px 7px; border: 1px solid transparent;
  border-radius: 7px; background: transparent; color: var(--muted);
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 13px; font-weight: 800; cursor: pointer;
  transition: color 140ms ease, background 140ms ease, border-color 140ms ease;
}
.vr-letter:hover { color: var(--ink, #111827); background: var(--page-bg); }
.vr-letter.is-on, .vr-letter.is-scroll-active {
  color: #fff; background: var(--nav-accent-ink, #008638);
  border-color: var(--nav-accent-ink, #008638);
  box-shadow: 0 4px 12px rgba(0, 134, 56, 0.35);
}
.vr-sep { color: #d1d5db; font-size: 12px; opacity: 0.6; user-select: none; }

/* --------------------------------------------------------------- the wall -- */
.vr-wall {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; gap: 16px;
  padding: 18px 30px 24px;
  overflow-y: auto !important;
  scrollbar-width: none !important;
  -ms-overflow-style: none;
  touch-action: pan-y;
}
.vr-wall::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
}
.vr-none { margin: 0; color: var(--muted); font-size: 14px; text-align: center; }

.vr-row {
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 16px;
  width: 100%;
}

.vr-card {
  flex: 0 0 calc((100% - 5 * 16px) / 6);
  width: calc((100% - 5 * 16px) / 6);
  max-width: calc((100% - 5 * 16px) / 6);
  display: flex; flex-direction: column; min-width: 0; padding: 0;
  border: 1px solid var(--hairline, #e5e7eb); border-radius: var(--radius-sm);
  background: var(--surface); overflow: hidden; cursor: pointer; text-align: left;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
  transition: border-color 220ms ease, box-shadow 220ms ease, transform 220ms ease;
}
.vr-card:hover {
  border-color: var(--nav-accent-ink, #008638);
  transform: translateY(-5px) scale(1.02);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.14);
  z-index: 2;
}

.vr-card__shot {
  position: relative; display: block; width: 100%; aspect-ratio: 16 / 9;
  background: #0f1216; overflow: hidden;
}
.vr-card__mono {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--brand-accent, #FFBB00);
  font-size: 26px; font-weight: 800; letter-spacing: 0.04em;
}
.vr-card__shot .fs-poster {
  position: relative; z-index: 1;
  transition: transform 420ms cubic-bezier(.2,.7,.3,1);
}
.vr-card:hover .vr-card__shot .fs-poster { transform: scale(1.05); }
.vr-card__play {
  position: absolute; inset: 0; z-index: 2; display: grid; place-items: center;
  opacity: 0; transition: opacity 180ms ease;
}
.vr-card__play .ic {
  width: 40px; height: 40px; padding: 10px; border-radius: 999px;
  background: var(--nav-accent, #008638); color: #fff;
}
.vr-card:hover .vr-card__play { opacity: 1; }
.vr-card__name {
  padding: 10px 12px 12px; font-size: 14px; font-weight: 800; line-height: 1.25;
  color: var(--ink, #111827);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .vr-rule, .vr-card { animation: none; }
  .vr-card, .vr-card__play, .vr-letter { transition: none; }
  .vr-card:hover .vr-card__shot .fs-poster { transform: none; }
}

/* ==========================================================================
   Certifications — three acts
   ==========================================================================
   01 The Register    the badges as a constellation, the totals, the claim
   02 Skills Unlocked what each credential actually tests
   03 The Gallery     the cohort artwork, one card per batch that passed

   The register is set in the serif and on a soft ground, because it is the one
   page in this deck that is making an argument rather than presenting evidence.
   The other two acts return to the deck's own voice.
   ========================================================================== */

.cs-root {
  display: flex; flex-direction: column;
  /* `.canvas-block > *` sets `flex: 1`, whose `flex-basis: 0%` overrides `height`
     on the main axis, so a min-height root grows to its content and FitSlide
     scales the slide down to fit. A definite basis is what lets a child scroll. */
  flex: 0 0 auto; height: 860px; max-height: 860px;
  overflow: hidden; border-radius: var(--radius-lg);
  background: var(--surface);
}
.cs-empty { display: grid; place-items: center; min-height: 400px; gap: 10px; text-align: center; }
.cs-hint { margin: 0; color: var(--muted); font-size: 13px; }

/* --------------------------------------------------------------- the head -- */
.cs-head {
  flex: 0 0 auto; position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 18px 30px 14px;
  border-bottom: 1px solid var(--hairline, #e5e7eb);
}
.cs-head__center { text-align: center; }
.cs-eyebrow {
  margin: 0 0 4px; color: var(--nav-second-ink, var(--accent-ink));
  font-size: 11px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
}
.cs-title {
  margin: 0; color: var(--nav-accent-ink, var(--accent-ink));
  font-size: 28px; font-weight: 800; letter-spacing: -0.024em; line-height: 1.05;
}

/* The act nav, numbered. Small caps and a hairline underline on the live one —
   this is a table of contents, not a row of buttons. */
.cs-steps {
  position: absolute; right: 30px; bottom: 14px;
  display: flex; align-items: baseline; gap: 26px; padding-bottom: 4px;
}
.cs-step {
  display: flex; align-items: baseline; gap: 8px;
  padding: 0 0 6px; border: 0; border-bottom: 2px solid transparent;
  background: none; color: var(--muted); cursor: pointer;
  transition: color 180ms ease, border-color 180ms ease;
}
.cs-step em {
  font-style: normal; font-size: 11px; font-weight: 800;
  font-variant-numeric: tabular-nums; opacity: 0.55;
}
.cs-step span {
  font-family: var(--font);
  font-size: 19px; letter-spacing: -0.01em;
}
.cs-step:hover { color: var(--ink, #111827); }
.cs-step.is-on {
  color: var(--nav-accent-ink, var(--accent-ink));
  border-bottom-color: var(--brand-accent, #FFBB00);
}
.cs-step.is-on em { opacity: 1; color: var(--nav-second-ink, var(--accent-ink)); }
.cs-step:focus-visible { outline: 2px solid var(--nav-accent-ink, var(--accent-ink)); outline-offset: 3px; }

/* One act visible at a time. Kept in the DOM rather than rebuilt, so returning to
   the gallery does not re-solve the wall from scratch. */
.cs-acts { flex: 1 1 auto; min-height: 0; position: relative; }
.cs-act { position: absolute; inset: 0; display: none; }
.cs-acts[data-act="register"] .cs-act--register,
.cs-acts[data-act="skills"] .cs-act--skills,
.cs-acts[data-act="gallery"] .cs-act--gallery { display: block; }

/* ======================================================== 01 THE REGISTER == */
/* Two arcs running edge to edge, the badges travelling them, the totals sitting in
   the clear space underneath. The apex is 15% down the stage so the act nav above
   never has a badge behind it. */
.cs-reg {
  position: relative; width: 100%; height: 100%; overflow: hidden;
  background:
    radial-gradient(46vw 40vw at 6% 12%, color-mix(in srgb, #CBE4D6 55%, transparent), transparent 70%),
    radial-gradient(52vw 44vw at 96% 4%, color-mix(in srgb, #BED3F5 55%, transparent), transparent 70%),
    radial-gradient(58vw 48vw at 88% 96%, color-mix(in srgb, #C9B6F5 52%, transparent), transparent 70%),
    var(--surface);
}
.cs-reg__back { position: absolute; inset: auto 0 0 0; height: 47%; overflow: hidden; }
.cs-reg__back img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 30%;
  opacity: 0.38; filter: saturate(0.5) contrast(0.95);
  animation: cs-burns 52s ease-in-out infinite alternate;
}
/* Torii's backdrop is the Beyond Boundaries group photograph (2026-09-17), whose
   students stand in the lower half under the poster's title. The band this box
   shows is 43% of the picture's height, and at 30% it fell on the lettering;
   at 72% it shows source rows 517–950 of 1151, with every row of faces (about
   560–800) inside it and the poster's title out of frame, which is what was
   asked for ("move it up, I can't see the students"). Torii only — NGI's
   backdrop is a different photograph framed for 30%. */
[data-org='torii'] .cs-reg__back img { object-position: center 72%; }
.cs-reg__back::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    var(--surface) 0%,
    color-mix(in srgb, var(--surface) 90%, transparent) 22%,
    color-mix(in srgb, var(--surface) 66%, transparent) 58%,
    color-mix(in srgb, var(--surface) 58%, transparent) 100%);
}
@keyframes cs-burns { from { transform: scale(1) } to { transform: scale(1.09) translateY(-1.5%) } }

/* The badge field and its lines drift together against the pointer, so the badges
   never come off the arcs they are riding. */
.cs-drift {
  position: absolute; inset: 0; z-index: 2; will-change: transform;
  transform:
    rotateY(calc(var(--px, 0) * 4deg)) rotateX(calc(var(--py, 0) * -3deg))
    translate3d(calc(var(--px, 0) * -34px), calc(var(--py, 0) * -22px), 0);
}
.cs-arcs { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

.cs-arc__base {
  fill: none; stroke: rgba(140, 145, 175, 0.45); stroke-width: 1px; stroke-linecap: round;
  stroke-dasharray: 5 4;
}
.cs-arc__glow {
  fill: none; stroke: url(#csArcGrad); stroke-width: 3.5px; stroke-linecap: round;
  filter: blur(5px); opacity: 0.4;
  animation: cs-neon 6.5s ease-in-out infinite;
  animation-delay: calc(var(--i) * -2s);
}
.cs-arc__line {
  fill: none; stroke: url(#csArcGrad); stroke-width: 1px; stroke-linecap: round;
  stroke-dasharray: 5 4;
  filter: drop-shadow(0 0 3px rgba(113, 189, 31, 0.5));
}
.cs-arc__pulse {
  fill: none; stroke: #71BD1F; stroke-width: 1.5px; stroke-linecap: round;
  stroke-dasharray: 18 1000; stroke-dashoffset: 1018; opacity: 0.95;
  filter: drop-shadow(0 0 5px rgba(113, 189, 31, 0.8));
  animation: cs-flow 9s linear infinite; animation-delay: calc(100ms + var(--i) * 1400ms);
}
@keyframes cs-draw { to { stroke-dashoffset: 0 } }
@keyframes cs-neon { 0%, 100% { opacity: 0.22 } 50% { opacity: 0.48 } }
@keyframes cs-flow { from { stroke-dashoffset: 1022 } to { stroke-dashoffset: 0 } }

/* Position and opacity are written by the loop; everything else is CSS, so the
   frame only ever touches two composited properties. */
.cs-pin {
  position: absolute; left: 0; top: 0; padding: 0; border: 0; background: none;
  aspect-ratio: 1; cursor: pointer; will-change: transform, opacity;
}
.cs-pin__in {
  display: block; width: 100%; height: 100%;
  animation: cs-pinin 1000ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(600ms + var(--i) * 30ms);
}
@keyframes cs-pinin { from { opacity: 0; transform: scale(0.2) rotate(-30deg); filter: blur(6px) } }
.cs-pin__bob {
  display: block; width: 100%; height: 100%;
  animation: cs-bob 9s ease-in-out infinite; animation-delay: calc(var(--i) * -420ms);
}
@keyframes cs-bob { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-6px) } }
.cs-pin__art {
  display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;
  transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}
.cs-pin__art img {
  max-width: 100%; max-height: 100%; width: auto; height: auto; display: block;
  filter: drop-shadow(0 7px 13px rgba(40, 42, 80, 0.22));
}
/* The vendor set in type, for a credential with no badge art. Sized off the
   pin rather than fixed at 12px, which was legible beside a 36px badge and lost
   beside a 60px one. */
.cs-pin__art em {
  font-style: normal; font-size: 0.28em; font-weight: 800; color: var(--muted);
  line-height: 1; letter-spacing: 0.02em;
}
.cs-pin__art { font-size: var(--cs-pin-size, 60px); }
.cs-pin:hover .cs-pin__art { transform: scale(1.34); }
.cs-pin__tag {
  position: absolute; left: 50%; top: calc(100% + 9px);
  transform: translateX(-50%) translateY(-5px);
  display: flex; align-items: center; gap: 7px; white-space: nowrap;
  padding: 5px 11px; border-radius: 100px;
  background: color-mix(in srgb, var(--surface) 95%, transparent);
  border: 1px solid var(--hairline, #e5e7eb);
  box-shadow: 0 10px 22px rgba(40, 42, 80, 0.14);
  opacity: 0; pointer-events: none; z-index: 4;
  transition: opacity 400ms ease, transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
.cs-pin:hover .cs-pin__tag { opacity: 1; transform: translateX(-50%) translateY(0); }
.cs-pin__tag b { font-size: 10.5px; font-weight: 650; max-width: 230px; overflow: hidden; text-overflow: ellipsis; }
.cs-pin__tag i {
  font-style: normal; font-size: 8.5px; font-weight: 750; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--nav-second-ink, var(--accent-ink));
}

/* The type sits in the clear band the badges are told to avoid. */
.cs-reg__mid {
  position: absolute; z-index: 5; left: 50%; bottom: 42px; transform: translateX(-50%);
  width: min(860px, calc(100% - 96px));
  display: flex; flex-direction: column; align-items: center; gap: 24px; text-align: center;
}
.cs-figs { display: flex; align-items: stretch; justify-content: center; gap: clamp(26px, 5vw, 76px); }
.cs-figs__rule { width: 1px; background: var(--hairline, #e5e7eb); }
.cs-fig {
  text-align: center;
  animation: cs-rise 1200ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(900ms + var(--i) * 130ms);
}
.cs-fig strong {
  display: block; font-size: clamp(30px, 3.4vw, 54px); font-weight: 800; line-height: 1;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
  color: var(--nav-accent-ink, var(--accent-ink));
}
.cs-fig span {
  display: block; margin-top: 11px; color: var(--muted);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.19em; text-transform: uppercase;
}
/* The year split under the total: the same figures a size down, quieter. */
.cs-figs--years { margin-top: -6px; gap: clamp(18px, 3vw, 44px); }
.cs-fig--year strong { font-size: clamp(18px, 1.7vw, 26px); font-weight: 700; color: var(--ink, #111827); }
.cs-fig--year span { margin-top: 7px; font-size: 9.5px; letter-spacing: 0.16em; }
/* Where a credential's count came from, under the count in the detail view. */
.cs-detail__where { margin: 0; font-size: 12px; color: var(--muted); letter-spacing: 0.01em; }
.cs-quote { margin: 0; max-width: 660px; animation: cs-rise 1400ms 1250ms cubic-bezier(0.16, 1, 0.3, 1) backwards; }
.cs-quote p { margin: 0; font-size: clamp(15px, 1.5vw, 21px); font-style: italic; line-height: 1.5; }
.cs-quote cite {
  display: block; margin-top: 14px; font-style: normal; color: var(--muted);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.19em; text-transform: uppercase;
}
@keyframes cs-rise { from { opacity: 0; transform: translateY(16px); filter: blur(6px) } }

/* ==================================================== 02 SKILLS UNLOCKED == */
.cs-skills {
  height: 100%; padding: 20px 30px 24px; overflow: hidden; position: relative;
}

/* Row and Column Grid View (4 cards per row) */
.cs-grid-view {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  height: 100%;
  overflow-y: auto;
  padding: 8px 12px 24px 6px;
}

.cs-grid-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 138px;
  padding: 16px 18px 14px;
  border-radius: 16px;
  background: var(--surface, #ffffff);
  border: 1px solid rgba(0, 0, 0, 0.08);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.05);
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 320ms ease, border-color 320ms ease;
  animation: cs-cardin 600ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(var(--i) * 18ms);
}

.cs-grid-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 20px 36px rgba(0, 0, 0, 0.12);
  border-color: var(--card-accent, #71BD1F);
}

.cs-grid-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.cs-grid-card__title {
  margin: 0;
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 14.5px;
  font-weight: 750;
  line-height: 1.3;
  color: var(--ink, #111827);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cs-grid-card__hrule {
  width: 100%;
  height: 1px;
  background: rgba(0, 0, 0, 0.07);
  margin: 10px 0 12px;
  display: block;
}

.cs-grid-card__body {
  display: flex;
  align-items: center;
  gap: 14px;
}

.cs-grid-card__art {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.03);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5px;
  flex-shrink: 0;
}

.cs-grid-card__art img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.cs-grid-card__vrule {
  width: 1px;
  height: 36px;
  background: rgba(0, 0, 0, 0.08);
  display: block;
  flex-shrink: 0;
}

.cs-grid-card__count-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.cs-grid-card__count-val {
  font-size: 19px;
  font-weight: 800;
  line-height: 1;
}

.cs-grid-card__count-lbl {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
}

/* Detail Single Card View (Screenshot 2 spec) */
.cs-detail-view {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  padding: 10px 24px 16px;
  position: relative;
  animation: cs-fadein 400ms ease;
}

@keyframes cs-fadein { from { opacity: 0; transform: scale(0.98); } to { opacity: 1; transform: scale(1); } }

.cs-detail__top-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  z-index: 5;
}

.cs-detail__home {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  color: var(--ink, #111827);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.12em;
  cursor: pointer;
  transition: color 200ms ease, transform 200ms ease;
}

.cs-detail__home:hover {
  color: #71BD1F;
  transform: translateX(-3px);
}

.cs-detail__total {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}

.cs-detail__total strong {
  font-size: 28px;
  font-weight: 800;
  color: var(--ink, #111827);
  line-height: 1;
  letter-spacing: -0.02em;
}

.cs-detail__total span {
  font-size: 9.5px;
  font-weight: 750;
  letter-spacing: 0.16em;
  color: var(--muted, #6b7280);
  margin-top: 4px;
}

.cs-detail__main-grid {
  flex: 1;
  display: grid;
  grid-template-columns: 340px 1fr 300px;
  gap: 24px;
  align-items: center;
  min-height: 0;
}

/* Left Column */
.cs-detail__left {
  display: flex;
  flex-direction: column;
  gap: 12px;
  z-index: 3;
}

.cs-detail__idx {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted, #6b7280);
}

.cs-detail__idx-num {
  font-weight: 800;
  color: var(--ink, #111827);
}

.cs-detail__oswald-title {
  margin: 0;
  font-family: 'Oswald', var(--display), sans-serif;
  font-size: 34px;
  font-weight: 600;
  text-transform: uppercase;
  line-height: 1.15;
  color: var(--ink, #111827);
  letter-spacing: 0.02em;
}

.cs-detail__held-box {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.cs-detail__held-num {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: 'Open Sans', var(--font), sans-serif;
}

.cs-detail__held-num strong {
  font-size: 26px;
  font-weight: 800;
  color: var(--ink, #111827);
}

.cs-detail__held-num span {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted, #6b7280);
}

.cs-detail__accent-bar {
  width: 140px;
  height: 2.5px;
  border-radius: 2px;
  background: #71BD1F;
}

/* Center Stage: Focal Main Logo + Falling-from-Sky Background Logo */
.cs-detail__center-stage {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Secondary Background Badge (Diagonal Falling from Top-Right towards Bottom-Left) */
.cs-detail__bg-float {
  position: absolute;
  top: 6%;
  right: 12%;
  width: 170px;
  height: 170px;
  opacity: 0.35;
  filter: blur(5px);
  pointer-events: none;
  z-index: 1;
}

.cs-sky-fall-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  animation: cs-fall-from-sky 750ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes cs-fall-from-sky {
  0% {
    opacity: 0;
    transform: translate(180px, -200px) scale(1.5) rotate(16deg);
    filter: blur(24px);
  }
  65% {
    opacity: 0.45;
    transform: translate(-14px, 14px) scale(0.95) rotate(-3deg);
    filter: blur(8px);
  }
  100% {
    opacity: 0.35;
    transform: translate(0, 0) scale(1) rotate(0deg);
    filter: blur(5px);
  }
}

.cs-detail__main-badge {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}

.cs-detail__halo-ring {
  position: absolute;
  inset: -35px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(113, 189, 31, 0.35) 0%, rgba(113, 189, 31, 0) 70%);
  filter: blur(18px);
  animation: cs-halo 3s ease-in-out infinite alternate;
}

.cs-detail__halo-ring--outer {
  inset: -75px;
  background: radial-gradient(circle, rgba(113, 189, 31, 0.18) 0%, rgba(113, 189, 31, 0) 70%);
  filter: blur(28px);
}

@keyframes cs-halo { from { opacity: 0.5; transform: scale(0.96); } to { opacity: 0.95; transform: scale(1.08); } }

.cs-detail__badge-img {
  width: 320px;
  height: 320px;
  object-fit: contain;
  filter: drop-shadow(0 26px 45px rgba(0, 0, 0, 0.16));
  animation: cs-float-breathe 6s ease-in-out infinite alternate;
  will-change: transform, filter, opacity;
}

@keyframes cs-float-breathe {
  0% {
    transform: translateY(0px) scale(1);
  }
  50% {
    transform: translateY(-8px) scale(1.03);
  }
  100% {
    transform: translateY(0px) scale(1);
  }
}

.cs-morph-img {
  animation: cs-soft-blur-focus 650ms cubic-bezier(0.16, 1, 0.3, 1) forwards, cs-float-breathe 6s ease-in-out infinite alternate 650ms;
}

@keyframes cs-soft-blur-focus {
  0% {
    opacity: 0;
    transform: scale(0.85) translateY(16px);
    filter: blur(14px);
  }
  50% {
    opacity: 0.9;
    transform: scale(1.04) translateY(-4px);
    filter: blur(4px);
  }
  100% {
    opacity: 1;
    transform: scale(1) translateY(0);
    filter: blur(0px);
  }
}

/* Right Column: Skills Unlocked (Pure Text List, No Card Box Layouts) */
.cs-detail__right {
  display: flex;
  flex-direction: column;
  gap: 16px;
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
}

.cs-detail__skills-head {
  margin: 0 0 4px;
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: #71BD1F;
}

.cs-detail__skills-ol {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 16px;
  background: transparent !important;
}

.cs-detail__skills-ol li {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  font-family: 'Open Sans', var(--font), sans-serif;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--ink, #111827);
  padding: 0 !important;
  border: none !important;
  box-shadow: none !important;
  background: transparent !important;
  animation: cs-skill-rise 500ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(var(--i) * 60ms);
}

.cs-detail__skills-ol li::after {
  display: none !important;
}

@keyframes cs-skill-rise {
  from { opacity: 0; transform: translateX(18px); filter: blur(4px); }
  to { opacity: 1; transform: translateX(0); filter: blur(0); }
}

@keyframes cs-skill-rise {
  from { opacity: 0; transform: translateX(18px); filter: blur(4px); }
  to { opacity: 1; transform: translateX(0); filter: blur(0); }
}

@keyframes cs-skill-rise {
  from { opacity: 0; transform: translateX(14px); }
  to { opacity: 1; transform: translateX(0); }
}

.cs-detail__skill-num {
  font-size: 11px;
  font-weight: 800;
  color: #71BD1F;
}

.cs-detail__skill-txt {
  flex: 1;
}

/* Bottom Right Nav Controls */
.cs-detail__nav-bar {
  position: absolute;
  bottom: 12px;
  right: 24px;
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 10;
}

.cs-detail__nav-btn {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: none;
  border: 1px solid rgba(0, 0, 0, 0.15);
  color: var(--ink, #111827);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease, color 200ms ease;
}

.cs-detail__nav-btn:hover, .cs-detail__nav-btn.is-active {
  background: #71BD1F;
  border-color: #71BD1F;
  color: #ffffff;
}
.cs-diag__nav--next { right: 10px; }

/* Right Side: Skills Unlocked */
.cs-detail__right {
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--surface, #ffffff);
  border-radius: 16px;
  padding: 20px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.06);
  max-height: 380px;
  overflow-y: auto;
}

.cs-detail__skills-title {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--ink, #111827);
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.cs-detail__skills-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cs-detail__skills-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink, #111827);
}

.cs-detail__skills-list em {
  font-style: normal;
  font-size: 11px;
  font-weight: 800;
  color: #71BD1F;
  background: rgba(113, 189, 31, 0.12);
  padding: 2px 7px;
  border-radius: 6px;
  flex-shrink: 0;
}

/* The chosen credential: the badge floating over its own shadow, the caption
   beneath it, and what it tests down the right. */
.cs-skill { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: 26px; min-height: 0; }
.cs-hero { position: relative; grid-column: 1; display: grid; place-items: center; min-height: 0; }
.cs-hero__bloom {
  position: absolute; width: 420px; height: 420px; border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, #C9B6F5 55%, transparent) 0%,
    color-mix(in srgb, #BED3F5 22%, transparent) 42%, transparent 70%);
  animation: cs-bloom 1900ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
@keyframes cs-bloom { 0% { opacity: 0; transform: scale(0.72) } 30% { opacity: 0.95 } 100% { opacity: 0.22; transform: scale(1.06) } }
.cs-hero__shadow {
  position: absolute; bottom: 16%; width: 40%; height: 26px; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(42, 44, 86, 0.26) 0%, rgba(42, 44, 86, 0) 70%);
  filter: blur(9px); animation: cs-contact 12s ease-in-out infinite;
}
@keyframes cs-contact { 0%, 100% { opacity: 0.85; transform: scale(1) } 50% { opacity: 0.5; transform: scale(0.86) } }
.cs-hero__art { position: relative; display: grid; place-items: center; animation: cs-breathe 12s ease-in-out infinite; }
@keyframes cs-breathe { 0%, 100% { transform: translateY(0) scale(1) } 50% { transform: translateY(-12px) scale(1.036) } }
.cs-hero__art img {
  max-width: 300px; max-height: 300px; width: auto; height: auto; display: block;
  filter: drop-shadow(0 34px 46px rgba(40, 42, 80, 0.17)) drop-shadow(0 4px 10px rgba(40, 42, 80, 0.06));
}
.cs-hero__art em { font-style: normal; font-size: 64px; font-weight: 800; color: var(--muted); }

.cs-cap { grid-column: 1; align-self: end; padding: 0 6px 4px; }
.cs-cap__no, .cs-cap__vendor {
  margin: 0 0 12px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.19em;
  text-transform: uppercase; color: var(--muted); font-variant-numeric: tabular-nums;
  animation: cs-mono 1200ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.cs-cap__no i { font-style: normal; color: var(--nav-accent-ink, var(--accent-ink)); }
.cs-cap__vendor { color: var(--nav-second-ink, var(--accent-ink)); animation-delay: 100ms; }
@keyframes cs-mono { from { opacity: 0; letter-spacing: 0.34em; transform: translateY(8px) } }
.cs-cap__name {
  margin: 0 0 12px; font-size: clamp(24px, 2.5vw, 38px); font-weight: 800;
  line-height: 1.1; letter-spacing: -0.026em;
}
/* Each word rises out of its own mask, so the name unfurls instead of fading. */
.cs-w {
  display: inline-block; overflow: hidden; vertical-align: bottom;
  padding: 0.06em 0.04em 0.16em 0; margin: 0 0.2em -0.16em 0;
}
.cs-w > span {
  display: inline-block;
  animation: cs-word 1250ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(180ms + var(--i, 0) * 52ms);
}
@keyframes cs-word { from { transform: translate3d(0, 112%, 0) rotate(2.2deg) } }
.cs-cap__domain {
  margin: 0 0 18px; color: var(--muted); font-size: 15px; font-style: italic;
  animation: cs-rise 1300ms 360ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.cs-cap__rule {
  display: block; height: 1px; max-width: 330px; background: var(--hairline, #e5e7eb);
  transform-origin: left; margin-bottom: 14px;
  animation: cs-rule 1500ms 420ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
@keyframes cs-rule { from { transform: scaleX(0); opacity: 0 } }
.cs-cap__held { margin: 0; display: flex; align-items: baseline; gap: 10px;
  animation: cs-rise 1300ms 500ms cubic-bezier(0.16, 1, 0.3, 1) backwards; }
.cs-cap__held b { font-size: 30px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; }
.cs-cap__held span { color: var(--muted); font-size: 12.5px; }

.cs-learn { grid-column: 2; grid-row: 1 / -1; align-self: center; }
.cs-learn__head {
  margin: 0 0 22px; color: var(--nav-second-ink, var(--accent-ink));
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.19em; text-transform: uppercase;
  animation: cs-mono 1200ms 240ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.cs-learn__list { list-style: none; margin: 0; padding: 0; }
.cs-learn__list li { position: relative; padding: 13px 0 13px 34px; }
.cs-learn__list li::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: var(--hairline, #e5e7eb); transform-origin: left;
  animation: cs-rule 1100ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(360ms + var(--i, 0) * 95ms);
}
.cs-learn__list em {
  position: absolute; left: 0; top: 15px; font-style: normal; font-size: 9.5px; font-weight: 800;
  letter-spacing: 0.14em; color: var(--nav-second-ink, var(--accent-ink)); opacity: 0.6;
  animation: cs-mono 1000ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(400ms + var(--i, 0) * 95ms);
}
.cs-learn__mask { display: block; overflow: hidden; padding-bottom: 0.18em; margin-bottom: -0.18em; }
.cs-learn__mask > span {
  display: block; font-size: 13px; line-height: 1.5;
  animation: cs-word 1150ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(430ms + var(--i, 0) * 95ms);
}

/* ======================================================= 03 THE GALLERY === */
.cs-gal { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 24px; height: 100%; padding: 22px 30px 24px; }
.cs-galside { display: flex; flex-direction: column; min-height: 0; }
.cs-galhead__pre { display: block; font-size: clamp(17px, 1.7vw, 26px); font-style: italic; color: var(--muted); line-height: 1; margin-bottom: 6px; }
.cs-galhead__name { margin: 0; font-size: clamp(28px, 3.2vw, 46px); font-weight: 800; line-height: 1.02; letter-spacing: -0.03em; }
.cs-galhead__meta { margin: 16px 0 0; color: var(--muted); font-size: 12.5px; }
.cs-galhead__rule {
  display: block; height: 1px; background: var(--hairline, #e5e7eb); margin: 22px 0 18px;
  transform-origin: left; animation: cs-rule 1300ms 300ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.cs-rail { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 7px 8px; overflow-y: auto; }
.cs-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 12px; border-radius: 100px;
  border: 1px solid var(--hairline, #e5e7eb); background: none; color: var(--muted); cursor: pointer;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  transition: color 400ms ease, border-color 400ms ease, background 400ms ease;
  animation: cs-rise 800ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(var(--i, 0) * 40ms);
}
.cs-chip:hover { color: var(--ink, inherit); border-color: color-mix(in srgb, var(--nav-accent, #008638) 45%, transparent); }
.cs-chip.is-on {
  color: #fff; background: var(--nav-accent, #008638);
  border-color: var(--nav-accent, #008638);
}
.cs-chip__glyph { display: grid; place-items: center; opacity: 0.8; }
.cs-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 150px; }
.cs-chip__n { font-variant-numeric: tabular-nums; opacity: 0.7; }

/* Three columns running continuously, the middle one against the other two. The
   set is duplicated and the track travels exactly half its height, so the loop
   closes on itself — the gap rides on each tile's margin, not on a flex gap, or
   the seam lands one gap short. */
.cs-cols {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.cs-col { position: relative; overflow: hidden; }
.cs-col__track {
  will-change: transform;
  animation: cs-scroll var(--dur, 50s) linear infinite;
  animation-direction: var(--dir, normal);
}
@keyframes cs-scroll { from { transform: translateY(0) } to { transform: translateY(-50%) } }
.cs-col:hover .cs-col__track { animation-play-state: paused; }

.cs-tile {
  position: relative; display: block; width: 100%; margin: 0 0 14px; padding: 0;
  border: 1px solid color-mix(in srgb, var(--surface) 75%, transparent);
  border-radius: 10px; overflow: hidden; background: var(--surface); cursor: pointer;
  box-shadow: 0 12px 26px rgba(40, 42, 80, 0.08);
  transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 600ms ease;
}
.cs-tile:hover { transform: translateY(-3px); box-shadow: 0 22px 40px rgba(40, 42, 80, 0.18); }
.cs-tile--empty { cursor: default; box-shadow: none; }
.cs-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.cs-tile__tag {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 24px 11px 10px; text-align: left;
  color: #fff; background: linear-gradient(to top, rgba(16, 17, 22, 0.66), transparent);
  opacity: 0; transform: translateY(6px);
  transition: opacity 450ms ease, transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
.cs-tile__tag em {
  font-style: normal; font-size: 11px; font-weight: 750; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.cs-tile__tag span {
  display: block; font-size: 9px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--brand-accent, #FFBB00);
}
.cs-tile__zoom {
  position: absolute; top: 8px; right: 8px; display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 7px;
  background: var(--brand-accent, #FFBB00); color: #1a1a1a;
  opacity: 0; transform: scale(0.86);
  transition: opacity 300ms ease, transform 300ms ease;
}
.cs-tile:hover .cs-tile__tag, .cs-tile:focus-visible .cs-tile__tag { opacity: 1; transform: translateY(0); }
.cs-tile:hover .cs-tile__zoom, .cs-tile:focus-visible .cs-tile__zoom { opacity: 1; transform: none; }

/* ---------------------------------------------------------- the lightbox -- */
.cs-light {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 3vh 3vw;
  background: rgb(9 11 14 / 0.92);
  opacity: 0; transition: opacity 220ms ease;
}
.cs-light.is-on { opacity: 1; }
.cs-light[hidden] { display: none; }
.cs-light__frame {
  margin: 0; display: flex; flex-direction: column; gap: 12px;
  max-width: calc(100% - 160px); max-height: 100%;
  transform: scale(0.97); transition: transform 260ms cubic-bezier(.2,.7,.3,1);
}
.cs-light.is-on .cs-light__frame { transform: none; }
.cs-light__img {
  display: block; max-width: 100%; max-height: calc(100vh - 14vh);
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--radius); background: #111;
}
.cs-light__foot { display: flex; width: 100%; align-items: baseline; gap: 12px; color: #e5e7eb; }
.cs-light__cap { margin: 0; font-size: 15px; font-weight: 700; }
.cs-light__meta { color: #9ca3af; font-size: 12px; font-variant-numeric: tabular-nums; }
.cs-light__count {
  margin-left: auto; color: var(--brand-accent, #FFBB00);
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.cs-light__close {
  position: absolute; top: 18px; right: 20px;
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 0; border-radius: 999px; background: rgb(255 255 255 / 0.12);
  color: #fff; cursor: pointer; transition: background 160ms ease;
}
.cs-light__close:hover { background: rgb(255 255 255 / 0.22); }
.cs-light__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 56px; height: 56px;
  border: 0; border-radius: 999px; background: rgb(255 255 255 / 0.12);
  color: #fff; cursor: pointer; transition: background 160ms ease, transform 160ms ease;
}
.cs-light__nav:hover { background: color-mix(in srgb, var(--nav-accent, #008638) 62%, transparent); }
.cs-light__nav:active { transform: translateY(-50%) scale(0.94); }
.cs-light__nav[hidden] { display: none; }
.cs-light__nav .ic { width: 28px; height: 28px; }
.cs-light__nav--prev { left: 22px; }
.cs-light__nav--next { right: 22px; }

@media (prefers-reduced-motion: reduce) {
  .cs-arc__base, .cs-arc__glow, .cs-arc__line, .cs-arc__pulse,
  .cs-pin__in, .cs-pin__bob, .cs-hero__art, .cs-hero__bloom, .cs-hero__shadow,
  .cs-fig, .cs-quote, .cs-card, .cs-chip, .cs-w > span, .cs-learn__list li::before,
  .cs-learn__list em, .cs-learn__mask > span, .cs-cap__no, .cs-cap__vendor,
  .cs-cap__domain, .cs-cap__rule, .cs-cap__held, .cs-galhead__rule,
  .cs-reg__back img, .cs-col__track { animation: none; }
  .cs-tile, .cs-tile__tag, .cs-tile__zoom, .cs-step, .cs-card,
  .cs-card__art, .cs-card__n, .cs-pin__art, .cs-pin__tag { transition: none; }
  .cs-tile:hover, .cs-drift { transform: none; }
  .cs-light, .cs-light__frame, .cs-light__nav { transition: none; }
}

/* ==========================================================================
   Leadership Journey — the process arc
   The slide opens as the arc and its titles. Clicking a segment brings that
   chapter's card in; the ones already open stay put, so the picture builds.
   The band, the titles and the card anchors are all solved from one circle in
   the component — this file styles them and never positions them.
   ========================================================================== */
.ja-root {
  --ja-ink: #0d1a12;
  --ja-band: #14251a;
  --ja-lime: var(--brand-highlight, #71bd1f);
  --ja-gold: var(--brand-accent, #ffbb00);
  --ja-paper: #f4f6f2;
  position: relative;
  display: block;
  /* `.canvas-block > *` sets flex: 1, and its flex-basis: 0% beats height on the main axis
     — hence flex: 0 0 auto, which restores an auto basis so the height below is honoured.
     100% of the block rather than the deck's 860: presenting, the slide is 1600x900 and those
     last 40 rows showed as a white band under the ground. The floor keeps the arc whole if a
     block ever hands down an indefinite height, where a percentage would collapse. */
  flex: 0 0 auto;
  height: 100%;
  min-height: 860px;
  /* Edge to edge. No radius and no padding, so the ground runs to the end of the slide
     instead of sitting on it as a rounded panel with paper showing around it. */
  border-radius: 0;
  padding: 0;
  background: var(--ja-paper);
  color: var(--ja-ink);
  overflow: hidden;
}

/* Over the top-left, where the arc leaves room. */
.ja-title {
  position: absolute;
  top: 30px;
  left: 38px;
  z-index: 3;
  margin: 0;
  font-size: 46px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  pointer-events: none;
}

.ja-stage {
  /* The arc is solved in a 1600x860 viewBox and the cards are placed as percentages of the
     stage, so the stage has to hold that aspect or the two drift apart as the ground grows.
     It centres in whatever height the ground has, and the ground fills the rest itself. */
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  aspect-ratio: 1600 / 860;
}

.ja-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* ------------------------------------------------------------------ the band */
.ja-seg {
  fill: var(--ja-band);
  cursor: pointer;
  transform-box: fill-box;
  transform-origin: center;
  animation: ja-seg-in 0.62s cubic-bezier(0.22, 0.9, 0.16, 1) both;
  animation-delay: calc(var(--i, 0) * 0.09s);
  transition: fill 0.34s ease, filter 0.34s ease;
}

.ja-seg:hover {
  fill: #1c3524;
}

/* An opened chapter keeps its colour, so the band itself records how far the presenter has
   got without any numbering to read. */
.ja-seg.is-open {
  fill: var(--brand-primary, #008638);
  filter: drop-shadow(0 6px 18px rgba(0, 134, 56, 0.34));
}

@keyframes ja-seg-in {
  from { opacity: 0; transform: scale(0.94); }
  to { opacity: 1; transform: none; }
}

.ja-seg__label {
  fill: #ffffff;
  cursor: pointer;
  font-family: var(--display);
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  animation: ja-fade 0.5s ease both;
  animation-delay: calc(0.24s + var(--i, 0) * 0.09s);
}

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

/* ---------------------------------------------------------------- the leaders
   A dotted run from the band to the card it belongs to, so a card reads as that chapter's own
   note rather than as loose paper near it. Aimed in the component, which knows where the card
   was seated; drawn under the band, so the end that starts inside it is covered. */
.ja-lead {
  stroke: color-mix(in srgb, var(--brand-primary, #008638) 62%, transparent);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-dasharray: 0.1 11;
  opacity: 0;
}

/* The dots run outward from the band to the card, arriving just ahead of it. */
.ja-lead.is-in {
  animation: ja-lead-in 0.52s cubic-bezier(0.22, 0.9, 0.16, 1) both;
}

@keyframes ja-lead-in {
  from { opacity: 0; stroke-dashoffset: 46; }
  to { opacity: 1; stroke-dashoffset: 0; }
}

/* ----------------------------------------------------------------- the cards
   Hidden until their segment is clicked, then they stay. Turned to the segment they belong
   to, so the content leans the way the band does. */
.ja-card {
  position: absolute;
  z-index: 2;
  /* One size for all five. Matches CARD in the component, which fits each chapter's words to
     this box — the height is fixed and the text gives, rather than five cards each as tall as
     its own paragraph. */
  width: 252px;
  height: 322px;
  padding: 15px 17px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--ja-ink) 12%, transparent);
  border-radius: 14px;
  background: #ffffff;
  box-shadow: 0 14px 34px rgba(13, 26, 18, 0.13);
  color: var(--ja-ink);
  text-align: left;
  /* Height comes from the content: nothing is clipped and nothing is cut off mid-sentence,
     which is what a max-height was doing before. */
  transform: translate(-50%, -50%) rotate(var(--tilt, 0deg)) scale(0.82);
  opacity: 0;
  pointer-events: none;
}

.ja-card[hidden] { display: none; }

/* The one just opened sits above any it overlaps. */
.ja-card.is-last { z-index: 4; }

/* Arriving: it grows out of the band with a little overshoot and settles, the shadow
   deepening as it comes forward — one motion rather than a fade. The rotation is held
   through the whole animation so the card never straightens up on the way in. */
.ja-card.is-in {
  animation: ja-card-in 0.66s cubic-bezier(0.22, 1.3, 0.32, 1) both;
}

@keyframes ja-card-in {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(var(--tilt, 0deg)) scale(0.8);
    box-shadow: 0 2px 6px rgba(13, 26, 18, 0.06);
    filter: blur(3px);
  }
  55% {
    opacity: 1;
    filter: blur(0);
  }
  72% {
    transform: translate(-50%, -50%) rotate(var(--tilt, 0deg)) scale(1.035);
  }
  100% {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(var(--tilt, 0deg)) scale(1);
    box-shadow: 0 14px 34px rgba(13, 26, 18, 0.13);
    filter: blur(0);
  }
}

/* The lines arrive after the card that holds them, in reading order. */
.ja-card.is-in .ja-card__role {
  animation: ja-line-in 0.42s cubic-bezier(0.22, 0.9, 0.16, 1) 0.2s both;
}

.ja-card.is-in .ja-card__list li {
  animation: ja-line-in 0.42s cubic-bezier(0.22, 0.9, 0.16, 1) both;
  animation-delay: calc(0.3s + var(--n, 0) * 0.08s);
}

@keyframes ja-line-in {
  from { opacity: 0; transform: translateX(10px); }
  to { opacity: 1; transform: none; }
}

.ja-card__role {
  display: block;
  margin-bottom: 8px;
  color: var(--brand-primary, #008638);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ja-card__list {
  display: grid;
  gap: 7px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ja-card__list li {
  position: relative;
  padding-left: 14px;
  font-size: 12px;
  line-height: 1.45;
}

.ja-card__list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.56em;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--ja-lime);
}

.ja-root--empty {
  display: grid;
  place-items: center;
  color: color-mix(in srgb, var(--ja-ink) 55%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .ja-seg,
  .ja-seg__label,
  .ja-lead,
  .ja-lead.is-in,
  .ja-card,
  .ja-card.is-in,
  .ja-card.is-in .ja-card__role,
  .ja-card.is-in .ja-card__list li {
    animation: none;
    transition: none;
  }

  .ja-card.is-in {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(var(--tilt, 0deg));
  }

  .ja-lead.is-in {
    opacity: 1;
    stroke-dashoffset: 0;
  }
}


/* ==========================================================================
   Photo ring — cards standing on a turntable
   ==========================================================================
   Cards placed around a vertical axis and seen in perspective. The ring holds
   on each card and then turns by exactly one, so there is always a subject
   rather than a drift. Placement, idle turn and hover are on three different
   elements because one `transform` cannot carry all three.
   ========================================================================== */
.pr-root {
  position: relative;
  display: flex; flex-direction: column;
  /* Never a bare 860: a slide in presentation mode is the screen's shape, not
     16:9, so a 16:10 display hands this section 1000 rows and a root pinned to
     860 stops 140 short. Presenting pins it with !important further down; this
     is what the collapsed view and the admin canvas read. */
  /* A definite height, not a minimum, and `flex: 0 0 auto` with it:
     `.canvas-block > *` sets `flex: 1`, whose `flex-basis: 0%` beats `height`
     on the main axis. Without this the root collapsed to its content the moment
     this block went full-bleed, and the fan sat at the top of a slide with a
     band of white under it — the same trap `.fd-root`, `.tb-root` and
     `.ev-root` each carry a note about. */
  flex: 0 0 auto;
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  /* The head is gone, so the top padding is the air above the titles and
     nothing more — the rows it used to take belong to the photographs. */
  padding: 22px 30px 0;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, var(--nav-accent-soft, rgb(233 90 34 / 0.10)) 0%, transparent 62%),
    var(--page-bg, #F8FAFC);
}

.pr-head { flex: 0 0 auto; text-align: center; }
.pr-eyebrow {
  margin: 0 0 8px;
  font-size: 13px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--nav-accent, var(--accent));
}
.pr-title {
  margin: 0;
  font-family: var(--display);
  font-size: 54px; line-height: 1.02; letter-spacing: -0.015em;
  color: var(--text);
}
.pr-lead {
  margin: 12px auto 0; max-width: 780px;
  font-size: 15px; line-height: 1.55; color: var(--text-muted, #4B5563);
}

/* --- the filters ---------------------------------------------------------- */
.pr-tabs {
  flex: 0 0 auto;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
  margin: 20px 0 0;
}
.pr-tab {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 18px;
  border: 1px solid rgb(11 18 32 / 0.14); border-radius: 999px;
  background: var(--surface, #fff); color: var(--text);
  font-family: inherit; font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
  cursor: pointer;
  transition: background 200ms ease, color 200ms ease, border-color 200ms ease, transform 200ms ease;
  animation: pr-tab-in 460ms cubic-bezier(.2, .7, .3, 1) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}
.pr-tab:hover { transform: translateY(-1px); border-color: rgb(11 18 32 / 0.3); }
.pr-tab.is-on {
  background: var(--nav-accent, var(--accent));
  /* `--nav-on-accent`, not `--nav-accent-ink`: the two are easy to confuse and
     only one is right here. The *ink* is a darkened shade of the brand colour,
     for brand-coloured text on a light ground; on the accent fill itself it is
     the same hue twice and measures 1.5:1. The *on-accent* is the one the deck
     picks for contrast against that fill — white on Torii's orange, dark on
     Technical Hub's gold — which is what a pill filled with the brand colour
     needs. */
  color: var(--nav-on-accent, var(--on-accent, #fff));
  border-color: transparent;
}
/* The count rides inside the chip, so a filter says how much is behind it
   before it is pressed. */
.pr-tab__n {
  min-width: 18px; padding: 1px 6px; border-radius: 999px;
  background: rgb(11 18 32 / 0.08);
  font-size: 11px; font-weight: 700; text-align: center;
}
.pr-tab.is-on .pr-tab__n { background: rgb(255 255 255 / 0.22); }
@keyframes pr-tab-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* The perspective lives on the stage, not the ring: put it on the element that
   is itself rotating and the vanishing point rotates with it, which flattens the
   whole effect at a quarter turn. `perspective-origin` sits above centre so the
   ring is read slightly from above, the way the reference is drawn. */
.pr-stage {
  flex: 1 1 auto; position: relative;
  display: grid; place-items: center;
  perspective: 1500px;
  perspective-origin: 50% 42%;
}

.pr-ring {
  position: relative;
  width: 1010px; height: 650px;
  transform-style: preserve-3d;
}

.pr-slot {
  position: absolute; top: 0; left: 50%;
  width: 1010px; margin-left: -505px;
  display: block; padding: 0; border: 0; background: none;
  transform-style: preserve-3d;
  cursor: pointer;
  /* Each card carries its own move, because they do not travel together: on a
     step the front card leaves to one side while the next arrives from the
     other, and both are the same eased transition running on different values.
     Eased at both ends so a step starts and settles rather than snapping — this
     is the whole of the "one by one" motion. */
  transition:
    transform var(--turn, 1100ms) cubic-bezier(.34, .08, .18, 1),
    opacity var(--turn, 1100ms) ease;
  will-change: transform;
}

.pr-card {
  position: relative;
  display: flex; flex-direction: column; gap: 10px;
  /* Resting size. Cards sit back a little so that pointing at one is a change
     rather than the whole ring already being at full size. Each step further
     round the ring takes a little more off, to a floor, so depth reads as depth
     and the back of the ring never disappears entirely. */
  transform: scale(calc(0.86 - min(var(--away, 0), 3) * 0.045));
  opacity: calc(1 - min(var(--away, 0), 3) * 0.2);
  transition: transform 420ms cubic-bezier(.2, .7, .3, 1), opacity 420ms ease;
  will-change: transform;
}
.pr-slot.is-front .pr-card { transform: scale(1); opacity: 1; }
/* Pointed at, a card comes up past full size. The ring has already stopped and
   brought it to the front by this point, so this is the only growth left. */
.pr-slot:hover .pr-card,
.pr-slot:focus-visible .pr-card { transform: scale(1.12); opacity: 1; }

/* The entrance, replayed whenever the filter changes. The cards are rebuilt on a
   swap, so the animation runs again by itself — no class to add and remove, and
   no chance of it being left on. The stagger runs along the fan, so a set
   arrives card by card rather than appearing all at once. */
.pr-card__in {
  animation: pr-card-in 640ms cubic-bezier(.2, .7, .3, 1) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes pr-card-in {
  from { opacity: 0; transform: translateY(26px) scale(0.9); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* One height for every card, width from the picture's own ratio — so the fan
   stands on a single line and no photograph is letterboxed into a frame that is
   not its shape. Capped, or a panorama would reach across the whole slide. */
.pr-card__frame {
  position: relative;
  /* Bigger, because the fan now walks itself and the photographs are the
     whole of the slide (2026-09-18, on request). 300 -> 430 tall, and the cap
     follows so a 16:9 picture is not clipped back to the old width. */
  height: 600px; width: auto; aspect-ratio: var(--ar, 16 / 9);
  max-width: 1010px;
  margin: 0 auto;
  border-radius: 16px; overflow: hidden;
  background: #0D1117;
  box-shadow: 0 26px 54px -22px rgb(11 18 32 / 0.55);
  transition: box-shadow 420ms ease;
}
.pr-slot.is-front .pr-card__frame { box-shadow: 0 38px 76px -26px rgb(11 18 32 / 0.62); }
/* Contained, never cropped — the deck's rule everywhere else, and these are
   designed cards whose own titles would be the first thing a crop cut off. */
.pr-card__img {
  display: block; width: 100%; height: 100%;
  object-fit: contain;
}
.pr-card__label {
  margin: 0; text-align: center;
  font-size: 13px; font-weight: 600; letter-spacing: -0.005em;
  color: var(--text);
  /* Only the front card names itself. Six labels around a ring is a list of
     captions nobody can read, and it competes with the headline. */
  opacity: 0; transition: opacity 320ms ease;
}
.pr-slot.is-front .pr-card__label { opacity: 1; }

.pr-empty {
  flex: 1 1 auto; display: grid; place-content: center; gap: 10px; text-align: center;
}
.pr-hint { margin: 0; font-size: 13px; color: var(--text-muted, #4B5563); }

@media (prefers-reduced-motion: reduce) {
  /* No turning at all, and no growth on hover — the fan is then a still
     arrangement that can still be filtered and clicked through. JS already
     refuses to start the timer under the same query; this covers the rest. */
  .pr-slot, .pr-card, .pr-card__frame, .pr-card__label, .pr-tab { transition: none; }
  .pr-slot:hover .pr-card, .pr-slot:focus-visible .pr-card { transform: scale(1); }
  /* The swap still has to *show*, or a filter press looks like nothing happened
     — so it keeps a plain fade and loses the movement and the blur. */
  .pr-card__in { animation: pr-card-in-still 200ms ease both; animation-delay: 0ms; }
  .pr-tab { animation: none; }
}
@keyframes pr-card-in-still {
  from { opacity: 0; }
  to   { opacity: 1; }
}


/* ==========================================================================
   Event orbit — a ring of events you turn with your hand
   ==========================================================================
   Panels standing at every angle, seen in perspective. Drag across to turn it,
   up and down to tip it, click a card to open that event's photographs. The
   ground is the deck's own page colour: every other section in the deck stands
   on it, and a black panel in the middle of them read as a different product
   rather than as a different section.
   ========================================================================== */
.eo-root {
  position: relative;
  display: flex; flex-direction: column;
  /* `.canvas-block > *` sets `flex: 1`, whose `flex-basis: 0%` beats `height` on
     the main axis — without a basis of its own this root collapses to its
     content, which for a ring whose cards are absolutely positioned is almost
     nothing: it measured 220px inside an 848px slide and the ring was clipped
     against the top edge. Every other full-bleed root in the deck pins the
     basis the same way. */
  flex: 0 0 auto;
  height: 860px;
  padding: 30px 30px 0;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 0%, var(--nav-accent-soft, rgb(233 90 34 / 0.10)) 0%, transparent 62%),
    var(--page-bg, #F8FAFC);
  color: var(--text);
}

.eo-title {
  margin: 0;
  font-family: var(--display);
  font-size: 52px; line-height: 1.02; letter-spacing: -0.015em;
  color: var(--text);
}

/* The perspective is on the stage, not the ring: put it on the element that is
   itself rotating and the vanishing point rotates with it, which flattens the
   whole thing a quarter turn in. */
.eo-stage {
  flex: 1 1 auto; position: relative;
  display: grid; place-items: center;
  /* Deeper than the fan's, because a closed ring is twice as long front to back
     and a short perspective blows the near cards up past the frame. */
  /* Deep. At 1700 the near card was magnified 1.46x and ran past the bottom of
     the panel; this holds it to about 1.3x, which still reads as depth. */
  perspective: 2200px;
  /* Dead centre. Lifting the vanishing point above the middle pushes the whole
     ring visibly down the panel and leaves the top third empty. */
  perspective-origin: 50% 50%;
  cursor: grab;
  touch-action: none;   /* the drag is ours, not the browser's scroll */
  outline: none;
}
.eo-stage.is-held { cursor: grabbing; }

.eo-ring {
  position: relative;
  width: 300px; height: 190px;
  transform-style: preserve-3d;
  will-change: transform;
}

.eo-card {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  padding: 0; border: 0; background: none;
  transform-style: preserve-3d;
  cursor: pointer;
  /* The far half of the ring is turned away from the viewer, and drawing it
     means drawing the backs of those cards — which renders their photographs
     and their names mirrored. Reversed type reads as a fault, not as depth, so
     a card facing away is not drawn at all. `--away` still dims the ones
     turning through the sides, so the ring keeps its falloff. */
  backface-visibility: hidden;
  opacity: calc(1 - var(--away, 0) * 0.72);
  transition: opacity 180ms linear;
  outline: none;
}
.eo-card__shot {
  position: relative; display: block;
  width: 300px; height: 168px;
  border-radius: 10px; overflow: hidden;
  background: #0d1117;
  box-shadow: 0 22px 46px -20px rgb(11 18 32 / 0.5);
  /* A hairline edge so a pale photograph does not dissolve into the page. */
  border: 1px solid rgb(11 18 32 / 0.1);
  transition: border-color 200ms ease, box-shadow 200ms ease, transform 260ms cubic-bezier(.2,.7,.3,1);
}
.eo-card__img { display: block; width: 100%; height: 100%; object-fit: cover; }
.eo-card:hover .eo-card__shot,
.eo-card:focus-visible .eo-card__shot {
  border-color: var(--nav-accent, #E95A22);
  box-shadow: 0 30px 58px -18px rgb(11 18 32 / 0.6), 0 0 0 3px rgb(233 90 34 / 0.3);
  transform: translateZ(26px) scale(1.04);
}
.eo-card__foot {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 4px 0;
}
.eo-card__name {
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.01em;
  color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.eo-card__n {
  flex: 0 0 auto;
  min-width: 18px; padding: 1px 6px; border-radius: 999px;
  background: rgb(11 18 32 / 0.08);
  font-size: 10.5px; font-weight: 700; color: var(--text-muted, #4B5563);
}

.eo-empty { flex: 1 1 auto; display: grid; place-content: center; gap: 10px; text-align: center; }
.eo-hint { margin: 0; font-size: 13px; color: var(--text-muted, #4B5563); }

@media (prefers-reduced-motion: reduce) {
  /* No idle drift — the JS refuses to start its loop under the same query, so
     the ring is a still arrangement that can still be dragged and clicked. */
  .eo-card, .eo-card__shot { transition: none; }
  .eo-card:hover .eo-card__shot, .eo-card:focus-visible .eo-card__shot { transform: none; }
}


/* ==========================================================================
   Sections must not be shorter than their slide
   ==========================================================================
   A slide in presentation mode is not 16:9 — it is the *screen's* shape. FitSlide
   gives the slide `1600 × (screenHeight / screenWidth)` nominal rows, so a 16:10
   display hands the section 1000 rows where the admin canvas has 860.

   Seven section roots carry a definite `height: 860px`, and they need it:
   `.canvas-block > *` sets `flex: 1`, whose `flex-basis: 0%` beats `height` on the
   main axis, so without a basis of their own they grow to their content and the
   whole slide is scaled down to fit. What is right at 860 is wrong at 1000 — the
   section stops short and leaves a dead band the room reads as the page failing
   to reach the bottom. It is invisible to FitSlide, which correctly reports the
   *slide* as filled; the gap is inside it.

   A percentage cannot do it. FitSlide takes its measurement with the slide's
   height cleared — it has to, or it would be reading back its own answer — and a
   percentage of nothing is `auto`, so the root grows to its full content during
   exactly the pass that decides whether to fill. Placements measured 1483 rows
   that way and gave up filling altogether.

   So FitSlide publishes the number instead, as `--slide-h` on the slide, and
   these roots take it as a definite length. It is only set while the slide is
   filling; unfilled, each root falls back to the 860 it always had. And because
   the property is cleared before the measurement for the same reason the height
   is, the value can never feed back into the decision that produced it.
   ========================================================================== */
body.is-presenting .fit-slide__inner .sw-root,
body.is-presenting .fit-slide__inner .pg-root,
body.is-presenting .fit-slide__inner .tw-root,
body.is-presenting .fit-slide__inner .ev-root,
body.is-presenting .fit-slide__inner .vr-root,
body.is-presenting .fit-slide__inner .cs-root,
body.is-presenting .fit-slide__inner .pw-root,
body.is-presenting .fit-slide__inner .sk-root,
body.is-presenting .fit-slide__inner .tt-root,
body.is-presenting .fit-slide__inner .cf-root,
body.is-presenting .fit-slide__inner .ts-root,
body.is-presenting .fit-slide__inner .pc-root,
body.is-presenting .fit-slide__inner .ew-root,
body.is-presenting .fit-slide__inner .fd-root,
body.is-presenting .fit-slide__inner .ps-root,
body.is-presenting .fit-slide__inner .tb-root,
body.is-presenting .fit-slide__inner .pr-root,
body.is-presenting .fit-slide__inner .eo-root,
body.is-presenting .fit-slide__inner .aa-root {
  /* !important because the per-section rules use it, and !important beats
     specificity however deep this selector is. */
  height: var(--slide-h, 860px) !important;
  min-height: var(--slide-h, 860px) !important;
  max-height: var(--slide-h, 860px) !important;
}

/* ==========================================================================
   Clearance for the deck bar
   ==========================================================================
   The bar floats over the slide. Over a full-bleed photograph that is the design
   and it carries its own scrim; over a card's caption, a credential's name or a
   student's tile it is just content covered up. These sections reserve the bar's
   own height at the bottom — the same 104px the wall of centres and the
   testimonial wall already reserve — which the section's ground still paints
   through, so the page reaches the edge and only the content steps back.
   ========================================================================== */
/* The bar's real height plus a little air, converted into the slide's own
   pixels. Written once here so no section has to know the arithmetic. */
body.is-presenting .fit-slide__inner {
  --deck-bar-clear: calc(96px / var(--slide-scale, 1));
}
body.is-presenting .fit-slide__inner .cs-root,
body.is-presenting .fit-slide__inner .vr-root,
body.is-presenting .fit-slide__inner .ev-root,
body.is-presenting .fit-slide__inner .pw-root,
body.is-presenting .fit-slide__inner .sw-root,
body.is-presenting .fit-slide__inner .pf-root,
/* The accordion's captions sit at the foot of each panel, which is the second
   kind of content the bar covers: a caption, not a full-bleed photograph that
   carries its own scrim. */
body.is-presenting .fit-slide__inner .aa-root {
  padding-bottom: var(--deck-bar-clear);
}
/* The ribbon takes the same clearance on its stage rather than on its root. Its
   stage is absolutely positioned, and an absolutely positioned child resolves
   against the padding box — which includes the padding — so padding on the root
   would move nothing at all. See `.sk-stage`. */
/* The head can give up some of its padding once the bar has taken a strip off
   the bottom — the deck between them is what has a card in it. */
body.is-presenting .fit-slide__inner .sw-head {
  padding-top: 16px;
}

/* ==========================================================================
   Executive Summary hero — presentation placement
   Scoped to the right-aligned full-bleed hero, which is the only one in the
   deck. Each rule is here rather than edited into the base so the original
   treatment is one deletion away.
   ========================================================================== */

/* Controls to the top corner. They sat bottom-right, which is where the
   paragraph now ends; the top is empty and clears the presenter bar outright. */
.hero__vc .vc,
body.is-presenting .hero__vc .vc {
  top: 22px;
  bottom: auto;
}

/* The copy sits on the floor of the frame rather than its middle, so the film
   carries the upper two thirds and the words close the slide. */
.slide--hero .hero--right {
  justify-content: flex-end;
}

/* And the floor is above the presenter bar, not under it. Sitting on the hero's
   own 44px padding, the last line of the paragraph ran straight through the
   bar — measured at 739 x 39px of overlap on all three decks, which is the
   second kind of collision the bar makes: a full-bleed photograph behind the
   bar is the design and carries its own scrim, a sentence behind it is a
   sentence nobody can read. The bar is 83 *real* pixels whatever the slide is
   scaled to, which is why the clearance is the scaled figure and not a flat
   one. */
body.is-presenting .fit-slide__inner .slide--hero .hero--right {
  padding-bottom: var(--deck-bar-clear);
}

/* A deeper panel on the reading side. The old ramp never dropped below 0.3, so
   it greyed the whole picture to light the text; this one is heavier where the
   words are and clears completely on the left, which keeps the film clean. */
.hero--right .hero__scrim {
  background: linear-gradient(270deg,
    rgba(0, 0, 0, 0.96) 0%,
    rgba(0, 0, 0, 0.92) 34%,
    rgba(0, 0, 0, 0.74) 56%,
    rgba(0, 0, 0, 0.38) 76%,
    rgba(0, 0, 0, 0) 100%) !important;
}

/* A wider measure: the paragraph was turning at 48rem and running to seven
   lines. */
.hero--right .hero__content {
  max-width: 64rem;
}

/* The paragraph carries its own 48rem cap, and the column shrinks to its widest
   child — so widening the column alone did nothing. This is the rule that
   actually lets the line run. */
.hero--right .hero__subheading {
  max-width: 64rem;
}

/* --------------------------------------------------------------------------
   NCET palette — Executive Summary hero only
   Orange, navy and white only. Green is reserved and used solely on request.
   Navy is absent here by necessity, not by choice: it is the brand ground
   colour and measures 1.19:1 on this dark hero — invisible. That leaves white
   to carry the name and orange to carry the accent, which is how the site
   itself sets white headings over navy with orange for anything active.
   Scoped to .hero so the rest of the deck is untouched until we convert it.
   -------------------------------------------------------------------------- */

/* The name in white. 12.63:1 — the strongest reading on this backdrop. */
.hero .text-brand-green {
  color: #FFFFFF !important;
}

/* NCET Orange at full strength. 5.06:1, fine at 52px. A hero that sets
   `--hero-accent` — measured off its own organization's mark — uses that
   instead; the value here is the default the two decks written first depend
   on, not a rule. */
.hero .text-brand-gold,
.hero .text-brand-green-line {
  color: var(--hero-accent, #F6872A) !important;
}

/* At 21px the NCET brand sheet calls for the lighter orange: "#FFB070 — orange
   text on navy grounds, where #F6872A loses contrast". 3.06:1 against 2.20:1.
   A hero with its own accent keeps it at both sizes rather than taking NCET's
   lightened one — Torii's #F05D29 measures 5.25:1 against its own black ground
   and has nothing to fix. */
.hero__subheading .text-brand-gold,
.hero__subheading .text-brand-green {
  color: var(--hero-accent, #FFB070) !important;
}

/* The rule under the year stays inside the orange family. */
.hero .text-brand-green-line::after {
  background: linear-gradient(90deg, #F6872A 0%, #FFB070 100%) !important;
}

/* ==========================================================================
   Governance council — a 2x2 wall of faces
   Each card is split: portrait on the left, name and role on the right, with
   no overlap between them. Card grounds alternate on the diagonal so the grid
   still reads as a checkerboard. Palette is orange, navy and white only.
   ========================================================================== */

/* The wall must be allowed to fill the slide. `.canvas-block > * { flex: 1 }`
   gives its child flex-basis 0, which beats a declared height, so without this
   the block collapses to its content and the slide letterboxes. The hero has
   the same override for the same reason. */
.slide--hero .canvas-block--council-grid {
  height: 100% !important;
  min-height: var(--slide-h, 860px) !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.council {
  --council-navy: #0E2455;
  --council-tint: #EAF0FF;
  --council-orange: #F6872A;
  /* One readable derivative of NCET Orange for anything that is *text* on a
     light ground. Raw #F6872A measures 2.49:1 on white and 2.19:1 on the tint,
     under the 3.0 a display line needs; this measures 3.86 and 3.38. */
  --council-orange-ink: #D45F06;
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.council__head {
  padding: 14px 30px 10px;
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}

.council__kicker {
  margin: 0;
  /* 19px, so the label clears 18.66px bold and is judged as large text at 3.0
     rather than as body copy at 4.5. */
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--council-orange-ink);
}

.council__standfirst {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: rgba(14, 36, 85, 0.62);
}

.council__grid {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  /* No gap: the cards butt together, so the grid reads as one plane. */
  gap: 0;
  min-height: 0;
}

/* Portrait left, words right. No padding on the leading edge — the figure runs
   to the card edge rather than floating with air around it. */
.council-card {
  --card-bg: #FFFFFF;
  position: relative;
  overflow: hidden;
  background: var(--card-bg);
  display: flex;
  flex-direction: row;
  align-items: stretch;
  height: 100%;
  min-height: 0;
  animation: block-in 0.62s cubic-bezier(0.16, 1, 0.3, 1) both;
  /* One after another, in reading order. */
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms);
}

/* The diagonal: cards 2 and 3 carry the tint. */
.council-card:nth-child(2),
.council-card:nth-child(3) {
  --card-bg: var(--council-tint);
}

/* Every portrait is drawn into this one box, whatever it arrived as: `cover`
   crops the overflow and the top anchor keeps the head in frame, because these
   are portraits and a centred crop takes the forehead. */
.council-card__photo {
  flex: 0 0 auto;
  width: 44%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0%;
  display: block;
  /* The portraits are shot on white, so they sit on the white card with no
     seam; on the tinted card `multiply` drops the white away rather than
     leaving a pale rectangle around the figure. */
  mix-blend-mode: multiply;
  animation: council-rise 0.72s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms + 120ms);
}

@keyframes council-rise {
  from { opacity: 0; transform: translateY(30px) scale(1.02); }
  to   { opacity: 1; transform: none; }
}

.council-card__photo--empty {
  background: rgba(14, 36, 85, 0.06);
  border: 1px dashed rgba(14, 36, 85, 0.25);
  mix-blend-mode: normal;
}

.council-card__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 12px;
  padding: 20px 30px 20px 34px;
}

/* The seat number — the thing that tells the eye which card to read next. */
.council-card__index {
  font-size: clamp(14px, 1.15vw, 19px);
  font-weight: 800;
  letter-spacing: 0.22em;
  color: var(--council-orange-ink);
  animation: block-in 0.5s cubic-bezier(0.22, 0.68, 0, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms + 260ms);
}

.council-card__name {
  margin: 0;
  display: flex;
  flex-direction: column;
  line-height: 0.96;
  font-weight: 900;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  font-size: clamp(20px, 2.9vw, 52px);
  animation: block-in 0.55s cubic-bezier(0.22, 0.68, 0, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms + 200ms);
}

/* Two-tone, as in the supplied reference: the given name in the dark neutral,
   the family name in the accent. Navy stands in for the reference's black —
   white would be invisible on these light cards, which is why the reference
   itself sets its first line dark. A one-line name stays navy rather than
   coming out entirely in accent. */
.council-card__name-line {
  color: var(--council-navy);
}

.council-card__name-line:not(:first-child) {
  color: var(--council-orange-ink);
}

/* The accent rule draws itself, as the rest of the deck does. */
.council-card__rule {
  width: 70px;
  height: 4px;
  border-radius: 99px;
  background: var(--council-orange-ink);
  transform-origin: left center;
  animation: council-draw 0.5s cubic-bezier(0.22, 0.68, 0, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms + 340ms);
}

@keyframes council-draw {
  from { transform: scaleX(0); opacity: 0; }
  to   { transform: scaleX(1); opacity: 1; }
}

.council-card__role {
  font-size: clamp(15px, 1.7vw, 29px);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--council-navy);
  line-height: 1.3;
  animation: block-in 0.5s cubic-bezier(0.22, 0.68, 0, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 170ms + 400ms);
}

@media (prefers-reduced-motion: reduce) {
  .council-card,
  .council-card__photo,
  .council-card__rule,
  .council-card__index,
  .council-card__name,
  .council-card__role {
    animation: none;
  }
}

/* ==========================================================================
   Slide title card
   A section with an `intro` opens on its title: the words fade up, hold, and
   fade away, and only then does the page behind them arrive. Set the field on
   any section and it gets this; leave it empty and the page arrives at once,
   which is what every other section still does.
   ========================================================================== */

/* The whole card, start to finish. Every entrance animation on an intro slide
   adds this to its own delay, so one number moves the entire sequence and the
   page can never begin before the title has left. */
/* Was 3s, and every entrance on an intro slide added it to its own delay so
   the page could not begin before the card had left. It is 0 now (2026-09-17)
   because the page is not *built* until the card is on its way out — see
   `SlideView`, which mounts the canvas at about two-thirds of the card's run.
   The two approaches solve the same problem, and only the second one works for
   everything: a CSS delay is honoured only by the entrances written to read it,
   and a JS arrival — an IntersectionObserver reveal, a rAF ease-in — fires the
   moment its element exists, which under an opaque card is to nobody. That is
   what "I can't see the entrance animation" was: the arrivals were playing out
   behind the card and were over when it lifted. Mounting late, every kind of
   entrance starts as the card goes, whether or not it knows about the card.
   The variable stays, at 0, so the `calc(var(--intro-hold, 0s) + …)` delays
   already written keep their own staggers. */
.slide--intro {
  --intro-hold: 0s;
}

/* Laid over the slide, not in it: an in-flow title card would add height and
   could flip FitSlide out of filling the display. */
.slide-intro {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  background: #FFFFFF;
  pointer-events: none;
  /* 0.9s up, held to 1.9s, gone by 2.5s. `both` so it is invisible from the
     first painted frame rather than flashing at full opacity before the
     animation takes hold. */
  animation: slide-intro-card 3s cubic-bezier(0.22, 0.68, 0, 1) both;
}

.slide-intro__text {
  /* A starting value only — fitToWidth measures the text and scales it so the
     title fills the card whatever its length. */
  font-size: 120px;
  font-weight: 900;
  letter-spacing: -0.025em;
  line-height: 1.02;
  text-align: center;
  text-transform: uppercase;
  /* The default, for a deck with no palette of its own. Each organization
     overrides the pair below. */
  color: #000000;
  /* One line, so the fit can size to the full width instead of wrapping. */
  white-space: nowrap;
  padding: 0 4%;
}

/* ---- the card in each deck's own colours (2026-09-17, on request) ---------
   `applyTheme` stamps `data-org` on the root, so the card can be branded
   without a class on the slide or a field on the section. Both are the
   organization's own two colours and nothing else: the ground is deep so the
   card reads as a cut to black between two bright slides, the way a title card
   in a film does, and the type is the colour that names the deck.

   The ground is set on `.slide-intro` rather than on the text's parent because
   the card is `position: absolute; inset: 0` over the whole slide — it has to
   cover the page arriving underneath it, or the two are seen at once. */

/* White ground on both decks (2026-09-17, second cut, on request: "no
   background colour, white background and colour for the text"). The first cut
   set each card on the deck's own dark colour; what was wanted is the deck's
   colour *as the type*, on white. */
.slide-intro { background: #FFFFFF; }

/* NGI: its own green. `--nav-accent-ink` is the palette's readable derivative
   of the lead colour, which for NGI is the green the logo leads with. */
[data-org='technical-hub'] .slide-intro__text { color: var(--nav-accent-ink, #008638); }

/* Torii: its orange. #F05D29 is the value measured off the gate in the brand
   film, not the older #E95A22 in the palette table. On white it is 3.4:1, which
   is above the 3:1 large-text floor, and this type is 160px. */
[data-org='torii'] .slide-intro__text { color: #F05D29; }

/* While the card is up the page under it has not been built yet (see
   `SlideView`), so the slide has to hold its height on its own or the card,
   which is `inset: 0` of the slide, is a strip a few pixels tall. `--slide-h`
   is the screen's own height while presenting and filling; 860 is the canvas. */
.slide--intro.is-holding { min-height: var(--slide-h, 860px); }


/* The card itself only needs to be opaque, then not. Holding full opacity to
   76% and clearing by the end is what lets the page behind it start moving
   underneath while the card is still on its way out. */
@keyframes slide-intro-card {
  0%   { opacity: 1; }
  76%  { opacity: 1; }
  100% { opacity: 0; visibility: hidden; }
}

}


/* Reduced motion: the title card is the whole point of the slide's opening, so
   it still appears and still clears — it simply does so without travel, and
   quickly. */
@media (prefers-reduced-motion: reduce) {
  .slide--intro {
    --intro-hold: 0s;
  }

  .slide-intro {
    animation: slide-intro-card 1.2s linear both;
  }

  .slide-intro__text {
    animation: none;
  }
}




/* ==========================================================================
   Roster — a roll of names as a bento wall
   The card follows the supplied infographic: a coloured bar down the leading
   edge, the seat number set large beside it, the mark opposite, then the
   person — name in the bar's colour, position beneath in grey.
   Palette is orange, navy and white only.
   ========================================================================== */

/* Allowed to fill the slide, the same override the hero and the council wall
   carry: `.canvas-block > * { flex: 1 }` gives its child flex-basis 0, which
   beats a declared height. */
.slide--hero .canvas-block--roster {
  height: 100% !important;
  min-height: var(--slide-h, 860px) !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.roster {
  --roster-navy: #0E2455;
  --roster-tint: #EAF0FF;
  --roster-orange: #F6872A;
  --roster-orange-ink: #D45F06;
  --roster-line: #E9EDF4;
  --roster-chip: #F3F5F9;
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* The tiles are white, so the wall behind them is not. */
  background: #F7F9FC;
  padding: 16px 26px 18px;
}

.roster__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--roster-line);
}

.roster__kicker {
  margin: 0;
  /* 19px, so the label clears 18.66px bold and is judged as large text at 3.0
     rather than as body copy at 4.5. */
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--roster-orange-ink);
}

.roster__standfirst {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: rgba(14, 36, 85, 0.62);
}

/* One module, repeated. Equal columns and equal rows, so no card is a
   different size from any other. */
.roster__grid {
  flex: 1 1 auto;
  min-height: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-auto-rows: 1fr;
  gap: 16px;
  align-content: stretch;
}

/* ------------------------------------------------------------------ card */
.rcard {
  --bar: var(--roster-orange-ink);
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* The reference's card is not flat white but lifts very slightly to a cool
     grey at its foot. */
  background: #FFFFFF;
  border-radius: 28px;
  overflow: hidden;
  /* Even padding now the bar has gone, and more of it: the crowding was the
     bar pushing the copy into two thirds of the card. */
  padding: 20px 24px 16px;
  box-shadow: 0 1px 2px rgba(14, 36, 85, 0.03), 0 6px 18px rgba(14, 36, 85, 0.045);
  transition: transform 0.34s cubic-bezier(0.16, 1, 0.3, 1),
              box-shadow 0.34s cubic-bezier(0.16, 1, 0.3, 1);
  /* Cinematic entrance: slow, eased, and one card at a time. `backwards`, not
     `both` — a filled animation keeps applying its final transform after it
     ends and an animation beats a hover rule in the cascade, which would stop
     the card lifting on hover. */
  animation: rcard-in 1s cubic-bezier(0.33, 1, 0.68, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 145ms);
}

/* The reference gives every row its own hue. Three colours only here, so the
   bar alternates down the reading order — enough to separate one card from the
   next without inventing a fourth. */
.rcard--navy {
  --bar: var(--roster-navy);
}

/* Slow, soft and from below, with the focus pulling in — the card settles
   rather than snapping into place. */
@keyframes rcard-in {
  from {
    opacity: 0;
    transform: translateY(26px) scale(0.965);
    filter: blur(7px);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

.rcard:hover {
  transform: translateY(-5px);
  box-shadow: 0 2px 4px rgba(14, 36, 85, 0.05), 0 16px 34px rgba(14, 36, 85, 0.12);
}

.rcard__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 13px;
}

/* "No." over the numeral, as the reference sets "Option" over its number. */
.rcard__index {
  display: flex;
  flex-direction: column;
  line-height: 1;
}

.rcard__indexLabel {
  font-size: clamp(9px, 0.68vw, 12px);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(14, 36, 85, 0.38);
  margin-bottom: 3px;
}

.rcard__indexNo {
  font-size: clamp(22px, 2.1vw, 36px);
  font-weight: 300;
  letter-spacing: -0.02em;
  color: rgba(14, 36, 85, 0.22);
}

/* The institution's mark, in a ring. The reference puts a line icon here; the
   brief is the mark and nothing else. */
.rcard__mark {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 1px solid #F1F4F9;
  background: #FFFFFF;
  display: grid;
  place-items: center;
  overflow: hidden;
  flex: 0 0 auto;
}

.rcard__mark img {
  width: 78%;
  height: 78%;
  object-fit: contain;
  display: block;
}

/* The person, picked out in the bar's colour — the reference's coloured lead-in
   to a grey paragraph. */
.rcard__name {
  flex: 0 0 auto;
  margin: 0;
  /* 21, not 23: at 23 the longest name ("Dr. K N Balasubramanya Murthy")
     broke to a third line in a 297px card and pushed the position text out of
     the box. Two points buys the line back. */
  font-size: clamp(15px, 1.22vw, 21px);
  font-weight: 800;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: var(--bar);
}

/* The person's own position, set quieter than the name: it is context, not
   the entry itself. */
.rcard__where {
  flex: 0 1 auto;
  min-height: 0;
  overflow: hidden;
  margin: 6px 0 0;
  font-size: clamp(10px, 0.88vw, 15px);
  font-weight: 500;
  line-height: 1.38;
  color: rgba(14, 36, 85, 0.55);
}

/* The seat is the whole footing — the hairline already says a new register
   begins, so it needs no label beside it. */
.rcard__foot {
  flex: 0 0 auto;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid #F1F4F9;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* Outlined rather than solid, so the bar and the name carry the card's colour
   and the footing does not compete with them. */
.rcard__seat {
  flex: 0 1 auto;
  max-width: 100%;
  white-space: nowrap;
  font-size: clamp(9px, 0.7vw, 12px);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bar);
  background: transparent;
  border: 1px solid currentColor;
  opacity: 0.78;
  border-radius: 999px;
  padding: 6px 12px;
  text-align: center;
  line-height: 1.15;
}

/* ------------------------------------------------------- the note tile */
/* The one dark block in a white grid, closing the last row. */
.rnote {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  min-height: 0;
  border-radius: 26px;
  padding: 22px 28px;
  background: var(--roster-navy);
  box-shadow: 0 1px 2px rgba(14, 36, 85, 0.04), 0 10px 30px rgba(14, 36, 85, 0.12);
  animation: rcard-in 1s cubic-bezier(0.33, 1, 0.68, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 145ms);
}

.rnote__text {
  margin: 0;
  font-size: clamp(13px, 1.12vw, 20px);
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: 0.01em;
  color: #FFFFFF;
}

@media (prefers-reduced-motion: reduce) {
  .rcard,
  .rnote {
    animation: none;
  }

  .rcard {
    transition: none;
  }
}

/* ==========================================================================
   Hub — a chooser of doors onto the pages beneath this one
   The supplied design: a bracket of brand colour behind one corner, a 45°
   chamfer cut from the opposite foot, a giant outlined numeral bled off the
   side, a badge, and the label revealed letter by letter.
   ========================================================================== */

.slide--hero .canvas-block--hub {
  height: 100% !important;
  min-height: var(--slide-h, 860px) !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.hub {
  --navy: #0E2455;
  --navy-light: #15306A;
  --navy-grad-end: #0C1F49;
  --orange: #F6872A;
  --orange-ink: #D45F06;
  --card-a: #FFFFFF;
  --card-b: #F2F4F7;
  --r: 30px;
  --r-accent: 46px;
  --chamfer: 30px;
  --band: 19px;
  --hub-ease: cubic-bezier(0.16, 1, 0.3, 1);
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: #F3F5F9;
  padding: 18px 30px 22px;
}

.hub__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  margin-bottom: 6px;
}

.hub__kicker {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--orange-ink);
}

.hub__standfirst {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: rgba(14, 36, 85, 0.62);
}

/* The brackets sit outside their cards, so the grid needs room around each
   cell for them to show. */
.hub__grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 54px 62px;
  padding: 26px 30px;
  align-content: stretch;
}

/* ------------------------------------------------------------------ cell */
/* The cell owns the shadow so it follows the chamfer rather than the box. */
.hub-door {
  position: relative;
  min-height: 0;
  cursor: pointer;
  filter: drop-shadow(0 16px 24px rgba(14, 36, 85, 0.14));
  transition: filter 0.55s var(--hub-ease);
}

.hub-door:hover,
.hub-door:focus-visible {
  filter: drop-shadow(0 26px 38px rgba(14, 36, 85, 0.22));
  outline: none;
}

/* ------------------------------------------------------------- bracket */
.hub-door__accent {
  position: absolute;
  width: 56%;
  height: 62%;
  background: linear-gradient(135deg, var(--navy-light), var(--navy-grad-end));
  opacity: 0;
  transition: opacity 0.75s var(--hub-ease), transform 0.75s var(--hub-ease),
              top 0.45s var(--hub-ease), left 0.45s var(--hub-ease),
              right 0.45s var(--hub-ease), bottom 0.45s var(--hub-ease);
}

.hub-door[data-corner="tl"] .hub-door__accent {
  top: calc(var(--band) * -1); left: calc(var(--band) * -1);
  border-radius: var(--r-accent) 0 0 0;
  transform-origin: top left; transform: scale(0.7);
}
.hub-door[data-corner="tr"] .hub-door__accent {
  top: calc(var(--band) * -1); right: calc(var(--band) * -1);
  border-radius: 0 var(--r-accent) 0 0;
  transform-origin: top right; transform: scale(0.7);
}
.hub-door[data-corner="bl"] .hub-door__accent {
  bottom: calc(var(--band) * -1); left: calc(var(--band) * -1);
  border-radius: 0 0 0 var(--r-accent);
  transform-origin: bottom left; transform: scale(0.7);
}
.hub-door[data-corner="br"] .hub-door__accent {
  bottom: calc(var(--band) * -1); right: calc(var(--band) * -1);
  border-radius: 0 0 var(--r-accent) 0;
  transform-origin: bottom right; transform: scale(0.7);
}

.hub-door[data-corner="tl"]:hover .hub-door__accent { top: -27px; left: -27px; }
.hub-door[data-corner="tr"]:hover .hub-door__accent { top: -27px; right: -27px; }
.hub-door[data-corner="bl"]:hover .hub-door__accent { bottom: -27px; left: -27px; }
.hub-door[data-corner="br"]:hover .hub-door__accent { bottom: -27px; right: -27px; }

/* ---------------------------------------------------------------- card */
/* Three rounded corners and one 45-degree chamfer, cut with a mask so the
   cell's drop-shadow follows the cut instead of the box. */
.hub-door__card {
  position: relative;
  z-index: 1;
  height: 100%;
  min-height: 0;
  padding: 30px 32px;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: linear-gradient(150deg, var(--card-a) 42%, var(--card-b));
  opacity: 0;
  transform: translateY(30px) scale(0.975);
  transition: transform 0.6s var(--hub-ease), opacity 0.6s var(--hub-ease);
}

.hub-door[data-chamfer="br"] .hub-door__card {
  border-radius: var(--r) var(--r) 0 var(--r);
  -webkit-mask-image: linear-gradient(315deg, transparent var(--chamfer), #000 0);
          mask-image: linear-gradient(315deg, transparent var(--chamfer), #000 0);
}
.hub-door[data-chamfer="bl"] .hub-door__card {
  border-radius: var(--r) var(--r) var(--r) 0;
  -webkit-mask-image: linear-gradient(45deg, transparent var(--chamfer), #000 0);
          mask-image: linear-gradient(45deg, transparent var(--chamfer), #000 0);
}

.hub-door:hover .hub-door__card { transform: translateY(-9px); }

/* ------------------------------------------------------------- numeral */
/* No webfont: the deck runs with no internet at presentation time, so the
   reference's Anton falls back to whatever condensed face is on the machine. */
.hub-door__numeral {
  position: absolute;
  top: 50%;
  font-family: Anton, Impact, Haettenschweiler, "Arial Narrow Bold", sans-serif;
  font-size: clamp(150px, 17vw, 250px);
  line-height: 0.78;
  color: transparent;
  -webkit-text-stroke: 1.4px rgba(14, 36, 85, 0.48);
  pointer-events: none;
  user-select: none;
  z-index: 0;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 1.05s var(--hub-ease), -webkit-text-stroke-color 0.45s var(--hub-ease);
}

@supports not (-webkit-text-stroke: 1px #000) {
  .hub-door__numeral { color: rgba(14, 36, 85, 0.13); }
}

.hub-door[data-side="left"] .hub-door__numeral { left: 14px; transform: translateY(-50%) skewX(-9deg); }
.hub-door[data-side="right"] .hub-door__numeral { right: -6px; transform: translateY(-50%) skewX(-9deg); }
.hub-door:hover .hub-door__numeral { -webkit-text-stroke-color: rgba(246, 135, 42, 0.75); }

/* --------------------------------------------------------------- badge */
/* The institution's mark, not a per-card line icon — the rule this deck keeps
   on every other card. */
.hub-door__badge {
  position: absolute;
  top: 24px;
  width: 60px; height: 60px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: #FFFFFF;
  box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.92), 0 10px 20px -8px rgba(14, 36, 85, 0.35);
  z-index: 3;
  opacity: 0;
  transform: scale(0.4) rotate(-25deg);
  transition: opacity 0.6s var(--hub-ease), transform 0.6s var(--hub-ease),
              box-shadow 0.45s var(--hub-ease);
}

.hub-door__badge img { width: 76%; height: 76%; object-fit: contain; display: block; }
.hub-door[data-icon="right"] .hub-door__badge { right: 28px; }
.hub-door[data-icon="left"] .hub-door__badge { left: 28px; }

.hub-door:hover .hub-door__badge {
  transform: scale(1.09) rotate(-8deg);
  box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.92), 0 16px 28px -8px rgba(246, 135, 42, 0.75);
}

.hub-door__badge::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--orange);
  opacity: 0;
}

.hub-door:hover .hub-door__badge::after { animation: hub-ring 1.5s var(--hub-ease) infinite; }

@keyframes hub-ring {
  0% { opacity: 0.5; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.75); }
}

/* --------------------------------------------------------------- label */
.hub-door__content { position: relative; z-index: 2; width: 58%; text-align: right; }
.hub-door[data-side="left"] .hub-door__content { margin-left: auto; }
.hub-door[data-side="right"] .hub-door__content { margin-right: auto; }

.hub-door__label {
  margin: 0;
  font-size: clamp(16px, 1.7vw, 26px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--navy);
  line-height: 1.28;
}

.hub-door__bar {
  display: block;
  height: 2px; width: 0;
  margin: 14px 0 0 auto;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--orange));
  transition: width 0.5s var(--hub-ease);
}

.hub-door:hover .hub-door__bar { width: 82px; }

/* ------------------------------------------------- per-letter reveal */
.hub-door__jit { display: inline-block; }
.hub-door__word { display: inline-block; white-space: nowrap; margin-right: 0.28em; }

.hub-door__ltr {
  display: inline-block;
  transform: translateZ(0) scale(2);
  opacity: 0;
  filter: blur(20px);
  /* The reset is instant, never reversed. */
  transition-property: none;
  transition-duration: 0ms;
}

/* ------------------------------------------------------------ arrival */
/* Everything waits out the slide's title card, then the doors deal in. */
.hub-door {
  animation: hub-cell-in 0.01s linear both;
  animation-delay: calc(var(--intro-hold, 0s) + var(--d, 0s));
}

@keyframes hub-cell-in { to { opacity: 1; } }

.hub-door.is-in .hub-door__accent {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--d) + 0.08s);
}

.hub-door.is-in .hub-door__card {
  opacity: 1;
  transform: none;
  transition: opacity 0.8s var(--hub-ease) var(--d), transform 0.8s var(--hub-ease) var(--d);
}

.hub-door.is-in .hub-door__numeral {
  clip-path: inset(0 0 0 0);
  transition-delay: calc(var(--d) + 0.16s), 0s;
}

.hub-door.is-in .hub-door__badge {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--d) + 0.3s), calc(var(--d) + 0.3s), 0s;
}

.hub-door.is-in .hub-door__ltr {
  opacity: 1;
  transform: translateZ(0) scale(1);
  filter: blur(0px);
  transition-property: opacity, transform, filter;
  transition-duration: 900ms;
  transition-timing-function: var(--hub-ease);
}

@media (prefers-reduced-motion: reduce) {
  .hub-door,
  .hub-door__accent,
  .hub-door__card,
  .hub-door__badge,
  .hub-door__numeral,
  .hub-door__ltr {
    transition: none !important;
    animation: none !important;
  }

  .hub-door__card,
  .hub-door__accent,
  .hub-door__badge { opacity: 1 !important; transform: none !important; }
  .hub-door__numeral { clip-path: none !important; }
  .hub-door__ltr { opacity: 1 !important; transform: none !important; filter: none !important; }
}

/* ==========================================================================
   Stat wall — a bento of figures, each with a drawing of itself
   The supplied reference's structure: a tinted panel carrying the drawing, the
   figure's name beneath, a line of context under that. Drawn in the deck's own
   orange, navy and white rather than the reference's blues.
   ========================================================================== */

.slide--hero .canvas-block--stat-wall {
  height: 100% !important;
  min-height: var(--slide-h, 860px) !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
  grid-row: 1 / -1 !important;
}

.statwall {
  --navy: #0E2455;
  --navy-soft: #1A3A5C;
  --orange: #F6872A;
  --orange-ink: #D45F06;
  --tint: #EAF0FF;
  --tint-2: #F3F6FD;
  --line: #E9EDF4;
  --sw-ease: cubic-bezier(0.16, 1, 0.3, 1);
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: #F7F9FC;
  padding: 16px 26px 14px;
}

.statwall__head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--line);
}

.statwall__kicker {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--orange-ink);
}

.statwall__standfirst {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: rgba(14, 36, 85, 0.62);
}

/* Six columns, so the reference's 2-up row and 3-up row both divide exactly. */
.statwall__grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: 1fr;
  gap: 16px;
  align-content: stretch;
}

.statwall__note {
  flex: 0 0 auto;
  margin: 12px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: clamp(12px, 1vw, 16px);
  font-weight: 700;
  color: var(--navy);
}

/* ------------------------------------------------------------------ tile */
.stat-tile {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: #FFFFFF;
  border: 1px solid var(--line);
  border-radius: 22px;
  padding: 14px 16px 16px;
  box-shadow: 0 1px 2px rgba(14, 36, 85, 0.03), 0 6px 18px rgba(14, 36, 85, 0.045);
  opacity: 0;
  transform: translateY(22px) scale(0.98);
  transition: transform 0.5s var(--sw-ease), box-shadow 0.4s var(--sw-ease);
  animation: sw-in 0.85s var(--sw-ease) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms);
}

@keyframes sw-in {
  from { opacity: 0; transform: translateY(22px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

.stat-tile.is-in { opacity: 1; transform: none; }

.stat-tile:hover {
  transform: translateY(-4px);
  box-shadow: 0 2px 4px rgba(14, 36, 85, 0.05), 0 14px 30px rgba(14, 36, 85, 0.12);
}

/* The tinted panel the drawing sits in — the reference's illustration well. */
.stat-tile__panel {
  flex: 1 1 auto;
  min-height: 0;
  border-radius: 16px;
  background: linear-gradient(160deg, var(--tint) 0%, var(--tint-2) 62%, #FFFFFF 100%);
  display: grid;
  place-items: center;
  padding: 12px;
  overflow: hidden;
  margin-bottom: 12px;
}

.stat-tile__title {
  margin: 0;
  font-size: clamp(14px, 1.2vw, 21px);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.18;
  color: var(--navy);
}

.stat-tile__caption {
  margin: 5px 0 0;
  font-size: clamp(10px, 0.86vw, 14px);
  font-weight: 500;
  line-height: 1.35;
  color: rgba(14, 36, 85, 0.55);
}

.stat-viz { width: 100%; height: 100%; display: grid; place-items: center; position: relative; }

/* -------------------------------------------------------------- count */
.stat-count { position: relative; z-index: 2; }

.stat-count__no {
  font-size: clamp(44px, 6vw, 108px);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}

/* Two rings that breathe, permanently — the loop the brief asks for. Slow and
   low-contrast, so it reads as life in the tile rather than as a distraction
   on a slide someone is talking over. */
.stat-count__ring {
  position: absolute;
  width: 156px; height: 156px;
  border-radius: 50%;
  border: 2px solid rgba(246, 135, 42, 0.32);
  animation: sw-breathe 4.2s ease-in-out infinite;
}

.stat-count__ring--2 {
  width: 202px; height: 202px;
  border-color: rgba(14, 36, 85, 0.14);
  animation-delay: -2.1s;
}

@keyframes sw-breathe {
  0%, 100% { transform: scale(0.94); opacity: 0.55; }
  50% { transform: scale(1.06); opacity: 1; }
}

/* --------------------------------------------------------------- bars */
.stat-viz--bars {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 26px;
  width: 100%;
  height: 100%;
  padding: 6px 8% 0;
}

.stat-bar {
  flex: 1 1 0;
  max-width: 132px;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-width: 0;
}

.stat-bar__no {
  font-size: clamp(14px, 1.3vw, 24px);
  font-weight: 800;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}

.stat-bar__track {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  background: rgba(14, 36, 85, 0.055);
  border-radius: 10px;
  overflow: hidden;
}

/* Grows to its share once the tile arrives, then a slow sheen runs up it. */
.stat-bar__fill {
  width: 100%;
  height: 0;
  border-radius: 10px;
  background: linear-gradient(180deg, var(--orange) 0%, var(--orange-ink) 100%);
  transition: height 1.05s var(--sw-ease);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 110ms + 260ms);
  position: relative;
  overflow: hidden;
}

.stat-tile.is-in .stat-bar__fill { height: var(--h, 0%); }

.stat-bar__fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 0%, rgba(255, 255, 255, 0.45) 50%, transparent 100%);
  transform: translateY(100%);
  animation: sw-sheen 3.6s ease-in-out infinite;
  animation-delay: calc(var(--j, 0) * 0.5s);
}

@keyframes sw-sheen {
  0% { transform: translateY(100%); }
  55%, 100% { transform: translateY(-100%); }
}

.stat-bar__label {
  font-size: clamp(9px, 0.72vw, 13px);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(14, 36, 85, 0.5);
  text-align: center;
  line-height: 1.2;
}

/* -------------------------------------------------------------- split */
.stat-viz--split {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 20px;
  width: 100%;
  padding: 0 6px;
}

.stat-split__rail {
  display: flex;
  width: 100%;
  height: 30px;
  border-radius: 99px;
  overflow: hidden;
  background: rgba(14, 36, 85, 0.055);
}

.stat-split__seg {
  width: 0;
  transition: width 1.05s var(--sw-ease);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 140ms + 260ms);
}

.stat-tile.is-in .stat-split__seg { width: var(--w, 0%); }

.stat-split__seg--0 { background: linear-gradient(90deg, var(--navy), var(--navy-soft)); }
.stat-split__seg--1 { background: linear-gradient(90deg, var(--orange), var(--orange-ink)); }

.stat-split__keys { display: flex; gap: 16px; flex-wrap: wrap; }
.stat-split__key { display: flex; align-items: center; gap: 8px; min-width: 0; }

.stat-split__dot {
  width: 12px; height: 12px;
  border-radius: 50%;
  flex: 0 0 auto;
  animation: sw-pip 3s ease-in-out infinite;
  animation-delay: calc(var(--j, 0) * 0.6s);
}

.stat-split__key--0 .stat-split__dot { background: var(--navy); }
.stat-split__key--1 .stat-split__dot { background: var(--orange); }

@keyframes sw-pip {
  0%, 100% { transform: scale(1); opacity: 0.8; }
  50% { transform: scale(1.28); opacity: 1; }
}

.stat-split__line { display: flex; align-items: baseline; gap: 5px; }

.stat-split__no {
  font-size: clamp(15px, 1.35vw, 24px);
  font-weight: 900;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.stat-split__pct {
  font-size: clamp(9px, 0.76vw, 13px);
  font-weight: 700;
  color: var(--orange-ink);
}

.stat-split__label {
  display: block;
  font-size: clamp(9px, 0.74vw, 12px);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(14, 36, 85, 0.5);
  line-height: 1.2;
}

/* -------------------------------------------------------------- ratio */
.stat-viz--ratio {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 12px;
}

.stat-ratio__value {
  font-size: clamp(28px, 3.4vw, 58px);
  font-weight: 900;
  letter-spacing: -0.03em;
  color: var(--navy);
  line-height: 1;
}

.stat-ratio__field { display: flex; align-items: center; gap: 10px; }

/* The one, against the field it teaches. */
.stat-ratio__one {
  width: 19px; height: 19px;
  border-radius: 50%;
  background: var(--orange);
  flex: 0 0 auto;
  animation: sw-pip 2.6s ease-in-out infinite;
}

.stat-ratio__dots {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 6px;
  max-width: 190px;
}

.stat-ratio__dot {
  width: 11px; height: 11px;
  border-radius: 50%;
  background: rgba(14, 36, 85, 0.28);
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.4s var(--sw-ease), transform 0.4s var(--sw-ease);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 45ms + 320ms);
}

.stat-tile.is-in .stat-ratio__dot { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .stat-tile,
  .stat-count__ring,
  .stat-bar__fill,
  .stat-bar__fill::after,
  .stat-split__seg,
  .stat-split__dot,
  .stat-ratio__one,
  .stat-ratio__dot {
    animation: none !important;
    transition: none !important;
  }

  .stat-tile { opacity: 1 !important; transform: none !important; }
  .stat-bar__fill { height: var(--h, 0%) !important; }
  .stat-split__seg { width: var(--w, 0%) !important; }
  .stat-ratio__dot { opacity: 1 !important; transform: none !important; }
}

/* ------------------------------------------------------------- donut */
/* A ring rather than a divided rail: a rail states a proportion, a ring shows
   it as an amount of a circle. The panel is wider than tall, so the ring takes
   the left and the figures stand beside it. */
.stat-viz--donut {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 2vw, 34px);
  width: 100%;
  padding: 0 6px;
}

.stat-donut {
  position: relative;
  flex: 0 0 auto;
  width: clamp(110px, 12vw, 178px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}

.stat-donut__svg { width: 100%; height: 100%; display: block; }

.stat-donut__track {
  stroke: rgba(14, 36, 85, 0.07);
  stroke-width: 15;
}

/* Swept round from nothing by dash length, in reading order. */
.stat-donut__seg {
  stroke-width: 15;
  stroke-linecap: butt;
  transform: rotate(-90deg);
  transform-origin: 60px 60px;
  stroke-dasharray: 0 999;
  stroke-dashoffset: var(--off, 0);
  transition: stroke-dasharray 1.1s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 240ms + 300ms);
}

.stat-tile.is-in .stat-donut__seg { stroke-dasharray: var(--len) var(--gap); }

.stat-donut__seg--0 { stroke: #0E2455; }
.stat-donut__seg--1 { stroke: #F6872A; }

/* The centre carries the whole the halves are shares of — the one number true
   of both, privileging neither. */
.stat-donut__hole {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  pointer-events: none;
}

.stat-donut__total {
  font-size: clamp(20px, 2.1vw, 36px);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1;
  color: #0E2455;
  font-variant-numeric: tabular-nums;
}

.stat-donut__cap {
  font-size: clamp(8px, 0.64vw, 11px);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(14, 36, 85, 0.42);
}

.stat-donut__keys {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.4vw, 20px);
  min-width: 0;
}

@media (prefers-reduced-motion: reduce) {
  .stat-donut__seg { transition: none !important; stroke-dasharray: var(--len) var(--gap) !important; }
}

/* ------------------------------------------------------------- rings */
/* Concentric rings — one per item, each an arc of the same whole, so two
   shares can be read against one another without following a single circle
   past a join. Geometry is the supplied component's: 250 / 18 / 8. */
.stat-viz--rings {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 2vw, 34px);
  width: 100%;
  padding: 0 6px;
}

.stat-rings {
  position: relative;
  flex: 0 0 auto;
  width: clamp(120px, 13.5vw, 196px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}

.stat-rings__svg { width: 100%; height: 100%; display: block; }

.stat-rings__track {
  stroke: rgba(14, 36, 85, 0.07);
  stroke-width: 18;
}

/* Swept round from nothing, outer ring first. */
.stat-rings__arc {
  stroke-width: 18;
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: 125px 125px;
  stroke-dasharray: 0 9999;
  transition: stroke-dasharray 1.15s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 260ms + 300ms);
}

.stat-tile.is-in .stat-rings__arc { stroke-dasharray: var(--len) var(--gap); }

.stat-rings__arc--0 { stroke: #0E2455; }
.stat-rings__arc--1 { stroke: #F6872A; }

@media (prefers-reduced-motion: reduce) {
  .stat-rings__arc { transition: none !important; stroke-dasharray: var(--len) var(--gap) !important; }
}

/* --------------------------------------------------------------- pie */
/* Hatched wedges rather than flat fills: the deck has two colours to spend,
   and two solid wedges of navy and orange say there is a split without saying
   which is which at the join. A ruled wedge against a barred one stays legible
   where they meet. */
.stat-viz--pie {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 2vw, 34px);
  width: 100%;
  padding: 0 6px;
}

.stat-pie {
  position: relative;
  flex: 0 0 auto;
  width: clamp(118px, 13vw, 190px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  /* A slow breath, the same loop the total's rings carry. */
  animation: sw-breathe-soft 5.4s ease-in-out infinite;
}

@keyframes sw-breathe-soft {
  0%, 100% { transform: scale(0.985); }
  50% { transform: scale(1.015); }
}

.stat-pie__svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* Each wedge grows out of the centre in turn. transform-box is what makes the
   origin resolve against the viewBox rather than the element's own bounds. */
.stat-pie__slice,
.stat-pie__edge {
  transform-box: view-box;
  transform-origin: 100px 100px;
  opacity: 0;
  transform: scale(0.25);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.75s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--intro-hold, 0s) + var(--i, 0) * 130ms + var(--j, 0) * 220ms + 320ms);
}

.stat-tile.is-in .stat-pie__slice,
.stat-tile.is-in .stat-pie__edge { opacity: 1; transform: none; }

/* The legend swatch carries the same hatching as its wedge. */
.stat-pie__swatch {
  width: 16px; height: 16px;
  border-radius: 4px;
  flex: 0 0 auto;
  border: 1.5px solid #0E2455;
  background-repeat: repeat;
}

.stat-pie__swatch--0 {
  border-color: #0E2455;
  background-image: repeating-linear-gradient(45deg, #0E2455 0 1.6px, transparent 1.6px 6px);
}

.stat-pie__swatch--1 {
  border-color: #F6872A;
  background-image: repeating-linear-gradient(0deg, #F6872A 0 1.6px, transparent 1.6px 6px);
}

.stat-pie__key--0,
.stat-pie__key--1 { display: flex; align-items: center; gap: 8px; min-width: 0; }

@media (prefers-reduced-motion: reduce) {
  .stat-pie { animation: none !important; }
  .stat-pie__slice,
  .stat-pie__edge {
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ==========================================================================
   Industry Alliances — the accordion of alliance photographs
   ==========================================================================
   Ported from the reactbits AccordionGallery stylesheet. The behaviour it
   describes is unchanged; what moved is where the values come from.

   The sketch hard-coded `#060010` for the scrim and `#ffffff` for the accent.
   Both are org tokens here, so the same section reads black-and-orange for
   Torii and green-and-gold for Technical Hub without a second stylesheet.

   The caption sits on the dark end of a scrim, which is the one ground the
   design rules allow the raw brand accent on: the gold that measures 1.7:1 on
   white is well clear of 4.5:1 over this.
   ========================================================================== */
.aa-root {
  --aa-gap: 12px;
  --aa-radius: var(--radius-lg);
  --aa-media: 320px;
  --aa-grow: 4;
  --aa-dur: 0.6s;
  --aa-stagger: 0.06s;
  /* power3.out. The sketch's default easing, as its cubic-bezier — the curve is
     the character of the whole interaction, so it is worth carrying over
     exactly rather than settling for `ease-out`. */
  --aa-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  /* The scrim's colour. Near-black with a little blue in it, so a photograph
     darkens towards ink rather than towards a flat grey. */
  --aa-shade: #0B0F14;

  display: flex;
  flex-direction: column;
  gap: 18px;
  /* Definite, and taken from the slide rather than written flat. `.canvas-block
     > *` sets `flex: 1`, whose `flex-basis: 0%` beats `height` on the main axis,
     so without this the root grows to its content and FitSlide scales the whole
     slide down to fit. 860 is the admin canvas; presenting publishes the real
     figure, which is the screen's shape and not 16:9. */
  height: 100%;
  min-height: 860px;
  max-height: 860px;
  padding: 30px 40px 34px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
}

/* Deliberately tight. Every row the head takes comes straight off the cards,
   which are the section — so it carries an eyebrow, a title and a rule, and
   stops. */
.aa-head { flex: 0 0 auto; text-align: center; padding-bottom: 2px; }

/* No rule under the heading, and none anywhere else in this section. Asked for
   directly: no accent underlines. The heading carries itself on type alone. */

/* The heading arrives the way the card captions do — same easing, same duration,
   a fade off a small offset — so the section reveals itself in one language
   rather than two. It rises rather than sliding in from the left, which is what
   a caption pinned to the left edge of a card does; on a centred heading the
   same sideways move reads as a drift. */
.aa-eyebrow,
.aa-title,
.aa-sub {
  animation: aa-reveal calc(var(--aa-dur, 0.6s) * 0.8) var(--aa-ease) backwards;
}
.aa-eyebrow { animation-delay: 0.04s; }
.aa-title   { animation-delay: 0.14s; }
.aa-sub     { animation-delay: 0.30s; }

@keyframes aa-reveal {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

.aa-eyebrow {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* The readable derivative, not the raw accent: this line is on white. */
  color: var(--accent-ink, var(--brand-accent));
}

.aa-title {
  margin: 0;
  font-family: var(--font-display, inherit);
  font-size: clamp(30px, 3vw, 48px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: 0.005em;
  color: var(--text);
}

.aa-sub {
  margin: 8px auto 0;
  max-width: 88ch;
  font-size: 16px;
  line-height: 1.5;
  color: var(--muted);
}

/* ------------------------------------------------------------------- the row
   The perspective lives here, on the shared parent, so every panel turns about
   the same vanishing point. Set on each panel instead, each one would get its
   own and the row would read as a strip of unrelated cards. */
.aa-row {
  flex: 1 1 auto;
  display: flex;
  flex-direction: row;
  gap: var(--aa-gap);
  min-height: 0;
  perspective: 1400px;
  perspective-origin: 50% 50%;
}

.aa-panel {
  position: relative;
  /* `flex-basis: 0` is what makes the grow weights read as shares of the row —
     with `auto`, the image's own width would be in the sum and the open panel
     would never measure the ratio it was solved for. */
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--aa-radius);
  cursor: pointer;
  display: block;
  text-decoration: none;
  outline: none;
  transform: rotateY(var(--aa-rot, 0deg));
  transform-style: preserve-3d;
  transform-origin: center center;
  background: var(--aa-shade);
  box-shadow: 0 14px 34px -20px rgba(0, 0, 0, 0.75);
  -webkit-tap-highlight-color: transparent;
  transition:
    flex-grow var(--aa-dur) var(--aa-ease),
    transform var(--aa-dur) var(--aa-ease);
}

.aa-panel.is-open { flex-grow: var(--aa-grow); }

.aa-panel:focus-visible {
  box-shadow:
    0 0 0 3px var(--brand-accent),
    0 14px 34px -20px rgba(0, 0, 0, 0.75);
}

.aa-panel__frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}

/* Wider than the panel on purpose — the overhang is what the parallax drifts
   through. At the panel's own width the image's edge would walk into frame on
   the first shift. */
.aa-panel__media {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--aa-media);
  height: 100%;
  transform: translate(calc(-50% + var(--aa-shift, 0px)), -50%);
  filter: grayscale(1);
  transition:
    transform var(--aa-dur) var(--aa-ease),
    filter var(--aa-dur) var(--aa-ease);
}

.aa-panel.is-open .aa-panel__media { filter: grayscale(0); }
.aa-row--colour .aa-panel__media { filter: none; }

.aa-panel__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* Legibility for the caption, always on. A scrim over a photograph is the one
   place the no-gradients rule gives way — the alternative is a flat plate that
   hides more of the picture than the text needs. */
.aa-panel__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(11, 15, 20, 0) 42%, rgba(11, 15, 20, 0.86) 100%);
}

/* The only thing separating a closed panel from the open one, and the only part
   of the two that moves. */
.aa-panel__dim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--aa-shade);
  opacity: 0.42;
  transition: opacity var(--aa-dur) var(--aa-ease);
}

.aa-panel.is-open .aa-panel__dim { opacity: 0; }

/* ---------------------------------------------------------- collapsed labels
   Set up the slat, so the row is never a line of anonymous grey bars from the
   back of a room. It fades as the panel opens and the real caption takes over. */
.aa-panel__slat {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%) rotate(180deg);
  writing-mode: vertical-rl;
  max-height: calc(100% - 44px);
  overflow: hidden;
  font-family: var(--font-display, inherit);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  z-index: 2;
  transition: opacity calc(var(--aa-dur) * 0.5) var(--aa-ease);
}

.aa-panel.is-open .aa-panel__slat { opacity: 0; }

/* ------------------------------------------------------------- open caption */
.aa-panel__cap {
  position: absolute;
  left: 26px;
  right: 26px;
  bottom: 24px;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  pointer-events: none;
  z-index: 3;
}

.aa-panel__bar {
  flex: 0 0 auto;
  width: 4px;
  align-self: stretch;
  min-height: 34px;
  border-radius: 4px;
  /* Raw accent, on a dark ground — the one place the design rules allow it. */
  background: var(--brand-accent);
  opacity: 0;
  transform: translateX(-14px);
  transition:
    opacity calc(var(--aa-dur) * 0.6) var(--aa-ease),
    transform calc(var(--aa-dur) * 0.6) var(--aa-ease);
}

.aa-panel__lines {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  opacity: 0;
  transform: translateX(-14px);
  transition:
    opacity calc(var(--aa-dur) * 0.6) var(--aa-ease),
    transform calc(var(--aa-dur) * 0.6) var(--aa-ease);
}

/* The stagger. In the sketch this was GSAP's `stagger` on a two-element tween;
   the bar leads and the text follows it in. */
.aa-panel.is-open .aa-panel__bar,
.aa-panel.is-open .aa-panel__lines {
  opacity: 1;
  transform: translateX(0);
}

.aa-panel.is-open .aa-panel__lines { transition-delay: var(--aa-stagger); }

.aa-panel__kicker {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--brand-accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.aa-panel__text {
  font-family: var(--font-display, inherit);
  font-weight: 600;
  font-size: clamp(20px, 1.9vw, 30px);
  line-height: 1.12;
  letter-spacing: 0.01em;
  color: #ffffff;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.6);
}

.aa-panel__note {
  margin-top: 2px;
  font-size: 14px;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.86);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.55);
  /* Two lines, then clipped. The caption sits over someone's photograph and a
     third line starts covering the thing it is describing. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The lead panel carries the section's own title, so it is set a size up. */
.aa-panel--lead .aa-panel__text { font-size: clamp(26px, 2.5vw, 40px); }

/* ----------------------------------------------------------------- entrance
   Reading order, left to right, the way the rest of the deck arrives. */
.aa-panel {
  animation: aa-arrive 0.5s var(--aa-ease) backwards;
  animation-delay: calc(var(--aa-i, 0) * 0.07s);
}

@keyframes aa-arrive {
  from { opacity: 0; transform: translateY(18px) rotateY(var(--aa-rot, 0deg)); }
  to   { opacity: 1; transform: translateY(0) rotateY(var(--aa-rot, 0deg)); }
}

/* ------------------------------------------------------------------- narrow
   Under 520px the row becomes a column and the 3D goes: a rotated slat 60px
   wide has nothing left to show. */
@media (max-width: 520px) {
  .aa-root { height: auto; min-height: 0; max-height: none; padding: 20px; }
  .aa-row { flex-direction: column; perspective: none; }
  .aa-panel { min-height: 92px; transform: none !important; }
  .aa-panel__media { width: 100%; }
  .aa-panel__slat {
    writing-mode: horizontal-tb;
    transform: none;
    left: 26px;
    bottom: 18px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .aa-panel,
  .aa-panel__media,
  .aa-panel__dim,
  .aa-panel__slat,
  .aa-panel__bar,
  .aa-panel__lines {
    transition: none !important;
    animation: none !important;
  }
  /* The tilt is motion too — with the transition gone it would still be a
     permanently skewed row. */
  .aa-panel { transform: none !important; }
}

/* ==========================================================================
   Industry Alliances — the intro card, the plates and the controls
   ==========================================================================
   Added after the row stopped being a wall of photographs. Three things the
   original port did not have to carry:

     - a card with no picture on it at all, which is where the row rests;
     - a plate for an agreement whose photograph has not arrived yet;
     - a black plate under the institute's name, because a gradient alone stops
       working the moment a photograph is bright at the bottom.
   ========================================================================== */

/* -------------------------------------------------------------- the intro card
   No photograph, no scrim to fight: the brand's own ink, and type on it. */
.aa-panel--intro .aa-panel__frame {
  background: var(--aa-shade);
}

/* The accent, held well back. It gives the card a centre of gravity without
   becoming a gradient in its own right — the design rule bans those as
   decoration, and at 0.10 this reads as a lit corner rather than a wash. */
.aa-panel--intro .aa-panel__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    78% 62% at 14% 8%,
    color-mix(in srgb, var(--brand-accent) 10%, transparent) 0%,
    transparent 64%
  );
}

/* The intro card is never dimmed to a photograph's depth — it has no photograph
   to hide, and at 0.42 its own type went with it. */
.aa-panel--intro .aa-panel__dim { opacity: 0.24; }
.aa-panel--intro.is-open .aa-panel__dim { opacity: 0; }
.aa-panel--intro .aa-panel__scrim { display: none; }

.aa-intro {
  position: absolute;
  left: 46px;
  right: 46px;
  bottom: 0;
  top: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  pointer-events: none;
  /* Clipped to the panel, so the words are not legible down the side of a slat
     that is 140px wide. */
  overflow: hidden;
}

/* Every line starts down and out, and is brought up by `.is-open`. Written on
   the shared selector so a line added later inherits the behaviour. */
.aa-intro > * {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity calc(var(--aa-dur) * 0.8) var(--aa-ease),
    transform calc(var(--aa-dur) * 0.8) var(--aa-ease);
}

/* Reading order, one line after another. These are the delays that make it read
   as typing itself in rather than appearing all at once — and because they hang
   off `.is-open`, the whole thing replays every time the pointer leaves the row
   and the card comes home. */
.aa-panel--intro.is-open .aa-intro > * { opacity: 1; transform: translateY(0); }
.aa-panel--intro.is-open .aa-intro__kicker { transition-delay: 0.04s; }
.aa-panel--intro.is-open .aa-intro__head   { transition-delay: 0.14s; }
.aa-panel--intro.is-open .aa-intro__body   { transition-delay: 0.36s; }

.aa-intro__kicker {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  /* Raw accent on a dark ground — the one place the design rules allow it. */
  color: var(--brand-accent);
}

.aa-intro__head {
  margin-top: 12px;
  font-family: var(--font-display, inherit);
  font-weight: 600;
  font-size: clamp(30px, 3.1vw, 52px);
  line-height: 1.06;
  letter-spacing: 0.005em;
  color: #ffffff;
  text-wrap: balance;
}



.aa-intro__body {
  max-width: 30ch;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}

/* ------------------------------------------------------- a plate, not a photo
   What an agreement wears until its photograph is supplied. */
.aa-panel__plate {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--aa-tint, var(--aa-shade));
}

/* Only on the open card. On a 140px slat a 72px monogram is two clipped
   letter-halves, and seven of them across the row read as one smeared word
   rather than as seven marks. */
.aa-panel__mono {
  opacity: 0;
  transition: opacity calc(var(--aa-dur) * 0.5) var(--aa-ease);
  font-family: var(--font-display, inherit);
  font-weight: 600;
  font-size: clamp(36px, 4.4vw, 72px);
  letter-spacing: 0.04em;
  /* On the partner's own colour, which can be anything from Snowflake's pale
     blue to o9's near-black, so the mark is set in the ground's own tone at low
     alpha rather than in a fixed white that would vanish on half of them. */
  color: rgba(255, 255, 255, 0.26);
  mix-blend-mode: overlay;
}

/* A plate has no photograph to grey out, so the desaturation would only mute
   the partner's colour for no reason. */
.aa-panel__plate { filter: none; }

.aa-panel.is-open .aa-panel__mono { opacity: 1; }

/* ------------------------------------------------- the black plate, on the photo
   "A small black overlay, and on it the institute and the MOU." A gradient
   cannot do this job on its own: three of these photographs are bright right
   down to the bottom edge. */
.aa-panel__cap {
  padding: 16px 20px 18px;
  border-radius: 14px;
  background: rgba(6, 9, 12, 0.74);
  /* A cheap frost where it is supported; where it is not, the flat 74% still
     carries the text on its own. */
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  backdrop-filter: blur(10px) saturate(120%);
  box-shadow: 0 16px 40px -24px rgba(0, 0, 0, 0.9);
  /* The plate arrives with the text rather than sitting there empty while the
     panel is still opening. */
  opacity: 0;
  transition: opacity calc(var(--aa-dur) * 0.6) var(--aa-ease);
}

.aa-panel.is-open .aa-panel__cap { opacity: 1; transition-delay: calc(var(--aa-dur) * 0.25); }

/* With a plate behind it, the shadows the text used to need are noise. */
.aa-panel__text,
.aa-panel__note { text-shadow: none; }

/* ------------------------------------------------------------- the controls
   A pill in the row's top-right corner. Inside the row on purpose: reaching for
   it must not read as leaving the cards. */
.aa-nav {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px;
  border-radius: 999px;
  background: rgba(6, 9, 12, 0.62);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 10px 28px -18px rgba(0, 0, 0, 0.9);
}

.aa-nav__btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(255, 255, 255, 0.86);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.aa-nav__btn:hover,
.aa-nav__btn:focus-visible {
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
  outline: none;
}

.aa-nav__btn .ic { width: 17px; height: 17px; }

.aa-nav__count {
  min-width: 46px;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.82);
}

/* The row needs to be the positioning context for the pill, and it is also what
   the pointer leaves to send the card home. */
.aa-row { position: relative; }

@media (max-width: 520px) {
  .aa-intro { left: 22px; right: 22px; }
  .aa-nav { top: 10px; right: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .aa-eyebrow,
  .aa-title,
  .aa-sub,
  .aa-intro > *,
  .aa-panel__cap {
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ==========================================================================
   The organization switcher
   ==========================================================================
   Three decks share the pane. One tab per organization sits between the brand
   head and the section tree, the current one filled in its own brand ink.

   A filled active state, deliberately, where the section rows below are colour
   rather than fill: those are places in one deck and this is a switch between
   decks. The two must not look like the same kind of thing, or the pane reads
   as one long list with the top three oddly highlighted. There is no bar under
   the active tab — it is the fill that says which is current.
   ========================================================================== */
.sidenav__orgs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 4px;
  margin: 0 14px 4px;
  padding: 4px;
  border-radius: 12px;
  background: var(--nav-sunken);
}

.sidenav__org {
  height: 34px;
  min-width: 0;
  padding: 0 6px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--nav-ink-soft);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
}

.sidenav__org:hover {
  color: var(--nav-ink);
  background: rgba(255, 255, 255, 0.75);
}

.sidenav__org:focus-visible {
  outline: 2px solid var(--nav-accent-ink, var(--accent-ink));
  outline-offset: 1px;
}

/* The ink, not the raw accent: this fill carries white text, and `inkFor`
   already steps every brand colour to where white reads on it. */
.sidenav__org.is-active {
  background: var(--nav-accent-ink, var(--accent-ink));
  color: #ffffff;
  cursor: default;
}

/* Collapsed: a column of short names on the dark rail. The initial alone was
   not enough — NGI and NCET share one. */
.sidenav--rail .sidenav__orgs {
  grid-auto-flow: row;
  gap: 3px;
  margin: 0 8px 6px;
  padding: 3px;
  background: rgba(255, 255, 255, 0.08);
}

.sidenav--rail .sidenav__org {
  height: 26px;
  padding: 0 4px;
  border-radius: 7px;
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.74);
}

.sidenav--rail .sidenav__org:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.14);
}

/* Gold on green, orange on black — the same pairing the selected glyph uses. */
.sidenav--rail .sidenav__org.is-active {
  background: var(--nav-rail-active, var(--nav-second, var(--brand-accent)));
  color: #14161a;
}

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

/* ==========================================================================
   Student Achievements — the backdrop, and a headline that follows the card
   ==========================================================================
   An achievement's other photographs fill the wall behind its card while that
   card is centred. The set crossfades as the collection is stepped through, and
   an achievement holding a single photograph has no set — the wall keeps its own
   gradient, which is the designed state rather than a fallback.

   Absolutely positioned on purpose: `.sw-root` is a three-row grid (head, deck,
   nav) and a fourth child in flow would add a fourth row. Out of flow it covers
   the whole wall and adds nothing to the layout. It also has to beat
   `.sw-root > * { position: relative; z-index: 1 }`, which is why position and
   z-index are both restated here.
   ========================================================================== */
.sw-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}

.sw-bg__set {
  position: absolute;
  inset: 0;
  display: flex;
  opacity: 0;
  transition: opacity 680ms cubic-bezier(.2,.7,.3,1);
}

.sw-bg__set.is-on { opacity: 1; }

/* Equal columns, each filling its own full height. Two photographs make two
   halves, six make six strips — the set fills the wall whatever it holds. */
.sw-bg__set img {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  object-fit: cover;
  /* Blurred and pushed past the edges: scaled up so the blur has material to
     work with at the seams instead of fading into the background. The cards are
     white and sit on top of this, so it has to read as light and texture, not as
     a second picture competing with the one on the card. */
  filter: blur(16px) saturate(1.18);
  transform: scale(1.14);
}

/* The veil belongs to the set, not to the layer. On the layer it would wash the
   wall's own gradient out on every achievement that has no backdrop. */
.sw-bg__set::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.80) 0%, rgb(255 255 255 / 0.62) 45%, rgb(244 247 249 / 0.78) 100%);
}

/* ------------------------------------------------------- the current name
   Dealt, the headline is the achievement's own name rather than the section's.
   These names are sentences — "ECE students secured Runner Up Position in the
   Inter Departmental Throw ball Competition" — so they are set well down from
   the 70px poster line and allowed to wrap, which the poster line never has to.
   `text-wrap: balance` keeps two lines even rather than leaving one word alone
   on the second. */
.sw-title.is-now { display: block; }

.sw-title__now {
  display: block;
  max-width: 26ch;
  margin: 0 auto;
  font-family: var(--display);
  font-weight: 600;
  text-transform: uppercase;
  font-size: clamp(20px, 2.2vw, 34px);
  line-height: 1.12;
  letter-spacing: 0.005em;
  text-wrap: balance;
  color: var(--sw-green);
  animation: sw-in 620ms cubic-bezier(.2,.7,.3,1) both;
}

@media (prefers-reduced-motion: reduce) {
  .sw-bg__set { transition: none; }
  .sw-bg__set img { filter: blur(16px); transform: scale(1.14); }
  .sw-title__now { animation: none; }
}

/* ==========================================================================
   The team — a skewed ribbon of cards  (SkewCarousel.js)
   ==========================================================================
   A row of leaning slabs on a red wall, with the centred person's name set
   across the wall behind them. The slab leans; the photograph inside it is
   counter-skewed so the face does not. The ribbon shears into a diagonal only
   while it is moving — `--sk-shear` and `--sk-heat` are written by the
   component, off the speed, and are both zero at rest.
   ========================================================================== */
.sk-root {
  --sk-red: #D91823;
  --sk-deep: #2b0206;
  --sk-mid: #6a0710;
  --sk-glow: rgba(217, 24, 35, 0.5);
  --sk-veil: rgba(217, 24, 35, 0.16);
  --sk-card-w: 300px;
  --sk-card-h: 404px;
  --sk-shear: 0deg;
  --sk-heat: 0;
  /* The ribbon's own lean, which is there whether or not anything is moving. */
  --sk-tilt: -3.2deg;
  /* The slab's lean, and the counter that stands the photograph back up. */
  --sk-skew: -8deg;
  /* Where the row begins. Proportional, not fixed: a slide in presentation mode
     is the screen's shape, so an offset that leaves air under the head on one
     display puts the cards through it on the next. The name behind them is hung
     off the same figure, which is what keeps the two overlapping by the same
     amount whether or not the deck bar has taken a strip off the bottom. */
  --sk-stage-top: clamp(200px, 28%, 290px);

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis, so a root with only a
     minimum grows to its content and FitSlide scales the slide down to fit.
     Same arrangement as `.pw-root` and `.ev-root`, and for the same reason. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  /* Near-black with the red in it rather than under it, so the vignette falls
     off to something that is still part of the same colour. */
  background: var(--sk-deep);
  color: #fff;
  isolation: isolate;
}

/* The ground. Kept on its own element so the heat can be turned up while the
   ribbon moves without touching anything the content is painted on. */
.sk-wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    /* the lit centre, behind the card that is being shown */
    radial-gradient(58% 52% at 50% 62%, var(--sk-glow) 0%, rgba(0, 0, 0, 0) 72%),
    /* a second, wider one that warms the whole wall */
    radial-gradient(90% 70% at 50% 18%, var(--sk-veil) 0%, rgba(0, 0, 0, 0) 76%),
    linear-gradient(168deg, var(--sk-mid) 0%, var(--sk-deep) 58%, #120103 100%);
  /* Brighter the faster it is going. `--sk-heat` runs 0 to 1 and is written on
     the root every frame the ribbon is in motion. */
  filter: saturate(calc(1 + var(--sk-heat) * 0.5)) brightness(calc(1 + var(--sk-heat) * 0.16));
  transition: filter 220ms linear;
}
/* The vignette, which belongs over the washes rather than in them: mixed into
   the same layer it would darken the lit centre by the same amount. */
.sk-wash::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 96% at 50% 46%, rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, 0.72) 100%);
}

/* --------------------------------------------------------------- the head */
.sk-head {
  position: absolute;
  z-index: 3;
  top: 42px;
  left: 54px;
  right: 54px;
  pointer-events: none;
}
.sk-eyebrow {
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}
.sk-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 40px;
  line-height: 1.04;
  letter-spacing: 0.004em;
  text-transform: uppercase;
  color: #fff;
}
.sk-lead {
  margin: 10px 0 0;
  max-width: 46ch;
  font-size: 14px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.68);
}

/* ------------------------------------------------------- the name behind it */
/* Big enough to be the wall rather than a caption on it, and sized to the wall
   by `fitToWidth` so a long name and a short one land at the same optical
   size. It sits above the cards and runs a little way behind them, which is
   what makes it read as being *behind* rather than above. */
.sk-ghost {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  /* Just above where the cards start, so the foot of the word runs behind them.
     That overlap is the whole point of it: clear of the row it would read as a
     second headline rather than as the wall the row is standing against. */
  top: calc(var(--sk-stage-top) - 30px);
  display: flex;
  justify-content: center;
  align-items: center;
  pointer-events: none;
  text-align: center;
}
.sk-ghost__word {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  line-height: 0.86;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Light enough to stay behind the faces, strong enough to read from the back
     of a room. The stroke is what keeps it legible where a card crosses it:
     a flat fill alone disappears into the plate's own red. */
  color: rgba(255, 255, 255, 0.14);
  -webkit-text-stroke: 1px rgba(255, 255, 255, 0.16);
}
/* The reveal gives every word a trailing 0.28em so words never touch. On a
   centred single line that margin is width the glyphs do not have, and it pulls
   the name a third of a letter to the left of the slide it is centred on.
   `!important` because the margin is written as an inline style. */
.sk-ghost__word > span > span:last-child { margin-right: 0 !important; }

/* ------------------------------------------------------------- the ribbon */
.sk-stage {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  top: var(--sk-stage-top);
  /* The deck bar floats over the slide, and what is under it here is a card's
     own name and the two arrows — content covered up, not a full-bleed
     photograph carrying its own scrim. Padding on the root cannot do this: the
     stage is positioned, and an absolutely positioned child resolves against the
     padding box, which *includes* the padding. Unset — not presenting — it is
     0 and the row takes the whole height. */
  bottom: var(--deck-bar-clear, 0px);
  overflow: hidden;
  perspective: 1500px;
  perspective-origin: 50% 44%;
  outline: none;
  /* The ribbon is dragged, so the browser's own drag-to-select is in the way. */
  user-select: none;
  cursor: grab;
  touch-action: pan-y;
}
.sk-stage.is-dragging { cursor: grabbing; }

.sk-track {
  position: absolute;
  left: 50%;
  /* The card centre, on the stage's own middle rather than at a measured offset
     down from its top — the stage is whatever height the display leaves, and
     the component sizes the cards to fit inside it. */
  top: 50%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  /* The constant lean, and the shear that only exists while it is moving. A
     skew is a 2D transform and does not flatten the 3D scene inside it. */
  transform: rotate(var(--sk-tilt)) skewY(var(--sk-shear));
  transition: transform 140ms linear;
}

.sk-card {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--sk-card-w);
  height: var(--sk-card-h);
  margin-left: calc(var(--sk-card-w) / -2);
  margin-top: calc(var(--sk-card-h) / -2);
  /* Every card's transform is written by the component each frame — the turn,
     the sink, the depth and the scale are all one function of how far it is
     from the centre — so there is nothing to interpolate here. */
  transform-style: preserve-3d;
  will-change: transform;
  cursor: pointer;
  /* The arrival: the ribbon glides in from the right while the cards fade up in
     reading order. Opacity only, because the transform belongs to the ribbon. */
  animation: sk-arrive 560ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--sk-i, 0) * 42ms + var(--intro-hold, 0s));
}
@keyframes sk-arrive {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The slab. This is the part that leans. */
.sk-card__slab {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  border-radius: 20px;
  transform: skewX(var(--sk-skew));
  box-shadow:
    0 42px 70px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(255, 255, 255, 0.08) inset;
  transition: box-shadow 240ms ease;
}
.sk-card.is-now .sk-card__slab {
  box-shadow:
    0 52px 92px rgba(0, 0, 0, 0.62),
    0 0 0 1px rgba(255, 255, 255, 0.2) inset;
}

/* Lit at the top and deep at the bottom, which is the whole reason the plate is
   a gradient and not a fill: everybody in these photographs wears the same red
   polo, and against a flat red the shirt disappears into the ground. The head
   sits in the light and the shirt sits in the dark. */
.sk-card__plate {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(178deg,
      color-mix(in srgb, var(--sk-red) 88%, #fff) 0%,
      var(--sk-red) 26%,
      var(--sk-mid) 62%,
      #1a0205 100%);
}
/* A soft sheen down the lit edge, so the slab reads as a surface rather than a
   printed rectangle. */
.sk-card__plate::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(104deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0) 42%);
}

/* The figure. Counter-skewed, so the slab leans and the person does not. */
.sk-card__shot {
  position: absolute;
  inset: 0;
  display: block;
  transform: skewX(calc(var(--sk-skew) * -1));
}
.sk-card__shot img {
  position: absolute;
  left: 50%;
  bottom: 0;
  /* The stylesheet caps every image at the width of its box. That cap is right
     nearly everywhere and wrong here: the figure is deliberately wider than the
     card, and capped it was drawn 300px wide in a 416px-tall box — a 28%
     horizontal squeeze on two dozen faces. */
  max-width: none;
  /* Taller than the card and bottom-aligned: the source is a square with the
     figure standing in the middle of it, so at the card's own width the person
     would sit in the upper half with a third of the plate empty under them. */
  height: 103%;
  width: auto;
  transform: translateX(-50%);
  filter: drop-shadow(0 18px 26px rgba(0, 0, 0, 0.4));
}

/* The name on the card. Counter-skewed like the figure, and padded wide enough
   that the slab's own slant cannot cut into it. */
.sk-card__foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: block;
  padding: 44px 40px 20px;
  text-align: center;
  background: linear-gradient(180deg, rgba(10, 1, 2, 0) 0%, rgba(10, 1, 2, 0.78) 62%, rgba(10, 1, 2, 0.92) 100%);
}
.sk-card__name,
.sk-card__role {
  display: block;
  transform: skewX(calc(var(--sk-skew) * -1));
}
.sk-card__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 21px;
  line-height: 1.1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: #fff;
}
.sk-card__role {
  margin-top: 5px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.66);
}

/* The dim on everything but the centre card. A real element rather than an
   `opacity` on the card, because the card carries the arrival animation and the
   two would fight over the same property. */
.sk-card__dim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 0, 1, 0.55) 0%, rgba(6, 0, 1, 0.8) 100%);
  opacity: 0;
  pointer-events: none;
}

/* ------------------------------------------------------------- the controls */
.sk-nav {
  position: absolute;
  z-index: 4;
  right: 40px;
  bottom: 20px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 6px;
  border-radius: 999px;
  background: rgba(12, 2, 4, 0.58);
  border: 1px solid rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(8px);
}
.sk-nav__btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background 180ms ease;
}
.sk-nav__btn:hover { background: rgba(255, 255, 255, 0.14); }
.sk-nav__btn .ic { width: 19px; height: 19px; }
.sk-nav__count {
  min-width: 56px;
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.8);
}

@media (prefers-reduced-motion: reduce) {
  .sk-card { animation: none; }
  .sk-track { transition: none; }
  .sk-wash { transition: none; }
}

/* ==========================================================================
   Campus Infrastructure — the tilted wall  (TiltedTiles.js)
   ==========================================================================
   Two states in one slide. The wall is columns of photographs leaning across
   the frame, adjacent columns drifting in opposite directions, with the
   section's name and one button held still in the middle. The button swaps in
   the filtered gallery, which is `PlacementWall` — not a second implementation
   of anything.
   ========================================================================== */
.tt-root {
  --tt-ink: #0A0E13;
  --tt-tilt: -14deg;
  --tt-cover: 1.55;
  --tt-col-w: 300px;
  --tt-gap: 22px;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis, so a root with only a
     minimum grows to its content and FitSlide scales the slide down to fit.
     Same arrangement as `.pw-root` and `.ev-root`. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--tt-ink);
  color: #fff;
  isolation: isolate;
}

/* ------------------------------------------------------------------ the wall */
/* Inset well past the frame, because a rotated box has to be bigger than the
   box it is rotated inside or its corners walk into view. */
.tt-wall {
  position: absolute;
  inset: -36%;
  z-index: 0;
  display: flex;
  justify-content: center;
  gap: var(--tt-gap);
  transform: rotate(var(--tt-tilt)) scale(var(--tt-cover));
  transform-origin: 50% 50%;
}

.tt-col {
  flex: 0 0 auto;
  width: var(--tt-col-w);
  height: 100%;
  /* The run is drawn twice and wrapped on its own height; the column is the
     window that wrap is invisible through. */
  overflow: hidden;
}
.tt-col__track {
  display: flex;
  flex-direction: column;
  gap: var(--tt-gap);
  will-change: transform;
}

.tt-tile {
  margin: 0;
  flex: 0 0 auto;
  width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
}
/* The tile's height is its own ratio times the column width — written on the
   element by the component — so the photograph fills it exactly and `cover`
   never has anything to crop. These files are small, 284 to 1920px wide, so
   they are scaled up rather than down and a soft one is the file, not the
   layout. */
.tt-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The veil. Two layers: a flat one that pushes the whole wall back behind the
   copy, and a pool of ink under the middle where the name and the button sit. */
.tt-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(64% 58% at 50% 50%, rgba(6, 9, 13, 0.92) 0%, rgba(6, 9, 13, 0.45) 62%, rgba(6, 9, 13, 0.2) 100%),
    linear-gradient(180deg, rgba(6, 9, 13, 0.62) 0%, rgba(6, 9, 13, 0.46) 40%, rgba(6, 9, 13, 0.72) 100%);
  transition: opacity 420ms ease;
}

/* ---------------------------------------------------------------- the middle */
.tt-centre {
  position: absolute;
  z-index: 2;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 40px 64px;
  text-align: center;
  transition: opacity 320ms ease, transform 420ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.tt-eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}
.tt-title {
  margin: 0;
  max-width: 18ch;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(44px, 4.6vw, 74px);
  line-height: 1.02;
  letter-spacing: 0.004em;
  text-transform: uppercase;
  text-wrap: balance;
}
.tt-lead {
  margin: 2px 0 0;
  max-width: 54ch;
  font-size: 16px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.78);
}

/* The one control on the wall. Filled in the accent, because it is the only
   thing on this slide that does anything. */
.tt-cta {
  margin-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 15px 26px;
  border: 0;
  border-radius: 999px;
  background: var(--brand-accent, #FFBB00);
  color: var(--on-accent, #111827);
  font-family: var(--display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 18px 38px rgba(0, 0, 0, 0.45);
  transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 220ms ease;
}
.tt-cta:hover,
.tt-cta:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.55);
}
.tt-cta .ic {
  width: 18px;
  height: 18px;
  transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.tt-cta:hover .ic { transform: translateX(4px); }

/* --------------------------------------------------------------- the gallery */
.tt-gallery {
  position: absolute;
  z-index: 3;
  inset: 0;
  background: var(--surface);
  opacity: 0;
  pointer-events: none;
  transform: translateY(14px);
  transition: opacity 320ms ease, transform 420ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.tt-root.is-open .tt-gallery {
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
/* Open, the wall is still there and still moving — it is just no longer what
   the slide is about. */
.tt-root.is-open .tt-centre {
  opacity: 0;
  pointer-events: none;
  transform: scale(0.97);
}

/* The gallery's own root is the full-height one this deck gives every wall, so
   it fills the slide it has been dropped into. The strip at the top is the way
   back: without it the button sat on the first filter chip, which is the one
   that clears the filter and the one most likely to be pressed. */
.tt-gallery .pw-root {
  height: 100%;
  max-height: 100%;
  padding-top: 56px;
  border-radius: inherit;
}

/* The way back. Top-left, where the gallery's own chapter rail is not. */
.tt-back {
  position: absolute;
  z-index: 4;
  top: 14px;
  left: 22px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px 9px 10px;
  border: 1px solid var(--line, rgba(17, 24, 39, 0.12));
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink, #111827);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 180ms ease, transform 180ms ease;
}
.tt-back:hover { transform: translateX(-2px); }
.tt-back .ic { width: 17px; height: 17px; }

@media (prefers-reduced-motion: reduce) {
  .tt-centre,
  .tt-gallery,
  .tt-cta,
  .tt-cta .ic,
  .tt-back { transition: none; }
}

/* ==========================================================================
   NT Square — a headline over a fan of cards  (CardFan.js)
   ==========================================================================
   Built to a layout supplied as a reference image: eyebrow, two-line headline,
   two pills, and under them a hand of rounded cards arching up out of the
   bottom edge — the middle one highest and square on, the rest stepping down
   and leaning away, every one of them cut off by the floor.

   Three layers per card, because three things move and they would otherwise be
   three rules fighting over one `transform`: `.cf-card` is the fan (written
   once by JS), `.cf-card__rise` is the entrance, `.cf-card__face` is the hover.
   ========================================================================== */
.cf-root {
  --cf-card-w: 500px;
  --cf-card-h: 500px;
  --cf-sink: 80px;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. Same arrangement as
     `.pw-root` and `.tt-root`. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink, #111827);
}

/* ------------------------------------------------------------------ the head */
.cf-head {
  position: absolute;
  z-index: 200;
  /* The head takes the top of the frame down to where the hand begins. The
     reference sets its eyebrow at an eighth of the way down and closes its
     buttons just before the cards; this lands in the same place. */
  /* 13% while the head carried an eyebrow and a two-line headline. With a
     wordmark in their place it starts higher, so the mark can be large and the
     buttons still clear the top of the hand. */
  top: 5%;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 0 64px;
  text-align: center;
  pointer-events: none;
}

/* The section's wordmark. Sized by height, so a mark of any ratio keeps its own
   width and the head's stack stays predictable; capped in width too, because a
   very wide lockup would otherwise reach the frame's edges.

   No `invert` — three colours on a pale ground. See the note in CardFan.js. */
.cf-mark {
  display: block;
  height: 168px;
  width: auto;
  max-width: min(600px, 100%);
  object-fit: contain;
}

@media (max-width: 900px) {
  .cf-mark { height: 108px; }
}
.cf-head .cf-cta { pointer-events: auto; }

.cf-eyebrow {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink, #111827);
  animation: cf-in 520ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: var(--intro-hold, 0s);
}
/* The little mark the reference sets before its eyebrow. Drawn, not an icon:
   it is a shape, not a symbol that has to mean anything. */
.cf-eyebrow__mark {
  width: 15px;
  height: 13px;
  border-radius: 3px 3px 3px 7px;
  background: currentColor;
}

.cf-title {
  margin: 0;
  max-width: 20ch;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(42px, 4.6vw, 74px);
  line-height: 1.06;
  letter-spacing: -0.028em;
}
.cf-title__line {
  display: block;
}

.cf-cta {
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.cf-btn {
  padding: 13px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 200ms cubic-bezier(0.22, 0.7, 0.28, 1), background 200ms ease, border-color 200ms ease;
  animation: cf-in 520ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 620ms + var(--cf-btn-i, 0) * 70ms);
}
.cf-btn--solid {
  background: var(--ink, #111827);
  color: var(--surface, #fff);
}
.cf-btn--ghost {
  background: transparent;
  border-color: var(--line, rgba(17, 24, 39, 0.16));
  color: var(--ink, #111827);
}
.cf-btn:hover { transform: translateY(-2px); }
.cf-btn--ghost:hover { border-color: var(--ink, #111827); }

@keyframes cf-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------------- the light */
/* Three orbs under the hand, so the cards are standing in light rather than on
   a sheet of white. Deliberately faint: measured against the white it sits on,
   the strongest of them drops the ground by about four percent — enough that
   the corners of the slide read as cooler than its middle, not enough that
   anybody looks at it instead of the photographs. The colour is the
   organization's own, passed in by the block and measured off its mark. */
.cf-glow {
  --cf-glow: var(--brand-accent, #F05D29);
  position: absolute;
  z-index: 0;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.cf-orb {
  position: absolute;
  border-radius: 50%;
  /* `closest-side` makes the stop land on the circle's own edge, so the orb
     fades out exactly where it ends and there is no seam to see. */
  background: radial-gradient(closest-side, var(--cf-glow) 0%, transparent 100%);
  /* The drift is 20px over half a minute. At this opacity it is not a movement
     anybody can point at; it is the reason the slide does not look like a
     still. */
  animation: cf-drift 34s ease-in-out infinite alternate both;
}
/* Positioned by their centres, not their corners. The first cut hung them off
   the edges, which put every core off screen or behind a card and left the
   visible ground 0.7% off white — there, but not there enough to be worth
   having. A radial is strongest at exactly one point, so that point has to be
   somewhere the eye can reach. */
.cf-orb {
  transform: translate(-50%, -50%);
}
.cf-orb--a {
  width: 900px;
  height: 900px;
  left: 16%;
  top: 56%;
  opacity: 0.06;
  animation-duration: 38s;
}
/* The middle one sits low, so its core is behind the hand and what shows is the
   halo either side of it — light coming out from behind the cards. */
.cf-orb--b {
  width: 1120px;
  height: 1120px;
  left: 50%;
  top: 64%;
  opacity: 0.09;
  animation-delay: -12s;
}
.cf-orb--c {
  width: 780px;
  height: 780px;
  left: 86%;
  top: 45%;
  opacity: 0.055;
  animation-duration: 29s;
  animation-delay: -6s;
}
/* The drift keeps the centring translate — an animation replaces the whole
   `transform`, so leaving it out would snap every orb a half-width down and to
   the right the moment the animation took hold. */
@keyframes cf-drift {
  from { transform: translate(-50%, -50%) translate3d(-14px, 8px, 0) scale(1); }
  to { transform: translate(-50%, -50%) translate3d(14px, -10px, 0) scale(1.05); }
}

/* ------------------------------------------------------------------- the fan */
/* Anchored to the floor and hung below it, so every card is cut by the bottom
   edge and only its top half is ever on screen. */
.cf-fan {
  position: absolute;
  z-index: 1;
  left: 50%;
  /* Lifted (2026-09-18, on request: "move the photos slightly upward so that
     every photo is clearly visible"). The hand still stands out of the floor —
     that is the layout it was built to — but 120px less of it is below the
     frame, which is what the end cards were losing. */
  bottom: calc(var(--cf-sink) * -1 + 120px);
  width: 0;
  height: 0;
}

.cf-card {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--cf-card-w);
  height: var(--cf-card-h);
  margin-left: calc(var(--cf-card-w) / -2);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* The fan's own transform is written by the component and never animated;
     everything that moves is on a layer inside. */
  transform-origin: 50% 100%;
}

/* The entrance: up from under the floor, in a stagger that runs outward from
   the middle of the hand. */
.cf-card__rise {
  width: 100%;
  height: 100%;
  animation: cf-rise 820ms cubic-bezier(0.19, 0.84, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 260ms + var(--cf-delay, 0ms));
}
@keyframes cf-rise {
  from { opacity: 0; transform: translateY(300px); }
  to { opacity: 1; transform: none; }
}

/* The face: the card itself, and the only thing the hover touches. */
.cf-card__face {
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 20px;
  background: var(--panel, #F3F4F6);
  box-shadow: 0 16px 36px rgba(17, 24, 39, 0.16);
  transition: transform 260ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 260ms ease;
}
.cf-card__face img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The card is square because seven of these nine photographs are, so seven of
     them are shown whole and only the two 16:9 ones give up anything — and they
     give it up at the sides, where a wide group shot has the least in it. The
     whole picture is one press away in the viewer either way. */
  object-position: 50% 50%;
}

/* Hover: one card clear of its neighbours. The lift is on the face, so it does
   not disturb the fan underneath it. */
.cf-card:hover,
.cf-card:focus-visible {
  z-index: 300 !important;
  outline: none;
}
.cf-card:hover .cf-card__face,
.cf-card:focus-visible .cf-card__face {
  transform: translateY(-30px) scale(1.04);
  box-shadow: 0 34px 64px rgba(17, 24, 39, 0.3);
}
/* And the rest of the hand steps back, so the one being pointed at is the one
   being read. */
.cf-fan:hover .cf-card:not(:hover) .cf-card__face {
  transform: translateY(6px) scale(0.985);
  box-shadow: 0 10px 24px rgba(17, 24, 39, 0.12);
}

/* ------------------------------------------------------------------ the deck */
/* One more pill after the reference's two, with the count set into it. */
.cf-btn--deck { display: inline-flex; align-items: center; gap: 10px; }
.cf-btn__count {
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--ink, #111827);
  color: var(--surface, #fff);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* The panel over the hand. Frosted rather than dark: the slide is white and the
   hand should still be sensed under it. */
.cf-deck {
  position: absolute;
  inset: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: blur(18px) saturate(1.1);
          backdrop-filter: blur(18px) saturate(1.1);
  outline: none;
  opacity: 0;
  animation: cf-deck-in 360ms ease both;
}
@keyframes cf-deck-in { to { opacity: 1; } }
.cf-deck.is-closing { animation: cf-deck-out 420ms ease both; }
@keyframes cf-deck-out { from { opacity: 1; } to { opacity: 0; } }

.cf-deck__head {
  position: absolute;
  /* A fixed distance, not a percentage: the ring below is placed off the
     slide's real height and the two have to keep their gap on a canvas of 860
     and on a filled screen of 900 alike. */
  top: 34px;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  pointer-events: none;
}
.cf-deck__eyebrow { animation: none; }
.cf-deck__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 48px;
  line-height: 1.04;
  letter-spacing: -0.02em;
}
.cf-deck__hint {
  margin: 0;
  font-size: 13px;
  color: var(--muted, rgba(17, 24, 39, 0.6));
}

/* The section's mark, in the corner the ring leaves empty. Not inverted, for
   the reason `.cf-mark` is not: this mark is a green N, an orange T and SQUARE
   in black, and the panel's ground is a pale frost. */
.cf-deck__mark {
  position: absolute;
  top: 40px;
  right: 56px;
  z-index: 300;
  display: block;
  height: 76px;
  width: auto;
  max-width: 300px;
  object-fit: contain;
  pointer-events: none;
}

/* The ring. Every card is placed by JS on an ellipse — a circle lying away from
   you — and the whole of it turns, one card every few seconds, for ever. The
   seat is written on the button and nothing in this file touches that transform;
   the entrance and the hover both live on the face inside it, which is the same
   division the fan in front of this panel uses and for the same reason. */
.cf-deck__hand {
  position: absolute;
  left: 50%;
  /* The ring rides up by most of the presenter bar's height when there is one.
     Presenting gives the slide a taller canvas AND takes 96 nominal px of it
     back for the bar, so a flat percentage puts the nearest card's foot on the
     Back button on exactly the display the deck is shown on. Measured both
     ways: 46px of daylight under the ring on the 860 canvas, 23px presenting. */
  top: calc(50% - var(--deck-bar-clear, 0px) * 0.55);
  width: 0;
  height: 0;
}
.cf-deck__card {
  position: absolute;
  left: 0;
  top: 0;
  width: 320px;
  height: 320px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* The seat only. JS writes it every frame; a transition here would put a
     second opinion on the same property and the ring would lag its own loop. */
  transform: translate(-50%, -50%);
}
.cf-deck__card:focus-visible { outline: none; }
.cf-deck__card:hover,
.cf-deck__card:focus-visible { z-index: 300 !important; }
.cf-deck__face {
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 20px;
  background: var(--panel, #F3F4F6);
  box-shadow:
    0 1px 2px rgba(17, 24, 39, 0.06),
    0 18px 40px rgba(17, 24, 39, 0.18);
  /* A hairline of white, so two cards standing edge to edge on the ring read as
     two cards and not as one long picture. */
  outline: 2px solid #fff;
  outline-offset: -1px;
  transform: scale(0.42);
  opacity: 0;
  transition:
    transform 620ms cubic-bezier(0.2, 0.8, 0.2, 1) var(--cf-in-delay, 0ms),
    opacity 520ms ease var(--cf-in-delay, 0ms),
    box-shadow 300ms ease;
}
/* Arriving: each card grows into the seat it has already been placed in. */
.cf-deck.is-dealt .cf-deck__face { transform: scale(1); opacity: 1; }
.cf-deck.is-dealt .cf-deck__card:hover .cf-deck__face,
.cf-deck.is-dealt .cf-deck__card:focus-visible .cf-deck__face {
  transform: scale(1.1);
  box-shadow: 0 34px 64px rgba(17, 24, 39, 0.3);
  transition:
    transform 300ms cubic-bezier(0.22, 0.7, 0.28, 1),
    box-shadow 300ms ease;
}
.cf-deck__face img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cf-deck__back {
  position: absolute;
  left: 50%;
  bottom: calc(40px + var(--deck-bar-clear, 0px));
  transform: translateX(-50%);
  animation: none;
}
.cf-deck__back:hover { transform: translateX(-50%) translateY(-2px); }

@media (prefers-reduced-motion: reduce) {
  .cf-eyebrow,
  .cf-btn,
  .cf-orb,
  .cf-card__rise { animation: none; }
  .cf-deck { animation: none; opacity: 1; }
  .cf-card__face,
  .cf-btn,
  .cf-deck__card,
  .cf-deck__face { transition: none; }
  .cf-deck__face { transform: scale(1); opacity: 1; }
}

/* ==========================================================================
   Trainings — the bookshelf  (TrainingShelf.js)
   ==========================================================================
   Built to a reference image: one frame, two zones, a shelf between them. Above
   the shelf the trainings stand as books, the selected one large and square on
   with the rest ranged to its right. Below it, where the reference puts a row of
   bestsellers, this puts the selected training's own information.

   The books are placed by the component, one transform each, written every
   frame — so nothing here transitions position. What is here is the shelf, the
   covers, and the entrances.
   ========================================================================== */
.ts-root {
  /* White and red, as asked. The red is Torii's own, measured off the polo
     everybody in the team photographs is wearing; a book takes its own `--ts-tint`
     where one is given and falls back to this. */
  --ts-red: #D91823;
  --ts-cream: #FBF4EC;
  --ts-paper: #FFFFFF;
  --ts-ink: #1A1614;
  --ts-book-w: 300px;
  --ts-book-h: 430px;
  /* Where the shelf sits — a fixed distance from the TOP of the frame, never a
     percentage of it.
     A slide learns its real height one frame after it is drawn: the root starts
     at the 860 fallback and becomes `--slide-h` the moment FitSlide decides to
     fill, which on a 16:9 screen is 900. As a percentage that moved the shelf
     25px down on that frame, taking every book on it — and the deck's own slide
     entrance then carried the whole thing back up over the next 430ms, so the
     shelf appeared to drop, stick, and slide into place. Measured on arrival at
     the slide, and it is exactly what "it looks like a glitch" was.
     Pinned to the top, the extra height falls into the zone below the shelf,
     whose content is top-aligned — so nothing moves at all. 533 is 62% of the
     860 canvas, which is where the reference puts it. Raised to 505 when the
     four facts under the shelf were replaced by a name and a paragraph set
     large enough to read from a room — that type needs the height, and the
     books had 28px to spare. */
  --ts-split: 505px;

  position: relative;
  display: block;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. Same arrangement as
     `.pw-root` and `.tt-root`. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--ts-paper);
  color: var(--ts-ink);
}

/* ------------------------------------------------------------- the top zone */
.ts-top {
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--ts-split);
  overflow: hidden;
  /* Fixed, so the books standing at its foot are fixed too. */
  background:
    radial-gradient(70% 90% at 82% 18%, rgba(217, 24, 35, 0.05), transparent 70%),
    var(--ts-cream);
}

.ts-head {
  position: absolute;
  z-index: 3;
  left: 58px;
  top: 78px;
  width: 300px;
}
.ts-eyebrow {
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(26, 22, 20, 0.55);
  animation: ts-in 560ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: var(--intro-hold, 0s);
}
/* The reference sets its headline in a serif, large and tight. */
.ts-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 58px;
  line-height: 1.02;
  letter-spacing: -0.015em;
  animation: ts-in 620ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 70ms);
}
.ts-lead {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(26, 22, 20, 0.6);
  animation: ts-in 620ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 130ms);
}

/* ---------------------------------------------------------------- the books */
/* The row is anchored to the shelf line, so every book stands ON the plank
   whatever size it is drawn at — `transform-origin` at the foot is what keeps
   the scaled ones from floating. */
.ts-stage {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;
  outline: none;
}
.ts-row {
  position: absolute;
  left: 406px;
  bottom: 0;
  width: 0;
  height: 0;
}

.ts-book {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--ts-book-w);
  height: var(--ts-book-h);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transform-origin: 50% 100%;
  /* Placed by the component every frame; only the fade is CSS's business. */
  animation: ts-rise 780ms cubic-bezier(0.19, 0.84, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 230ms + var(--ts-i, 0) * 70ms);
}
.ts-book.is-gone { opacity: 0; pointer-events: none; }
@keyframes ts-rise {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The book itself: a cover, a spine down its left edge and a block of pages
   down its right. */
.ts-book__body {
  position: absolute;
  inset: 0;
  display: block;
  border-radius: 4px 10px 10px 4px;
  box-shadow:
    0 22px 34px rgba(26, 22, 20, 0.18),
    0 2px 5px rgba(26, 22, 20, 0.12);
  transition: box-shadow 300ms ease, transform 300ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.ts-book.is-front .ts-book__body {
  box-shadow:
    0 40px 60px rgba(26, 22, 20, 0.24),
    0 4px 10px rgba(26, 22, 20, 0.14);
}
.ts-book:hover .ts-book__body { transform: translateY(-8px); }

/* White into red, as asked: the cover is mostly paper and the colour gathers at
   its foot and its outer corner, which is where a printed cover carries it. */
.ts-book__cover {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 38px 34px 34px 40px;
  border-radius: 4px 10px 10px 4px;
  /* The red has to arrive early enough to be the other half of "white and
     red". A first cut held it back to the last third and every cover came out
     a pale pink card — on the books ranged behind the front one, which are
     drawn smaller, it read as no colour at all. */
  background:
    linear-gradient(162deg,
      #FFFFFF 0%,
      #FFFFFF 16%,
      color-mix(in srgb, var(--ts-tint, var(--ts-red)) 10%, #ffffff) 32%,
      color-mix(in srgb, var(--ts-tint, var(--ts-red)) 42%, #ffffff) 56%,
      color-mix(in srgb, var(--ts-tint, var(--ts-red)) 78%, #ffffff) 78%,
      var(--ts-tint, var(--ts-red)) 100%);
  overflow: hidden;
}
/* A single soft sheen across the board, so the cover reads as a printed surface
   rather than as a swatch. */
.ts-book__cover::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(112deg, rgba(255, 255, 255, 0.26) 0%, rgba(255, 255, 255, 0) 34%);
  pointer-events: none;
}
.ts-book__track {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--ts-tint, var(--ts-red)) 78%, #1A1614);
}
.ts-book__name {
  margin-top: 16px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 27px;
  line-height: 1.12;
  letter-spacing: -0.01em;
  text-align: left;
  color: var(--ts-ink);
  text-wrap: balance;
}
.ts-book__rule {
  margin-top: 16px;
  width: 42px;
  height: 2px;
  background: color-mix(in srgb, var(--ts-tint, var(--ts-red)) 70%, transparent);
}
.ts-book__meta {
  margin-top: auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* The foot of the cover is the strong end of the gradient, so this line is
     white on red rather than ink on white like everything above it. */
  color: rgba(255, 255, 255, 0.92);
}

/* The spine, and the block of pages on the fore-edge. */
.ts-book__spine {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 13px;
  border-radius: 4px 0 0 4px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--ts-tint, var(--ts-red)) 70%, #2a0508) 0%,
    rgba(26, 22, 20, 0.16) 58%,
    rgba(26, 22, 20, 0) 100%);
  z-index: 2;
}
.ts-book__pages {
  position: absolute;
  right: -7px;
  top: 7px;
  bottom: 7px;
  width: 8px;
  border-radius: 0 3px 3px 0;
  background: repeating-linear-gradient(90deg,
    #FFFFFF 0 1px, #E7E0D8 1px 2px);
  box-shadow: inset -1px 0 2px rgba(26, 22, 20, 0.14);
}
/* What the book throws onto the plank it is standing on. */
.ts-book__cast {
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: -9px;
  height: 18px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(26, 22, 20, 0.34), transparent);
  filter: blur(4px);
}

/* ---------------------------------------------------------------- the shelf */
.ts-shelf {
  position: absolute;
  z-index: 4;
  left: 0;
  right: 0;
  top: var(--ts-split);
  height: 0;
  pointer-events: none;
}
/* The plank's top face, its front edge, and the shadow it throws down the wall
   below — three strips rather than one border, because a shelf is a thing with
   a thickness. */
.ts-shelf__top {
  position: absolute;
  left: 0;
  right: 0;
  top: -9px;
  height: 9px;
  /* The lit top face of the board, with the line where the books meet it. */
  background: linear-gradient(180deg, #FFFDFB 0%, #F6EFE6 62%, #EADFD1 100%);
  box-shadow: 0 -1px 0 rgba(26, 22, 20, 0.06) inset;
}
.ts-shelf__edge {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 17px;
  /* The front edge — the thickness of the board seen straight on, which is what
     makes it a shelf rather than a rule across the page. */
  background: linear-gradient(180deg, #E9DDCD 0%, #DBCCB8 55%, #CBB9A2 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.5) inset,
    0 -1px 2px rgba(26, 22, 20, 0.12);
}
.ts-shelf__under {
  position: absolute;
  left: 0;
  right: 0;
  top: 17px;
  height: 64px;
  background: linear-gradient(180deg, rgba(26, 22, 20, 0.22) 0%, rgba(26, 22, 20, 0.05) 46%, rgba(26, 22, 20, 0) 100%);
}

/* ---------------------------------------------------------- the information */
.ts-under {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--ts-split);
  bottom: 0;
  display: flex;
  align-items: stretch;
  gap: 30px;
  /* The bar floats over the slide and what is under it here is the training's
     own tools — content covered up, not a photograph carrying its own scrim.
     Taken on this element rather than on the root, because the root's children
     are positioned and an absolutely positioned child resolves against the
     padding box, which includes the padding. */
  padding: 44px 58px calc(30px + var(--deck-bar-clear, 0px));
  background: var(--ts-paper);
}
/* A hairline across the zone, then two columns under it: the programme's number
   and name on the left, what it covers and its syllabus on the right. Side by
   side because the zone under the shelf is wide and shallow — stacked, a
   paragraph set large enough to read runs out of height before it runs out of
   words. The rule is what makes it read as a considered band rather than as
   text that fell to the bottom of the slide. */
.ts-info {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.55fr);
  align-items: start;
  column-gap: 64px;
  row-gap: 26px;
}
.ts-info > * {
  animation: ts-info-in 520ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
}
.ts-info > *:nth-child(2) { animation-delay: 70ms; }
.ts-info > *:nth-child(3) { animation-delay: 130ms; }
@keyframes ts-info-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

.ts-info__rule {
  grid-column: 1 / -1;
  height: 1px;
  background: rgba(26, 22, 20, 0.14);
}

/* The number in the programme's own red, then what kind of programme it is. */
.ts-info__index {
  margin: 0 0 14px;
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.ts-info__num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--ts-tint, var(--ts-red));
  font-variant-numeric: tabular-nums;
}
.ts-info__kind {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(26, 22, 20, 0.45);
}

.ts-info__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 52px;
  line-height: 1.03;
  letter-spacing: -0.018em;
  text-wrap: balance;
}
.ts-info__blurb {
  margin: 0;
  /* Read from the back of a room, which the 13.5px this started at was not. */
  font-size: 22px;
  line-height: 1.55;
  letter-spacing: -0.004em;
  color: rgba(26, 22, 20, 0.78);
}
/* The syllabus, as one line. A bulleted list of seven subjects is a list to
   scroll; the same seven separated by dots is a line to read. */
.ts-info__subjects {
  margin: 18px 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 10px;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.9;
  color: rgba(26, 22, 20, 0.58);
}
.ts-info__dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--ts-tint, var(--ts-red));
  opacity: 0.6;
}

/* ------------------------------------------------------------- the controls */
.ts-nav {
  position: absolute;
  z-index: 5;
  right: 40px;
  top: 74px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 6px;
  border-radius: 999px;
  background: var(--ts-paper);
  box-shadow: 0 2px 12px rgba(26, 22, 20, 0.1);
  animation: ts-in 560ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 250ms);
}
.ts-nav__btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ts-ink);
  cursor: pointer;
  transition: background 180ms ease;
}
.ts-nav__btn:hover { background: rgba(26, 22, 20, 0.07); }
.ts-nav__btn .ic { width: 18px; height: 18px; }
.ts-nav__count {
  min-width: 52px;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: rgba(26, 22, 20, 0.6);
}

@keyframes ts-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .ts-eyebrow, .ts-title, .ts-lead, .ts-nav,
  .ts-book, .ts-info > * { animation: none; }
  .ts-book__body { transition: none; }
}


/* ==========================================================================
   Project Week — the collage  (PhotoCollage.js)
   ==========================================================================
   Copy in the left third, a staggered collage of nine photographs to its right,
   a short list set into a gap in the collage. Built to a reference image; the
   reference is black and white, this keeps the photographs in colour and takes
   its ink, its button and its accent from the organization's own palette.

   Every card is placed by the component in canvas pixels. What is here is the
   card itself, and its entrance — of which there are five, so the collage
   assembles rather than fades in.
   ========================================================================== */
.pc-root {
  --pc-ink: #111111;
  --pc-paper: #FFFFFF;
  /* Torii's own orange, measured off the mark on its brand film's closing card.
     It is on the arrows and the eyebrow's mark, and nowhere else. */
  --pc-accent: #F05D29;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. Everything inside is
     anchored to the TOP in canvas pixels, so when presenting hands the root
     more height than the 860 canvas, the extra falls harmlessly below the
     collage and nothing moves — the lesson the Trainings shelf taught. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  /* The root is the scroller: the first screen, then the wall of every further
     photograph under it. Scrolled by hand off the wheel (see the component), so
     no `scroll-behavior: smooth` here, ever; and no scrollbar drawn. */
  overflow: hidden auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  border-radius: var(--radius-lg);
  background: var(--pc-paper);
  color: var(--pc-ink);
}
.pc-root::-webkit-scrollbar { width: 0; height: 0; }

/* The first screen. It takes the root's full height — 860 on the canvas,
   `--slide-h` in the room — so the wall begins exactly one screen down; and
   because everything on it is anchored to the top in canvas pixels, the stage
   growing by the difference a frame after arrival moves nothing on it. */
.pc-stage {
  position: relative;
  height: 100%;
  flex: 0 0 auto;
}

/* ----------------------------------------------------------------- the light */
/* Soft blobs behind the collage, in the organization's own two measured
   colours — the orange off its mark and the red off the polo in its team
   photographs. Deliberately faint: the ground stops being a sheet of paper, and
   that is all it does. Two things this pattern already taught, on NT Square's
   fan: a radial is strongest at exactly one point, so that point has to sit
   somewhere the eye can reach — hung off the frame's edges the cores land off
   screen and the visible ground measures under a percent off white, which is
   not worth having. And an animation replaces the WHOLE transform, so the drift
   has to repeat the centring translate in both keyframes or every blob snaps
   half its own width down and right the moment it takes hold. */
.pc-glow {
  --pc-glow: #F05D29;
  --pc-glow-2: #D91823;
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.pc-blob {
  position: absolute;
  border-radius: 50%;
  /* `closest-side` puts the stop on the circle's own edge, so a blob fades out
     exactly where it ends and there is no seam anywhere. */
  background: radial-gradient(closest-side, var(--pc-tone) 0%, transparent 100%);
  transform: translate(-50%, -50%);
  /* `pc-blob-drift`, not `pc-drift` — that name was already a card entrance in
     this same stylesheet, and `@keyframes` names are global. The card's won:
     the blobs were being faded 0 to 1 across forty-four seconds and having
     their centring transform replaced by `none`, so they sat half a width off
     and all but invisible. Measured before the rename: opacity 0.013, 0.32 and
     0.54 against the 0.07, 0.055 and 0.06 set here. */
  animation: pc-blob-drift 44s ease-in-out infinite alternate both;
}
/* Placed where the ground is actually visible, which on this layout is the wide
   band above the collage, the column beside the copy, and the foot of the left
   side. A blob centred under the middle of the collage is a blob nobody sees:
   the photographs cover it, and only one sample point in seven moved at all. */
.pc-blob--a {
  --pc-tone: var(--pc-glow);
  width: 1180px;
  height: 1180px;
  left: 57%;
  top: 20%;
  opacity: 0.085;
}
/* The foot of the left side, under the copy and around the first card. */
.pc-blob--b {
  --pc-tone: var(--pc-glow-2);
  width: 880px;
  height: 880px;
  left: 19%;
  top: 80%;
  /* Lower than the other two: red reads stronger than the orange at the same
     alpha, and at 0.065 this one's core measured 14.8% below white against a
     5.2% mean across the ground — the one place the wash stopped being a wash. */
  opacity: 0.05;
  animation-duration: 37s;
  animation-delay: -11s;
}
/* The right edge, filling the gaps between the tall column and the frame. */
.pc-blob--c {
  --pc-tone: var(--pc-glow);
  width: 900px;
  height: 900px;
  left: 90%;
  top: 64%;
  opacity: 0.07;
  animation-duration: 51s;
  animation-delay: -23s;
}
@keyframes pc-blob-drift {
  from { transform: translate(-50%, -50%) translate3d(-16px, 10px, 0) scale(1); }
  to { transform: translate(-50%, -50%) translate3d(16px, -12px, 0) scale(1.06); }
}

/* ------------------------------------------------------------------ the copy */
.pc-copy {
  position: absolute;
  left: 60px;
  top: 44px;
  width: 470px;
}

/* The section's wordmark, where the eyebrow, the headline and the lead were.
   It fills the copy column rather than being set to a fixed size, so it is as
   large as that column allows — 470px wide, which on a 1600px slide is the
   whole of the left third the copy used to occupy.

   No `invert` here, deliberately. Project Street's mark is black on a dark
   film and has to be turned white; this one is two-tone black and orange on
   this slide's pale ground, its orange already the deck's own accent, and
   inverting it would throw both away. Two marks, two treatments, and the
   difference is the ground each one stands on. */
.pc-mark {
  display: block;
  width: 100%;
  height: auto;
  max-width: 470px;
  margin: 8px 0 34px;
}

@media (max-width: 900px) {
  .pc-mark { max-width: 340px; margin-bottom: 22px; }
}
.pc-eyebrow {
  margin: 0 0 36px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  animation: pc-in 600ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: var(--intro-hold, 0s);
}
/* The mark the reference sets before its wordmark: two dots, one filled. */
.pc-eyebrow__mark {
  position: relative;
  width: 22px;
  height: 10px;
}
.pc-eyebrow__mark::before,
.pc-eyebrow__mark::after {
  content: '';
  position: absolute;
  top: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.pc-eyebrow__mark::before { left: 0; border: 2px solid var(--pc-ink); }
.pc-eyebrow__mark::after { left: 12px; background: var(--pc-accent); }

.pc-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 68px;
  line-height: 1.04;
  letter-spacing: -0.02em;
}
.pc-title__line { display: block; }

.pc-lead {
  margin: 28px 0 0;
  max-width: 34ch;
  font-size: 15px;
  line-height: 1.6;
  color: rgba(17, 17, 17, 0.72);
  animation: pc-in 700ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 720ms);
}

/* The one pill, in ink, as the reference has it. */
.pc-cta {
  margin-top: 30px;
  padding: 13px 24px;
  border: 0;
  border-radius: 999px;
  background: var(--pc-ink);
  color: var(--pc-paper);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1), background 220ms ease;
  animation: pc-in 700ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 900ms);
}
.pc-cta:hover { transform: translateY(-2px); background: #000; }

/* ------------------------------------------------------------------ the list */
.pc-points {
  position: absolute;
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 14px;
}
.pc-points li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.005em;
  animation: pc-in 640ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
  animation-delay: calc(var(--intro-hold, 0s) + 1000ms + var(--pc-i, 0) * 110ms);
}
.pc-points__arrow {
  color: var(--pc-accent);
  font-size: 14px;
  transform: translateY(1px);
}

/* --------------------------------------------------------------- the collage */
.pc-card {
  position: absolute;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* The turn is a real turn, so the card needs depth of its own. */
  perspective: 1000px;
  /* Each slot names its own entrance, delay and duration; the component writes
     the two numbers, the class picks the motion. `both` so a card is invisible
     until its turn. */
  animation-duration: var(--pc-dur, 1000ms);
  animation-delay: calc(var(--intro-hold, 0s) + var(--pc-delay, 0ms));
  animation-timing-function: cubic-bezier(0.16, 0.84, 0.3, 1);
  animation-fill-mode: both;
}
/* The half-turn that swaps a photograph (2026-09-18). The rotation is on the
   IMAGE, never on `.pc-card__face`: the face already carries the hover, and two
   rules fighting over one transform is the trap this component's own note
   warns about — it is why the entrance, the hover and the layout are three
   layers here in the first place.

   Half the turn runs, the component puts the new picture in at the 90° point
   where nothing of the old one is facing the room, and the second half brings
   it back. So one flip is one transition played out and back, and there is no
   moment where the card is empty. */
.pc-card__face img {
  transition: transform 0.36s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.36s ease;
  backface-visibility: hidden;
}

.pc-card.is-flip .pc-card__face img {
  transform: rotateY(90deg);
  opacity: 0.55;
}

@media (prefers-reduced-motion: reduce) {
  .pc-card__face img { transition: none; }
}

.pc-card--rise   { animation-name: pc-rise; }
.pc-card--drift  { animation-name: pc-drift; animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1); }
.pc-card--slide  { animation-name: pc-slide; }
.pc-card--unfold { animation-name: pc-unfold; }
.pc-card--sink   { animation-name: pc-sink; }

@keyframes pc-rise   { from { opacity: 0; transform: translateY(64px); }  to { opacity: 1; transform: none; } }
@keyframes pc-drift  { from { opacity: 0; transform: translateY(96px) scale(1.05); } to { opacity: 1; transform: none; } }
@keyframes pc-slide  { from { opacity: 0; transform: translateX(56px); }  to { opacity: 1; transform: none; } }
@keyframes pc-unfold { from { opacity: 0; transform: scale(0.9); }        to { opacity: 1; transform: none; } }
@keyframes pc-sink   { from { opacity: 0; transform: translateY(-44px); } to { opacity: 1; transform: none; } }

/* The face carries the hover, so it never fights the entrance for the same
   transform. */
.pc-card__face {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  border-radius: 16px;
  background: #EEE;
  box-shadow: 0 10px 28px rgba(17, 17, 17, 0.12);
  transition: transform 320ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 320ms ease;
}
.pc-card:hover .pc-card__face,
.pc-card:focus-visible .pc-card__face {
  transform: translateY(-6px) scale(1.02);
  box-shadow: 0 24px 48px rgba(17, 17, 17, 0.2);
}
.pc-card:focus-visible { outline: none; }
/* A card is a window on its photograph, and the photographs are not the shape
   of the windows — five are 16:9 and four are 3:2, against cards from 0.74 to
   2.0. So the face covers, looking where the publisher pointed it, and the
   whole picture is one press away in the viewer. Same trade as NT Square's
   fan, and made for the same reason. */
.pc-card__face img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
}

@keyframes pc-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ the wall */
/* Every further photograph, under the collage. Justified rows off the same
   solver the placements wall uses, so nothing is cropped: each tile is its own
   aspect ratio times the row's solved height. The bottom padding clears the
   presenter bar, which otherwise sits on the last row. */
.pc-more {
  position: relative;
  padding: 8px 60px calc(56px + var(--deck-bar-clear, 0px));
}
.pc-more__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 26px 0 22px;
  border-top: 1px solid rgba(17, 17, 17, 0.1);
}
.pc-more__label { margin: 0; animation: none; }
.pc-more__count {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(17, 17, 17, 0.5);
  font-variant-numeric: tabular-nums;
}
.pc-more__row {
  display: flex;
  gap: 16px;
  margin-bottom: 16px;
  justify-content: flex-start;
}
.pc-more__row:not(.is-full) { justify-content: center; }
.pc-tile {
  flex: 0 0 auto;
  position: relative;
  padding: 0;
  border: 0;
  border-radius: 16px;
  overflow: hidden;
  background: #EEE;
  box-shadow: 0 10px 28px rgba(17, 17, 17, 0.12);
  cursor: zoom-in;
  /* Off until it scrolls into view; a row arriving together staggers by its
     place in the row. Its own class, not the card's: the card's animations are
     an entrance for a slide, these are a reveal for a scroll. */
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 640ms cubic-bezier(0.22, 0.7, 0.28, 1) calc(var(--pc-n, 0) * 70ms),
    transform 760ms cubic-bezier(0.16, 0.84, 0.3, 1) calc(var(--pc-n, 0) * 70ms),
    box-shadow 320ms ease;
}
.pc-tile.is-seen { opacity: 1; transform: none; }
.pc-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 420ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.pc-tile.is-seen:hover { box-shadow: 0 24px 48px rgba(17, 17, 17, 0.2); }
.pc-tile.is-seen:hover img { transform: scale(1.035); }
.pc-tile:focus-visible { outline: 2px solid var(--pc-accent); outline-offset: 3px; }

.pc-more__foot {
  display: flex;
  justify-content: center;
  padding-top: 20px;
}
/* The way back up: the same pill, in outline. */
.pc-cta--ghost {
  margin-top: 0;
  background: transparent;
  color: var(--pc-ink);
  box-shadow: inset 0 0 0 1px rgba(17, 17, 17, 0.2);
  animation: none;
}
.pc-cta--ghost:hover { background: var(--pc-ink); color: var(--pc-paper); }

@media (prefers-reduced-motion: reduce) {
  .pc-eyebrow, .pc-lead, .pc-cta, .pc-points li, .pc-card, .pc-blob { animation: none; }
  .pc-card__face, .pc-cta, .pc-tile, .pc-tile img { transition: none; }
  .pc-tile { opacity: 1; transform: none; }
}

/* ==========================================================================
   Trainings — the open page  (TrainingShelf.js, second state)
   ==========================================================================
   Press the front book and the shelf closes around it: the book grows and
   travels to the left, and the right side carries everything about the
   programme. The shelf underneath fades but never moves, so the book can find
   its way back to exactly where it stood.
   ========================================================================== */

/* The way in, in the band under the shelf. */
.ts-info__open {
  margin-top: 22px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px 11px 20px;
  border: 0;
  border-radius: 999px;
  background: var(--ts-ink);
  color: #fff;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: transform 200ms cubic-bezier(0.22, 0.7, 0.28, 1), background 200ms ease;
}
.ts-info__open:hover { transform: translateY(-2px); background: #000; }
.ts-info__open .ic { width: 15px; height: 15px; transition: transform 200ms cubic-bezier(0.22, 0.7, 0.28, 1); }
.ts-info__open:hover .ic { transform: translateX(3px); }

/* The shelf, the plank, the band and the pill fade when a programme is open —
   fade, not display:none, because the book has to be able to measure its way
   back onto the shelf when it closes. */
.ts-top, .ts-shelf, .ts-under {
  transition: opacity 340ms ease;
}
.ts-root.is-open .ts-top,
.ts-root.is-open .ts-shelf,
.ts-root.is-open .ts-under {
  opacity: 0;
  pointer-events: none;
}
.ts-root.is-closing .ts-top,
.ts-root.is-closing .ts-shelf,
.ts-root.is-closing .ts-under {
  opacity: 1;
  transition-delay: 220ms;
}

/* ------------------------------------------------------------- the page */
.ts-detail {
  position: absolute;
  inset: 0;
  z-index: 6;
  overflow: hidden;
  background: var(--ts-paper);
  outline: none;
  visibility: hidden;
  opacity: 0;
  transition: opacity 360ms ease, visibility 0s linear 360ms;
}
.ts-root.is-open .ts-detail {
  visibility: visible;
  opacity: 1;
  transition: opacity 360ms ease;
}
.ts-root.is-closing .ts-detail {
  opacity: 0;
  transition: opacity 420ms ease 220ms;
}
/* It scrolls only when there is something below the fold. */
.ts-detail.has-photos { overflow-y: auto; }
.ts-detail::-webkit-scrollbar { width: 0; height: 0; }
.ts-detail { scrollbar-width: none; }

.ts-detail__bar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 40px 0 22px;
  pointer-events: none;
}
.ts-detail__bar > * { pointer-events: auto; }
.ts-detail__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 16px 9px 10px;
  border: 1px solid rgba(26, 22, 20, 0.14);
  border-radius: 999px;
  background: var(--ts-paper);
  color: var(--ts-ink);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: transform 180ms ease;
}
.ts-detail__back:hover { transform: translateX(-2px); }
.ts-detail__back .ic { width: 17px; height: 17px; }
.ts-nav--detail { position: static; animation: none; }

.ts-detail__grid {
  display: grid;
  grid-template-columns: 380px minmax(0, 1fr);
  column-gap: 76px;
  align-items: start;
  padding: 26px 58px 0;
  min-height: calc(860px - 70px);
}

/* The book, grown. Same parts as a book on the shelf, at 380 wide — and the
   transform that carries it there is written by the component, once, and
   released; this is the transition that carries it. */
.ts-big {
  position: relative;
  width: 380px;
  height: 545px;
  margin-top: 30px;
  transform-origin: 0 0;
  transition: transform 720ms cubic-bezier(0.2, 0.75, 0.2, 1);
  will-change: transform;
}
.ts-big .ts-book__body {
  box-shadow:
    0 48px 80px rgba(26, 22, 20, 0.24),
    0 6px 14px rgba(26, 22, 20, 0.12);
}
.ts-big .ts-book__cover { padding: 48px 42px 44px 50px; }
.ts-big .ts-book__track { font-size: 12px; }
.ts-big .ts-book__name { margin-top: 20px; font-size: 38px; }
.ts-big .ts-book__rule { margin-top: 22px; width: 54px; }
.ts-big .ts-book__meta { font-size: 13px; }
.ts-big .ts-book__spine { width: 16px; }
.ts-big .ts-book__pages { right: -9px; width: 10px; }

/* The information. Set large, because the slide is now about one thing. */
.ts-detail__info {
  padding-top: 26px;
  padding-bottom: 40px;
}
.ts-detail__info > * {
  animation: ts-info-in 560ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
}
.ts-detail__info > *:nth-child(2) { animation-delay: 70ms; }
.ts-detail__info > *:nth-child(3) { animation-delay: 140ms; }
.ts-detail__info > *:nth-child(4) { animation-delay: 210ms; }
.ts-detail__info > *:nth-child(5) { animation-delay: 280ms; }
/* Turning to another programme while open replays the entrance. */
.ts-detail.is-turning .ts-detail__info > * { animation-name: ts-info-in; }

.ts-detail__index {
  margin: 0 0 16px;
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.ts-detail__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 66px;
  line-height: 1.0;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.ts-detail__blurb {
  margin: 26px 0 0;
  max-width: 44ch;
  font-size: 22px;
  line-height: 1.55;
  color: rgba(26, 22, 20, 0.78);
}

.ts-detail__label {
  margin: 0 0 14px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(26, 22, 20, 0.45);
}
.ts-detail__teach { margin-top: 40px; }
/* The syllabus, highlighted: each subject on its own line with its number in
   the programme's red, in two columns when there are more than four. */
.ts-detail__subjects {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: 10px 32px;
}
.ts-detail__subjects li {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid rgba(26, 22, 20, 0.1);
}
.ts-detail__n {
  flex: 0 0 auto;
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  color: var(--ts-tint, var(--ts-red));
  font-variant-numeric: tabular-nums;
}
.ts-detail__subject {
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.008em;
}

.ts-detail__more {
  margin-top: 36px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: 26px 40px;
}
.ts-detail__text {
  margin: 0;
  font-size: 17px;
  line-height: 1.55;
  color: rgba(26, 22, 20, 0.74);
}

/* ------------------------------------------------------------ the photos */
.ts-detail__photos {
  padding: 24px 58px calc(48px + var(--deck-bar-clear, 0px));
}
.ts-detail__photos:empty { display: none; }
.ts-detail__row {
  display: flex;
  gap: 14px;
  margin-bottom: 14px;
  justify-content: flex-start;
}
.ts-detail__row:not(.is-full) { justify-content: center; }
.ts-detail__photo {
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  background: #EFE9E1;
  border-radius: 14px;
  overflow: hidden;
  cursor: zoom-in;
}
.ts-detail__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (prefers-reduced-motion: reduce) {
  .ts-top, .ts-shelf, .ts-under, .ts-detail, .ts-big { transition: none; }
  .ts-detail__info > * { animation: none; }
}


/* ==========================================================================
   Project Street — the film, then the thread board  (ThreadBoard.js)
   ==========================================================================
   Built to a reference: a pale sheet of graph paper, white cards leaning a
   few degrees each way, a dashed thread running pin to pin between them. The
   reference reads down a page; this reads across a slide, the cards alternating
   high and low with the thread weaving between the rows. Two screens stacked:
   the film first, the board below it, moved by the component's own eased loop.
   ========================================================================== */
.tb-root {
  --tb-paper: #F3F3F1;
  --tb-grid: rgba(20, 20, 20, 0.055);
  --tb-ink: #171717;
  --tb-muted: rgba(23, 23, 23, 0.62);
  --tb-thread: #2B2B2B;
  --tb-card: #FFFFFF;
  --tb-accent: #F05D29;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: #0B0B0B;
  color: var(--tb-ink);
  outline: none;
}
.tb-pages {
  position: absolute;
  inset: 0;
  will-change: transform;
}
/* Each screen is the root's full height; the board sits exactly one screen
   down. Percentages are safe here because every thing on either screen is
   anchored to its own top in px — the root growing 860 -> 900 on arrival moves
   the board, which is off screen, and nothing on the film. */
.tb-film, .tb-board {
  position: absolute;
  left: 0;
  right: 0;
  height: 100%;
}
.tb-film { top: 0; }
.tb-board { top: 100%; }

/* ------------------------------------------------------------------ the film */
.tb-film { background: #0B0B0B; }
.tb-film__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.35s ease;
}
/* Held invisible over the film's own dark ground until it is at `videoStart`
   and playing, so the room sees a film begin rather than a frame replaced. */
.tb-film__media.is-seeking { opacity: 0; }
.tb-film__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(0deg, rgba(6, 6, 8, 0.86) 0%, rgba(6, 6, 8, 0.5) 34%, rgba(6, 6, 8, 0.08) 62%, rgba(6, 6, 8, 0) 100%),
    linear-gradient(90deg, rgba(6, 6, 8, 0.45) 0%, rgba(6, 6, 8, 0) 55%);
}
.tb-film__copy {
  position: absolute;
  left: 64px;
  bottom: calc(64px + var(--deck-bar-clear, 0px));
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  color: #fff;
}
.tb-film__title {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 110px;
  line-height: 0.96;
  letter-spacing: -0.02em;
  color: #fff;
}

/* The section's own wordmark, where the name was set in type. Sized to stand
   where the 110px line stood rather than to the file's own pixels, so the
   screen's composition is unchanged.

   `invert(1)` is what makes it white. The file is black on transparency, so
   inverting turns every black pixel white and leaves the transparency exactly
   as it was — including the anti-aliased edges, which keep their alpha and so
   stay clean. That is why no second white copy of the file is needed, and why
   there is nothing to keep in step if the original is ever replaced.

   The drop-shadow is not decoration. The film runs bright in places and the
   scrim cannot be deepened without changing the other two decks that share it,
   so the mark carries its own separation — the same reasoning that put a black
   plate under the alliance captions rather than a deeper gradient. */
.tb-film__mark {
  display: block;
  width: auto;
  height: 132px;
  max-width: min(560px, 100%);
  object-fit: contain;
  filter: invert(1) drop-shadow(0 6px 26px rgb(0 0 0 / 0.55));
}

@media (max-width: 900px) {
  .tb-film__mark { height: 88px; }
}

/* The outlined pill the reference sets above its headline. */
.tb-pill {
  margin: 0;
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border: 1px solid rgba(23, 23, 23, 0.34);
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tb-ink);
  background: transparent;
}
.tb-pill--light { color: #fff; border-color: rgba(255, 255, 255, 0.5); }

.tb-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 18px 13px 22px;
  border: 0;
  border-radius: 999px;
  background: var(--tb-ink);
  color: #fff;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1), background 220ms ease, color 220ms ease;
}
.tb-cta:hover { transform: translateY(-2px); }
.tb-cta--light { background: #fff; color: #111; }
.tb-cta--light:hover { background: #fff; }
.tb-chev { width: 16px; height: 16px; display: block; }
.tb-cta .tb-chev { animation: tb-nudge 1.8s ease-in-out infinite; }
@keyframes tb-nudge { 0%, 100% { translate: 0 0; } 50% { translate: 0 3px; } }

/* ----------------------------------------------------------------- the board */
.tb-board {
  background-color: var(--tb-paper);
  /* The graph paper: two hairline grids, one each way. */
  background-image:
    linear-gradient(var(--tb-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--tb-grid) 1px, transparent 1px);
  background-size: 48px 48px;
  background-position: 0 0;
}
.tb-intro {
  position: absolute;
  left: 60px;
  top: 92px;
  width: 400px;
  z-index: 2;
}
.tb-intro > * { opacity: 0; transform: translateY(14px); transition: opacity 600ms cubic-bezier(0.22, 0.7, 0.28, 1), transform 700ms cubic-bezier(0.22, 0.7, 0.28, 1); }
.tb-intro.is-seen > * { opacity: 1; transform: none; }
.tb-intro.is-seen > :nth-child(2) { transition-delay: 80ms; }
.tb-intro.is-seen > :nth-child(3) { transition-delay: 520ms; }
.tb-intro__title {
  margin: 26px 0 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 54px;
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--tb-ink);
}
.tb-intro__line { display: block; }
.tb-intro__lead {
  margin: 24px 0 0;
  max-width: 34ch;
  font-size: 15px;
  line-height: 1.6;
  color: var(--tb-muted);
}

/* The band: everything right of the copy, clipped, with the paper fading the
   cards out as they pass behind the copy rather than cutting them off. */
.tb-band {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 500px;
  right: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 130px, #000 calc(100% - 24px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 130px, #000 calc(100% - 24px), transparent 100%);
}
.tb-track {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  will-change: transform;
}
.tb-thread {
  position: absolute;
  top: 0;
  left: 0;
  overflow: visible;
  pointer-events: none;
}
/* A segment of the thread. Dashed, so it cannot be "drawn" with a dash offset;
   it is uncovered instead, left to right, by a clip that opens across it. The
   reference box of a clip on an SVG element is the element's own bounding box,
   which is exactly the segment. */
.tb-seg {
  fill: none;
  stroke: var(--tb-thread);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-dasharray: 7 7;
  clip-path: inset(-6px 100% -6px -6px);
  transition: clip-path 560ms cubic-bezier(0.4, 0, 0.3, 1);
}
.tb-seg.is-seen { clip-path: inset(-6px -6px -6px -6px); }

/* A pin: a black head with a ring of paper around it, so it sits on the card
   rather than in it. */
.tb-pin {
  position: absolute;
  width: 11px;
  height: 11px;
  margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: #141414;
  box-shadow: 0 0 0 3px var(--tb-paper), 0 3px 6px rgba(0, 0, 0, 0.28);
  z-index: 3;
  transform: scale(0);
  transition: transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.tb-pin.is-seen { transform: scale(1); }

/* The card. Three layers, so nothing fights over a transform: the button
   arrives, the paper leans and lifts on hover, the photograph zooms. */
.tb-card {
  position: absolute;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  opacity: 0;
  transform: translateY(30px) scale(0.965);
  transition: opacity 720ms cubic-bezier(0.22, 0.7, 0.28, 1), transform 820ms cubic-bezier(0.16, 0.84, 0.3, 1);
  z-index: 2;
}
.tb-card.is-seen { opacity: 1; transform: none; }
.tb-card:focus-visible { outline: none; }
.tb-card__paper {
  position: absolute;
  inset: 0;
  display: block;
  padding: 0 14px 14px;
  border-radius: 14px;
  background: var(--tb-card);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 6px 14px rgba(0, 0, 0, 0.06),
    0 24px 48px rgba(0, 0, 0, 0.13);
  transform: rotate(var(--tb-tilt, 0deg));
  transition: transform 360ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 360ms ease;
  text-align: left;
}
.tb-card:hover .tb-card__paper,
.tb-card:focus-visible .tb-card__paper {
  transform: rotate(0deg) translateY(-8px) scale(1.015);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.06),
    0 14px 28px rgba(0, 0, 0, 0.1),
    0 40px 70px rgba(0, 0, 0, 0.18);
}
.tb-card__head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  height: 26px;
  padding: 0 2px;
}
.tb-card__num {
  font-family: var(--display);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: rgba(23, 23, 23, 0.55);
  font-variant-numeric: tabular-nums;
  line-height: 26px;
}
.tb-card__name {
  font-size: 12px;
  font-weight: 600;
  color: var(--tb-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tb-card__shot {
  display: block;
  border-radius: 8px;
  overflow: hidden;
  background: #EAEAE6;
}
.tb-card__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 520ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.tb-card:hover .tb-card__shot img { transform: scale(1.04); }
.tb-card.is-broken { display: none; }

/* The sign-off the thread ends on, in a hand. */
.tb-note {
  position: absolute;
  z-index: 2;
  opacity: 0;
  transform: translateY(10px) rotate(-3deg);
  transition: opacity 600ms ease, transform 700ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
.tb-note.is-seen { opacity: 1; transform: rotate(-3deg); }
.tb-note__text {
  display: block;
  font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', cursive;
  font-size: 24px;
  line-height: 1.2;
  color: var(--tb-ink);
  white-space: nowrap;
}
.tb-note__under {
  display: block;
  width: 220px;
  height: 14px;
  margin-top: 2px;
  overflow: visible;
}
.tb-note__under path { stroke: var(--tb-ink); stroke-width: 1.6; stroke-linecap: round; }

/* One card either way. Inside the board so it turns with it. */
.tb-nav {
  position: absolute;
  top: 28px;
  right: 40px;
  z-index: 4;
  display: inline-flex;
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 10px 24px rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(6px);
}
.tb-nav__btn {
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tb-ink);
  cursor: pointer;
  transition: background 180ms ease;
}
.tb-nav__btn:hover { background: rgba(23, 23, 23, 0.08); }
.tb-nav__btn .tb-chev { width: 18px; height: 18px; }

@media (prefers-reduced-motion: reduce) {
  .tb-seg, .tb-pin, .tb-card, .tb-card__paper, .tb-card__shot img, .tb-note, .tb-intro > *, .tb-cta { transition: none; }
  .tb-cta .tb-chev { animation: none; }
}


/* ==========================================================================
   Torii Minds — Project Showcase  (ProjectShowcase.js)
   ==========================================================================
   A port of the page the user supplied, kept rule for rule. Everything is
   scoped under `.ps-root` rather than living on `:root`/`.hero`, because the
   original sets `--ink`, `--line`, `--card`, `--font`, `--mono` and `--orange`
   — which are this deck's own variables — and because `.hero` is this deck's
   full-bleed hero block. Every other class name is the original's. The
   keyframes are prefixed for the same reason: `@keyframes` names are global,
   and this stylesheet already lost one animation to a name collision.
   The web fonts are gone (no internet at presentation time); the families are
   still named first and fall through to the stack the original listed.
   ========================================================================== */
.ps-root{
  --orange:#FF6F32; --orange-deep:#F05E25;
  --ink:#1B1B1B; --ink-2:#4A4744; --ink-3:#8B8681;
  --line:rgba(27,27,27,.10); --card:#FFFFFF;
  --bg-top:#F6F5F4; --bg-bottom:#E7E5E3;
  --title-top:#E3DDD8; --title-mid:#FFB58E; --title-bottom:var(--orange);

  --bezel:#212121; --bezel-edge:#151515; --bezel-lit:#3a3a3a; --screen:#F9F8FD;
  --neck-l:#666666; --neck-r:#4e4e4e; --neck-edge:#1c1c1c;
  --plate:#333333; --plate-dark:#2a2a2a;
  --slat-hi:#7e7e7e; --slat-hi2:#666666; --slat-mid:#414141; --slat-mid2:#303030; --slat-gap:#282828;

  --ease:cubic-bezier(.16,1,.3,1);
  --font:Inter,system-ui,-apple-system,"Segoe UI",Arial,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,Menlo,monospace;

  position:relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. */
  flex:0 0 auto;
  height:860px;
  max-height:860px;
  overflow:hidden;
  border-radius:var(--radius-lg);
  background:linear-gradient(180deg,var(--bg-top),var(--bg-bottom));
  font-family:var(--font);
  color:var(--ink);
  outline:none;
}
.ps-root.dark{
  --ink:#F3EFEB; --ink-2:#C9C2BC; --ink-3:#8D857F; --line:rgba(255,255,255,.10);
  --card:#1E1B19; --bg-top:#171412; --bg-bottom:#0E0C0B;
  --title-top:#2B2624; --title-mid:#7A3B22;
}
.ps-root *{box-sizing:border-box;}
/* Measures `--deck-bar-clear` — see the note in the component. */
.ps-root .ps-probe{position:absolute;left:0;top:0;width:0;height:var(--deck-bar-clear,0px);visibility:hidden;pointer-events:none;}
.ps-root .canvas{position:absolute;left:0;top:0;width:1920px;height:1080px;transform-origin:0 0;
  transform:translate(var(--ox,0px),var(--oy,0px)) scale(var(--s,1));}
.ps-root .canvas::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(55% 50% at 70% 40%,rgba(255,255,255,.55),rgba(255,255,255,0) 70%),
             radial-gradient(38% 38% at 90% 18%,rgba(255,111,50,.08),rgba(255,111,50,0) 70%);}
.ps-root.dark .canvas::before{background:radial-gradient(55% 50% at 70% 40%,rgba(255,255,255,.04),rgba(255,255,255,0) 70%),
             radial-gradient(38% 38% at 90% 18%,rgba(255,111,50,.14),rgba(255,111,50,0) 70%);}

/* ---------------- brand ---------------- */
.ps-root .brand{position:absolute;left:96px;top:64px;display:flex;align-items:center;gap:12px;z-index:5;
  font-weight:600;font-size:15px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);
  opacity:0;animation:ps-fadeUp .9s var(--ease) 1.4s forwards;}
.ps-root .brand i{display:block;width:22px;height:22px;border:3px solid var(--orange);border-bottom:0;position:relative;}
.ps-root .brand i::after{content:"";position:absolute;left:8px;top:6px;width:3px;height:16px;background:var(--ink);}
/* The real mark, sized to the drawn gate it replaces. The file is a 420px square
   whose ink runs 54–366 across and 45–374 down — 78.6% of the height — so a 28px
   box puts the gate at the 22px the drawn one stood, rather than three-quarters
   of it. `contain`, so the transparent margin stays margin. */
.ps-root .brand__mark{display:block;width:28px;height:28px;object-fit:contain;max-width:none;}

/* ---------------- title behind the monitor ---------------- */
.ps-root .title{position:absolute;left:770px;top:52px;margin:0;z-index:1;font-weight:500;font-size:300px;line-height:1.1;
  letter-spacing:-.045em;white-space:nowrap;padding-bottom:.12em;pointer-events:none;user-select:none;
  opacity:0;transform:translateY(40px);filter:blur(14px);font-family:var(--font);}
.ps-root .title.in{animation:ps-titleIn 1.3s var(--ease) .95s forwards;}
.ps-root .title .ch{display:inline-block;background:linear-gradient(180deg,var(--title-top) 0%,var(--title-mid) 58%,var(--title-bottom) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;}
.ps-root .title .ch.space{width:.22em;}
.ps-root .title.swap-out .ch{animation:ps-chOut .32s var(--ease) both;}
.ps-root .title.swap-in .ch{animation:ps-chIn .7s var(--ease) both;}
@keyframes ps-titleIn{to{opacity:1;transform:none;filter:blur(0);}}
@keyframes ps-chOut{to{opacity:0;transform:translateY(-.18em);filter:blur(10px);}}
@keyframes ps-chIn{from{opacity:0;transform:translateY(.22em);filter:blur(12px);}to{opacity:1;transform:none;filter:blur(0);}}

/* ---------------- the rig (measured 735x870), shifted right ---------------- */
/* Raised from scale 1.7 and moved up and right (2026-09-17): the screen was
   854x598 of the 1920x1080 canvas and a recording or a site in it could not be
   read from a room. The three numbers are bounded by things that must not move:
   the left column of files and the detail sheet end at canvas x=756, so the
   screen's left edge stays past 780; the canvas is 1920 wide, so its right edge
   stays inside 1900; and the presenter bar covers the last 138 canvas pixels, so
   its bottom edge stays above 942. At 2.1 the screen is 1055x739 — 23% wider —
   from (829,88) to (1884,827), which is as large as those three allow. */
.ps-root .rig{position:absolute;left:0;top:0;width:735px;height:870px;transform-origin:0 0;transform:translate(600px,-300px) scale(2.1);z-index:2;}
.ps-root .desk{position:absolute;inset:0;opacity:0;transform:translateY(70px);animation:ps-riseIn 1s var(--ease) .05s forwards;}
.ps-root .display{position:absolute;inset:0;opacity:0;transform:translateY(46px);animation:ps-riseIn 1.05s var(--ease) .45s forwards;}
@keyframes ps-riseIn{to{opacity:1;transform:none;}}
.ps-root .panel{position:absolute;left:-1500px;top:642px;width:4000px;height:600px;transform-origin:1867.5px 0;transform:rotate(-1.4deg);
  background:repeating-linear-gradient(90deg,var(--slat-hi) 0px,var(--slat-hi2) 5px,var(--slat-mid) 5px,var(--slat-mid2) 20px,var(--slat-gap) 20px,var(--slat-gap) 33px);background-position:2px 0;}
.ps-root .panel::before{content:"";position:absolute;left:0;right:0;top:0;height:12px;
  background:repeating-linear-gradient(90deg,rgba(255,255,255,.14) 0px,rgba(255,255,255,.10) 5px,rgba(255,255,255,0) 5px,rgba(255,255,255,0) 20px,rgba(0,0,0,.10) 20px,rgba(0,0,0,.10) 33px),
             linear-gradient(180deg,#6d6d6d 0%,#626262 40%,#555555 100%);background-position:2px 0,0 0;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 1px 0 rgba(0,0,0,.35);}
.ps-root .panel::after{content:"";position:absolute;inset:12px 0 0 0;background:linear-gradient(180deg,rgba(0,0,0,.10),rgba(0,0,0,0) 30%,rgba(0,0,0,0) 70%,rgba(0,0,0,.08));}
.ps-root .neck{position:absolute;left:336px;top:470px;width:120px;height:162px;transform:skewX(-3deg);transform-origin:0 100%;
  background:linear-gradient(90deg,var(--neck-l) 0%,#5a5a5a 45%,var(--neck-r) 93%,#6a6a6a 94.5%,var(--neck-edge) 96%,#202020 100%);}
.ps-root .neck::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.22) 0%,rgba(0,0,0,.22) 44%,rgba(0,0,0,0) 70%,rgba(0,0,0,.08) 84%,rgba(0,0,0,.34) 100%);}
.ps-root .plate{position:absolute;left:287px;top:636px;width:176px;height:10px;border-radius:3px 4px 3px 3px;
  background:linear-gradient(180deg,#5c5c5c 0,#565656 3px,#3a3a3a 4px,var(--plate) 60%,var(--plate-dark) 100%);
  box-shadow:0 1px 1px rgba(0,0,0,.45),inset 1px 0 0 rgba(255,255,255,.08);}
/* The monitor is laid out at twice its size and scaled back by half.
   ------------------------------------------------------------------
   The rig above it is scaled 2.1x, and the `matrix3d` here — the perspective
   that turns the monitor — makes this element its own 3D render surface.
   Chromium rasterises such a surface at the element's own size and the ancestor
   scale then stretches the result, so on a 1920 display a 1920x1080 screen
   recording was being drawn into a 503px box and blown up to 868px: every word
   in the film, and every word of a site opened in it, arrived as a smear.

   Doubling the box and pre-multiplying the transform by `scale(.5)` puts the
   same pixels in exactly the same place — `M · S` maps a point of the doubled
   box to `M` applied to the point it had before — while the surface, and the
   film or site drawn into it, is rasterised at twice the resolution. Every
   length below is doubled with it, so the bezel keeps its thickness.

   Measured before and after on a 1920x1080 screen: the film's box is 868x608
   at x 1001, y 198 either way, and the type in it goes from unreadable to
   legible. Nothing is scaled down and no quality is thrown away — this is the
   step that stops quality being thrown away.

   `.state`, the only other thing in the screen, counter-scales by a half so the
   two cards it draws keep the size they had; see below. */
.ps-root .monitor{position:absolute;left:0;top:0;width:1032px;height:700px;transform-origin:0 0;
  transform:matrix3d(0.96430412,-0.06434557,0,-0.00005601, 0,0.96,0,0, 0,0,1,0, 104.5,208,0,1) scale(.5);
  background:var(--bezel);border-radius:10px 12px 12px 10px;padding:8px 18px 16px 8px;
  box-shadow:inset 2px 2px 0 rgba(255,255,255,.06),inset -2px 0 0 var(--bezel-lit),inset 0 -2px 0 #2c2c2c,0 80px 160px -60px rgba(0,0,0,.35);}
.ps-root .screen{position:relative;width:100%;height:100%;background:var(--screen);border-radius:4px;overflow:hidden;box-shadow:0 0 0 2px var(--bezel-edge);}
.ps-root .screen .media{position:absolute;inset:0;opacity:0;transition:opacity .6s var(--ease);}
.ps-root .screen .media.show{opacity:1;}
.ps-root .screen .media > img,.ps-root .screen .media > video{display:block;width:100%;height:100%;object-fit:cover;border:0;}
/* A screen recording is shown whole, at its own ratio. These are browser-window
   captures at about 1.95:1 on a 1.47:1 screen, and `cover` cut a quarter off the
   sides — so the film is letterboxed on a dark screen, as a monitor would. */
.ps-root .screen .media > video{object-fit:contain;}
.ps-root .screen .media.media--video{background:#0B0B0B;}
/* The site is laid out for a real laptop viewport and scaled down into the
   screen, so it looks the way it does on a desk rather than as its own narrow
   breakpoint blown up 1.4x. The component writes the size and the scale from
   the screen it is given; pointer events map through the transform. */
.ps-root .screen .media > iframe{display:block;border:0;background:#fff;transform-origin:0 0;}
/* Doubled, because the monitor around it is now scaled by a half: every length
   written below is in the doubled box's pixels, and two of those are one of the
   old ones, so the card would come out half the size it was. Scaled about its
   own centre, where its content already sits, it lands exactly where it did —
   measured, the heading is 296x52 either way. The 8px the card rises through is
   16 here for the same reason, and is written before the scale so it is read in
   the box's own pixels rather than the scaled ones. */
.ps-root .state{position:absolute;inset:0;display:grid;place-items:center;text-align:center;color:#2A2723;
  transform-origin:50% 50%;
  opacity:0;transform:translateY(16px) scale(2);transition:opacity .5s var(--ease),transform .5s var(--ease);
  /* The two state cards sit over the screen whether or not they are shown, and
     `opacity:0` is still hit-testable — with a live site in the screen they ate
     every click. They carry nothing interactive, so they never take the
     pointer. The original page had nothing clickable in there to notice. */
  pointer-events:none;}
.ps-root .state.show{opacity:1;transform:scale(2);}
.ps-root .state .orb{position:absolute;left:50%;top:50%;width:260px;height:260px;border-radius:50%;transform:translate(-50%,-50%);
  background:radial-gradient(circle,rgba(255,111,50,.20),rgba(255,111,50,0) 70%);animation:ps-breathe 4.5s ease-in-out infinite;}
@keyframes ps-breathe{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.8;}50%{transform:translate(-50%,-50%) scale(1.22);opacity:1;}}
.ps-root .state .k{font-family:var(--mono);font-size:8px;letter-spacing:.24em;text-transform:uppercase;color:#8B8681;margin-bottom:8px;}
.ps-root .state .h{font-size:20px;font-weight:600;letter-spacing:-.02em;line-height:1.15;color:#1B1B1B;}
.ps-root .state .sub{font-size:9.5px;color:#6E6862;margin-top:8px;line-height:1.5;}
.ps-root .state .arrow{display:inline-block;width:26px;height:1.5px;background:#1B1B1B;position:relative;vertical-align:middle;margin-right:8px;animation:ps-nudge 1.8s var(--ease) infinite;}
.ps-root .state .arrow::before{content:"";position:absolute;left:0;top:-3.5px;width:8px;height:8px;border-left:1.5px solid #1B1B1B;border-bottom:1.5px solid #1B1B1B;transform:rotate(45deg);}
@keyframes ps-nudge{0%,100%{transform:translateX(0);}50%{transform:translateX(-6px);}}
.ps-root .state .bar{width:120px;height:2px;margin:14px auto 0;border-radius:2px;background:#E8E4E0;overflow:hidden;position:relative;}
.ps-root .state .bar::after{content:"";position:absolute;left:-40%;top:0;width:40%;height:100%;background:linear-gradient(90deg,transparent,var(--orange),transparent);animation:ps-shimmer 1.6s linear infinite;}
@keyframes ps-shimmer{to{left:100%;}}

/* ---------------- left: files ---------------- */
.ps-root .files{position:absolute;left:0;top:0;width:1920px;height:1080px;z-index:4;pointer-events:none;}
.ps-root .files > *{pointer-events:auto;}

.ps-root .list-head{position:absolute;left:96px;top:150px;width:656px;display:flex;justify-content:space-between;
  font-family:var(--mono);font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-3);
  opacity:0;animation:ps-fadeUp .8s var(--ease) 1.3s forwards;transition:opacity .4s var(--ease);}
.ps-root.has-selection .list-head{opacity:0 !important;pointer-events:none;}

/* the file — outer is positioned by JS, inner carries the look + entrance */
.ps-root .file{position:absolute;left:0;top:0;width:200px;height:250px;transform-origin:0 0;
  transition:transform .8s var(--ease),opacity .5s var(--ease);cursor:pointer;}
.ps-root .file-inner{
  position:relative;width:100%;height:100%;
  --g1:#FFFFFF; --g2:#FFE3D5;
  background:linear-gradient(160deg,var(--g1) 0%,var(--g2) 100%);
  clip-path:polygon(0 0,calc(100% - 46px) 0,100% 46px,100% 100%,0 100%);
  border-radius:14px 0 14px 14px;
  box-shadow:0 1px 2px rgba(27,27,27,.05),0 22px 44px -26px rgba(27,27,27,.35);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
  opacity:0;animation:ps-fadeUp .9s var(--ease) calc(1.35s + var(--d,0ms)) forwards;
}
.ps-root .file:hover .file-inner{transform:translateY(-6px) rotate(-.6deg);box-shadow:0 2px 4px rgba(27,27,27,.05),0 34px 60px -28px rgba(255,111,50,.35);}
.ps-root .file.selected .file-inner,.ps-root .file.mini .file-inner{transform:none;}
/* dog-ear fold */
.ps-root .file-inner::before{content:"";position:absolute;right:0;top:0;width:46px;height:46px;
  background:linear-gradient(225deg,transparent 50%,#F3E9E3 50%,#E8D9D0 100%);
  filter:drop-shadow(-2px 3px 3px rgba(27,27,27,.10));}
/* thin orange rule near the top */
/* NO RULE UNDER THE MARK (2026-09-18, on request). The original page drew a
   2px orange bar across the card at top:74 — under a 40px logo plate it read as
   an underline, and with the plate at 72px it runs straight through the mark.
   The card is its logo and its name now, so there is nothing for a rule to
   separate. Left as a commented selector rather than deleted, because it is the
   supplied page's own detail and this deck keeps those recoverable. */
/* .ps-root .file-inner::after{content:"";position:absolute;left:22px;right:70px;top:74px;height:2px;border-radius:2px;background:var(--orange);opacity:.85;} */
.ps-root .file .logo{position:absolute;left:22px;top:20px;width:72px;height:72px;border-radius:50%;background:var(--orange);display:grid;place-items:center;overflow:hidden;
  color:#fff;font-weight:600;font-size:16px;box-shadow:0 6px 14px -6px rgba(255,111,50,.7);}
.ps-root .file .logo img{width:100%;height:100%;object-fit:cover;display:block;max-width:none;}
/* A product's own logo (2026-09-17, on request). The design's badge is a 40px
   orange disc with the product's initial, and the five logos supplied are
   wordmarks 1.9:1 to 3.4:1 — in a 40px circle each is a cropped middle nobody
   can name, which is why the initial stood in until real art arrived. With art
   the badge is a white plate the same 40px tall, as wide as the wordmark needs
   up to 118px, the mark contained inside with 5px of air, and a hairline so
   the plate reads as a plate on the paper rather than as a mark floating on it.
   118 is the room the card has: it is 200 wide, the plate starts at 22, and the
   dog-ear fold takes the last 46 — 22 + 118 leaves 14 before the fold. Height
   stays 40 so the orange rule at 74 and the index at 94 keep their places. */
.ps-root .file .logo.has-art{width:auto;max-width:132px;min-width:72px;padding:7px 10px;border-radius:12px;
  background:#fff;border:1px solid rgba(27,27,27,.10);box-shadow:0 6px 14px -8px rgba(27,27,27,.25);}
/* Sized by HEIGHT, and nothing else. `width:auto; height:100%; max-width:100%`
   measured 100x53 on a 1.89:1 mark inside a 30px-tall content box — the
   percentage max-width resolved against the plate, the width won, and the
   height followed the ratio out of the bottom of the plate, where
   `overflow:hidden` cut it. A fixed height with an unconstrained width gives a
   1.85:1 mark 55px and a 3.41:1 mark 102px, both inside the 102px the plate's
   padding leaves; the plate, being shrink-to-fit, then wraps each mark rather
   than standing at its maximum for all seven. */
/* Bigger on request (2026-09-18), still sized by height alone — the rule this
   plate learned once: a percentage max-width lets the width win and the height
   falls out of the bottom of the plate. 30px -> 42, and the plate's cap grows
   with it so no mark is squeezed. The card is 200 wide, the plate starts at 22
   and the dog-ear takes the last 46, so 150 is the room there is. */
/* BOTH dimensions are capped here, which is the opposite of the rule this
   plate learned the first time — and the reason is that the mark is bigger now.
   Height alone was right while every mark fitted the card's width at that
   height; at 58px tall a 3.6:1 lockup wants 209px and the card has 132, so the
   plate's `overflow: hidden` cut it. With `width` and `height` both auto and a
   cap on each, the image scales down to fit inside whichever it meets first and
   keeps its ratio — no overflow, no squeeze, and the tall marks are unaffected
   because they never reach the width. The card is 200 wide, the plate starts at
   22 and the dog-ear takes the last 46, so 132 is the room there is. */
.ps-root .file .logo.has-art img{height:auto;width:auto;max-height:58px;max-width:112px;object-fit:contain;}
.ps-root .file .idx{position:absolute;left:22px;top:94px;font-family:var(--mono);font-size:10px;letter-spacing:.18em;color:var(--ink-3);}
.ps-root .file .meta{position:absolute;left:22px;right:22px;bottom:22px;}
.ps-root .file .name{display:block;font-size:19px;font-weight:600;letter-spacing:-.015em;line-height:1.15;color:#1B1B1B;}
.ps-root .file .tag{display:block;font-size:12px;color:#6E6862;margin-top:5px;line-height:1.35;}
.ps-root .file .kind{position:absolute;right:20px;bottom:22px;font-family:var(--mono);font-size:9px;letter-spacing:.22em;color:var(--ink-3);writing-mode:vertical-rl;transform:rotate(180deg);opacity:.7;}

/* opened header text next to the selected file */
.ps-root .head{position:absolute;left:224px;top:156px;width:520px;opacity:0;transform:translateY(10px);transition:opacity .5s var(--ease) .35s,transform .5s var(--ease) .35s;pointer-events:none;}
.ps-root.has-selection .head{opacity:1;transform:none;pointer-events:auto;}
.ps-root .head .eyebrow{font-family:var(--mono);font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-3);margin-bottom:10px;}
.ps-root .head .name{font-size:46px;font-weight:600;letter-spacing:-.03em;line-height:1;color:var(--ink);}
.ps-root .head .tag{font-size:16px;color:var(--ink-3);margin-top:12px;}
/* Three large buttons and nothing else in the sheet (2026-09-18, on request).
   They were 10.5px mono pills sized to sit under a name and a tag; with the
   sheet emptied they are the only controls on that half of the slide, and a
   control a presenter has to aim at from a lectern is one that has to be seen
   from one. Wrapped, because three at this size do not fit the 660px column on
   one line at every scale. */
.ps-root .head__actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;margin-top:18px;}
.ps-root .head .close,.ps-root .head .site,.ps-root .head .nextp{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
  background:none;border:1px solid var(--line);border-radius:999px;padding:14px 24px;cursor:pointer;transition:border-color .3s,color .3s,background .3s;}
.ps-root .head .close:hover,.ps-root .head .nextp:hover{border-color:var(--orange);color:var(--ink);}
/* The way into the site itself: the one filled control on the slide, because
   it is the one that changes what the monitor is showing. */
.ps-root .head .site{border-color:var(--orange);background:var(--orange);color:#fff;}
.ps-root .head .site:hover{background:var(--orange-deep);border-color:var(--orange-deep);}
.ps-root .head .site__mark{font-size:12px;line-height:1;transition:transform .3s var(--ease);}
.ps-root .head .site:hover .site__mark{transform:translate(2px,-2px);}
.ps-root.on-site .head .site{background:none;color:var(--ink-3);border-color:var(--line);}
.ps-root.on-site .head .site .site__mark{transform:rotate(180deg);}
.ps-root .head .site[hidden]{display:none;}

/* detail sheet */
/* The sheet is one mark now. It sits where the description used to start and
   takes the height the whole of it used to take — the point of emptying it was
   to give the product's own mark the room. Contained, never cropped: these are
   eight different lockups at eight different ratios and a cover fit would take
   the ends off the wide ones. */
.ps-root .detail{position:absolute;left:96px;top:330px;width:660px;height:300px;opacity:0;transform:translateY(16px);
  display:grid;place-items:center;
  transition:opacity .55s var(--ease),transform .55s var(--ease);pointer-events:none;}
.ps-root .detail.show{opacity:1;transform:none;pointer-events:auto;transition-delay:.42s;}
.ps-root .detail .mark{max-width:100%;max-height:300px;width:auto;height:auto;object-fit:contain;display:block;}
/* A product with no file of its own is set in its own name. */
.ps-root .detail .mark--type{font-family:var(--font);font-size:64px;font-weight:800;letter-spacing:-.02em;color:var(--ink);text-align:center;line-height:1.1;}
.ps-root .detail .desc{font-size:16.5px;line-height:1.7;color:var(--ink-2);margin:0 0 26px;max-width:56ch;}
.ps-root .detail .lbl{font-family:var(--mono);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-3);margin:0 0 12px;}
.ps-root .detail ul{list-style:none;margin:0 0 26px;padding:0;display:grid;grid-template-columns:1fr 1fr;gap:10px 28px;}
.ps-root .detail li{position:relative;padding-left:22px;font-size:14.5px;line-height:1.5;color:var(--ink-2);}
.ps-root .detail li::before{content:"";position:absolute;left:0;top:8px;width:8px;height:8px;border-radius:50%;background:var(--orange);}
.ps-root .detail .chips{display:flex;flex-wrap:wrap;gap:8px;}
.ps-root .detail .chip{font-family:var(--mono);font-size:11px;padding:7px 11px;border-radius:999px;border:1px solid var(--line);color:var(--ink-2);background:rgba(255,255,255,.5);}
.ps-root.dark .detail .chip{background:rgba(255,255,255,.04);}
/* A demo login: the role, and two buttons that copy. The credential itself is
   never drawn — see the note at the top of Platforms.js. */
.ps-root .detail .chip--login{display:inline-flex;align-items:center;gap:8px;padding:5px 6px 5px 12px;}
.ps-root .detail .chip--login b{font-weight:600;letter-spacing:.02em;}
.ps-root .detail .chip--login button{border:0;border-radius:999px;background:rgba(27,27,27,.07);color:var(--ink-2);
  font:inherit;font-family:var(--mono);font-size:10px;letter-spacing:.06em;padding:4px 9px;cursor:pointer;transition:background .2s,color .2s;}
.ps-root .detail .chip--login button:hover{background:var(--orange);color:#fff;}
.ps-root .detail .links{display:flex;gap:18px;margin-top:24px;}
.ps-root .detail .links a{font-size:13.5px;font-weight:500;color:var(--ink);text-decoration:none;border-bottom:1.5px solid var(--orange);padding-bottom:2px;}
.ps-root .detail > *{opacity:0;transform:translateY(8px);transition:opacity .5s var(--ease),transform .5s var(--ease);}
.ps-root .detail.show > *{opacity:1;transform:none;}
.ps-root .detail.show > *:nth-child(1){transition-delay:.5s}.ps-root .detail.show > *:nth-child(2){transition-delay:.56s}
.ps-root .detail.show > *:nth-child(3){transition-delay:.62s}.ps-root .detail.show > *:nth-child(4){transition-delay:.68s}
.ps-root .detail.show > *:nth-child(5){transition-delay:.74s}.ps-root .detail.show > *:nth-child(6){transition-delay:.80s}

/* dock */
.ps-root .dock{position:absolute;left:96px;top:var(--ps-dock-top,934px);display:flex;align-items:center;gap:12px;
  font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-2);
  background:rgba(255,255,255,.55);border:1px solid var(--line);border-radius:999px;padding:10px 16px 10px 14px;cursor:pointer;
  backdrop-filter:blur(8px);opacity:0;transform:translateY(14px);transition:opacity .5s var(--ease),transform .6s var(--ease),border-color .3s;pointer-events:none;}
.ps-root.dark .dock{background:rgba(255,255,255,.06);}
.ps-root.has-selection .dock{opacity:1;transform:none;pointer-events:auto;transition-delay:.45s;}
.ps-root .dock:hover{border-color:var(--orange);}
.ps-root .dock .stack{display:flex;}
.ps-root .dock .stack i{display:block;width:12px;height:15px;border-radius:2px 0 2px 2px;background:linear-gradient(160deg,#fff,#FFD9C6);border:1px solid rgba(27,27,27,.12);margin-left:-5px;}
.ps-root .dock .stack i:first-child{margin-left:0;}
.ps-root .dock svg{width:14px;height:14px;transition:transform .45s var(--ease);}
.ps-root.dock-open .dock svg{transform:rotate(180deg);}
.ps-root .dock-label{position:absolute;left:96px;top:706px;font-family:var(--mono);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-3);
  opacity:0;transform:translateY(6px);transition:opacity .4s var(--ease),transform .4s var(--ease);pointer-events:none;}
.ps-root.dock-open .dock-label{opacity:1;transform:none;transition-delay:.25s;}

@keyframes ps-fadeUp{to{opacity:1;transform:none;}}

/* The dock is the one control at the floor of the slide, and presenting floats
   the deck bar over exactly that band — so `--ps-dock-top` is solved in the
   component, in canvas pixels, once the scale is known. */

@media (prefers-reduced-motion:reduce){
  .ps-root *{animation-duration:.01ms !important;animation-delay:0s !important;transition-duration:.01ms !important;transition-delay:0s !important;}
  .ps-root .title,.ps-root .brand,.ps-root .list-head,.ps-root .file-inner,.ps-root .desk,.ps-root .display{opacity:1 !important;transform:none !important;filter:none !important;}
}

/* ==========================================================================
   Torii Connect — the folder that opens into a bento  (PhotoFolder.js)
   ==========================================================================
   Built to a reference the user supplied, shape for shape: a manila folder in
   the middle of the frame — back panel with its tab, document cards fanned out
   of the mouth, the pocket across the front with the folder's name and count.
   The button empties it into a bento of unequal tiles; Back sends the cards
   home along the same path. The reference is pale blue on blue-white; this is
   the same tonal structure in Torii's warm tints — cream fills, a faint orange
   rule, white paper.

   A card's box is its bento slot; `--fd-tucked` is the transform that carries
   that box back into the mouth, solved in the component. Opening and closing
   are one transition played in both directions, and nothing but a transform
   ever moves.
   ========================================================================== */
.fd-root {
  --fd-ink: #14110F;
  --fd-muted: rgba(20, 17, 15, 0.52);
  --fd-paper: #FFFFFF;
  --fd-accent: #F05D29;      /* measured off the mark on the brand film */
  --fd-accent-2: #D91823;    /* measured off the team's polo */
  /* The folder's own tints: the reference's pale blue, done in Torii's orange. */
  --fd-fill: #FFF6F1;
  --fd-fill-deep: #FDEDE4;
  --fd-rule: #F3CDBB;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: linear-gradient(176deg, #FFFFFF 0%, #FCF7F4 46%, #F7EFEA 100%);
  color: var(--fd-ink);
  outline: none;
}

/* ----------------------------------------------------------------- the ground */
/* Two soft washes of Torii's two colours, drifting. Each blob is its own element
   because a background's positions cannot be animated smoothly, and the drift
   repeats the centring translate in both keyframes. */
.fd-glow {
  --fd-glow: #F05D29;
  --fd-glow-2: #D91823;
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.fd-blob {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(84px);
  will-change: transform;
}
.fd-blob--a {
  left: 24%; top: 12%;
  width: 760px; height: 760px;
  background: radial-gradient(circle, color-mix(in srgb, var(--fd-glow) 34%, transparent), transparent 68%);
  opacity: 0.5;
  transform: translate(-50%, -50%);
  animation: fd-blob-drift 34s ease-in-out infinite;
}
.fd-blob--b {
  left: 82%; top: 30%;
  width: 640px; height: 640px;
  background: radial-gradient(circle, color-mix(in srgb, var(--fd-glow-2) 26%, transparent), transparent 68%);
  opacity: 0.38;
  transform: translate(-50%, -50%);
  animation: fd-blob-drift 44s ease-in-out infinite reverse;
}
.fd-blob--c {
  left: 56%; top: 88%;
  width: 900px; height: 900px;
  background: radial-gradient(circle, color-mix(in srgb, var(--fd-glow) 22%, transparent), transparent 70%);
  opacity: 0.42;
  transform: translate(-50%, -50%);
  animation: fd-blob-drift 52s ease-in-out infinite;
}
@keyframes fd-blob-drift {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(1.16) translateX(38px); }
}

/* ------------------------------------------------------------------- the head */
/* Only the way back lives up here, and only while the wall is open. */
.fd-head {
  position: absolute;
  left: 60px;
  right: 60px;
  /* 52 while the head held only the way back. The wordmark is taller than that
     button, so the row starts higher and the wall below it was given the ground
     this frees — see WALL_CY in PhotoFolder.js. */
  top: 34px;
  z-index: 40;
  display: flex;
  align-items: center;
  /* The mark at one end and the way back at the other. `space-between` puts
     them there without either one needing a width. */
  justify-content: space-between;
  gap: 24px;
  pointer-events: none;
}

/* The section's wordmark. Sized by height so its own width follows, and drawn
   in both states — folder standing and wall open — so the slide is named the
   whole time it is on screen. No `invert`: black and orange on a pale ground. */
.fd-mark {
  display: block;
  height: 86px;
  width: auto;
  max-width: min(460px, 46%);
  object-fit: contain;
}

/* With no mark the way back keeps the right-hand end to itself. */
.fd-head:not(:has(.fd-mark)) { justify-content: flex-end; }

@media (max-width: 900px) {
  .fd-mark { height: 56px; }
}
.fd-head > * { pointer-events: auto; }
.fd-back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 18px 10px 14px;
  border: 1px solid rgba(20, 17, 15, 0.14);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(8px);
  color: var(--fd-ink);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 420ms ease, transform 520ms cubic-bezier(0.22, 0.7, 0.28, 1), border-color 240ms ease;
}
.fd-root.is-open .fd-back {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  transition-delay: 520ms;
}
.fd-back:hover { border-color: var(--fd-accent); }
.fd-back__arrow { transition: transform 240ms cubic-bezier(0.22, 0.7, 0.28, 1); }
.fd-back:hover .fd-back__arrow { transform: translateX(-3px); }

/* ----------------------------------------------------------------- the folder */
/* The back panel — an SVG, because the tab needs a real path: a rounded sheet
   whose top edge steps down on the right through one soft curve. */
.fd-shell {
  position: absolute;
  z-index: 10;
  overflow: visible;
  transform-origin: 50% 100%;
  /* `backwards`, never `both`: a filling animation keeps writing its last
     keyframe and outranks every declaration, so with `both` the `opacity: 0`
     the open state asks for lost to the entrance's own `opacity: 1` and the
     folder simply never went away. */
  animation: fd-rise 900ms cubic-bezier(0.19, 0.84, 0.28, 1) backwards;
  animation-delay: var(--intro-hold, 0s);
  transition: opacity 420ms ease, transform 620ms cubic-bezier(0.22, 0.7, 0.28, 1);
  filter: drop-shadow(0 26px 44px rgba(20, 17, 15, 0.10));
}
.fd-shell__body {
  fill: var(--fd-fill);
  stroke: var(--fd-rule);
  stroke-width: 1.5;
}
/* The pocket across the front, with the folder's own name on it. */
.fd-pocket {
  position: absolute;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  padding: 0 44px 0 46px;
  border: 1.5px solid var(--fd-rule);
  border-radius: 30px;
  background: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 42%, var(--fd-fill) 100%);
  box-shadow:
    0 1px 2px rgba(20, 17, 15, 0.04),
    0 30px 56px -34px rgba(20, 17, 15, 0.40);
  animation: fd-rise 900ms cubic-bezier(0.19, 0.84, 0.28, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + 120ms);
  transition: opacity 380ms ease, transform 620ms cubic-bezier(0.22, 0.7, 0.28, 1);
}
@keyframes fd-rise {
  from { opacity: 0; transform: translateY(48px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
/* Open: the folder gets out of the way — and it is *behind* the cards the whole
   time, so a card reads as leaving the mouth rather than appearing over it. */
.fd-root.is-open .fd-shell,
.fd-root.is-open .fd-pocket {
  opacity: 0;
  transform: translateY(26px) scale(0.97);
  pointer-events: none;
}

.fd-pocket__copy { min-width: 0; }
/* The reference sets its name in a plain sans, dark, semibold — not the deck's
   display face — and this follows it. */
.fd-pocket__title {
  margin: 0;
  font-family: var(--font, 'Open Sans', 'Segoe UI', sans-serif);
  font-weight: 600;
  font-size: 40px;
  line-height: 1.08;
  letter-spacing: -0.022em;
  color: var(--fd-ink);
}
.fd-pocket__line { display: block; }
.fd-pocket__count {
  margin: 10px 0 0;
  font-size: 22px;
  color: var(--fd-muted);
  font-variant-numeric: tabular-nums;
}
.fd-open {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 26px;
  border: 0;
  border-radius: 999px;
  background: var(--fd-ink);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1), background 220ms ease;
}
.fd-open:hover { transform: translateY(-2px); background: var(--fd-accent); }
.fd-open__arrow { font-size: 13px; transition: transform 220ms cubic-bezier(0.22, 0.7, 0.28, 1); }
.fd-open:hover .fd-open__arrow { transform: translate(2px, -2px); }

/* ------------------------------------------------------------- a document */
/* Its box is the bento slot; closed, `--fd-tucked` carries that box into the
   folder's mouth. One transition, played both ways. */
.fd-card {
  position: absolute;
  z-index: 20;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  transform: var(--fd-tucked);
  transition:
    transform 900ms cubic-bezier(0.22, 0.86, 0.26, 1) var(--fd-delay, 0ms),
    opacity 420ms ease;
  /* The cards are in the folder when the slide arrives, so the entrance only
     fades them — `transform` is the property carrying the tuck. */
  animation: fd-fade 700ms cubic-bezier(0.22, 0.7, 0.28, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + 160ms + var(--fd-delay, 0ms));
}
.fd-root.is-open .fd-card { transform: none; }
.fd-card:focus-visible { outline: none; }
/* White paper with the reference's thin rule, the photograph set into it at its
   own ratio, the caption beneath. */
.fd-card__paper {
  position: absolute;
  inset: 0;
  display: block;
  padding: 14px;
  border: 1.5px solid var(--fd-rule);
  border-radius: 22px;
  background: var(--fd-paper);
  box-shadow:
    0 1px 2px rgba(20, 17, 15, 0.04),
    0 22px 44px -26px rgba(20, 17, 15, 0.38);
  transition: transform 340ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 340ms ease;
}
/* Closed, the cards are a fan in a pocket and must not lift out of it. */
.fd-root.is-open .fd-card:hover .fd-card__paper,
.fd-root.is-open .fd-card:focus-visible .fd-card__paper {
  transform: translateY(-8px) scale(1.012);
  box-shadow:
    0 2px 6px rgba(20, 17, 15, 0.06),
    0 36px 64px -28px rgba(20, 17, 15, 0.46);
}
.fd-card__shot {
  display: block;
  width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background: #EFE9E6;
}
.fd-card__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;   /* the box is the photograph's own ratio, so nothing is cropped */
}
.fd-card__text {
  display: block;
  height: var(--fd-cap, 96px);
  padding: 14px 6px 0;
  overflow: hidden;
}
.fd-card__name {
  display: block;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.014em;
  line-height: 1.2;
  color: var(--fd-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fd-card--small .fd-card__name { font-size: 17px; }
.fd-card__desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  margin-top: 6px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--fd-muted);
  overflow: hidden;
}

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

@media (prefers-reduced-motion: reduce) {
  .fd-blob { animation: none; }
  .fd-card, .fd-card__paper, .fd-shell, .fd-pocket, .fd-back, .fd-open { transition: none; }
  .fd-shell, .fd-pocket, .fd-card { animation: none; opacity: 1; }
}

/* ==========================================================================
   Events — the wheel of albums  (EventWheel.js)
   ==========================================================================
   Built to a reference the user supplied: on the left the rightmost sweep of a
   great ring whose rim is the photographs themselves — a continuous curved strip
   of tiles, one per event, each turned to follow the arc — the open event's tile
   large and lit with its name on a card beside it; on the right the open album
   running down a column. The reference's dark room and its ring of icons are
   both left out on request: this is Torii's light sheet with its two measured
   colours washing softly behind. Everything that moves is placed by the
   component's own rAF loop — the tiles each frame from the wheel's position,
   the column from its scroll — so nothing here transitions `transform`.
   ========================================================================== */
.ew-root {
  --ew-ink: #14110F;
  --ew-muted: rgba(20, 17, 15, 0.56);
  --ew-faint: rgba(20, 17, 15, 0.22);
  --ew-accent: #F05D29;          /* measured off the mark on the brand film */
  --ew-accent-2: #D91823;        /* measured off the team's polo */
  --ew-band: #F1EBE6;            /* the rim between tiles */
  --ew-card: #FFFFFF;

  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. */
  flex: 0 0 auto;
  height: 860px;
  max-height: 860px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: linear-gradient(176deg, #FFFFFF 0%, #FCF7F4 46%, #F7EFEA 100%);
  color: var(--ew-ink);
  outline: none;
}

/* ----------------------------------------------------------------- the ground */
/* Two soft washes of Torii's two colours, drifting — the same ground as Torii
   Connect, NT Square and Project Week, at the same strength. */
.ew-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.ew-blob {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(84px);
  will-change: transform;
}
.ew-blob--a {
  left: 62%; top: 10%;
  width: 760px; height: 760px;
  background: radial-gradient(circle, color-mix(in srgb, var(--ew-accent) 30%, transparent), transparent 68%);
  opacity: 0.46;
  transform: translate(-50%, -50%);
  animation: ew-blob-drift 34s ease-in-out infinite;
}
.ew-blob--b {
  left: 92%; top: 62%;
  width: 640px; height: 640px;
  background: radial-gradient(circle, color-mix(in srgb, var(--ew-accent-2) 24%, transparent), transparent 68%);
  opacity: 0.36;
  transform: translate(-50%, -50%);
  animation: ew-blob-drift 44s ease-in-out infinite reverse;
}
.ew-blob--c {
  left: 34%; top: 96%;
  width: 900px; height: 900px;
  background: radial-gradient(circle, color-mix(in srgb, var(--ew-accent) 20%, transparent), transparent 70%);
  opacity: 0.4;
  transform: translate(-50%, -50%);
  animation: ew-blob-drift 52s ease-in-out infinite;
}
@keyframes ew-blob-drift {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(1.16) translateX(38px); }
}

/* ------------------------------------------------------------------- the ring */
.ew-wheel {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.ew-band {
  position: absolute;
  left: 0;
  top: 0;
  width: 1600px;
  height: 900px;
  overflow: visible;
  pointer-events: none;
}
/* The rim shows only in the seams between tiles and beyond the ends of the
   visible strip — a soft, slightly darker sheet. */
.ew-band__ring { fill: none; stroke: var(--ew-band); }

/* A tile on the rim: one event's cover. Placed, turned and scaled by the loop;
   only its look is here. Its box is radial x tangential, so at the front it
   stands a little taller than wide and the strip reads as a bent ribbon. */
.ew-tile {
  position: absolute;
  left: 0;
  top: 0;
  padding: 0;
  border: 0;
  border-radius: 10px;
  overflow: hidden;
  background: #E8E1DB;
  cursor: pointer;
  transform-origin: 50% 50%;
  box-shadow: 0 8px 22px -12px rgba(20, 17, 15, 0.45);
  /* Entrance only — the loop owns `transform`, so this fades. */
  animation: ew-fade 700ms cubic-bezier(0.22, 0.7, 0.28, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + 120ms);
  outline: 3px solid transparent;
  outline-offset: 0;
  transition: outline-color 260ms ease, box-shadow 260ms ease;
}
.ew-tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ew-tile.is-open {
  outline-color: var(--ew-accent);
  box-shadow: 0 22px 44px -18px rgba(20, 17, 15, 0.5);
}
.ew-tile:focus-visible { outline-color: var(--ew-accent); }

/* ------------------------------------------------------------ the name */
/* Inside the ring, against the inner rim. The word carries a quiet gradient —
   ink into Torii's orange over its last letters — and nothing else. */
.ew-title {
  position: absolute;
  left: 0;
  z-index: 4;
  padding-left: 40px;
  text-align: right;
  transform: translateY(-50%);
  pointer-events: none;
  animation: ew-rise 800ms cubic-bezier(0.22, 0.7, 0.28, 1) backwards;
  animation-delay: calc(var(--intro-hold, 0s) + 260ms);
}
.ew-title__word {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: 44px;
  line-height: 1;
  letter-spacing: 0.02em;
  background: linear-gradient(100deg, var(--ew-ink) 0%, var(--ew-ink) 46%, #7A3B22 74%, var(--ew-accent) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
.ew-title__sub {
  display: block;
  margin-top: 8px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ew-muted);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------- the card */
.ew-card {
  position: absolute;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 14px;
  transform: translateY(-50%);
}
/* The leader from the lit tile to the card, as the reference draws it. */
.ew-card__lead {
  display: block;
  width: 34px;
  height: 1px;
  background: linear-gradient(90deg, var(--ew-faint), rgba(20, 17, 15, 0.5));
}
.ew-card__body {
  width: 236px;
  padding: 16px 18px 14px;
  border-radius: 14px;
  background: var(--ew-card);
  border: 1px solid rgba(240, 93, 41, 0.16);
  box-shadow:
    0 1px 2px rgba(20, 17, 15, 0.04),
    0 24px 48px -30px rgba(20, 17, 15, 0.4);
}
.ew-card.is-in .ew-card__body > * {
  animation: ew-rise 520ms cubic-bezier(0.22, 0.7, 0.28, 1) both;
}
.ew-card.is-in .ew-card__body > :nth-child(2) { animation-delay: 60ms; }
.ew-card.is-in .ew-card__body > :nth-child(3) { animation-delay: 110ms; }
.ew-card.is-in .ew-card__body > :nth-child(4) { animation-delay: 150ms; }
.ew-card.is-in .ew-card__body > :nth-child(5) { animation-delay: 190ms; }
.ew-card__kicker {
  margin: 0 0 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ew-accent);
}
.ew-card__name {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.012em;
  line-height: 1.2;
  color: var(--ew-ink);
}
.ew-card__meta {
  margin: 6px 0 0;
  font-size: 12.5px;
  color: var(--ew-muted);
}
.ew-card__meta[hidden] { display: none; }
.ew-card__count {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--ew-muted);
  font-variant-numeric: tabular-nums;
}

/* The pills: two arrows, one pair for events in the card, one for photographs
   beside the album. Same drawing. */
.ew-pill {
  display: inline-flex;
  padding: 3px;
  border-radius: 999px;
  background: rgba(20, 17, 15, 0.05);
  border: 1px solid rgba(20, 17, 15, 0.08);
}
.ew-pill--events { margin-top: 14px; }
.ew-pill--photos { flex-direction: column; }
.ew-pill__btn {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ew-ink);
  cursor: pointer;
  transition: background 180ms ease, color 180ms ease, opacity 180ms ease;
}
.ew-pill__btn:hover { background: var(--ew-accent); color: #fff; }
.ew-pill__btn:disabled { opacity: 0.28; cursor: default; }
.ew-pill__btn:disabled:hover { background: transparent; color: var(--ew-ink); }
.ew-chev { width: 18px; height: 18px; display: block; }
/* In the card the pair lies side by side and points left and right. */
.ew-pill--events .ew-pill__btn:first-child .ew-chev { transform: rotate(90deg) !important; }
.ew-pill--events .ew-pill__btn:last-child .ew-chev { transform: rotate(-90deg) !important; }

/* ------------------------------------------------------------------ the album */
.ew-album {
  position: absolute;
  top: 0;
  bottom: 0;
  overflow: hidden;
  /* The column runs off both ends; the sheet swallows it rather than a hard cut. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 84px, #000 calc(100% - 84px), transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 84px, #000 calc(100% - 84px), transparent 100%);
}
.ew-column {
  position: absolute;
  left: 0;
  top: 84px;
  width: 100%;
  will-change: transform;
  transition: opacity 260ms ease;
}
.ew-column.is-leaving { opacity: 0; pointer-events: none; }
.ew-photo {
  position: absolute;
  left: 0;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 12px;
  overflow: hidden;
  background: #EFE9E6;
  cursor: zoom-in;
  box-shadow:
    0 1px 2px rgba(20, 17, 15, 0.05),
    0 18px 40px -24px rgba(20, 17, 15, 0.45);
  animation: ew-rise 640ms cubic-bezier(0.22, 0.7, 0.28, 1) backwards;
  animation-delay: calc(var(--ew-n, 0) * 60ms);
  transition: transform 300ms cubic-bezier(0.22, 0.7, 0.28, 1), box-shadow 300ms ease;
}
.ew-photo:hover { transform: translateY(-3px) scale(1.008); box-shadow: 0 2px 6px rgba(20, 17, 15, 0.06), 0 30px 56px -26px rgba(20, 17, 15, 0.5); }
.ew-photo:focus-visible { outline: 2px solid var(--ew-accent); outline-offset: 2px; }
.ew-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;   /* the box is the photograph's own ratio, so nothing is cropped */
}

/* ------------------------------------------------------------------- the meta */
.ew-meta {
  position: absolute;
  top: 50%;
  width: 220px;
  transform: translateY(-50%);
  z-index: 5;
}
.ew-meta__kicker {
  margin: 0 0 10px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ew-muted);
}
.ew-meta__name {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ew-ink);
}
.ew-meta__count {
  margin: 8px 0 18px;
  font-size: 13px;
  color: var(--ew-muted);
  font-variant-numeric: tabular-nums;
}

@keyframes ew-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ew-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .ew-blob, .ew-tile, .ew-photo, .ew-card.is-in .ew-card__body > * { animation: none; }
  .ew-photo, .ew-column, .ew-pill__btn, .ew-tile { transition: none; }
}

/* The open project's card is hidden by `layout()`, which writes opacity inline
   and would beat a rule here anyway; this only stops it catching the pointer. */
.ps-root .file.selected{pointer-events:none;}

/* The name under the mark in the opened sheet. */
.ps-root .detail .mark-name{display:block;margin-top:22px;font-family:var(--font);font-size:34px;font-weight:800;letter-spacing:-.015em;color:var(--ink);text-align:center;line-height:1.15;}

/* The three buttons: black, heavy and large. They are the only controls on that
   half of the slide and a presenter aims at them from a lectern. */
.ps-root .head .close,.ps-root .head .site,.ps-root .head .nextp{font-family:var(--font);font-size:17px;font-weight:800;letter-spacing:.01em;text-transform:none;color:#12100F;border-width:2px;border-color:#12100F;padding:16px 30px;}
.ps-root .head .close:hover,.ps-root .head .nextp:hover{background:#12100F;border-color:#12100F;color:#fff;}
.ps-root .head .site{color:#fff;border-color:var(--orange);}
.ps-root.on-site .head .site{color:#12100F;border-color:#12100F;}

/* ==========================================================================
   Curriculum deck — Torii's Trainings (2026-09-18)

   Eight programmes on a dark sheet; press one and it opens onto its numbered
   topics. Nothing here sets a description, because there are none.

   The type does not shrink to fit. The ask was that everything be readable
   from the room, so a programme with fifteen topics takes more columns rather
   than smaller words — `columnsFor` in the component picks 1, 2 or 3 off the
   count, and this sheet sets them all at one size.
   ========================================================================== */
.cu-root {
  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`,
     whose `flex-basis: 0%` beats `height` on the main axis. And never a bare
     860 — a slide presenting is the screen's shape, not 16:9. */
  flex: 0 0 auto;
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  display: flex;
  flex-direction: column;
  padding: 48px 64px 0;
  overflow: hidden;
  color: #F6F4F2;
  background:
    radial-gradient(120% 80% at 12% 0%, rgb(255 90 31 / 0.14) 0%, transparent 58%),
    #100E0D;
  --cu-accent: #FF5A1F;
  --cu-line: rgb(255 255 255 / 0.10);
  --cu-card: rgb(255 255 255 / 0.035);
}

.cu-head { flex: 0 0 auto; margin-bottom: 34px; }

.cu-eyebrow {
  margin: 0 0 10px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 13px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--cu-accent);
}

.cu-title {
  margin: 0;
  font-family: var(--display, inherit);
  font-size: 62px;
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: #FFF;
}

/* --------------------------------------------------------------- the grid */
.cu-grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* The rows share whatever height is left rather than standing at their
     minimum with the sheet empty below them. `minmax` keeps the minimum as a
     floor, so a deck with more programmes than fit still gets readable cards
     instead of squeezed ones. */
  grid-auto-rows: minmax(188px, 1fr);
  gap: 20px;
  align-content: stretch;
  padding-bottom: var(--deck-bar-clear, 32px);
}

.cu-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  min-height: 188px;
  padding: 26px 24px;
  text-align: left;
  border: 1px solid var(--cu-line);
  border-radius: 18px;
  background: var(--cu-card);
  color: inherit;
  cursor: pointer;
  font: inherit;
  /* Minimal, and once. This is the one slide in the deck that is read rather
     than watched, so nothing here loops or drifts. */
  opacity: 0;
  transform: translateY(14px);
}

.cu-root.is-ready .cu-card {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.46s cubic-bezier(0.22, 0.68, 0, 1) calc(var(--cu-i, 0) * 40ms),
    transform 0.46s cubic-bezier(0.22, 0.68, 0, 1) calc(var(--cu-i, 0) * 40ms),
    border-color 0.24s ease,
    background 0.24s ease;
}

.cu-card:hover,
.cu-card:focus-visible {
  border-color: var(--cu-accent);
  background: rgb(255 255 255 / 0.06);
  outline: none;
}

/* The number is a filled orange box — this sheet's one filled element, and what
   makes a card read as a step in a course rather than a tile. */
.cu-card__n {
  display: grid;
  place-items: center;
  min-width: 46px;
  height: 46px;
  padding: 0 10px;
  border-radius: 12px;
  background: var(--cu-accent);
  color: #12100F;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 18px;
  font-weight: 700;
}

.cu-card__name {
  font-family: var(--display, inherit);
  font-size: 25px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: #FFF;
}

.cu-card__meta {
  margin-top: auto;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(246 244 242 / 0.5);
}

/* ------------------------------------------------------------- the detail */
/* Over the grid, not in place of a card: fifteen topics cannot fit a tile on an
   eight-up grid at a size anyone can read, and a grid that reflows around one
   open card moves every other card while somebody is reading it. */
.cu-panel {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  padding: 48px 64px var(--deck-bar-clear, 40px);
  background:
    radial-gradient(120% 80% at 12% 0%, rgb(255 90 31 / 0.16) 0%, transparent 58%),
    #100E0D;
  overflow: hidden;
}

.cu-panel[hidden] { display: none; }

.cu-panel__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 22px;
}

.cu-back {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 22px;
  border: 1px solid var(--cu-line);
  border-radius: 999px;
  background: none;
  color: #F6F4F2;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.24s ease, background 0.24s ease;
}

.cu-back::before { content: '\2190'; font-size: 17px; line-height: 1; }
.cu-back:hover { border-color: var(--cu-accent); background: rgb(255 255 255 / 0.05); }

.cu-panel__n {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 15px;
  letter-spacing: 0.22em;
  color: rgb(246 244 242 / 0.45);
}

.cu-panel__name {
  flex: 0 0 auto;
  margin: 0 0 6px;
  font-family: var(--display, inherit);
  font-size: 52px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #FFF;
}

.cu-teach {
  flex: 0 0 auto;
  margin: 0 0 26px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--cu-accent);
}

/* Column-major, so a numbered list reads down each column and then across —
   01 to 05 on the left and 06 to 10 beside it, rather than 1, 2, 3 across the
   top. `grid-template-rows` is written by the component, because only it knows
   how many rows each column should hold. */
.cu-topics {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--cu-cols, 1), minmax(0, 1fr));
  /* The rows share the height, for the reason the grid's do: a five-topic
     programme should not leave two-thirds of the sheet empty under it. */
  grid-template-rows: repeat(var(--cu-rows, 1), minmax(58px, 1fr));
  grid-auto-flow: column;
  gap: 14px 40px;
  align-content: stretch;
  padding-bottom: 8px;
}

.cu-topic {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  border: 1px solid var(--cu-line);
  border-radius: 12px;
  background: var(--cu-card);
  opacity: 0;
  transform: translateY(10px);
}

.cu-panel.is-in .cu-topic {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.4s cubic-bezier(0.22, 0.68, 0, 1) calc(var(--cu-i, 0) * 34ms),
    transform 0.4s cubic-bezier(0.22, 0.68, 0, 1) calc(var(--cu-i, 0) * 34ms);
}

.cu-topic__n {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--cu-accent);
  color: #12100F;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 15px;
  font-weight: 700;
}

.cu-topic__name {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.3;
  color: #F6F4F2;
}

/* Three columns means narrower cells, so the type steps down once — and once
   only, to 17px, which is still well above this deck's body size. */
.cu-topics.is-wide .cu-topic__name { font-size: 17px; }
.cu-topics.is-wide .cu-topic { padding: 10px 14px; gap: 13px; }
.cu-topics.is-wide .cu-topic__n { width: 34px; height: 34px; font-size: 14px; }

@media (prefers-reduced-motion: reduce) {
  .cu-card,
  .cu-topic { opacity: 1; transform: none; transition: none; }
}

/* ==========================================================================
   AI Partners — the scroll stack
   Ported from the reactbits Pro `ScrollStack`: the behaviour, not the package.
   Cards pin one over another; the ones behind lift, shrink, turn and dissolve.

   Every transform on a card is written by `ScrollStack.js` every frame and
   NOTHING in this file touches it. The entrance and the hover live on the face
   inside — the same three-layer division the card fan and the ribbon use, for
   the same reason: two rules fighting over one transform is the trap.
   ========================================================================== */
.ss-root {
  position: relative;
  /* A definite height, not a minimum: `.canvas-block > *` sets `flex: 1`, whose
     `flex-basis: 0%` beats `height` on the main axis. And never a bare 860 — a
     slide presenting is the screen's shape, not 16:9. */
  flex: 0 0 auto;
  height: var(--slide-h, 860px);
  max-height: var(--slide-h, 860px);
  overflow: hidden;
  outline: none;
  color: var(--ink, #111827);
  background: #fff;
  --ss-accent: var(--accent-ink, #C2410C);
  --ss-line: rgb(17 24 39 / 0.10);
}

/* The ground is not white, quite. Two soft orbs in the organization's own
   measured colours — the orange off the gate in its brand film and the red off
   the polo in its team photographs — positioned by their centres so the core of
   each is somewhere the eye can reach, and placed where the ground is visible
   rather than under the cards. */
.ss-orb {
  position: absolute;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, var(--ss-glow) 0%, transparent 100%);
  animation: ss-orb-drift 38s ease-in-out infinite alternate both;
  pointer-events: none;
}
.ss-orb--a {
  width: 940px; height: 940px;
  left: 20%; top: 74%;
  --ss-glow: rgb(240 93 41 / 0.13);
}
.ss-orb--b {
  width: 760px; height: 760px;
  left: 82%; top: 14%;
  --ss-glow: rgb(217 24 35 / 0.09);
  animation-delay: -14s;
}
/* An animation replaces the WHOLE transform, so the centring has to be repeated
   in both keyframes — leave it out and every orb snaps half its own width down
   and right the moment the animation takes hold. And the name carries both the
   component and the purpose: `@keyframes` names are global, and this
   stylesheet has already handed one component's keyframes to another once. */
@keyframes ss-orb-drift {
  from { transform: translate(-50%, -50%) translate(0, 0); }
  to   { transform: translate(-50%, -50%) translate(26px, -20px); }
}

/* ------------------------------------------------------------------ the stack */
/* The stage clips, so a card waiting below rises into the frame rather than
   appearing on the spot — and so the one behind it never reaches the presenter
   bar. `--deck-bar-clear` is 0 when the section is not presenting. */
.ss-stage {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: var(--deck-bar-clear, 0px);
  z-index: 4;
  /* NO MASK. A `mask-image` on a box whose children move means the whole
     masked region is re-composited every frame, and on a stage this size that
     was the single biggest cost on the slide: measured over one travel, taking
     the mask off moved p90 from 33.4ms to 16.8 and frames over 20ms from 26 of
     76 to 4. The edge it softened is the top of the next card, which is
     already drawn at 14% opacity, so a hard clip there is very nearly the same
     picture for none of the work. */
  overflow: hidden;
}

.ss-card {
  position: absolute;
  left: 50%;
  /* 100, not less. A card three back has lifted 3 x STACK_Y = 78px, and the
     stage clips at its own top — pinned any higher, the deepest card in the
     stack loses its head to the clip instead of showing as a sliver. */
  top: 88px;
  width: 1240px;
  margin-left: -620px;
  /* Every card is the same size whatever it holds, which a stack needs and a
     list does not: cards of different heights shuffle their own bottom edge as
     the stack turns. A partner with nothing under its rule simply has air
     there — which is where the rest of the information is going. */
  min-height: 560px;
  /* The card is a flex column so its face fills the min-height rather than
     shrinking to its own copy — otherwise the box is 560 and the white card
     inside it is whatever the words happen to need. */
  display: flex;
  /* A card is as tall as its own content and is anchored by its TOP, which is
     what "pinned" means: a partner with four points is taller than one with
     none, and a card centred instead would move its own head up and down as
     the copy changed. Growing downward, a short card simply ends higher.

     `transform-origin` is that same pin, so a receding card shrinks toward the
     line it is pinned on rather than toward its own middle — without that, the
     stack fans apart at the top instead of stacking. */
  transform: translate3d(0, 0, 0);
  transform-origin: 50% 0%;
  /* `filter` is deliberately NOT in this list. Naming it promotes every card to
     a compositor layer for as long as the declaration stands, and a blurred
     720px layer is not free — this is the same cost `letterReveal` was caught
     by when it kept the hint on every glyph. Only the cards actually behind the
     front one carry a blur, and they carry it for a second. */
  will-change: transform, opacity;
}

.ss-card__face {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 52px 64px 56px;
  border-radius: 26px;
  border: 1px solid var(--ss-line);
  background: #fff;
  /* 40px of blur, not 80. A large-radius shadow is redrawn every time its
     element's transform changes, and a receding card's does on every frame of
     the travel — measured, an 80px shadow and the copy fade were the two costs
     between this slide and the vsync floor, and neither showed while the other
     was there. At 40 with a touch more opacity the card still stands off the
     sheet and the shadow is a quarter of the work. */
  box-shadow:
    0 1px 2px rgb(17 24 39 / 0.05),
    0 18px 40px rgb(17 24 39 / 0.20);
  /* The partner's own colour, measured off its mark, as a band across the top
     of its card and nowhere else. This is the one place a card is not white,
     and with no list beside the stack it is also the only thing that tells the
     slivers of the cards behind apart. */
  border-top: 6px solid var(--ss-accent);
}

/* Everything the card says, in one element. */
.ss-card__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  /* 120ms, measured. Fading a card's copy is fading a block of live type, and
     every frame of that fade re-rasterises it — over one travel, 260ms of fade
     cost 10 frames of 76 over 20ms, 120ms costs 6, and no fade at all costs 2.
     Promoting the element with `will-change: opacity` to make the fade the
     compositor's problem made it WORSE, not better (15 of 76): four more large
     layers cost more than the raster they saved. So the dial is the duration,
     and 120ms is the shortest that still reads as the card turning away rather
     than its words being switched off. */
  transition: opacity 120ms ease;
}
/* A card behind the front one keeps its plate, its colour and its shape and
   loses its copy — which is what a stack of cards looks like from the side. */
.ss-card.is-behind .ss-card__body { opacity: 0; }

.ss-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}

/* The mark on a white plate, SIZED BY HEIGHT AND NOTHING ELSE. `width: auto;
   height: 100%; max-width: 100%` measured 100x53 on a 1.89:1 mark in a 30px box
   the last time this was written: the percentage max-width resolves against the
   plate, the width wins, the height follows the ratio out of the bottom and
   `overflow: hidden` cuts it. Give the mark its own width and let the plate wrap
   it. */
.ss-card__plate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 118px;
  padding: 0 20px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid var(--ss-line);
}
/* Three of these four marks are lockups with their own frame and their own
   white ground burned in, so the plate is a mount for a badge rather than a
   tint behind a glyph — and a badge set small reads as a sticker. 62px is the
   height at which the smallest of the four, OpenAI's, is still readable from a
   room. */
.ss-card__plate img {
  display: block;
  height: 86px;
  width: auto;
  max-width: none;
  object-fit: contain;
}
/* No artwork anywhere in the library: the name in type, never a mark drawn by
   hand. */
.ss-card__plate--type {
  padding: 0 28px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 32px;
  letter-spacing: -0.01em;
}

/* What the rail used to say, on the card: "01 / 04". */
.ss-card__n {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 19px;
  letter-spacing: 0.14em;
  color: var(--muted, rgb(17 24 39 / 0.42));
  padding-top: 12px;
}

.ss-card__id { margin-top: 40px; }
.ss-card__note {
  margin: 0 0 12px;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 15px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ss-accent);
}
.ss-card__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 84px;
  line-height: 1.05;
  letter-spacing: -0.025em;
}
.ss-card__tag {
  margin: 14px 0 0;
  font-size: 29px;
  line-height: 1.4;
  color: var(--muted, rgb(17 24 39 / 0.62));
}

/* A rule that draws itself, the way every other head's does. */
.ss-card__rule {
  height: 2px;
  margin: 38px 0 30px;
  background: var(--ss-accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  animation: ss-rule 720ms cubic-bezier(0.22, 0.68, 0, 1) 420ms both;
}

.ss-card__points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px 56px;
}
.ss-card__points li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  font-size: 24px;
  line-height: 1.4;
  color: var(--ink, #111827);
  animation: ss-rise 520ms cubic-bezier(0.22, 0.68, 0, 1) 540ms both;
}
.ss-card__tick {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  margin-top: 11px;
  border-radius: 2px;
  background: var(--ss-accent);
}

@keyframes ss-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ss-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ss-rule {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .ss-orb { animation: none; }
  .ss-card__points li { animation: none; opacity: 1; transform: none; }
  .ss-card__rule { animation: none; transform: scaleX(1); }
}
