/* ==========================================================================
   ONI STORM 2 — hero title screen
   Layer order: .bg (fog · beam · embers · motes · vignette · grain) → .stage
   (logo · katana · jp row) → wisps · .hint · .copy · .flash · .slash.
   Animate transform/opacity only.
   Pointer parallax is expressed with the `translate` property, so every
   element's `transform` stays free for its own CSS animation / press state.
   ========================================================================== */

:root{
  --ink:#050506;  --ink-2:#0d0c10;  --ink-3:#17141b;
  --gold-hi:#fff6e0; --gold-1:#f0d79f; --gold-2:#d3a854; --gold-3:#9a6f26; --gold-4:#6e4a12;
  --red-hi:#ff8f7d; --red-1:#e23a3a; --red-2:#b8102a; --red-3:#7e0a1b;
  --ivory:#f4e7cd; --text-mute:#c8b697; --text-dim:#8b8172; --copy:#877b6c;

  --font-title:"OniStorm",Georgia,"Times New Roman",serif;
  --font-num:"AmanojakuNum","OniStorm",Georgia,serif;
  --font-jp:"ZenMinchoTitle","Yu Mincho",serif;
  --font-jp-ui:"ZenMinchoUI","Yu Mincho",serif;
  --font-ui:"Segoe UI",system-ui,-apple-system,sans-serif;
}

*,*::before,*::after{margin:0;box-sizing:border-box}

html,body{height:100%}
body{background:var(--ink);color:var(--ivory);font-family:var(--font-ui);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
img{display:block;max-width:100%}

/* ---------- 4. layout ---------------------------------------------------- */
.hero{
  position:relative;overflow:hidden;isolation:isolate;
  min-height:100vh;min-height:100dvh;
  background:radial-gradient(120% 85% at 50% 6%,#1a141c 0%,#0b0a0d 48%,#050506 100%);
}
.stage{
  position:absolute;inset:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(10px,1.8vmin,22px);
  transform:translateY(-0.8vh);text-align:center;
  transition:transform .9s ease;
}

/* ---------- 5. background ------------------------------------------------ */
.bg{position:absolute;inset:0;pointer-events:none}

/* 5.1b smoke video (pexels 9694807) + mandatory dark overlay so text stays readable */
.vid{position:absolute;inset:0;overflow:hidden;
  background:#040405 url(../assets/video/smoke-poster.jpg) center/cover no-repeat;
  opacity:0;animation:vid-in 2s ease .15s both}
.vid video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:brightness(.82) contrast(1.05) saturate(.82);
  animation:vid-breathe 52s ease-in-out infinite alternate}
.vid::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,.46),rgba(0,0,0,.38) 45%,rgba(0,0,0,.52))}

/* 5.1c red smoke — second video layer; `screen` blend drops its black
   background out so only the blood-red drift reads over the gray smoke.
   Decorative: no poster, and it simply disappears if the file is missing. */
.vid-red{position:absolute;inset:0;mix-blend-mode:screen;pointer-events:none;
  -webkit-mask-image:linear-gradient(90deg,transparent 26%,rgba(0,0,0,.34) 46%,#000 68%);
  mask-image:linear-gradient(90deg,transparent 26%,rgba(0,0,0,.34) 46%,#000 68%);
  opacity:0;animation:red-in 2s ease .4s both}
.vid-red video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(1.05) contrast(1.04);
  animation:red-drift 46s ease-in-out 1s infinite alternate}
@keyframes red-drift{from{transform:scale(1.06) translate3d(0,0,0)}
  to{transform:scale(1.12) translate3d(-1.6%,1%,0)}}
@keyframes red-in{from{opacity:0}to{opacity:.6}}
@supports not (mix-blend-mode: screen){.vid-red{display:none}}
@keyframes vid-in{from{opacity:0}to{opacity:1}}
@keyframes vid-breathe{from{transform:scale(1.02)}to{transform:scale(1.065)}}

/* 5.1d center scrim: darkens the middle band under the logo, where gray and
   red smoke overlap, so the gold keeps its contrast. Sits over both videos. */
.core{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(58% 44% at 50% 40%,rgba(4,4,6,.68),rgba(4,4,6,.34) 52%,transparent 76%)}
.core::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent 34%,rgba(4,4,6,.34) 50%,transparent 66%)}

/* 5.2 fog — container owns `translate` (JS parallax), ::before owns the drift */
.fog{position:absolute;inset:0;pointer-events:none;
  opacity:0;animation:fog-in 1.6s ease .1s both}
.fog::before{content:"";position:absolute;
  background:radial-gradient(closest-side,var(--fog-c),transparent 72%);
  width:var(--fog-w);height:var(--fog-h);left:var(--fog-x);top:var(--fog-y);
  animation:drift var(--fog-t) ease-in-out infinite alternate}
@keyframes drift{
  from{transform:translate3d(-2.5%,-1.5%,0) scale(1)}
  to{transform:translate3d(2.5%,2%,0) scale(1.06)}
}
@keyframes fog-in{from{opacity:0}to{opacity:.9}}

.f1{--fog-c:rgba(120,116,146,.12);--fog-w:70vmax;--fog-h:52vmax;--fog-x:-18%;--fog-y:-12%;--fog-t:74s}
.f2{--fog-c:rgba(148,130,118,.10);--fog-w:62vmax;--fog-h:46vmax;--fog-x:58%;--fog-y:-6%;--fog-t:88s}
.f3{--fog-c:rgba(98,104,126,.09);--fog-w:80vmax;--fog-h:54vmax;--fog-x:8%;--fog-y:52%;--fog-t:96s}

/* 5.3 beam */
.beam{position:absolute;inset:-12% 0 auto 50%;width:min(74vw,940px);height:82%;
  transform:translateX(-50%);
  background:radial-gradient(58% 100% at 50% 0%,rgba(255,212,160,.12),rgba(255,180,120,.05) 42%,transparent 74%);
  opacity:0;animation:beam-in 2s ease .3s both}
@keyframes beam-in{from{opacity:0}to{opacity:.7}}

/* 5.4 embers — 16 CSS-only sparks */
.embers{position:absolute;inset:0;pointer-events:none}
.embers i{position:absolute;bottom:-6vh;left:calc(var(--x)*1vw);
  width:calc(var(--sz)*1.25px);height:calc(var(--sz)*1.25px);border-radius:50%;
  background:radial-gradient(circle,#ffd9a0 0%,#ff9a3c 55%,transparent 72%);
  box-shadow:0 0 calc(var(--sz)*3.2px) rgba(255,160,80,.58);
  opacity:0;animation:rise var(--dur) linear var(--delay) infinite}
@keyframes rise{
  0%{opacity:0;transform:translate3d(0,0,0)}
  7%{opacity:var(--op)}
  55%{opacity:calc(var(--op)*.7)}
  100%{opacity:0;transform:translate3d(var(--dx),-108vh,0)}
}

/* 5.5 vignette */
.vignette{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(0,0,0,.55),transparent 16% 76%,rgba(0,0,0,.72)),
    radial-gradient(140% 100% at 50% 40%,transparent 52%,rgba(0,0,0,.6) 100%)}

/* 5.6 grain (static) */
.grain{position:absolute;inset:0;background:url(../assets/grain.png) repeat;
  background-size:64px;opacity:.045;mix-blend-mode:overlay}

/* ---------- 6. logo ------------------------------------------------------ */
.title{position:relative}
.title-glow{position:absolute;inset:-22% -8%;z-index:-1;
  background:radial-gradient(56% 72% at 50% 56%,rgba(255,48,32,.22),rgba(170,18,10,.09) 48%,transparent 76%);
  animation:ember-flicker 4.6s ease-in-out infinite}
@keyframes ember-flicker{0%,100%{opacity:.72;transform:scale(1)}50%{opacity:1;transform:scale(1.035)}}

.logo{position:relative;display:flex;align-items:flex-end;justify-content:center;white-space:nowrap}

/* 6.2 metallic gold wordmark */
.en{position:relative;isolation:isolate;font-family:var(--font-title);font-weight:400;
  font-size:clamp(2.2rem,13vw,12.4rem);line-height:.94;
  letter-spacing:.045em;text-indent:.045em;
  background:linear-gradient(180deg,#fffdf6 0%,#f6dfae 14%,#e5c072 30%,#c69a46 44%,
    #8f6420 48.5%,#fdf3d3 50.5%,#caa052 58%,#f6e4b4 74%,#fff8e6 85%,#c8a054 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  -webkit-text-stroke:1px rgba(64,42,12,.5);
  filter:drop-shadow(0 1px 0 rgba(0,0,0,.9)) drop-shadow(0 9px 16px rgba(0,0,0,.72))
         drop-shadow(0 0 16px rgba(255,196,130,.12));
  animation:en-in 1.05s cubic-bezier(.16,1,.3,1) .25s both}
.en::before{content:attr(data-text);position:absolute;inset:0;z-index:-1;
  color:rgba(10,7,12,.9);transform:translateY(.045em)}
.en::after{content:attr(data-text);position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(100deg,transparent 42%,rgba(255,255,255,.5) 50%,transparent 58%);
  background-size:300% 100%;background-position:130% 0;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:sheen 1.7s ease-out 1.35s 1 both}
@keyframes sheen{from{background-position:130% 0}to{background-position:-40% 0}}
@keyframes en-in{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:translateY(0)}}

/* 6.3 the "2" */
.num{position:relative;isolation:isolate;font-family:var(--font-num);font-weight:400;
  font-size:clamp(2.35rem,12.5vw,13.1rem);line-height:1;
  /* the user's previous coral-red fill, kept as-is */
  background:linear-gradient(180deg,#ff9d8a 0%,#d81f2c 26%,#b40e20 47%,
    #71060f 58%,#c9312b 78%,#f79a84 100%);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  filter:drop-shadow(0 2px 0 rgba(58,3,9,.92)) drop-shadow(0 8px 13px rgba(0,0,0,.72))
         drop-shadow(0 0 14px rgba(226,58,40,.22));
  /* brush tail dips just past the gold baseline, classic sequel-numeral overhang */
  transform:rotate(-3deg) translateY(.17em);margin-left:.03em;z-index:2;
  animation:num-in .9s cubic-bezier(.34,1.56,.64,1) .5s both}
/* solid crimson backing: a copy of the glyph under the gradient, swollen with a
   thick stroke so the font's dry-brush gaps close up and read as wet paint.
   The swell stays a single soft mass — no second contour line, no blur halo. */
.num::before{content:attr(data-num);position:absolute;inset:0;z-index:-1;
  color:#a80f1f;-webkit-text-fill-color:#a80f1f;
  -webkit-text-stroke:9px #a80f1f;
  text-shadow:0 0 4px #a80f1f,0 0 4px #a80f1f}
/* gold edge light: same glyph, same position, hairline stroke centred on the
   outline — half sits in the red, half on the dark, so it reads as a rim and
   never as a floating second copy. */
.num::after{content:attr(data-num);position:absolute;inset:0;z-index:1;
  -webkit-text-fill-color:transparent;color:transparent;
  -webkit-text-stroke:1.5px rgba(247,209,134,.9)}
/* looping interior light: a soft band that drifts across the INSIDE of the
   glyph, clipped to the numeral itself, slow and low-contrast. */
.num .sheen{position:absolute;inset:0;z-index:2;pointer-events:none;
  -webkit-text-fill-color:transparent;color:transparent;
  background:linear-gradient(100deg,transparent 38%,rgba(255,232,206,.62) 50%,transparent 62%);
  background-size:250% 100%;background-repeat:no-repeat;background-position:135% 0;
  -webkit-background-clip:text;background-clip:text;
  animation:num-sheen 6.5s ease-in-out 7s infinite}
@keyframes num-sheen{
  0%,10%{background-position:135% 0;opacity:0}
  22%{opacity:.85}
  62%{background-position:-45% 0;opacity:.85}
  78%,100%{background-position:-45% 0;opacity:0}}
@keyframes num-in{
  from{opacity:0;transform:rotate(-10deg) translateY(.08em) scale(.72)}
  to{opacity:1;transform:rotate(-3deg) translateY(.17em) scale(1)}
}

/* 6.5 no background-clip:text support */
@supports not ((background-clip:text) or (-webkit-background-clip:text)){
  .en{color:#e8c878;-webkit-text-fill-color:#e8c878}
  .num{color:#d3273c;-webkit-text-fill-color:#d3273c}
}

/* ---------- 7. katana ---------------------------------------------------- */
.katana{position:relative;width:clamp(280px,62vw,880px);margin-top:clamp(4px,1.2vmin,14px)}
.katana img{display:block;width:100%;height:auto;user-select:none;-webkit-user-drag:none;
  filter:drop-shadow(0 16px 22px rgba(0,0,0,.78)) drop-shadow(0 34px 30px rgba(0,0,0,.5)) drop-shadow(0 0 46px rgba(255,140,50,.14));
  animation:katana-in .95s cubic-bezier(.16,1,.3,1) .68s both}
@keyframes katana-in{from{opacity:0;transform:scaleX(.965)}to{opacity:1;transform:scaleX(1)}}

/* Blade shine v6 - the blade art is near-white (RGB 224,228,233), so every
   white-on-white layer v5 shipped was invisible (measured peak pixel change
   15/255). Contrast now comes from a COOL DARK band (shading on the steel)
   plus a saturated blue bloom spilling outside the silhouette. */
.blade-shine{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:url(../assets/blade-edge.png) center/contain no-repeat;
  filter:brightness(0) drop-shadow(0 0 5px rgba(120,190,255,.85)) drop-shadow(0 0 16px rgba(60,140,255,.5));
  -webkit-mask-image:linear-gradient(100deg,transparent 45.8%,rgba(236,244,255,.9) 50%,transparent 54.2%);
  mask-image:linear-gradient(100deg,transparent 45.8%,rgba(236,244,255,.9) 50%,transparent 54.2%);
  -webkit-mask-size:220% 100%;mask-size:220% 100%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  animation:sweep 26s cubic-bezier(.5,0,.5,1) 2s infinite}
@keyframes sweep{
  0%{-webkit-mask-position:28% 0;mask-position:28% 0;opacity:0}
  12%{opacity:.7}
  46%{opacity:.7}
  60%{-webkit-mask-position:94% 0;mask-position:94% 0;opacity:0}
  100%{-webkit-mask-position:28% 0;mask-position:28% 0;opacity:0}
}

/* ---------- 8. japanese title row ---------------------------------------- */
.jp{display:flex;align-items:center;gap:clamp(10px,1.6vw,20px);margin-top:clamp(12px,2.6vmin,30px)}
.jp .jp-text{font-family:var(--font-jp);font-weight:900;font-size:clamp(1.3rem,4.1vw,3rem);line-height:1;
  letter-spacing:.3em;text-indent:.3em;color:#ecd9ae;
  text-shadow:0 1px 0 rgba(10,6,3,.8),0 0 22px rgba(255,170,90,.22);
  animation:jp-in .9s cubic-bezier(.16,1,.3,1) .95s both}
.jp .rule{height:1px;width:clamp(34px,8vw,130px);
  animation:rule-in 1s cubic-bezier(.16,1,.3,1) .95s both}
.jp .rule.l{background:linear-gradient(90deg,transparent,rgba(214,173,97,.8));transform-origin:left}
.jp .rule.r{background:linear-gradient(90deg,rgba(214,173,97,.8),transparent);transform-origin:right}
@keyframes jp-in{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
@keyframes rule-in{from{transform:scaleX(0)}to{transform:scaleX(1)}}

/* ---------- 9. hint + copy ----------------------------------------------- */
.hint{display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;
  margin-top:clamp(12px,2vmin,26px);
  opacity:0;
  animation:fade-in 1.1s ease 1.5s both,pulse 2.6s ease-in-out 2.6s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.45}}

.h-en{font-family:var(--font-ui);font-size:.94rem;letter-spacing:.5em;text-indent:.5em;
  color:#f4e8ca;text-shadow:0 1px 7px rgba(0,0,0,.8),0 0 18px rgba(0,0,0,.55);text-transform:uppercase}
.h-jp{font-family:var(--font-jp-ui);font-size:.86rem;letter-spacing:.28em;text-indent:.28em;
  color:#dbcdaa;text-shadow:0 1px 7px rgba(0,0,0,.75)}
.only-touch{display:none}
@media (hover:none){.only-desk{display:none}.only-touch{display:inline}}

.copy{position:absolute;left:50%;bottom:max(18px,calc(env(safe-area-inset-bottom) + 12px));
  transform:translateX(-50%);width:min(92vw,760px);text-align:center;
  font-family:var(--font-ui);font-size:clamp(10px,1.15vw,11.5px);letter-spacing:.06em;
  color:var(--copy);opacity:0;animation:fade-in 1.2s ease 1.8s both}
@keyframes fade-in{from{opacity:0}to{opacity:1}}

/* ---------- 11. interaction ---------------------------------------------- */
.flash{position:fixed;inset:0;z-index:5;pointer-events:none;opacity:0;
  background:radial-gradient(closest-side at 50% 44%,rgba(255,236,200,.5),transparent 72%)}
.is-pressed .flash{animation:flash .95s ease-out both}
@keyframes flash{0%{opacity:0}16%{opacity:.9}46%{opacity:.34}100%{opacity:0}}
.is-pressed .hint{opacity:.25!important}      /* !important beats the pulsing animation */
.is-pressed .stage{transform:translateY(-0.8vh) scale(1.014)}

/* ---------- 12. reduced motion ------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
  .fog,.beam,.hint,.copy,.en,.num,.jp-text,.jp .rule,.katana img{
    opacity:1!important;transform:none!important;filter:none!important}
  .fog,.beam,.stage,.katana{translate:none!important}
  .vid{opacity:1!important}
  .vid video{display:none}
  .vid-red{display:none}
  .embers{display:none}
  .blade-shine{display:none}
  .blade-live{display:none}
  .num .sheen{display:none}
}

/* ---------- 13. responsive ----------------------------------------------- */
@media (max-width:640px){
  .stage{gap:clamp(8px,1.4vmin,18px)}
  .katana{width:88vw}
  .jp .jp-text{letter-spacing:.24em}
  .hint{margin-top:clamp(10px,1.4vh,18px)}
}
@media (max-width:420px){
  .jp .rule{width:26px}
}
@media (max-height:520px){
  .stage{gap:6px;transform:translateY(-1vh)}
  .is-pressed .stage{transform:translateY(-1vh) scale(1.014)}
  .en{font-size:clamp(1.4rem,min(13vw,9.5vh),12.4rem)}
  .num{font-size:clamp(2rem,min(12.5vw,11vh),13.1rem)}
  .katana{width:min(56vw,420px)}
  .hint{margin-top:8px;gap:5px}
  .h-jp{display:none}
}

/* ---------- 14. custom cursors ------------------------------------------- */
/* Katana blade as the mouse pointer — hotspot 1 1 sits on the blade tip. The
   32 px PNG covers everything; where image-set() is honoured, the 64 px @2x
   file keeps the edge crisp on HiDPI screens. The red-hot variant is the
   pointer state: pressing anywhere, or hovering the START hint. */
html{cursor:url(../assets/cursor-katana.png) 1 1,auto}
.hero.is-pressed,.hint,a,button,[role="button"]{
  cursor:url(../assets/cursor-katana-hot.png) 1 1,pointer}
@supports (cursor:image-set(url(x) 1x)){
  html{cursor:image-set(url(../assets/cursor-katana.png) 1x,url(../assets/cursor-katana@2x.png) 2x) 1 1,auto}
  .hero.is-pressed,.hint,a,button,[role="button"]{
    cursor:image-set(url(../assets/cursor-katana-hot.png) 1x,url(../assets/cursor-katana-hot@2x.png) 2x) 1 1,pointer}
}


/* ---------- 15. v4 polish — living atmosphere + press feedback ----------- */
/* Floating ash: eight motes, deliberately fainter than the embers — slow
   vertical drift that adds depth without competing with the title. */
.motes{position:absolute;inset:0;pointer-events:none}
.motes i{position:absolute;left:calc(var(--x)*1vw);top:calc(var(--y)*1vh);
  width:calc(var(--sz)*1px);height:calc(var(--sz)*1px);border-radius:50%;
  background:radial-gradient(circle,rgba(255,242,214,.8) 0%,rgba(255,226,170,.28) 62%,transparent 100%);
  opacity:0;animation:mote var(--d) linear var(--dl) infinite}
@keyframes mote{
  0%{opacity:0;transform:translate3d(0,0,0)}
  14%{opacity:var(--o,.2)}
  82%{opacity:var(--o,.2)}
  100%{opacity:0;transform:translate3d(var(--dx,16px),-44px,0)}
}

/* Foreground wisps: two huge, almost invisible smoke veils that cross slowly
   IN FRONT of the stage — one element each, no blur, transform/opacity only. */
.wisp{position:absolute;pointer-events:none;width:92vmax;height:58vmax;border-radius:50%;
  background:radial-gradient(closest-side,rgba(150,140,162,.055),transparent 72%);opacity:0}
.wisp.w1{top:-24%;left:-50%;animation:wisp-a 47s ease-in-out infinite}
.wisp.w2{top:32%;left:-62%;animation:wisp-b 63s ease-in-out 9s infinite}
@keyframes wisp-a{
  0%{opacity:0;transform:translate3d(0,0,0)}
  20%{opacity:1}55%{opacity:1}
  100%{opacity:0;transform:translate3d(34vw,5vh,0)}}
@keyframes wisp-b{
  0%{opacity:0;transform:translate3d(0,0,0)}
  24%{opacity:.85}58%{opacity:.85}
  100%{opacity:0;transform:translate3d(38vw,-6vh,0)}}

/* Press feedback: one blade-light streak sweeps the screen, and the katana
   answers with a single quick glint. Both exist only while pressed. */
.slash{position:absolute;inset:-12% -8%;z-index:6;pointer-events:none;opacity:0;
  background:linear-gradient(103deg,transparent 45.6%,rgba(255,238,206,.10) 48.6%,
    rgba(255,255,255,.5) 49.9%,rgba(255,215,150,.14) 51.2%,transparent 54.4%)}
.is-pressed .slash{animation:slash-sweep .72s cubic-bezier(.22,.58,.25,1) both}
@keyframes slash-sweep{
  0%{opacity:0;transform:translate3d(-26vw,0,0)}
  14%{opacity:1}
  100%{opacity:0;transform:translate3d(26vw,0,0)}}

/* Looping blade light: the blade art is near-white, so a white streak is
   invisible. Instead the WHOLE blade cools down (translucent steel-blue over
   blade-mask.png) and a thin pure-white glint crosses it — white reads as bright
   only against the tint. Ends are mapped to the blade's actual extent
   (mask bbox = left 71% of the art), so the glint never wastes travel time
   over the handle. */
.blade-live{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(96deg,rgba(24,50,104,.46) 0%,rgba(24,50,104,.46) 49.2%,
    rgba(255,255,255,.96) 49.7%,rgba(255,255,255,.96) 50.3%,rgba(24,50,104,.46) 50.8%,
    rgba(24,50,104,.46) 100%);
  background-size:400% 100%;background-repeat:no-repeat;background-position:40% 0;
  -webkit-mask-image:url(../assets/blade-mask.png);
  mask-image:url(../assets/blade-mask.png);
  -webkit-mask-size:100% 100%;mask-size:100% 100%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  filter:drop-shadow(0 0 5px rgba(120,190,255,.9)) drop-shadow(0 0 18px rgba(60,140,255,.55));
  animation:blade-live 16s ease-in-out 3s infinite}
@keyframes blade-live{
  0%,5%{background-position:40% 0;opacity:0}
  18%{opacity:.72}
  62%{background-position:68% 0;opacity:.72}
  78%,100%{background-position:68% 0;opacity:0}}

/* Press glint: same white-on-white fix — dark edge line + blue bloom, one pass
   per press, so TAP/PRESS ANY KEY visibly answers on the blade. */
.blade-flash{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:url(../assets/blade-edge.png) center/contain no-repeat;
  filter:brightness(0) drop-shadow(0 0 6px rgba(150,205,255,.95)) drop-shadow(0 0 18px rgba(70,150,255,.6));
  -webkit-mask-image:linear-gradient(100deg,transparent 44%,rgba(255,255,255,.98) 50%,transparent 56%);
  mask-image:linear-gradient(100deg,transparent 44%,rgba(255,255,255,.98) 50%,transparent 56%);
  -webkit-mask-size:200% 100%;mask-size:200% 100%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}
.is-pressed .blade-flash{animation:blade-flash .95s cubic-bezier(.4,0,.2,1) both}
@keyframes blade-flash{
  0%{-webkit-mask-position:135% 0;mask-position:135% 0;opacity:0}
  15%{opacity:.85}
  70%{opacity:.8}
  100%{-webkit-mask-position:-38% 0;mask-position:-38% 0;opacity:0}}

@media (prefers-reduced-motion: reduce){
  .motes,.wisp,.slash,.blade-flash{display:none}
}
