/* Copyright (c) 2026 FR&D. All rights reserved. */

/* Kinetic type and rendered loops, shared by the landing page (styles.css) and the concept
   pages on context-relay.app (concept.css). The vocabulary comes from the product video
   (video/src/assets): a split-flap board, digit reels, a slot reel of assistants, verbs that
   punch in, block letters that sweep in, and a wordmark that resolves out of scrambled glyphs.
   Everything here is CSS; /kinetic.js only decides *when* a piece runs (once it is on screen)
   and which palette a rendered loop loads. With scripting off every piece still plays, on its
   own clock, and under reduced motion every piece sits in its settled state.

   No inline styles anywhere: the public host's CSP forbids them, so stagger comes from
   nth-child and per-piece custom properties are set in this file.

   Colour tokens bridge to whichever stylesheet is loaded. Both define --ink, --muted, --faint,
   --line, --accent and --mono; the panel tone falls back across the two names for it. */

.k,
.render-stage {
  --k-ink: var(--ink);
  --k-muted: var(--muted);
  --k-faint: var(--faint);
  --k-line: var(--line);
  --k-accent: var(--accent);
  --k-panel: var(--paper-deep, var(--wash, rgba(0, 0, 0, .05)));
  --k-lift: var(--surface, var(--lift, transparent));
  --k-red: #dc2f3c;
  --k-red-glow: rgba(220, 47, 60, .55);
  --k-teal: #2c8888;
  --k-glow: rgba(92, 61, 130, .28);
  /* The flap board is always dark: ink cells on paper, near-black on the dark ground. */
  --k-board: #1d1b1a;
  --k-board-flap: #2a2725;
  --k-glyph: #f5f2eb;
  --k-display: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --k-mono: var(--mono, ui-monospace, "SF Mono", Menlo, monospace);
  --k-out: cubic-bezier(.16, 1, .3, 1);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .k,
  :root:not([data-theme="light"]) .render-stage {
    --k-red: #ff3b47;
    --k-red-glow: rgba(255, 59, 71, .75);
    --k-teal: #3fb8ae;
    --k-glow: rgba(196, 167, 231, .32);
    --k-board: #1f1d24;
    --k-board-flap: #26232c;
  }
}
:root[data-theme="dark"] .k,
:root[data-theme="dark"] .render-stage {
  --k-red: #ff3b47;
  --k-red-glow: rgba(255, 59, 71, .75);
  --k-teal: #3fb8ae;
  --k-glow: rgba(196, 167, 231, .32);
  --k-board: #1f1d24;
  --k-board-flap: #26232c;
}

/* Every decorative piece here carries a spoken alternative in this class; styles.css defines
   it too, concept.css does not. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Block letters: the video's heavy uppercase, one line, tight. */
.k-block {
  margin: 0;
  font-family: var(--k-display);
  font-weight: 900;
  line-height: .94;
  letter-spacing: -.035em;
  text-transform: uppercase;
  color: var(--k-ink);
}
.k-kicker {
  margin: 0;
  font-family: var(--k-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--k-faint);
}

/* ---------------------------------------------------------------------------
   Sweep. Block letters arrive from one side and decelerate into place.
   .k-sweep-r comes from the right, .k-sweep-l from the left. The clip stops the
   run-in from widening the page. */
.k-sweep-clip { overflow: hidden; }
.k-sweep-r,
.k-sweep-l { display: block; }
[data-kinetic="wait"] .k-sweep-r,
[data-kinetic="wait"] .k-sweep-l { opacity: 0; }
[data-kinetic=""] .k-sweep-r,
[data-kinetic="run"] .k-sweep-r { animation: k-sweep-r 1.1s var(--k-out) both; }
[data-kinetic=""] .k-sweep-l,
[data-kinetic="run"] .k-sweep-l { animation: k-sweep-l 1.1s .12s var(--k-out) both; }
@keyframes k-sweep-r { from { opacity: 0; transform: translateX(60vw); } 12% { opacity: 1; } to { opacity: 1; transform: none; } }
@keyframes k-sweep-l { from { opacity: 0; transform: translateX(-60vw); } 12% { opacity: 1; } to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------------------
   Stack. Five verbs punch in one after another, the newest lit and the rest
   stepped back, then all of them light at once. */
.k-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .02em;
  margin: 0;
  padding: 0;
  list-style: none;
}
.k-stack > li {
  font-family: var(--k-display);
  font-weight: 900;
  font-size: var(--k-stack-size, clamp(40px, 6.2vw, 84px));
  line-height: .92;
  letter-spacing: -.04em;
  text-transform: uppercase;
  color: var(--k-ink);
  transform-origin: left center;
  text-shadow: 0 0 22px var(--k-glow);
}
.k-stack > li:nth-child(1) { --i: 0; }
.k-stack > li:nth-child(2) { --i: 1; }
.k-stack > li:nth-child(3) { --i: 2; }
.k-stack > li:nth-child(4) { --i: 3; }
.k-stack > li:nth-child(5) { --i: 4; }
.k-stack[data-kinetic="wait"] > li { opacity: 0; text-shadow: none; }
.k-stack[data-kinetic=""] > li,
.k-stack[data-kinetic="run"] > li {
  text-shadow: none;
  animation:
    k-stack-punch .55s cubic-bezier(.2, .9, .25, 1) calc(var(--i) * .34s) both,
    k-stack-dim .3s ease calc(var(--i) * .34s + .36s) forwards,
    k-stack-relight .45s ease 2.35s forwards;
}
.k-stack[data-kinetic=""] > li:last-child,
.k-stack[data-kinetic="run"] > li:last-child {
  animation:
    k-stack-punch .55s cubic-bezier(.2, .9, .25, 1) calc(var(--i) * .34s) both,
    k-stack-relight .45s ease 2.35s forwards;
}
@keyframes k-stack-punch { from { opacity: 0; transform: scale(1.5); } 40% { opacity: 1; } to { opacity: 1; transform: none; } }
@keyframes k-stack-dim { to { opacity: .26; } }
@keyframes k-stack-relight { to { opacity: 1; text-shadow: 0 0 22px var(--k-glow); } }

/* ---------------------------------------------------------------------------
   Split flap. Each cell holds six glyph layers in the order the board shows them: blank,
   three random letters, the letter it lands on, blank again. Every layer paints its top half
   (::before) and bottom half (::after) of its glyph. A flip is one layer's top half falling
   and the next layer's bottom half rising behind it; the top halves stack earliest-on-top and
   the bottom halves latest-on-top, so whichever layer has not fallen yet shows above and
   whichever has risen last shows below. The cycle is 8 s: blank, a fast run through the three
   random letters, the word held, then a flip back to blank. It starts and ends blank, so the
   loop's restart is invisible and the board never fades out. Cells start left to right, 40 ms
   apart. */
.k-flap {
  --k-cell-w: clamp(22px, 5vw, 54px);
  --k-cell-h: calc(var(--k-cell-w) * 1.5);
  --k-persp: calc(var(--k-cell-w) * 7);
  display: inline-flex;
  gap: calc(var(--k-cell-w) * .11);
}
.k-flap-sm { --k-cell-w: clamp(19px, 2.1vw, 22px); }
.k-cell {
  position: relative;
  isolation: isolate;
  display: block;
  width: var(--k-cell-w);
  height: var(--k-cell-h);
}
/* No `perspective` on a layer: it would make the layer a stacking context, and the halves of
   the layers have to interleave (a falling top flap over the next layer's top, a rising
   bottom flap over the previous layer's bottom), which the z-indexes below can only do inside
   one context, the cell's. So the depth rides on each flap's own transform (perspective() in
   front of the rotate) instead. With it on the layers, the last layer painted over the rest
   entirely and the board showed the last glyph's top halves over the first glyph's bottom
   halves for the whole opening hold. */
.k-cell > i {
  position: absolute;
  inset: 0;
  display: block;
  font-style: normal;
}
.k-cell > i::before,
.k-cell > i::after {
  content: attr(data-g);
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  overflow: hidden;
  display: flex;
  justify-content: center;
  box-sizing: border-box;
  background: var(--k-board);
  color: var(--k-glyph);
  font-family: var(--k-display);
  font-weight: 800;
  font-size: calc(var(--k-cell-w) * .82);
  line-height: var(--k-cell-h);
  letter-spacing: -.02em;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.k-cell > i::before { top: 0; align-items: flex-start; border-radius: 14% 14% 0 0; transform-origin: 50% 100%; }
.k-cell > i::after { top: 50%; align-items: flex-end; border-radius: 0 0 14% 14%; transform-origin: 50% 0; background: var(--k-board-flap); }
/* z-order: top halves earliest-on-top (1..6 → 6..1), bottom halves latest-on-top (7..12), the
   hinge line over all. Every bottom half carries the flap tone, so the opening blank and the
   closing blank look the same and the loop restart is invisible. */
.k-cell > i:nth-child(1)::before { z-index: 6; }
.k-cell > i:nth-child(2)::before { z-index: 5; }
.k-cell > i:nth-child(3)::before { z-index: 4; }
.k-cell > i:nth-child(4)::before { z-index: 3; }
.k-cell > i:nth-child(5)::before { z-index: 2; }
.k-cell > i:nth-child(6)::before { z-index: 1; }
.k-cell > i:nth-child(1)::after { z-index: 7; }
.k-cell > i:nth-child(2)::after { z-index: 8; }
.k-cell > i:nth-child(3)::after { z-index: 9; }
.k-cell > i:nth-child(4)::after { z-index: 10; }
.k-cell > i:nth-child(5)::after { z-index: 11; }
.k-cell > i:nth-child(6)::after { z-index: 12; }
.k-cell::after {
  content: "";
  position: absolute;
  z-index: 13;
  left: 0;
  right: 0;
  top: calc(50% - 1px);
  height: 2px;
  background: rgba(0, 0, 0, .85);
}
/* Settled state (no script, or reduced motion): the fifth layer, the word, shows. The four
   before it have fallen; the sixth's top sits under the fifth's and its bottom has not risen. */
.k-cell > i:nth-child(-n+4)::before { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
.k-cell > i:nth-child(n+2)::after { transform: perspective(var(--k-persp)) rotateX(90deg); }
.k-cell > i:nth-child(5)::after { transform: none; }
/* Waiting (scripted, not yet on screen): the first frame of the cycle, a blank board. */
.k-flap[data-kinetic="wait"] .k-cell > i::before { transform: none; opacity: 1; }
.k-flap[data-kinetic="wait"] .k-cell > i:nth-child(n+2)::after { transform: perspective(var(--k-persp)) rotateX(90deg); }
/* Running: flip k turns layer k's top down and layer k+1's bottom up, staggered by cell. */
.k-flap[data-kinetic=""] .k-cell > i:nth-child(1)::before,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(1)::before { animation: k-flap-fall-1 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(2)::after,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(2)::after { animation: k-flap-rise-2 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(2)::before,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(2)::before { animation: k-flap-fall-2 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(3)::after,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(3)::after { animation: k-flap-rise-3 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(3)::before,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(3)::before { animation: k-flap-fall-3 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(4)::after,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(4)::after { animation: k-flap-rise-4 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(4)::before,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(4)::before { animation: k-flap-fall-4 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(5)::after,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(5)::after { animation: k-flap-rise-5 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(5)::before,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(5)::before { animation: k-flap-fall-5 8s linear infinite; }
.k-flap[data-kinetic=""] .k-cell > i:nth-child(6)::after,
.k-flap[data-kinetic="run"] .k-cell > i:nth-child(6)::after { animation: k-flap-rise-6 8s linear infinite; }
.k-cell:nth-child(2) > i::before, .k-cell:nth-child(2) > i::after { animation-delay: 0.04s; }
.k-cell:nth-child(3) > i::before, .k-cell:nth-child(3) > i::after { animation-delay: 0.08s; }
.k-cell:nth-child(4) > i::before, .k-cell:nth-child(4) > i::after { animation-delay: 0.12s; }
.k-cell:nth-child(5) > i::before, .k-cell:nth-child(5) > i::after { animation-delay: 0.16s; }
.k-cell:nth-child(6) > i::before, .k-cell:nth-child(6) > i::after { animation-delay: 0.2s; }
.k-cell:nth-child(7) > i::before, .k-cell:nth-child(7) > i::after { animation-delay: 0.24s; }
.k-cell:nth-child(8) > i::before, .k-cell:nth-child(8) > i::after { animation-delay: 0.28s; }
.k-cell:nth-child(9) > i::before, .k-cell:nth-child(9) > i::after { animation-delay: 0.32s; }
.k-cell:nth-child(10) > i::before, .k-cell:nth-child(10) > i::after { animation-delay: 0.36s; }
.k-cell:nth-child(11) > i::before, .k-cell:nth-child(11) > i::after { animation-delay: 0.4s; }
.k-cell:nth-child(12) > i::before, .k-cell:nth-child(12) > i::after { animation-delay: 0.44s; }
/* 8 s cycle, in hundredths: blank to 1.2 s; three fast flips of .12 s and a landing flip of
   .16 s (15%–21.5%); the word held to 6 s; a .2 s flip back to blank (75%–77.5%); blank to the
   end. Each fall's opacity drops a hair after it has turned edge-on, so it is never seen going. */
@keyframes k-flap-fall-1 {
  0%, 15.00% { transform: none; opacity: 1; animation-timing-function: cubic-bezier(.5, 0, 1, .6); }
  15.75% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 1; }
  15.80%, 100% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
}
@keyframes k-flap-rise-2 {
  0%, 15.75% { transform: perspective(var(--k-persp)) rotateX(90deg); animation-timing-function: cubic-bezier(0, .4, .5, 1); }
  16.50%, 100% { transform: none; }
}
@keyframes k-flap-fall-2 {
  0%, 16.50% { transform: none; opacity: 1; animation-timing-function: cubic-bezier(.5, 0, 1, .6); }
  17.25% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 1; }
  17.30%, 100% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
}
@keyframes k-flap-rise-3 {
  0%, 17.25% { transform: perspective(var(--k-persp)) rotateX(90deg); animation-timing-function: cubic-bezier(0, .4, .5, 1); }
  18.00%, 100% { transform: none; }
}
@keyframes k-flap-fall-3 {
  0%, 18.00% { transform: none; opacity: 1; animation-timing-function: cubic-bezier(.5, 0, 1, .6); }
  18.75% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 1; }
  18.80%, 100% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
}
@keyframes k-flap-rise-4 {
  0%, 18.75% { transform: perspective(var(--k-persp)) rotateX(90deg); animation-timing-function: cubic-bezier(0, .4, .5, 1); }
  19.50%, 100% { transform: none; }
}
@keyframes k-flap-fall-4 {
  0%, 19.50% { transform: none; opacity: 1; animation-timing-function: cubic-bezier(.5, 0, 1, .6); }
  20.50% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 1; }
  20.55%, 100% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
}
@keyframes k-flap-rise-5 {
  0%, 20.50% { transform: perspective(var(--k-persp)) rotateX(90deg); animation-timing-function: cubic-bezier(0, .4, .5, 1); }
  21.50%, 100% { transform: none; }
}
@keyframes k-flap-fall-5 {
  0%, 75.00% { transform: none; opacity: 1; animation-timing-function: cubic-bezier(.5, 0, 1, .6); }
  76.25% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 1; }
  76.30%, 100% { transform: perspective(var(--k-persp)) rotateX(-90deg); opacity: 0; }
}
@keyframes k-flap-rise-6 {
  0%, 76.25% { transform: perspective(var(--k-persp)) rotateX(90deg); animation-timing-function: cubic-bezier(0, .4, .5, 1); }
  77.50%, 100% { transform: none; }
}

/* ---------------------------------------------------------------------------
   Reels. A window three rows tall with the strip inside; the strip travels up
   by (rows − 1) rows and blurs while it is moving fast. The odometer and the
   roulette are both instances. */
.k-reel {
  position: relative;
  display: block;
  height: calc(var(--k-row) * 3);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 30%, #000 70%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 30%, #000 70%, transparent 100%);
}
.k-strip {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--k-row);
  margin: 0;
  padding: 0;
  list-style: none;
  transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row)));
}
.k-strip > li {
  height: var(--k-row);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--k-display);
  font-weight: 900;
  line-height: 1;
  color: var(--k-ink);
  white-space: nowrap;
}

/* Odometer: four digit reels roll and land, left to right, on 0. */
.k-odometer {
  --k-row: clamp(52px, 8vw, 104px);
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.2vw, 14px);
  animation: k-loop-fade 10s linear infinite;
}
.k-odometer .k-reel {
  width: calc(var(--k-row) * .86);
  border: 1px solid var(--k-line);
  border-radius: calc(var(--k-row) * .16);
  background: var(--k-panel);
  -webkit-mask-image: none;
  mask-image: none;
}
.k-odometer .k-reel::before {
  content: "";
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--k-line);
}
.k-odometer .k-reel::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom, var(--k-panel), transparent 34%, transparent 66%, var(--k-panel));
}
.k-odometer .k-strip > li {
  font-size: calc(var(--k-row) * .78);
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
/* Row counts of the four strips in the markup (each runs from its first digit up through a
   few full turns to a final 0). They have to match, or a reel lands short or runs off its end:
   7 8 9 0 + one turn = 14; 3 4 … 0 + two turns = 28; 9 0 + three turns = 32; 4 5 … 0 + three
   turns = 37. */
.k-odometer .k-reel:nth-child(1) { --k-rows: 14; }
.k-odometer .k-reel:nth-child(2) { --k-rows: 28; }
.k-odometer .k-reel:nth-child(3) { --k-rows: 32; }
.k-odometer .k-reel:nth-child(4) { --k-rows: 37; }
.k-odometer-unit {
  margin-left: clamp(6px, 1.4vw, 18px);
  font-family: var(--k-display);
  font-weight: 800;
  font-size: calc(var(--k-row) * .34);
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--k-muted);
}
.k-odometer[data-kinetic="wait"] { animation: none; }
.k-odometer[data-kinetic="wait"] .k-strip { transform: none; }
.k-odometer[data-kinetic=""] .k-reel:nth-child(1) .k-strip,
.k-odometer[data-kinetic="run"] .k-reel:nth-child(1) .k-strip { animation: k-odo-a 10s linear infinite; }
.k-odometer[data-kinetic=""] .k-reel:nth-child(2) .k-strip,
.k-odometer[data-kinetic="run"] .k-reel:nth-child(2) .k-strip { animation: k-odo-b 10s linear infinite; }
.k-odometer[data-kinetic=""] .k-reel:nth-child(3) .k-strip,
.k-odometer[data-kinetic="run"] .k-reel:nth-child(3) .k-strip { animation: k-odo-c 10s linear infinite; }
.k-odometer[data-kinetic=""] .k-reel:nth-child(4) .k-strip,
.k-odometer[data-kinetic="run"] .k-reel:nth-child(4) .k-strip { animation: k-odo-d 10s linear infinite; }
/* Each reel starts a little after the last and lands a little after it, with a small settle. */
@keyframes k-odo-a {
  0%, 6% { transform: none; filter: blur(0); animation-timing-function: cubic-bezier(.2, .7, .15, 1.06); }
  14% { filter: blur(3px); }
  26%, 100% { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))); filter: blur(0); }
}
@keyframes k-odo-b {
  0%, 7% { transform: none; filter: blur(0); animation-timing-function: cubic-bezier(.2, .7, .15, 1.06); }
  16% { filter: blur(4px); }
  30%, 100% { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))); filter: blur(0); }
}
@keyframes k-odo-c {
  0%, 8% { transform: none; filter: blur(0); animation-timing-function: cubic-bezier(.2, .7, .15, 1.06); }
  18% { filter: blur(5px); }
  34%, 100% { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))); filter: blur(0); }
}
@keyframes k-odo-d {
  0%, 9% { transform: none; filter: blur(0); animation-timing-function: cubic-bezier(.2, .7, .15, 1.06); }
  20% { filter: blur(6px); }
  38%, 100% { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))); filter: blur(0); }
}
@keyframes k-loop-fade {
  0%, 2% { opacity: 0; }
  5%, 94% { opacity: 1; }
  97%, 100% { opacity: 0; }
}

/* Roulette: a slot reel of assistants spins, overshoots, and settles on YOUR CHOICE in red. */
.k-roulette {
  --k-row: clamp(44px, 6.4vw, 84px);
  position: relative;
  padding: 0 clamp(28px, 4vw, 48px);
  animation: k-loop-fade 9s linear infinite;
}
.k-roulette .k-strip > li {
  font-size: calc(var(--k-row) * .78);
  letter-spacing: -.03em;
  text-transform: uppercase;
  color: var(--k-ink);
  opacity: .8;
}
.k-roulette .k-strip > li.k-choice { color: var(--k-red); opacity: 1; }
.k-roulette::before,
.k-roulette::after {
  content: "";
  position: absolute;
  top: calc(50% - 8px);
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  opacity: .9;
}
.k-roulette::before { left: 0; border-left: 14px solid var(--k-accent); }
.k-roulette::after { right: 0; border-right: 14px solid var(--k-accent); }
.k-roulette .k-reel::before,
.k-roulette .k-reel::after {
  content: "";
  position: absolute;
  left: 6%;
  right: 6%;
  height: 1px;
  background: var(--k-line);
}
.k-roulette .k-reel::before { top: var(--k-row); }
.k-roulette .k-reel::after { top: calc(var(--k-row) * 2); }
.k-roulette .k-reel { --k-rows: 11; }
.k-roulette[data-kinetic="wait"] { animation: none; }
.k-roulette[data-kinetic="wait"] .k-strip { transform: none; }
.k-roulette[data-kinetic=""] .k-strip,
.k-roulette[data-kinetic="run"] .k-strip { animation: k-roulette-spin 9s linear infinite; }
.k-roulette[data-kinetic=""] .k-choice,
.k-roulette[data-kinetic="run"] .k-choice { animation: k-roulette-land 9s linear infinite; }
@keyframes k-roulette-spin {
  0%, 6% { transform: none; filter: blur(0); animation-timing-function: cubic-bezier(.2, .75, .2, 1.08); }
  16% { filter: blur(5px); }
  42%, 100% { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))); filter: blur(0); }
}
@keyframes k-roulette-land {
  0%, 39% { text-shadow: none; transform: none; }
  43% { text-shadow: 0 0 34px var(--k-red-glow); transform: scale(1.05); }
  52%, 100% { text-shadow: 0 0 18px var(--k-red-glow); transform: none; }
}

/* ---------------------------------------------------------------------------
   Decoder. /kinetic.js wraps each character of a [data-decode] element and
   cycles it through glyphs before locking it, left to right. Without the
   script the text simply reads. */
.k-decode { display: inline-block; white-space: pre; }
.k-glyph { display: inline-block; text-align: center; }
.k-glyph.is-scrambling { min-width: .58em; font-family: var(--k-mono); color: var(--k-accent); opacity: .55; font-size: .78em; vertical-align: .06em; }
.k-glyph.is-locked { animation: k-lock .28s var(--k-out) both; }
@keyframes k-lock { from { transform: scale(1.1); } to { transform: none; } }

/* ---------------------------------------------------------------------------
   Rendered loops. The stage frames a <video> whose source /kinetic.js picks for
   the visitor's palette (/renders/<name>-light.mp4 or -dark.mp4). Its poster is
   what shows with scripting off, before the first frame, and under reduced
   motion. The aspect ratio is set here so the box is right before any media
   loads. */
.render-stage {
  position: relative;
  margin: 0;
  border: 1px solid var(--k-line);
  border-radius: 30px 30px 10px 30px;
  background: var(--k-lift);
  box-shadow: var(--shadow-md, 0 24px 64px -22px rgba(0, 0, 0, .18));
  overflow: hidden;
  isolation: isolate;
}
.render-stage video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 8 / 5;
  object-fit: cover;
  background: var(--k-lift);
}
.render-stage[data-render^="popover"] video { aspect-ratio: 4 / 5; }
.render-caption {
  position: absolute;
  z-index: 2;
  left: 18px;
  bottom: 16px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border: 1px solid var(--k-line);
  border-radius: 999px;
  background: var(--k-lift);
  color: var(--k-muted);
  font-family: var(--k-mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
}
.render-caption i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--k-teal);
  box-shadow: 0 0 0 4px rgba(44, 136, 136, .14);
}
.render-stage.is-playing .render-caption i { animation: k-live 1.6s ease-in-out infinite; }
@keyframes k-live { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* Loops are paused while off screen; they keep their place and pick up where they were. */
[data-kinetic="pause"],
[data-kinetic="pause"] * { animation-play-state: paused !important; }

/* Reduced motion: every piece in its settled state, nothing moves, nothing loads. */
@media (prefers-reduced-motion: reduce) {
  .k, .k *, .k::before, .k::after, .k *::before, .k *::after { animation: none !important; transition: none !important; }
  .k-stack > li { opacity: 1 !important; }
  .k-flap .k-cell > i:nth-child(-n+4)::before { transform: perspective(var(--k-persp)) rotateX(-90deg) !important; opacity: 0 !important; }
  .k-flap .k-cell > i:nth-child(5)::before, .k-flap .k-cell > i:nth-child(6)::before { transform: none !important; opacity: 1 !important; }
  .k-flap .k-cell > i:nth-child(n+2)::after { transform: perspective(var(--k-persp)) rotateX(90deg) !important; }
  .k-flap .k-cell > i:nth-child(5)::after { transform: none !important; }
  .k-strip { transform: translateY(calc(-1 * (var(--k-rows) - 1) * var(--k-row))) !important; filter: none !important; }
  .k-sweep-r, .k-sweep-l { opacity: 1 !important; transform: none !important; }
  .k-roulette .k-choice { text-shadow: 0 0 18px var(--k-red-glow); }
}
