/* ============================================================
   BRAUN HAUS - TRANSITION SPACE
   Tube-light response to an NFC bracelet tap.

   PROPOSALS. Not in the prototype - motion.css is the shipped
   layer and stays verbatim. This file is the new work, kept
   separate so the two never get confused.

   The rig: 5-6 vertical tubes on the wall, tablet mounted in
   front of them. A guest taps the reader; the wall answers.

   Every timing below uses --ease from motion.css. Nothing here
   introduces a second curve.
   ============================================================ */

:root{
  --tube-off:#241a10;          /* the idle ember, from idletube */
  --tube-on:#fff4e2;           /* full warm white, from .tube.on */
  --tube-glow:rgba(255,224,178,.75);
  --tube-glow-hot:rgba(255,240,214,1);
}

/* ---- Rig ----------------------------------------------------
   Tubes carry two custom properties, set at render time:
     --i  index, left to right   (0..n-1)  drives directional stagger
     --c  rings out from centre  (0..2)    drives symmetrical stagger
   For 6 tubes --c is 2,1,0,0,1,2. For 5 it is 2,1,0,1,2.
   The <b> inside each tube is the fill element. Only Level uses it. */

.rig__tubes i{
  position:relative;width:9px;border-radius:5px;overflow:hidden;
  background:var(--tube-off);box-shadow:none;
  animation:idletube 3s ease-in-out infinite;
  animation-delay:calc(var(--i) * .2s);
}
.rig__tubes i b{
  position:absolute;left:0;right:0;bottom:0;height:100%;border-radius:5px;
  background:linear-gradient(to top,var(--tube-on) 0 70%,#ffe9c8 100%);
  transform:scaleY(0);transform-origin:bottom;opacity:0;
}

/* ---- 1. STRIKE -----------------------------------------------
   Sequential ignition, left to right, 90ms apart. Each tube does a
   fluorescent stutter before it holds. Characterful, mechanical.
   Risk: a stutter reads as a fault as easily as a flourish. */
@keyframes tapStrike{
  0%       {background:var(--tube-off);box-shadow:none}
  6%       {background:var(--tube-on);box-shadow:0 0 22px 4px rgba(255,224,178,.6)}
  10%      {background:#3a2a18;box-shadow:none}
  16%      {background:var(--tube-on);box-shadow:0 0 26px 5px var(--tube-glow)}
  22%      {background:#4a331c;box-shadow:0 0 6px 1px rgba(255,224,178,.2)}
  30%,72%  {background:var(--tube-on);box-shadow:0 0 26px 5px var(--tube-glow)}
  100%     {background:var(--tube-off);box-shadow:none}
}
.rig--strike.tap i{animation:tapStrike 1.9s var(--ease) both;animation-delay:calc(var(--i) * 90ms)}

/* ---- 2. BLOOM ------------------------------------------------
   Centre out. The pair nearest the tablet lights first, then each
   ring outward, 130ms apart. The lit segment draws back to 86% before
   it grows, so the wall gathers before it answers. The light starts
   where the hand is.

   Rest, 0% and 100% are all scaleY(1) on purpose. The tube at rest is
   a full-length fixture, so any keyframe that starts or ends short
   snaps the moment the class goes on or comes off. Keep them equal. */
@keyframes tapBloom{
  0%       {background:var(--tube-off);box-shadow:none;transform:scaleY(1)}
  10%      {background:#1a1209;box-shadow:none;transform:scaleY(.86)}
  40%      {background:var(--tube-on);box-shadow:0 0 30px 6px rgba(255,224,178,.8);transform:scaleY(1)}
  72%      {background:var(--tube-on);box-shadow:0 0 26px 5px var(--tube-glow);transform:scaleY(1)}
  100%     {background:var(--tube-off);box-shadow:none;transform:scaleY(1)}
}
.rig--bloom.tap i{animation:tapBloom 2s var(--ease) both;animation-delay:calc(var(--c) * 130ms)}

/* ---- 3. SWEEP ------------------------------------------------
   A bright band travels the wall, each tube overshooting to #fff8ec
   before falling back and settling. 55ms apart, so the settle reads
   as near-simultaneous. This is the wall reading the bracelet. */
@keyframes tapSweep{
  0%       {background:var(--tube-off);box-shadow:none}
  12%      {background:#fff8ec;box-shadow:0 0 34px 8px rgba(255,236,200,.95)}
  26%      {background:#6b4a26;box-shadow:0 0 8px 1px rgba(255,224,178,.25)}
  46%,74%  {background:var(--tube-on);box-shadow:0 0 26px 5px var(--tube-glow)}
  100%     {background:var(--tube-off);box-shadow:none}
}
.rig--sweep.tap i{animation:tapSweep 2s var(--ease) both;animation-delay:calc(var(--i) * 55ms)}

/* ---- 4. CHARGE -----------------------------------------------
   No stagger. The whole wall drops below its idle for 200ms, then
   flares past full and settles. One decisive beat. The dip is what
   sells it: the room goes quiet before it answers. */
@keyframes tapCharge{
  0%       {background:var(--tube-off);box-shadow:none}
  14%      {background:#0d0906;box-shadow:none}
  26%      {background:#fffaf0;box-shadow:0 0 46px 12px var(--tube-glow-hot)}
  40%,72%  {background:var(--tube-on);box-shadow:0 0 26px 5px var(--tube-glow)}
  100%     {background:var(--tube-off);box-shadow:none}
}
.rig--charge.tap i{animation:tapCharge 1.7s var(--ease) both}

/* ---- 5. LEVEL ------------------------------------------------
   Light rises inside each tube, bottom to top, 70ms apart. The only
   option that reads as duration rather than acknowledgement - use it
   if something is actually loading behind the tap. Glow on the tube,
   fill on the <b>. */
@keyframes tapLevelFill{
  0%       {transform:scaleY(0);opacity:0}
  12%      {opacity:1}
  55%,74%  {transform:scaleY(1);opacity:1}
  100%     {transform:scaleY(0);opacity:0}
}
@keyframes tapLevelGlow{
  0%       {box-shadow:none}
  55%,74%  {box-shadow:0 0 24px 5px rgba(255,224,178,.6)}
  100%     {box-shadow:none}
}
.rig--level.tap i{background:var(--tube-off);animation:tapLevelGlow 2.1s var(--ease) both;animation-delay:calc(var(--i) * 70ms)}
.rig--level.tap i b{animation:tapLevelFill 2.1s var(--ease) both;animation-delay:calc(var(--i) * 70ms)}

/* ---- 6. RIPPLE -----------------------------------------------
   Two passes outward from centre, 110ms per ring. The signal made
   visible - the wall radiates what the bracelet just sent.
   Iteration count 2 with a fixed per-tube delay keeps the second
   pass reading outward like the first. */
@keyframes tapRipple{
  0%       {background:var(--tube-off);box-shadow:none}
  22%      {background:var(--tube-on);box-shadow:0 0 30px 6px rgba(255,224,178,.85)}
  55%,100% {background:var(--tube-off);box-shadow:none}
}
.rig--ripple.tap i{animation:tapRipple .95s var(--ease) 2 both;animation-delay:calc(var(--c) * 110ms)}

/* ---- 7. REJECT -----------------------------------------------
   Bracelet not read. Two sharp dips in --orange, the single accent
   already in the build. Deliberately shorter and colder than any
   accept state, so the two can never be mistaken across a room. */
@keyframes tapReject{
  0%       {background:var(--tube-off);box-shadow:none}
  12%      {background:#EA5B0C;box-shadow:0 0 22px 4px rgba(234,91,12,.6)}
  24%      {background:var(--tube-off);box-shadow:none}
  36%      {background:#EA5B0C;box-shadow:0 0 22px 4px rgba(234,91,12,.6)}
  50%,100% {background:var(--tube-off);box-shadow:none}
}
.rig--reject.tap i{animation:tapReject 1.1s var(--ease) both}

/* ---- 8. COMPOSED ---------------------------------------------
   The three moments of a tap, built from the options above:
     s1  detect   200ms   the dip from Charge. Instant, unmissable.
     s2  read     1000ms  the band from Sweep. Covers the lookup.
     s3  confirm  1350ms  the centre-out settle from Bloom, held.
   Driven by JS class swaps, because it is a state machine, not a
   loop. See the JS panel on the demo card. */
@keyframes seqDetect{
  0%   {background:var(--tube-off);box-shadow:none}
  100% {background:#0d0906;box-shadow:none}
}
@keyframes seqScan{
  0%       {background:#0d0906;box-shadow:none;transform:scaleY(1)}
  30%      {background:#fff8ec;box-shadow:0 0 34px 8px rgba(255,236,200,.95);transform:scaleY(1)}
  60%,100% {background:#4a331c;box-shadow:0 0 8px 1px rgba(255,224,178,.25);transform:scaleY(.92)}
}
@keyframes seqHold{
  0%       {background:#4a331c;box-shadow:0 0 8px 1px rgba(255,224,178,.25);transform:scaleY(.92)}
  26%,66%  {background:var(--tube-on);box-shadow:0 0 30px 6px rgba(255,224,178,.8);transform:scaleY(1)}
  100%     {background:var(--tube-off);box-shadow:none;transform:scaleY(1)}
}
.rig--seq.tap.s1 i{animation:seqDetect .2s var(--ease) both}
.rig--seq.tap.s2 i{animation:seqScan .7s var(--ease) both;animation-delay:calc(var(--i) * 55ms)}
.rig--seq.tap.s3 i{animation:seqHold 1.2s var(--ease) both;animation-delay:calc(var(--c) * 90ms)}

/* ---- Reduced motion ------------------------------------------
   A tap still has to answer, so this fallback does not remove the
   feedback - it removes the travel. No stagger, no stutter, no
   overshoot: every tube cross-fades to full together and holds.
   Same information, one move. */
@media (prefers-reduced-motion:reduce){
  .rig__tubes i,.rig__tubes i b{animation:none !important}
  .rig__tubes i{transition:background .2s var(--ease),box-shadow .2s var(--ease)}
  .rig.tap i{background:var(--tube-on) !important;box-shadow:0 0 20px 4px rgba(255,224,178,.55) !important}
  .rig--reject.tap i{background:#EA5B0C !important;box-shadow:0 0 18px 4px rgba(234,91,12,.5) !important}
}

/* ---- 9. GRADIENT FIELD ---------------------------------------
   The same two responses treated as one continuous surface rather
   than discrete tubes: a gradient across the whole screen.

   Warm white carries it and amber is only the falloff edge. The
   dawn palette from motion.css is the right family but the wrong
   proportion for a 2s acknowledgement: run it at full strength and
   the screen reads as a sunset, not as a light coming on.

   The spread is horizontal. scaleX travels further than scaleY, so
   the light crosses the screen rather than swelling in place, which
   is what carries the centre-out read once the tubes are gone.

   Both layers start and end at opacity 0, which is what makes the
   files loop-safe: rest, first frame and last frame are the same
   black field, so a cut back to an idle loop has nothing to jump. */

.scr{position:relative;overflow:hidden;background:#0a0a0a}
.scr .g{position:absolute;inset:0;opacity:0;will-change:opacity,transform}
.scr .g--core{background:radial-gradient(64% 86% at 50% 50%,
  #fff4e2 0%,#ffeacb 24%,#f4cf9b 42%,#d98a41 62%,#6d2f0e 78%,rgba(10,10,10,0) 94%)}
.scr .g--halo{background:radial-gradient(96% 128% at 50% 50%,
  #e8a45c 0%,#8a3a12 38%,rgba(10,10,10,0) 72%)}
.scr .g--rej{background:radial-gradient(78% 104% at 50% 50%,
  #EA5B0C 0%,#a83a08 46%,rgba(10,10,10,0) 82%)}

/* Bloom. The core carries the read; the halo trails it by 90ms and
   never passes 38% opacity, which is what stops the whole thing
   reading as a flat wash. The 10% keyframe is the same inhale as the
   tube version: it contracts before it grows. Peak scale stays under
   1 so the light never quite reaches the edges of the frame. */
@keyframes gradBloomCore{
  0%   {opacity:0;   transform:scale(.20,.42)}
  10%  {opacity:0;   transform:scale(.15,.38)}
  40%  {opacity:1;   transform:scale(.92,.80)}
  72%  {opacity:.92; transform:scale(.97,.83)}
  100% {opacity:0;   transform:scale(1.06,.90)}
}
@keyframes gradBloomHalo{
  0%   {opacity:0;   transform:scale(.36,.52)}
  10%  {opacity:0;   transform:scale(.30,.48)}
  46%  {opacity:.38; transform:scale(1.02,.88)}
  72%  {opacity:.28; transform:scale(1.10,.93)}
  100% {opacity:0;   transform:scale(1.22,1)}
}
.scr--bloom.tap .g--core{animation:gradBloomCore 2s var(--ease) both}
.scr--bloom.tap .g--halo{animation:gradBloomHalo 2s var(--ease) 90ms both}

/* Reject. Two dips, no travel, no scale: the field itself flashes.
   Deliberately flat where Bloom expands, so the two never read alike. */
@keyframes gradReject{
  0%       {opacity:0}
  12%      {opacity:1}
  24%      {opacity:0}
  36%      {opacity:1}
  50%,100% {opacity:0}
}
.scr--reject.tap .g--rej{animation:gradReject 1.1s var(--ease) both}

@media (prefers-reduced-motion:reduce){
  .scr .g{animation:none !important;transform:none !important}
  .scr.tap .g--core,.scr.tap .g--rej{opacity:1}
}
