/* ==========================================================================
   Comic primitives — panels, captions, balloons, SFX, stickers, krackle.
   Everything on this site is built out of these seven things.
   ========================================================================== */

/* --------------------------------------------------------------------------
   PANEL — a hard-bordered frame with a hard drop shadow. Never soft.
   -------------------------------------------------------------------------- */

.panel {
  position: relative;
  background: linear-gradient(160deg,
    rgba(23, 29, 85, .93),
    rgba(6, 6, 15, .97));
  border: var(--border) solid #000;
  box-shadow: var(--shadow-hard), 0 0 0 1px rgba(255, 255, 255, .06);
  padding: clamp(1.1rem, 2.6vw, 1.9rem);
  isolation: isolate;
  overflow: hidden;
}

/* Every panel is printed on the same bad press. */
.panel::before {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at center,
    rgba(255, 255, 255, .16) 22%, transparent 26%);
  background-size: 5px 5px;
  opacity: .5;
  pointer-events: none;
  z-index: 0;
}
.panel > * { position: relative; z-index: 1; }

/* A thin neon rule inside the ink line — the city bleeding through. */
.panel--lit {
  box-shadow:
    var(--shadow-hard),
    inset 0 0 0 1px color-mix(in srgb, var(--grade-a) 55%, transparent),
    0 0 34px -8px color-mix(in srgb, var(--grade-a) 70%, transparent);
}

.panel--paper {
  background: var(--paper);
  color: var(--paper-ink);
  box-shadow: var(--shadow-hard);
}
.panel--paper::before { mix-blend-mode: multiply; background-image: radial-gradient(circle at center, rgba(0,0,0,.12) 22%, transparent 26%); }

.panel--black { background: #000; }
.panel--flush { padding: 0; }

/* Skewed gutters. Comic pages are never a neat grid. */
.panel--tilt-l  { transform: rotate(-.9deg); }
.panel--tilt-r  { transform: rotate(.7deg); }
.panel--shear   { clip-path: polygon(0 0, 100% 2.5%, 100% 100%, 0 97%); }
.panel--shear-r { clip-path: polygon(0 2.5%, 100% 0, 100% 97%, 0 100%); }

/* The border draws itself on entry. */
.panel--draw { border-color: transparent; }
.panel--draw .panel-stroke {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 3; pointer-events: none; overflow: visible;
}
.panel--draw .panel-stroke rect {
  fill: none; stroke: #000; stroke-width: 6;
  stroke-dasharray: var(--len, 2000);
  stroke-dashoffset: var(--len, 2000);
  transition: stroke-dashoffset .8s var(--ease);
}
.panel--draw.is-in .panel-stroke rect { stroke-dashoffset: 0; }

/* --------------------------------------------------------------------------
   CAPTION BOX — the narrator. Yellow, boxed, tilted, always terse.
   -------------------------------------------------------------------------- */

.caption {
  display: inline-block;
  max-width: 46ch;
  background: var(--yellow);
  color: #0c0c0c;
  border: var(--border) solid #000;
  box-shadow: var(--shadow-hard-sm);
  padding: .62rem .9rem;
  font-family: var(--f-body);
  font-weight: 600;
  font-size: var(--t-sm);
  line-height: 1.38;
  transform: rotate(-.8deg);
}
.caption--cream { background: var(--paper); }
.caption--red   { background: var(--red); color: #fff; }
.caption--cyan  { background: var(--cyan); color: #04121a; }
.caption--right { transform: rotate(.9deg); }
.caption strong { font-weight: 700; }
.caption em { font-style: italic; }

/* A caption box that starts the chapter, clipped to the top-left corner. */
.caption--anchor {
  position: absolute;
  top: calc(var(--border) * -1);
  left: calc(var(--border) * -1);
  transform: none;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, .8);
  z-index: 4;
}

/* --------------------------------------------------------------------------
   BALLOON — dialogue. Tail is a pseudo-element triangle stack.
   -------------------------------------------------------------------------- */

.balloon {
  position: relative;
  display: inline-block;
  max-width: 34ch;
  background: #fff;
  color: #0a0a0a;
  border: var(--border) solid #000;
  border-radius: 22px;
  padding: .85rem 1.15rem;
  font-weight: 600;
  font-size: var(--t-sm);
  line-height: 1.4;
  box-shadow: var(--shadow-hard-sm);
}
.balloon::after,
.balloon::before {
  content: '';
  position: absolute;
  bottom: -18px; left: 34px;
  border: 10px solid transparent;
  border-top-color: #fff;
  border-right-width: 16px;
}
.balloon::before {
  bottom: -25px; left: 31px;
  border-top-color: #000;
  border-width: 13px; border-right-width: 20px;
}
.balloon--think { border-radius: 30px; }
.balloon--think::before, .balloon--think::after { display: none; }

/* --------------------------------------------------------------------------
   SFX — onomatopoeia. Three stacked layers: knockout, outline, fill.
   -------------------------------------------------------------------------- */

.sfx {
  font-family: var(--f-comic);
  font-size: clamp(2rem, 6vw, 5rem);
  line-height: .85;
  letter-spacing: .02em;
  color: var(--yellow);
  -webkit-text-stroke: 4px #000;
  paint-order: stroke fill;
  text-shadow:
    5px 5px 0 #000,
    0 0 26px color-mix(in srgb, var(--grade-a) 75%, transparent);
  transform: rotate(-6deg);
  display: inline-block;
  user-select: none;
}
.sfx--sm  { font-size: clamp(1.1rem, 2.4vw, 1.9rem); -webkit-text-stroke-width: 3px; text-shadow: 3px 3px 0 #000; }
/* In flow (not absolutely placed) an SFX still needs room to breathe. */
.panel > .sfx:not(:only-child) { align-self: flex-start; margin-bottom: .35rem; }
.sfx--red { color: var(--red); }
.sfx--cyan{ color: var(--cyan); }
.sfx--mag { color: var(--magenta); }
.sfx--r   { transform: rotate(5deg); }

/* SFX doesn't fade in. It ARRIVES. */
.sfx.rise { transform: rotate(-6deg) scale(.4); opacity: 0; }
.sfx.rise.is-in {
  opacity: 1;
  transform: rotate(-6deg) scale(1);
  transition: transform .34s var(--pop), opacity .12s linear;
}
.sfx--r.rise    { transform: rotate(5deg) scale(.4); }
.sfx--r.rise.is-in { transform: rotate(5deg) scale(1); }

/* --------------------------------------------------------------------------
   BURST — the jagged star an SFX sits on.
   -------------------------------------------------------------------------- */

.burst {
  position: relative;
  display: grid;
  place-items: center;
  padding: clamp(1.4rem, 4vw, 3rem);
}
.burst::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--grade-a);
  clip-path: polygon(
    50% 0%, 60% 18%, 78% 8%, 74% 28%, 95% 26%, 82% 42%, 100% 52%,
    82% 62%, 95% 78%, 74% 76%, 78% 96%, 60% 86%, 50% 100%, 40% 86%,
    22% 96%, 26% 76%, 5% 78%, 18% 62%, 0% 52%, 18% 42%, 5% 26%,
    26% 28%, 22% 8%, 40% 18%);
  filter: drop-shadow(0 0 30px color-mix(in srgb, var(--grade-a) 60%, transparent));
  z-index: 0;
}
.burst > * { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   STICKER — the HELLO my name is badge. Peels, rotates, casts a real shadow.
   -------------------------------------------------------------------------- */

.sticker {
  --sticker-red: #e8112d;
  display: inline-block;
  width: clamp(190px, 22vw, 280px);
  background: #fff;
  border-radius: 14px;
  box-shadow: 6px 8px 0 rgba(0, 0, 0, .55), 0 14px 40px rgba(0, 0, 0, .5);
  overflow: hidden;
  transform: rotate(-3deg);
  font-family: var(--f-body);
  color: #111;
}
.sticker-top {
  background: var(--sticker-red);
  color: #fff;
  text-align: center;
  padding: .5rem .5rem .55rem;
  line-height: 1;
}
.sticker-top b {
  display: block;
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 3.2vw, 2.15rem);
  letter-spacing: .01em;
}
.sticker-top span {
  display: block;
  font-weight: 700;
  font-size: clamp(.72rem, 1.4vw, .92rem);
  margin-top: .18rem;
}
.sticker-name {
  display: block;
  padding: .7rem .6rem .95rem;
  text-align: center;
  font-family: var(--f-hand);
  font-size: clamp(1.25rem, 2.9vw, 1.95rem);
  letter-spacing: .01em;
  color: #111;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   CHIP / TAG — stack labels, EARTH codes, metadata.
   -------------------------------------------------------------------------- */

.chip {
  display: inline-block;
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: .22rem .5rem;
  border: 2px solid color-mix(in srgb, var(--on-surface) 28%, transparent);
  color: color-mix(in srgb, var(--on-surface) 82%, transparent);
  background: rgba(0, 0, 0, .35);
}
.chip--lit {
  border-color: var(--grade-a);
  color: var(--grade-a);
  text-shadow: 0 0 12px color-mix(in srgb, var(--grade-a) 60%, transparent);
}
.chip-row { display: flex; flex-wrap: wrap; gap: .4rem; }

.earth-code {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--grade-b);
}

/* --------------------------------------------------------------------------
   CHAPTER HEAD — the big numbered title card.
   -------------------------------------------------------------------------- */

.ch-head {
  position: relative;
  margin-bottom: clamp(2rem, 5vh, 3.5rem);
}
.ch-num {
  font-family: var(--f-comic);
  font-size: clamp(3rem, 12vw, 9rem);
  line-height: .8;
  color: transparent;
  -webkit-text-stroke: 2px color-mix(in srgb, var(--grade-a) 85%, transparent);
  letter-spacing: .04em;
  display: block;
}
.ch-kicker {
  display: inline-block;
  font-family: var(--f-comic);
  font-size: var(--t-md);
  letter-spacing: .16em;
  color: var(--grade-b);
  margin-bottom: .35rem;
}
.ch-title {
  font-size: var(--t-2xl);
  letter-spacing: -.01em;
}
.ch-sub {
  max-width: var(--measure);
  color: var(--muted);
  font-size: var(--t-md);
  margin-top: .9rem;
}

/* --------------------------------------------------------------------------
   STAT — numbers are punchlines. They land alone and they land big.
   -------------------------------------------------------------------------- */

.stat { display: flex; flex-direction: column; gap: .15rem; }
.stat b {
  font-family: var(--f-display);
  font-size: clamp(1.9rem, 5vw, 3.4rem);
  line-height: .9;
  color: var(--grade-a);
  text-shadow: 3px 3px 0 #000;
}
.stat span {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   WEB LINES — SVG connectors drawn between panels on scroll.
   -------------------------------------------------------------------------- */

.weblines {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 1;
  overflow: visible;
}
.weblines path {
  fill: none;
  stroke: color-mix(in srgb, var(--grade-b) 55%, transparent);
  stroke-width: 1.5;
  stroke-dasharray: var(--len, 1200);
  stroke-dashoffset: var(--len, 1200);
  transition: stroke-dashoffset 1.1s var(--ease);
}
.is-in .weblines path { stroke-dashoffset: 0; }

/* --------------------------------------------------------------------------
   SPEED LINES — radial motion streaks, opacity driven by scroll velocity.
   -------------------------------------------------------------------------- */

.speedlines {
  position: fixed; inset: -20%;
  pointer-events: none;
  z-index: 56;
  opacity: calc(var(--vel) * .28);
  background: repeating-conic-gradient(from 0deg at 50% 50%,
    rgba(255, 255, 255, .55) 0deg .22deg,
    transparent .22deg 2.4deg);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 26%, #000 72%);
          mask-image: radial-gradient(circle at 50% 50%, transparent 26%, #000 72%);
  mix-blend-mode: screen;
  transition: opacity .18s linear;
  will-change: opacity;
  contain: strict;
}
.speedlines.is-idle { visibility: hidden; }

/* --------------------------------------------------------------------------
   HEADING GLITCH — the dimension coming apart, contained to the title.
   A full-screen flash on every chapter boundary was blinding; this keeps the
   idea but puts it where the reader is already looking.
   -------------------------------------------------------------------------- */

.glitch-head {
  position: relative;
  will-change: transform, clip-path;
}
.is-in > .glitch-head,
.glitch-head.is-in {
  animation: head-glitch .62s steps(1, end) 1 both;
}

@keyframes head-glitch {
  0%   { transform: translate3d(0,0,0); clip-path: inset(0 0 0 0);
         text-shadow: -5px 0 0 var(--red), 5px 0 0 var(--cyan), 6px 6px 0 #000; }
  12%  { transform: translate3d(-9px,0,0); clip-path: inset(14% 0 52% 0);
         text-shadow: 7px 0 0 var(--red), -7px 0 0 var(--cyan), 6px 6px 0 #000; }
  22%  { transform: translate3d(7px,0,0); clip-path: inset(56% 0 12% 0);
         text-shadow: -6px 0 0 var(--magenta), 6px 0 0 var(--cyan), 6px 6px 0 #000; }
  32%  { transform: translate3d(-4px,0,0); clip-path: inset(0 0 0 0);
         text-shadow: 4px 0 0 var(--red), -4px 0 0 var(--cyan), 6px 6px 0 #000; }
  44%  { transform: translate3d(6px,0,0); clip-path: inset(38% 0 34% 0);
         text-shadow: -8px 0 0 var(--cyan), 8px 0 0 var(--magenta), 6px 6px 0 #000; }
  56%  { transform: translate3d(-2px,0,0); clip-path: inset(0 0 0 0);
         text-shadow: 3px 0 0 var(--red), -3px 0 0 var(--cyan), 6px 6px 0 #000; }
  70%  { transform: translate3d(3px,0,0); clip-path: inset(72% 0 6% 0);
         text-shadow: -4px 0 0 var(--magenta), 4px 0 0 var(--cyan), 6px 6px 0 #000; }
  84%  { transform: translate3d(-1px,0,0); clip-path: inset(0 0 0 0);
         text-shadow: 2px 0 0 var(--red), -2px 0 0 var(--cyan), 6px 6px 0 #000; }
  100% { transform: translate3d(0,0,0); clip-path: inset(0 0 0 0); }
}

/* --------------------------------------------------------------------------
   CURSOR — web-shooter reticle. Moves on 2s while the page runs smooth.
   -------------------------------------------------------------------------- */

.reticle {
  position: fixed;
  top: 0; left: 0;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  z-index: 90;
  pointer-events: none;
  will-change: transform;
}
.reticle::before,
.reticle::after {
  content: '';
  position: absolute;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(0, 0, 0, .35);
}
.reticle::before { inset: 0; }
.reticle::after {
  inset: 12px;
  background: #fff;
  border: 0;
  transition: inset .18s var(--pop);
}
.reticle.is-hot::after { inset: 7px; }
.reticle.is-hot::before { animation: sense 1.1s steps(8, end) infinite; }

@keyframes sense {
  0%   { transform: scale(1);   opacity: 1; }
  100% { transform: scale(2.1); opacity: 0; }
}

@media (pointer: coarse) { .reticle { display: none; } }

/* --------------------------------------------------------------------------
   Krackle canvas — Kirby energy dots at the story payoffs.
   -------------------------------------------------------------------------- */

.krackle {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .speedlines, .krackle { display: none; }
  .glitch-head, .is-in > .glitch-head, .glitch-head.is-in { animation: none; }
  .panel--draw { border-color: #000; }
  .panel--draw .panel-stroke { display: none; }
  .weblines path { stroke-dashoffset: 0; }
  .sfx.rise, .sfx.rise.is-in { opacity: 1; transform: rotate(-6deg); }
  .reticle::before { animation: none; }
}
