/* crane.blue home · "Seven vertebrae".
   Theme tokens for the scroll-craft engine, the page's own classes, and a restyle
   of the shared chrome (nav-footer.js markup) in the same tokens. Engine selectors
   in scrollcraft.css are never restyled here. */

:root {
  --sc-canvas:      #FBFBF8;
  --sc-surface:     #F2F2ED;
  --sc-ink:         #171A1F;
  --sc-ink-soft:    #5B6473;
  --sc-accent:      #2563EB;
  --sc-accent-ink:  #FFFFFF;
  --sc-font-display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --sc-font-text:    "Geist", system-ui, -apple-system, sans-serif;
  --sc-shadow-color: 222 30% 12%;
  --sc-measure: 56ch;

  --plate-w: 46vw;          /* the specimen column */
  --plate-h: 42svh;         /* phone: the specimen band on top */
  --nav-h: 64px;
  color-scheme: light;
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body.home {
  background: var(--sc-canvas);
  color: var(--sc-ink);
  font-family: var(--sc-font-text);
  font-size: var(--sc-t-base);
  line-height: var(--sc-leading-body);
  -webkit-font-smoothing: antialiased;
}

/* ============================================================ THE CHROME ==
   The shared nav/footer markup from nav-footer.js, restyled for this page:
   a quiet static row at the top (no fixed bar), and the site footer below the
   close. The theme pills are hidden: this page is designed light-only. */
#crane-nav nav {
  position: absolute; top: 0; left: 0; right: 0; z-index: 60;
  background: transparent;
}
#crane-nav nav .inner {
  display: flex; align-items: center; justify-content: space-between;
  height: var(--nav-h); padding: 0 var(--sc-gutter);
}
#crane-nav nav .brand { display: flex; align-items: center; text-decoration: none; }
#crane-nav nav .brand img { height: 40px; width: auto; display: block; }
#crane-nav nav .links { display: flex; gap: var(--sc-5); align-items: center; margin: 0 auto; }
#crane-nav nav .links a {
  color: var(--sc-ink-soft); text-decoration: none; font-size: var(--sc-t-sm); font-weight: 500;
  white-space: nowrap; transition: color var(--sc-d-fast) var(--sc-ease-out);
}
#crane-nav nav .links a:hover { color: var(--sc-ink); }
#crane-nav nav .links a[style] { color: var(--sc-accent) !important; font-weight: 600 !important; }
#crane-nav nav .links a:focus-visible, #crane-footer footer a:focus-visible { outline: 2px solid var(--sc-accent); outline-offset: 3px; border-radius: 2px; }
.home .theme-pills { display: none !important; }
.home .nav-actions { display: flex; align-items: center; gap: var(--sc-2); }
.home .mobile-menu-controls { display: none; }
.home .lang-switcher { position: relative; }
.home .lang-toggle {
  background: none; border: 1px solid var(--sc-hairline-strong); border-radius: var(--sc-r-sm);
  padding: 6px 10px; cursor: pointer; color: var(--sc-ink-soft);
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: var(--sc-t-xs); font-weight: 500; letter-spacing: var(--sc-track-wide);
  transition: color var(--sc-d-fast) var(--sc-ease-out), border-color var(--sc-d-fast) var(--sc-ease-out);
}
.home .lang-toggle:hover { color: var(--sc-ink); border-color: var(--sc-ink); }
.home .lang-toggle .icon-globe { width: 16px; height: 16px; flex-shrink: 0; }
.home .lang-dropdown {
  display: none; position: absolute; top: calc(100% + 8px); right: 0;
  background: var(--sc-canvas); border: 1px solid var(--sc-hairline-strong);
  border-radius: var(--sc-r-md); padding: 6px 0; min-width: 200px;
  box-shadow: var(--sc-e3); z-index: 200; max-height: 360px; overflow-y: auto;
}
.home .lang-dropdown.open { display: block; }
.home .lang-divider { height: 1px; background: var(--sc-hairline); margin: 6px 0; }
.home .lang-option {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 7px 14px; background: none; border: none; cursor: pointer;
  font: inherit; font-size: var(--sc-t-sm); color: var(--sc-ink); white-space: nowrap;
}
.home .lang-option:hover { background: var(--sc-surface); }
.home .lang-option.active { color: var(--sc-accent); font-weight: 600; }
.home .menu-toggle {
  display: none; background: none; border: 0; cursor: pointer; padding: 8px; width: 44px; height: 44px;
  flex-direction: column; justify-content: center; align-items: center; gap: 5px;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.home .menu-toggle span { display: block; width: 22px; height: 2px; background: var(--sc-ink); border-radius: 2px; transition: transform 240ms var(--sc-ease-out), opacity 240ms var(--sc-ease-out); }
.home .menu-toggle.active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.home .menu-toggle.active span:nth-child(2) { opacity: 0; }
.home .menu-toggle.active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

#crane-footer footer {
  position: relative; z-index: 10;
  margin-left: var(--plate-w);
  padding: var(--sc-6) var(--sc-gutter) var(--sc-7);
  border-top: 1px solid var(--sc-hairline);
  background: var(--sc-canvas);
}
#crane-footer footer .inner { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--sc-4); }
#crane-footer footer .copy { font-size: var(--sc-t-xs); color: var(--sc-ink-soft); }
#crane-footer footer .links { display: flex; flex-wrap: wrap; gap: var(--sc-4); }
#crane-footer footer .links a { font-size: var(--sc-t-xs); color: var(--sc-ink-soft); text-decoration: none; }
#crane-footer footer .links a:hover { color: var(--sc-ink); }

/* ============================================================ THE PLATE == */
.plate {
  position: fixed; left: 0; top: 0;
  /* widens toward the full viewport during the canal dive (--pexp, home.js) so the tunnel
     isn't cropped by the column's own edge; back to the normal column the rest of the time */
  width: calc(var(--plate-w) + (100vw - var(--plate-w)) * var(--pexp, 0));
  height: 100vh; height: 100svh;
  z-index: 5;
  overflow: hidden;
}
.plate__grid {
  position: absolute; inset: -6%;
  background-image: radial-gradient(color-mix(in oklab, var(--sc-ink) 9%, transparent) 1px, transparent 1.2px);
  background-size: 28px 28px;
  opacity: 0.55;
  transform: translate3d(var(--gx, 0px), var(--gy, 0px), 0);
  will-change: transform;
}
.plate__stage {
  position: absolute; inset: 0;
  transform: translate3d(var(--sx, 0px), var(--sy, 0px), 0);
}
.plate__stage canvas { display: block; width: 100%; height: 100%; }
.plate__poster {
  position: absolute; left: 50%; top: 50%;
  height: 78%; width: auto; max-width: none;
  transform: translate(-50%, -50%);
  display: none;
}
.plate.no-webgl canvas { display: none; }
.plate.no-webgl .plate__poster { display: block; }

/* the end beat: the canvas fades under --cv and the mark rises under --lg (home.js) */
.plate__stage canvas, .plate__poster { opacity: var(--cv, 1); }
.plate__logo {
  position: absolute; left: 50%; top: 50%;
  width: min(52%, 260px);
  transform: translate(-50%, -50%) scale(calc(0.9 + 0.1 * var(--lg, 0)));
  opacity: var(--lg, 0);
  pointer-events: none;
}
.plate__logo img { display: block; width: 100%; height: auto; }
/* the mark arrives as a clip, then the page takes over: the bird stretches and its head and
   neck are redrawn in place as the logo lines while the body fades. At 5.0 s of the clip
   (body gone, growth not yet begun) the lines sit at a measured spot in the 16:9 box; the
   clip pauses, the real SVG is shown on that exact spot, then springs into the brand lockup:
   the mark large on the left, the accent lines sweeping in, the CRANE wordmark rising in
   letter by letter. The still is the fallback. */
/* the clip is framed wide (the bird never leaves its frame), so the box is shown at 2.4x
   the plate width and the plate crops it: the bird reads at a real size, and the widest
   wing spread (34% to 67% of the box) still sits inside the visible 29% to 71%. */
.plate__logo { width: 240%; }
.plate__mark-svg, .plate__word { overflow: visible; }
/* desktop: anchor the clip box to the normal column, not to the plate, because the plate
   is still full width when the crossfade starts. Same geometry as 240% of a 46vw plate. */
@media (min-width: 901px) {
  /* --push (home.js): 0 while the clip plays in the middle of the screen, 1 once the
     camera has pushed right and the lockup sits in the left column */
  .plate__logo { left: calc(var(--plate-w) * 0.5 + (50vw - var(--plate-w) * 0.5) * (1 - var(--push, 1))); width: calc(var(--plate-w) * 2.4); }
}
.plate__morph { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; background: #fff; transform: translateZ(0); transition: opacity 0.12s ease; }
.plate__mark-svg {
  position: absolute; left: 51.09%; top: 33.75%; width: 6.19%; height: auto; opacity: 0;
  transition: opacity 0.25s ease,
    left 1.1s cubic-bezier(.34,1.3,.5,1), top 1.1s cubic-bezier(.34,1.3,.5,1), width 1.1s cubic-bezier(.34,1.3,.5,1);
}
/* accent lines: pivot at the neck base like the neck itself; each line also has its own
   top-anchored origin for the draw-in */
.plate__mark-svg .acc { transform-box: view-box; transform-origin: 40px 95px; }
.plate__word { position: absolute; left: 42.9%; top: 46.1%; width: 22.9%; height: auto; overflow: visible; pointer-events: none; }
.plate__word .ltr { opacity: 0; transform: translateY(14px); transition: opacity 0.45s ease, transform 0.7s cubic-bezier(.22,1,.36,1); }
.plate__logo.morph-hand .plate__morph { opacity: 0; }
.plate__logo.morph-hand .plate__mark-svg { opacity: 1; }
/* colour continuity: the clip draws the lines in one deep flat blue, the brand mark is a
   light-to-dark gradient. The SVG takes over in the clip's flat blue and keeps it until
   the lockup has settled; then a filter that made the gradient look like that flat blue
   eases off and the gradient shows. The switch from flat fill to gradient happens under
   the filter, where the two are indistinguishable. */
.plate__logo.morph-hand .plate__mark-svg path { fill: #1f4de3; }
.plate__logo.morph-word .plate__mark-svg path { fill: revert-layer; }
/* light from the front, top right: the lockup casts a shadow down and to the left. Two
   layers, a tight contact shadow and a wide soft one, sized in vw because the lockup is. */
.plate__logo {
  --logo-shadow: drop-shadow(-0.22vw 0.32vw 0.28vw rgba(15, 23, 42, 0.16)) drop-shadow(-0.7vw 1vw 1.2vw rgba(15, 23, 42, 0.12));
}
.plate__word, .plate__tm, .plate__mark-still { filter: var(--logo-shadow); }
/* the mark keeps the same function list in every state so the filter still interpolates */
.plate__mark-svg { filter: brightness(1) saturate(1) var(--logo-shadow); }
.plate__logo.morph-hand .plate__mark-svg { filter: brightness(0.6) saturate(2.1) var(--logo-shadow); }
.plate__logo.morph-word .plate__mark-svg { filter: brightness(1) saturate(1) var(--logo-shadow); transition: opacity 0.12s ease, left 1.1s cubic-bezier(.34,1.3,.5,1), top 1.1s cubic-bezier(.34,1.3,.5,1), width 1.1s cubic-bezier(.34,1.3,.5,1), filter 1s ease 1.7s; }
.plate__logo.morph-grow .plate__mark-svg { left: 32.5%; top: 40.6%; width: 8.33%; }
.plate__logo.morph-word .plate__word .ltr { opacity: 1; transform: none; }
.plate__logo.morph-word .plate__word .ltr1 { transition-delay: 0s; } .plate__logo.morph-word .plate__word .ltr2 { transition-delay: 0.07s; }
.plate__logo.morph-word .plate__word .ltr3 { transition-delay: 0.14s; } .plate__logo.morph-word .plate__word .ltr4 { transition-delay: 0.21s; }
.plate__logo.morph-word .plate__word .ltr5 { transition-delay: 0.28s; }
/* the TM lives in CSS content so the i18n extractor never sees it as copy */
.plate__tm { position: absolute; left: 66.1%; top: 46.4%; font: 600 0.78vw/1 var(--sc-font-text); letter-spacing: 0.06em; color: #3B82F6;
  opacity: 0; transform: translateY(14px); transition: opacity 0.45s ease 0.4s, transform 0.7s cubic-bezier(.22,1,.36,1) 0.4s; pointer-events: none; }
.plate__tm::after { content: "TM"; }
.plate__logo.morph-word .plate__tm { opacity: 1; transform: none; }
.plate__logo.is-still .plate__tm { display: none; }
/* the rig (neck + the four lines, one group) springs once the lockup lands: back
   extension and magnification together, pivoting at the neck base, overshooting forward
   once and settling. The lines are inside the rig, so they ride it. */
.plate__mark-svg .rig { transform-box: view-box; transform-origin: 40px 95px; }
.plate__logo.morph-word .plate__mark-svg .rig { animation: rigSpring 1.25s cubic-bezier(.2,.9,.3,1) 0.4s 1 both; }
@keyframes rigSpring {
  0%   { transform: rotate(0deg) scale(1); }
  30%  { transform: rotate(-13deg) scale(1.16); }
  56%  { transform: rotate(4.5deg) scale(0.965); }
  76%  { transform: rotate(-1.6deg) scale(1.02); }
  100% { transform: rotate(0deg) scale(1); }
}
/* the lines light up one after another, a pulse running down the dashes, from the moment
   the SVG takes over from the clip, and forever */
.plate__logo.morph-grow .plate__mark-svg .acc { animation: accRun 1.1s ease-in-out infinite; }
@keyframes accRun {
  0%   { opacity: 0.28; }
  14%  { opacity: 1; }
  40%  { opacity: 0.28; }
  100% { opacity: 0.28; }
}
/* per-line delays must outrank the shorthand above (which resets delay to 0) */
.plate__logo.morph-grow .plate__mark-svg .acc1 { animation-delay: 0s; }    .plate__logo.morph-grow .plate__mark-svg .acc2 { animation-delay: 0.22s; }
.plate__logo.morph-grow .plate__mark-svg .acc3 { animation-delay: 0.44s; } .plate__logo.morph-grow .plate__mark-svg .acc4 { animation-delay: 0.66s; }
@media (prefers-reduced-motion: reduce) { .plate__mark-svg .rig, .plate__mark-svg .acc { animation: none !important; } }
.plate__logo.is-still .plate__morph, .plate__logo.is-still .plate__mark-svg, .plate__logo.is-still .plate__word { display: none; }
.plate__logo:not(.is-still) .plate__mark-still { display: none; }
.plate__logo.is-still { width: min(52%, 260px); }
.plate.is-diving .plate__stage { transform: none; }
/* leaving the canal: the whole screen goes to the clip's white (--wo), the plate's own
   layer sits under the clip, and the close copy comes in on top (--c7in) */
.plate::after {
  content: ""; position: absolute; inset: 0; background: #fff; pointer-events: none;
  opacity: var(--wo, 0); z-index: 3;
}
.plate__logo { z-index: 4; }
.whiteout {
  /* starts where the plate ends: while the plate is still full width for the centred clip
     it must not cover the bird's right half (the plate paints its own white) */
  position: fixed; top: 0; right: 0; bottom: 0; left: calc(var(--plate-w) + (100vw - var(--plate-w)) * var(--pexp, 0));
  background: #fff; pointer-events: none; opacity: var(--wo, 0); z-index: 6;
}
/* z-index only: 'position: relative' here would override the engine's sticky pin and let
   the close section scroll away during the dive (it did). */
.chapter--close .sc-stage { z-index: 7; }
.chapter--close .chapter__inner, .chapter--close #crane-footer { opacity: var(--c7in, 0); }
@media (max-width: 900px) { .whiteout { left: 0; top: var(--plate-h); } }


/* the index */
.rail {
  opacity: var(--railo, 1); transition: opacity 0.2s linear;
  position: absolute; right: var(--sc-5); top: 50%; z-index: 2;
  transform: translateY(-50%);
  display: grid; gap: var(--sc-3);
  padding-left: var(--sc-4);
  font-family: var(--sc-font-text);
}
.rail__track {
  position: absolute; left: 0; top: 8px; bottom: 8px; width: 2px;
  background: var(--sc-hairline);
  border-radius: 2px;
}
.rail__fill {
  position: absolute; left: 0; top: 0; width: 100%;
  height: calc(var(--rail-p, 0) * 100%);
  background: var(--sc-accent);
  border-radius: 2px;
}
.rail a {
  display: grid; grid-template-columns: 2rem auto; align-items: baseline; gap: var(--sc-2);
  min-height: 1.25rem;
  text-decoration: none; color: var(--sc-ink-soft);
  font-size: var(--sc-t-xs); letter-spacing: var(--sc-track-wide);
  padding: 2px 0;
  transition: color var(--sc-d-fast) var(--sc-ease-out);
}
.rail__id { font-weight: 600; font-variant-numeric: tabular-nums; }
.rail__name { text-transform: uppercase; font-size: 0.68rem; opacity: 0.8; }
.rail a.is-active { color: var(--sc-accent); }
.rail a.is-past { color: var(--sc-ink); }
.rail a:hover { color: var(--sc-ink); }
.rail a:focus-visible { outline: 2px solid var(--sc-accent); outline-offset: 3px; border-radius: 2px; }

/* the leader line from the lit bone to its chapter */
.callout {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 30;
  opacity: 0; transition: opacity 240ms var(--sc-ease-out);
}
.callout.is-on { opacity: 1; }
.callout path { fill: none; stroke: var(--sc-accent); stroke-width: 1.25; }
.callout circle { fill: var(--sc-canvas); stroke: var(--sc-accent); stroke-width: 1.5; }

/* ============================================================ THE FOLIO == */
.folio {
  margin-left: var(--plate-w);
  padding: 0 var(--sc-gutter);
  position: relative; z-index: 10;
}

.chapter { position: relative; }
.chapter__inner {
  max-width: 42rem;
  padding: var(--sc-section) 0;
}
.chapter--hero .chapter__inner {
  min-height: calc(100svh - 4rem);
  display: flex; flex-direction: column; justify-content: center;
  padding-top: calc(var(--nav-h) + var(--sc-6));
}
.chapter__stage { display: flex; align-items: center; }
.chapter--pin .chapter__inner { padding: var(--sc-8) 0; position: relative; width: 100%; }
.chapter--pin [data-sc-cue] { will-change: opacity, transform; }
/* cues that take turns share one slot, so nothing else moves when they swap */
.slot { position: relative; min-height: 7.5rem; }
.slot .body { position: absolute; top: 0; left: 0; }

.title {
  font-family: var(--sc-font-display);
  font-weight: 500; font-variation-settings: "opsz" 144;
  font-size: var(--sc-t-3xl);
  line-height: var(--sc-leading-none);
  letter-spacing: var(--sc-track-tight);
  text-wrap: balance;
  margin: 0;
}
.chapter__title {
  font-family: var(--sc-font-display);
  font-weight: 500; font-variation-settings: "opsz" 96;
  font-size: var(--sc-t-2xl);
  line-height: var(--sc-leading-tight);
  letter-spacing: var(--sc-track-snug);
  text-wrap: balance;
  margin: 0;
}
.lede {
  font-size: var(--sc-t-lg); line-height: 1.45;
  color: var(--sc-ink); max-width: 34ch;
  text-wrap: pretty;
}
.body {
  max-width: var(--sc-measure); color: var(--sc-ink);
  text-wrap: pretty; margin: 0;
}
.sc-stack > .body + .body { margin-top: var(--sc-4); }
.pull {
  font-family: var(--sc-font-display); font-weight: 400; font-variation-settings: "opsz" 48;
  font-size: var(--sc-t-xl); line-height: 1.25; letter-spacing: var(--sc-track-snug);
  max-width: 24ch; margin: 0; text-wrap: balance;
  padding-left: var(--sc-5); border-left: 1px solid var(--sc-accent);
}
.cite {
  font-size: var(--sc-t-xs); color: var(--sc-ink-soft); max-width: var(--sc-measure);
  margin: 0; line-height: 1.5;
}
.cite em { font-style: italic; }
#c1 .cite { margin-top: var(--sc-4); }

/* the figure on C1 */
.figure { display: grid; gap: 0; margin: var(--sc-7) 0 var(--sc-6); }
.figure__num {
  font-family: var(--sc-font-display); font-weight: 600; font-variation-settings: "opsz" 144;
  font-size: var(--sc-t-4xl); line-height: 0.9; letter-spacing: -0.04em;
  color: var(--sc-accent);
  font-variant-numeric: tabular-nums;
}
.figure__unit { font-size: var(--sc-t-lg); color: var(--sc-ink); margin-top: var(--sc-3); }
.figure__tilt { font-size: var(--sc-t-sm); color: var(--sc-ink-soft); font-variant-numeric: tabular-nums; }

/* Both C3's panel and C4's banners park off to the right before they reveal,
   which on a phone gave the document 784px of scrollWidth in a 390px viewport.
   `overflow-x: clip` is set on body but not html, so it has to be said here.
   Clipping at the viewport rather than around each element matters: a clip box
   drawn close to the art slices its drop-shadow into a visible rectangle, which
   is exactly what an earlier overflow-clip-margin of 1.5rem did to the toasts —
   the shadow reaches 28px below the pill and the margin allowed 24px.
   `clip` (not `hidden`) never becomes a scroll container, so sticky and the
   engine's pinned chapters are unaffected. */
html { overflow-x: clip; }

/* the panel on C3 */
.panel { margin: var(--sc-7) 0 0; max-width: 36rem; }
/* Arrives like the banners on C4 do — in from the right, decelerating into
   place — rather than the clip-path wipe it used to run. The travel is on the
   img, not on the observed figure: the engine's IntersectionObserver needs
   1% of the [data-sc-in] element inside the viewport to fire, and a figure
   parked at 100% + 4rem to the right is entirely outside it once the figure
   is wider than the margin beside it. At 15rem a sliver stayed in view and
   masked this; at 36rem the reveal simply never happened. The figure keeps
   the engine's 14px rise and opacity fade; only the image slides. */
.panel[data-sc-in] { transition: opacity 260ms var(--sc-ease-out), transform 260ms var(--sc-ease-out); }
.panel[data-sc-in] > img {
  transform: translate3d(calc(100% + 4rem), 0, 0);
  transition: transform 700ms var(--sc-ease-drawer);
}
.panel[data-sc-in].sc-in > img { transform: none; }
.panel > img {
  display: block; width: 100%; height: auto;
  border-radius: var(--sc-r-lg);
  /* the asset is an opaque dark rectangle, so a heavy elevation reads as a grey
     slab under it. One tight contact shadow + one soft lift, no third layer. */
  box-shadow: 0 1px 2px hsl(var(--sc-shadow-color) / 0.10),
              0 14px 30px -18px hsl(var(--sc-shadow-color) / 0.30);
}
/* C3 is a pinned scene: the engine publishes the act's scroll progress as
   --sc-p on the section, and each layer is a pure function of it. Four beats
   down 2.2 viewports of scroll: the two measuring lines draw out from the
   laptop's webcam (dashed, marching), the panel and then its feed come up,
   and the toast slides in from the right and drifts. Nothing is timed, so
   scrolling back rewinds it. Layer positions are percentages of the
   1800x1200 layout the flat composite was built on. */
.chapter--scene .sc-stage { display: flex; flex-direction: column; justify-content: center; height: auto; min-height: 100svh; overflow: visible; }
.chapter--scene .chapter__inner { padding: var(--sc-5) 0; }
.chapter--scene .panel { margin-top: var(--sc-5); }
.chapter--scene {
  --t-head:  clamp(0, calc((var(--sc-p, 0) - 0.10) / 0.18), 1);
  --t-sh:    clamp(0, calc((var(--sc-p, 0) - 0.18) / 0.18), 1);
  --t-panel: clamp(0, calc((var(--sc-p, 0) - 0.36) / 0.14), 1);
  --t-feed:  clamp(0, calc((var(--sc-p, 0) - 0.48) / 0.14), 1);
  --t-toast: clamp(0, calc((var(--sc-p, 0) - 0.64) / 0.16), 1);
}
.scene__frame {
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--sc-r-lg); background: #fff;
  box-shadow: 0 1px 2px hsl(var(--sc-shadow-color) / 0.10),
              0 14px 30px -18px hsl(var(--sc-shadow-color) / 0.30);
}
.scene__frame > * { position: absolute; display: block; height: auto; }
.scene__desk { inset: 0; width: 100%; height: 100%; }
.scene__lines { inset: 0; width: 100%; height: 100%; fill: none; stroke: var(--sc-accent); stroke-width: 2.5; stroke-linecap: round; }
.scene__lines path { stroke-dasharray: 0.028 0.022; animation: sc-march 1.4s linear infinite; }
/* revealed from the camera end: the paths run right-to-left, so an inset that
   shrinks from the left uncovers them from the webcam outward. stroke-box so
   the round caps are not shaved off. */
.scene__line--head     { clip-path: inset(0 0 0 calc((1 - var(--t-head)) * 100%)) stroke-box; }
.scene__line--shoulder { clip-path: inset(0 0 0 calc((1 - var(--t-sh)) * 100%)) stroke-box; }
.scene__cam { fill: var(--sc-accent); stroke: none; opacity: clamp(0, calc(var(--sc-p, 0) * 12), 1); }
.scene__panel { left: 4.444%; top: 5.833%; width: 36.667%; filter: drop-shadow(0 14px 22px hsl(var(--sc-shadow-color) / 0.22));
  opacity: var(--t-panel); transform: scale(calc(0.96 + 0.04 * var(--t-panel))); }
.scene__feed  { left: 7.656%; top: 35.32%; width: 30.24%;
  opacity: var(--t-feed); transform: scale(calc(0.88 + 0.12 * var(--t-feed))); }
.scene__toast { left: 61.667%; top: 4.167%; width: 34.444%;
  opacity: var(--t-toast); transform: translate3d(calc((1 - var(--t-toast)) * 70%), 0, 0); }
.scene__toast img { display: block; width: 100%; height: auto; filter: drop-shadow(0 10px 16px hsl(var(--sc-shadow-color) / 0.18)); animation: sc-drift 4.6s ease-in-out infinite; }
@keyframes sc-march { to { stroke-dashoffset: -0.05; } }
@keyframes sc-drift {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50%      { transform: translate3d(4px, -7px, 0) rotate(-0.5deg); }
}
.panel figcaption {
  font-size: var(--sc-t-xs); color: var(--sc-ink-soft); margin-top: var(--sc-3);
  letter-spacing: var(--sc-track-wide); text-transform: uppercase;
}

/* the toasts on C4 */
.toasts { display: grid; gap: var(--sc-3); margin-top: var(--sc-7); max-width: 26rem; }
.toasts img {
  display: block; width: 100%; height: auto;
  /* The PNGs used to carry a baked shadow whose alpha bottomed out at 4 instead
     of 0, so it filled the image rectangle and read as a grey box behind the
     pill. That haze is stripped from the assets now, which is what lets a real
     drop-shadow() follow the rounded shape. */
  filter: drop-shadow(0 10px 18px hsl(var(--sc-shadow-color) / 0.22));

  /* Each banner arrives the way it does on the desktop: in from the right,
     decelerating hard into place. The engine staggers .sc-in onto the children
     (data-sc-stagger="220"), so the second one follows the first. */
  opacity: 0;
  transform: translate3d(calc(100% + 4rem), 0, 0);
  transition: opacity 260ms var(--sc-ease-out),
              transform 700ms var(--sc-ease-drawer);
}
.toasts > .sc-in { opacity: 1; transform: none; }

/* breathing on C5 */
.chapter__inner--breath {
  display: grid; grid-template-columns: 11rem 1fr; gap: var(--sc-7); align-items: center;
}
.breath {
  position: relative; width: 11rem; height: 11rem;
  /* dropped below the heading line so the leader line has clear air */
  margin-top: var(--sc-7);
}
.breath__ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid var(--sc-accent);
  background: radial-gradient(circle at 50% 45%, color-mix(in oklab, var(--sc-accent) 18%, transparent), transparent 70%);
  transform: scale(var(--breath, 0.72));
  will-change: transform;
}
.breath__phase {
  position: absolute; inset: 0; display: grid; place-items: center; margin: 0;
  font-family: var(--sc-font-display); font-variation-settings: "opsz" 48;
  font-size: var(--sc-t-lg); color: var(--sc-ink);
  opacity: 0; transition: opacity 0.6s var(--sc-ease-out);
}
.breath__phase.is-on { opacity: 1; }
/* C5 is a normal scrolling chapter now, sized like a slide so the loop has room */
@media (min-width: 901px) {
  .chapter--breath { min-height: 100svh; display: grid; align-items: center; }
}
.breath__copy { display: grid; gap: var(--sc-4); position: relative; }
/* the screen's left and right edges breathe with the ring: teal in, yellow hold, purple out */
.breath-edges { position: fixed; inset: 0; pointer-events: none; z-index: 1; }
/* the glow breathes too: --reach (home.js) grows it in from both edges on the inhale,
   holds it, draws it back out on the exhale, and holds it small. Each edge is its own
   layer scaled from its own side, so this is a transform, not a repaint. */
.breath-edges > span { position: absolute; inset: 0; will-change: opacity; }
.breath-edges > span::before, .breath-edges > span::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 44vw;
  transform: scaleX(var(--reach, 1)); will-change: transform;
}
.breath-edges > span::before {
  left: 0; transform-origin: left center;
  background: radial-gradient(ellipse 50% 70% at 0% 50%, color-mix(in oklab, var(--edge) 55%, transparent), transparent 100%);
}
.breath-edges > span::after {
  right: 0; transform-origin: right center;
  background: radial-gradient(ellipse 50% 70% at 100% 50%, color-mix(in oklab, var(--edge) 55%, transparent), transparent 100%);
}
.breath-edges > .breath-edges__in { --edge: #14b8a6; opacity: var(--inhale, 0); }
.breath-edges > .breath-edges__hold { --edge: #eab308; opacity: var(--hold, 0); }
.breath-edges > .breath-edges__out { --edge: #7c3aed; opacity: var(--exhale, 0); }

/* profiles on C6 — three rows, not three columns: the folio column is 42rem
   wide beside the plate, which is not enough for a readable 3-up grid (and the
   long meta lines blow the tracks out past the viewport). */
.profiles {
  margin-top: var(--sc-8); padding: 0; list-style: none;
  border-top: 1px solid var(--sc-hairline);
}
.pcard {
  position: relative; display: grid; grid-template-columns: 7rem minmax(0, 1fr); column-gap: var(--sc-4); row-gap: var(--sc-1);
  align-items: start;
  padding: var(--sc-4) 0;
  border-bottom: 1px solid var(--sc-hairline);
  transition: background var(--sc-d-fast) var(--sc-ease-out);
}
.pcard:hover { background: color-mix(in oklab, var(--sc-ink) 3%, transparent); }
/* the name carries the link; its ::after stretches it over the whole row, so
   the card is one click target without wrapping the card in an <a> (which
   would make the extractor treat the entire card as one translation unit) */
.pcard__name a { color: inherit; text-decoration: none; }
.pcard__name a::after { content: ""; position: absolute; inset: 0; }
.pcard__name a:focus-visible { outline: none; }
.pcard:has(a:focus-visible) { outline: 2px solid var(--sc-accent); outline-offset: 2px; }
.pcard__art {
  grid-row: 1 / span 4;
  width: 7rem; height: 7rem; border-radius: 50%; object-fit: cover;
  background: color-mix(in oklab, var(--sc-ink) 5%, transparent);
}
.pcard__tag {
  min-width: 0;
  font-size: 0.68rem; font-weight: 600; letter-spacing: var(--sc-track-wide);
  text-transform: uppercase; color: var(--sc-ink-soft);
}
.pcard__tag--pro { color: var(--sc-accent); }
.pcard__name {
  min-width: 0; margin: 0;
  font-family: var(--sc-font-display); font-weight: 500;
  font-variation-settings: "opsz" 48;
  font-size: var(--sc-t-lg); line-height: 1.15;
}
.pcard__meta {
  min-width: 0; margin: 0;
  font-size: var(--sc-t-xs); color: var(--sc-ink-soft);
  font-variant-numeric: tabular-nums;
}
.pcard__note { min-width: 0; margin: 0; font-size: var(--sc-t-sm); color: var(--sc-ink); }
/* the rota at a glance: the work block, then the movement that follows it */
.pcard__bar {
  grid-column: 2; display: flex; gap: 2px; margin-top: var(--sc-2);
  /* drawn on a shared 90-minute axis, so a 30-minute cycle is visibly a third
     of a 90-minute one instead of every profile reading the same length */
  min-width: 0; width: calc(var(--total) / 90 * 22rem); max-width: 100%;
}
.pcard__bar i { flex: var(--w) 1 0; height: 4px; border-radius: 2px; background: var(--sc-accent); }
.pcard__bar i:first-child { background: color-mix(in oklab, var(--sc-accent) 30%, var(--sc-canvas)); }
/* on C6 the rota plays: a playhead runs through the segments at one shared speed and
   fills each in turn, then the cycle starts over. --play is minutes into the cycle
   (home.js); --start is where the segment begins. Without JS each fill is complete. */
.pcard__bar i { position: relative; overflow: visible; }
.pcard__bar.is-playing i { background: color-mix(in oklab, var(--sc-accent) 10%, var(--sc-canvas)); }
.pcard__bar.is-playing i::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--sc-accent); transform-origin: left center;
  transform: scaleX(clamp(0, calc((var(--play, 999) - var(--start, 0)) / var(--w)), 1));
}
.pcard__bar.is-playing i:first-child::after { background: color-mix(in oklab, var(--sc-accent) 30%, var(--sc-canvas)); }
/* the segment the playhead is in glows */
.pcard__bar.is-playing i.is-now::after { box-shadow: 0 0 10px 1px color-mix(in oklab, var(--sc-accent) 55%, transparent); }
/* Each profile arrives in two beats: the artwork first, in from the right and
   swelling up like a bubble surfacing, then the copy fades up beside it once
   the bubble has landed. The engine staggers .sc-in onto the cards
   (data-sc-stagger on .profiles), so the two beats play card by card down the
   list. The card box itself never moves — the row and its hairlines are the
   shelf the bubble settles onto — which is why the engine's own rise for
   [data-sc-stagger] > * is cancelled here and the children carry the motion. */
.profiles > .pcard { opacity: 1; transform: none; }
.pcard__art {
  opacity: 0;
  transform: translate3d(22rem, 0, 0) scale(0.55);
  transition: opacity 320ms var(--sc-ease-out),
              transform 820ms cubic-bezier(0.34, 1.32, 0.64, 1);
}
/* .pcard__name is deliberately not translated: a transform would turn it into
   the containing block for its stretched-link ::after and shrink the click
   target from the whole row to the heading. It only fades. */
.pcard__name { opacity: 0; transition: opacity 520ms var(--sc-ease-out) 420ms; }
.pcard__tag, .pcard__meta, .pcard__note, .pcard__bar {
  opacity: 0; transform: translate3d(0, 6px, 0);
  transition: opacity 520ms var(--sc-ease-out) 420ms,
              transform 520ms var(--sc-ease-out) 420ms;
}
.pcard.sc-in :is(.pcard__art, .pcard__name, .pcard__tag, .pcard__meta, .pcard__note, .pcard__bar) {
  opacity: 1; transform: none;
}
.profiles__more { margin: var(--sc-5) 0 0; font-size: var(--sc-t-sm); }
.profiles__more a { color: var(--sc-accent); text-underline-offset: 3px; }

/* actions */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sc-5); margin-top: var(--sc-6); }
.cta {
  display: inline-flex; align-items: center; height: 3.25rem; padding: 0 1.5rem;
  background: var(--sc-accent); color: var(--sc-accent-ink);
  text-decoration: none; font-weight: 600; font-size: var(--sc-t-base);
  border-radius: var(--sc-r-md);
  box-shadow: 0 8px 22px -8px color-mix(in oklab, var(--sc-accent) 60%, transparent);
  transition: transform var(--sc-d-fast) var(--sc-ease-out), box-shadow var(--sc-d-fast) var(--sc-ease-out);
  white-space: nowrap;
}
.cta:hover { box-shadow: 0 12px 26px -8px color-mix(in oklab, var(--sc-accent) 70%, transparent); }
.cta:active { transform: translateY(1px); }
.cta:focus-visible { outline: 2px solid var(--sc-accent); outline-offset: 3px; }
.stores { display: flex; gap: var(--sc-4); font-size: var(--sc-t-sm); }
.stores a { color: var(--sc-ink-soft); text-decoration: underline; text-underline-offset: 0.2em; }
.stores a:hover { color: var(--sc-ink); }

/* The close is the last screen, so the site footer lives inside its pinned
   stage rather than after it: the stage is a column, the copy fills it and
   centres, and the footer sits on the bottom edge. Nothing is left below the
   fold once the pin ends. The footer's plate offset is dropped here because
   the stage already sits beside the plate. */
.chapter--close .sc-stage { display: flex; flex-direction: column; height: auto; min-height: 100svh; overflow: visible; }
.chapter--close .chapter__inner { display: grid; gap: var(--sc-5); flex: 1; align-content: center; padding: var(--sc-5) 0; }
.chapter--close .more { margin-top: var(--sc-6); }
.chapter--close #crane-footer footer { margin-left: 0; padding: var(--sc-4) 0 var(--sc-5); background: transparent; z-index: auto; }
/* The close carries the engine's spotlight (data-sc-spotlight on its stage).
   Its overlay is the stage's own box, which is only the column, so a 28rem
   glow ends on a hard seam at the column's left edge. Let the overlay run a
   viewport past the column on both sides and remap the centre so it still
   sits under the pointer: the box is now stage + 200vw wide, so the stage's
   width is (100% - 200vw) and its left edge is at 100vw. */
.chapter--close .sc-stage::after {
  left: -100vw; right: -100vw;
  background: radial-gradient(
    28rem 28rem at calc(100vw + var(--sc-mx, 0.5) * (100% - 200vw)) calc(var(--sc-my, 0.5) * 100%),
    color-mix(in oklab, var(--sc-accent) 16%, transparent), transparent 70%);
}
.more {
  display: flex; flex-wrap: wrap; gap: var(--sc-4) var(--sc-5);
  margin: var(--sc-8) 0 0; padding: var(--sc-4) 0 0; list-style: none;
  border-top: 1px solid var(--sc-hairline);
  font-size: var(--sc-t-sm);
}
.more li { margin: 0; }
.more a { color: var(--sc-ink-soft); text-decoration: none; }
.more a:hover { color: var(--sc-ink); text-decoration: underline; text-underline-offset: 0.2em; }

/* ================================================================ PHONE == */
@media (max-width: 900px) {
  :root { --plate-w: 100vw; --nav-h: 56px; }
  #crane-nav nav { position: fixed; background: color-mix(in oklab, var(--sc-canvas) 92%, transparent); backdrop-filter: blur(10px); }
  #crane-nav nav .inner { padding: 0 var(--sc-4); }
  #crane-nav nav .brand img { height: 32px; }
  #crane-nav nav .links {
    display: none; position: absolute; top: var(--nav-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0; margin: 0;
    background: var(--sc-canvas); border-bottom: 1px solid var(--sc-hairline);
    padding: var(--sc-3) var(--sc-4) var(--sc-5);
  }
  #crane-nav nav .links.open { display: flex; }
  #crane-nav nav .links a { padding: 12px 0; border-bottom: 1px solid var(--sc-hairline); font-size: var(--sc-t-base); }
  #crane-nav nav .links a:last-of-type { border-bottom: 0; }
  .home .nav-actions .menu-toggle { display: flex; }
  .home .nav-actions .lang-switcher { display: none; }
  .home .mobile-menu-controls { display: flex; justify-content: flex-end; padding: var(--sc-2) 0 var(--sc-3); }
  .home .mobile-menu-controls .lang-switcher { display: block; }
  .home .mobile-menu-controls .lang-dropdown { right: 0; }

  .plate {
    top: var(--nav-h); width: 100%; height: var(--plate-h);
    /* The band is a fixed header on phones, so the folio scrolls UNDER it.
       That needs both halves: an opaque background (a gradient fading to
       transparent showed the copy through it) and a z-index above the folio's
       10 (below it, the dot grid and the C1-C7 rail painted over the copy,
       because the folio has no background of its own to hide them). Stays
       under the nav's 60. */
    background: var(--sc-canvas);
    z-index: 20;
  }
  .plate__stage { top: 4%; bottom: 10%; }
  .plate__poster { height: 92%; }
  .rail {
    right: auto; left: 50%; top: auto; bottom: var(--sc-2);
    transform: translateX(-50%);
    display: flex; gap: var(--sc-3); padding: 0;
  }
  .rail__track, .rail__name { display: none; }
  .rail a { grid-template-columns: auto; }
  .callout { display: none; }

  .folio { margin-left: 0; padding-top: calc(var(--plate-h) + var(--nav-h)); }
  #crane-footer footer { margin-left: 0; }
  .chapter__inner { padding: var(--sc-8) 0; }
  .chapter--hero .chapter__inner { min-height: 0; padding-top: var(--sc-6); }
  .title { font-size: var(--sc-t-2xl); }
  .chapter__title { font-size: var(--sc-t-xl); }
  /* the stage sits under the fixed nav + plate; top + height still equals the
     viewport so the engine's pinned progress and the sticky travel stay in step */
  .folio .sc-stage { top: calc(var(--plate-h) + var(--nav-h)); height: calc(100svh - var(--plate-h) - var(--nav-h)); }
  /* the close carries the footer, so it may run longer than the band under
     the plate; grow from the band rather than from a full viewport */
  .chapter--close .sc-stage,
  .chapter--scene .sc-stage { height: auto; min-height: calc(100svh - var(--plate-h) - var(--nav-h)); }
  .chapter--pin .chapter__inner { padding: var(--sc-5) 0; }
  .slot { min-height: 8.5rem; }
  .figure { margin: var(--sc-5) 0 var(--sc-4); }
  .figure__num { font-size: var(--sc-t-3xl); }
  /* ring beside the copy, not above it: stacked, the ring had already scrolled under the
     spine band for part of the time C5 was the active chapter, so the loop played unseen */
  .chapter__inner--breath { grid-template-columns: 6rem 1fr; gap: var(--sc-4); align-items: center; }
  .breath { width: 6rem; height: 6rem; margin-top: 0; }
  .breath__phase { font-size: var(--sc-t-base); }
  .profiles { margin-top: var(--sc-6); }
  .pcard { grid-template-columns: 4.5rem minmax(0, 1fr); column-gap: var(--sc-3); padding: var(--sc-3) 0; }
  .pcard__art { width: 4.5rem; height: 4.5rem; }
}

/* Short windows. Every size above answers to width only, so a wide-but-low
   viewport — a laptop with the dock up, a browser at half height — gets
   big-window type in a small-window height and a chapter runs past one
   screen. This tier answers to height instead: less section air, one step
   down on display type, and the profile cards packed so the C6 heading and
   all three cards sit on one screen. Phones keep their own rules. */
@media (max-height: 820px) and (min-width: 901px) {
  :root { --sc-section: clamp(2rem, 4vh, 4rem); }
  .title { font-size: var(--sc-t-2xl); }
  .chapter__title { font-size: var(--sc-t-xl); }
  .body { font-size: var(--sc-t-sm); }
  .chapter--hero .chapter__inner { min-height: 0; }
  .profiles { margin-top: var(--sc-5); }
  .pcard { grid-template-columns: 5rem minmax(0, 1fr); column-gap: var(--sc-4); row-gap: 2px; padding: var(--sc-3) 0; }
  .pcard__art { width: 5rem; height: 5rem; }
  .pcard__note { font-size: var(--sc-t-xs); }
  .pcard__bar { margin-top: var(--sc-1); }
  .profiles__more { margin-top: var(--sc-4); }
}

@media (prefers-reduced-motion: reduce) {
  .plate__grid, .plate__stage { transform: none !important; }
  .cta { transition: none; }
  /* The engine drops every reveal's travel here, but .toasts img and
     .panel[data-sc-in] img outrank its rule, so both slides have to be zeroed
     explicitly. Opacity still carries the reveal. */
  .toasts img, .panel[data-sc-in] > img { transform: none; transition-duration: 220ms; }
  .scene__lines path, .scene__toast img { animation: none; }
  .pcard__art, .pcard__tag, .pcard__meta, .pcard__note, .pcard__bar {
    transform: none; transition-duration: 220ms; transition-delay: 0ms;
  }
  .pcard__name { transition-duration: 220ms; transition-delay: 0ms; }
}
