/* il-reveal.css — the landing page's scroll layer. Paired with il-reveal.js;
   read that file's header for the why. Tokens only, both themes, all moods. */

/* ── Reveal states ────────────────────────────────────────────────────────
   Hidden ONLY under .rv-armed, which il-reveal.js adds after the observer has
   actually called back. Without it every element is simply visible. */
.rv-armed .landing [data-rv]:not(.rv-in) { opacity: 0; }
.rv-armed .landing [data-rv="text"]:not(.rv-in) { transform: translateY(16px); filter: blur(4px); }
.landing [data-rv="text"] {
  transition: opacity .75s cubic-bezier(.2, .8, .25, 1), transform .75s cubic-bezier(.2, .8, .25, 1),
    filter .75s ease;
}
.landing [data-rv="text"].rv-in { transition-delay: 0s; }

/* The terminal's held cards (il-reveal.js section 5), under the same rule. */
.tv-armed .view.active [data-tv] { opacity: 0; }

/* Print, and anything else that is not a screen, gets everything at once. */
@media print {
  .landing [data-rv], [data-tv] { opacity: 1 !important; transform: none !important; filter: none !important; }
}

/* ── Progress ─────────────────────────────────────────────────────────── */
.lprog {
  position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 60;
  transform-origin: 0 50%; transform: scaleX(0); pointer-events: none;
  background: linear-gradient(90deg, var(--brand-deep), var(--brand));
  box-shadow: 0 0 10px color-mix(in srgb, var(--brand) 60%, transparent);
}
:root[data-stage="app"] .lprog, :root[data-stage="app"] .lrail { display: none; }

/* ── The header stays ────────────────────────────────────────────────────
   Sticky rather than fixed, so it keeps its place in the flow and nothing
   under it jumps. The backing is full-bleed through a spread shadow, clipped
   to the header's own height, because the header itself is shell-width. */
.lheader {
  position: sticky; top: 0; z-index: 50;
  transition: background-color .3s ease, box-shadow .3s ease, padding .3s ease;
}
/* Solid, not 94%. Six percent of a headline is still a headline: on a phone,
   where the page's text runs straight under a two-row header, "10% while the
   market did 20%..." read as grey type inside the header, behind the wordmark
   and the button. The terminal's phone chrome went to a solid --plane for the
   same reason (round 5); the scene loses nothing, since at 94% it barely
   showed through here either. */
.lheader.is-stuck {
  padding-top: 10px; padding-bottom: 10px;
  background-color: var(--plane);
  box-shadow: 0 0 0 100vmax var(--plane),
    0 1px 0 100vmax color-mix(in srgb, var(--border) 70%, transparent);
  clip-path: inset(0 -100vmax -1px);
}
.lheader.is-stuck .brandtext > span:last-child { display: none; }
/* On a phone the wordmark and the button do not fit one row, so the stuck
   header was two rows -- 109px of an 860px screen for as long as you read.
   Stuck, the family mark alone stands for the name (it is the brand, and the
   top of the page has just shown the words beside it), and the header is one
   row. */
@media (max-width: 480px) {
  .lheader.is-stuck .lbrand .brandtext { display: none; }
}

/* ── The rail ─────────────────────────────────────────────────────────────
   Ticks on the right edge, one per section; the current one is long and
   labelled, the rest say their name on hover. Hidden until the hero is
   gone -- on the first screen the page has not got a shape yet. */
.lrail {
  position: fixed; right: 18px; top: 50%; z-index: 40;
  transform: translate(8px, -50%); opacity: 0; pointer-events: none;
  display: flex; flex-direction: column; align-items: flex-end; gap: 4px;
  transition: opacity .35s ease, transform .35s ease;
}
.lrail.on { opacity: 1; transform: translate(0, -50%); pointer-events: auto; }
.lrail-i {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  min-height: 22px; padding: 0 2px 0 10px; text-decoration: none;
}
.lrail-t {
  font-family: var(--font-mono, "IBM Plex Mono", monospace); font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--text-secondary); white-space: nowrap;
  opacity: 0; transform: translateX(6px);
  transition: opacity .2s ease, transform .2s ease, color .2s ease;
  padding: 3px 8px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--plane) 80%, transparent);
}
.lrail-d {
  width: 12px; height: 2px; border-radius: 2px; flex: none;
  background: color-mix(in srgb, var(--text-muted) 70%, transparent);
  transition: width .3s cubic-bezier(.2, .9, .25, 1), background .3s ease, box-shadow .3s ease;
}
.lrail-i:hover .lrail-t, .lrail-i:focus-visible .lrail-t { opacity: 1; transform: none; }
.lrail-i:hover .lrail-d { width: 18px; background: var(--text-secondary); }
/* The current section is said by the tick, not the label: at 1440px the
   margin is 60px, and a label that stays up sits on the content beside it. */
.lrail-i.on .lrail-t { color: var(--brand); }
.lrail-i.on .lrail-d {
  width: 26px; background: var(--brand);
  box-shadow: 0 0 10px color-mix(in srgb, var(--brand) 55%, transparent);
}
.lrail-i:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }
/* It needs a margin to live in. Below this the page's own gutter is the
   content's, and a rail over the text is worse than no rail. */
@media (max-width: 1340px) { .lrail { display: none; } }

/* ── Grids that divide their count ─────────────────────────────────────────
   auto-fit breaks a row wherever the width runs out, so six "why" cards went
   4 + 2 at 1440px, six stats 4 + 2 and eight classes 5 + 3 -- the same stray
   the four feature cards had. Each grid now takes a column count that divides
   what it holds, stepping down at the widths where a column gets too narrow. */
.lterms, .lstatgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.lclasses { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1060px) { .lclasses { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px) { .lterms, .lstatgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) {
  .lterms, .lstatgrid, .lclasses { grid-template-columns: minmax(0, 1fr); }
}

/* ── Depth ────────────────────────────────────────────────────────────────
   --l-par drives the room's push-in, which lives in il-scenes.css. The rule
   that used to sit here targeted `.motif:not(.market-floor)` -- and the market
   floor is the only `.motif` there is, so it matched nothing, ever. */

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion-choice="user"]) .landing [data-rv="text"] { transition: none; }
  :root:not([data-motion-choice="user"]) .lheader, :root:not([data-motion-choice="user"]) .lrail,
  :root:not([data-motion-choice="user"]) .lrail * { transition: none; }
}

/* The header sticks now, so anything scrolled to -- a rail click, a
   #lmodel link, the browser's own find -- lands with its heading under it
   rather than behind it. */
.landing .lsection, .landing .lplay, .landing .lcta { scroll-margin-top: 84px; }
