html, body { overflow-x: clip; }  /* the page never pans sideways; wide art clips */
/* ================================================================
   sleeve.css — the shared constitution of musicality.app.
   ONE living gradient behind everything, ONE veil over it, and
   nothing else has a background. Every visible mark is a hole in
   the veil (CUT), a capsule outline (RING), or a sampled solid
   (SAMPLE). No element declares a color. The page's base color is
   a sentinel that must never render; the leak scan proves zero.
   Proven in experiments/ — 52/52 shots zero-leak, chromium+webkit.
   Do not edit constants without re-running the contrast sweep.
   ================================================================ */

:root {
  /* gradients are emitted by JS as multi-stop strings lerped in
     oklab — engine-proof: no dependency on `in oklab` support */
  --g: linear-gradient(#0034FE, #FE2400);
  --g-disp: var(--g-disp-l);
  --g-read: var(--g-read-l);
  /* THE SENTINEL: rides inheritance only; must never render */
  --sentinel: #FF00FF;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  font-weight: 300;
  color: var(--sentinel);
  -webkit-font-smoothing: antialiased;
}

/* the world: sky, then one veil, then everything else */
#gradient { position: fixed; inset: 0; z-index: -2; background: var(--g); }
#veil {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: #fff; opacity: 0.8;
  transition: background 0.7s ease, opacity 0.7s ease;
}

/* the veil's three named states — the whole vocabulary, nothing ad-hoc.
   THE TEXT RULE: text is the sky's hue wearing GUARANTEED luminance.
   Reading clamps oklab L ≤ 0.40 light / ≥ 0.82 cinema (worst 5.89:1);
   display clamps L ≤ 0.55 / ≥ 0.72 (worst 3.69:1). Only art wears --g raw. */
body[data-state="rest"]   { --g-disp: var(--g-disp-l); --g-read: var(--g-read-l); }
body[data-state="rest"]   #veil { background: #fff; opacity: 0.80; }
body[data-state="deep"]   { --g-disp: var(--g-disp-l); --g-read: var(--g-read-l); }
body[data-state="deep"]   #veil { background: #fff; opacity: 0.90; }
body[data-state="cinema"] { --g-disp: var(--g-disp-d); --g-read: var(--g-read-d); }
body[data-state="cinema"] #veil { background: #000; opacity: 0.78; }

/* ---- CUT: background-clip / mask holes ---- */
.cut-disp, .cut-read {
  color: transparent;
  -webkit-background-clip: text;
  background-clip: text;
  background-repeat: no-repeat;
  background-size: var(--fill-size); background-position: var(--fill-pos);
}
.cut-disp { background-image: var(--g-disp); }
.cut-read { background-image: var(--g-read); }

/* a divider is a thin hole */
hr.fill-hole {
  border: none; height: 2px;
  background-image: var(--g-read); background-repeat: no-repeat;
  background-size: var(--fill-size); background-position: var(--fill-pos);
}

/* an icon is a shaped hole: the sky masked by the glyph */
.icon-cut {
  width: 44px; height: 44px; display: inline-block;
  background-image: var(--g-disp); background-repeat: no-repeat;
  background-size: var(--fill-size); background-position: var(--fill-pos);
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* the blockquote's bar is a hole too — the host carries .stencil-fill
   so the pseudo-element inherits the fill variables */
blockquote { position: relative; padding-left: 1.2rem; margin: 1.2rem 0; }
blockquote::before {
  content: ""; position: absolute; left: 0; top: 0.2em; bottom: 0.2em; width: 3px;
  background-image: var(--g-read); background-repeat: no-repeat;
  background-size: var(--fill-size); background-position: var(--fill-pos);
  border-radius: 2px;
}

/* ---- RING: mask-composite capsule outlines ---- */
.pill {
  position: relative; display: inline-block;
  padding: 0.75em 2.1em; border: none; border-radius: 999px;
  text-decoration: none; font-size: 0.95rem;
  background: transparent; cursor: pointer; font-family: inherit;
}
.pill::before, .ring::before {
  content: ""; position: absolute; inset: 0;
  border-radius: 999px; padding: 1.5px;
  background-image: var(--g-read); background-repeat: no-repeat;
  background-size: var(--fill-size); background-position: var(--fill-pos);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
}
.ring { position: relative; display: inline-block; border-radius: 999px; }

/* ---- SAMPLE: JS solids from the clamped sky (surfaces the clip
   cannot reach) — the sentinel fallback is the sanctioned tripwire */
input.sample-text {
  border: none; outline-offset: 3px; background: transparent;
  font: inherit; color: var(--sample-solid, var(--sentinel));
  caret-color: var(--sample-solid, var(--sentinel));
}
input.sample-text::placeholder { color: var(--sample-solid, var(--sentinel)); opacity: 0.55; }

li.sample-marker,
li.sample-marker::marker { color: var(--sample-solid, var(--sentinel)); }

a.sample-deco { text-decoration: underline; text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  text-decoration-color: var(--sample-solid, var(--sentinel)); }

/* ================================================================
   MOTION — the five movements (MOTION.md). Every moving mark is
   still CUT, RING, or SAMPLE; every new stroke rides the sentinel
   fallback. Motion always means something; the still forms below
   keep the meaning when the motion is taken away.
   ================================================================ */

/* ---- §1 THE PULSE: the cosmogony's first element ----
   A CUT-hole dot swelling on CH1's 1.2s beat (smoothstep twin),
   emitting SAMPLE-stroked ripples at the grid's wake speed.
   Pure keyframes: zero JS per frame, compositor only. */


/* ---- §3 THE RINGS: real progress, nothing fake ----
   The playing row's mark grows the app's ring: bare-circle possibility
   underneath, 1.5px round-cap arc from twelve o'clock, driven by the
   audio clock. Only the appearance eases — 0.514s, the ring's breath. */
.ring-base {
  fill: none; stroke: var(--sample-solid, var(--sentinel));
  stroke-width: 1; opacity: 0.25;          /* a possibility, not a score */
}
.ring-arc {
  fill: none; stroke: var(--sample-solid, var(--sentinel));
  stroke-width: 1.5; stroke-linecap: round;
  transform: rotate(-90deg);               /* twelve o'clock, clockwise */
  transform-box: fill-box; transform-origin: center;
}

/* ---- §5 ARRIVALS: sections enter the way the board grows ----
   The hidden state is applied by JS only when motion is allowed —
   no-JS and reduced-motion never hide content. */
.pre-arrival { opacity: 0; translate: 0 16px; }
.pre-arrival.arrived {
  opacity: 1; translate: 0 0;
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1) var(--arr, 0ms),
              translate 0.6s cubic-bezier(0.22, 1, 0.36, 1) var(--arr, 0ms);
}

/* ---- stillness ----
   .still is stamped by the page when reduced motion OR ?t= holds
   (frozen implies still); the media query is the no-JS guarantee.
   The ring is absent: progress is information — it still fills,
   stepwise, and appears without its fade. */
@media (prefers-reduced-motion: reduce) {
  #veil { transition: none; }
  .pre-arrival { opacity: 1; translate: none; }
}
:root.still .pre-arrival { opacity: 1; translate: none; }

/* the atmosphere: two fixed canvases above the veil (later in the DOM,
   same z), each alive only while its phase is */
#gridfield, #smoke { position: fixed; inset: 0; z-index: -1; pointer-events: none;
                     width: 100%; height: 100%; }  /* never 100vw: it includes the scrollbar */
.ratio-stage { max-width: 34em; margin: 2.6rem auto 0; }
.ratio-stage canvas { display: block; width: 100%; height: 130px; }
.ratio-name { font-size: 0.85rem; letter-spacing: 0.05em; margin-top: 0.7rem; min-height: 1.2rem; }

/* the clearing: reading text in the smoke gets a breath of dark behind
   it — the same dignity the app gives its pills over a busy board */
#liner .inner { position: relative; }
#liner .inner::before { content: ""; position: absolute; inset: -2.5rem -3rem; z-index: -1;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.45), transparent 72%); }

/* ---- typographic law: no orphans, balanced display lines ---- */
h1, h2, h3, .display { text-wrap: balance; }
p, .reading, .lede { text-wrap: pretty; }

/* ---- the board, as it truly is: horizontal strings stacked on top of
   each other — the pitch axis IS the page's vertical, so each string
   shows its own pitch's sky. Still by nature; the app's board does
   not wave. ---- */
.board { margin-top: 2.6rem; }
.board-stack { display: flex; flex-direction: column-reverse; align-items: center;
               gap: clamp(9px, 1.6vh, 14px); }
.board .bar { width: min(340px, 62%); height: clamp(9px, 1.3vh, 12px); border-radius: 999px;
              background-image: var(--g); background-repeat: no-repeat;
              background-size: var(--fill-size); background-position: var(--fill-pos);
              transition: transform 0.45s cubic-bezier(0.34, 1.3, 0.64, 1); }
.board .bar.selected { transform: scale(1.1); }

/* ---- the rings: the overview drawn on one canvas (round caps, inner
   disc — the app's own strokes); labels are holes above it ---- */
.rings { position: relative; margin: 2.6rem auto 0; }
.rings canvas { display: block; margin: 0 auto; }
.ring-label { position: absolute; display: grid; place-items: center;
              font-size: calc(0.6rem * var(--ring-scale, 1));
              line-height: 1.25; letter-spacing: 0.01em; text-align: center; }
.rings-caption { font-size: 0.78rem; margin-top: 1.1rem; }

/* ---- the back cover: a printed tracklist, the music stays inside ---- */
.tracks-print { list-style: none; columns: 2; column-gap: 3rem; max-width: 26rem;
                margin: 1.2rem auto 0; text-align: left; font-size: 0.9rem; line-height: 2; }
@media (max-width: 480px) { .tracks-print { columns: 1; text-align: center; } }

/* the note link under the doubt: a door, not a headline */
.note-link { font-size: 0.9rem; margin-top: 1.6rem; }

/* the doubt's hierarchy: claim, quiet door, the ratio as interlude,
   then the big door — each layer with its own air */
#doubt .reading { margin-top: 2.4rem; }
#doubt .note-link { margin-top: 1.9rem; }
#doubt .ratio-stage { margin-top: 4.2rem; }
#doubt .badge { margin-top: 3.4rem; }
