/* ---- the run-through's threshold ----
   The chapter's simulator sits behind a door. Closed, this section is the
   splash: who takes part, what the eight events are, and one key. Pressed,
   the splash lifts away and the deck rises in its place.

   Tokens are pinned rather than inherited, the same way .run-learnings pins
   the deck's skin, so the splash renders identically to the standalone
   design at docs/RUN_THROUGH_SPLASH_FINAL.html no matter what the page
   around it resolves. */
.runbay {
  --canvas: #f5eee8; --surface: #fffdf9;
  --ink: #292522; --ink-soft: #433c37; --muted: #6f655e; --faint: #938880;
  --line: #e5ded8; --line-strong: #d3c9c1; --wire: #8d8177;
  --omm-green: #04e26d;
  --dt-emission: #00e5ff; --dt-emission-ink: #006f7b; --dt-rgb: 0, 229, 255;
  --app-ink: #0a6b39; --crt-ink: #0a7a86;
  --rt-accent: #5a4bd6; --rt-emboss: #ccbbaf; --rt-stone: #786a5e;
  --rt-on-accent: #fff9f2;
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --serif: 'Newsreader', Georgia, serif;
  --mono: 'JetBrains Mono', ui-monospace, monospace;

  position: relative;
  /* The homepage DemoThemis product card's edge, all three layers of it, from
     assets/v/ac641bc775b2/styles.css: the 20px radius off .product-pair .product, then the
     1px cyan hairline at 34% with its 1px ring at 8% and 4px halo, then the
     orbiting rim below. It frames the splash, and only the splash: once the
     door is open the deck carries the animated edge itself, on its event nav,
     with a line running from it into the app screen. So the edge fades out as
     the door finishes opening -- colour and opacity only, never border-width,
     so the box keeps its size and the deck cannot shift under it. */
  border-radius: 20px;
  border: 1px solid rgba(var(--dt-rgb), .34);
  box-shadow: 0 0 0 1px rgba(var(--dt-rgb), .08), 0 0 4px rgba(var(--dt-rgb), .035);
  overflow: hidden;
  /* the door and the room behind it are the same box, so the height carries
     from one to the other in a single move */
  transition: height .52s cubic-bezier(.22, .61, .36, 1),
              border-color .34s ease, box-shadow .34s ease;
}
.runbay[data-state="open"] { border-color: transparent; box-shadow: none; }
.runbay[data-state="open"]::after { opacity: 0; }
/* The card's own material: ivory-to-stone on a 104deg diagonal, sampled from
   the full DemoThemis wordmark, with a warm highlight thrown up out of the
   bottom-right corner. Same two tokens the homepage sets on the card, so the
   bay stays in step if the brand material is ever resampled. Closed and
   opening only — once the deck is up it fills the bay and nothing shows. */
.runbay[data-state="closed"],
.runbay[data-state="opening"] {
  background-color: var(--canvas);
  background-image: var(--dt-brand-gradient);
}
/* The rim that orbits the card on the homepage. Two lit arcs half a turn apart
   ride a conic gradient whose start angle animates through --dt-card-rim-angle;
   the mask keeps only the 1.5px the padding leaves outside the content box, so
   what shows is a light travelling the border itself. The @property and the
   keyframes are the card's own, declared in assets/v/ac641bc775b2/styles.css, which this page
   already loads — the bay runs the same animation, not a copy of it. */
.runbay::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  padding: 1.5px;
  border-radius: inherit;
  background: conic-gradient(
    from var(--dt-card-rim-angle),
    transparent 0deg 112deg,
    rgba(var(--dt-rgb), .04) 126deg,
    rgba(var(--dt-rgb), .28) 146deg,
    rgba(var(--dt-rgb), .9) 166deg,
    rgba(224, 252, 255, .98) 171deg,
    rgba(var(--dt-rgb), .18) 178deg,
    transparent 180deg 292deg,
    rgba(var(--dt-rgb), .04) 306deg,
    rgba(var(--dt-rgb), .28) 326deg,
    rgba(var(--dt-rgb), .9) 346deg,
    rgba(224, 252, 255, .98) 351deg,
    rgba(var(--dt-rgb), .18) 358deg,
    transparent 360deg
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: .72;
  filter: drop-shadow(0 0 3px rgba(var(--dt-rgb), .42));
  pointer-events: none;
  animation: dt-card-rim-orbit 7.5s linear infinite;
  transition: opacity .34s ease;
}
.runbay-stack { position: relative; }

/* ================= the splash ================= */
.rt-splash {
  display: flex; flex-direction: column; gap: .7rem;
  padding: clamp(.9rem, 2.4vw, 1.6rem);
  min-height: clamp(30rem, 80dvh, 54rem);
  font-family: var(--sans); color: var(--ink); line-height: 1.6;
}
.runbay[data-state="opening"] .rt-splash,
.runbay[data-state="open"] .rt-splash { position: absolute; inset: 0; }
.runbay[data-state="open"] .rt-splash { display: none; }

/* The chapter's racetrack card is the splash's opening statement now: what
   the reader is about to see, on two entirely separate products. It keeps
   its own markup and its own rules in assets/v/ac641bc775b2/run-through.css; what changes
   inside the bay is the height budget. As a standalone chapter opener it
   ran 559px tall at desktop, which would push the board and the key below
   the fold, so every measure that buys height without costing legibility is
   tightened here and the card takes the board's cyan edge so the two stack
   as siblings rather than as two different kinds of panel. */
.rt-card--splash {
  margin: 0;
  padding: .85rem .95rem 1rem;
  border-color: rgba(var(--dt-rgb), .4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7),
              0 0 0 1px rgba(var(--dt-rgb), .08),
              0 10px 28px -22px rgba(41, 37, 34, .5);
}
/* Newsreader, the face the chapter title is set in. The card is the one part
   of this bay that is prose rather than interface -- two sentences telling the
   reader what they are about to watch -- and the serif says so, while putting
   the furthest possible distance between it and the mono strip label a few
   pixels below. Balanced lines keep the second one from being a stranded word
   once it wraps. */
.rt-card--splash .rt-head b {
  font-family: var(--serif); font-size: 1.52rem; font-weight: 500;
  letter-spacing: -.015em; line-height: 1.16; color: var(--ink);
  text-transform: none; text-wrap: balance;
}
.rt-card--splash .rt-node { padding: .68rem .78rem; }
.rt-card--splash .rt-node small { font-size: .76rem; line-height: 1.42; }
.rt-card--splash .rt-omen-wordmark { width: min(10.4rem, 76%); }
.rt-card--splash .dt-wordmark-emission { width: min(10.4rem, 72%); }
.rt-card--splash .rt-nodes { gap: .8rem clamp(2.2rem, 7vw, 4.2rem); }
/* A station chip is absolutely positioned from left: 50%, so its shrink-to-fit
   width is capped at half the band and it wraps to two or three lines on a
   narrow screen, where it then runs into the node card below. Sizing it to
   max-content and capping that against the band lets it stay one line all the
   way down to 320px. */
.rt-card--splash .rt-chip { width: max-content; max-width: calc(100% - .5rem); }
/* Wide: the chips are a single nowrap line 24px tall, so a 1.15rem inset
   clears them and the band can be tight. */
@media (min-width: 721px) {
  .rt-card--splash .rt-band { margin: 1.15rem 0; padding: 1.15rem 3rem; }
}
/* Narrow: the nodes stack and the chips wrap to two lines, 44px tall. The
   band keeps a 1.5rem inset so a wrapped chip still lands clear of the node
   below it, and the height comes back out of the wordmarks and the copy. */
@media (max-width: 720px) {
  .rt-card--splash .rt-band { margin: 2rem 0 2.1rem; padding: 1.5rem 1.9rem; }
  .rt-card--splash .rt-node small { font-size: .74rem; line-height: 1.4; }
  .rt-card--splash .rt-omen-wordmark { width: min(9rem, 62%); }
  .rt-card--splash .dt-wordmark-emission { width: min(9rem, 58%); }
}

.rt-board {
  position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  border-radius: 13px; background: var(--surface); border: 1px solid rgba(var(--dt-rgb), .4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7), 0 0 0 1px rgba(var(--dt-rgb), .08),
              0 10px 34px -18px rgba(var(--dt-rgb), .85);
}
.rt-board-head {
  display: flex; align-items: center; gap: .4rem; padding: .38rem .75rem;
  border-bottom: 1px solid rgba(var(--dt-rgb), .22);
  background: linear-gradient(180deg, rgba(var(--dt-rgb), .11), rgba(var(--dt-rgb), .02));
  font-family: var(--mono); font-size: .57rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dt-emission-ink); border-radius: 12px 12px 0 0;
}
.rt-board-head .rt-n { margin-left: auto; color: var(--faint); letter-spacing: .06em; }
.rt-scroller {
  position: relative; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  padding: .55rem .75rem .65rem; display: flex; flex-direction: column; justify-content: flex-start;
  scrollbar-width: thin; scrollbar-color: rgba(var(--dt-rgb), .55) transparent;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
}
.rt-scroller::-webkit-scrollbar { width: 7px; }
.rt-scroller::-webkit-scrollbar-thumb { background: rgba(var(--dt-rgb), .5); border-radius: 99px; }

/* The foot holds two keys now, side by side while there is room and stacked
   with Fill the page on top when there is not, over the line that says why it
   is worth pressing. */
.rt-foot { display: flex; flex-direction: column; align-items: center; gap: .42rem;
  flex: 0 0 auto; padding-top: .15rem; }
.rt-keys { display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch;
  gap: .6rem; width: 100%; }
.rt-why { margin: 0; max-width: 32rem; text-align: center;
  font-size: .72rem; line-height: 1.45; color: var(--muted); }
.rt-why b { color: var(--dt-emission-ink); font-weight: 700; }

/* ---- the sequence ---- */
/* The sequence used to hold the page's 44rem reading measure, which left it
   centred in a board half again as wide and every row stopping 129px short of
   the edge at 1280. These are single-line list items with a jump control
   pinned to their right, not prose, so the measure buys nothing and costs the
   rows the width they are sitting in. The rail is fixed, so the lanes and the
   arrows between them do not move; only the text side grows. */
/* Two lanes need much less rail than four did, but not half: the face size is
   capped at 32px however wide the lane gets, so a lane sized to the old one
   left the pair 4.8px apart and reading as one blob. These are sized by the
   gap instead -- 12px between the two faces on a wide screen, 9.6 and 6.4 as
   it narrows -- which also holds the faces at a full 32px on touch, where the
   old four-lane rail had them down at 22px, under what a finger wants.
   Everything the rail gives back goes to the row text beside it. */
.rt-seq { --rail: 5.5rem; width: 100%;
  flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.rt-seq-body { position: relative; flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.rt-seq-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; z-index: 0; }
.rt-seq-head { position: absolute; z-index: 2; top: 0; left: 0; width: var(--rail); height: 0;
  pointer-events: none; }
.rt-seq-head .rt-faces { position: relative; height: 0; pointer-events: auto; }
/* space-between, not grow: every row keeps the height its own content needs
   and the slack becomes air between them, so a row never ends up tall with its
   text stranded at the top */
.rt-seq-rows { flex: 1 1 auto; display: flex; flex-direction: column;
  justify-content: space-between; gap: .25rem; }
.rt-seq-row {
  position: relative; z-index: 1; display: flex; align-items: flex-start; gap: .42rem; min-height: 2.4rem;
  width: calc(100% - var(--rail)); margin-left: var(--rail); padding: .62rem .15rem .62rem 0;
  border: 0; border-radius: 8px; font: inherit; color: inherit; text-align: left;
  background: transparent; cursor: pointer; appearance: none; -webkit-appearance: none;
  transition: opacity .13s ease, background .12s ease;
}
.rt-seq-row:hover, .rt-seq-row:focus-visible { background: rgba(var(--dt-rgb), .075); }
.rt-seq-row:focus-visible { outline: 2px solid var(--dt-emission); outline-offset: -2px; }
.rt-act { flex: 0 0 auto; width: 1.6rem; height: 1.6rem; border-radius: 8px; display: grid; place-items: center;
  margin-top: -.26rem; }
.rt-act svg { width: .95rem; height: .95rem; display: block; }
.rt-seq-row.is-app .rt-act { background: rgba(4, 226, 109, .16); color: var(--app-ink); }
.rt-seq-row.is-court .rt-act { background: rgba(var(--dt-rgb), .19); color: var(--crt-ink); }
.rt-t { min-width: 0; font-size: .795rem; line-height: 1.3; color: var(--ink); text-wrap: pretty; }
.rt-t em {
  font-style: normal; font-family: var(--mono); font-size: .58rem; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; margin-right: .32rem; white-space: nowrap;
}
.rt-t em::after { content: "·"; margin-left: .32rem; color: var(--faint); }
.rt-seq-row.is-app .rt-t em { color: var(--app-ink); }
.rt-seq-row.is-court .rt-t em { color: var(--crt-ink); }

/* while an actor is being explained, their own events stay lit and the rest recede */
.rt-seq-body[data-lit] .rt-seq-row { opacity: .32; }
.rt-seq-body[data-lit="market"] .rt-seq-row[data-who="market"],
.rt-seq-body[data-lit="court"]  .rt-seq-row[data-who="court"],
.rt-seq-body[data-lit] .rt-seq-row:hover,
.rt-seq-body[data-lit] .rt-seq-row:focus-visible { opacity: 1; }
.rt-ll, .rt-nd, .rt-arw { transition: opacity .13s ease, stroke-width .13s ease; }
.rt-seq-body[data-lit] .rt-ll { opacity: .1; }
.rt-seq-body[data-lit] .rt-nd { opacity: .22; }
.rt-seq-body[data-lit] .rt-arw { opacity: .16; }
.rt-seq-body[data-lit="market"] .rt-ll-market,
.rt-seq-body[data-lit="court"]  .rt-ll-court { opacity: .95; stroke-width: 2.6; }
.rt-seq-body[data-lit="market"] .rt-nd-market,
.rt-seq-body[data-lit="court"]  .rt-nd-court { opacity: 1; }
.rt-hit { cursor: pointer; }

/* ---- the four faces ---- */
.rt-hd {
  position: absolute; top: 50%; transform: translate(-50%, -50%); padding: 0; border-radius: 50%;
  cursor: pointer; display: grid; place-items: center; background: transparent;
  border: 1.6px solid currentColor; transition: border-width .12s ease, box-shadow .12s ease, background .12s ease;
}
.rt-hd.is-app { color: var(--app-ink); background: rgba(4, 226, 109, .14); }
.rt-hd.is-court { color: var(--crt-ink); background: rgba(var(--dt-rgb), .16); }
.rt-hd:hover, .rt-hd.is-on { border-width: 2.4px; }
.rt-hd.is-app:hover, .rt-hd.is-app.is-on { background: rgba(4, 226, 109, .3); box-shadow: 0 0 0 4px rgba(4, 226, 109, .22); }
.rt-hd.is-court:hover, .rt-hd.is-court.is-on { background: rgba(var(--dt-rgb), .34); box-shadow: 0 0 0 4px rgba(var(--dt-rgb), .32); }
.rt-hd:focus-visible { outline: 2px solid var(--dt-emission); outline-offset: 3px; }
.rt-hd svg { width: 62%; height: 62%; display: block; }
.rt-hd-i {
  position: absolute; border-radius: 50%; display: grid; place-items: center;
  font-family: var(--serif); font-style: italic; font-weight: 600; line-height: 1;
  color: #fff; box-shadow: 0 0 0 1.5px var(--surface); pointer-events: none;
}
.rt-hd.is-app .rt-hd-i { background: var(--app-ink); }
.rt-hd.is-court .rt-hd-i { background: var(--crt-ink); }

/* ---- the inspector: a tooltip anchored to the face it explains ----
   It sits on the sequence body rather than in the rail-width head, so it can
   use the board's full width, and assets/v/ac641bc775b2/run-through-splash.js gives it a
   left, a top and a --caret that keep it centred under its own face and
   inside the board. Opaque with a lifted edge, because it lands over the
   first rows and has to read as a card in front of them, not as a wash. */
.rt-insp {
  --insp-edge: rgba(var(--dt-rgb), .5);
  position: absolute; z-index: 6; left: 0; top: 0;
  width: max-content; max-width: min(25rem, 100%);
  display: flex; align-items: flex-start; gap: .5rem;
  border-radius: 12px; padding: .5rem .72rem .56rem;
  background: var(--surface); border: 1px solid var(--insp-edge);
  box-shadow: 0 16px 30px -18px rgba(41, 37, 34, .6), 0 2px 7px -4px rgba(41, 37, 34, .3);
  /* purely informational: a row underneath stays clickable through it */
  pointer-events: none;
  transition: opacity .14s ease, transform .14s cubic-bezier(.22, .61, .36, 1);
}
/* the caret rides the top edge under the face, and the two lit borders are
   the only part of the square that shows once the pill paints over the rest */
.rt-insp::before {
  content: ""; position: absolute; top: -6px; left: var(--caret, 24px);
  width: 10px; height: 10px; transform: translateX(-50%) rotate(45deg);
  background: var(--surface); border-radius: 2px 0 0 0;
  border-left: 1px solid var(--insp-edge); border-top: 1px solid var(--insp-edge);
}
.rt-insp.is-app { --insp-edge: rgba(4, 226, 109, .55); }
.rt-insp.is-court { --insp-edge: rgba(var(--dt-rgb), .6); }
.rt-insp-av {
  flex: 0 0 auto; width: 1.35rem; height: 1.35rem; margin-top: .05rem;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--line-strong); color: var(--surface); transition: background .12s ease;
}
.rt-insp-av svg { width: .78rem; height: .78rem; display: block; }
.rt-insp.is-app .rt-insp-av { background: var(--app-ink); }
.rt-insp.is-court .rt-insp-av { background: var(--crt-ink); box-shadow: 0 0 9px -1px rgba(var(--dt-rgb), .9); }
/* at rest there is nothing to say, so the pill is not there to be read */
.rt-insp.is-rest { opacity: 0; visibility: hidden; transform: translateY(-5px); }
.rt-insp-txt { min-width: 0; font-size: .755rem; line-height: 1.46; color: var(--ink-soft); }
/* the name is a heading over the sentence, not a run-on prefix inside it */
.rt-insp b {
  display: block; font-family: var(--mono); font-size: .565rem; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; margin: .06rem 0 .16rem;
  color: var(--faint);
}
.rt-insp.is-app b { color: var(--app-ink); }
.rt-insp.is-court b { color: var(--crt-ink); }

/* ---- Fill the page: the second key ----
   The chapter's own keycap in cyan instead of iris, so it reads as a sibling
   and not as an intruder: same construction, same height, same lamp-and-glyph
   grammar, and the same three states. It is a second way to START, not a
   setting to arm first, so neither route costs two presses. */
.rt-fill {
  --line: #eae4d9; --surface: #fffdf9; --ink: #23262e;
  /* the resting face, named so the asking state can lay a sheen over it
     without restating it */
  --face: linear-gradient(180deg, #fff 0,
    color-mix(in srgb, var(--dt-emission) 6%, var(--surface)) 55%,
    color-mix(in srgb, var(--dt-emission) 13%, var(--surface)) 100%);
  /* The band is cyan, not white: the face is already near-white, so a white
     sheen shifted it by about four points of RGB and was invisible for it.
     Both ends of the tile are transparent, so where one tile meets the next
     there is nothing to see -- which is what lets the shimmer repeat forever
     without a join. */
  --sheen: linear-gradient(100deg, transparent 0 36%,
    rgba(var(--dt-rgb), .22) 47% 53%, transparent 64% 100%);
  --seat: 0 3px 0 color-mix(in srgb, var(--dt-emission) 46%, var(--line-strong));
  flex: 0 1 auto; min-width: min(100%, 13rem); max-width: 18rem;
  appearance: none; -webkit-appearance: none; position: relative;
  display: flex; align-items: center; gap: .6rem; padding: .82rem .9rem;
  border: 1px solid color-mix(in srgb, var(--dt-emission) 55%, var(--line-strong));
  border-radius: 13px;
  /* The shimmer is part of the key, not part of a state it can be talked out
     of. It used to live on .is-asking, which the first hover took away for
     good, and hover removed the layer outright on top of that -- so it went
     on the first pass of the pointer and never returned. */
  /* Named so the keyframe can shift by exactly one of it. One size for every
     width, and deliberately: the band spans 28% of the tile, so it can only
     fall entirely off a key narrower than the tile if 0.28T is less than
     T - W, which is to say if the tile is more than about 1.39 times the key.
     240 clears that at every width the key takes, from 208 on the desk to
     full width on a phone. Growing the tile on narrow screens was tried and
     put back: at a 320px viewport the key is 265 and a 380px tile left the
     band able to disappear between passes, which is the very thing that made
     the old shimmer read as stopped. A second band on a very wide key is a
     smaller price than a moment with none. */
  --sheen-tile: 240px;
  background-image: var(--sheen), var(--face);
  background-size: var(--sheen-tile) 100%, auto;
  background-repeat: repeat-x, no-repeat;
  background-position: 0 0, 0 0;
  animation: rt-sheen 2.8s linear infinite;
  color: var(--ink); font-family: var(--sans); text-align: left; cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), var(--seat),
    0 4px 5px rgba(46, 40, 30, .14), 0 9px 18px -7px rgba(46, 40, 30, .3);
  transition: border-color .18s ease, box-shadow .18s ease, transform .14s ease;
}
/* Hover reads like the iris key's, in cyan: the key rises a millimetre, the
   seat under it deepens, and a ring lights the edge. Border-colour alone was
   too quiet to register as a state at all. */
.rt-fill:hover {
  border-color: var(--dt-emission); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95),
    0 4px 0 color-mix(in srgb, var(--dt-emission) 58%, var(--line-strong)),
    0 5px 7px rgba(46, 40, 30, .16), 0 12px 22px -8px rgba(46, 40, 30, .32),
    0 0 0 1px var(--dt-emission), 0 0 14px rgba(var(--dt-rgb), .3);
}
.rt-fill:hover .rt-fill-glyph {
  border-color: var(--dt-emission);
  box-shadow: var(--glyph-seat), 0 0 0 4px rgba(var(--dt-rgb), .17),
    0 0 10px rgba(var(--dt-rgb), .3);
}
/* Press bottoms the key out on its seat, fast enough to feel mechanical. */
.rt-fill:active {
  transform: translateY(2px); transition-duration: 45ms;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
    inset 0 2px 4px rgba(46, 40, 30, .18),
    0 0 0 color-mix(in srgb, var(--dt-emission) 46%, var(--line-strong)),
    0 1px 2px rgba(46, 40, 30, .12);
}
.rt-fill:active .rt-fill-glyph {
  box-shadow: inset 0 1px 3px rgba(46, 40, 30, .26), 0 0 0 3px rgba(var(--dt-rgb), .1);
}
.rt-fill:focus-visible { outline: 2px solid var(--dt-emission-ink); outline-offset: 2px; }
.rt-fill-glyph {
  --glyph-seat: inset 0 1px 0 rgba(255, 255, 255, .95), 0 1px 2px rgba(46, 40, 30, .18);
  flex: none; box-sizing: content-box; padding: .34rem; border-radius: 9px;
  border: 1px solid color-mix(in srgb, var(--dt-emission) 42%, var(--line));
  background: radial-gradient(circle at 50% 30%, #fff 0,
    color-mix(in srgb, var(--dt-emission) 12%, var(--surface)) 78%);
  color: var(--dt-emission-ink); display: grid; place-items: center; line-height: 0;
  box-shadow: var(--glyph-seat);
  transition: border-color .18s ease, box-shadow .18s ease;
}
.rt-fill-glyph svg { width: 19px; height: 19px; display: block; }
.rt-fill-copy { display: grid; gap: .12rem; min-width: 0; flex: 1 1 auto; justify-items: center; }
.rt-fill-kicker { font-size: .6rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dt-emission-ink); }
.rt-fill-label { font-size: .88rem; font-weight: 700; line-height: 1.35; color: var(--ink); white-space: nowrap; }

/* ---- the key asking to be pressed ----
   One gesture, repeated every 2.4s: the lamp pings, the key jumps and settles,
   and a band of light crosses its face. The first attempt failed on amplitude
   and on duty cycle -- a 3px lift eased down to well under 2px of real travel,
   and the band sat parked off the edge for 3.2 of every 4.4 seconds, so three
   quarters of the time there was simply nothing to see. The travel is 6px now
   on a 62px key, and the whole gesture lands inside a window a third the
   length, which is what makes it read as one movement rather than a twitch.

   The band is a background layer, not an overlay. Backgrounds paint behind
   content and clip themselves to the border box, so the label keeps its
   contrast and the key needs no overflow clip -- which in turn leaves the
   lamp's ping free to ring outside its own edge. */
@keyframes rt-rock {
  0%, 26% { transform: translateY(0); }
  36% { transform: translateY(-6px); }
  48% { transform: translateY(0); }
  56% { transform: translateY(-3px); }
  66%, 100% { transform: translateY(0); }
}
/* Shifted by exactly one tile per cycle: moving a repeating pattern one whole
   period lands on a state identical to the one it left, so there is no seam to
   hide and nothing is ever held still. What was here before did both of the
   things that make a shimmer look broken. It parked at 0-26% and again at
   62-100%, so for nearly two thirds of every cycle the band did not move -- on
   a fresh page that reads as an animation that has not started. And it moved
   the band with percentage background-position, which is not a translation:
   the offset is P x (container - image), so a band 55% as wide as the key only
   ever travelled 99% of the key's width and could never clear it. It was still
   lying across the face at both ends of the loop, and jumped from one side to
   the other on the wrap. */
@keyframes rt-sheen {
  from { background-position: 0 0, 0 0; }
  to   { background-position: var(--sheen-tile) 0, 0 0; }
}
@keyframes rt-ping {
  0%, 22% { box-shadow: var(--glyph-seat), 0 0 0 0 rgba(var(--dt-rgb), .55); }
  62%, 100% { box-shadow: var(--glyph-seat), 0 0 0 9px rgba(var(--dt-rgb), 0); }
}
/* The gesture rides on top of the shimmer rather than replacing it. rt-sheen
   is restated first and identically, so when .is-asking is dropped the browser
   matches it to the one already running and only the rock leaves -- the
   shimmer does not restart, and does not jump. */
.rt-fill.is-asking {
  animation: rt-sheen 2.8s linear infinite, rt-rock 2.4s ease-in-out infinite;
}
.rt-fill.is-asking .rt-fill-glyph { animation: rt-ping 2.4s ease-out infinite; }
/* An animation outranks any plain declaration, so while the key is asking it
   owns transform and box-shadow outright and hover and press have nothing left
   to say. Only the gesture is cancelled under those states, and only on the
   element the gesture is on: the shimmer is not a state and has no business
   being switched off by a pointer passing over it. The gesture is retired for
   good on the first engagement anyway -- see stopAsking -- so this covers the
   frame between the pointer arriving and the class coming off. */
.rt-fill.is-asking:hover, .rt-fill.is-asking:active {
  animation: rt-sheen 2.8s linear infinite;
}
.rt-fill.is-asking:hover .rt-fill-glyph,
.rt-fill.is-asking:active .rt-fill-glyph { animation: none; }

/* ---- the jump label on each row ---- */
.rt-jump {
  position: absolute; right: .2rem; display: inline-flex; align-items: center; gap: .26rem;
  font-family: var(--mono); font-size: .5rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; white-space: nowrap; color: var(--dt-emission-ink);
  background: var(--surface); border-radius: 6px; padding: .14rem .42rem;
  box-shadow: 0 0 0 1px rgba(var(--dt-rgb), .38);
  opacity: 0; transform: translateX(4px); pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
}
.rt-seq-row:hover .rt-jump, .rt-seq-row:focus-visible .rt-jump { opacity: 1; transform: none; }
.rt-jump svg { width: .62rem; height: .62rem; display: block; }
.rt-jump.is-tight { padding: .16rem .24rem; }
/* Where no line has room beside the text, the label takes a strip of its own.
   The strip has to be deep enough to hold it clear: the label stands 17.3px
   and sits .16rem off the floor, so anything under about 20px puts it back on
   the last line of the text -- 1.05rem reserved 16.8px and left it lying 2.5px
   over the words it was meant to sit below. 1.5rem clears the label and leaves
   a few pixels of air under the text. */
.rt-seq-row.has-below { padding-bottom: 1.5rem; }
.rt-seq-row.has-below .rt-jump { top: auto; bottom: .16rem; }
.rt-jump.is-tight span { display: none; }
@media (hover: none) {
  .rt-jump { opacity: .55; transform: none; padding: .16rem .24rem; box-shadow: 0 0 0 1px rgba(var(--dt-rgb), .26); }
  .rt-jump span { display: none; }
}

/* ---- the key: the homepage's product-card summon, same materials ---- */
.rt-go {
  --accent: #5a4bd6; --line: #eae4d9; --surface: #fffdf9; --ink: #23262e; --muted: #5c6470;
  flex: 0 1 auto; width: 35%; min-width: min(100%, 15rem); max-width: 22rem;
  appearance: none; -webkit-appearance: none; position: relative; display: flex; align-items: center; gap: .72rem;
  padding: .82rem .9rem;
  border: 1px solid color-mix(in srgb, var(--accent) 38%, var(--line)); border-radius: 13px;
  background: linear-gradient(180deg, #fff 0, color-mix(in srgb, var(--surface) 92%, white) 55%,
    color-mix(in srgb, var(--rt-emboss) 20%, var(--surface)) 100%);
  color: var(--ink); font-family: var(--sans); text-align: left; cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    0 3px 0 color-mix(in srgb, var(--accent) 52%, var(--line)),
    0 4px 5px rgba(46, 40, 30, .14),
    0 9px 18px -7px rgba(46, 40, 30, .3);
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease, transform .14s ease;
}
.rt-go:hover {
  border-color: var(--dt-emission); transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    0 4px 0 color-mix(in srgb, var(--accent) 52%, var(--line)),
    0 5px 6px rgba(46, 40, 30, .14),
    0 12px 22px -7px rgba(46, 40, 30, .32),
    0 0 0 1px var(--dt-emission),
    0 0 12px rgba(var(--dt-rgb), .2);
}
.rt-go:active {
  transform: translateY(2px); transition-duration: 45ms;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .5),
    inset 0 2px 4px rgba(46, 40, 30, .18),
    0 0 0 color-mix(in srgb, var(--accent) 42%, var(--line)),
    0 1px 2px rgba(46, 40, 30, .12);
}
.rt-go:focus-visible { outline: 2px solid var(--dt-emission-ink); outline-offset: 2px; }
.rt-go-led {
  flex: none; width: .62rem; height: .62rem; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line));
  background: radial-gradient(circle at 50% 32%, color-mix(in srgb, var(--rt-emboss) 62%, var(--surface)) 0,
    color-mix(in srgb, var(--rt-stone) 28%, var(--surface)) 100%);
  box-shadow: inset 0 1px 2px rgba(46, 40, 30, .4), 0 1px 0 rgba(255, 255, 255, .82);
  transition: background .25s ease, border-color .25s ease, box-shadow .25s ease;
}
.rt-go-copy { display: grid; gap: .12rem; min-width: 0; flex: 1 1 auto; justify-items: center; }
.rt-go-kicker { font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.rt-go-label { font-size: .9rem; font-weight: 700; line-height: 1.35; color: var(--ink); }
.rt-go-power {
  flex: none; margin-left: auto; box-sizing: content-box; padding: .34rem; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--dt-emission) 32%, var(--line));
  background: radial-gradient(circle at 50% 30%, #fff 0, color-mix(in srgb, var(--dt-emission) 7%, var(--surface)) 78%);
  color: var(--dt-emission-ink);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), 0 1px 2px rgba(46, 40, 30, .2),
              0 0 0 3px rgba(var(--dt-rgb), .08);
  transition: color .18s ease, border-color .18s ease, box-shadow .18s ease, transform .42s cubic-bezier(.22,.61,.36,1);
}
.rt-go:hover .rt-go-power {
  border-color: var(--dt-emission);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), 0 1px 2px rgba(46, 40, 30, .2),
              0 0 0 4px rgba(var(--dt-rgb), .17), 0 0 10px rgba(var(--dt-rgb), .28);
}
.rt-go:active .rt-go-power { box-shadow: inset 0 1px 3px rgba(46, 40, 30, .26), 0 0 0 3px rgba(var(--dt-rgb), .1); }
/* the moment the key is thrown: the lamp lights and the switch turns over */
.rt-go.is-on .rt-go-led {
  border-color: color-mix(in srgb, var(--omm-green) 55%, var(--line));
  background: var(--omm-green);
  box-shadow: 0 0 9px rgba(4, 226, 109, .85), inset 0 1px 1px rgba(255, 255, 255, .45);
}
.rt-go.is-on .rt-go-power {
  color: var(--omm-green); transform: rotate(180deg);
  border-color: color-mix(in srgb, var(--omm-green) 52%, var(--line));
  background: radial-gradient(circle at 50% 30%, #fff 0, color-mix(in srgb, var(--omm-green) 8%, var(--surface)) 78%);
}

/* ================= the reveal ================= */
/* The splash lifts away in two beats — statements first, board and key a
   moment later — while the deck rises behind it. The bay's own height
   carries the move, so the page below travels once, smoothly, instead of
   snapping when the frame appears. */
.rt-splash > * { transition: opacity .26s ease, transform .34s cubic-bezier(.4, 0, .2, 1); }
.runbay[data-state="opening"] .rt-splash > *,
.runbay[data-state="open"] .rt-splash > * { opacity: 0; transform: translateY(-14px); }
.runbay[data-state="opening"] .rt-card--splash { transition-delay: 0ms; }
.runbay[data-state="opening"] .rt-board { transition-delay: 60ms; }
.runbay[data-state="opening"] .rt-foot { transition-delay: 110ms; }

.runbay-drawer { position: relative; opacity: 0; transform: scale(.985) translateY(10px); }
.runbay-drawer[hidden] { display: none; }
.runbay[data-state="opening"] .runbay-drawer,
.runbay[data-state="open"] .runbay-drawer {
  opacity: 1; transform: none;
  transition: opacity .42s ease .16s, transform .52s cubic-bezier(.22, .61, .36, 1) .16s;
}
/* A transformed ancestor becomes the containing block for position:fixed
   descendants, so for the 680ms the reveal above is running -- 520ms of
   transform after a 160ms delay -- this drawer captures the deck's full-screen
   embed and lays it out inside itself. Pressing Fill the page from the splash
   lands the promotion squarely in that window: inset:0 resolved against a
   drawer that was still nearly zero high, so the simulator collapsed to
   nothing and then snapped out to the viewport when the transform finished.
   Full screen cancels the reveal outright. There is nothing left to reveal --
   the frame is about to cover the whole viewport. */
.runbay.is-fullscreen .runbay-drawer { transform: none; transition: none; }

/* a cyan wash that sweeps once as the deck arrives, then gets out of the way */
.runbay-flash {
  position: absolute; inset: 0; pointer-events: none; z-index: 3; opacity: 0;
  background: radial-gradient(58% 42% at 50% 42%, rgba(var(--dt-rgb), .5), transparent 72%);
}
.runbay[data-state="opening"] .runbay-flash { animation: rt-bloom .74s cubic-bezier(.22, .61, .36, 1) forwards; }
@keyframes rt-bloom {
  0%   { opacity: 0; transform: scale(.9); }
  28%  { opacity: .85; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.06); }
}

/* without scripting the key cannot open anything, so the deck gets a plain link */
.rt-noscript {
  margin: 0; padding: 0 clamp(.9rem, 2.4vw, 1.6rem) 1.1rem;
  font-family: var(--sans); font-size: .85rem; color: var(--ink-soft);
}
.rt-noscript a { color: var(--dt-emission-ink); font-weight: 700; }

.rt-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ================= sizes ================= */
@media (max-width: 680px) {
  .rt-splash { gap: .5rem; min-height: clamp(28rem, 76dvh, 44rem); }
  .rt-card--splash { padding: .7rem .8rem .8rem; }
  .rt-card--splash .rt-head b { font-size: 1.24rem; }
  .rt-scroller { padding: .5rem .55rem .6rem; }
  /* the faces are tap targets, so the lanes have to be wide enough to carry
     one: 24px apart satisfies the WCAG 2.5.8 spacing exception */
  .rt-seq { --rail: 5.2rem; }
  /* every row wraps at this width, so its own content already gives the height;
     the padding only has to keep neighbours apart */
  .rt-seq-row { padding-top: .34rem; padding-bottom: .34rem; }
  .rt-t { font-size: .755rem; }
  .rt-t em { font-size: .55rem; }
  /* the head goes back into the flow here: the faces stack above the rows
     rather than sitting beside them, so the run reads top to bottom */
  .rt-seq-head { position: relative; z-index: 1; width: auto; height: auto;
    display: flex; flex-direction: column-reverse; gap: .35rem; padding: .25rem 0 .1rem; }
  .rt-seq-rows { padding-top: 0; }
  .rt-seq-head .rt-faces { height: 2.3rem; }
  .rt-insp { max-width: 100%; }
  .rt-insp-txt { font-size: .73rem; line-height: 1.44; }
}
@media (max-width: 640px) {
  .rt-go, .rt-fill { width: 100%; max-width: none; }
  /* Stacked, the recommended key leads. Side by side it sits second, where the
     eye lands last and the sentence below points back at it; in a column that
     would put the recommendation under the thing it is recommending against.
     Only the paint order moves -- both keys keep their place in the tab order,
     and each says in its own label what it does. */
  .rt-fill { order: -1; }
}
@media (max-height: 660px) {
  .rt-splash { gap: .45rem; padding: .7rem .9rem .75rem; min-height: 0; }
  .rt-card--splash { padding: .6rem .75rem .7rem; }
  .rt-card--splash .rt-head b { font-size: 1.14rem; }
  .rt-card--splash .rt-node { padding: .5rem .62rem; }
  .rt-card--splash .rt-node small { font-size: .72rem; line-height: 1.36; }
}
@media (min-width: 721px) and (max-height: 660px) {
  .rt-card--splash .rt-band { margin: .95rem 0; padding: .9rem 2.6rem; }
}
@media (max-width: 400px) {
  .rt-seq { --rail: 4.8rem; }
  .rt-act { width: 1.25rem; height: 1.25rem; }
  .rt-act svg { width: .74rem; height: .74rem; }
}
@media (max-width: 360px) {
  /* the label is on its own, shorter line here, so the chip lifts further —
     and the row's top padding has to cover the lift, or the chip hangs out
     of the box that tints under it on hover */
  .rt-act { margin-top: -.42rem; }
  .rt-seq-row { padding-top: .46rem; }
  .rt-t em { display: block; white-space: normal; margin-right: 0; }
  .rt-t em::after { content: none; }
}

@media (prefers-reduced-motion: reduce) {
  .runbay, .rt-splash > *, .runbay-drawer, .rt-hd, .rt-seq-row, .rt-ll, .rt-nd, .rt-arw,
  .rt-insp, .rt-insp-av, .rt-go, .rt-go-power, .rt-go-led, .rt-jump, .rt-fill { transition: none !important; }
  .rt-fill, .rt-fill.is-asking, .rt-fill.is-asking .rt-fill-glyph,
  .rt-fill.is-asking:hover, .rt-fill.is-asking:active { animation: none; }
  /* the standing shimmer goes with it: one flat face, no travelling band */
  .rt-fill { background-image: var(--face); background-size: auto;
    background-repeat: no-repeat; background-position: 0 0; }
  /* no gesture at all: the face goes back to one layer, and a still ring says
     the same thing the movement was there to say */
  .rt-fill.is-asking { background-image: var(--face); background-size: auto;
    background-repeat: repeat; background-position: 0 0;
    /* the ambient layer has to be restated with the rest: box-shadow replaces
       rather than adds, and leaving 0 9px 18px -7px off the end took the key's
       depth away for as long as it was asking -- it sat visibly flatter than
       Start the run beside it, then popped to full elevation the first time a
       pointer or a tab reached it. The ring is the only thing this state adds
       now, so retiring it costs the ring and nothing else. */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95), var(--seat),
      0 4px 5px rgba(46, 40, 30, .14), 0 9px 18px -7px rgba(46, 40, 30, .3),
      0 0 0 4px rgba(var(--dt-rgb), .14); }
  .runbay, .runbay::after { transition: none !important; }
  .rt-insp.is-rest { transform: none; }
  .runbay[data-state="opening"] .runbay-flash { animation: none; }
  /* the card holds the rim still and dims it rather than hiding it */
  .runbay::after { animation: none; opacity: .48; }
  /* both keys, not just the iris one: they carry the same 1px lift and 2px
     press, and stilling only .rt-go left the cyan key hopping under the
     pointer next to a sibling that did not -- unsmoothed, at that, since the
     transition reset above covers .rt-fill too */
  .rt-go:hover, .rt-go:active,
  .rt-fill:hover, .rt-fill:active { transform: none; }
  .rt-go.is-on .rt-go-power { transform: none; }
}
@media (forced-colors: active) {
  .runbay, .rt-board, .rt-card--splash, .rt-go, .rt-fill { border: 1px solid ButtonText; }
  .runbay::after { display: none; }
  .rt-jump { opacity: 1; }
}
