/* tank.tektology.com - the arena screen.

   Layered on top of /assets/css/app.css, which owns the app bar, the pop
   panels, the buttons, the cards, the leaderboard and the toast. This file is
   what is genuinely local to the game: the WebGL canvas, the overlay, the HUD,
   the radar, the control pad, the weapon strip and the garage.

   The layout rule has not changed: the canvas IS the page. It is fixed to the
   viewport, nothing scrolls but the overlay, and everything else floats over
   the top.

   NO INLINE STYLES ANYWHERE - the vhost sends a strict CSP with no
   'unsafe-inline'. That reaches into the JS too: anything that needs a size or
   a colour at runtime uses a class, an SVG attribute or a canvas, and never
   el.style. Every bar on this screen is an SVG rect whose width is an
   attribute, and the four shield quadrants step through five states with a
   data-q attribute rather than a computed height. */

html, body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

#view,
#fx {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}

/* The overlay canvas takes no input - every pointer event on it belongs to the
   3-D underneath, which is what the aiming drag listens to. */
#fx { pointer-events: none; z-index: 2; }

/* Once you deploy, the app bar goes. A game that fills the screen should not
   have a website's navigation across the top of it - and on a phone the bar
   sits exactly where the scoreline wants to be. It comes back the moment you
   leave the arena. */
body.in-game .appbar { opacity: 0; pointer-events: none; }
body.in-game .appbar:focus-within { opacity: 1; pointer-events: auto; }
.appbar { transition: opacity var(--med); }

/* Refitting in your home base puts the garage back over a live game, so the
   overlay needs its scrim back without the game having ended. The SETTINGS
   panel deliberately does not do this - see .settings-panel. */
body.is-refitting .overlay { display: flex; }

/* The app bar comes back while the settings are open. The panel is a window in
   a corner rather than a screen of its own, so a bar faded out behind it just
   looks broken. */
body.in-game.is-settings .appbar { opacity: 1; pointer-events: auto; }

/* ------------------------------------------------------------ scoreline -- */

.scores {
  position: fixed;
  top: var(--gap-t);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
  /* Above the movable controls (.feed is 5, same as the rest) on purpose:
     the battle log's default spot is under this line, but "Move controls"
     can put it - or land it, from a saved layout made on a different
     screen - wherever a player drags it, including right over this. The
     score is the one thing on the HUD nothing may ever cover. */
  z-index: 6;
  pointer-events: none;
  max-width: calc(100vw - 24px);
}

/* Cut corners rather than a pill, at a cut small enough not to eat the team
   dot. Same shape as everything on the site now; see the notch in app.css. */
.sc {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: rgba(7, 10, 16, 0.74);
  border: 1px solid var(--line);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px),
                     calc(100% - 6px) 100%, 0 100%, 0 6px);
  backdrop-filter: blur(6px);
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
}
.sc.is-me { border-color: var(--accent); background: rgba(10, 26, 33, 0.85); }
.sc-dot { width: 9px; height: 9px; flex: none; }
.sc-n {
  color: var(--fg-2);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.sc-v {
  color: var(--fg);
  font-family: var(--mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.sc-f {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 2px 5px;
}
.sc-f.is-carried { background: var(--bad); color: #140407; }
.sc-f.is-dropped { background: var(--warn); color: #1a1204; }

@media (max-width: 640px) {
  .sc-n { display: none; }
  .sc { padding: 4px 7px; }
}

/* ---------------------------------------------------------- battle log -- */

/* THE BATTLE LOG. One window, always there, that you can scroll back through -
   see "the battle log" in main.js. Spec as asked for (2026-09-12): a dark
   background at 70% opacity, light text, colour for meaning, and ONE font -
   the page's own, set on the window and inherited by everything in it, with
   no monospace anywhere inside.

   It TAKES THE POINTER, unlike the kill feed it replaced, because you scroll it
   with a thumb. touch-action pan-y lets that scroll happen even though the
   body refuses every touch gesture; the window is its own scroll container, so
   the body's `none` does not reach into it.

   Movable like the other controls - it is "Battle log" in Move controls. */
.feed {
  position: fixed;
  top: calc(var(--gap-t) + 34px);
  left: var(--gap-l);
  width: min(400px, 46vw);
  max-height: min(34vh, 240px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  pointer-events: auto;
  z-index: 5;
  display: block;
  padding: 6px 4px 6px 0;
  background: rgba(8, 11, 17, 0.7);
  border: 1px solid rgba(150, 200, 235, 0.14);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.4;
  color: #dfe7ef;
  scrollbar-width: thin;
  scrollbar-color: rgba(150, 200, 235, 0.35) transparent;
  /* No text selection: on a tablet a long press would select a line and pop
     the callout over the arena instead of scrolling. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.feed::-webkit-scrollbar { width: 6px; }
.feed::-webkit-scrollbar-thumb { background: rgba(150, 200, 235, 0.35); }

/* The lines themselves (.fe and friends) are in app.css now: the same log
   runs down the side of every page. See battlelog.js. */

/* --------------------------------------------------------------- radar -- */

/* THE RADAR. Cut corners like everything else, and the clip goes through the
   canvas BITMAP as well as the frame - which is the whole reason it needs no
   diagonal hairline of its own: on a panel the diagonal is a background layer
   under the content, and here the content is a canvas that would paint over
   it anyway. The cut edge of the sweep is the edge. */
.radar {
  position: fixed;
  top: var(--gap-t);
  right: var(--gap-r);
  width: 170px;
  height: 170px;
  border: 1px solid var(--line-hard);
  background: rgba(7, 10, 16, 0.55);
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px),
                     calc(100% - 10px) 100%, 0 100%, 0 10px);
  z-index: 5;
  pointer-events: none;
}

@media (max-width: 640px) {
  .radar { width: 108px; height: 108px; top: calc(var(--gap-t) + 30px); }
}

/* -------------------------------------------------------------- banner -- */

/* THE SETTINGS COG. Under the radar, top right, and the only thing on the HUD
   that is a button rather than a readout - the app bar it would otherwise live
   in is faded out and click-through during a match. */
.cogbtn {
  position: fixed;
  top: calc(var(--gap-t) + 178px);
  right: var(--gap-r);
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line-hard);
  background: rgba(7, 10, 16, 0.55);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px),
                     calc(100% - 8px) 100%, 0 100%, 0 8px);
  color: var(--fg-2);
  cursor: pointer;
  /* ABOVE THE PANEL, not below it. The cog is the toggle: press it once to open
     the settings and again to close them. A panel that is 70% of the screen can
     reach the cog on a short window, and a toggle you cannot press the second
     time is a button that looks broken. */
  z-index: 26;
  -webkit-tap-highlight-color: transparent;
}
.cogbtn svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.cogbtn:hover { color: var(--fg); border-color: var(--accent-line); }
.cogbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Lit while the panel is open, so the cog reads as the pressed half of a toggle
   rather than as a button that did nothing. Driving closes the panel without
   anybody touching the cog, and this is what says so. */
body.is-settings .cogbtn {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(7, 10, 16, 0.85);
}

/* Follows the smaller radar up the screen, and gets BIGGER rather than smaller
   - it is a thumb target here, not a mouse one. This has to sit after the rules
   above rather than up with the radar's own media query: same specificity, so
   whichever comes last wins, and up there it silently lost. */
@media (max-width: 640px) {
  .cogbtn { top: calc(var(--gap-t) + 144px); width: 44px; height: 44px; }
  .cogbtn svg { width: 22px; height: 22px; }
}

/* THE SETTINGS PANEL FLOATS. It is not in the overlay and it must never behave
   like it is: no scrim, no blur, nothing covering the arena. The game keeps
   running behind it and the turret still answers, which is the only way to tell
   whether the setting you just changed is the one you wanted.

   IT IS SEVEN TENTHS OF THE SCREEN, CENTRED. It used to be a 372px column in
   the right-hand corner, which kept it out of the way at the cost of turning
   every key binding into a scroll nobody reached the bottom of. What buys
   the middle of the screen now is that DRIVING DISMISSES IT - see DRIVE in
   main.js - so it is never both this big and in the way.

   The corner it still leaves alone is the bottom left: 70vw centred stops well
   short of the driving pad, so a thumb can always reach the control that puts
   this away.

   The cards on /settings/ are a column of separate cards on a page; here it is
   one card, so the section headings do the separating that the card edges did
   there. */
.settings-panel {
  position: fixed;
  /* Seventy per cent of the viewport, dead centre. Deliberately no max-width:
     70% is the size that was asked for, and a cap would quietly make it
     something else on a wide monitor. */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 70vw;
  height: 70vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 24;
  /* Opaque enough to read a key binding against a bright sky, sheer enough that
     the arena is still visibly there behind it.

     SET THE TOKENS, NOT `background`. This element is a .card, and .card paints
     the two notch diagonals and its accent tick as background-IMAGE layers.
     `background:` is a shorthand and resets background-image to none, so
     setting it here left the panel a cut rectangle with no line across either
     diagonal. --panel-fill and --panel-edge are the supported way in; see the
     notch block in app.css.

     No box-shadow either: a clip-path clips it, so it was paying for nothing.
     The bright edge is what separates this from the arena. */
  --panel-fill: rgba(13, 18, 26, 0.94);
  --panel-edge: var(--accent-line);
}
.settings-panel .note { margin: 8px 0 0; }
.settings-panel .switch + .switch { margin-top: 10px; }
/* STICKY TO THE BOTTOM OF THE PANEL'S OWN SCROLL, the same way the controls
   page's bar rides the viewport. This panel scrolls a long way and Save was at
   the end of it. `position: sticky` works against the nearest scrolling
   ancestor, which here is .settings-panel itself. */
.settings-panel .actions-row {
  position: sticky;
  bottom: -1px;
  z-index: 2;
  margin-top: 16px;
  padding: 12px 0 2px;
  background: linear-gradient(180deg, rgba(13, 18, 26, 0),
                                      rgba(13, 18, 26, 0.97) 22%);
}

/* The header carries the close button, so it is a row rather than a stack. */
.settings-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.settings-head h2 { margin-right: 2px; }
.settings-head .eyebrow { flex: 1; min-width: 0; }

.panel-x {
  flex: none;
  width: 28px;
  height: 28px;
  align-self: center;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  background: transparent;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px),
                     calc(100% - 6px) 100%, 0 100%, 0 6px);
  color: var(--fg-2);
  cursor: pointer;
}
.panel-x svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}
.panel-x:hover { color: var(--bad); border-color: var(--bad); }
.panel-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* On a phone 70vw is a column too narrow to put a key binding in, so it takes
   the full width and seven tenths of the HEIGHT instead - the same 70%, in the
   only direction a phone has to spare.

   And it takes the TOP seven tenths, not the bottom. It used to sit on the
   bottom edge, over the pad and the trigger; that was fine while it was a panel
   you closed with the X, and it is wrong now that driving is what dismisses it.
   The pad has to be reachable for the gesture that puts this away to exist. */
@media (max-width: 640px) {
  .settings-panel {
    /* BELOW THE APP BAR, not under it. The bar is z-40 and comes back while the
       settings are open, so a panel starting at the top of the screen has its
       own close button buried under the leaderboard button - on the one device
       where the X is the easiest way out. */
    top: calc(var(--gap-t) + var(--bar-h) + 8px);
    bottom: auto;
    left: var(--gap-l);
    right: var(--gap-r);
    transform: none;
    width: auto;
    height: min(70vh, calc(100vh - var(--bl-bottom) - var(--gap-t) - var(--bar-h) - 16px));
    /* Nearly opaque here, unlike the desktop panel. A phone screen has no open
       sky to float over - this lands on the radar and the feed - and a weapon
       chip showing faintly through a key binding is just noise.
       The token, not the shorthand - see the desktop rule above for why. */
    --panel-fill: rgba(13, 18, 26, 0.985);
  }

  /* AND THE COG STEPS ASIDE WHILE IT IS OPEN. On a phone the panel takes the
     whole width, so there is nowhere left to float the cog that is not on top
     of a switch or a key binding - it landed squarely on a description line.
     The X in the header is the way out here, and it is finger-sized and in the
     corner people already look in. On the desktop there is clear screen beside
     the panel, so there the cog stays up and toggles. */
  body.is-settings .cogbtn { display: none; }
}

/* The one line of text that interrupts a fight. Cut corners and a bright edge
   in the colour of whatever it is telling you, which is what it always did -
   only now the shape says "system message" as well as the colour. */
.banner {
  position: fixed;
  top: 21%;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 8px 18px;
  background: rgba(7, 10, 16, 0.82);
  border: 1px solid var(--line-hard);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px),
                     calc(100% - 9px) 100%, 0 100%, 0 9px);
  color: var(--fg);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  z-index: 6;
  pointer-events: none;
  text-align: center;
  max-width: min(520px, 88vw);
}
.banner[data-kind="dead"] { background: rgba(66, 12, 20, 0.88); border-color: var(--bad); }
.banner[data-kind="flat"] { background: rgba(62, 42, 8, 0.88); border-color: var(--warn); }
.banner[data-kind="flag"] { background: rgba(16, 46, 26, 0.88); border-color: var(--ok); }

/* ----------------------------------------------------------------- HUD -- */

/* The bottom-left corner is ONE COLUMN: the readout, then the pad under it.
   The HUD used to be fixed on its own with `bottom: calc(var(--gap-b) + 168px)`
   - the pad's height plus a gap, typed in by hand. That number cannot exist any
   more now that the pad's size is a slider, and a column does the same job for
   free: whatever the pad measures, the readout sits the same 14px above it.

   It takes no input. Only the pad inside it does, so a drag that misses the box
   still reaches the arena underneath. */
.drivecol {
  position: fixed;
  left: var(--gap-l);
  bottom: var(--gap-b);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  pointer-events: none;
}

/* THE READOUT IS A PLATE NOW, not four bare bars floating on the sky.
   Armour, shield, charge and speed were 6px bars with 9px labels sitting
   directly on the arena, and over a lit horizon the light ones simply were not
   there. A ground of its own, a hairline, and the same cut corners as
   everything else: it costs a rectangle in the corner that was already
   occupied by the readout anyway.

   Still takes no input - only the pad below it does, so a drag that misses the
   box reaches the arena underneath. */
.hud {
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  background: rgba(7, 10, 16, 0.58);
  border: 1px solid var(--line);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px),
                     calc(100% - 9px) 100%, 0 100%, 0 9px);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* The four shield faces, as a ring of quadrants round a hull-shaped hole.
   Each steps through five states by data-q rather than by a computed height,
   because a proportional size would have to be a style. */
.hud-bars { display: flex; flex-direction: column; gap: 6px; }
.hud-row { display: flex; align-items: center; gap: 8px; }
.hud-l {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--fg-3);
  width: 28px;
}

/* Squared and a third taller than it was, with a frame - a 6px rounded sliver
   is a progress bar on a web page; this is a gauge. The rounding came off the
   `rx` attributes in the markup, not from here: an attribute is what the CSP
   leaves alone, and a CSS `rx` would be a second place to look. */
.hud-bar {
  width: 148px;
  height: 9px;
  display: block;
  border: 1px solid var(--line-hard);
}
.hud-bar .b { fill: rgba(150, 200, 235, 0.09); }
#barArmour .f { fill: var(--ok); transition: fill var(--fast); }
/* The shield keeps the pale blue the four face arcs used to be drawn in, so it
   is not mistaken for armour at a glance - the two sit one above the other and
   both drop under fire, and the one you can afford to lose should not look like
   the one you cannot. */
#barShield .f { fill: rgba(140, 220, 250, 0.95); transition: fill var(--fast); }
#barArmour.is-low .f { fill: var(--bad); }
#barShield.is-low .f { fill: rgba(120, 200, 230, 0.42); }

/* THE BATTERY IS A SHADER - chargebar.js draws the fill, its colour and the
   charging and draining animation, so none of that is here. It is a few
   pixels taller than the other two gauges so the moving light has room to be
   seen at a glance, and the frame lights up with it. */
.hud-bar-charge { height: 12px; transition: border-color var(--fast), box-shadow var(--fast); }
.hud-row.is-charging .hud-bar-charge {
  border-color: var(--accent);
  box-shadow: 0 0 10px rgba(53, 214, 240, 0.45);
}
.hud-row.is-charging .hud-l,
.hud-row.is-charging .hud-v { color: var(--accent-hi); }
.hud-row.is-draining .hud-bar-charge {
  border-color: var(--bad);
  box-shadow: 0 0 10px rgba(255, 77, 94, 0.45);
}
.hud-row.is-draining .hud-l,
.hud-row.is-draining .hud-v { color: var(--bad); }
.hud-speed { margin-top: 2px; }
.hud-v {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  width: 30px;
  text-align: right;
  white-space: nowrap;
}

/* The speed, which is the one reading with a UNIT in it - "0 m/s" does not fit
   the 30px column the three gauge figures share, and it wrapped to two lines.
   This has to come AFTER .hud-v: same specificity, so the later rule is the
   one that wins, and the other way round the width never applied. */
.hud-v-wide { width: auto; text-align: left; }

/* The figure goes the colour of its own bar once the bar is in trouble. A red
   bar with a white number beside it makes you read the bar to find out whether
   the number is bad news. */
#barArmour.is-low + .hud-v { color: var(--bad); }
#barCharge.is-low + .hud-v { color: var(--warn); }

@media (max-width: 640px) {
  .hud-bar { width: 104px; }
  .hud { padding: 8px 10px; }
}

/* ----------------------------------------------------------------- pad -- */

/* ONE BOX THAT YOU DRAG, not four buttons. Everything about how it looks that
   can change at runtime is an SVG attribute set by main.js - the size on the
   svg element, the opacity on the group inside it - so THERE IS DELIBERATELY NO
   width, height OR opacity FOR .pad-face HERE. A stylesheet rule outranks a
   presentation attribute, and adding one would leave the size slider moving a
   number that nothing reads.

   The wrapper is only a hit box: it shrink-wraps the svg (hence the flex, which
   also drops the inline-element baseline gap under it) and takes the pointer
   events, so a thumb in a rounded corner still counts as being on the pad. */
.pad {
  display: flex;
  pointer-events: auto;
  touch-action: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* An SVG child would ignore the UA's [hidden] rule, and the wrapper's own
   display:flex overrides it - setInGame toggles this attribute, so say it. */
.pad[hidden] { display: none; }
.pad-face { display: block; }

.pad-bg {
  fill: rgba(7, 10, 16, 0.58);
  stroke: var(--line-hard);
  stroke-width: 1.2;
  transition: stroke var(--fast);
}
.pad-cross {
  fill: none;
  stroke: rgba(150, 200, 235, 0.16);
  stroke-width: 0.9;
  stroke-linecap: round;
}
.pad-dead { fill: none; stroke: rgba(150, 200, 235, 0.2); stroke-width: 0.9; }
.pad-ar {
  fill: none;
  stroke: var(--fg-3);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The thumb marker exists only while a thumb does. A knob parked in the middle
   of an untouched pad reads as a control that is being held at neutral. */
.pad-knob {
  display: none;
  fill: var(--accent-dim);
  stroke: var(--accent);
  stroke-width: 1.6;
}
.pad.is-live .pad-knob { display: block; }
.pad.is-live .pad-bg { stroke: var(--accent); }
.pad.is-live .pad-ar { stroke: var(--accent); }

/* ----------------------------------------------------------------- gun -- */

.gun {
  position: fixed;
  right: var(--gap-r);
  bottom: var(--gap-b);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
  touch-action: none;
}

.mods { display: flex; gap: 5px; }
.mod {
  min-width: 54px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  background: rgba(7, 10, 16, 0.58);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px),
                     calc(100% - 6px) 100%, 0 100%, 0 6px);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--fg-2);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  cursor: pointer;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}
.mod.is-down {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--accent-hi);
  box-shadow: 0 0 12px rgba(53, 214, 240, 0.3);
}
.mod.is-cool { opacity: 0.42; }
/* A SWITCH that is on - the cloak, which is tapped on and off rather than held.
   Lit for as long as it is on, in the pale glass colour the ghost is drawn in,
   so it cannot be mistaken for a held button caught mid-press. */
.mod.is-on {
  background: rgba(40, 90, 110, 0.7);
  border-color: #9fe8ff;
  color: #d8f7ff;
  box-shadow: 0 0 14px rgba(159, 232, 255, 0.45);
}
/* Change tank: only there inside your home base, and amber so it reads as
   something to do rather than one more module. */
#mod-garage { border-color: var(--warn); color: var(--warn); }

/* The weapon strip. One button per bay, in bay order, with its ammunition and
   a reload bar driven off the SERVER's clock rather than a CSS transition -
   which is what the old fire button did and what left it out of step whenever
   the cooldown was retuned. */
.wsel { display: flex; gap: 5px; }
.wpn {
  position: relative;
  min-width: 60px;
  padding: 7px 9px 10px;
  border: 1px solid var(--line);
  background: rgba(7, 10, 16, 0.58);
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px),
                     calc(100% - 7px) 100%, 0 100%, 0 7px);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--fg-2);
  cursor: pointer;
  overflow: hidden;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
/* The gun on the trigger. A lit edge AND a glow: on a bright horizon a border
   colour alone was the difference between two nearly identical dark tiles. */
.wpn.is-on {
  border-color: var(--accent);
  background: rgba(10, 30, 38, 0.85);
  color: var(--fg);
  box-shadow: 0 0 12px rgba(53, 214, 240, 0.28);
}
/* Held, which means this gun is FIRING - the tile is its own trigger, so it has
   to look pressed while a thumb is on it. Same treatment the module buttons
   get, for the same reason: without it there is no way to tell a tile you are
   holding from one you merely selected. */
.wpn.is-down {
  background: var(--accent-dim);
  border-color: var(--accent-hi);
  color: var(--accent-hi);
  box-shadow: 0 0 16px rgba(53, 214, 240, 0.42);
}
.wpn.is-dry { opacity: 0.45; }
.wpn-n {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* The key that fires this gun, printed on the tile - see buildWeapons. Which
   key it is depends on the loadout, so it cannot live in the settings page or
   in anyone's head; it lives next to the weapon. Empty when the slot is
   unbound, and the :empty rule stops that leaving a gap. */
.wpn-k {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 1px 4px;
  background: rgba(150, 200, 235, 0.12);
  color: var(--fg-3);
}
.wpn-k:empty { display: none; }
.wpn.is-on .wpn-k, .wpn.is-down .wpn-k { color: var(--fg); }
.wpn-a {
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.wpn-r {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 3px;
  display: block;
}
.wpn-r rect { fill: var(--accent); }
.wpn.is-loading .wpn-r rect { fill: var(--warn); }

/* THE TRIGGER STAYS ROUND. Everything else on this screen has its corners cut
   and this deliberately does not: it is the one control a thumb has to find
   without looking, and round is the shape a thumb finds. */
.firebtn {
  position: relative;
  width: 86px;
  height: 86px;
  border-radius: 50%;
  border: 1px solid var(--bad);
  background: rgba(58, 14, 22, 0.5);
  color: #ffc3c9;
  display: grid;
  place-items: center;
  cursor: pointer;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--fast), border-color var(--fast);
}
.firebtn.is-down {
  background: rgba(168, 34, 48, 0.78);
  border-color: #ff8f9a;
  box-shadow: 0 0 22px rgba(255, 77, 94, 0.45);
}

/* Centre-the-gun, beside the trigger. Smaller than the trigger on purpose: it
   is used between fights rather than during one, and a second 86px circle under
   the thumb is one the thumb finds by accident. */
.gun-row { display: flex; align-items: flex-end; gap: 10px; }

.centrebtn {
  width: 52px;
  height: 52px;
  margin-bottom: 17px;              /* sits on the trigger's centre line */
  border-radius: 50%;
  border: 1px solid var(--line-hard);
  background: rgba(7, 10, 16, 0.6);
  color: var(--fg-2);
  display: grid;
  place-items: center;
  padding: 0;
  cursor: pointer;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}
.centrebtn svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.centrebtn:hover { color: var(--fg); border-color: var(--accent); }
.centrebtn.is-on { color: var(--accent); border-color: var(--accent); }
.centrebtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.firebtn.is-loading { opacity: 0.55; }
.fire-ico { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

@media (max-width: 640px) {
  .firebtn { width: 74px; height: 74px; }
  .wpn { min-width: 48px; padding: 5px 6px 8px; }
}

/* -------------------------------------------------------------- garage -- */

/* The garage does NOT scroll itself - the overlay does.
   A card with its own max-height inside a grid overflows its track the moment
   the track is shorter than the cap, and lands on top of whatever is in the
   next row. Letting the one scroll container be the overlay means there is
   only ever one, and it works at any screen size. */
.garage { width: min(940px, 100%); }

.g-head h2 { margin: 0 0 4px; }

/* A section heading inside the garage. Accented and ruled, the same way the
   key-binding groups are on the controls page - this panel is five sections
   deep and the headings are what stop it reading as one long list. */
.g-h3 {
  margin: 20px 0 8px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

.g-presets { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.g-preset-list { display: flex; gap: 6px; flex-wrap: wrap; }
.g-preset {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 6px 11px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px),
                     calc(100% - 6px) 100%, 0 100%, 0 6px);
  color: var(--fg);
  font-size: 12px;
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}
.g-preset:hover { border-color: var(--accent); background: var(--surface-3); }
.g-preset-ch {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* The four chassis, as cards. A horizontal scroller on a narrow screen rather
   than a wrapping grid: four cards that reflow to one column make the page
   enormous, and the whole point is comparing them side by side. */
.g-chassis {
  display: grid;
  grid-template-columns: repeat(4, minmax(168px, 1fr));
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 4px;
}
.g-card {
  text-align: left;
  padding: 12px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px),
                     calc(100% - 9px) 100%, 0 100%, 0 9px);
  color: var(--fg);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 7px;
  transition: border-color var(--fast), background var(--fast);
}
.g-card:hover { border-color: var(--line-hard); background: var(--surface-3); }
/* The hull you are about to drive. A lit edge, a lit name and a bar down the
   side - on a row of four dark cards the border colour alone was easy to lose
   at a glance. */
.g-card.is-on {
  border-color: var(--accent);
  background: rgba(10, 30, 38, 0.8);
  box-shadow: inset 2px 0 0 var(--accent);
}
.g-card.is-on .g-card-name { color: var(--accent); }
.g-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; }
.g-card-name { font-weight: 650; font-size: 15px; }
.g-card-role {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.g-card-blurb { margin: 0; font-size: 11.5px; line-height: 1.45; color: var(--fg-2); }
.g-builtin { margin: 0; font-family: var(--mono); font-size: 10.5px; color: var(--gold); }

.g-stats { display: flex; flex-direction: column; gap: 4px; }
.gstat { display: grid; grid-template-columns: 48px 1fr 30px; align-items: center; gap: 6px; }
.gstat-l {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.09em;
  color: var(--fg-3);
  text-transform: uppercase;
}
.gstat-t { width: 100%; height: 7px; display: block; border: 1px solid var(--line); }
.gstat-bg { fill: rgba(150, 200, 235, 0.07); }
.gstat-f { fill: var(--fg-3); }
.gstat-f.is-hp { fill: var(--ok); }
.gstat-f.is-sp { fill: var(--accent); }
.gstat-f.is-tn { fill: #8f7fd8; }
.gstat-f.is-bt { fill: var(--gold); }
.gstat-f.is-by { fill: #d78f5f; }
.gstat-v {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  color: var(--fg-2);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.g-bayhead { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.g-bays-left {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
}
.g-bays-left.is-full { color: var(--warn); }

.g-fitted { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
/* A fitted bay. The left edge is the tell: steel for a gun or a module you
   chose, gold for the one welded to the hull, dashed for an empty slot. */
.g-fit {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--line-hard);
  background: var(--surface-2);
  font-size: 13px;
}
.g-fit.is-builtin { color: var(--gold); border-left-color: var(--gold); border-style: dashed; }
.g-fit.is-empty {
  color: var(--fg-3);
  justify-content: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  border-style: dashed;
}
.g-fit-n { flex: 1; }
.g-fit-b { font-family: var(--mono); font-size: 10.5px; color: var(--fg-3); }
.g-fit-x {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg-3);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 4px 9px;
  cursor: pointer;
}
.g-fit-x:hover { border-color: var(--bad); color: var(--bad); background: rgba(255, 77, 94, 0.12); }

.g-tabs { display: flex; gap: 6px; margin: 16px 0 10px; }
.g-tab {
  padding: 7px 15px;
  border: 1px solid var(--line);
  background: transparent;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px),
                     calc(100% - 7px) 100%, 0 100%, 0 7px);
  color: var(--fg-3);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--fast), background var(--fast), border-color var(--fast);
}
.g-tab:hover { color: var(--fg-2); border-color: var(--line-hard); }
.g-tab.is-on { border-color: var(--accent); color: var(--accent-hi); background: var(--accent-dim); }

.g-shop {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}
.g-item {
  padding: 12px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px),
                     calc(100% - 9px) 100%, 0 100%, 0 9px);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.g-item.is-off { opacity: 0.5; }
.g-item-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.g-item-n { font-weight: 620; font-size: 13.5px; }
.g-item-b {
  font-family: var(--mono);
  font-size: 9.5px;
  color: var(--fg-3);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.g-item-blurb { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--fg-2); }
.g-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--fg-3);
}
.g-fact b { color: var(--accent); font-weight: 600; }
.g-item-add {
  align-self: flex-start;
  padding: 6px 13px;
  border: 1px solid var(--line-hard);
  background: var(--surface-3);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px),
                     calc(100% - 6px) 100%, 0 100%, 0 6px);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color var(--fast), color var(--fast), background var(--fast);
}
.g-item-add:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent-hi);
  background: var(--accent-dim);
}
.g-item-add:disabled { cursor: default; color: var(--fg-3); }

/* .btn-quiet used to be redefined here, with a flat `background` and a
   `border`. That now fights app.css, where a button's rank is expressed as
   --panel-fill and --panel-edge and a plain `background` wipes the notch
   diagonals off it. app.css owns the button ranks; this file has no business
   restating one. */
.g-actions { margin-top: 20px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* "Connecting to the battle", beside the Deploy button it answers - where the
   thumb that pressed it already is. It was first a bar stuck to the bottom of
   the overlay, and that sat on top of the button whenever the button was near
   the bottom of the screen, which is where you press it. */
.g-wait { margin: 0 0 0 8px; font-size: 13.5px; color: var(--fg); }

@media (max-width: 640px) {
  .g-chassis { grid-template-columns: repeat(4, 168px); }
  .g-shop { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------- overlay -- */

/* Flex column rather than grid. With grid, a child taller than its implicit
   row overflows the row and sits on top of the next one; a column flex
   container grows to whatever its children need and lets this element scroll,
   which is what a stack of cards of unknown height actually wants. */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: calc(var(--bar-h-game) + 16px) 12px 24px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(6, 8, 12, 0.72);
  backdrop-filter: blur(3px);
}
.overlay[hidden] { display: none; }
.overlay > .card { width: min(940px, 100%); flex: none; }

/* The tournament banner rides the top of the garage and goes when the garage
   does - the arranger, the start card, a respawn wait - and during a refit,
   which is a match still running behind the scrim. */
.overlay > .tbanner { width: min(940px, 100%); flex: none; margin: 0; }
.overlay:has(> #garage[hidden]) > .tbanner,
body.is-refitting .overlay > .tbanner { display: none; }

/* "Getting your garage ready", where the garage is about to be (see .wait in
   app.css). */
.overlay > .garage-wait { margin: 0; padding: 16px 20px; font-size: 14px; }

/* THE BATTLE LOG PANEL ON THIS PAGE (see "battle log" in app.css). It is up
   whenever you are editing something here - the garage before you deploy, a
   refit in your base, the settings panel over a match - and the thing being
   edited moves over to make room for it. In a plain match the panel goes and
   the movable in-match log (.feed) carries the battle instead; while the
   panel is up that one steps aside, so the battle is never shown twice.

   This page's bar is the shorter floating one. */
body { --bl-top: var(--bar-h-game); }
body.has-battle .overlay { right: var(--bl-right); bottom: var(--bl-bottom); }
body.has-battle .feed { display: none; }

/* IN A MATCH THE PANEL GOES UNDER THE HUD, never over it. It comes up over a
   live match while the settings panel is open, and at app.css's z-index 35 it
   sat on top of every control in its column: the trigger, the centre button,
   the weapon tiles, the modules and the cog on a wide screen, and on a tablet
   held upright - where it is a band across the bottom - the driving pad as
   well. That made "driving closes the panel" and "try the gun with the panel
   open" impossible, and a second tap on the cog landed on the log. Found
   2026-09-13 while making every control work at once. Here it sits over the
   arena (#fx is 2) and under the controls (5 and 6), the settings panel (24)
   and the cog (26); the controls float over the log's lines, as they do over
   the arena. */
body.in-game .battle { z-index: 4; }

/* Above the phone breakpoint only: below it the panel is full width from the
   top (see the 640px rule further down), and this rule's higher specificity
   would drag it back to the centre. */
@media (min-width: 641px) {
  body.has-battle.in-game .settings-panel {
    left: calc((100vw - var(--bl-right)) / 2);
    top: calc((100vh - var(--bl-bottom)) / 2);
    width: min(70vw, calc(100vw - var(--bl-right) - 24px));
    height: min(70vh, calc(100vh - var(--bl-bottom) - var(--bar-h-game) - 24px));
  }
}

/* ------------------------------------------------------------- arrange -- */

/* MOVING THE CONTROLS - see layout.js. A control that has been put somewhere
   of its own carries .is-placed and is positioned by a rule layout.js writes
   into this sheet (`#pad.is-placed { left; top }`), because this page has no
   inline styles. Nothing here gives any control a position.

   While the arranger is up every movable control takes the pointer - the
   readout normally lets a drag through to the arena, and has to stop doing
   that to be picked up - and none of them may start a text selection, a
   callout or a scroll under a thumb. */
body.is-arranging .is-movable {
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
body.is-arranging .is-movable.is-dragging { cursor: grabbing; }

/* An EMPTY strip still has to be something to hold. A hull with no modules
   fitted has no module buttons, and a control with no size cannot be found,
   framed or dragged - so while arranging, an empty row keeps a thumb-sized
   footprint. The module row is never literally empty - it always holds the
   Change tank button, hidden outside the home base - so it is "empty" when
   nothing in it is showing. */
body.is-arranging .wsel:empty,
body.is-arranging .mods:empty,
body.is-arranging .mods:not(:has(> :not([hidden]))) {
  min-width: 96px;
  min-height: 34px;
  border: 1px dashed var(--line-hard);
  background: rgba(7, 10, 16, 0.4);
}

/* The frames and name tags are drawn on the overlay canvas, which normally
   sits under the HUD. Arranging, it comes up OVER the controls, so a tag that
   had nowhere clear to go in a crowded corner is still readable on top of
   one. It never takes a pointer, so the drag still lands on the control. */
body.is-arranging #fx { z-index: 30; }

/* The app bar would sit exactly where the arrange bar does. */
body.is-arranging .appbar { opacity: 0; pointer-events: none; }

.arrange-bar {
  position: fixed;
  top: var(--gap-t);
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 14px;
  width: max-content;
  max-width: calc(100vw - 20px);
  padding: 10px 12px 10px 16px;
  border: 1px solid var(--accent);
  /* See-through enough to show the frame of a control underneath it, and
     click-through everywhere except the buttons, so that control can still be
     picked up - layout.js keeps the buttons themselves off the controls. */
  background: rgba(7, 12, 18, 0.8);
  pointer-events: none;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px),
                     calc(100% - 10px) 100%, 0 100%, 0 10px);
}
/* Carried to the bottom of the screen while a control is being dragged up to
   where the bar was. */
.arrange-bar.is-low { top: auto; bottom: var(--gap-b); }
/* And to the middle when both edges are full of controls - see settleBar. */
.arrange-bar.is-mid { top: 50%; transform: translate(-50%, -50%); }
.arrange-say { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.arrange-say b { font-size: 14px; color: var(--fg); }
.arrange-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.arrange-btns .btn { pointer-events: auto; }

@media (max-width: 640px) {
  .arrange-bar { flex-direction: column; align-items: stretch; gap: 8px; }
  .arrange-btns { justify-content: flex-end; }
}

/* The in-game panel's lone button row for Move controls. */
.actions-inline { margin-top: 10px; display: flex; gap: 8px; }
