/*
 * lanobots-tech.css
 * LanoBots — Tech animation system
 * Sprint 3: LanoVerse OS interface motion language
 *
 * Depends on: lanobots-tokens.css
 * Load order: tokens → homepage → typography → components → animations → tech → [page].css
 *
 * All pre-animation hidden states are scoped under `.js` so the page
 * remains fully visible when JavaScript is disabled or fails.
 */

/* ============================================================
   1.  JS-GATED PROGRESSIVE ENHANCEMENT
       Add `document.documentElement.classList.add('js')` to
       the top of every template's <head> (inline, not deferred).
   ============================================================ */

/* Without JS: everything visible, no transforms */
.lb-reveal-up,
.lb-reveal-down,
.lb-reveal-left,
.lb-reveal-right,
.lb-reveal-fade,
.lb-reveal-mask,
[data-reveal],
.lb-char-card,
.lb-dispatch,
.lb-blog-post-card,
.lb-post-card,
.lb-blog-featured,
.lb-dispatches,
.lb-shop-product,
.lb-shop-cat,
.lb-panel-card,
.lb-runway,
.lb-lanes,
.lb-ig-strip,
.lb-drops,
.lb-universe,
.lb-char-roster,
.lb-char-lanes,
.lb-char-note,
.lb-featured-issue,
.lb-panels,
.lb-comics-archive,
.lb-shop-cats,
.lb-shop-products,
.lb-shop-featured,
.lb-shop-signals,
.lb-shop-cta,
.lb-contact-form-wrap,
.lb-contact-info,
.lb-contact-prep,
.lb-contact-cta {
  opacity: 1;
  transform: none;
  transition: none;
}

/* With JS: apply the hidden initial state + transition */
.js .lb-reveal-up    { opacity: 0; transform: translateY(28px);  transition: opacity var(--dur-slow, 600ms) var(--ease-out), transform var(--dur-slow, 600ms) var(--ease-out); transition-delay: var(--stagger-delay, 0ms); }
.js .lb-reveal-down  { opacity: 0; transform: translateY(-20px); transition: opacity var(--dur-slow, 600ms) var(--ease-out), transform var(--dur-slow, 600ms) var(--ease-out); transition-delay: var(--stagger-delay, 0ms); }
.js .lb-reveal-left  { opacity: 0; transform: translateX(-28px); transition: opacity var(--dur-slow, 600ms) var(--ease-out), transform var(--dur-slow, 600ms) var(--ease-out); transition-delay: var(--stagger-delay, 0ms); }
.js .lb-reveal-right { opacity: 0; transform: translateX(28px);  transition: opacity var(--dur-slow, 600ms) var(--ease-out), transform var(--dur-slow, 600ms) var(--ease-out); transition-delay: var(--stagger-delay, 0ms); }
.js .lb-reveal-fade  { opacity: 0; transition: opacity var(--dur-slow, 600ms) ease; transition-delay: var(--stagger-delay, 0ms); }
.js .lb-reveal-mask  { opacity: 1; clip-path: inset(0 100% 0 0); transition: clip-path var(--dur-slow, 600ms) var(--ease-out); transition-delay: var(--stagger-delay, 0ms); }

/* Section reveals */
.js [data-reveal],
.js .lb-char-card,
.js .lb-dispatch,
.js .lb-blog-post-card,
.js .lb-post-card,
.js .lb-blog-featured,
.js .lb-dispatches,
.js .lb-shop-product,
.js .lb-shop-cat,
.js .lb-panel-card,
.js .lb-runway,
.js .lb-lanes,
.js .lb-ig-strip,
.js .lb-drops,
.js .lb-universe,
.js .lb-char-roster,
.js .lb-char-lanes,
.js .lb-char-note,
.js .lb-featured-issue,
.js .lb-panels,
.js .lb-comics-archive,
.js .lb-shop-cats,
.js .lb-shop-products,
.js .lb-shop-featured,
.js .lb-shop-signals,
.js .lb-shop-cta,
.js .lb-contact-form-wrap,
.js .lb-contact-info,
.js .lb-contact-prep,
.js .lb-contact-cta {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.65s cubic-bezier(.2,.7,.2,1), transform 0.65s cubic-bezier(.2,.7,.2,1);
}

/* Revealed state — added by JS */
.lb-revealed,
.js .lb-reveal-up.lb-revealed,
.js .lb-reveal-down.lb-revealed,
.js .lb-reveal-left.lb-revealed,
.js .lb-reveal-right.lb-revealed,
.js .lb-reveal-fade.lb-revealed,
.js .lb-reveal-mask.lb-revealed,
.js [data-reveal].lb-revealed,
.js .lb-char-card.lb-revealed,
.js .lb-dispatch.lb-revealed,
.js .lb-blog-post-card.lb-revealed,
.js .lb-post-card.lb-revealed,
.js .lb-blog-featured.lb-revealed,
.js .lb-dispatches.lb-revealed,
.js .lb-shop-product.lb-revealed,
.js .lb-shop-cat.lb-revealed,
.js .lb-panel-card.lb-revealed,
.js .lb-runway.lb-revealed,
.js .lb-lanes.lb-revealed,
.js .lb-ig-strip.lb-revealed,
.js .lb-drops.lb-revealed,
.js .lb-universe.lb-revealed,
.js .lb-char-roster.lb-revealed,
.js .lb-char-lanes.lb-revealed,
.js .lb-char-note.lb-revealed,
.js .lb-featured-issue.lb-revealed,
.js .lb-panels.lb-revealed,
.js .lb-comics-archive.lb-revealed,
.js .lb-shop-cats.lb-revealed,
.js .lb-shop-products.lb-revealed,
.js .lb-shop-featured.lb-revealed,
.js .lb-shop-signals.lb-revealed,
.js .lb-shop-cta.lb-revealed,
.js .lb-contact-form-wrap.lb-revealed,
.js .lb-contact-info.lb-revealed,
.js .lb-contact-prep.lb-revealed,
.js .lb-contact-cta.lb-revealed {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0% 0 0);
}

/* ============================================================
   2.  TECH HEADING REVEAL
       .lb-tech-title wraps a heading.
       Inner .lb-tech-title__text clips upward from below.
   ============================================================ */
.lb-tech-title {
  position: relative;
  overflow: hidden;
}
.lb-tech-title__text {
  display: block;
}
.js .lb-tech-title__text {
  transform: translateY(110%);
  filter: blur(6px);
  opacity: 0;
  transition:
    transform 700ms var(--ease-out, cubic-bezier(.2,.7,.2,1)),
    filter    700ms var(--ease-out, cubic-bezier(.2,.7,.2,1)),
    opacity   450ms ease;
  transition-delay: var(--stagger-delay, 0ms);
}
.js .lb-tech-title.lb-revealed .lb-tech-title__text,
.js .lb-tech-title__text.lb-revealed {
  transform: translateY(0);
  filter: blur(0);
  opacity: 1;
}

/* ============================================================
   3.  SCANNING ACCENT LINE
       <div class="lb-heading-line"></div> after a section title.
   ============================================================ */
.lb-heading-line {
  display: block;
  width: 100%;
  max-width: 6rem;
  height: 2px;
  background: var(--page-accent, #7b4fd4);
  margin: 0.6rem 0 1.25rem;
}
.js .lb-heading-line {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 600ms cubic-bezier(.2,.7,.2,1) 200ms;
}
.js .lb-heading-line.lb-revealed {
  transform: scaleX(1);
}

/* ============================================================
   4.  GLITCH ACCENT — hero headings only
       .lb-glitch wraps a span.
       Applied once on load, not looping.
   ============================================================ */
@keyframes lb-glitch-1 {
  0%   { clip-path: inset(20% 0 60% 0); transform: translate(-2px, 0); }
  20%  { clip-path: inset(50% 0 10% 0); transform: translate(2px,  0); }
  40%  { clip-path: inset(10% 0 80% 0); transform: translate(-1px, 0); }
  60%  { clip-path: inset(70% 0 5%  0); transform: translate(1px,  0); }
  80%  { clip-path: inset(30% 0 40% 0); transform: translate(-2px, 0); }
  100% { clip-path: inset(0 0 0 0);     transform: translate(0, 0);    }
}
@keyframes lb-glitch-2 {
  0%   { clip-path: inset(60% 0 20% 0); transform: translate(2px,  0); color: rgba(38,140,255,0.7);  }
  25%  { clip-path: inset(5%  0 70% 0); transform: translate(-1px, 0); color: rgba(239,59,69,0.6);   }
  50%  { clip-path: inset(40% 0 30% 0); transform: translate(1px,  0); color: inherit;               }
  75%  { clip-path: inset(80% 0 5%  0); transform: translate(-2px, 0); color: rgba(38,140,255,0.5);  }
  100% { clip-path: inset(0 0 0 0);     transform: translate(0, 0);    color: inherit;               }
}

.lb-glitch {
  position: relative;
  display: inline-block;
}
.lb-glitch.lb-glitch--active::before,
.lb-glitch.lb-glitch--active::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  color: inherit;
  font: inherit;
  pointer-events: none;
  opacity: 0.85;
}
.lb-glitch.lb-glitch--active::before {
  animation: lb-glitch-1 400ms steps(1) 1;
  color: rgba(239, 59, 69, 0.7);
}
.lb-glitch.lb-glitch--active::after {
  animation: lb-glitch-2 400ms steps(1) 1;
  color: rgba(38, 140, 255, 0.6);
}

/* ============================================================
   5.  DATA DECODE LABEL
       .lb-decode — reveals characters left-to-right via clip mask.
       JS handles the actual character swap; CSS styles the container.
   ============================================================ */
.lb-decode {
  display: inline-block;
  font-family: var(--font-mono, 'IBM Plex Mono', monospace);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--page-accent, #7b4fd4);
}
.lb-decode[aria-label] { cursor: default; }

/* ============================================================
   6.  IMAGE DECODE
       .lb-image-decode — container wraps <img>.
       Scan overlay + blur sharpen effect.
   ============================================================ */
.lb-image-decode {
  position: relative;
  overflow: hidden;
}
.lb-image-decode img {
  display: block;
  width: 100%;
  height: auto;
}
.js .lb-image-decode img {
  opacity: 0;
  filter: blur(8px) saturate(0.7);
  transform: scale(1.03);
  transition:
    opacity    600ms ease,
    filter     900ms var(--ease-out, cubic-bezier(.2,.7,.2,1)),
    transform 1200ms var(--ease-out, cubic-bezier(.2,.7,.2,1));
}
.js .lb-image-decode.lb-revealed img {
  opacity: 1;
  filter: blur(0) saturate(1);
  transform: scale(1);
}

/* Scan overlay: single downward sweep */
@keyframes lb-image-scan {
  0%   { top: -30%; opacity: 0.9; }
  60%  { top: 90%;  opacity: 0.6; }
  100% { top: 120%; opacity: 0; }
}
.lb-image-decode::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: -30%;
  height: 30%;
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(var(--page-accent-rgb, 123,79,212), 0.3),
    transparent
  );
  opacity: 0;
  pointer-events: none;
  z-index: 3;
}
.js .lb-image-decode.lb-revealed::after {
  animation: lb-image-scan 900ms ease-out 300ms 1;
}

/* ============================================================
   7.  TECH BORDER — corner bracket activation
       Add .lb-tech-border to any card/panel.
       Four pseudo-corners draw in on reveal.
   ============================================================ */
.lb-tech-border {
  position: relative;
}
.lb-tech-border::before,
.lb-tech-border::after {
  content: '';
  position: absolute;
  pointer-events: none;
  z-index: 4;
  box-sizing: border-box;
}
/* Top-left + bottom-right corners */
.lb-tech-border::before {
  top: 0; left: 0;
  width: 16px; height: 16px;
  border-top: 1px solid var(--page-accent, #7b4fd4);
  border-left: 1px solid var(--page-accent, #7b4fd4);
  opacity: 0;
  transition: opacity 300ms ease, width 400ms var(--ease-out), height 400ms var(--ease-out);
  transition-delay: 200ms;
}
.lb-tech-border::after {
  bottom: 0; right: 0;
  width: 16px; height: 16px;
  border-bottom: 1px solid var(--page-accent, #7b4fd4);
  border-right: 1px solid var(--page-accent, #7b4fd4);
  opacity: 0;
  transition: opacity 300ms ease, width 400ms var(--ease-out), height 400ms var(--ease-out);
  transition-delay: 300ms;
}
.lb-tech-border.lb-revealed::before,
.lb-tech-border.lb-revealed::after {
  opacity: 1;
  width: 20px; height: 20px;
}

/* Hover: corners expand */
.lb-tech-border:hover::before,
.lb-tech-border:hover::after {
  width: 28px; height: 28px;
  transition-delay: 0ms;
}

/* ============================================================
   8.  IMAGE SIGNAL SWEEP
       Hover sweep across card images.
       Applied to .lb-image-decode or any .lb-img-wrap with data-sweep.
   ============================================================ */
@keyframes lb-signal-sweep {
  0%   { left: -60%; opacity: 0; }
  20%  { opacity: 0.6; }
  100% { left: 110%;  opacity: 0; }
}
.lb-image-decode:hover::before,
[data-sweep]:hover::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: -60%; width: 40%;
  background: linear-gradient(
    105deg,
    transparent 30%,
    rgba(var(--page-accent-rgb, 123,79,212), 0.18) 50%,
    transparent 70%
  );
  animation: lb-signal-sweep 600ms ease-out 1;
  pointer-events: none;
  z-index: 5;
}

/* ============================================================
   9.  CARD INTERFACE MODULE
       .lb-card-module = full card reveal sequence:
       opacity → top border → image → title → meta → CTA
   ============================================================ */
.lb-card-module {
  position: relative;
  overflow: hidden;
}
/* Top border draws in */
.lb-card-module::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 100%;
  height: 1px;
  background: var(--page-accent, #7b4fd4);
  transition: right 500ms var(--ease-out, cubic-bezier(.2,.7,.2,1));
  transition-delay: var(--stagger-delay, 0ms);
  z-index: 3;
}
.lb-card-module.lb-revealed::before {
  right: 0;
}

/* Inner title + meta stagger */
.lb-card-module .lb-card-module__title {
  font-family: var(--font-display, 'Barlow Condensed', sans-serif);
}
.js .lb-card-module .lb-card-module__meta {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 400ms ease 400ms, transform 400ms ease 400ms;
}
.js .lb-card-module.lb-revealed .lb-card-module__meta {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================================
   10.  BUTTON EDGE TRACE
        .lb-btn--trace adds an animated border sweep on hover.
   ============================================================ */
.lb-btn--trace {
  position: relative;
  overflow: hidden;
}
.lb-btn--trace::before {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid var(--page-accent, #7b4fd4);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 350ms var(--ease-out), opacity 200ms ease;
  pointer-events: none;
}
.lb-btn--trace:hover::before {
  transform: scaleX(1);
  opacity: 1;
}

/* Secondary button: border activates left-to-right */
.lb-btn--secondary {
  position: relative;
  overflow: hidden;
}
.lb-btn--secondary::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 100%;
  height: 1px;
  background: var(--page-accent, #7b4fd4);
  transition: right 300ms var(--ease-out);
}
.lb-btn--secondary:hover::after { right: 0; }

/* ============================================================
   11.  POINTER TILT
        Applied by JS to selected cards only (not all cards).
        CSS just defines the transition.
   ============================================================ */
.lb-tilt {
  transition: transform 250ms ease, box-shadow 250ms ease;
  transform-style: preserve-3d;
  will-change: transform;
}
.lb-tilt.lb-tilt--resetting {
  transition: transform 500ms ease;
  transform: perspective(800px) rotateX(0deg) rotateY(0deg);
}

/* ============================================================
   12.  MEDIA: EQUALIZER BARS
        JS appends .lb-eq-bars to .lb-media-hero__rail--right
   ============================================================ */
.lb-eq-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 24px;
  margin-top: 0.5rem;
}
.lb-eq-bar {
  display: block;
  width: 3px;
  background: var(--page-accent, #ff7a18);
  border-radius: 1px 1px 0 0;
  animation: lb-eq 1.2s ease-in-out infinite alternate;
  transform-origin: bottom center;
}
@keyframes lb-eq {
  from { height: 4px;  opacity: 0.5; }
  to   { height: 24px; opacity: 1;   }
}
/* Each bar gets a different height range from its animation-delay offset */
.lb-eq-bar:nth-child(1) { animation-duration: 0.9s;  }
.lb-eq-bar:nth-child(2) { animation-duration: 1.1s;  }
.lb-eq-bar:nth-child(3) { animation-duration: 0.7s;  }
.lb-eq-bar:nth-child(4) { animation-duration: 1.3s;  }
.lb-eq-bar:nth-child(5) { animation-duration: 0.85s; }
.lb-eq-bar:nth-child(6) { animation-duration: 1.0s;  }
.lb-eq-bar:nth-child(7) { animation-duration: 1.2s;  }
.lb-eq-bar:nth-child(8) { animation-duration: 0.95s; }

/* ============================================================
   13.  CONTACT: FORM PANEL WIPE
        The form container wipes in from left.
   ============================================================ */
.js .lb-contact-form-wrap {
  clip-path: inset(0 100% 0 0);
  opacity: 1;
  transform: none;
  transition: clip-path 700ms cubic-bezier(.2,.7,.2,1);
}
.js .lb-contact-form-wrap.lb-revealed {
  clip-path: inset(0 0% 0 0);
}

/* ============================================================
   14.  MEDIA: VIDEO SCANLINE OVERLAY
        Add .lb-media-scanlines to any video thumbnail container.
   ============================================================ */
.lb-media-scanlines {
  position: relative;
  overflow: hidden;
}
.lb-media-scanlines::before {
  content: '';
  position: absolute; inset: 0;
  background: repeating-linear-gradient(
    0deg, transparent, transparent 2px,
    rgba(0,0,0,0.18) 2px, rgba(0,0,0,0.18) 3px
  );
  pointer-events: none;
  z-index: 2;
  opacity: 0.6;
}
/* Play icon pulse */
.lb-media-play-pulse {
  animation: lb-pulse-glow 2.5s ease-in-out infinite;
}

/* ============================================================
   15.  CHARACTERS: TARGETING BRACKETS
        Added by JS on .lb-char-card__img-wrap via .lb-targeting
   ============================================================ */
.lb-targeting::before,
.lb-targeting::after {
  content: '';
  position: absolute;
  width: 14px; height: 14px;
  pointer-events: none;
  z-index: 5;
  opacity: 0;
  transition: opacity 200ms ease, transform 200ms ease;
}
.lb-targeting::before {
  top: 8px; left: 8px;
  border-top: 2px solid var(--page-accent, #ef3b45);
  border-left: 2px solid var(--page-accent, #ef3b45);
  transform: translate(-4px, -4px);
}
.lb-targeting::after {
  bottom: 8px; right: 8px;
  border-bottom: 2px solid var(--page-accent, #ef3b45);
  border-right: 2px solid var(--page-accent, #ef3b45);
  transform: translate(4px, 4px);
}
.lb-char-card:hover .lb-targeting::before,
.lb-char-card:hover .lb-targeting::after {
  opacity: 1;
  transform: translate(0, 0);
}

/* ============================================================
   REDUCED MOTION — disable everything
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  /* Un-hide everything immediately */
  .js .lb-reveal-up, .js .lb-reveal-down, .js .lb-reveal-left,
  .js .lb-reveal-right, .js .lb-reveal-fade, .js .lb-reveal-mask,
  .js [data-reveal], .js .lb-char-card, .js .lb-dispatch,
  .js .lb-blog-post-card, .js .lb-post-card, .js .lb-blog-featured,
  .js .lb-dispatches, .js .lb-shop-product, .js .lb-shop-cat,
  .js .lb-panel-card, .js .lb-runway, .js .lb-lanes, .js .lb-ig-strip,
  .js .lb-drops, .js .lb-universe, .js .lb-char-roster, .js .lb-char-lanes,
  .js .lb-char-note, .js .lb-featured-issue, .js .lb-panels,
  .js .lb-comics-archive, .js .lb-shop-cats, .js .lb-shop-products,
  .js .lb-shop-featured, .js .lb-shop-signals, .js .lb-shop-cta,
  .js .lb-contact-form-wrap, .js .lb-contact-info, .js .lb-contact-prep,
  .js .lb-contact-cta {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }

  .js .lb-tech-title__text {
    opacity: 1 !important; transform: none !important;
    filter: none !important; transition: none !important;
  }
  .js .lb-heading-line { transform: scaleX(1) !important; transition: none !important; }
  .js .lb-image-decode img { opacity: 1 !important; filter: none !important; transform: none !important; }
  .lb-image-decode::after, .lb-glitch::before, .lb-glitch::after { display: none !important; }
  .lb-eq-bar { animation: none !important; height: 12px !important; }
  .lb-tech-border::before, .lb-tech-border::after { opacity: 1 !important; }
  .lb-tilt, .lb-btn--trace::before { transition: none !important; }
}
