/* motifs.css · pure CSS/SVG motif kit. Source of truth: DESIGN.md. Original work, no Bungie assets. */

/* corner brackets */
.bracket{position:relative;padding:var(--gap)}
.bracket::before,.bracket::after{content:"";position:absolute;width:14px;height:14px}
.bracket::before{top:0;left:0;border-top:2px solid var(--acid);border-left:2px solid var(--acid)}
.bracket::after{bottom:0;right:0;border-bottom:2px solid var(--acid);border-right:2px solid var(--acid)}

/* registration mark */
.regmark{width:16px;height:16px;background:
  linear-gradient(var(--ink-dim),var(--ink-dim)) 50%/1px 100% no-repeat,
  linear-gradient(var(--ink-dim),var(--ink-dim)) 50%/100% 1px no-repeat,
  radial-gradient(circle,transparent 4px,var(--ink-dim) 4px 5px,transparent 5px)}

/* barcode strip: pixel stops, never percentages */
.barcode{height:36px;background:repeating-linear-gradient(90deg,
  var(--ink) 0 2px,transparent 2px 4px,var(--ink) 4px 7px,transparent 7px 8px,
  var(--ink) 8px 9px,transparent 9px 12px)}

/* dither: a 2px checkerboard in --line, the dimmed-tile treatment (past calendar days). Static, small tiles only. */
.dither{background-image:repeating-conic-gradient(var(--line) 0 25%,transparent 0 50%);background-size:4px 4px}

/* scanline overlay */
.scan{position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(to bottom,transparent 0 2px,rgba(0,0,0,.18) 2px 3px)}

/* glitch text with chromatic aberration; markup: <h1 class="wordmark"><a class="glitch" data-text="FOMO">FOMO</a></h1>
   ::before (magenta) and ::after (blue) are clipped copies of the text over a bg-colored box. At rest both are
   fully clipped, so the mark is plain ink whenever no animation is running: no fill-mode, no visibility tricks. */
.glitch{position:relative;color:var(--ink);
  text-shadow:1px 0 var(--magenta),-1px 0 var(--blue),0 0 2px var(--acid)}
.glitch::before,.glitch::after{content:attr(data-text);position:absolute;inset:0;
  background:var(--bg);overflow:hidden;pointer-events:none;clip-path:inset(0 0 100% 0)}
.glitch::before{color:var(--magenta)}
.glitch::after{color:var(--blue)}

/* Motion (DESIGN.md · Motion rules). Wordmark only. Only opacity, transform and clip-path move.
   Everything sits behind prefers-reduced-motion:no-preference and the header MOTION toggle (html[data-motion=off]). */
@media (prefers-reduced-motion:no-preference){
  /* tear: three-iteration burst on load (g1/g2), then a ~300ms tear about every 13s (t1/t2).
     The later name wins a property, so the burst is listed after the loop and hands back to it when it ends.
     Hover/focus append a second g1/g2: a fresh burst that stops the moment the pointer leaves. */
  .glitch::before{animation:t1 13s steps(1,end) infinite,g1 2.4s steps(2) 3;will-change:clip-path}
  .glitch::after{animation:t2 13s steps(1,end) infinite,g2 3.1s steps(2) 3;will-change:clip-path}
  .glitch:hover::before,.glitch:focus-visible::before{animation:t1 13s steps(1,end) infinite,g1 2.4s steps(2) 3,g1 2.4s steps(2) 3}
  .glitch:hover::after,.glitch:focus-visible::after{animation:t2 13s steps(1,end) infinite,g2 3.1s steps(2) 3,g2 3.1s steps(2) 3}
  /* ambient flicker: two opacity loops on prime periods, 7.3s on the mark and 11.9s on its h1, so the rhythm never
     repeats (they realign every 14.5 min). Each is awake under 400ms per cycle. The 7.3s loop dips twice, the 11.9s loop
     once in two steps, so even when both land in the same second the mark dims three times at most. */
  .wordmark{animation:fB 11.9s steps(1,end) infinite}
  .wordmark .glitch{animation:fA 7.3s steps(1,end) infinite}
}
@media (prefers-reduced-motion:reduce){
  .glitch::before,.glitch::after{display:none}
}
/* MOTION OFF: the header toggle sets html[data-motion=off] (persisted in localStorage "motion") and every animation dies. */
:root[data-motion="off"] *,:root[data-motion="off"] ::before,:root[data-motion="off"] ::after{animation:none!important;transition:none!important}
:root[data-motion="off"] .glitch::before,:root[data-motion="off"] .glitch::after{display:none}

/* load burst; steps(2) gives the hard cuts. 0% and 100% match so three iterations read as one burst. */
@keyframes g1{0%{clip-path:inset(0 0 60% 0)}
              10%{clip-path:inset(20% 0 55% 0);transform:translateX(-2px)}
              30%{clip-path:inset(80% 0 5% 0);transform:translateX(1px)}
              100%{clip-path:inset(0 0 60% 0)}}
@keyframes g2{0%{clip-path:inset(60% 0 0 0)}
              15%{clip-path:inset(70% 0 10% 0);transform:translateX(2px)}
              45%{clip-path:inset(35% 0 40% 0);transform:translateX(-1px)}
              100%{clip-path:inset(60% 0 0 0)}}
/* ambient tear: 10.92s-11.22s of every 13s cycle (three band positions, 100ms each), clipped away otherwise */
@keyframes t1{0%{clip-path:inset(0 0 100% 0);transform:none}
              84%{clip-path:inset(18% 0 62% 0);transform:translateX(-2px)}
              84.77%{clip-path:inset(64% 0 12% 0);transform:translateX(1px)}
              85.54%{clip-path:inset(40% 0 45% 0);transform:translateX(-1px)}
              86.31%,100%{clip-path:inset(0 0 100% 0);transform:none}}
@keyframes t2{0%{clip-path:inset(0 0 100% 0);transform:none}
              84.31%{clip-path:inset(70% 0 8% 0);transform:translateX(2px)}
              85.08%{clip-path:inset(5% 0 80% 0);transform:translateX(-1px)}
              85.85%{clip-path:inset(48% 0 30% 0);transform:translateX(1px)}
              86.62%,100%{clip-path:inset(0 0 100% 0);transform:none}}
/* ambient flicker A, 7.3s: awake 5.00s-5.20s (dip to .35 with a 1px shift for 60ms, dip to .6 for 50ms) */
@keyframes fA{0%{opacity:1;transform:none}
              68.49%{opacity:.35;transform:translateX(1px)}
              69.32%{opacity:1;transform:none}
              70.55%{opacity:.6}
              71.23%,100%{opacity:1}}
/* ambient flicker B, 11.9s: awake 8.60s-8.76s (one dip that falls in two steps, .55 then .25, shifted -1px) */
@keyframes fB{0%{opacity:1;transform:none}
              72.27%{opacity:.55;transform:translateX(-1px)}
              72.94%{opacity:.25}
              73.61%,100%{opacity:1;transform:none}}
