/* ══════════════════════════════════════════════════════════════
   Component — Battlefield
   Extracted from shoot/v0.4/style.css + v0.8a overrides
══════════════════════════════════════════════════════════════ */

#battlefield { grid-column: 2; position: relative; overflow: hidden; background: var(--bg-primary); }

/* ── Shared SVG overlay layers (vignette, drag, terrain) ── */
.bf-svg-overlay {
  position: absolute; top: 0; left: 0;
  width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}

/* ── SVG group: no user select ── */
.bf-no-select { user-select: none; -webkit-user-select: none; }

/* ── Back to archive link ── */
/* active-faction: generic chrome — themes to the active player's turn. */
.backlink {
  position: absolute; top: 8px; left: 8px; z-index: var(--z-overlay);
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  font: 700 9px/1 'Rajdhani', sans-serif; letter-spacing: 1.8px; text-transform: uppercase;
  color: var(--text-sec); text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--active-faction) 16%, transparent);
  background: rgba(8,12,16,.72);
  backdrop-filter: blur(4px);
  transition: all .15s;
}
.backlink:hover {
  color: var(--active-faction);
  border-color: var(--active-faction-glow);
  background: rgba(8,12,16,.9);
  box-shadow: 0 0 14px var(--active-faction-tint);
}

/* ── Phase Header Pill ── */
#phase-header {
  position: absolute; top: 52px; left: 50%; transform: translateX(-50%);
  width: min(440px, calc(100% - 64px));
  background: transparent; display: flex; flex-direction: column; align-items: stretch;
  gap: 0px;
  z-index: var(--z-overlay); pointer-events: none;
}
/* active-faction: phase pill border + glow + title text track active player. */
.phase-pill {
  margin-top: 8px;
  background: linear-gradient(180deg, rgba(8,12,16,.95), rgba(8,12,16,.84));
  border: 1px solid var(--active-faction-tint);
  border-top: 2px solid var(--active-faction);
  border-radius: 14px;
  padding: 10px 28px 11px;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  pointer-events: auto;
  box-shadow: 0 0 24px color-mix(in srgb, var(--active-faction) 8%, transparent), 0 8px 28px rgba(0,0,0,.54);
  backdrop-filter: blur(8px);
}
/* active-faction: phase title text */
.phase-title {
  font: normal 400 17px/1 'Anton', sans-serif;
  letter-spacing: 4px; color: var(--active-faction); text-transform: uppercase;
  text-align: center;
}
.phase-subtitle {
  display: block;
  width: 100%;
  max-width: 100%;
  font: 700 11px/1.15 'Rajdhani', sans-serif;
  letter-spacing: 1.65px; color: var(--text-muted); text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Battlefield inner (Pixi paints into #canvas-mount inside #stage) ──
 * Pan/zoom is owned by the Pixi renderer's `world` Container, NOT by a
 * CSS transform on this element. Keeping `transform: none` here:
 *   - prevents double-transform when legacy callers still hit
 *     `setCamera()` paths,
 *   - keeps `position: fixed` children (banners, drag-overlays) anchored
 *     to the viewport instead of this element,
 *   - lets `#bf-svg-terrain`-style fixed-position screenshot pins work
 *     for the parity SSIM harness.
 * Don't reintroduce a transform here — pan/zoom belongs in renderer.world. */
/* #stage fits between #vp-bar (top: 48px) and #action-bar (bottom: 48px)
 * so the Pixi canvas stays inside the playable region in /game. The values
 * match the heights declared in shared/css/vp-bar.css and action-bar.css.
 *
 * In forge mode (`body.forge-board-active`) those bars are hidden, so the
 * stage takes the whole battlefield rect — otherwise we'd reserve 96 px
 * of void for invisible chrome and the canvas would render off-centre. */
#stage {
  position: absolute;
  top: var(--vp-bar-height, 48px);
  left: 0; right: 0;
  bottom: var(--action-bar-height, 48px);
  cursor: grab;
  transform: none;
}
body.forge-board-active #stage {
  top: 0; bottom: 0;
}
#stage.dragging { cursor: grabbing !important; }

/* canvas-mount sizing — moved from game/render/pixi/css/pixi.css to
   consolidate Pixi-related chrome rules into shared/css/battlefield.css
   for the main merge.

   #canvas-mount fills the #stage rect (#stage is `position: absolute`,
   not a flex parent — flex props would have no effect). Pixi's
   `_onResize` picks up the new client dims and resizes the WebGL
   surface; the world camera centres + fits the board.

   `!important` on the inner canvas is load-bearing: Pixi's
   `autoDensity: true` sets inline `style.width`/`height` in pixels on
   the canvas (to the logical render size). Inline styles beat
   stylesheet rules by default, so without !important the canvas
   displays at its inline size — which is the *init* size (720×528)
   until the first resize callback fires, and even then may lag by a
   frame. Forcing 100% wins over Pixi's inline values; Pixi's internal
   framebuffer size is still driven by `PixiRenderer._onResize` so
   render resolution stays correct. */
#canvas-mount { width: 100%; height: 100%; min-width: 0; min-height: 0; background: #000; }
/* `transform: translateZ(0)` forces the WebGL canvas onto its own
   permanent GPU compositing layer. Without this, sibling elements with
   `backdrop-filter` (notably `.turn-interstitial` — the deploy roll-off
   modal) sample the canvas as black during their fade-in animation:
   Chrome creates a transient compositing layer for the animated element
   and the backdrop-sampling pass reads stale/empty data until the
   animation completes, at which point it snaps to the correct sample.
   With the canvas pinned to its own permanent layer, backdrop-filter
   sampling is stable from the first frame. SVG didn't need this because
   DOM elements always live in the compositor's reachable tree. */
#canvas-mount canvas { display: block; width: 100% !important; height: 100% !important; cursor: grab; transform: translateZ(0); }

/* Battlefield grid removed — Pixi (scene/board-surface.js) renders the
   grid as part of the WebGL board surface; the CSS pseudo-element was
   a legacy overlay that doubled it up in the SVG era. */

/* SVG layer + .model-base / .unit-hull / .status-badge SVG rules removed in
 * epic #432 PR 19d — Pixi paints models, hulls, and status badges natively.
 * Objective-marker pills (PRIORITY / VIGILANT) still render via
 * createSVGPill() into objective hex <g> groups inside the obj-hex-wrap
 * HTML overlay; their .status-badge class is harmless without the matching
 * CSS rules (Pixi's badges.js handles its own styling). */

/* ── Terrain ── */
.tp-scatter {
  background: rgba(40,50,80,.4);
  border: 1px solid rgba(60,80,120,.2);
  position: absolute;
}

/* ── Objective area rings ── */
.obj-area-ring {
  display: none;
}

/* Range-ring SVG/CSS rules removed in epic #432 PR 4 — Pixi
 * `phase-overlays.js → renderRangeRings` now owns range-ring rendering;
 * radii spec extracted to `core/rules/movement-rules.js → getRangeRingsForPhase`.
 * The legacy `.range-ring` SVG class is still set by
 * `shared/world/range-rings.js` (used by /game/phases/move/move-drag.js
 * and friends until those phase files are migrated to Pixi in sibling
 * PRs). The class without a CSS rule has no visual effect; the SVG
 * `circle` elements still render with their inline `fill`/`stroke`
 * attributes. The `.range-circle` / `.rc-*` / `.range-label` / `.rl-*`
 * CSS-circle approach was unused (search confirmed: only mockups). */

/* ── Objectives (SVG hex) ── */
.obj-hex-wrap {
  position: absolute; transform: translate(-50%,-50%);
  width: 5.7%; aspect-ratio: 84/97; z-index: 1; cursor: default;
  pointer-events: auto;
}
.obj-hex-wrap.controlled { filter: drop-shadow(0 0 8px color-mix(in srgb, var(--faction-a) 50%, transparent)); }
.obj-hex-wrap.enemy      { filter: drop-shadow(0 0 8px color-mix(in srgb, var(--faction-b) 50%, transparent)); }
.obj-svg { display: block; overflow: visible; width: 100%; height: 100%; }
.obj-bg  { fill: rgba(8,12,16,.92); }
.obj-ring { fill: none; stroke-width: 1.5; }
.obj-n {
  font-family: 'Anton', sans-serif; font-size: 22px; font-weight: 400;
  text-anchor: middle; dominant-baseline: central;
}
.obj-l {
  font-family: 'Rajdhani', sans-serif; font-size: 7px; font-weight: 700;
  letter-spacing: 2.5px; text-anchor: middle; dominant-baseline: central;
}
/* Neutral */
.obj-hex-wrap.neutral .obj-ring { stroke: rgba(74,96,128,.55); }
.obj-hex-wrap.neutral .obj-n    { fill: var(--text-sec); }
.obj-hex-wrap.neutral .obj-l    { fill: var(--text-dis); }
/* Player held */
.obj-hex-wrap.controlled .obj-ring { stroke: var(--faction-a); }
.obj-hex-wrap.controlled .obj-bg   { fill: color-mix(in srgb, var(--faction-a) 7%, transparent); }
.obj-hex-wrap.controlled .obj-n    { fill: var(--faction-a); }
.obj-hex-wrap.controlled .obj-l    { fill: var(--faction-a-glow-strong); }
/* Enemy held */
.obj-hex-wrap.enemy .obj-ring { stroke: var(--faction-b); }
.obj-hex-wrap.enemy .obj-bg   { fill: color-mix(in srgb, var(--faction-b) 7%, transparent); }
.obj-hex-wrap.enemy .obj-n    { fill: var(--faction-b); }
.obj-hex-wrap.enemy .obj-l    { fill: var(--faction-b-glow-strong); }

/* SVG hull/model state rules (.unit-hull.shoot-*, .charge-target-selected,
 * .move-zone, zoom-easing filter suppressors) removed in epic #432 PR 19d —
 * Pixi paints all hull/model state visuals natively
 * (`game/render/pixi/scene/eligible-glow.js`, `state-decorations.js`,
 * `phase-overlays.js`). Objective-hex zoom suppressors retained below. */
#stage.zoom-easing .obj-hex-wrap.controlled,
#stage.zoom-easing .obj-hex-wrap.enemy {
  filter: none !important;
}

/* ── Transient phase-invalid toasts ──
   Persistent error/warn banners live in shared/css/banners.css and are
   mounted dynamically by game/core/error-banner-registry.js. Fight pile-in
   placement still uses this transient feedback; charge placement now
   routes through the registry as `BANNER_IDS.INVALID_CHARGE`. */
#fight-invalid-banner {
  box-sizing: border-box;
  text-align: center;
  padding: 6px 12px !important;
  font: 700 10px/1 'Rajdhani', sans-serif !important;
  letter-spacing: 1.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  box-shadow: 0 0 12px rgba(0,0,0,.4);
  position: static !important;
  transform: none !important;
  margin: 0 6px;
  background: rgba(255,140,0,.96);
  color: #fff;
  border: 2px solid #ffaa40;
}

/* ── Version badge ── */
/* active-faction: chrome detail tracks active player. */
.vshoot-badge {
  position: absolute; top: 32px; right: 12px; z-index: var(--z-overlay);
  font: 600 9px/1 'Rajdhani', sans-serif; letter-spacing: 2px; text-transform: uppercase;
  color: var(--active-faction);
  padding: 3px 8px;
  border: 1px solid var(--active-faction-tint);
}

/* ── Global Tooltip ── */
/* owning-faction: tooltip themes to the unit being hovered. The
   tooltip element itself sits directly under <body>, so the showTip
   handler in shared/world/tooltip-helpers.js sets `.side-a` / `.side-b`
   on `#global-tooltip` based on the hovered unit's side, which rebinds
   --owning-faction via colors.css. Terrain tooltips toggle
   `.tooltip-neutral` to swap to gold (no faction owner). */
#global-tooltip.tooltip-neutral { --owning-faction: var(--gold); }
#global-tooltip {
  position: fixed; z-index: var(--z-tooltip); visibility: hidden; opacity: 0;
  background: var(--bg-raised);
  border: 1px solid color-mix(in srgb, var(--owning-faction) 15%, transparent);
  border-top: 2px solid var(--owning-faction);
  color: var(--text-sec); font: 400 12px/1.5 'Rajdhani', sans-serif;
  padding: 7px 12px; pointer-events: none;
  box-shadow: 0 4px 16px rgba(0,0,0,.9);
  width: 240px; max-height: 180px; overflow-y: auto;
  /* Preserve \n line breaks in plain-string tooltips set via showTip
     (the multi-line passenger composition tooltip uses \n separators). */
  white-space: pre-line;
  transition: opacity .15s;
}
#global-tooltip .tip-title {
  font: 600 11px/1 'Rajdhani', sans-serif; letter-spacing: 1px;
  color: var(--owning-faction); text-transform: uppercase; margin-bottom: 6px;
}
#global-tooltip .tip-title-gold {
  color: var(--gold); font: 700 10px/1 'Rajdhani', sans-serif;
  letter-spacing: 2px; text-transform: uppercase;
}
#global-tooltip ul { padding-left: 12px; }
#global-tooltip ul li { margin-bottom: 3px; }

/* Only in Death Does Duty End (#301): pending-death models fade to
   0 opacity over 600ms during end-of-fight-activation cleanup. */
.model-base.model-dying {
  transition: opacity 600ms ease-out;
  opacity: 0;
  pointer-events: none;
}

