/* ==========================================================================
   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) {
  /* This figure is a phone, so on a phone it stays one. The screen keeps its
     shape — 340 x 604 is a phone screen, and at this width it is very nearly
     drawn at its own size — and the fight keeps running: five against two,
     the strikes crossing the lane, the bars draining, the outcome at the
     foot. Everything the column takes away is what only made sense beside
     it, which is the second column.

     What used to be here instead was the fight stopped and the teams relaid
     as two lists. A battle that is not happening is not a battle. */
  .woh { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
}

@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; }
}
