:root { color-scheme: dark; }
/* Vanta Supply · round 3 sketch · shared by index, vault and particles */

/* The two typefaces, from this site rather than Google (2026-11-06, page speed): Google's latin variable files,
   the same ones the React pages bundle (src/app/(site)/fonts/). Each page's head preloads both. */
@font-face {
  font-family: "Space Grotesk"; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url("/site/fonts/space-grotesk-latin-wght.woff2") format("woff2");
}
@font-face {
  font-family: "Instrument Sans"; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url("/site/fonts/instrument-sans-latin-wght.woff2") format("woff2");
}

@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out .18s ease-out both; }
::view-transition-new(root) { animation: vt-in .32s ease-out both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; } }

:root {
  --bg: #0c0a09;
  --raise: #141110;
  --raise-2: #1b1816;
  --panel: #161311;
  --ink: #f3eee7;
  --mute: #a59c91;
  --faint: #7d756c;
  --line: rgba(243, 238, 231, 0.08);
  --fill: rgba(243, 238, 231, 0.06);
  --fill-hover: rgba(243, 238, 231, 0.11);
  --accent: #f0733a;

  --sans: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  --font-head: "Space Grotesk", "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  --head-w: 500;   /* heading weight */
  --head-t: -0.035em; /* heading tracking at display size */
  --head-s: 1;     /* optical size correction per face */

  --ease: cubic-bezier(0.165, 0.84, 0.44, 1); /* ease-out-quart */
  --gutter: 48px;
  --max: 1320px;
  --gap: 12px;
  --r: 12px;

  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Heading faces. Each one gets its own tracking so it looks set, not swapped. */
html[data-head="instrument"] { --font-head: "Instrument Sans", "Helvetica Neue", Arial, sans-serif; --head-t: -0.032em; }
html[data-head="geist"]      { --font-head: "Geist", "Instrument Sans", "Helvetica Neue", Arial, sans-serif; --head-t: -0.042em; }
html[data-head="schibsted"]  { --font-head: "Schibsted Grotesk", "Instrument Sans", "Helvetica Neue", Arial, sans-serif; --head-t: -0.03em; }
html[data-head="familjen"]   { --font-head: "Familjen Grotesk", "Instrument Sans", "Helvetica Neue", Arial, sans-serif; --head-t: -0.026em; --head-s: 1.05; }

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; background: var(--bg); }
/* Round 11: the page keeps its scrollbar gutter, so locking it for an overlay moves nothing.
   v2.js adds .is-locked, and sets --sbw only where a browser gave the gutter back anyway. */
html { scrollbar-gutter: stable; }
html.is-locked { overflow: hidden; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 500 16px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  padding-right: var(--sbw, 0px);
}
img, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, p, ul, ol, dl, dd, figure, fieldset { margin: 0; padding: 0; }
fieldset { border: 0; min-width: 0; }
ul, ol { list-style: none; }
button { -webkit-appearance: none; appearance: none; font: inherit; color: inherit; background: none; border: 0; border-radius: 0; margin: 0; padding: 0; cursor: pointer; text-align: inherit; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 6px; }
::selection { background: var(--ink); color: var(--bg); }

.wrap { width: 100%; max-width: calc(var(--max) + var(--gutter) * 2); margin: 0 auto; padding: 0 var(--gutter); }
.mute { color: var(--mute); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ---------- type ---------- */
.h1, .h2, .h3, .wordmark { font-family: var(--font-head); font-weight: var(--head-w); }
.h1 { font-size: calc(56px * var(--head-s)); line-height: 1.04; letter-spacing: var(--head-t); text-wrap: balance; }
.h1--page { font-size: calc(44px * var(--head-s)); line-height: 1.08; }
.h2 { font-size: calc(34px * var(--head-s)); line-height: 1.12; letter-spacing: calc(var(--head-t) * 0.9); text-wrap: balance; }
.h3 { font-size: calc(21px * var(--head-s)); line-height: 1.25; letter-spacing: calc(var(--head-t) * 0.55); }
.lead { font-size: 17px; line-height: 1.55; color: var(--mute); text-wrap: pretty; }
.small { font-size: 14px; line-height: 1.5; }

/* ---------- buttons, links, small parts ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px; border-radius: 999px;
  font-size: 15px; font-weight: 500; white-space: nowrap;
  background: var(--fill); color: var(--ink);
  transition: background-color .25s ease-out, color .25s ease-out, transform .2s ease-out;
}
.btn:hover { background: var(--fill-hover); }
.btn:active { transform: scale(0.98); }
.btn--solid { background: var(--ink); color: var(--bg); }
.btn--solid:hover { background: #fff; }
.btn--sm { min-height: 36px; padding: 0 16px; font-size: 14px; }
.btn--block { width: 100%; }
.btn--locked { color: var(--faint); cursor: default; }
.btn--locked:hover { background: var(--fill); }
.btn--locked:active { transform: none; }
.btn svg { width: 14px; height: 14px; flex: none; }
.btn--pass { color: var(--ink); }
.btn--pass svg { color: var(--mute); }
.btn__tag { margin-left: 4px; padding-left: 12px; border-left: 1px solid rgba(243, 238, 231, 0.14); font-size: 13px; color: var(--mute); }

.link {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
  font-size: 15px; color: var(--ink);
  transition: color .25s ease-out;
}
.link .arr { width: 16px; height: 16px; transition: transform .35s var(--ease), color .25s ease-out; color: var(--mute); }
.link:hover .arr { transform: translateX(3px); color: var(--ink); }
.link--mute { color: var(--mute); }
.link--mute:hover { color: var(--ink); }

.kinds { display: flex; flex-wrap: wrap; gap: 6px; }
.kinds li {
  display: inline-flex; align-items: center; height: 26px; padding: 0 11px; border-radius: 999px;
  font-size: 12.5px; color: var(--mute); box-shadow: inset 0 0 0 1px var(--line);
}

.tag { display: inline-flex; align-items: center; gap: 6px; height: 22px; font-size: 12px; font-weight: 500; flex: none; }
.tag--free { padding: 0 9px; border-radius: 999px; background: rgba(12, 10, 9, 0.6); color: var(--ink); }
.tag--free::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.tag--lock { color: rgba(243, 238, 231, 0.6); }
.tag--lock svg { width: 13px; height: 13px; }

/* ---------- nav ---------- */
header.top { position: relative; z-index: 40; }
.nav { position: relative; height: 76px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; }
.wordmark { justify-self: start; font-size: calc(20px * var(--head-s)); line-height: 1; letter-spacing: calc(var(--head-t) * 0.7); display: inline-flex; align-items: center; min-height: 44px; }
/* 2026-10-22: the logo mark replaces the "Vanta Supply" words */
.wordmark__logo { width: 26px; height: 26px; user-select: none; -webkit-user-drag: none; }
.foot__brand .wordmark__logo { width: 29px; height: 29px; }
.nav__links { display: flex; align-items: center; gap: 4px; }
.nav__link {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 14px; border-radius: 999px;
  font-size: 15px; color: var(--mute);
  transition: color .2s ease-out, background-color .25s ease-out;
}
.nav__link:hover, .nav__link[aria-current], .nav__link[aria-expanded="true"] { color: var(--ink); }
.nav__link[aria-expanded="true"] { background: var(--fill); }
.nav__link .chev { width: 10px; height: 10px; opacity: .7; transition: transform .3s var(--ease); }
.nav__link[aria-expanded="true"] .chev { transform: rotate(180deg); }
.nav__right { justify-self: end; display: flex; align-items: center; gap: 24px; font-size: 15px; }
.nav__right .signin { color: var(--mute); transition: color .2s ease-out; display: inline-flex; align-items: center; min-height: 44px; }
.nav__right .signin:hover { color: var(--ink); }
.lbl-s { display: none; }

/* ---------- vault menu ----------
   One panel under the nav, and it grows: the panel is a real box that changes
   size, so its corners, hairline and shadow stay true the whole way. What is
   inside does not fade or move; it is already in place and the growing panel
   uncovers it. v2.js measures the finished size (--mw, --mh).
   It starts as a small pill under "Vault" and swells into two rows of two.
   The wrapper is click-through; a short invisible bridge joins the panel to
   the trigger so the pointer never crosses dead space on the way down. */
:root { --menu-max: 664px; --spring: cubic-bezier(0.3, 1.36, 0.5, 1); }
.menu {
  --mw: var(--menu-max); --mh: 232px;
  position: absolute; left: 0; right: 0; top: calc(100% - 6px);
  display: flex; justify-content: center;
  pointer-events: none; visibility: hidden;
  transition: visibility 0s linear .24s;
}
.menu.is-open { visibility: visible; transition-delay: 0s; }
.menu.is-open::before { content: ""; position: absolute; left: 50%; top: -14px; width: var(--mw); height: 14px; translate: -50% 0; pointer-events: auto; }
.menu__panel {
  position: relative; flex: none; width: 176px; height: 46px; overflow: hidden;
  border-radius: 24px; background: var(--panel);
  box-shadow: 0 0 0 1px var(--line), 0 28px 64px -16px rgba(0, 0, 0, 0.75), 0 2px 6px rgba(0, 0, 0, 0.3);
  opacity: 0;
  transition: width .2s cubic-bezier(0.4, 0, 1, 1), height .2s cubic-bezier(0.4, 0, 1, 1), opacity .14s ease-out .08s;
}
.menu.is-open .menu__panel {
  width: var(--mw); height: var(--mh); opacity: 1; pointer-events: auto;
  transition: width .5s var(--spring), height .56s var(--spring), opacity .1s ease-out;
}

/* the contents: laid out once at the finished width, pinned to the top centre of the growing panel */
.menu__grid {
  position: absolute; left: 50%; top: 0; width: var(--mw); translate: -50% 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; padding: 12px;
}
.menu__hl {
  position: absolute; left: 0; top: 0; width: 50%; height: 50%; border-radius: 16px;
  background: var(--fill); opacity: 0; pointer-events: none;
  transition: transform .34s var(--ease), width .34s var(--ease), height .34s var(--ease), opacity .2s ease-out;
}
.menu__hl.is-on { opacity: 1; }
.menu__hl.is-snap { transition: opacity .2s ease-out; }

.menu__row {
  position: relative; display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 20px; align-items: center;
  padding: 14px; border-radius: 16px;
}
.menu__thumb { position: relative; display: block; width: 72px; aspect-ratio: 1 / 1; border-radius: 12px; overflow: hidden; background: #000; }
.menu__thumb::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.1); }
.menu__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.menu.is-open .menu__thumb img { animation: breathe 9s ease-in-out infinite alternate; }
@keyframes breathe { from { transform: scale(1); } to { transform: scale(1.12); } }
.menu__name { display: block; font-family: var(--font-head); font-weight: var(--head-w); font-size: calc(17px * var(--head-s)); line-height: 1.2; letter-spacing: calc(var(--head-t) * 0.5); }
.menu__line { display: block; margin-top: 5px; font-size: 14px; line-height: 1.4; color: var(--mute); }
.menu__row:focus-visible { outline-offset: -2px; border-radius: 16px; }


/* ---------- opening (home) ---------- */
.open { text-align: center; padding-top: 64px; padding-bottom: 56px; }
.open .lead { max-width: 500px; margin: 20px auto 0; }
.open__cta { display: flex; justify-content: center; gap: 10px; margin-top: 28px; }
.proof { margin-top: 24px; display: inline-flex; align-items: center; gap: 10px; font-size: 14px; color: var(--mute); }
.stars { display: inline-flex; gap: 2px; color: var(--accent); }
.stars svg { width: 12px; height: 12px; }

/* ---------- page head (vault, shelf) ---------- */
.phead { padding-top: 48px; padding-bottom: 40px; }
.phead .lead { margin-top: 14px; max-width: 560px; }
.crumb { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--faint); }
.crumb a { display: inline-flex; align-items: center; min-height: 44px; margin: -12px 0; color: var(--mute); transition: color .2s ease-out; }
.crumb a:hover { color: var(--ink); }
.phead--shelf { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gap); align-items: end; }
.phead__main { grid-column: 1 / span 7; }
.phead__main .kinds { margin-top: 20px; }
.phead__side { grid-column: 9 / -1; }
.facts { display: grid; gap: 8px; font-size: 14px; color: var(--mute); }
.facts div { display: flex; gap: 12px; align-items: baseline; }
.facts dt { color: var(--ink); min-width: 92px; }
.facts .soon { color: var(--faint); }
.phead__side .btn { margin-top: 20px; }
.phead__note { margin-top: 12px; font-size: 14px; line-height: 1.5; color: var(--faint); max-width: 380px; }

/* ---------- chips ---------- */
.toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px 32px; flex-wrap: wrap; margin-bottom: 16px; }
.countline { margin-bottom: 24px; font-size: 14px; line-height: 1.5; color: var(--mute); font-variant-numeric: tabular-nums; text-wrap: pretty; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 0 16px; border-radius: 999px;
  font-size: 15px; font-weight: 500; color: var(--mute);
  background: var(--fill);
  transition: background-color .25s ease-out, color .25s ease-out;
}
.chip:hover { color: var(--ink); background: var(--fill-hover); }
.chip .n { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; transition: color .25s ease-out; }
.chip[aria-pressed="true"] { background: var(--ink); color: var(--bg); }
.chip[aria-pressed="true"] .n { color: rgba(12, 10, 9, 0.55); }
.toolbar__count { font-size: 14px; color: var(--faint); font-variant-numeric: tabular-nums; }

/* ---------- grid + tiles ---------- */
.index { container-type: inline-size; }
.grid {
  --cols: 4; --ratio: 1;
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  grid-auto-rows: calc((100cqw - (var(--cols) - 1) * var(--gap)) / var(--cols) * var(--ratio));
  grid-auto-flow: dense;
  gap: var(--gap);
  transition: opacity .22s ease-out;
}
.grid.is-out { opacity: 0; }
.grid--32 { --ratio: 0.6667; }
.grid--32 .tile--wide { grid-row: span 2; }

.tile {
  position: relative; display: block; overflow: hidden; width: 100%; height: 100%;
  border-radius: var(--r); background: var(--raise);
  isolation: isolate;
}
.tile--wide { grid-column: span 2; }
/* a wide Filament tile: the whole drawing, centred on the stills' own ground (#111, the pack's paper), not a crop */
.tile--wide[data-shelf="filament"] { background: #111; }
.tile--wide[data-shelf="filament"] > img { object-fit: contain; }
.tile > img, .tile > video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .9s var(--ease);
}
.tile > img, .tile > video, .menu__thumb img, .menu__thumb video {
  pointer-events: none; -webkit-user-drag: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.tile--contain { background: #070606; }
.tile--contain > img { inset: 12%; width: 76%; height: 76%; object-fit: contain; }
.tile:hover > img, .tile:hover > video { transform: scale(1.035); }
.tile::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, rgba(12, 10, 9, 0.62) 0%, rgba(12, 10, 9, 0) 38%);
  opacity: .85; transition: opacity .3s ease-out;
}
.tile:hover::after { opacity: 1; }
.tile__meta {
  position: absolute; z-index: 2; left: 14px; right: 14px; bottom: 12px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  font-size: 13px; line-height: 1; color: rgba(243, 238, 231, 0.78);
  transition: color .25s ease-out;
}
.tile:hover .tile__meta { color: var(--ink); }
.tile__meta .name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; padding: 4px 0; }
.tile__meta .kind { color: rgba(243, 238, 231, 0.5); margin-left: 6px; }
/* a piece that only has a number ("No. 047", "Video 05") shows no name on its tile; the number is still in the viewer.
   On Materials the space is kept so Free and the padlock stay where they were. Delete this rule to bring them back. */
.tile__meta .name--no { visibility: hidden; }
/* and with no name to read, the tile needs no dark fade along its foot: the picture runs clean to the edge */
.tile:has(.tile__meta):not(:has(.name:not(.name--no)))::after { display: none; }
.tile:has(.tile__meta):not(:has(.name:not(.name--no))) .tag--lock svg { filter: drop-shadow(0 0 3px rgba(12, 10, 9, 0.7)); }
/* where a collection mixes kinds (Particles: images, videos, code) each tile says which it is, with a small icon.
   It has a ground of its own, so it reads on a pale picture too. It goes when the filter is already one kind. */
.tile__meta .lhs { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.tag--kind { gap: 5px; padding: 0 9px 0 7px; border-radius: 999px; background: rgba(12, 10, 9, 0.6); color: rgba(243, 238, 231, 0.9); }
.tag--kind svg { width: 12px; height: 12px; flex: none; }
#browse:has(.chips[data-group="kind"] :is([data-value="image"], [data-value="loop"], [data-value="code"])[aria-pressed="true"]) .tag--kind { display: none; }
@media (max-width: 640px) {
  /* a phone tile is narrow: a named piece keeps its name, so its kind is the icon alone and "Demo" steps aside */
  .tile__meta .lhs { gap: 7px; }
  .tile__meta .lhs:has(.name) .tag--kind { font-size: 0; gap: 0; padding: 0 6px; }
  .tile__meta .lhs:has(.name) + .tags .tag--demo { display: none; }
}

.index__foot { display: flex; justify-content: center; align-items: center; gap: 20px; margin-top: 32px; font-size: 15px; color: var(--mute); }
.index__foot--stack { flex-direction: column; gap: 16px; margin-top: 40px; }
.index__foot .count { font-size: 14px; color: var(--faint); font-variant-numeric: tabular-nums; }
.empty { padding: 96px 0; text-align: center; color: var(--mute); font-size: 15px; }

/* ---------- sections (home) ---------- */
.section { padding-top: 168px; }
.shelves__head { text-align: center; max-width: 560px; margin: 0 auto 104px; }
.shelves__head .lead { margin-top: 16px; }
.shelf { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--gap); align-items: start; }
.shelf + .shelf { margin-top: 120px; }
.shelf__text { grid-column: 1 / span 4; padding-right: 48px; padding-top: 4px; }
.shelf__text .h2 a { transition: opacity .2s ease-out; }
.shelf__text .h2 a:hover { opacity: .8; }
.shelf__text .lead { margin-top: 12px; }
.shelf__text .kinds { margin-top: 20px; }
.shelf__text .facts { margin-top: 24px; }
.shelf__links { display: flex; gap: 24px; margin-top: 16px; }
.shelf__previews { grid-column: 5 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap); }
.shelf__previews .tile { aspect-ratio: 1 / 1; height: auto; }

.next {
  margin-top: 120px; display: flex; align-items: center; gap: 24px;
  padding: 12px 28px 12px 12px; border-radius: 16px; background: var(--raise);
  transition: background-color .3s ease-out;
}
.next:hover { background: var(--raise-2); }
.next__img { width: 132px; height: 84px; border-radius: 8px; overflow: hidden; flex: none; background: #000; }
.next__img img { width: 100%; height: 100%; object-fit: cover; }
.next__label { font-size: 13px; color: var(--mute); display: flex; align-items: center; gap: 8px; }
.next__label::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.next__body { flex: 1; min-width: 0; }
.next__body .h3 { display: block; margin-top: 4px; }
.next__desc { display: block; font-size: 15px; color: var(--mute); margin-top: 2px; }
.next__note { font-size: 14px; color: var(--mute); white-space: nowrap; }

/* ---------- footer: the current site's orange glow and grain ---------- */
.foot { position: relative; overflow: hidden; margin-top: 168px; border-top: 1px solid var(--line); }
.foot__glow { position: absolute; left: 0; right: 0; bottom: 0; height: 256px; pointer-events: none; }
.foot__glow::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 55% 70% at 50% 100%, rgba(251, 146, 60, 0.22) 0%, transparent 65%),
    radial-gradient(ellipse 85% 100% at 50% 100%, rgba(249, 115, 22, 0.30) 0%, rgba(249, 115, 22, 0.10) 45%, transparent 72%);
  mix-blend-mode: plus-lighter;
}
.foot__glow::after {
  content: ""; position: absolute; inset: 0; opacity: .25;
  background-image: var(--grain); background-size: 160px 160px;
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0.6) 45%, transparent 100%);
  mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0.6) 45%, transparent 100%);
}
.foot__in { position: relative; padding-top: 64px; padding-bottom: 32px; }
.foot__grid { display: grid; grid-template-columns: 1.5fr repeat(4, minmax(0, 1fr)); column-gap: 24px; row-gap: 40px; }
.foot__brand p { font-size: 14px; color: var(--mute); margin-top: 8px; max-width: 260px; }
.foot__col h3 { font: 500 13px/1.4 var(--sans); color: var(--faint); margin-bottom: 14px; }
.foot__col a { display: flex; align-items: center; padding: 5px 0; font-size: 14.5px; color: var(--mute); transition: color .2s ease-out; }
.foot__col a:hover { color: var(--ink); }
.foot__base { margin-top: 64px; padding: 24px 0 0; border-top: 1px solid var(--line); font-size: 13px; color: var(--faint); }

/* ---------- fixed helpers for the review ---------- */
.fontsw {
  position: fixed; left: 16px; bottom: 16px; z-index: 30;
  display: flex; align-items: center; gap: 2px; padding: 3px; border-radius: 999px;
  background: rgba(27, 24, 22, 0.94); box-shadow: 0 0 0 1px var(--line);
  max-width: calc(100vw - 208px); overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
.fontsw::-webkit-scrollbar { display: none; }
.fontsw__label { padding: 0 8px 0 10px; font-size: 12px; color: var(--faint); white-space: nowrap; }
.fontsw button {
  position: relative; height: 30px; padding: 0 11px; border-radius: 999px; flex: none;
  font-size: 12px; font-weight: 500; color: var(--mute); white-space: nowrap;
  transition: color .2s ease-out, background-color .2s ease-out;
}
.fontsw button::after { content: ""; position: absolute; inset: -7px 0; } /* 44px tall hit area */
.fontsw button:hover { color: var(--ink); }
.fontsw button[aria-pressed="true"] { background: var(--ink); color: var(--bg); }
.drafts {
  position: fixed; right: 16px; bottom: 16px; z-index: 30;
  display: inline-flex; align-items: center; height: 36px; padding: 0 5px; border-radius: 999px;
  background: rgba(27, 24, 22, 0.94); box-shadow: 0 0 0 1px var(--line);
}
.drafts a {
  position: relative; display: inline-flex; align-items: center; height: 36px; padding: 0 9px;
  font-size: 12px; font-weight: 500; color: var(--mute); white-space: nowrap;
  transition: color .2s ease-out;
}
.drafts a + a::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 1px; background: var(--line); }
.drafts a::after { content: ""; position: absolute; inset: -4px 0; }
.drafts a:hover { color: var(--ink); }

/* ---------- item view: a stage and a side panel on a dimmed page ---------- */
dialog.sheet {
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; padding: 28px; border: 0;
  background: rgba(5, 4, 3, 0.88); color: var(--ink);
  display: none; place-items: center; overflow: hidden;
  opacity: 0; transition: opacity .18s ease-out;
}
dialog.sheet[open] { display: grid; }
/* Side by side, the one row is the window's height, never the panel's content: a long list of controls
   (Pointer on, say) once made the row taller than the window, and the panel, centred in it, slid down and
   off the bottom. The controls scroll inside the panel instead (2026-11-12). Stacked (900 and under), the
   sheet scrolls as a whole, so its row stays as tall as its content. */
@media (min-width: 901px) {
  dialog.sheet { grid-template-rows: minmax(0, 1fr); }
}
dialog.sheet::backdrop { background: transparent; }
dialog.sheet.is-open { opacity: 1; transition: opacity .26s ease-out; }
.sheet__panel {
  position: relative; width: 100%; max-width: 1280px; height: min(760px, 100%);
  display: grid; grid-template-columns: minmax(0, 1fr) 392px; grid-template-rows: minmax(0, 1fr); gap: 12px;
}
.stage, .side {
  border-radius: 28px; background: var(--raise);
  box-shadow: 0 0 0 1px var(--line), 0 40px 90px -30px rgba(0, 0, 0, 0.85);
  transform: translateY(12px) scale(0.985);
  transition: transform .18s ease-out, background-color .45s ease-out;
}
dialog.sheet.is-open .stage, dialog.sheet.is-open .side { transform: none; transition: transform .46s var(--ease), background-color .45s ease-out; }
dialog.sheet.is-open .side { transition-delay: 45ms, 0s; }

/* stage */
.stage {
  --on: var(--ink); --on-mute: var(--mute); --on-fill: rgba(243, 238, 231, 0.07); --on-pill: var(--bg);
  --glow: 0.6;
  position: relative; min-width: 0; min-height: 0; overflow: hidden; isolation: isolate;
  container-type: size;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.stage[data-bg="light"]:not([data-view="code"]) {
  --on: #0c0a09; --on-mute: rgba(12, 10, 9, 0.62); --on-fill: rgba(12, 10, 9, 0.08); --on-pill: #0c0a09;
  background: #e9e3da;
}
.stage img, .stage video { -webkit-user-drag: none; pointer-events: none; }
.stage__ambient {
  position: absolute; z-index: -1; inset: -15%; width: 130%; height: 130%; max-width: none; object-fit: cover;
  filter: blur(64px) saturate(1.15); opacity: .26;
}
.stage__view { position: absolute; inset: 0; display: grid; place-items: center; padding: 76px 28px 68px; }
.js .stage__view:not([hidden]) { animation: fade .35s ease-out both; }

.stage__tabs {
  position: absolute; z-index: 4; left: 16px; top: 16px;
  display: flex; padding: 4px; border-radius: 999px; background: var(--on-fill);
  transition: background-color .3s ease-out;
}
.stage__tabs button {
  position: relative; width: 46px; height: 36px; border-radius: 999px; display: grid; place-items: center;
  color: var(--on-mute); transition: color .2s ease-out, background-color .25s ease-out;
}
.stage__tabs button::after { content: ""; position: absolute; inset: -4px 0; }
.stage__tabs button:hover { color: var(--on); }
.stage__tabs button[aria-pressed="true"] { background: var(--on-pill); color: var(--ink); }
.stage__tabs svg { width: 17px; height: 17px; }

.stage__steps { position: absolute; z-index: 4; right: 14px; top: 16px; display: flex; gap: 2px; }
.stage__icon {
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  color: var(--on-mute); transition: color .2s ease-out, background-color .25s ease-out;
}
.stage__icon:hover { color: var(--on); background: var(--on-fill); }
.stage__icon svg { width: 17px; height: 17px; }
.stage__icon--prev svg { transform: rotate(180deg); }

.stage__badge { position: absolute; z-index: 4; left: 24px; bottom: 26px; font-size: 13px; line-height: 1; color: var(--on-mute); pointer-events: none; }
.stage__play {
  position: absolute; z-index: 4; right: 16px; bottom: 16px; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: var(--on); background: var(--on-fill);
  transition: background-color .25s ease-out, transform .2s ease-out;
}
.stage__play:hover { background: rgba(243, 238, 231, 0.13); }
.stage[data-bg="light"]:not([data-view="code"]) .stage__play:hover { background: rgba(12, 10, 9, 0.14); }
.stage__play:active { transform: scale(0.94); }
.stage__play svg { width: 16px; height: 16px; }
.stage__play .ic-play, .stage__play[data-state="paused"] .ic-pause { display: none; }
.stage__play[data-state="paused"] .ic-play { display: block; }

/* images and loops: small on purpose */
.pv { display: grid; justify-items: center; gap: 18px; margin: 0; }
.pv__frame {
  --ar: 1.5;
  position: relative; overflow: hidden; border-radius: 14px; background: #000;
  width: min(520px, 78cqw, calc(min(520px, 58cqh) * var(--ar)));
  aspect-ratio: var(--ar);
  box-shadow: 0 0 0 1px var(--line), 0 30px 60px -28px rgba(0, 0, 0, 0.95);
}
.pv__img, .pv__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pv__shield { position: absolute; inset: 0; z-index: 3; }
.pv__mark, .tile[data-mark]::before {
  position: absolute; z-index: 2; left: 58%; top: 66%;
  font: 500 12px/1.4 var(--sans); letter-spacing: 0.02em; white-space: nowrap;
  color: rgba(255, 255, 255, 0.36); text-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
  pointer-events: none;
  animation: mark-x 23s linear infinite alternate, mark-y 17s linear infinite alternate;
}
.tile[data-mark]::before { content: "vanta.supply"; z-index: 1; font-size: 11px; }
@keyframes mark-x { from { left: 5%; } to { left: calc(95% - 6.4em); } }
@keyframes mark-y { from { top: 7%; } to { top: calc(93% - 1.4em); } }
.pv__bar { position: absolute; z-index: 4; left: 0; right: 0; bottom: 0; height: 2px; background: rgba(255, 255, 255, 0.14); }
.pv__bar i { display: block; height: 100%; background: rgba(255, 255, 255, 0.72); transform-origin: 0 50%; transform: scaleX(0); }
.pv__cap { max-width: 36ch; text-align: center; font-size: 13px; line-height: 1.5; color: var(--mute); text-wrap: balance; }

/* live shape (shaders): a glowing gradient form drawn in CSS */
.live { position: absolute; inset: 0; overflow: hidden; }
.live__shape { position: absolute; left: 50%; top: 50%; width: min(300px, 44cqw, 46cqh); aspect-ratio: 1; margin: 0; transform: translate(-50%, -50%); filter: hue-rotate(var(--hue, 0deg)); }
.live__halo, .live__core {
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(from 200deg, #4c1d95, #a855f7 18%, #f5ecff 34%, #c084fc 50%, #5b21b6 68%, #7c3aed 84%, #4c1d95);
  animation: live-spin 16s linear infinite;
}
.live__halo { filter: blur(calc(10px + var(--glow) * 54px)); opacity: calc(0.15 + var(--glow) * 0.85); scale: 1.12; }
.live__core { filter: blur(calc(1.5px + var(--glow) * 9px)); }
.live[data-shape="ring"] .live__core {
  -webkit-mask-image: radial-gradient(circle at 56% 46%, transparent 0 27%, #000 34%);
  mask-image: radial-gradient(circle at 56% 46%, transparent 0 27%, #000 34%);
}
.live[data-shape="crescent"] .live__core {
  -webkit-mask-image: radial-gradient(circle at 68% 36%, transparent 0 44%, #000 50%);
  mask-image: radial-gradient(circle at 68% 36%, transparent 0 44%, #000 50%);
}
.live[data-shape="sphere"] .live__core { box-shadow: inset -18px -24px 48px rgba(20, 8, 48, 0.75); }
.live__grain { position: absolute; inset: 0; background-image: var(--grain); background-size: 160px 160px; mix-blend-mode: overlay; opacity: .4; pointer-events: none; }
.stage[data-paused] .live__halo, .stage[data-paused] .live__core { animation-play-state: paused; }
@keyframes live-spin { to { rotate: 360deg; } }

/* code pieces shown as a still (Filament, Particles) */
.still {
  --ar: 1; --w: 0; --tint: #fff;
  position: relative; overflow: hidden; isolation: isolate; border-radius: 14px; background: #000;
  width: min(460px, 74cqw, calc(min(460px, 60cqh) * var(--ar)));
  aspect-ratio: var(--ar);
  box-shadow: 0 0 0 1px var(--line), 0 30px 60px -28px rgba(0, 0, 0, 0.95);
}
.still img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.still[data-mode="filament"] .still__img { filter: blur(calc(var(--w) * 1.1px)) brightness(calc(1 + var(--w) * 2.2)) contrast(calc(1 + var(--w) * 3)); }
.still[data-mode="filament"] .still__bloom { display: none; }
.still__bloom { mix-blend-mode: screen; opacity: var(--glow); filter: blur(calc(4px + var(--glow) * 22px)) brightness(1.5); }
.still__tint { position: absolute; inset: 0; background: var(--tint); mix-blend-mode: multiply; transition: background-color .35s ease-out; }
.still[data-tone="warm"] .still__img, .still[data-tone="warm"] .still__bloom { filter: sepia(0.75) saturate(1.6) hue-rotate(-14deg); }
.still[data-tone="warm"] .still__bloom { filter: sepia(0.75) saturate(1.6) hue-rotate(-14deg) blur(calc(4px + var(--glow) * 22px)) brightness(1.5); }

/* code tab */
.code { align-items: center; }
.code__box {
  position: relative; width: min(600px, 100%); max-height: 100%; overflow: hidden; display: grid;
  border-radius: 16px; background: rgba(0, 0, 0, 0.36); box-shadow: inset 0 0 0 1px var(--line);
}
.code__pre {
  margin: 0; padding: 22px 24px; min-height: 0; overflow: auto; overscroll-behavior: contain;
  font: 400 13px/1.75 ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--mute); white-space: pre; tab-size: 2;
  -webkit-user-select: text; user-select: text;
}
.code__pre:focus-visible { outline-offset: -4px; border-radius: 14px; }
.code__pre .ln { display: block; }
.code__pre .c { color: var(--faint); }
.code[data-locked] .code__pre {
  overflow: hidden; -webkit-user-select: none; user-select: none; padding-bottom: 56px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 20%, transparent 74%);
  mask-image: linear-gradient(to bottom, #000 0, #000 20%, transparent 74%);
}
.code[data-locked] .ln:nth-child(n+4) { filter: blur(1.5px); }
.code[data-locked] .ln:nth-child(n+6) { filter: blur(3px); }
.code[data-locked] .ln:nth-child(n+8) { filter: blur(5px); }
.code__gate { position: absolute; left: 0; right: 0; bottom: 0; display: grid; justify-items: center; gap: 12px; padding: 0 20px 26px; text-align: center; }
.code__title { font-family: var(--font-head); font-weight: var(--head-w); font-size: calc(17px * var(--head-s)); letter-spacing: calc(var(--head-t) * 0.5); color: var(--ink); }
.code__copy { position: absolute; right: 10px; top: 10px; background: rgba(27, 24, 22, 0.92); box-shadow: 0 0 0 1px var(--line); }
.code__copy:hover { background: #26221f; }

/* side panel */
.side {
  position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0;
  padding: 26px 26px 24px; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: rgba(243, 238, 231, 0.14) transparent;
}
.sheet__close {
  position: absolute; top: 15px; right: 15px; z-index: 5; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: var(--mute);
  transition: color .2s ease-out, background-color .2s ease-out;
}
.sheet__close:hover { color: var(--ink); background: var(--fill); }
.sheet__close svg { width: 17px; height: 17px; }
.side__name { padding-right: 44px; font-size: calc(22px * var(--head-s)); line-height: 1.2; letter-spacing: calc(var(--head-t) * 0.7); text-wrap: balance; }
.side__meta { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 13.5px; line-height: 1.4; color: var(--faint); }
.side__meta .tag--free { background: var(--fill); }
.side__meta .pass { display: inline-flex; align-items: center; gap: 6px; color: var(--mute); }
.side__meta svg { width: 13px; height: 13px; }
.side__desc { margin-top: 14px; font-size: 15px; line-height: 1.5; color: var(--mute); text-wrap: pretty; }
.side__body { margin-top: 26px; display: grid; gap: 8px; }
.side__label { margin-bottom: 2px; font-size: 13px; color: var(--faint); }
.side__hint { margin-top: 4px; font-size: 13.5px; line-height: 1.5; color: var(--faint); text-wrap: pretty; }
/* Reset: every control back where the piece starts (double-click a slider for just that one) */
.side__reset { align-self: flex-start; width: fit-content; min-height: 44px; min-width: 44px; margin: -4px 0 0 -10px; padding: 0 10px; font: inherit; font-size: 13px; color: var(--faint); background: none; border: 0; cursor: pointer; transition: color 0.15s; }
.side__reset:hover, .side__reset:focus-visible { color: var(--ink); }

/* controls: label + segmented pill, and bar sliders */
.row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 48px; font-size: 15px; }
.row__name { color: var(--mute); }
/* a switch's name with an "i" beside it, whose note opens under it (2026-11-12) */
.row__lead { position: relative; display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.tip {
  position: relative; flex: none; width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center; color: var(--faint);
  transition: color .2s ease-out, background-color .2s ease-out;
}
.tip::after { content: ""; position: absolute; inset: -11px; } /* a 44px target for a finger */
.tip svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
.tip:hover, .tip[aria-expanded="true"] { color: var(--ink); background: var(--fill); }
.tip:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.tip__pop, .row__lead::after {
  opacity: 0; visibility: hidden; transform: translateY(-4px); pointer-events: none;
  transition: opacity .16s ease-out, transform .16s ease-out, visibility 0s linear .16s;
}
.tip__pop {
  position: absolute; z-index: 6; left: -6px; top: calc(100% + 10px); width: 252px;
  padding: 11px 13px 12px; border-radius: 12px; background: #1f1b19;
  box-shadow: 0 0 0 1px rgba(243, 238, 231, 0.1), 0 16px 36px -10px rgba(0, 0, 0, 0.8);
  font-size: 13px; line-height: 1.45; font-weight: 400; color: rgba(243, 238, 231, 0.86); white-space: normal; text-wrap: pretty;
}
/* a small nub under the "i", pointing up at it */
.row__lead::after {
  content: ""; position: absolute; z-index: 7; right: 6px; top: calc(100% + 5px); width: 10px; height: 10px;
  background: #1f1b19; border-radius: 2px 0 0 0; rotate: 45deg;
  box-shadow: -1px -1px 0 0 rgba(243, 238, 231, 0.1);
}
.tip[aria-expanded="true"] + .tip__pop, .row__lead:has(.tip[aria-expanded="true"])::after {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .2s ease-out, transform .28s var(--ease), visibility 0s;
}
@media (hover: hover) {
  .tip:hover + .tip__pop, .row__lead:has(.tip:hover)::after {
    opacity: 1; visibility: visible; transform: none;
    transition: opacity .2s ease-out .08s, transform .28s var(--ease) .08s, visibility 0s .08s;
  }
}
@media (prefers-reduced-motion: reduce) { .tip__pop, .row__lead::after { transform: none; } }
.seg { display: inline-flex; flex: none; padding: 3px; border-radius: 999px; background: var(--fill); }
.seg button, .seg i {
  display: inline-flex; align-items: center; min-height: 38px; padding: 0 15px; border-radius: 999px;
  font-style: normal; font-size: 14.5px; font-weight: 500; color: var(--mute); white-space: nowrap;
  transition: color .2s ease-out, background-color .25s ease-out, box-shadow .25s ease-out;
}
.seg button:hover { color: var(--ink); }
.seg button[aria-pressed="true"], .seg i.on {
  background: var(--bg); color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.13), 0 1px 3px rgba(0, 0, 0, 0.45);
}
.bar {
  position: relative; display: flex; align-items: center; justify-content: space-between; gap: 16px;
  height: 52px; padding: 0 18px; border-radius: 14px; overflow: hidden; background: var(--fill);
  font-size: 15px; cursor: ew-resize; touch-action: pan-y;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
.bar::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--p, 50%);
  background: rgba(243, 238, 231, 0.1); transition: background-color .2s ease-out;
}
.bar:hover::before, .bar.is-drag::before, .bar:focus-visible::before { background: rgba(243, 238, 231, 0.15); }
.bar:focus-visible { outline-offset: 2px; border-radius: 14px; }
.bar__name, .bar__val { position: relative; pointer-events: none; }
.bar__name { color: var(--mute); }
.bar__val { color: var(--ink); font-variant-numeric: tabular-nums; }
.swatches { display: flex; gap: 6px; }
.swatches i { width: 22px; height: 22px; border-radius: 50%; }

/* the rest of the controls fade and blur out under the gate */
.gate { position: relative; }
.gate__ctls {
  display: grid; gap: 8px; pointer-events: none; -webkit-user-select: none; user-select: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.6) 26%, transparent 76%);
  mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.6) 26%, transparent 76%);
}
.gate__ctls > * { min-height: 46px; height: 46px; }
.gate__ctls > :nth-child(1) { filter: blur(0.5px); }
.gate__ctls > :nth-child(2) { filter: blur(2px); }
.gate__ctls > :nth-child(3) { filter: blur(4px); }
.gate__ctls > :nth-child(4) { filter: blur(6px); }
.gate__msg { position: absolute; left: 0; right: 0; bottom: 0; display: grid; justify-items: center; text-align: center; padding: 0 8px 2px; }
.gate__title { font-family: var(--font-head); font-weight: var(--head-w); font-size: calc(17px * var(--head-s)); line-height: 1.3; letter-spacing: calc(var(--head-t) * 0.5); color: var(--ink); }
.gate__line { margin-top: 4px; max-width: 30ch; font-size: 14px; line-height: 1.45; color: var(--mute); text-wrap: balance; }
.gate__msg .btn { margin-top: 14px; }

/* what you get: the same bar, as a choice */
.opts { display: grid; gap: 8px; }
.opt {
  position: relative; display: grid; grid-template-columns: 14px 58px minmax(0, 1fr) auto; align-items: center; gap: 12px;
  height: 52px; padding: 0 18px 0 16px; border-radius: 14px; cursor: pointer;
  background: var(--fill); font-size: 15px; color: var(--mute);
  transition: background-color .2s ease-out, box-shadow .2s ease-out;
}
.opt:hover { background: var(--fill-hover); }
.opt input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.opt__dot { width: 14px; height: 14px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--faint); transition: box-shadow .2s ease-out; }
.opt__name { color: var(--ink); }
.opt__w { font-variant-numeric: tabular-nums; }
.opt:has(input:checked) { background: rgba(243, 238, 231, 0.13); box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.14); }
.opt:has(input:checked) .opt__dot { box-shadow: inset 0 0 0 4.5px var(--ink); }
.opt:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }

.side__actions { position: relative; margin-top: auto; padding-top: 22px; display: grid; gap: 8px; }
/* the vault's "not now" line (v2.js vaultSay): over the panel, just above the button */
.vsay { white-space: normal; }
.side__actions .btn { min-height: 48px; }
.btn--line { background: transparent; box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.16); color: var(--mute); }
.btn--line:hover { background: var(--fill); color: var(--ink); }
.btn--line svg { width: 16px; height: 16px; }
.side__saves { display: flex; justify-content: center; align-items: center; gap: 6px; min-height: 44px; font-size: 14px; color: var(--faint); }
.side__saves a, .side__saves button { display: inline-flex; align-items: center; min-height: 44px; padding: 0 8px; color: var(--mute); transition: color .2s ease-out; }
.side__saves a:hover, .side__saves button:hover { color: var(--ink); }
.side__saves[data-locked] { gap: 8px; padding: 4px 0; font-size: 13.5px; line-height: 1.45; text-align: center; }
.side__saves svg { width: 13px; height: 13px; flex: none; }
.side__note { text-align: center; font-size: 13.5px; line-height: 1.5; color: var(--faint); padding: 6px 0 2px; }
.side__note a { color: var(--mute); text-decoration: underline; text-decoration-color: rgba(165, 156, 145, 0.4); text-underline-offset: 3px; transition: color .2s ease-out; display: inline-block; padding: 10px 0; margin: -10px 0; }
.side__note a:hover { color: var(--ink); }

/* ---------- motion (only when JS opts in) ---------- */
/* The entrances start at 1% opacity, not 0: it looks the same, but Chrome
   ignores anything painted fully transparent when it judges a page's main
   content, so a hero that faded in from 0 never counted and Google's speed
   score picked some small late element instead (2026-11-06). Same in
   home.css rise-blur. */
@keyframes rise { from { opacity: 0.01; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.js .enter { animation: rise .9s var(--ease) both; animation-delay: calc(var(--i, 0) * 80ms); }
.js .grid.animate .tile { animation: rise .8s var(--ease) both; animation-delay: calc(var(--d, 0ms) + var(--i, 0) * 40ms); }
.rv-on .rv { opacity: 0; transform: translateY(16px); transition: opacity .9s ease-out, transform 1.1s var(--ease), background-color .3s ease-out; transition-delay: calc(var(--i, 0) * 70ms), calc(var(--i, 0) * 70ms), 0s; }
.rv-on .rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

/* ---------- responsive ---------- */
@media (max-width: 1023px) {
  :root { --gutter: 32px; }
  .grid { --cols: 2; }
  .grid--browse { --cols: 3; }
  .section { padding-top: 120px; }
  .shelves__head { margin-bottom: 72px; }
  .shelf { display: block; }
  .shelf + .shelf { margin-top: 88px; }
  .shelf__text { padding-right: 0; max-width: 560px; }
  .shelf__previews { margin-top: 28px; }
  .phead--shelf { display: block; }
  .phead__side { margin-top: 32px; }
  .foot__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .foot__brand { grid-column: 1 / -1; }
}
@media (max-width: 1100px) {
  dialog.sheet { padding: 20px; }
  .sheet__panel { grid-template-columns: minmax(0, 1fr) 360px; }
  .side { padding: 24px 22px 22px; }
}
@media (max-width: 900px) {
  dialog.sheet { padding: 12px; place-items: start center; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; }
  /* iPhone Safari draws a pinned panel only between its two bars, and paints the bars a solid colour taken from
     the panel's top and bottom edges. These two soft bands carry that colour (the dim over the page), so the bars
     read as part of the overlay and the content fades into them instead of stopping at a hard line. */
  dialog.sheet::before, dialog.sheet::after {
    content: ""; position: fixed; left: 0; right: 0; height: 28px; z-index: 6; pointer-events: none;
  }
  dialog.sheet::before { top: 0; background: linear-gradient(to bottom, rgb(6, 5, 4), rgba(6, 5, 4, 0)); }
  dialog.sheet::after { bottom: 0; background: linear-gradient(to top, rgb(6, 5, 4), rgba(6, 5, 4, 0)); }
  .sheet__panel { height: auto; grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .stage { height: min(52vh, 460px); min-height: 320px; }
  .stage, .side { border-radius: 24px; }
  .side { overflow: visible; padding: 24px; }
  .side__name { padding-right: 0; }
  .side__actions { margin-top: 0; }
  .sheet__close { top: 16px; right: 14px; color: var(--ink); background: rgba(12, 10, 9, 0.6); }
  .sheet__close:hover { background: rgba(12, 10, 9, 0.85); }
  .stage__steps { right: 64px; }
}
@media (max-width: 640px) {
  :root { --gutter: 24px; --gap: 8px; --r: 10px; }
  .h1 { font-size: calc(38px * var(--head-s)); line-height: 1.08; }
  .h1--page { font-size: calc(34px * var(--head-s)); }
  .h2 { font-size: calc(28px * var(--head-s)); }
  .nav { height: 68px; grid-template-columns: auto 1fr auto; }
  .wordmark { font-size: calc(18px * var(--head-s)); }
  .nav__links { justify-self: end; gap: 0; }
  .nav__link { padding: 0 9px; font-size: 14.5px; gap: 4px; }
  .nav__right { gap: 0; }
  .nav__right .signin { display: none; }
  .nav__right .btn--sm { min-height: 44px; padding: 0 14px; font-size: 13.5px; margin-left: 4px; }
  .lbl-l { display: none; }
  .lbl-s { display: inline; }
  .menu__panel { border-radius: 20px; }
  .menu__grid { grid-template-columns: minmax(0, 1fr); gap: 4px; padding: 8px; }
  .menu__row { grid-template-columns: 60px minmax(0, 1fr); gap: 16px; padding: 10px; }
  .menu__thumb { width: 60px; }
        .open { padding-top: 40px; padding-bottom: 44px; }
  .open__cta { flex-wrap: wrap; }
  .phead { padding-top: 28px; padding-bottom: 28px; }
  .chip { min-height: 44px; padding: 0 14px; }
  .toolbar { margin-bottom: 20px; }
  .grid--browse { --cols: 2; }
  .tile__meta { left: 10px; right: 10px; bottom: 10px; font-size: 12px; }
  .tile:not(.tile--wide) .kind { display: none; }
  .index__foot { flex-direction: column; gap: 4px; }
  .index__foot--stack { gap: 16px; }
  .section { padding-top: 96px; }
  .shelf__previews .tile__meta .name { display: none; }
  .shelf__previews .tile__meta { justify-content: flex-end; }
  .next { flex-wrap: wrap; padding: 12px 16px 16px 12px; gap: 16px; margin-top: 88px; }
  .next__img { width: 96px; height: 64px; }
  .next__note { width: 100%; white-space: normal; }
  .foot { margin-top: 96px; }
  .foot__in { padding-top: 48px; }
  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot__col a { min-height: 44px; padding: 0; }
  .foot__base { margin-top: 48px; padding-bottom: env(safe-area-inset-bottom); }
  .fontsw { left: 12px; bottom: 12px; max-width: calc(100vw - 186px); }
  .fontsw__label { display: none; }
  .drafts { right: 12px; bottom: 12px; }
  dialog.sheet { padding: 8px; }
  .sheet__panel { gap: 8px; }
  .stage, .side { border-radius: 20px; }
  .stage { height: min(50vh, 380px); min-height: 300px; }
  .stage__view { padding: 68px 16px 60px; }
  .stage__tabs { left: 12px; top: 12px; }
  .stage__steps { top: 12px; right: 58px; }
  .sheet__close { top: 12px; right: 10px; }
  .stage__badge { left: 18px; bottom: 22px; }
  .stage__play { right: 12px; bottom: 12px; }
  .pv { gap: 12px; }
  .pv__cap { font-size: 12.5px; }
  .code__pre { padding: 18px; font-size: 12px; }
  .side { padding: 20px; }
  .opt { grid-template-columns: 14px 52px minmax(0, 1fr) auto; gap: 10px; padding: 0 14px; }
  .seg button { padding: 0 13px; }
  .countline { margin-bottom: 20px; }
}

/* Round 7: "Browse the vault" scrolls to the collections on the home page */
#shelves { scroll-margin-top: 32px; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }


/* ===== Round 8 =====
   One main action, "Join for free", and three people who might be looking:
   a visitor, someone signed in without the pass, and a member.
   html[data-as] is set by the "Viewing as" control (visitor when absent). */
html:not([data-as="signed"]):not([data-as="member"]) :is(.as-signed, .as-member, .as-in) { display: none !important; }
html[data-as="signed"] :is(.as-visitor, .as-member, .as-out) { display: none !important; }
html[data-as="member"] :is(.as-visitor, .as-signed, .as-out, .as-nopass) { display: none !important; }

/* home: one button, one quiet line under it. The block keeps its height in
   every state so the reviews line never moves. */
.open__cta { flex-direction: column; align-items: center; justify-content: flex-start; gap: 12px; min-height: 76px; }
.cta__note { font-size: 13.5px; line-height: 20px; color: var(--mute); }

/* the review control, bottom right (the collection pages restate this in shelf.css) */
.review { position: fixed; right: 16px; bottom: 16px; z-index: 30; display: grid; justify-items: end; gap: 8px; }
.review--home { bottom: 60px; } /* clear of the earlier-rounds links */
.review__row {
  display: flex; align-items: center; gap: 2px; padding: 3px; border-radius: 999px;
  background: rgba(27, 24, 22, 0.94); box-shadow: 0 0 0 1px var(--line);
}
.review__label { padding: 0 8px 0 10px; font-size: 12px; color: var(--faint); white-space: nowrap; }
.review__row button, .review__row a {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  height: 30px; min-width: 30px; padding: 0 11px; border-radius: 999px; flex: none;
  font-size: 12px; font-weight: 500; color: var(--mute); white-space: nowrap;
  transition: color .2s ease-out, background-color .2s ease-out;
}
.review__row button::after, .review__row a::after { content: ""; position: absolute; inset: -7px 0; } /* 44px tall hit area */
.review__row button:hover, .review__row a:hover { color: var(--ink); }
.review__row button[aria-pressed="true"], .review__row a[aria-current] { background: var(--ink); color: var(--bg); }
.review__row--ed a { padding: 0; width: 32px; }

/* an edition pill that is a link (sections you scroll) lights up the same way as a pressed one */
.chip[aria-current="true"] { background: var(--ink); color: var(--bg); }
.chip[aria-current="true"] .n { color: rgba(12, 10, 9, 0.55); }

/* one material, several formats: the name needs the room, not a fixed 58px */
.opt.opt--fmt { grid-template-columns: 14px minmax(0, 1fr) auto auto; gap: 12px; }
.opt--fmt .opt__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.opt--fmt .opt__spec { font-size: 13.5px; white-space: nowrap; }
.opt--fmt .opt__w { min-width: 3.4em; text-align: right; font-size: 13.5px; white-space: nowrap; }

/* an edition's promo in the item view: the video, larger, and nothing to step through */
.stage[data-promo] .pv__frame { width: min(720px, 86cqw, calc(min(720px, 72cqh) * var(--ar))); }
.stage[data-promo] .stage__steps { display: none; }
.pv__cap:empty { display: none; }

@media (max-width: 640px) {
  .review { right: 12px; bottom: 56px; }
  .review--home { bottom: 56px; }
  .review__row--as .review__label { display: none; }
  /* a member has no button in the nav, so the account link takes its place */
  html[data-as="member"] .nav__right .signin { display: inline-flex; padding: 0 6px; font-size: 14.5px; }
  .opt.opt--fmt { gap: 10px; padding: 0 14px; }
}


/* ===== Round 9 · the item view on Materials =====
   One material comes in several formats: a switch on the stage picks the
   format, the panel lists sizes for it. Locked materials preview small.
   (Round 10 moved the switch and the arrows; see the end of this file.) */

/* a locked material, seen without Pro: clearly smaller, with dark room around it */
.stage[data-small] .pv__frame { width: min(340px, 70cqw, calc(min(340px, 48cqh) * var(--ar))); border-radius: 12px; }
.stage[data-small] .stage__ambient { opacity: .16; }

/* alpha formats sit on quiet checks so the transparent ground can be seen */
.pv__frame[data-alpha] {
  background-color: #141211;
  background-image: conic-gradient(#221f1d 25%, transparent 0 50%, #221f1d 0 75%, transparent 0);
  background-size: 22px 22px;
}
.pv__frame[data-alpha="cut"] .pv__img, .pv__frame[data-alpha="cut"] .pv__video {
  -webkit-mask-image: radial-gradient(ellipse closest-side at 50% 50%, #000 68%, transparent 96%);
  mask-image: radial-gradient(ellipse closest-side at 50% 50%, #000 68%, transparent 96%);
}

/* sizes for the format the stage is showing */
.side__label--split { display: flex; justify-content: space-between; gap: 12px; }
.side__label--split span:last-child { color: var(--mute); }
.opts--off .opt, .opts--off .opt:hover { background: transparent; box-shadow: inset 0 0 0 1px var(--line); cursor: default; }
.opts--off .opt input { cursor: default; }
.opts--off .opt__dot { visibility: hidden; }
.opts--off .opt__name { color: var(--mute); }
.opts--off .opt { color: var(--faint); }
.btn__n { font-weight: 400; opacity: .56; font-variant-numeric: tabular-nums; }
.side__saves button { gap: 7px; }

/* an edition's promo: a normal video, at a size for watching, with plain controls under it */
.stage[data-promo] .pv { gap: 12px; }
.stage[data-promo] .pv__frame { width: min(880px, 90cqw, calc((100cqh - 232px) * var(--ar))); }
.vctl {
  justify-self: stretch; min-width: 0; display: flex; align-items: center; gap: 8px;
  height: 48px; padding: 0 2px; border-radius: 999px; background: var(--on-fill);
}
.vctl[hidden] { display: none; }
.vctl__btn {
  flex: none; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; color: var(--ink);
  transition: background-color .2s ease-out;
}
.vctl__btn:hover { background: rgba(243, 238, 231, 0.1); }
.vctl__btn svg { width: 15px; height: 15px; }
.vctl__btn .ic-play, .vctl__btn[data-state="paused"] .ic-pause { display: none; }
.vctl__btn[data-state="paused"] .ic-play { display: block; }
.vctl__btn .ic-sound, .vctl__btn[aria-pressed="true"] .ic-mute { display: none; }
.vctl__btn[aria-pressed="true"] .ic-sound { display: block; }
.vctl__t { flex: none; min-width: 2.5em; text-align: center; font-size: 12.5px; line-height: 1; color: var(--mute); font-variant-numeric: tabular-nums; }
.vctl__scrub {
  --p: 0%;
  flex: 1 1 0; width: 0; min-width: 0; height: 44px; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer;
}
.vctl__scrub::-webkit-slider-runnable-track {
  height: 3px; border-radius: 3px;
  background: linear-gradient(to right, rgba(243, 238, 231, 0.86) var(--p), rgba(243, 238, 231, 0.18) var(--p));
}
.vctl__scrub::-moz-range-track {
  height: 3px; border-radius: 3px;
  background: linear-gradient(to right, rgba(243, 238, 231, 0.86) var(--p), rgba(243, 238, 231, 0.18) var(--p));
}
.vctl__scrub::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; margin-top: -4.5px; border: 0; border-radius: 50%; background: var(--ink); }
.vctl__scrub::-moz-range-thumb { width: 12px; height: 12px; border: 0; border-radius: 50%; background: var(--ink); }
.vctl__scrub:focus-visible { outline: 2px solid var(--ink); outline-offset: -8px; border-radius: 999px; }
.facts { display: grid; }
.fact { display: flex; justify-content: space-between; gap: 20px; padding: 13px 0; border-top: 1px solid var(--line); font-size: 14.5px; line-height: 1.45; }
.fact:last-child { border-bottom: 1px solid var(--line); }
.fact span:first-child { flex: none; color: var(--mute); }
.fact span:last-child { text-align: right; color: var(--ink); text-wrap: balance; }

@media (max-width: 900px) {
  /* the close button and the arrows share the top right of the stage here */
}
@media (max-width: 640px) {
  /* phones: the switch takes its own row under the arrows, so all four formats show */
  .stage[data-small] .pv__frame { width: min(340px, 52cqw, calc(40cqh * var(--ar))); }
  .stage[data-promo] .pv__frame { width: min(880px, 92cqw, calc((100cqh - 204px) * var(--ar))); }
  .vctl { gap: 4px; }
  .vctl [data-vt] { display: none; }
}


/* ===== Round 10 · the opened material, tidied =====
   The format switch and the arrows belong to the stage; the panel holds the
   name and one small mark. (Round 11 pinned the switch and the arrows to the
   stage edges and removed the colour experiment; see below.) */
/* (the format switch itself is styled in round 13, at the end of this file) */
.fmtsw[hidden] { display: none; }

.pvsteps { display: flex; align-items: center; gap: 2px; color: var(--on-mute); }
.pvsteps[hidden] { display: none; }
.pvsteps__n { min-width: 5.6em; text-align: center; font-size: 13px; line-height: 1; font-variant-numeric: tabular-nums; }

/* side panel: the name and a small mark */
.side__meta:empty, .side__desc:empty { display: none; }
.side__name:has(.side__mark) { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.side__mark { display: inline-flex; align-items: center; color: var(--faint); }
.side__mark svg { width: 14px; height: 14px; }
.side__mark .tag--free { background: var(--fill); font-family: var(--sans); letter-spacing: 0; }
.btn--dl { padding: 8px 16px; white-space: normal; text-align: center; line-height: 1.25; }
.btn--dl .btn__n { white-space: nowrap; }



/* ===== Round 11 =====
   1. Every overlay blurs the page behind it and dims it a little.
   2. The access prompt, and the promo as a plain video lightbox.
   3. The opened material: bars pinned to the top and bottom of the stage.
      (Round 13 settled on the slim side panel; its rules are at the end.) */

/* ---------- 1 · the page behind an overlay ---------- */
.review, .drafts { margin-right: var(--sbw, 0px); } /* see the note on html.is-locked at the top */

dialog.ask, dialog.vbox {
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0; border: 0; color: var(--ink);
  display: none; place-items: center; overflow: hidden;
  opacity: 0; transition: opacity .18s ease-out;
}
dialog.ask[open], dialog.vbox[open] { display: grid; }
dialog.ask::backdrop, dialog.vbox::backdrop { background: transparent; }
dialog.ask.is-open, dialog.vbox.is-open { opacity: 1; transition: opacity .3s ease-out; }

/* a plain dim where a browser cannot blur what is behind */
dialog.sheet, dialog.ask, dialog.vbox { background: rgba(8, 7, 6, 0.82); }
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  dialog.sheet, dialog.vbox {
    background: rgba(8, 7, 6, 0.4);
    -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  }
  /* the prompt is words on the page itself, so it gets a touch more of both, most of it behind the words */
  dialog.ask {
    background: radial-gradient(ellipse 62% 58% at 50% 50%, rgba(8, 7, 6, 0.64) 0%, rgba(8, 7, 6, 0.44) 100%);
    -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  }
}

.ovl__close {
  position: absolute; top: 20px; right: 20px; z-index: 2; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink); background: rgba(243, 238, 231, 0.08);
  transition: background-color .2s ease-out;
}
.ovl__close:hover { background: rgba(243, 238, 231, 0.16); }
.ovl__close svg { width: 17px; height: 17px; }

/* ---------- 2a · the access prompt ---------- */
dialog.ask { padding: 24px; }
.ask__in {
  width: min(560px, 100%); display: grid; justify-items: center; text-align: center;
  transform: translateY(8px); transition: transform .18s ease-out;
}
dialog.ask.is-open .ask__in { transform: none; transition: transform .5s var(--ease); }
.ask__tag {
  display: inline-flex; align-items: center; height: 26px; padding: 0 11px; border-radius: 999px;
  font-size: 12.5px; line-height: 1; color: var(--mute); box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.16);
}
.ask__title {
  margin-top: 20px; font-family: var(--font-head); font-weight: var(--head-w);
  font-size: calc(48px * var(--head-s)); line-height: 1.06; letter-spacing: var(--head-t); text-wrap: balance;
}
.ask__line { margin-top: 16px; max-width: 34ch; font-size: 17px; line-height: 1.5; color: rgba(243, 238, 231, 0.72); text-wrap: balance; }
.ask__go { margin-top: 32px; min-width: 240px; min-height: 52px; padding: 0 28px; font-size: 16px; }
.ask__quiet {
  margin-top: 6px; display: inline-flex; align-items: center; min-height: 44px; padding: 0 12px;
  font-size: 14.5px; color: var(--mute); transition: color .2s ease-out;
}
.ask__quiet:hover { color: var(--ink); }

/* ---------- 2b · the promo: a video on its own ---------- */
dialog.vbox { padding: 76px 28px; }
.vbox__in {
  margin: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px;
  /* 16:9, up to 1100 wide, and never taller than the window (controls and caption included) */
  width: min(1100px, 100%, calc((100vh - 248px) * 16 / 9));
  width: min(1100px, 100%, calc((100dvh - 248px) * 16 / 9));
  transform: translateY(10px) scale(0.985); transition: transform .18s ease-out;
}
dialog.vbox.is-open .vbox__in { transform: none; transition: transform .5s var(--ease); }
.vbox__frame {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 20px; background: #000; cursor: pointer;
  box-shadow: 0 0 0 1px var(--line), 0 40px 90px -30px rgba(0, 0, 0, 0.85);
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.vbox__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; -webkit-user-drag: none; }
.vbox .vctl { background: rgba(20, 17, 16, 0.72); box-shadow: inset 0 0 0 1px var(--line); }
.vbox__cap { text-align: center; font-size: 13px; line-height: 20px; color: var(--mute); }

/* ---------- 3 · the opened material ---------- */
/* three rows: top bar, preview, bottom bar. The preview is centred in what is left. */
.stage[data-mat] { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; }
.stage[data-mat] .stage__view { position: relative; inset: auto; min-height: 0; padding: 20px 24px; container-type: size; }
.stage[data-mat] .stage__steps, .stage[data-mat] .stage__play, .stage[data-mat] .stage__badge { display: none; }
.stage[data-mat] .pv { gap: 0; }
/* (the preview's size is set in round 13, at the end of this file) */

/* a bar: left, centre, right. The side columns are equal, so the centre is the stage's centre line. */
.mbar {
  position: relative; z-index: 4; min-width: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; column-gap: 16px;
}
.mbar[hidden] { display: none; }
.mbar > * { grid-row: 1; min-width: 0; }
.mbar > :nth-child(1) { grid-column: 1; justify-self: start; }
.mbar > :nth-child(2) { grid-column: 2; }
.mbar > :nth-child(3) { grid-column: 3; justify-self: end; }
.mbar--top { padding: 16px 16px 0; }
.mbar--bot { padding: 0 16px 16px; }
.mbar__end { width: 44px; height: 44px; } /* keeps the switch on the centre line; on narrow screens the close button sits over it */
.mbar__name {
  display: flex; align-items: center; gap: 10px; padding-left: 8px;
  font-family: var(--font-head); font-weight: var(--head-w); font-size: calc(18px * var(--head-s));
  line-height: 1.2; letter-spacing: calc(var(--head-t) * 0.55); white-space: nowrap;
}

/* a loop pauses and plays on a press of the preview; the mark shows only while it is paused */
.pv__toggle { position: absolute; inset: 0; z-index: 5; width: 100%; height: 100%; display: grid; place-items: center; }
.pv__toggle[hidden] { display: none; }
.pv__toggle span {
  width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center;
  color: var(--ink); background: rgba(12, 10, 9, 0.62); box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.2);
  opacity: 0; transform: scale(0.92); transition: opacity .2s ease-out, transform .3s var(--ease);
}
.pv__toggle svg { width: 18px; height: 18px; margin-left: 2px; }
.pv__toggle[data-state="paused"] span { opacity: 1; transform: none; }
.pv__toggle:focus-visible { outline-offset: -4px; border-radius: 14px; }

/* (the two panels of the opened material are sized in round 13, at the end of this file) */

@media (max-width: 900px) {
  dialog.vbox { padding: 72px 16px; }
  .vbox__in {
    width: min(1100px, 100%, calc((100vh - 240px) * 16 / 9));
    width: min(1100px, 100%, calc((100dvh - 240px) * 16 / 9));
  }
  .vbox__frame { border-radius: 16px; }

  /* stacked. The name joins the close button on the first line; the switch takes the second. */
  .mbar--top { grid-template-columns: minmax(0, 1fr) 44px; row-gap: 8px; padding: 16px 14px 0 16px; }
  .mbar--top > .mbar__name { grid-area: 1 / 1; }
  .mbar--top > .mbar__end { grid-area: 1 / 2; }
  .mbar--top > .fmtsw { grid-area: 2 / 1 / 3 / -1; justify-self: center; }
  .stage[data-mat] .stage__view { padding: 16px; }
}
@media (max-width: 640px) {
  .ovl__close { top: 12px; right: 12px; }
  .ask__title { font-size: calc(34px * var(--head-s)); }
  .ask__line { font-size: 16px; }
  .ask__go { margin-top: 28px; }
  dialog.vbox { padding: 64px 8px; }
  .vbox__in {
    width: min(1100px, 100%, calc((100vh - 224px) * 16 / 9));
    width: min(1100px, 100%, calc((100dvh - 224px) * 16 / 9));
  }

  .mbar--top { padding: 12px 10px 0 12px; }
  .mbar--bot { padding: 0 12px 12px; }
  .mbar__name { font-size: calc(17px * var(--head-s)); }
}

/* ==================================================================
   Round 12 · "Pro", the paid tier, as one small chip.
   One component (.pro): in the item view's "Get access" button, in the
   "Pro to download" line of the code-piece views, and (a step larger) as
   the tag in the access prompt. Members never see it.
   (Round 13 took it off the grid tiles and off the material's name, and
   gave it its place in the button; see the end of this file.)
   ================================================================== */
.pro {
  display: inline-flex; align-items: center; flex: none; vertical-align: baseline;
  height: 18px; padding: 0 7px; border-radius: 999px;
  font-family: var(--sans); font-size: 11px; font-weight: 600; line-height: 1; letter-spacing: 0.02em;
  white-space: nowrap; text-transform: none;
  color: #f3b08c;
  background: linear-gradient(rgba(240, 115, 58, 0.15), rgba(240, 115, 58, 0.15)), rgba(12, 10, 9, 0.62);
  box-shadow: inset 0 0 0 1px rgba(240, 115, 58, 0.26);
}
.pro--lg { height: 24px; padding: 0 11px; font-size: 12.5px; }
html[data-as="member"] .pro { display: none; }

/* the line under the name in the other item views: "Pro to download" */
.side__meta .pass { gap: 7px; align-items: baseline; }
html[data-as="member"] .side__meta .pass { display: none; }

/* the access prompt: the tag is the chip itself */
.ask__tag { height: auto; padding: 0; box-shadow: none; }


/* ==================================================================
   Round 13 · Materials, settled.
   1. No chip on grid tiles: a tile is either plain or tagged "Free".
   2. The opened material is one viewer at one size: a stage and a slim
      panel. Neither changes by a pixel with the format or the material.
   3. Smaller previews: 260 on the long side for a locked material seen
      without Pro, 380 for free materials and members.
   4. The format switch: a solid track, one off-white thumb that slides.
   5. The panel: name, small file facts, the size, the button.
   6. "Get access": the chip sits at the right end of the pill.
   ================================================================== */

/* ---------- 1 · tiles ---------- */
/* the row keeps the height the "Free" tag gives it, so every name sits on the same line */
.tile__meta { min-height: 22px; }

/* ---------- 2 · one viewer, one size ---------- */
dialog.sheet[data-mode="mat"] .sheet__panel {
  width: min(980px, 100%); max-width: none; height: min(620px, 100%);
  grid-template-columns: minmax(0, 1fr) 300px;
}
/* the bars are given their height outright, so the preview's room never depends on what is in them */
.stage[data-mat] { grid-template-rows: 60px minmax(0, 1fr) 60px; }
.mbar--top { align-self: start; }
.mbar--bot { display: flex; justify-content: center; align-self: end; }

/* ---------- 3 · the preview: small, centred, calm room around it ---------- */
.stage[data-mat] { --pv: 380px; }
.stage[data-mat][data-small] { --pv: 260px; }
/* --pv is the long side, whichever way the picture lies; it only gives way to a stage too small to hold it */
.stage[data-mat] .pv__frame,
.stage[data-mat][data-small] .pv__frame {
  width: min(var(--pv), calc(var(--pv) * var(--ar)), 100cqw, calc(100cqh * var(--ar)));
}

/* ---------- 4 · the format switch ---------- */
/* Every option is one width (--seg), so the switch has one width per set of
   formats (--n of them) and the thumb's place is plain arithmetic: its index
   (--at) times that width. Nothing is measured, so it cannot land off. */
.fmtsw {
  --seg: 104px; --n: 4; --at: 0;
  position: relative; display: flex; width: calc(var(--n) * var(--seg) + 8px); max-width: 100%;
  padding: 4px; border-radius: 999px;
  background: var(--raise); box-shadow: inset 0 0 0 1px var(--line); /* the side panel's own grey, solid */
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain;
}
.fmtsw::-webkit-scrollbar { display: none; }
.fmtsw__thumb {
  position: absolute; z-index: 0; left: 4px; top: 4px; width: var(--seg); height: 36px; border-radius: 999px;
  background: var(--ink); pointer-events: none;
  transform: translateX(calc(var(--at) * var(--seg)));
  transition: transform .22s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.fmtsw.is-snap .fmtsw__thumb { transition: none; } /* a material just opened: the thumb is simply there */
.fmtsw button {
  position: relative; z-index: 1; flex: none; width: var(--seg); height: 36px; padding: 0; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13.5px; font-weight: 500; line-height: 1; white-space: nowrap; color: var(--mute);
  transition: color .22s ease-out;
}
.fmtsw button::after { content: ""; position: absolute; inset: -4px 0; } /* 44px tall hit area */
.fmtsw button:hover { color: var(--ink); }
.fmtsw button[aria-pressed="true"] { color: var(--bg); }

/* ---------- 5 · the slim panel ---------- */
dialog.sheet[data-mode="mat"] .side { padding: 22px; }
dialog.sheet[data-mode="mat"] .side__name {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; min-height: 30px; padding-right: 40px;
  font-size: calc(18px * var(--head-s));
}
dialog.sheet[data-mode="mat"] .side__body { margin-top: 20px; display: block; }
dialog.sheet[data-mode="mat"] .side__label { margin: 0 0 6px; font-size: 12px; line-height: 16px; color: var(--faint); }
dialog.sheet[data-mode="mat"] .side__label + .facts + .side__label { margin-top: 16px; }

/* file facts: small label and value rows, no rules between them. Room for
   four always; "Length" is the fourth and only loops have it, so it comes
   and goes without moving anything. */
.facts--mini { display: grid; align-content: start; min-height: 96px; }
.facts--mini .fact { align-items: center; min-height: 24px; padding: 0; border: 0; font-size: 12.5px; line-height: 16px; }
.facts--mini .fact span:last-child { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* the size: one real row, chosen, and one that is not there yet */
.opts--mini { gap: 6px; }
.opt.opt--mini {
  grid-template-columns: 12px minmax(0, 1fr) auto; gap: 10px;
  height: 40px; padding: 0 14px 0 12px; border-radius: 12px; font-size: 13px; line-height: 1;
}
.opt--mini .opt__dot { width: 12px; height: 12px; }
.opt--mini:has(input:checked) .opt__dot { box-shadow: inset 0 0 0 4px var(--ink); }
.opt--mini .opt__spec { font-variant-numeric: tabular-nums; white-space: nowrap; }
.opt.opt--off, .opt.opt--off:hover {
  background: transparent; box-shadow: inset 0 0 0 1px var(--line); color: rgba(243, 238, 231, 0.34); cursor: default;
}
.opt--off .opt__name { color: inherit; }
/* a Particles video's sizes: the one most people should take says so, quietly, after its name */
.opt--mini .opt__name { display: flex; align-items: center; gap: 6px; min-width: 0; white-space: nowrap; }
@media (max-width: 640px) { .opt.opt--mini { height: 44px; } } /* a choice you tap: a full finger's height */
.opt--off .opt__dot { box-shadow: inset 0 0 0 1.5px rgba(243, 238, 231, 0.14); }

/* the one button keeps one height and one line, whatever it says */
dialog.sheet[data-mode="mat"] .side__actions .btn { height: 48px; min-height: 48px; white-space: nowrap; line-height: 1; }
dialog.sheet[data-mode="mat"] .btn--dl { padding: 0 12px; gap: 7px; font-size: 14px; }

/* ---------- 6 · "Get access" with the chip ---------- */
/* The words stay centred in the pill; the chip is placed at the right end,
   the same distance from the right edge as from the top and bottom
   ((48 - 22) / 2 = 13), so it sits concentric with the pill's end. Dark, so
   it reads on the off-white button. The 1px under the word lifts it half a
   pixel: "Pro" has no descender, so its true centre is a touch high. */
.btn--pro { position: relative; height: 48px; min-height: 48px; }
.btn--pro .pro {
  position: absolute; right: 13px; top: 0; bottom: 0; margin: auto 0;
  height: 22px; padding: 0 9px 1px; font-size: 12px; letter-spacing: 0.01em;
  color: #f8c3a4; background: #2a1a12; box-shadow: none;
}

@media (max-width: 900px) {
  /* stacked: the stage keeps a set height and the panel sits beneath it, both in one column */
  dialog.sheet[data-mode="mat"] .sheet__panel { width: min(640px, 100%); height: auto; grid-template-columns: minmax(0, 1fr); }
  dialog.sheet[data-mode="mat"] .stage { height: 600px; min-height: 0; }
  .stage[data-mat] { grid-template-rows: 112px minmax(0, 1fr) 60px; } /* name and close on one line, the switch on the next */
  dialog.sheet[data-mode="mat"] .mbar__name { display: flex; }
  dialog.sheet[data-mode="mat"] .side__name { display: none; }
  dialog.sheet[data-mode="mat"] .side__body { margin-top: 0; }
}
@media (min-width: 901px) {
  dialog.sheet[data-mode="mat"] .mbar__name { display: none; } /* the name is in the panel */
}
@media (max-width: 640px) {
  dialog.sheet[data-mode="mat"] .stage { height: 460px; }
  .stage[data-mat] { grid-template-rows: 108px minmax(0, 1fr) 56px; }
  .stage[data-mat][data-small] { --pv: 220px; } /* still plainly the smaller of the two on a phone */
  dialog.sheet[data-mode="mat"] .sheet__close { top: 12px; right: 10px; }
  /* four options still fit across a phone */
  .fmtsw { --seg: min(78px, calc((100vw - 64px) / 4)); }
  .fmtsw button { font-size: 12.5px; }
}


/* ==================================================================
   Round 14 · Filament: the real pieces, in the Materials viewer.
   A Filament piece opens in the same viewer as a material (the dialog
   carries data-mode="mat" as well as data-fil), so the size, the bars,
   the switch and the stacking on small screens are the rules above,
   untouched. Only what is Filament's own is here:
   1. The stage: a live canvas, one size, and the code box.
   2. The panel: slim bars, the gate, Export.
   ================================================================== */

/* ---------- 1 · the stage ---------- */
/* one square, whichever piece it is; it only gives way to a stage too small to hold it */
.stage[data-fil] { --fl: 420px; }
.fl, .stage[data-fil] .still { width: min(var(--fl), 100cqw, 100cqh); aspect-ratio: 1; }
.fl {
  position: relative; overflow: hidden; isolation: isolate; border-radius: 14px; background: #111; /* the pack's own paper */
  box-shadow: 0 0 0 1px var(--line), 0 30px 60px -28px rgba(0, 0, 0, 0.95);
  touch-action: pan-y; /* a finger moves the piece sideways and still scrolls the view */
}
.fl canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.fl__bloom { pointer-events: none; mix-blend-mode: plus-lighter; opacity: 0; } /* the glow, where the canvas cannot blur */
.fl[data-paper="light"] { background: #f2f2f0; } /* Paper: Light (the pack's own light paper) */
.fl[data-paper="light"] .fl__mode { color: rgba(12, 10, 9, 0.5); }

/* the code: one box at one size, the code scrolls inside it */
.stage[data-fil] .code__box { width: min(560px, 100%); height: 100%; }
.stage[data-fil] .code__pre { padding: 18px 20px; font-size: 12.5px; line-height: 1.7; }
/* without Pro: a smear, and one line */
.stage[data-fil] .code[data-locked] .code__pre {
  -webkit-mask-image: none; mask-image: none; padding-bottom: 18px;
  filter: blur(7px); opacity: .42;
}
.stage[data-fil] .code[data-locked] .ln { filter: none; }
.stage[data-fil] .code__gate { inset: 0; padding: 20px; align-content: center; }
.stage[data-fil] .code__gate .btn { display: none; }

/* ---------- 2 · the panel ---------- */
dialog.sheet[data-fil] .side__body { display: grid; align-content: start; gap: 6px; }
dialog.sheet[data-fil] .side__label { margin: 0; }
dialog.sheet[data-fil] .side__label--gap { margin-top: 10px; }

/* the bars, a size down to suit the slim panel */
dialog.sheet[data-fil] .bar { height: 36px; padding: 0 12px; border-radius: 10px; font-size: 13px; }
dialog.sheet[data-fil] .bar:focus-visible { border-radius: 10px; }
dialog.sheet[data-fil] .row {
  height: 36px; min-height: 36px; padding: 0 4px 0 12px; border-radius: 10px; background: var(--fill); font-size: 13px;
}
dialog.sheet[data-fil] .seg { padding: 0; background: transparent; }
dialog.sheet[data-fil] .seg button, dialog.sheet[data-fil] .seg i { min-height: 28px; padding: 0 11px; font-size: 12.5px; }

/* Colour: the bar is the spectrum, and a mark shows where you are. All the way left is no colour. */
.bar--hue {
  --spec: linear-gradient(90deg, #f3eee7 0%, #f3eee7 2%, hsl(14, 70%, 75%) 6%, hsl(60, 70%, 75%) 20%, hsl(120, 70%, 75%) 36%,
    hsl(180, 70%, 75%) 52%, hsl(240, 70%, 75%) 68%, hsl(300, 70%, 75%) 84%, hsl(360, 70%, 75%) 100%);
}
.bar--hue::before, .bar--hue:hover::before, .bar--hue.is-drag::before, .bar--hue:focus-visible::before {
  width: 100%; background: var(--spec); opacity: .2; transition: opacity .2s ease-out;
}
.bar--hue:hover::before, .bar--hue.is-drag::before, .bar--hue:focus-visible::before { opacity: .3; }
.bar--hue::after {
  content: ""; position: absolute; top: 7px; bottom: 7px; left: clamp(6px, var(--p, 0%), calc(100% - 6px));
  width: 2px; margin-left: -1px; border-radius: 1px; background: var(--ink); opacity: .7;
}
.bar--hue .bar__name, .bar--hue .bar__val { z-index: 1; } /* the words stay over the mark */

/* The gate. What is under it is plainly not yours to move: one heavy blur
   over the lot, well faded, no slider cursor, nothing to focus (the markup
   marks it inert). One height, so Export sits in the same place on every piece. */
.gate--fil { height: 162px; overflow: hidden; border-radius: 10px; }
.gate--fil .gate__ctls {
  gap: 6px; opacity: .4; filter: blur(5px); cursor: default;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.6) 40%, transparent 94%);
  mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.6) 40%, transparent 94%);
}
.gate--fil .gate__ctls > * { height: 36px; min-height: 36px; filter: none; cursor: default; }
.gate--fil .gate__msg { inset: 0; padding: 0 8px; align-content: center; gap: 10px; }
.gate--fil .gate__title { font-size: calc(15px * var(--head-s)); }
.gate--fil .gate__msg .btn { margin-top: 0; background: var(--raise-2); box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.14); }
.gate--fil .gate__msg .btn:hover { background: #26221f; }

/* Export: three quiet rows, each with its own action at the right */
.xp { display: grid; }
.xp__row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  height: 32px; font-size: 12.5px; line-height: 16px; color: var(--mute);
}
.xp__a {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  height: 26px; padding: 0 11px; border-radius: 999px;
  font-size: 12.5px; font-weight: 500; line-height: 1; white-space: nowrap;
  color: var(--ink); background: var(--fill);
  transition: background-color .2s ease-out;
}
button.xp__a { position: relative; }
button.xp__a::after { content: ""; position: absolute; inset: -3px 0; }
button.xp__a:hover { background: var(--fill-hover); }
/* a Pro piece, seen without Pro: the rows are there, greyed, and do nothing */
.xp[data-off] .xp__row { color: rgba(243, 238, 231, 0.3); }
.xp[data-off] .xp__a { color: rgba(243, 238, 231, 0.28); background: transparent; box-shadow: inset 0 0 0 1px var(--line); cursor: default; }

@media (max-width: 900px) {
  /* stacked, and for fingers */
  dialog.sheet[data-fil] .bar, dialog.sheet[data-fil] .row { height: 44px; min-height: 44px; }
  dialog.sheet[data-fil] .seg button, dialog.sheet[data-fil] .seg i { min-height: 36px; padding: 0 14px; }
  .gate--fil { height: 194px; }
  .gate--fil .gate__ctls > * { height: 44px; min-height: 44px; }
  .xp__row { height: 44px; font-size: 13.5px; }
  .xp__a { height: 32px; padding: 0 14px; font-size: 13px; }
  button.xp__a::after { inset: -6px 0; }
}
@media (max-width: 640px) {
  .stage[data-fil] .code__pre { padding: 14px 16px; font-size: 11.5px; }
}


/* ==================================================================
   Round 15 · Filament: own shape controls, and recorded mode.
   A Pro piece seen without Pro is a recording (dialog[data-rec]): the
   sliders step through frames, and the stage says so, quietly.
   ================================================================== */
/* which mode the stage is in: one small line at the foot of the piece */
.fl__mode {
  position: absolute; z-index: 2; left: 12px; bottom: 11px; display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 500; line-height: 1; letter-spacing: .01em; color: rgba(243, 238, 231, 0.5);
  pointer-events: none; -webkit-user-select: none; user-select: none;
}
.fl__mode::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
/* recorded: a ring, not a lit dot */
dialog.sheet[data-rec] .fl__mode::before { background: transparent; box-shadow: inset 0 0 0 1px currentColor; }
/* a recording does not follow a finger, so the stage scrolls like any other part of the view */
dialog.sheet[data-rec] .fl { touch-action: auto; }

/* a recorded slider shows its frames while it is held: 32 of them, edge to edge */
dialog.sheet[data-rec] .bar[data-key]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; pointer-events: none;
  background: repeating-linear-gradient(90deg, rgba(243, 238, 231, 0.3) 0, rgba(243, 238, 231, 0.3) 1px, transparent 1px, transparent calc(100% / 31));
  opacity: 0; transition: opacity .2s ease-out;
}
dialog.sheet[data-rec] .bar[data-key].is-drag::after, dialog.sheet[data-rec] .bar[data-key]:focus-visible::after { opacity: 1; }

/* the filler behind "The code comes with Pro": blocks, well out of focus */
dialog.sheet[data-rec] .stage[data-fil] .code[data-locked] .code__pre { filter: blur(9px); opacity: .34; }


/* ==================================================================
   Round 16 · Filament: demo mode (dialog[data-demo]).
   A Pro piece with a recorded demo plays as a video on the same stage,
   and its Shape sliders follow the recording. They are readouts: no
   slider cursor, no hover, nothing to hold.
   ================================================================== */
.fl canvas[hidden] { display: none; }
.fl__demo { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; background: #111; pointer-events: none; }
.fl__demo[hidden] { display: none; }

.bar--demo { cursor: default; touch-action: auto; }
.bar--demo::before, .bar--demo:hover::before { background: rgba(243, 238, 231, 0.1); transition: background-color .3s ease-out; }
.bar--demo .bar__name { transition: color .3s ease-out; }
/* the control the recording is moving right now: a little more light, and its name comes forward */
.bar--demo.is-live::before, .bar--demo.is-live:hover::before { background: rgba(243, 238, 231, 0.2); }
.bar--demo.is-live .bar__name { color: var(--ink); }

/* on a tile: this Pro piece has a demo to watch */
.tag--demo { gap: 5px; font-size: 11.5px; letter-spacing: .01em; color: rgba(243, 238, 231, 0.5); }
.tag--demo::before { content: ""; width: 5px; height: 5px; border-radius: 50%; box-shadow: inset 0 0 0 1px currentColor; }
.tile:hover .tag--demo { color: rgba(243, 238, 231, 0.75); }


/* ==================================================================
   Round 17 · Filament: three ways to say "this is a demo"
   A named cursor on the moving slider, and a nudge when one is pressed.
   ================================================================== */
dialog.sheet[data-fil] .side__body { position: relative; }

/* ---- a named cursor holds the slider that is moving ---- */
.dcur {
  --demo: #fdba74; /* the demo's own colour: a pastel Material orange, well clear of the deeper accent orange */
  position: absolute; z-index: 3; left: 0; top: 0; width: 0; height: 0; pointer-events: none;
  opacity: 0; transition: opacity .25s ease-out; will-change: transform;
}
.dcur.is-on { opacity: 1; }
.dcur svg {
  position: absolute; left: -3px; top: -3px; width: 22px; height: 22px; overflow: visible;
  fill: var(--demo); stroke: #111; stroke-width: 1.2; stroke-linejoin: round;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
  transform-origin: 2px 2px; transition: transform .18s ease-out;
}
.dcur.is-down svg { transform: scale(.86); }
.dcur b {
  position: absolute; left: 13px; top: 15px; padding: 4px 8px; border-radius: 4px 9px 9px 9px;
  font-size: 11px; font-weight: 600; line-height: 1; letter-spacing: .01em; white-space: nowrap;
  color: #2b1608; background: var(--demo); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}

/* ---- they look like sliders, and answer a press ---- */
.bar--try { cursor: ew-resize; }
.bar--try:hover::before { background: rgba(243, 238, 231, 0.15); }
.bar--try.is-nudge { animation: dnudge .36s cubic-bezier(.3, .7, .3, 1); }
@keyframes dnudge { 0% { transform: none; } 28% { transform: translateX(5px); } 62% { transform: translateX(-2px); } 100% { transform: none; } }
.dtip {
  position: absolute; z-index: 4; left: 0; right: 0; top: 0; padding: 9px 12px; border-radius: 10px;
  font-size: 12.5px; line-height: 1.35; color: var(--ink); background: #26221f;
  box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.14), 0 10px 24px -8px rgba(0, 0, 0, 0.8);
  opacity: 0; transform: translateY(-4px); pointer-events: none;
  transition: opacity .2s ease-out, transform .2s ease-out;
}
.dtip.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .dtip, .dcur svg { transition: none; } }

.dtip .pro { margin-left: 3px; vertical-align: 1px; }

/* ---- the gate, deeper: more behind it, and it fades out to nothing ---- */
.gate--fil { height: 250px; border-radius: 10px 10px 0 0; }
.gate--fil .gate__ctls {
  -webkit-mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.5) 38%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0, rgba(0, 0, 0, 0.5) 38%, transparent 100%);
}
.gate--fil .gate__msg { padding-bottom: 46px; }

/* ---- the Export tab: what a piece can be saved or copied as ---- */
.xpo[hidden], .code__box[hidden] { display: none; }
.xpo {
  width: min(400px, 100%); max-height: 100%; overflow: auto; overscroll-behavior: contain;
  display: grid; gap: 6px; scrollbar-width: none;
}
.xpo::-webkit-scrollbar { display: none; }
.xpo__label { padding: 0 2px 2px; font-size: 12px; line-height: 16px; color: var(--faint); }
.xpo__label--gap { margin-top: 16px; }
/* each export is its own row, with room around it */
.xpo__row {
  display: flex; align-items: center; gap: 12px; height: 44px; padding: 0 9px 0 14px; border-radius: 12px;
  background: var(--fill); font-size: 13.5px; line-height: 1; color: var(--ink);
}
.xpo__row { position: relative; overflow: hidden; transition: background-color .2s ease-out, transform .25s var(--ease); }
.xpo__row > * { position: relative; }
.xpo__glyph { flex: none; display: grid; width: 15px; height: 15px; }
.xpo__glyph svg { grid-area: 1 / 1; width: 15px; height: 15px; transition: opacity .18s ease-out, transform .25s var(--ease); }
.xpo__ic { color: var(--mute); }
.xpo__tick { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; opacity: 0; transform: scale(.6); }
/* taken: the row settles a touch, one band of light crosses it once, the button turns solid, the icon becomes a tick */
.xpo__row[data-taken] { background: rgba(243, 238, 231, 0.1); transform: translateX(2px); }
.xpo__row[data-taken] .xpo__ic { opacity: 0; transform: scale(.6); }
.xpo__row[data-taken] .xpo__tick { opacity: 1; transform: none; }
.xpo .xp__a { transition: background-color .15s ease-out, color .15s ease-out; }
.xpo .xp__a[data-done], .xpo button.xp__a[data-done]:hover { color: var(--bg); background: var(--ink); }
.xpo__row > .xpo__sweep {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(243, 238, 231, 0.14) 46%, rgba(255, 226, 200, 0.2) 52%, transparent 78%);
}
.xpo__row[data-taken] > .xpo__sweep { animation: xsweep .62s var(--ease) 1; }
@keyframes xsweep { from { opacity: 1; transform: translateX(-105%); } to { opacity: 1; transform: translateX(105%); } }
@media (prefers-reduced-motion: reduce) {
  .xpo__row[data-taken] { transform: none; }
  .xpo__row[data-taken] > .xpo__sweep { animation: none; }
}
.xpo__name { white-space: nowrap; }
.xpo__spec { margin-left: auto; font-size: 12.5px; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.xpo__row--sw .xpo__name { margin-right: auto; }
.xpo__row--sw { padding-right: 14px; }
.xpo .xp__a { min-width: 58px; }
.xp__a--lock { gap: 5px; }
.xp__a--lock svg { width: 12px; height: 12px; margin-left: -2px; }
.xpo[data-off] .xpo__name { color: var(--mute); }
.xpo[data-off] .xp__a { color: rgba(243, 238, 231, 0.42); background: transparent; box-shadow: inset 0 0 0 1px var(--line); cursor: default; }
/* the switch */
.xpo__sw {
  position: relative; flex: none; width: 34px; height: 20px; border-radius: 999px; background: rgba(243, 238, 231, 0.14);
  transition: background-color .2s ease-out;
}
.xpo__sw::after { content: ""; position: absolute; inset: -12px -6px; }
.xpo__sw i { position: absolute; left: 3px; top: 3px; width: 14px; height: 14px; border-radius: 50%; background: var(--mute); transition: transform .2s var(--ease), background-color .2s ease-out; }
.xpo__sw[aria-checked="true"] { background: var(--ink); }
.xpo__sw[aria-checked="true"] i { transform: translateX(14px); background: var(--bg); }
.xpo__sw:disabled { opacity: .45; cursor: default; }

@media (max-width: 900px) {
  .gate--fil { height: 290px; }
  .xpo__row { height: 48px; }
}
@media (max-width: 640px) {
  .xpo { width: 100%; }
}


/* ==================================================================
   Round 19 · Shaders: the real pieces, in the Filament viewer.
   A shader opens in the same viewer as a Filament piece: the dialog
   carries data-mode="mat" and data-fil (so the size, the bars, the
   switch, the slim panel and the gate are the rules above, untouched)
   and data-sh, which says what is on the stage: "live", "demo" or
   "still". Only what is the shader's own is here.
   ================================================================== */
/* one square, as Filament's; it only gives way to a stage too small to hold it */
.stage[data-sh] { --fl: 420px; }
.sh {
  position: relative; overflow: hidden; isolation: isolate;
  width: min(var(--fl), 100cqw, 100cqh); aspect-ratio: 1;
  border-radius: 14px; background: #111; /* the preset's own ground is set on the box when it opens */
  box-shadow: 0 0 0 1px var(--line), 0 30px 60px -28px rgba(0, 0, 0, 0.95);
  touch-action: pan-y; /* a finger moves the piece sideways and still scrolls the view */
}
/* three layers: the still, the live drawing over it, a recorded demo over that */
.sh__still, .sh__host, .sh__demo { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.sh__still, .sh__demo { object-fit: cover; pointer-events: none; }
.sh__host { z-index: 1; opacity: 0; transition: opacity .35s ease-out; } /* the engine puts its canvas in here */
.sh__host.is-on { opacity: 1; }
.sh__host canvas { display: block; width: 100%; height: 100%; }
.sh__demo { z-index: 2; }
/* which mode the stage is in. Some presets are pale, so the line carries a ground of its own. */
.sh__mode {
  position: absolute; z-index: 3; left: 10px; bottom: 10px; display: inline-flex; align-items: center; gap: 6px;
  height: 22px; padding: 0 9px 0 8px; border-radius: 999px; background: rgba(12, 10, 9, 0.5);
  font-size: 11.5px; font-weight: 500; line-height: 1; letter-spacing: .01em; color: rgba(243, 238, 231, 0.78);
  pointer-events: none; -webkit-user-select: none; user-select: none;
}
.sh__mode::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
/* a demo: a ring, not a lit dot */
dialog.sheet[data-sh="demo"] .sh__mode::before { background: transparent; box-shadow: inset 0 0 0 1px currentColor; }
/* a recording or a still does not follow a finger, so the stage scrolls like the rest of the view */
dialog.sheet[data-sh="demo"] .sh, dialog.sheet[data-sh="still"] .sh { touch-action: auto; }

/* Hue as a readout (a demo, or a Pro piece's still): the spectrum stays, a touch brighter while the recording moves it */
dialog.sheet[data-sh] .bar--hue.bar--demo::before,
dialog.sheet[data-sh] .bar--hue.bar--demo:hover::before { background: var(--spec); }
dialog.sheet[data-sh] .bar--hue.bar--demo.is-live::before { opacity: .34; }

/* A live shader has more controls than the panel is tall, so its one button stays in reach while they scroll */
@media (min-width: 901px) {
  dialog.sheet[data-sh="live"] .side__actions {
    position: sticky; bottom: 0; z-index: 5; margin-bottom: -22px; padding-bottom: 22px; /* the panel's own padding */
    background: linear-gradient(to bottom, rgba(20, 17, 16, 0) 0, var(--raise) 20px);
  }
}
@media (prefers-reduced-motion: reduce) { .sh__host { transition: none; } }

/* A Pro shader with two shape dials shows five basic sliders; the gate gives up a little height so the button stays whole */
@media (min-width: 901px) {
  dialog.sheet[data-sh="demo"] .gate--fil, dialog.sheet[data-sh="still"] .gate--fil { height: 210px; }
}

/* ==================================================================
   Round 19b · the shader panel: the name and the close button stay put,
   the controls scroll away under them, and the button has air above it.
   ================================================================== */
@media (min-width: 901px) {
  /* the scrollbar ran outside the panel's round corner: the fades say there is more instead */
  dialog.sheet[data-sh] .side { scrollbar-width: none; }
  dialog.sheet[data-sh] .side::-webkit-scrollbar { display: none; }
  dialog.sheet[data-sh] .side__name {
    position: sticky; top: 0; z-index: 4; flex: none;
    margin: -22px -22px 0; padding: 22px 62px 18px 22px; min-height: 0;
    background: linear-gradient(to bottom, var(--raise) 0, var(--raise) calc(100% - 16px), rgba(20, 17, 16, 0) 100%);
  }
  dialog.sheet[data-sh] .side__body { margin-top: 2px; }
  dialog.sheet[data-sh="live"] .side__body { padding-bottom: 14px; }
  dialog.sheet[data-sh="live"] .side__actions {
    padding-top: 30px;
    background: linear-gradient(to bottom, rgba(20, 17, 16, 0) 0, var(--raise) 26px);
  }
}

/* ==================================================================
   Round 19b · Export rows are solid dark (Dan chose it over a light version),
   so sizes and labels read on any stage colour.
   ================================================================== */
.xpo__label { color: rgba(243, 238, 231, 0.78); text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35); }
/* A · solid dark */
.xpo__row { background: #1b1816; box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.07), 0 6px 18px -10px rgba(0, 0, 0, 0.5); }
.xpo__row[data-taken] { background: #26221f; }
.xpo__spec { color: rgba(243, 238, 231, 0.74); }
.xpo__ic { color: rgba(243, 238, 231, 0.8); }
.xpo .xp__a:not([data-done]):not(.xp__a--lock) { background: rgba(243, 238, 231, 0.12); color: var(--ink); }
.xpo button.xp__a:not([data-done]):hover { background: rgba(243, 238, 231, 0.2); }
.xpo[data-off] .xp__a { color: rgba(243, 238, 231, 0.6); }
/* the panel's own padding sits outside what "sticky" measures from, so each bar paints that strip itself */
@media (min-width: 901px) {
  dialog.sheet[data-sh] .side__name { margin: 0; padding: 0 40px 18px 0; box-shadow: 0 -22px 0 var(--raise); }
  dialog.sheet[data-sh="live"] .side__actions { margin-bottom: 0; padding-bottom: 0; box-shadow: 0 22px 0 var(--raise); }
}

/* a taken row slides 2px to the right: the list keeps a little room each side so nothing clips it */
.xpo { box-sizing: border-box; width: min(412px, 100%); padding: 0 6px 14px; }

/* ==================================================================
   Round 20 · a particle preset in the shader viewer. The stage carries
   data-pt beside data-sh. Only what differs from a shader is here: the
   picture is 3 by 2 (as the pack's stills are), and the panel has a row
   of colour swatches and a background colour.
   ================================================================== */
.stage[data-sh][data-pt] { --fl: 600px; }
.sh[data-pt] { aspect-ratio: 3 / 2; width: min(var(--fl), 100cqw, calc(100cqh * 1.5)); touch-action: pan-y; }
/* the engine's canvas takes the pointer (a drag turns the piece); a still or a recording does not */
.sh[data-pt] .sh__host canvas { position: absolute; inset: 0; }

.ptrow { display: grid; gap: 9px; padding: 6px 0 4px; }
.ptrow .row__name { color: var(--mute); }
.ptsw { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 6px; border-radius: 10px; }
.ptsw > * {
  display: block; width: 100%; aspect-ratio: 1; padding: 0; border: 0; border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.16); transition: box-shadow .2s ease-out, transform .2s ease-out;
}
.ptsw > button { cursor: pointer; }
.ptsw > button:hover { transform: scale(1.08); }
.ptsw > button[aria-pressed="true"], .ptsw > i.on { box-shadow: 0 0 0 1.5px var(--raise, #141110), 0 0 0 3px rgba(243, 238, 231, 0.9); }
.ptsw > button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* a Pro piece's swatches: there to see, and a press is answered with the nudge */
.ptsw--try { cursor: pointer; }
.ptsw--try.is-nudge { animation: dnudge .36s cubic-bezier(.3, .7, .3, 1); }
.ptsw[data-demo] > i { opacity: .8; }
.ptsw[data-demo].is-live > i, .ptsw[data-demo] > i.on { opacity: 1; }
.ptbg { position: relative; display: inline-flex; width: 44px; height: 26px; }
.ptbg i { display: block; width: 100%; height: 100%; border-radius: 8px; box-shadow: inset 0 0 0 1px rgba(243, 238, 231, 0.22); }
.ptbg input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
.ptbg:focus-within i { outline: 2px solid var(--ink); outline-offset: 2px; }
/* on a narrow screen the swatches are finger sized */
@media (max-width: 900px) {
  .ptsw { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 10px; }
  .ptbg { height: 36px; width: 56px; }
}
@media (prefers-reduced-motion: reduce) { .ptsw > * { transition: none; } }
/* Round 20 · the Colours label sits at the size of the slider names; on a wide panel the swatches keep to a finger's size */
@media (min-width: 901px) { .ptrow .row__name { font-size: 13px; line-height: 16px; } }
@media (max-width: 900px) { .ptsw { grid-template-columns: repeat(auto-fill, minmax(34px, 40px)); justify-content: start; } }

/* Round 20 · a Particles video that really loops carries a small "Loop" mark; a tile can hold it beside "Free" */
.tile__meta .tags { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.tag--loop { gap: 5px; padding: 0 9px 0 7px; border-radius: 999px; background: rgba(12, 10, 9, 0.6); color: var(--ink); }
.tag--loop::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid currentColor; border-right-color: transparent; transform: rotate(-45deg); opacity: .85;
}
.side__mark .tag--loop { background: var(--fill); font-family: var(--sans); letter-spacing: 0; }

/* ==================================================================
   Round 20 · who sees what size (Danny, 2026-10-11).
   A member sees the preview at the size of the viewer, on Materials and
   on Particles stills and videos; everyone else keeps the small preview.
   On the Particles grid a video is a double tile only for a member:
   for a visitor or someone signed in it is the size of a still.
   ================================================================== */
.stage[data-mat][data-full] .pv__frame { width: min(100cqw, calc(100cqh * var(--ar))); }
html:not([data-as="member"]) .page-particles .grid .tile--wide { grid-column: auto; grid-row: auto; }

/* Round 20 · a Particles still or video seen without Pro: bigger than a locked material's preview, because these
   are dark, fine pictures that cannot be read at 260px (Danny, 2026-10-11). Still short of a member's, which fills the viewer. */
.stage[data-mat][data-ptm], .stage[data-mat][data-ptm][data-small] { --pv: 460px; }
.stage[data-mat][data-ptm][data-small] .stage__ambient { opacity: .3; }
@media (max-width: 900px) { .stage[data-mat][data-ptm][data-small] { --pv: 460px; } }

/* ==================================================================
   Round 37 · Export in two groups, Code then Image (Dan, 2026-10-22).
   Each heading carries a small icon; the transparent ground is a plain
   line under Image, not a row; a locked button shakes and says why.
   ================================================================== */
.xpo { position: relative; }
.xpo__label--ic { display: flex; align-items: center; gap: 7px; }
.xpo__lic { flex: none; width: 13px; height: 13px; color: rgba(243, 238, 231, 0.78); }
.xpo__opt {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 2px 14px 6px 2px; font-size: 12.5px; line-height: 16px; color: rgba(243, 238, 231, 0.78);
}
.xpo__sw[aria-disabled="true"] { opacity: .45; cursor: default; }
.xpo button.xp__a.xp__a--lock { cursor: default; }
.xpo button.xp__a.xp__a--lock:hover { background: transparent; }
.xpo .xp__a.is-nudge, .xpo .xpo__sw.is-nudge { animation: dnudge .36s cubic-bezier(.3, .7, .3, 1); }
.xtip { left: 6px; right: 6px; }

/* ---------- opened from Your vault (Dan, 2026-10-25): the collection page loads inside the vault page,
   and only its viewer shows, laid over the vault. The vault page draws the dim behind it (saves.js). ---------- */
html.is-embed, html.is-embed body { background: transparent !important; }
html.is-embed body > *:not(dialog):not(svg) { display: none !important; }
html.is-embed dialog.sheet { background: transparent; }
html.is-embed [data-act="prev"], html.is-embed [data-act="next"], html.is-embed dialog.sheet [data-pos] { visibility: hidden; }

/* ==================================================================
   Dan, 2026-10-26 · big: a code piece's viewer fills the window.
   The button sits at the right end of the stage's top bar. Pressed, the
   preview grows to take the window and the panel stays its own width,
   pinned to the right, the same gap between them. Desktop only: below
   901px the viewer already stacks and fills the width.
   ================================================================== */
.stage__big[hidden] { display: none; }
.stage__big .stage__big-shut { display: none; }
.stage__big[aria-pressed="true"] .stage__big-open { display: none; }
.stage__big[aria-pressed="true"] .stage__big-shut { display: inline; }
@media (min-width: 901px) {
  dialog.sheet[data-mode="mat"] .sheet__panel { transition: width .42s var(--ease), height .42s var(--ease); }
  dialog.sheet[data-big] { padding: 16px; }
  dialog.sheet[data-mode="mat"][data-big] .sheet__panel { width: 100%; height: 100%; }
  /* the square (or a particle preset's 3:2) takes all the room the stage has */
  dialog.sheet[data-big] .stage[data-fil], dialog.sheet[data-big] .stage[data-sh] { --fl: 100vmax; }
  dialog.sheet[data-big] .stage[data-fil] .code__box { width: min(880px, 100%); }
}
@media (max-width: 900px) {
  .stage__big { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  dialog.sheet[data-mode="mat"] .sheet__panel { transition: none; }
}

/* ---------- homepage headline: the pearl foil on "animated" ----------
   A washed-out version of the logo's foil: silver with faint pink, lilac
   and mint, with light and dark bands drifting through continuously. Every
   6s a white glint crosses the word over about 3s, and each letter softens
   slightly as the glint passes it.
   Built per letter (split into .foil__c letters, positioned by a small script after the h1): each letter paints
   its slice of one shared gradient (offset by --x, its position in em), and
   its blur is timed by --d to when the glint reaches it. The blur sits on the
   letter's wrapper: a filter on the gradient-filled text itself paints nothing.
   Motion off: the global reduced-motion rule leaves a sharp still frame.
   The letters are drawn from data-ch by ::before, not written as text: the
   real word sits once in the .sr span, so Google and copy-paste read
   "animated" once instead of "animatedanimated" (2026-11-06). */
.foil { display: inline-block; }
.foil__l { display: inline-block; }
.foil__c { display: inline-block; animation: foil-soft 6s ease-in-out infinite; animation-delay: var(--d, 0s); }
.foil__ct::before { content: attr(data-ch); }
.foil__ct {
  --x: calc(var(--xn, 0) * 1em);
  display: inline-block;
  color: transparent;
  background-image:
    linear-gradient(100deg, rgba(255,255,255,0) 0%, #fff 50%, rgba(255,255,255,0) 100%),
    linear-gradient(100deg, #959ca7 0%, #f4f5f7 11%, #eec2d5 21%, #fbf2e0 31%, #c5e4da 41%, #d4c7f1 51%, #ffffff 59%, #8c94a0 71%, #eec2d5 83%, #f4f5f7 92%, #959ca7 100%);
  background-size: 1.6em 100%, 6em 100%;
  background-repeat: no-repeat, repeat-x;
  background-position: calc(-2em - var(--x)) 0, calc(1.4em - var(--x)) 0;
  -webkit-background-clip: text;
  background-clip: text;
  animation: foil 6s linear infinite;
}
/* glint: across the word in the first 45% (~2.7s); colour band: one steady drift all the way round */
@keyframes foil {
  0%   { background-position: calc(-2em - var(--x)) 0, calc(0em - var(--x)) 0; }
  45%  { background-position: calc(6em - var(--x)) 0, calc(2.7em - var(--x)) 0; }
  100% { background-position: calc(6em - var(--x)) 0, calc(6em - var(--x)) 0; }
}
@keyframes foil-soft {
  0%, 11%, 100% { filter: blur(0); }
  5.5% { filter: blur(1.3px); }
}


/* ==================================================================
   Round 44 (2026-11-06, Dan's mobile review): code pieces on a phone.
   A piece with live controls keeps its preview pinned at the top and only
   the controls scroll beneath it, so a change is seen as it is made. On the
   Export tab the controls step aside and the export list gets the room.
   A soft fade marks any list that runs on past its box.
   ================================================================== */
@media (max-width: 900px) {
  /* the stage never scrolls itself: a tap on Export used to slide its contents sideways */
  .stage { overflow: clip; }
}
@media (max-width: 900px) and (min-height: 560px) {
  dialog.sheet:has(.side__body .bar) { overflow: hidden; grid-template-rows: minmax(0, 1fr); }
  dialog.sheet:has(.side__body .bar) .sheet__panel { height: 100%; grid-template-rows: auto minmax(0, 1fr); gap: 8px; }
  dialog.sheet:has(.side__body .bar) .stage { height: clamp(300px, 50dvh, 460px); }
  dialog.sheet:has(.side__body .bar) .side { overflow: hidden; padding: 0; }
  dialog.sheet:has(.side__body .bar) .side__body {
    flex: 1 1 auto; min-height: 0; margin-top: 0; padding: 22px 22px 16px;
    overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
  }
  dialog.sheet:has(.side__body .bar) .side__body::-webkit-scrollbar { display: none; }
  dialog.sheet:has(.side__body .bar) .side__actions { flex: none; margin-top: 0; padding: 4px 22px 22px; }
  /* Export: the list takes the stage's full height; the controls wait until Preview comes back */
  dialog.sheet:has(.side__body .bar):has(.stage[data-view="code"]) .sheet__panel { grid-template-rows: minmax(0, 1fr) auto; }
  dialog.sheet:has(.side__body .bar):has(.stage[data-view="code"]) .stage { height: auto; }
  dialog.sheet:has(.side__body .bar):has(.stage[data-view="code"]) .side__body { display: none; }
  dialog.sheet:has(.side__body .bar):has(.stage[data-view="code"]) .side__actions { padding-top: 18px; }
}
/* more to scroll to: the far edge fades (set by v2.js, round 44) */
.side__body[data-more], .xpo[data-more] {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 56px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 56px), transparent);
}
.side__body[data-less], .xpo[data-less] {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 28px);
  mask-image: linear-gradient(to bottom, transparent, #000 28px);
}
.side__body[data-more][data-less], .xpo[data-more][data-less] {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 28px, #000 calc(100% - 56px), transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 28px, #000 calc(100% - 56px), transparent);
}
