/* Demo pieces: keyframes and per-piece styles. Every CSS-driven piece is
   gated by .is-running on its root so it only moves when the gallery says so. */

/* ===== Walk cycle rig ===== */
.walk { position: absolute; inset: 0; }
.piece .walk { bottom: 2.7rem; }
.walk svg { width: 100%; height: 100%; overflow: visible; }
.walk .j-thigh, .walk .j-shin, .walk .j-uarm, .walk .j-farm, .walk .j-head, .walk .j-bob, .walk .ground, .walk .lid { animation-duration: 1s; animation-iteration-count: infinite; animation-timing-function: ease-in-out; animation-delay: calc(var(--delay, 0s) + var(--g, 0s)); }
.walk.pose-walk .j-thigh { animation-name: walk-thigh; }
.walk.pose-walk .j-shin  { animation-name: walk-shin; }
.walk.pose-walk .j-uarm  { animation-name: walk-uarm; }
.walk.pose-walk .j-farm  { animation-name: walk-farm; }
.walk.pose-walk .j-bob   { animation-name: walk-bob; }
.walk.pose-walk .j-head  { animation-name: walk-head; }
.walk.pose-walk .ground  { animation-name: walk-ground; animation-timing-function: linear; }
.walk .lid { animation-name: blink-lid; animation-duration: 4.2s; animation-timing-function: linear; transform: scaleY(0); }
.walk.on-twos .j-thigh, .walk.on-twos .j-shin, .walk.on-twos .j-uarm, .walk.on-twos .j-farm, .walk.on-twos .j-bob, .walk.on-twos .j-head { animation-timing-function: steps(3, end); }
.walk.on-twos .ground { animation-timing-function: steps(12, end); }
.walk:not(.is-running) .j-thigh, .walk:not(.is-running) .j-shin, .walk:not(.is-running) .j-uarm, .walk:not(.is-running) .j-farm, .walk:not(.is-running) .j-bob, .walk:not(.is-running) .j-head, .walk:not(.is-running) .ground, .walk:not(.is-running) .lid { animation-play-state: paused; }
.walk .ghost { display: none; }
.walk.show-onion .ghost { display: inline; }
.walk .ghost * { fill: none !important; stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.walk .ghost-a * { stroke: var(--reg) !important; opacity: .55; }
.walk .ghost-b * { stroke: var(--pencil) !important; opacity: .45; }
.walk .ghost .lid, .walk .ghost .pupil, .walk .ghost .eye-white, .walk .ghost .ground { display: none; }
.walk .pupils { transition: transform .25s var(--ease-out); }
@keyframes walk-thigh { 0% { transform: rotate(-30deg); } 25% { transform: rotate(0deg); } 50% { transform: rotate(30deg); } 75% { transform: rotate(0deg); } 100% { transform: rotate(-30deg); } }
@keyframes walk-shin  { 0% { transform: rotate(0deg); } 25% { transform: rotate(6deg); } 50% { transform: rotate(18deg); } 75% { transform: rotate(58deg); } 100% { transform: rotate(0deg); } }
@keyframes walk-uarm  { 0% { transform: rotate(28deg); } 25% { transform: rotate(0deg); } 50% { transform: rotate(-28deg); } 75% { transform: rotate(0deg); } 100% { transform: rotate(28deg); } }
@keyframes walk-farm  { 0% { transform: rotate(-8deg); } 25% { transform: rotate(-22deg); } 50% { transform: rotate(-46deg); } 75% { transform: rotate(-22deg); } 100% { transform: rotate(-8deg); } }
@keyframes walk-bob   { 0% { transform: translateY(4px); } 25% { transform: translateY(-5px); } 50% { transform: translateY(4px); } 75% { transform: translateY(-5px); } 100% { transform: translateY(4px); } }
@keyframes walk-head  { 0% { transform: rotate(2.5deg); } 25% { transform: rotate(-2deg); } 50% { transform: rotate(2.5deg); } 75% { transform: rotate(-2deg); } 100% { transform: rotate(2.5deg); } }
@keyframes walk-ground { to { stroke-dashoffset: -96; } }
@keyframes blink-lid { 0%, 90%, 100% { transform: scaleY(0); } 93%, 97% { transform: scaleY(1); } }
/* Idle pose: a slow breath and a head tilt, used for the portrait and 404 */
.walk.pose-idle .j-bob  { animation-name: idle-bob; animation-duration: 3.2s; }
.walk.pose-idle .j-head { animation-name: idle-head; animation-duration: 3.2s; }
.walk.pose-idle .j-uarm { animation-name: idle-arm; animation-duration: 3.2s; }
.walk.pose-idle .j-farm { animation-name: idle-farm; animation-duration: 3.2s; }
.walk.pose-idle .j-thigh, .walk.pose-idle .j-shin, .walk.pose-idle .ground { animation: none; }
.walk.pose-idle .leg-r .j-thigh { transform: rotate(8deg); }
.walk.pose-idle .leg-l .j-thigh { transform: rotate(-6deg); }
.walk.pose-idle .leg-l .j-shin { transform: rotate(4deg); }
.walk.pose-idle .arm-r .j-uarm { transform: rotate(-10deg); }
@keyframes idle-bob  { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes idle-head { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(2deg); } }
@keyframes idle-arm  { 0%, 100% { transform: rotate(6deg); } 50% { transform: rotate(2deg); } }
@keyframes idle-farm { 0%, 100% { transform: rotate(-10deg); } 50% { transform: rotate(-16deg); } }

/* ===== Bouncing-ball lab ===== */
.pr { position: absolute; inset: 0; display: flex; gap: .4rem; padding: .5rem .5rem 3rem; }
.pr-main { flex: 1; min-width: 0; height: 100%; }
.pr-side { width: 112px; flex: none; display: flex; flex-direction: column; gap: .35rem; }
.pr-editor { width: 100%; aspect-ratio: 1; background: rgba(18, 15, 23, .6); border: 1px solid rgba(255, 255, 255, .1); border-radius: 8px; touch-action: none; }
.pr-editor .grid line { stroke: rgba(255, 255, 255, .07); }
.pr-editor .curve { fill: none; stroke: var(--cel); stroke-width: 2.5; stroke-linecap: round; }
.pr-editor .arm { stroke: var(--pencil); stroke-width: 1.5; opacity: .8; }
.pr-editor .handle { fill: var(--pencil); stroke: var(--ink); stroke-width: 2; cursor: grab; }
.pr-editor .handle:focus-visible { outline: none; stroke: var(--paper); }
.pr-editor .handle:active { cursor: grabbing; }
.piece .pr-presets { display: none; }
.pr-presets { display: grid; grid-template-columns: repeat(3, 1fr); gap: .25rem; }
.pr-presets button { font: inherit; font-size: .62rem; font-weight: 700; letter-spacing: .02em; padding: .3rem .2rem; border-radius: 6px; border: 1px solid rgba(255, 255, 255, .12); background: rgba(18, 15, 23, .6); color: var(--muted-on-ink); cursor: pointer; min-height: 28px; }
.pr-presets button:hover { color: var(--pencil); border-color: var(--pencil); }
.pr-presets button[aria-pressed="true"] { background: var(--cel); color: var(--ink); border-color: var(--cel); }
.pr .floor { stroke: var(--paper); stroke-width: 2; opacity: .7; }
.pr .arc { fill: none; stroke: var(--pencil); stroke-width: 1.5; stroke-dasharray: 3 5; opacity: .7; }
.pr .ball { fill: var(--cel); }
.pr .ball-hi { fill: #ffd2b8; opacity: .9; }
.pr .shadow { fill: #000; opacity: .35; }
.pr .chart-line { stroke: rgba(255, 255, 255, .15); stroke-width: 1; }
.pr .chart-dot { fill: var(--pencil); }
.pr .chart-dot.key { fill: var(--cel); }
.pr .chart-label, .pr .lbl { fill: var(--muted-on-ink); font: 700 10px var(--font-body); letter-spacing: .08em; }
.pr .note { fill: var(--pencil); font: 600 15px var(--font-pencil); }
@media (max-width: 420px) { .pr-side { width: 86px; } }

/* ===== Sprite sheet ===== */
.sp { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .5rem; padding: .5rem .6rem 3rem; }
.piece .sp-view { width: 108px; height: 144px; background-size: 1296px 144px; animation-name: sprite-play-sm; }
@keyframes sprite-play-sm { to { background-position: -1296px 0; } }
.sp-view { width: 144px; height: 192px; background-repeat: no-repeat; background-size: 1728px 192px; animation: sprite-play var(--dur, 1s) steps(12, end) infinite; filter: drop-shadow(0 0 18px rgba(255, 122, 61, .35)); }
.sp-strip { position: relative; width: 100%; max-width: 560px; aspect-ratio: 9 / 1; background-repeat: no-repeat; background-size: 100% 100%; border-radius: 4px; border: 1px solid rgba(255, 255, 255, .1); background-color: rgba(18, 15, 23, .5); }
.sp-win { position: absolute; top: -2px; bottom: -2px; left: 0; width: calc(100% / 12); border: 2px solid var(--cel); border-radius: 3px; box-shadow: 0 0 0 2px rgba(255, 122, 61, .25); animation: sprite-win var(--dur, 1s) steps(12, end) infinite; }
.sp:not(.is-running) .sp-view, .sp:not(.is-running) .sp-win { animation-play-state: paused; }
@keyframes sprite-play { to { background-position: -1728px 0; } }
@keyframes sprite-win { to { transform: translateX(1200%); } }
.lb-stage .sp-view { width: 192px; height: 256px; background-size: 2304px 256px; animation-name: sprite-play-lg; }
@keyframes sprite-play-lg { to { background-position: -2304px 0; } }

/* ===== Flipbook ===== */
.fb { position: absolute; inset: 0; padding-bottom: 3rem; outline: none; touch-action: pan-y; cursor: ew-resize; }
.fb:focus-visible { box-shadow: inset 0 0 0 3px var(--pencil); }
.fb svg { width: 100%; height: 100%; }
.fb .frame { fill: none; stroke: var(--paper); stroke-width: 3; stroke-linejoin: round; }
.fb .ghost { fill: none; stroke-width: 2; stroke-linejoin: round; display: none; }
.fb.show-onion .ghost { display: inline; }
.fb .ghost-p1 { stroke: var(--reg); opacity: .5; } .fb .ghost-p2 { stroke: var(--reg); opacity: .22; }
.fb .ghost-n1 { stroke: var(--pencil); opacity: .5; } .fb .ghost-n2 { stroke: var(--pencil); opacity: .22; }
.fb .fb-corner { fill: none; stroke: rgba(255, 255, 255, .2); stroke-width: 1.5; }
.fb .fb-num { fill: var(--muted-on-ink); font: 600 16px var(--font-pencil); }
.fb .fb-hint { fill: var(--muted-on-ink); font: 700 9px var(--font-body); letter-spacing: .1em; }
.fb-controls input[type="range"] { flex: 1; min-width: 80px; width: auto; }

/* ===== Particles ===== */
.pt { position: absolute; inset: 0; }
.pt canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
.pt .pt-hint { position: absolute; right: .7rem; top: .5rem; font-family: var(--font-pencil); font-size: 1.1rem; color: var(--pencil); opacity: .8; pointer-events: none; }

/* ===== Title card ===== */
.tc { position: absolute; inset: 0; overflow: hidden; background: radial-gradient(900px 400px at 80% 20%, rgba(127, 184, 255, .18), transparent 60%), linear-gradient(135deg, #18131f, #0e0b12); font-family: var(--font-display); }
.tc-sweep { position: absolute; inset: -40% -60%; background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, .06) 50%, transparent 60%); animation: tc-sweep 6s linear infinite; }
.tc-safe { position: absolute; inset: 9% 6%; border: 1px dashed rgba(255, 255, 255, .14); pointer-events: none; }
.tc-safe::before { content: "ACTION SAFE"; position: absolute; left: .4rem; top: -.65rem; font: 700 .55rem var(--font-body); letter-spacing: .14em; color: rgba(255, 255, 255, .3); background: #120f17; padding: 0 .3rem; }
.tc-rec { position: absolute; right: 9%; top: 11%; display: flex; align-items: center; gap: .4rem; font: 700 .65rem var(--font-body); letter-spacing: .12em; color: rgba(255, 255, 255, .55); font-variant-numeric: tabular-nums; }
.tc-rec::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--reg); animation: blink 1s steps(2, end) infinite; }
.tc-lower { position: absolute; left: 9%; bottom: 20%; }
.piece .tc-lower { bottom: 34%; }
.tc-lower { display: flex; align-items: stretch; gap: .9rem; }
.tc-bar { width: 6px; border-radius: 3px; background: var(--cel); transform: scaleY(0); transform-origin: bottom; }
.tc-text { display: flex; flex-direction: column; justify-content: center; }
.tc-name, .tc-role { overflow: hidden; }
.tc-name span, .tc-role span { display: block; transform: translateY(110%); }
.tc-name span { font-size: clamp(1.4rem, 6cqw, 2.6rem); font-weight: 800; letter-spacing: -.02em; line-height: 1.05; color: var(--paper); }
.tc-role span { font: 500 clamp(.72rem, 2.6cqw, 1rem) var(--font-body); letter-spacing: .14em; text-transform: uppercase; color: var(--pencil); margin-top: .2rem; }
.tc-dot { position: absolute; right: -1.6rem; top: -.4rem; width: 14px; height: 14px; border-radius: 50%; background: var(--cel); transform: scale(0); }
.tc-plate { position: absolute; left: 0; right: -1rem; bottom: -.5rem; height: 3px; background: linear-gradient(90deg, var(--cel), transparent); transform: scaleX(0); transform-origin: left; }
.tc.tc-go .tc-bar { animation: tc-bar 6s var(--ease-out) infinite; }
.tc.tc-go .tc-name span { animation: tc-rise 6s var(--ease-spring) infinite; }
.tc.tc-go .tc-role span { animation: tc-rise 6s var(--ease-spring) .12s infinite; }
.tc.tc-go .tc-dot { animation: tc-dot 6s var(--ease-spring) infinite; }
.tc.tc-go .tc-plate { animation: tc-plate 6s var(--ease-out) infinite; }
.tc:not(.is-running) .tc-bar, .tc:not(.is-running) .tc-name span, .tc:not(.is-running) .tc-role span, .tc:not(.is-running) .tc-dot, .tc:not(.is-running) .tc-plate, .tc:not(.is-running) .tc-sweep, .tc:not(.is-running) .tc-rec::before { animation-play-state: paused; }
@keyframes tc-bar   { 0% { transform: scaleY(0); } 8% { transform: scaleY(1); } 86% { transform: scaleY(1); transform-origin: top; } 92%, 100% { transform: scaleY(0); transform-origin: top; } }
@keyframes tc-rise  { 0%, 6% { transform: translateY(110%); } 16% { transform: translateY(0); } 84% { transform: translateY(0); } 92%, 100% { transform: translateY(-110%); } }
@keyframes tc-dot   { 0%, 10% { transform: scale(0); } 18% { transform: scale(1); } 50% { transform: scale(1.25); } 82% { transform: scale(1); } 88%, 100% { transform: scale(0); } }
@keyframes tc-plate { 0%, 12% { transform: scaleX(0); } 24% { transform: scaleX(1); } 84% { transform: scaleX(1); } 90%, 100% { transform: scaleX(0); } }
@keyframes tc-sweep { from { transform: translateX(-30%); } to { transform: translateX(30%); } }
.stage { container-type: inline-size; }

/* ===== Reduced motion: the pieces hold their frame unless Play is pressed ===== */
@media (prefers-reduced-motion: reduce) {
  .walk .pupils { transition: none; }
  .tc-rec::before { animation: none; }
}
