/* Copyright (c) 2026 Pavel Bardukov. All rights reserved. See LICENSE. */
/* Fit stats window styles, ported from fleet-manager-online web/styles.css. */

@font-face{
  font-family:"FM Display";
  src:url('fonts/orbitron-700.woff2') format('woff2');
  font-weight:700; font-style:normal; font-display:swap;
}

:root{
  --bg-deep:#000000; --bg-panel:#121216; --bg-card:#1a1a20; --bg-header:#0a0a0d;
  --border:#2b2b33; --border-lt:#3c3c46;
  --accent:#4fc3f7; --accent2:#00bcd4;
  --text:#c8d8e8; --dim:#6a8a9a;
  --green:#4caf50; --red:#ef5350; --yellow:#f0c040; --blue:#4a9eff;
  --purple:#b06fe6; --amber:#e0a526;
  /* The four damage types, sampled from the game's own fitting window. ⚠️ FIXED,
     never repainted by applyTheme(): a pilot reads EM-blue off the game and off us
     in the same glance, and eleven faction themes must not touch that. Declared once
     because two surfaces wear them now — the fit window's resist grid and the
     Sansha NPC table — and two copies of a colour is two colours eventually. */
  --dt-em:#2075a5; --dt-th:#a42020; --dt-kin:#8b8b8b; --dt-exp:#a47420;
  --primary-bg:#0d3a5a; --primary-bg-hover:#1a5070;
  /* The 3D button recipe, declared once so it can be tuned in one place (see the buttons
     section below for why it is built out of translucent white/black rather than colours). */
  --btn-face:linear-gradient(180deg, rgba(255,255,255,.07), rgba(0,0,0,.22));
  --btn-face-hover:linear-gradient(180deg, rgba(255,255,255,.12), rgba(0,0,0,.18));
  --btn-face-down:linear-gradient(180deg, rgba(0,0,0,.22), rgba(255,255,255,.04));
  /* --btn-edge = top edge catching the light, then a hard inner edge (the machined look),
     then the drop that makes the button sit ON the panel rather than in it. Keep comments
     OUT of the value: a custom property stores its token stream verbatim, comments and all. */
  --btn-edge:inset 0 1px 0 rgba(255,255,255,.18), inset 0 0 0 1px rgba(0,0,0,.35), 0 1px 0 rgba(0,0,0,.6);
  --btn-edge-down:inset 0 2px 3px rgba(0,0,0,.55), inset 0 0 0 1px rgba(0,0,0,.4);
  /* Shallower press for the 18px-tall roster toggles, which pair it with a coloured ring. */
  --btn-edge-down-sm:inset 0 1px 3px rgba(0,0,0,.5);
  /* Every control label wears the display face; body text and tables keep Segoe UI. Orbitron
     runs ~18% wider per glyph, so the negative tracking is not decoration — it is what keeps
     the FC roster's width:1% columns from pushing the table wider. */
  --font-display:"FM Display", "Segoe UI", Arial, sans-serif;
  --display-tracking:-.3px;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{
  background:
    linear-gradient(rgba(0,0,0,0.45), rgba(0,0,0,0.45)),
    url('bg.jpg') center center / cover no-repeat fixed,
    var(--bg-deep);
  color:var(--text);
  font-family:"Segoe UI",Arial,sans-serif; font-size:13px;
}
.hidden{display:none !important}

.spacer{flex:1}

.muted{color:var(--dim)}

button{
  background-color:var(--bg-panel); color:var(--text);
  background-image:var(--btn-face);
  border:1px solid var(--border); border-radius:3px;
  box-shadow:var(--btn-edge);
  padding:6px 7px; font-size:12px; cursor:pointer;
  font-family:var(--font-display); letter-spacing:var(--display-tracking);
  transition:background-color .12s, box-shadow .1s, border-color .12s, color .12s;
}
button:hover{border-color:var(--accent); color:var(--accent); background-image:var(--btn-face-hover)}
/* Pressed, and — the point of this — every toggle that is ON. In the FC roster a row carries
   up to eight buttons whose on/off differed only by tint; now it also differs by shape. */
button:active,button.on,button.active{
  transform:translateY(1px);
  background-image:var(--btn-face-down);
  box-shadow:var(--btn-edge-down);
}

button.seg{color:var(--dim); font-size:11px; padding:4px 6px}
button.seg.active{background-color:var(--primary-bg); color:var(--accent); border-color:var(--accent)}

/* ── flat by design: <button> elements that are NOT pressable ──────────────────────────
   Volume is the affordance, so anything the user cannot press must give it back. These are
   all real <button>s that would otherwise inherit the bevel and lie about being clickable.
   .popup-item is the load-bearing one: without it every context menu (right-click a pilot,
   Transfer fleet, guest groups A/B/C) turns into a stack of raised tiles.

   ⚠️ Every selector here is qualified with `button` on purpose. Unqualified `.popup-item`
   (0,1,0) LOSES to `button.on` (0,1,1) above. Qualified, they tie on specificity and win on
   source order, which is why this block must stay BELOW the base. */
button.popup-item,                              /* menu rows, not buttons */
button:disabled{
  background-image:none; box-shadow:none; transform:none;
}

table{width:100%; border-collapse:collapse}
th{
  background:var(--bg-header); color:var(--dim); text-align:left;
  padding:6px 8px; font-size:11px; font-weight:bold; border-bottom:1px solid var(--border);
}
td{padding:6px 8px; border-bottom:1px solid var(--border)}

.modal-overlay{position:fixed; inset:0; background:rgba(0,0,0,.6); display:flex; align-items:center; justify-content:center; z-index:1000}

.fit-close{background-color:transparent; border:1px solid var(--accent); color:var(--accent);
  border-radius:3px; padding:3px 10px; font-size:14px; line-height:1.1}
.fit-close:hover{background-color:rgba(255,255,255,.10)}

.imp-tip{position:fixed; z-index:1307; max-width:min(380px, 92vw);
  background-color:var(--bg-panel); border:1px solid var(--accent); border-radius:5px;
  padding:8px 10px; font-size:12px; box-shadow:0 6px 20px rgba(0,0,0,.5); pointer-events:none}
.imp-title{color:var(--text); font-weight:bold; margin-bottom:4px}
.imp-slot{color:var(--dim); font-size:11px; margin-bottom:6px;
  border-bottom:1px solid var(--border); padding-bottom:4px}
/* "the item → the item as this pilot flies it" — says what the two numbers below are. */
.imp-note{color:var(--muted); font-size:10px; margin-bottom:4px}
.imp-line{display:flex; gap:12px; justify-content:space-between; padding:1px 0}
.imp-a{color:var(--dim)}
.imp-v{color:var(--accent); font-weight:bold; white-space:nowrap}
/* `data-tip="type:<id>"` panels (and the description under a slot's / implant's numbers).
   The panel never takes the pointer, so it cannot scroll: a very long text is cut at the screen. */
.imp-tip{max-height:calc(100vh - 12px); overflow:hidden}
.tip-head{display:flex; align-items:center; gap:8px; margin-bottom:4px}
.tip-ico{width:32px; height:32px; flex:0 0 32px}
.tip-headtext{min-width:0}
.tip-head .imp-title{margin-bottom:0}
.tip-group{color:var(--dim); font-size:11px}
.tip-desc{white-space:pre-wrap; color:var(--text); font-size:11px; line-height:1.35;
  margin:6px 0; padding-top:6px; border-top:1px solid var(--border)}
.tip-attrs{border-top:1px solid var(--border); padding-top:4px}

.cant-fly-banner{font-weight:bold; margin:0 0 10px}
.cant-fly-banner.below-perfect{color:var(--amber)}

.skills-panel{margin-top:6px}
/* The panel's head is a spoiler, shut by default: the SP still to train and the missing count
   read without opening it. Looks like a stat section's head. */
button.sk-spoiler{display:flex; align-items:baseline; gap:6px; width:100%; margin:0; padding:6px 10px;
  text-align:left; font-size:12px; font-weight:normal; text-transform:uppercase; letter-spacing:.5px;
  color:var(--dim); background-color:var(--bg-deep); border:1px solid var(--border); border-radius:4px}
button.sk-spoiler:hover{color:var(--text); border-color:var(--accent2)}
.skills-panel.has-missing button.sk-spoiler{color:var(--amber)}
button.sk-spoiler b{color:var(--accent); font-weight:bold; text-transform:none; letter-spacing:0; white-space:nowrap;
  font-family:var(--font-display)}
button.sk-spoiler .warn{text-transform:none; letter-spacing:0}
.sk-caret{flex:0 0 10px; font-size:11px}
.sk-body{padding:6px 2px 0}
/* Fold the prerequisites back in: a real button at the top of the open panel. */
button.seg.sk-toggle{margin:0 0 6px; padding:2px 8px; font-size:11px}
/* A prerequisite shown only because it's missing (or "show all" is on) — dimmer name, so the
   skills the fit asks for in its own right still read first. */
.sk-row.implied .sk-name{color:var(--dim)}
.sk-row.implied.missing .sk-name{color:var(--red)}
/* Grid, not wrapped flex: with `flex:1 1 300px` the last line's few items grew to fill the
   width and drifted out of line with the columns above them. A grid keeps every cell the
   width of its column whether the row is full or not. */
.sk-list{display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr));
  gap:2px 18px}
.sk-group{margin-bottom:8px}
.sk-group h5{margin:8px 0 3px; font-size:11px; letter-spacing:.5px; text-transform:uppercase;
  color:var(--accent); border-bottom:1px solid var(--border); padding-bottom:2px}
.sk-group:first-of-type h5{margin-top:0}
.sk-row{display:flex; align-items:center; gap:8px; min-width:0; font-size:12px;
  color:var(--text); padding:1px 0}
.sk-row.missing .sk-name{color:var(--red)}
.sk-name{flex:1 1 auto}
/* The roman level has a column of its own, so the numerals line up down the panel instead
   of drifting with the length of each skill's name. */
.sk-lvl{flex:0 0 24px; text-align:right; color:var(--dim)}
.sk-row.missing .sk-lvl{color:var(--red)}
.lvl-bars{display:inline-flex; gap:2px; flex:0 0 auto}
.lvl-box{width:8px; height:10px; border:1px solid var(--border-lt); border-radius:1px}
.lvl-box.on{background:var(--accent); border-color:var(--accent)}
.lvl-box.want{border-color:var(--red); background:transparent}
/* Untrained levels the FLEET asks for, above what the game demands. */
.lvl-box.want-min{border-color:var(--amber); background:transparent}
.lvl-box.want-perfect{border-color:var(--green); background:transparent}
.sk-row.below-min .sk-name{color:var(--amber)}
/* A click on a row sets a temporary level (the editor's menu). */
.sk-row[data-skill]{cursor:pointer; border-radius:2px}
.sk-row[data-skill]:hover{background-color:rgba(255,255,255,.05)}
/* A temporary level: the badge in the accent, the name in italics. */
.sk-row.sk-ovr .sk-name{font-style:italic}
.sk-ovr-mark{flex:0 0 auto; color:var(--accent); font-size:11px; font-weight:bold; white-space:nowrap;
  border:1px solid var(--accent2); border-radius:3px; padding:0 4px; line-height:14px}
/* The spoiler and the Reset skills button share one row when any level is overridden. */
.sk-head{display:flex; align-items:stretch; gap:4px}
.sk-head button.sk-spoiler{flex:1 1 auto; width:auto; min-width:0}
button.seg.sk-reset{flex:0 0 auto; margin:0; padding:2px 8px; font-size:11px; white-space:nowrap}
.ok-v{color:var(--green)}

/* No rule above it any more (2026-09-27, the user's request): Ammo is one more row like Drones
   and Implants, spaced like them. */
.ammo-row{display:flex; align-items:center; gap:8px; margin:0 0 6px}
.ammo-picks{display:flex; flex-wrap:wrap; align-items:center; gap:4px}
/* The picker is as narrow as its content: the weapon icon and the word `empty` when nothing
   is loaded, and the ROUND's icon alone once something is. A round names itself by its
   picture the way every other item here does. */
/* 38×38 with a 32 px picture - the size of every cell in the Implants, Boosters and Bonuses
   rows under it (2026-09-27, the user's request: the rounds had been 24 px and the row sat
   glued to the next one). An EMPTY picker is as wide as the word it carries. */
.ammo-btn{display:inline-flex; align-items:center; justify-content:center; gap:4px;
  min-width:38px; height:38px; margin:0; padding:0 2px; line-height:0; box-sizing:border-box;
  background-color:var(--bg-deep); border-color:var(--border)}
.ammo-btn:hover{border-color:var(--accent)}
.ammo-btn.on{border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent),
  var(--btn-edge-down-sm)}
.ammo-empty{font-size:11px; line-height:1; color:var(--dim); letter-spacing:.3px}
.ammo-mod{width:32px; height:32px}
/* Where the manual picks end and the hold begins. */
.ammo-sep{display:inline-block; width:1px; height:30px; margin:0 3px;
  background-color:var(--border-lt)}
/* The round list. ⚠️ On <body>, like every popup here: the editor's columns each have their
   own `overflow-y` and would clip a panel parented inside one.
   ⚠️ `right` and `bottom` MUST be reset, AND the selector has to out-specify `.popup`.
   `.popup` pins itself to the bottom-right of a RELATIVE parent (`right:0; bottom:calc(100% +
   6px)`), and this one is `fixed` and placed by JS with `left`/`top` — with both edges set on
   an axis the box stretches between them, which is why it first appeared as an empty band
   running to the right edge of the window instead of a list. `.ammo-popup` alone does NOT win:
   it ties with `.popup` on specificity and is declared EARLIER in this file, so the later rule
   takes it. The same trap as `.ammo-opt` against `.popup-item` below. */
.popup.ammo-popup{position:fixed; right:auto; bottom:auto; z-index:1310;
  max-height:min(420px, 70vh); overflow-y:auto; min-width:250px}
/* ⚠️ Scoped under `.ammo-popup`, and that is load-bearing: `.popup-item{display:block}` is
   declared LATER in this file, and at equal specificity the later rule wins — so an
   unqualified `.ammo-opt{display:flex}` is silently dead. */
.ammo-popup .ammo-opt{display:flex; align-items:center; gap:8px; width:100%; text-align:left}
.ammo-popup .ammo-opt-ico{flex:0 0 24px; line-height:0}
.ammo-popup .ammo-opt-ico img{width:24px; height:24px}
.ammo-popup .ammo-opt-name{flex:1 1 auto; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap}
.ammo-popup .ammo-opt-dmg{flex:0 0 auto; color:var(--dim); font-size:11px}
.ammo-popup .ammo-opt.on .ammo-opt-name{color:var(--accent)}

.stat-head{display:flex; align-items:center; gap:12px; margin-bottom:12px}
/* Copy the EFT block to the clipboard — the fit exists to be pasted into the game. A fifth
   larger than a plain .seg: it is one of the two things a pilot came here to press. */
/* ⚠️ `button.` is load-bearing, not tidiness. Every copy button in the app is written
   `class="seg copy-fit"`, and `button.seg{color:var(--dim)}` is (0,1,1) against a bare
   `.copy-fit`'s (0,1,0) — so the accent LOST on specificity and all three of them rendered
   grey, whatever the theme. Qualified they tie, and this rule is the later one. */
button.copy-fit{color:var(--accent); border-color:var(--accent); white-space:nowrap;
  font-size:13px; padding:5px 8px}
button.copy-fit:hover{background-color:rgba(255,255,255,.10)}
button.copy-fit.done{color:var(--green); border-color:var(--green)}
/* Nothing ticked = nothing to copy. Without this it wears the full accent and reads as a
   live button that does nothing when pressed — there is no global dimming for :disabled. */
button.copy-fit:disabled{opacity:.45; cursor:not-allowed}

/* ── The fitting ring, as the game draws it ───────────────────────────────────────────
   The ring itself is SVG; the slots on it are ordinary HTML placed by trigonometry, so a
   slot keeps every class, data-attribute and handler it had in the old flat racks. */
/* A fixed 480 so the ring is a known square; it may SHRINK but never grow, because a
   half-empty circle stretched across a wide window reads as a mistake. At 968 px of
   content the panel beside it still gets ~470, which is what its two columns want. */
.fitwin{flex:0 1 480px; min-width:280px; max-width:100%}
/* The slot size follows the ring, so a narrow window shrinks the slots instead of letting two
   of them overlap. ⚠️ A percentage and no px term: the SVG bed under each slot is sized in
   viewBox units from the same number, and a px cap would let the two disagree on a narrow
   window. 7.95 % of the box is the game's own ratio. The fallback only; `fitRing` computes
   the real one from the pitch its solver chose. */
.fit-ring{position:relative; width:100%; aspect-ratio:1/1; margin-bottom:10px;
  --ring-slot:7.95%}
.ring-bg{position:absolute; inset:0; width:100%; height:100%; pointer-events:none}
/* ⚠️ TWO backdrop layers, and the order is the whole point: `.ring-under` carries the floor
   and lies BENEATH the hull picture, `.ring-over` carries the slot beds, the rim and the
   gauges and lies above it. A picture that fills the disc would otherwise bury all of them. */
.ring-bg.ring-over .ring-track{fill:none; stroke:var(--border-lt); stroke-width:1.5}
/* Outside the disc is BLACK in the game, not the card — a dark annulus carrying the two load
   gauges. A circle rather than a rectangle: a black square inside a rounded card reads as a
   fault, and the corners are meant to stay card-coloured. */
.ring-bg .ring-void{fill:#000}
/* The floor, in the game's own colour, measured off its window: rgb(39,56,69). Fixed hex for
   the reason the four damage types are — a pilot compares it with the game, and the eleven
   faction themes must not repaint it. Hidden behind the hull picture on any ordinary day, and
   kept because `images.evetech.net` is not always reachable: without it the circle would be a
   hole rather than a fitting window. */
.ring-bg .ring-fill{fill:#273845}
/* The holographic floor under the hull, as in the game. Same reason it survives: it is what
   is left when the picture does not arrive. */
.ring-grid line{stroke:#bee6f2; stroke-width:.8; opacity:.34}
/* The radial lines between two racks, centre to rim. Fixed colour, not a theme var. */
.ring-seps line{stroke:rgba(255,255,255,.3); stroke-width:1; pointer-events:none}
/* The bed under the module icons: ONE ring wide enough to carry a slot, dark and half
   transparent, drawn under them. It replaces the disc that used to sit behind each slot —
   the ring says it once, and the empty slots and the breaks between racks stay part of the
   same track instead of each needing their own patch. Width comes from `slotU`, the one
   number the slot itself is sized from. */
.ring-tab{fill:none; stroke:#05070a; opacity:.5}
/* CPU, powergrid and calibration, as gauge arcs on the band. ⚠️ Fixed hex sampled from the
   game's own window, for the same reason the four damage types are: a pilot reads teal for
   CPU and red for the reactor off EVE's screen, and eleven faction themes must not repaint
   them. */
.ring-gauge{pointer-events:auto}
.ring-gauge .gg-track{fill:none; stroke:#22262b; stroke-width:12}
.ring-gauge .gg-fill{fill:none; stroke-width:12}
.ring-gauge .gg-ticks line{stroke:#9aa0a6; stroke-width:1; opacity:.55}
.gg-cpu .gg-fill{stroke:#2f6b68}
.gg-pg .gg-fill{stroke:#6e1a0c}
.gg-cal .gg-fill{stroke:#4a5054}
.ring-gauge.over .gg-fill{stroke:var(--red)}
/* The hull's own render, filling the WHOLE disc. ⚠️ OPAQUE by necessity: CCP serves this as a
   JPEG with its own baked backdrop and no alpha at any size, so there is no ship to cut out.
   Covering the circle is the arrangement in which that stops being a defect — no seam, no gap
   for the floor to show through, and the render's own lit lower edge reads as the floor.
   An earlier version put a small disc in the middle and faded its edge; that punched holes
   through the backdrop and the grid showed through them. */
.ring-ship{position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:89%; height:89%; object-fit:cover; border-radius:50%}
/* ⚠️ The size goes on the POSITIONED box, not on the slot inside it: a percentage width on
   the slot would resolve against an absolutely-positioned parent of no width at all.
   ⚠️ And the slots stand UPRIGHT — the game does not turn a module icon to face the middle.
   That is why `ringRoom` measures the gap along the axes rather than along the chord. */
.ring-slot{position:absolute; transform:translate(-50%,-50%);
  line-height:0; width:var(--ring-slot); height:var(--ring-slot)}
.ring-slot .slot{width:100%; height:100%}
.ring-slot .slot .sl-icon{width:84%; height:84%}
/* Rigs are passive and the game frames them differently; ours says so with the corners. */
.ring-slot.rs-rig .slot{border-radius:9px}

/* Hardpoints: a run of lozenges out on the band, flanking the high slots that spend them. */
/* ⚠️ The row covers the whole ring and is drawn AFTER the slots, so it must let clicks
   and hovers through; only the pips and the glyph take them back. */
/* The logi's incoming cap link, a switch in the ring's top-right corner. Shown whenever the
   fit HAS a transmitter, on or off - `cap.chain_source` is reported either way, which is what
   keeps the button from vanishing the moment it is used. */
.ring-chain{position:absolute; right:2%; top:4%; padding:2px; line-height:0;
  background-color:var(--bg-deep); border-color:var(--border)}
.ring-chain img{width:26px; height:26px; filter:grayscale(1) brightness(.7)}
.ring-chain.on{border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent),
  var(--btn-edge-down-sm)}
.ring-chain.on img{filter:none}
.hp-row{position:absolute; inset:0; pointer-events:none}
.hp-pip,.hp-mark{pointer-events:auto}
.hp-pip,.hp-mark{position:absolute; transform:translate(-50%,-50%) rotate(var(--tilt, 0deg))}
.hp-pip{width:5px; height:9px; border-radius:3px; background-color:#3a3f45;
  box-shadow:inset 0 0 0 1px #555b62}
.hp-pip.on{background-color:var(--accent); box-shadow:none}
.hp-mark{line-height:0}
.hp-mark .g-ico{width:12px; height:12px; opacity:.75}
.hp-row.over .hp-pip.on{background-color:var(--red)}

/* The two fitting figures under the ring. No bar: the gauge arcs above are the bar, and the
   labels wear their colours so the two halves read as one statement. */
/* The two figures sit at the CIRCLE's edges, under the gauge each belongs to: CPU hard left
   under the teal arc, powergrid hard right under the red one. The ring fills this box, so the
   box's edges are the circle's. Label on one line, the figure under it - the pair reads as one
   column instead of a run of four words. */
/* ⚠️ INSIDE the ring box, in the two corners the circle leaves empty. The bottom is a
   PERCENTAGE of the square and 9 % is the geometric limit, not taste: the powergrid block is
   the wider of the two (~15 % of the box) and its inner top corner must stay outside the drawn
   radius of 47.5 %, which puts the ceiling at 11.4 %. Widen that block - a longer figure, a
   larger font - and this has to come back down. */
/* At the square's BOTTOM EDGE, which is where the circle's chord is narrowest and so the two
   corners are at their widest - 149px of room against a 72px powergrid block on a 480 ring, and
   still clear at the 335 a 400px window leaves. Anywhere higher and the corner closes in: the
   9 % perch this replaced had 7px of clearance and could not have taken a longer figure. */
.fit-readout{position:absolute; left:0; right:0; bottom:0; display:flex;
  justify-content:space-between; align-items:flex-end; gap:16px; font-size:11px;
  /* ⚠️ The strip runs the FULL width, so anywhere it crosses the ring it would swallow the
     hover panel of whatever is under it - measured at the 9 % perch this replaced: four bottom
     slots lost theirs. At the bottom edge it now clears them (measured: none blocked with the
     guard off), so this is insurance against the strip ever being raised again rather than a
     live fix. Only the two blocks take events back; nothing else here has anything to say. */
  pointer-events:none}
.fr-item{display:flex; flex-direction:column; gap:1px; pointer-events:auto}
.fr-cpu{align-items:flex-start; text-align:left}
.fr-pg{align-items:flex-end; text-align:right}
.fr-k{text-transform:uppercase; letter-spacing:.5px}
.fr-cpu .fr-k{color:#39c8b5}
.fr-pg .fr-k{color:#d4483b}
.fr-item b{color:var(--text); font-weight:bold; white-space:nowrap;
  font-family:var(--font-display); letter-spacing:var(--display-tracking)}

/* ── The statistics panel, in the game's own sections ─────────────────────────────── */
.statcols{flex:1 1 0; min-width:300px; display:flex; flex-direction:column; gap:6px}
.stat-sec{background-color:var(--bg-deep); border:1px solid var(--border); border-radius:4px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.04), 0 1px 3px rgba(0,0,0,.4)}
.sec-head{display:flex; align-items:baseline; gap:6px; margin:0; padding:6px 10px;
  font-size:14px; font-weight:normal; text-transform:uppercase; letter-spacing:.5px;
  color:var(--dim); cursor:pointer; user-select:none}
.sec-head:hover{color:var(--text)}
.sec-caret{font-size:11px; transition:transform .12s}
.stat-sec.closed .sec-caret{transform:rotate(-90deg)}
.sec-name{flex:1 1 auto}
.sec-num{color:var(--accent); font-weight:bold; text-transform:none; letter-spacing:0;
  font-size:16px; font-family:var(--font-display); white-space:nowrap}
.sec-body{padding:0 9px 8px}
.stat-sec.closed .sec-body{display:none}
/* The Defense head's incoming damage button (`dmgProfileBtn`): ⚙, then the round's name and a
   four-colour strip of its damage split once one is picked. `background-color`, never the
   shorthand, or the bevel goes. */
button.sec-gear{align-self:center; display:inline-flex; align-items:center; gap:5px; padding:1px 6px;
  min-width:0; font-size:12px; line-height:16px; text-transform:none; letter-spacing:0;
  color:var(--dim); background-color:var(--bg-deep); border-color:var(--border)}
button.sec-gear:hover{color:var(--accent)}
button.sec-gear.on{color:var(--text); border-color:var(--accent2)}
.sec-gear .dp-name{max-width:140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:11px}
.dp-strip{display:inline-flex; flex:0 0 auto; width:44px; height:6px; border-radius:2px;
  overflow:hidden; background-color:var(--border)}
.dp-strip i{display:block; height:100%}
.dp-strip i.dt-em{background-color:var(--dt-em)}
.dp-strip i.dt-th{background-color:var(--dt-th)}
.dp-strip i.dt-kin{background-color:var(--dt-kin)}
.dp-strip i.dt-exp{background-color:var(--dt-exp)}
.sec-inrep{margin-top:6px}
/* A section's figures go on ONE line, as the game writes them, and drop to the next only
   when the panel is too narrow to hold them. A fixed two-column grid put a break in the
   middle of every section whether or not there was any reason for one — and with an odd
   number of cells it left a hole. ⚠️ `flex-wrap:wrap` is the whole of the narrow-window
   behaviour: the cells never shrink (`flex:0 0 auto`), so a figure is never clipped or
   ellipsised, it moves down. The three resist ROWS are NOT part of this — they are their
   own table. */
.sec-grid{display:flex; flex-wrap:wrap; align-items:center; gap:4px 22px}
.sec-grid:empty{display:none}
/* Icons at twice the old size, text a quarter larger — this panel is read at a glance mid-op,
   and the glyph is what the eye lands on first. The three resist ROWS keep their old size on
   purpose: twelve figures and their bars have to stay on one line each. */
.sec-item{display:flex; align-items:center; gap:7px; font-size:15px; min-width:0; flex:0 0 auto}
/* A figure that is currently switched off is still worth showing - it is the other half of
   the comparison the toggle exists for - but it must not read as live. */
.sec-item.off-v b{color:var(--dim)}
.sec-item.off-v .g-png{opacity:.4}
.sec-item b{color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
/* Our own glyphs. `currentColor` is what makes one set serve all eleven faction themes. */
.g-ico{width:26px; height:26px; flex:0 0 26px; fill:currentColor; color:var(--dim)}
/* The game's own icons are raster and already coloured, so `currentColor` does not
   reach them and the hover tint below must not try. */
.g-png{opacity:.85}
.sec-item:hover .g-png{opacity:1}
.sec-item:hover .g-ico{color:var(--accent)}
/* A figure that OPENS something. It has to sit in the same row as the plain ones and read the
   same, so every button default is taken back off it.
   ⚠️ `background-color`, never the `background` shorthand: the shorthand flattens the bevel
   every other button in this application carries. `button.sec-item` rather than `.sec-click`
   so it outranks the bare class above it. */
button.sec-item{padding:0; border:0; font:inherit; color:inherit; cursor:pointer;
  background-color:transparent}
button.sec-item.sec-click b{border-bottom:1px dashed var(--dim)}
button.sec-item.sec-click:hover b{color:var(--accent); border-bottom-color:var(--accent)}
.rack{display:flex; align-items:center; gap:8px; margin-bottom:6px}
.rack-label{flex:0 0 var(--rack-lw, 88px); min-width:0; white-space:nowrap; font-size:11px; text-transform:uppercase; letter-spacing:.5px;
  color:var(--dim); text-align:right}
.rack-slots{display:flex; flex-wrap:wrap; gap:4px}
/* The editor's Implants / Boosters rows draw only what is plugged in, so an empty row still needs
   a body a drop can land on (and its `.drop` outline a size); the label opens that tab. */
.imp-rack .rack-slots{flex:1 1 auto; min-height:38px}
/* ⚠️ Every label that DOES something on a click wears the same affordance, and only those:
   the Drones and Fighters labels switch the browser to the Drones tab, the Implants and
   Boosters labels open their own tab. The High / Mid / Low rack labels are pure captions and
   stay dead - a pointer over them would promise a click that does nothing. */
.imp-rack .rack-label[data-headkind], .rack-label.rack-pick{cursor:pointer}
.imp-rack .rack-label[data-headkind]:hover, .rack-label.rack-pick:hover{color:var(--accent)}
/* An implant / booster the pilot switched off: grey icon, like an unpowered module. */
.imp-rack .slot[data-imp]{cursor:pointer}
.slot.imp-off{opacity:.75}
.slot.imp-off .sl-icon{filter:grayscale(1) brightness(.55)}
/* Worn by the pilot profile, not the fit: a small dot in the corner. */
.slot.imp-prof::after{content:""; position:absolute; top:2px; right:2px; width:5px; height:5px;
  border-radius:50%; background-color:var(--accent2); pointer-events:none}
/* The Bonuses row: one chip per bonus source - the hull, then its burst families. */
.bonus-chip{position:relative; display:inline-flex; align-items:center; gap:2px; height:38px; padding:0 4px 0 2px;
  background-color:var(--bg-deep); border:1px solid var(--border-lt); border-radius:3px; cursor:pointer}
.bonus-chip:hover{border-color:var(--accent2)}
.bonus-chip .bc-hull{width:32px; height:32px}
.bc-fams{display:inline-flex; flex-direction:column; flex-wrap:wrap; height:34px; gap:1px; align-content:flex-start}
.bc-fam{width:16px; height:16px}
.bc-fam-txt{display:inline-block; font-size:10px; line-height:16px; text-align:center; color:var(--dim)}
.bonus-chip.off{opacity:.7; border-color:var(--border)}
.bonus-chip.off img{filter:grayscale(1) brightness(.55)}
button.bonus-add{width:38px; height:38px; margin:0; padding:0; font-size:18px; line-height:1;
  color:var(--dim); background-color:var(--bg-deep); border:1px dashed var(--border-lt); border-radius:3px}
button.bonus-add:hover{color:var(--accent); border-color:var(--accent2)}
.slot{position:relative; display:inline-flex; align-items:center; justify-content:center;
  width:38px; height:38px; background:var(--bg-deep); border:1px solid var(--border);
  border-radius:3px}
/* ⚠️ The icon must NOT be its own drag source. A fitted cell is `draggable="true"` (so a
   module can be dragged out of the ring into the Buffer), and an <img> is draggable natively:
   without this the drag would start on the PICTURE, handing the drop the image URL instead of
   the slot. Chromium/WebView2 only — which is the only engine this app runs in. (2026-09-20) */
.slot .sl-icon{width:32px; height:32px; -webkit-user-drag:none; user-select:none}
.slot.empty{border-style:dashed; opacity:.5}
/* OFFLINE reads exactly as it does in the game: there, but dark and doing nothing. */
.slot.off{border-color:var(--red)}
.slot.off .sl-icon{filter:grayscale(1) brightness(.55)}
/* Drone packs are a radio: one flies, the rest sit in the bay. */
/* ⚠️ The module's STATE, in its own class — never by redefining `.off` or `.on`, which
   the editor's handlers read back as state. The game rings a module that can be ACTIVATED in green
   and leaves a passive one plain; measured on its own window, the green frames sit on the
   highs and mids and nowhere else, because that fit's lows and rigs are passive. Fixed
   colours, like the damage types: a pilot compares them against the game.
   `st-overheated` fires for a module the pilot overheats (Shift+click, or the menu): the editor
   sends its index as `overheat` and the engine reports that module's state as overheated.
   `st-idle` is the same module with its cycle stopped (`inactive`), `st-offline` one with no
   power at all — three ways to be idle, and the pilot undoes each with a different key. */
/* ⚠️ The four frames are a TRAFFIC LIGHT (2026-09-16, the user's words): green runs, red is
   hot, plain is powered-but-stopped, grey is unpowered. They are NOT theme accents any more —
   the accent moved onto hover, where "the mouse is here" belongs, and the state colours stay
   put so the same fit reads the same way under all eleven faction themes. --green / --red are
   fixed in :root and applyTheme() never repaints them. */
.slot.st-active{border-color:var(--green);
  box-shadow:0 0 0 1px var(--green), 0 0 6px rgba(76,175,80,.40)}
.slot.st-overheated{border-color:var(--red);
  box-shadow:0 0 0 1px var(--red), 0 0 7px rgba(239,83,80,.45)}
/* Powered, cycle stopped. A plain border and a full-colour icon on purpose: it must not
   borrow the offline look, or "I switched this off" and "this has no power" would be the same
   picture. Only a module that COULD run ever wears it (`slotCell` checks `can_activate`). */
.slot.st-idle{border-color:var(--border-lt); box-shadow:none}
.slot.st-idle .sl-icon{filter:none}
/* Unpowered: the grey icon comes from `.slot.off` above, the frame is dim rather than red -
   red now means overheated. Declared after `.slot.off` (same specificity) so it wins, and it
   does not need `[data-mod]`: a slot drawn before the first answer has no engine line. */
.slot.st-offline{border-color:var(--border-lt); box-shadow:none; opacity:.75}
/* A fitted module can be switched, so it takes the pointer and the accent on hover. */
.slot[data-mod]{cursor:pointer}
.slot[data-mod]:hover{border-color:var(--accent2)}
/* ⚠️ Hover must NOT repaint a state frame. `.slot[data-mod]:hover` is class+attribute+
   pseudo-class, so it outranks `.slot.st-*` (class+class) and used to turn an overheated
   slot cyan while the pointer rested on it - only the red glow survived, and a pilot
   pointing at a module to read its colour got the wrong one (measured 2026-09-16:
   computed border-color rgb(0,188,212) on a Shift+clicked gun). A slot that already
   carries a state keeps it and answers the pointer with a brighter ring instead. */
.slot[data-mod].st-active:hover{border-color:var(--green);
  box-shadow:0 0 0 1px var(--green), 0 0 9px rgba(76,175,80,.60)}
.slot[data-mod].st-overheated:hover{border-color:var(--red);
  box-shadow:0 0 0 1px var(--red), 0 0 10px rgba(239,83,80,.65)}
.slot.off[data-mod]{border-color:var(--border-lt); box-shadow:none; opacity:.75}
/* MUTATED (abyssal), roll KNOWN: NO mark at all - it looks like any other module (the pilot's
   word, 2026-09-21 and again 2026-09-27). The colour on an abyssal cell means one thing only:
   the roll is NOT known yet (grey, below). The panel still says MUTATED · #id.
   ⚠️ History, so nobody "restores" it: the cell had a violet bed until 2026-09-21, then a violet
   corner wedge (`.slot.st-mut::before`). The wedge rule was swallowed by a comment closed one
   line early and never drew; fixing the comment on 2026-09-27 brought the violet back, and the
   pilot asked for it to go. `st-mut` stays on the cell as a HANDLE (the Buffer and tests read
   it) and must not be given a look.
   ⚠️ Whatever marks an abyssal must stay out of `border-color` and `box-shadow`: those two are
   the traffic light's, and a mutated module can be running, stopped, unpowered or hot. */
/* Abyssal, roll NOT confirmed (2026-09-21). An imported fit carries no roll — EFT has no room
   for one — so the module's real numbers are unknown and what the stats show is the BASE type.
   The cell says so the way a switched-off implant does (`.slot.imp-off`): grey icon, dimmed.
   Still not a fifth state: border and shadow stay with the traffic light. */
.slot.st-mut-unknown{opacity:.75}
.slot.st-mut-unknown .sl-icon{filter:grayscale(1) brightness(.55)}
/* The corner wedge is drawn hollow: the mark is there, waiting to be filled in. */
.slot.st-mut-unknown::before{content:""; position:absolute; left:3px; top:3px; width:0; height:0;
  border-top:9px solid var(--border-lt); border-right:9px solid transparent;
  pointer-events:none}
/* Tried on, not fitted (2026-09-27): the pointer rests on a module row in the item list and the
   editor puts it in the first free slot for a look. Grey like an unconfirmed roll, no wedge. */
.slot.st-preview{opacity:.6; border-style:dashed}
.slot.st-preview .sl-icon{filter:grayscale(1) brightness(.6)}
.slot.drone-pick{cursor:pointer}
.slot.drone-pick:not(.off){border-color:var(--green)}
.slot.drone-pick:hover{border-color:var(--accent)}
.slot-qty{position:absolute; right:1px; bottom:0; font-size:9px; font-style:normal;
  color:var(--text); text-shadow:0 0 3px #000}
.slot-blank{display:inline-block; width:32px; height:32px}

/* The game's defence panel: raw HP per layer, then the twelve resistances. Each resistance
   is a BAR filled to its own percentage, which is how EVE draws it — the width says at a
   glance what a column of bare numbers makes you read one by one. */
.resists{width:100%; border-collapse:collapse; margin:4px 0 2px; font-size:12px}
.resists th{font-weight:normal; font-size:10px; padding:0 2px 3px; text-align:center}
.resists th .g-ico{width:22px; height:22px; flex:0 0 22px}
/* The local-repair figure shares this header row with the four damage-type icons, which is
   where the game puts it. */
.resists th.rs-rep{text-align:left; padding-bottom:4px}
.resists th.rs-rep .sec-item{font-size:15px}
.resists td{padding:1px 2px; text-align:right; white-space:nowrap}
.resists .rs-l{text-align:left; width:1%; padding-right:4px; line-height:0}
.resists .rs-l .g-ico{width:18px; height:18px; flex:0 0 18px}
.resists .rs-hp{color:var(--text); font-weight:bold; padding-right:8px; width:1%;
  text-align:left; line-height:1.15}
/* The shield's recharge time, under its HP — the game writes it there and nowhere else, which
   is why this is the only two-line cell in the table. */
.rs-sub{display:block; font-weight:normal; font-size:11px; color:var(--dim)}
.resists th.rs-hp{color:var(--dim); font-weight:normal; text-align:right}
.rbar{position:relative; display:block; height:15px; border-radius:2px;
  background-color:#1b1e22; overflow:hidden}
.rbar i{position:absolute; left:0; top:0; bottom:0; display:block}
.rbar b{position:relative; display:block; padding:0 4px; line-height:15px; font-size:11px;
  font-weight:normal; text-align:left; color:#f0f0f2; text-shadow:0 0 3px rgba(0,0,0,.85)}
/* Fixed hex, sampled from the game's own defence panel: a pilot matches these against EVE's
   damage-type glyphs, and the eleven faction themes must not repaint them. Same reasoning as
   the rank tints in the two log tables. */
.resists .dt-em .rbar i{background-color:var(--dt-em)}
.resists .dt-th .rbar i{background-color:var(--dt-th)}
.resists .dt-kin .rbar i{background-color:var(--dt-kin)}
.resists .dt-exp .rbar i{background-color:var(--dt-exp)}
.stat-problems{margin-top:12px; border-top:1px solid var(--border); padding-top:8px}
.stat-problems h4{margin:0 0 4px; font-size:11px; text-transform:uppercase; color:var(--amber);
  letter-spacing:.5px; font-weight:normal}
.stat-problems ul{margin:0; padding-left:18px; font-size:12px; color:var(--text)}
.warn{color:var(--amber)}

.status{color:var(--dim); font-size:12px; min-height:18px}

.popup{position:absolute; right:0; bottom:calc(100% + 6px); z-index:20; min-width:180px;
  background:var(--bg-panel); border:1px solid var(--border); border-radius:4px;
  padding:4px; box-shadow:0 6px 20px rgba(0,0,0,.5)}
.popup-item{display:block; width:100%; text-align:left; background:transparent;
  border:none; padding:6px 10px; border-radius:3px; color:var(--text); font-size:13px}
.popup-item:hover{background:var(--primary-bg); color:var(--accent)}
.popup-empty{padding:6px 10px; font-size:12px}

/* right-click "Select main character" context menu (positioned at the cursor) */
.ctx-menu{position:fixed; right:auto; bottom:auto; z-index:50; min-width:170px}
.popup-item:disabled{opacity:.4; cursor:default}
.popup-item:disabled:hover{background:transparent; color:var(--text)}

/* The rounds actually aboard: a shortcut, never the whole list — the picker beside them
   offers everything the gun accepts. Green ring = this is what is loaded right now. */
.cargo-ammo{display:inline-flex; align-items:center; justify-content:center; width:38px;
  height:38px; margin:0; padding:0; line-height:0; box-sizing:border-box; border-color:var(--border);
  background-color:var(--bg-deep)}
.cargo-ammo:hover{border-color:var(--accent)}
.cargo-ammo.on{border-color:var(--green); box-shadow:inset 0 0 0 1px var(--green),
  var(--btn-edge-down-sm)}

/* What the fit expects the pilot to have bought. The engine does NOT plug a cargo implant
   in, so the figures above are the un-implanted ones — the amber ring is the only thing
   that says so. Fixed hex on the warning, like the log tints: it must read the same in all
   eleven faction themes, and `--accent` is repainted per theme from JS. */
.cargo-imp .rack-label{color:#d9a441}
.slot.cargo-implant{position:relative}
.slot.cargo-implant.missing{border-color:#d9a441}
.slot.cargo-implant.missing .sl-icon{opacity:.55}
.slot-warn{position:absolute; right:-2px; bottom:-3px; font-size:11px; line-height:1;
  color:#d9a441; text-shadow:0 0 2px #000}
