/* ==========================================================================
   battle.css — the World of Heroes case: five against two, on a phone.

   The case is a hero-collector mobile RPG whose backend resolves the fights, so
   the figure on that page is a fight — in a phone frame, because that is where
   it ran — with the three things the backend does while it plays listed beside
   it. The schedule lives in tools/battle.py and arrives as the generated
   battle-timeline.css: keyframes and delays only. This file is the frame, the
   layout and which animation each part runs.

   On a phone there is no room for a phone inside the page, and a fight nobody
   asked for is not what a reader on a train wants: below the breakpoint the
   line-up is laid out as two plain teams and nothing moves. Same under
   prefers-reduced-motion.
   ========================================================================== */

/* Three panels in a row, each one the same phone: the fight, the reel and the
   store screenshots. Capped at the screenshots' own 340px so nothing is
   upscaled past its resolution. */
.wohrow {
  display: grid; grid-template-columns: repeat(3, minmax(0, 340px));
  justify-content: start;
  gap: clamp(14px, 2vw, 32px);
  margin: var(--s-5) 0 var(--s-4);
}
.woh, .wohv, .wohs { margin: 0; }

/* --- the phone ------------------------------------------------------------ */
.woh__phone { display: flex; justify-content: center; }
.woh__bezel {
  position: relative; width: 100%;
  padding: 10px;
  border: 1px solid var(--line-strong);
  border-radius: 30px;
  background: linear-gradient(160deg, #0d1424, #070b14);
  box-shadow: 0 24px 60px -28px rgba(0, 0, 0, .9),
              inset 0 1px 0 rgba(255, 255, 255, .05);
}
.woh__cam {
  position: absolute; top: 17px; left: 50%; transform: translateX(-50%);
  width: 54px; height: 5px; border-radius: 999px;
  background: rgba(255, 255, 255, .10);
}
/* One screen shape for all three, taken from the screenshots themselves so the
   store shots fill it exactly. */
.woh__screen {
  position: relative; overflow: hidden;
  aspect-ratio: 340 / 604;
  border-radius: 22px;
  background:
    radial-gradient(ellipse 120% 70% at 50% 0%, rgba(132, 128, 212, .18), transparent 70%),
    radial-gradient(ellipse 120% 60% at 50% 100%, rgba(201, 126, 160, .12), transparent 70%),
    #05080f;
}
/* The fight's own screen is laid out in three rows: the HUD, the fight, the
   outcome — the middle one takes what is left, which is what lets the teams
   stand the height of the screen against each other. */
.woh__screen--fight {
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 24px 10px 14px;
}

.woh__hud {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; text-align: center; color: var(--ink-3);
}

/* --- the two teams -------------------------------------------------------- */
/* Vertical columns facing each other: five on the left, two on the right, the
   lane between them is where the strikes cross. */
.woh__teams {
  /* --row is one place in the line; --ax the distance across the lane. A turn
     moves a unit --ax sideways and --ay along the line, which is how it ends up
     level with whoever it is hitting. */
  --row: clamp(42px, 4.2vw, 58px);
  --ax: clamp(52px, 5.4vw, 76px);
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  align-items: stretch; min-height: 0;
}
.woh-u { position: relative; }
/* Nothing here says where anyone's target is: the timeline works that out from
   the turn order and writes the distance into the keyframes. */
.woh__team {
  list-style: none; margin: 0; padding: 0;
  display: grid; align-content: space-between; justify-items: center; gap: 4px;
}
/* Two against five: the pair stands level with the middle of the line. */
.woh__team--r { align-content: center; gap: 22px; }

.woh-u {
  position: relative;
  animation-name: woh-enter;
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.woh-u--l { --from: -14px; }
.woh-u--r { --from: 14px; }

/* A hero is a figure standing on the ground, not a row in a table: the drawing
   first, its health under its feet, its name under that. */
.woh-u__body {
  display: grid; justify-items: center; gap: 3px;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.woh-fig { width: 34px; height: 47px; display: block; overflow: visible; }
/* Filled shapes, two layers: the body in the team's colour, the steel in the
   accent, so a whole team recolours from one rule. */
.woh-fig__b { fill: var(--c-violet); }
.woh-fig__w { fill: var(--c-teal); }
/* The parts that move. A leg turns about its own hip and the weapon about the
   hand, so both need the drawing's own coordinates — hence view-box rather than
   fill-box, whose origin would be each shape's own middle. */
.woh-fig__leg, .woh-fig__sw {
  transform-box: view-box;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
/* Each leg turns about its hip. The two are a pair either side of centre, so one
   origin serves both — the figures are drawn with their legs at the same height. */
.woh-fig__leg--a { transform-origin: 12px 29px; }
.woh-fig__leg--b { transform-origin: 20px 29px; }
.woh-u--r .woh-fig__leg--a { transform-origin: 11px 32px; }
.woh-u--r .woh-fig__leg--b { transform-origin: 21px 32px; }

/* The bow is the one drawn line in the set. */
.woh-fig__arc { fill: none; stroke: currentColor; stroke-width: 1.8; }
.woh-fig__w .woh-fig__arc { stroke: var(--c-teal); }

/* The enemy is the same drawing turned around, and bigger. */
.woh-u--r .woh-fig { width: 46px; height: 63px; transform: scaleX(-1); }
.woh-u--r .woh-fig__b { fill: var(--c-pink); }
.woh-u--r .woh-fig__w { fill: var(--c-coral); }
.woh-u--r .woh-fig__w .woh-fig__arc { stroke: var(--c-coral); }

.woh-u__hp {
  display: block; width: 30px; height: 3px; border-radius: 2px;
  background: rgba(255, 255, 255, .12); overflow: hidden;
}
.woh-u--r .woh-u__hp { width: 42px; }
.woh-u__hpfill {
  display: block; height: 100%; transform-origin: left center;
  background: linear-gradient(90deg, var(--c-teal), var(--c-violet));
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.woh-u--r .woh-u__hpfill {
  background: linear-gradient(90deg, var(--c-pink), var(--c-coral));
}
.woh-u__n {
  font-size: 8.5px; line-height: 1.2; letter-spacing: .02em;
  color: var(--ink-3); white-space: nowrap;
}

/* --- what the ranged units send across ------------------------------------ */
/* The projectile sits in its shooter's own row and crosses to the target, so it
   needs no coordinates beyond the ones the timeline gives it. The unit does not
   clip; the screen does. */
.woh-shot {
  position: absolute; left: 70%; top: 44%; z-index: 2;
  opacity: 0; pointer-events: none;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.woh-shot--arrow {
  width: 14px; height: 2px; border-radius: 1px;
  background: linear-gradient(90deg, transparent, var(--c-teal) 55%, var(--c-teal));
  box-shadow: 0 0 5px color-mix(in oklab, var(--c-teal) 50%, transparent);
}
.woh-shot--bolt {
  width: 9px; height: 9px; margin-top: -3px; border-radius: 50%;
  background: radial-gradient(circle at 35% 40%, #fff, var(--c-violet) 55%, transparent 76%);
  box-shadow: 0 0 10px 2px color-mix(in oklab, var(--c-violet) 65%, transparent);
}
/* The cleric's charge goes to one of its own, so it is the one thing on this
   screen that travels up the line rather than across it. */
.woh-shot--heal {
  left: 40%; width: 9px; height: 9px; margin-top: -3px; border-radius: 50%;
  background: radial-gradient(circle at 35% 40%, #fff, var(--c-teal) 50%, transparent 76%);
  box-shadow: 0 0 10px 2px color-mix(in oklab, var(--c-teal) 70%, transparent);
}

/* --- what lands, where it lands ------------------------------------------- */
.woh-spark {
  position: absolute; left: 50%; top: 40%;
  width: 13px; height: 13px; margin: -6px 0 0 -6px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--c-coral), transparent 70%);
  opacity: 0; pointer-events: none;
  animation-name: woh-spark;
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
}
.woh-spark--heal { background: radial-gradient(circle, var(--c-teal), transparent 70%); }
.woh-num {
  position: absolute; left: 50%; top: 12%;
  transform-origin: center;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 600;
  color: var(--c-coral); opacity: 0; white-space: nowrap;
  animation-name: woh-num;
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
}
.woh-num--heal { color: var(--c-teal); }

/* --- the outcome ---------------------------------------------------------- */
.woh__win {
  text-align: center;
  font-family: var(--font-mono); font-size: 13px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--c-teal);
  text-shadow: 0 0 18px color-mix(in oklab, var(--c-teal) 55%, transparent);
  opacity: 0;
  animation-name: woh-win;
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
}

/* --- the link above the row ----------------------------------------------- */
/* The game is delisted, so a review of it is worth more than anything that
   could be written here. It reads as a line of type above the three phones
   rather than as a card under them — a card made it look like a fourth panel. */
.woh__links {
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5);
  margin: var(--s-4) 0 0;
}
.woh-link {
  display: inline-flex; align-items: baseline; gap: var(--s-2);
  font-size: var(--fs-lg); color: var(--ink);
  border-bottom: 1px solid color-mix(in oklab, var(--c) 45%, transparent);
  transition: color var(--t-mid) var(--ease), border-color var(--t-mid) var(--ease);
}
.woh-link:hover, .woh-link:focus-visible { color: var(--c); border-color: var(--c); }
.woh-link__u {
  font-family: var(--font-mono); font-size: var(--fs-3xs); color: var(--ink-3);
}

/* --- narrow: the line-up, at rest ---------------------------------------- */
@media (max-width: 1000px) {
  .woh { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .woh__bezel {
    width: 100%; padding: 0; border-radius: var(--r-md);
    background: none; box-shadow: none;
  }
  .woh__cam { display: none; }
  .woh__screen {
    aspect-ratio: auto; padding: var(--s-4);
    border-radius: var(--r-md); border: 1px solid var(--line);
    grid-template-rows: auto auto;
  }
  .woh__team { align-content: start; }
  .woh__win { display: none; }
  /* Three classes deep on purpose: battle-timeline.css names every moving part
     with two — `.woh-l1 .woh-u__hpfill`, `.woh-l1 .woh-fig__sw` — and it is
     linked after this file, so anything shorter loses and the fight keeps
     running here: bars draining behind a still picture, swords left mid-swing. */
  .woh__screen .woh-u,
  .woh__screen .woh-u .woh-u__body,
  .woh__screen .woh-u .woh-u__hpfill,
  .woh__screen .woh-u .woh-fig__leg,
  .woh__screen .woh-u .woh-fig__sw,
  .woh__screen .woh-u .woh-shot,
  .woh__screen .woh-u .woh-spark,
  .woh__screen .woh-u .woh-num,
  .woh__phases .woh-ph { animation-name: none; }
  .woh-shot, .woh-spark, .woh-num { display: none; }
  .woh__screen .woh-u .woh-u__hpfill { transform: scaleX(1); }
  .woh__team--r { align-content: start; gap: 6px; }
  .woh-u__n { font-size: var(--fs-2xs); }
}

@media (prefers-reduced-motion: reduce) {
  .woh__screen .woh-u,
  .woh__screen .woh-u .woh-u__body,
  .woh__screen .woh-u .woh-u__hpfill,
  .woh__screen .woh-u .woh-fig__leg,
  .woh__screen .woh-u .woh-fig__sw,
  .woh__screen .woh-u .woh-shot,
  .woh__screen .woh-u .woh-spark,
  .woh__screen .woh-u .woh-num,
  .woh__win { animation-name: none; }
  .woh-shot, .woh-spark, .woh-num { display: none; }
  .woh__screen .woh-u .woh-u__hpfill { transform: scaleX(1); }
  .woh__screen .woh-u .woh-fig__leg,
  .woh__screen .woh-u .woh-fig__sw { transform: none; }
  .woh__win { opacity: 1; }
}

/* ==========================================================================
   The reel and the screenshots.

   Side by side, both the shape of a phone screen: the demo reel is a short, and
   the store shots are 340×604, so neither has to be cropped to stand beside the
   other.

   The player is a poster and a button until it is clicked — js/case-media.js
   puts the iframe in then — so nothing reaches YouTube on load. The shots are a
   plain scrolling row in the markup and become a slider only when that script
   runs; without it all five are still there and still readable.
   ========================================================================== */
/* Both panels are capped at the screenshots' own 340px: stretched to the width
   of the column they would be upscaled past their resolution, and a page of two
   thousand-pixel phone screens is a page of nothing else. */
.wohm--unused {
  display: grid; grid-template-columns: repeat(2, minmax(0, 340px));
  justify-content: start;
  gap: clamp(16px, 2.4vw, 40px);
  align-items: start;
  margin: var(--s-5) 0 var(--s-6);
}

/* --- the reel ------------------------------------------------------------- */
.wohv { margin: 0; }
.wohv__play, .wohv__frame {
  display: block; width: 100%; height: 100%; border: 0;
  background: #05080f;
}
.wohv__play { position: relative; padding: 0; cursor: pointer; }
.wohv__poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: .5;
  transition: opacity var(--t-mid) var(--ease), transform var(--t-mid) var(--ease);
}
.wohv__play:hover .wohv__poster { opacity: .62; transform: scale(1.03); }
/* A triangle drawn with borders rather than a glyph: it is a shape, not a
   character, and it should not depend on a font. */
.wohv__mark {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 54px; height: 54px; border-radius: 50%;
  background: rgba(6, 10, 20, .74);
  border: 1px solid color-mix(in oklab, var(--c) 55%, transparent);
  box-shadow: 0 0 24px -6px color-mix(in oklab, var(--c) 70%, transparent);
}
.wohv__mark::after {
  content: ""; position: absolute; left: 54%; top: 50%;
  transform: translate(-50%, -50%);
  border: 9px solid transparent; border-left: 14px solid var(--ink);
}
.wohv__label {
  position: absolute; left: 0; right: 0; bottom: 14px;
  font-family: var(--font-mono); font-size: var(--fs-3xs);
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-2);
}
/* One caption under each of the three panels, all the same: the fight's used to
   inherit the page's body size while the other two were set small, so the row
   read as two labels and a sentence. */
.woh-cap {
  margin-top: var(--s-3);
  font-size: var(--fs-2xs); line-height: 1.45; color: var(--ink-3);
  text-align: left;
}

/* --- the screenshots ------------------------------------------------------ */
.wohs { margin: 0; }
.wohs__track {
  list-style: none; margin: 0; padding: 0; height: 100%;
  display: flex; overflow-x: auto;      /* the no-JavaScript state: a row */
  scroll-snap-type: x mandatory;
}
.wohs__slide { flex: 0 0 100%; scroll-snap-align: start; }
.wohs__shot { display: block; width: 100%; height: 100%; object-fit: cover; }
.wohs__dots { display: none; }

/* Switched on by the script: one shot at a time, in the space the row had. */
.wohs.is-slider .wohs__track { display: grid; overflow: hidden; cursor: pointer; }
.wohs.is-slider .wohs__slide {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity .45s var(--ease-out);
}
.wohs.is-slider .wohs__slide.is-on { opacity: 1; visibility: visible; }
/* The dots sit under the slider's own frame, aligned with it rather than
   centred in the column, so the three captions still start on one line. */
.wohs.is-slider .wohs__dots {
  display: flex; gap: 6px; justify-content: center;
  margin-top: var(--s-2);
}
.wohs__dot {
  width: 7px; height: 7px; padding: 0; border-radius: 50%;
  border: 1px solid var(--line-strong); background: none;
  cursor: pointer;
  transition: background var(--t-mid) var(--ease), border-color var(--t-mid) var(--ease);
}
.wohs__dot:hover { border-color: var(--c); }
.wohs__dot.is-on { background: var(--c); border-color: var(--c); }

@media (max-width: 1080px) {
  .wohrow { grid-template-columns: repeat(2, minmax(0, 340px)); }
}
@media (max-width: 720px) {
  .wohrow { grid-template-columns: minmax(0, 340px); }
}
@media (prefers-reduced-motion: reduce) {
  .wohs.is-slider .wohs__slide { transition: none; }
  .wohv__poster { transition: none; }
}
