/* ═══════════════════════════════════════════════════════════════════
   src/char-panel-v3.css — the Inventory panel, SURGE Console v3 "Obsidian"
   INV-PANEL-REDESIGN-01 (#6576)

   ── Why this file exists ────────────────────────────────────────────
   These rules lived in styles.css and styles-game.css, two global sheets
   totalling ~4,000 lines. The panel already owned a JS module; its CSS
   living in the commons is *how* three competing cyans and a stray
   'Segoe UI' drifted in — nobody editing a 3,000-line global sheet can
   see which panel a rule belongs to. Co-locating the CSS with the module
   makes the panel's surface finite and reviewable.

   Loaded AFTER styles.css and styles-game.css so it wins the cascade for
   everything it claims. Order locked in
   alaknanda-server/smoke-browser-load.test.js.

   ── The look ────────────────────────────────────────────────────────
   Obsidian, not navy. v2's ramp is blue-slate; v3 drops the blue so the
   ITEM TILES carry the only saturation on the panel — Albion's actual
   rule. Chrome signals with tarnished brass, because cyan belongs to the
   world (visual-bible §9, amended 2026-08-11) and UI must not compete
   with the game for the same colour.

   Every colour here is a --v3-* or --k-* token, or a live condition
   band. There is no cyan in this file, and no hardcoded font stack.

   ── Two rules that look like style but are contracts ────────────────
   1. NO DASHED BORDERS ANYWHERE (AC7). Dashes read as "broken/invalid"
      in every ARPG, so a dashed empty slot tells the player something is
      wrong when the truth is just "nothing here yet". Empty states are
      SOLID RECESSED WELLS — deliberately carved, not missing.
   2. The brass frame appears ONLY on a filled slot. An interrupted
      outline around nothing reads as damage.
   ═══════════════════════════════════════════════════════════════════ */

/* ── shell ─────────────────────────────────────────────────────────
   A right-docked drawer, not a centred modal. That is what makes
   walk-while-open work: the world stays visible and live beside it, and
   the panel never takes the screen. modal:false is locked in JS. */
#char-panel-v3{
  position:fixed; right:16px; top:56px; bottom:16px; z-index:150;
  width:min(900px,96vw);
  flex-direction:column; overflow:hidden;
  /* Opaque (locked invariant 5). A translucent panel over a live 3D world
     makes every tile's colour depend on what happens to be behind it,
     which destroys the generation ramp's whole job. */
  background:linear-gradient(180deg,var(--v3-surf-b),var(--v3-bg0));
  border:1px solid #26262b;
  border-radius:12px;
  box-shadow:0 28px 80px -26px rgba(0,0,0,.85);
  font-family:var(--k-sans); color:var(--v3-ink2);
}
@media (max-width:940px){ #char-panel-v3{ left:16px; right:16px; width:auto; } }

#char-v3-header{
  display:flex; align-items:center; gap:10px; flex:0 0 auto;
  padding:13px 16px;
  background:linear-gradient(180deg,#141416,#0e0e10);
  border-bottom:1px solid #26262b;
}
#char-v3-title{
  flex:1 1 auto;
  font:800 15px/1.2 var(--k-disp); letter-spacing:.14em;
  text-transform:uppercase; color:var(--v3-ink);
}
/* The one brass tick — a maker's mark, not decoration. It is the only
   thing in the header that is not type. */
#char-v3-header::before{
  content:''; flex:0 0 auto; width:3px; height:18px; border-radius:2px;
  background:linear-gradient(180deg,var(--v3-brass-hi),var(--v3-brass-lo));
}
#char-v3-close{
  flex:0 0 auto; width:30px; height:30px; cursor:pointer;
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border:1px solid #2e2e34; border-radius:var(--v3-rad-tile);
  color:var(--v3-ink3); font-size:13px; line-height:1;
  transition:color .09s,border-color .09s;
}
#char-v3-close:hover{ color:var(--v3-ink); border-color:var(--v3-brass-lo); }
#char-v3-close:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:2px; }

/* Paper-doll LEFT, grid RIGHT — locked gameplay decision (invariant 2). */
#char-v3-body{ display:flex; flex-direction:row; flex:1 1 auto; min-height:0; }
#char-v3-body-rail{
  flex:0 0 43%; display:flex; flex-direction:column; gap:14px;
  padding:16px; overflow-y:auto; border-right:1px solid #212126;
}
#char-v3-inventory{
  flex:1 1 57%; display:flex; flex-direction:column; gap:12px;
  padding:16px; overflow-y:auto; min-width:0;
}
#char-v3-hint{
  flex:0 0 auto; padding:9px 16px; border-top:1px solid #212126;
  font:400 10px/1.5 var(--k-sans); color:var(--v3-ink3);
}

/* ── the hero: ITEM POWER ──────────────────────────────────────────
   One number dominates. A player answers "am I stronger?" in under a
   second or the aggregate has no reason to exist. Tabular numerals are
   mandatory: a 4-digit value in the 700–1200 band must not reflow as it
   ticks, or the eye reads movement where there is none. */
.cv3-hero{
  display:flex; align-items:stretch; gap:12px;
  padding:14px 15px;
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border:1px solid #2a2a30; border-radius:var(--v3-rad-card);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
}
.cv3-hero-main{ flex:1 1 auto; min-width:0; }
.cv3-hero-lbl{
  font:700 9px/1 var(--k-disp); letter-spacing:.16em;
  text-transform:uppercase; color:var(--v3-ink3);
}
.cv3-hero-val{
  margin-top:6px;
  font:400 44px/1 var(--k-mono); font-variant-numeric:tabular-nums;
  color:var(--v3-ink); letter-spacing:-.01em;
}
.cv3-hero-well{
  flex:0 0 auto; display:flex; flex-direction:column; justify-content:center; gap:5px;
  padding:8px 11px; min-width:96px;
  background:linear-gradient(180deg,var(--v3-well-a),var(--v3-well-b));
  border:1px solid #232329; border-radius:var(--v3-rad-tile);
}
.cv3-hero-row{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.cv3-hero-row-lbl{
  font:700 8px/1 var(--k-disp); letter-spacing:.12em;
  text-transform:uppercase; color:var(--v3-ink3);
}
.cv3-hero-row-val{
  font:400 13px/1 var(--k-mono); font-variant-numeric:tabular-nums; color:var(--v3-ink2);
}
/* Wong colourblind-safe palette — deliberately NOT the semantic ok/danger
   set. A delta is a comparison, not a status, and the two must not share
   a vocabulary. The ▲/▼/= glyph is always present, so colour is never
   the only channel (WCAG 2.2 §1.4.1). */
.cv3-hero-row--up   .cv3-hero-row-val{ color:#009E73; }
.cv3-hero-row--down .cv3-hero-row-val{ color:#D55E00; }
.cv3-hero-row--flat .cv3-hero-row-val{ color:#94a3b8; }

/* ── the loadout cross ─────────────────────────────────────────────
   Exactly five slots in a 3×3 anatomical arrangement. Owner-locked; the
   cells may be restyled but NOT rearranged, and the geometry
   (head.y < torso.y < legs.y, weapon.x < torso.x) is measured by an
   automated spec.

   The named grid areas are load-bearing, not cosmetic: LOADOUT-HTL-01
   renamed the worn slots in JS while these rules still targeted the
   retired keys, so head/torso/legs fell out of their areas into
   auto-placement and the anatomical column silently scrambled — head
   landed BESIDE torso. If a shield ever ships it takes the free cell
   opposite Weapon; add it to the middle row THEN, not before. */
.cv3-equipment-grid{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  grid-template-areas:
    ".      head  ."
    "weapon torso consumable"
    ".      legs  .";
  gap:8px;
  /* Capped and centred. Uncapped, the three columns stretch to the full rail
     width and the tiles grow past the point where a square icon reads as an
     item — the cross stops looking like a body and starts looking like a
     spreadsheet. */
  width:100%; max-width:352px; margin:0 auto;
}
#char-v3-body-rail [data-slot-key="head"]{ grid-area:head; }
#char-v3-body-rail [data-slot-key="torso"]{ grid-area:torso; }
#char-v3-body-rail [data-slot-key="legs"]{ grid-area:legs; }
#char-v3-body-rail [data-slot-key="weapon"]{ grid-area:weapon; }
#char-v3-body-rail [data-slot-key="consumable"]{ grid-area:consumable; }

/* ── tiles: the slot and the bag cell share one skin ───────────────
   Same corners, same field, same frame — an item must not look like one
   thing in the bag and another once worn. */
.cv3-slot,.cv3-cell{
  position:relative; box-sizing:border-box; min-width:0; aspect-ratio:1/1;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
  padding:8px; overflow:hidden; cursor:pointer; text-align:center;
  border-radius:var(--v3-rad-tile);
  /* The default is the RECESSED WELL. A tile earns its raised frame by
     holding something; empty is carved, never outlined. */
  background:linear-gradient(180deg,var(--v3-well-a),var(--v3-well-b));
  border:1px solid #1e1e23;
  box-shadow:inset 0 2px 5px rgba(0,0,0,.55);
  transition:border-color .09s,background .09s;
}
/* Filled: raised, with the tarnished frame. */
.cv3-slot-filled,.cv3-cell[data-item-source],.cv3-cell-material{
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border-color:var(--v3-brass-lo);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05),0 2px 8px rgba(0,0,0,.42);
}
.cv3-slot:hover,.cv3-cell[data-item-source]:hover{
  border-color:var(--v3-brass); background:linear-gradient(180deg,#1e1e22,#141417);
}
.cv3-slot:focus-visible,.cv3-cell:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:2px; }
.cv3-slot:active,.cv3-cell:active{ transform:scale(.97); transition:none; }
.cv3-slot-filled{ cursor:grab; }
.cv3-cell[data-equip-cell]{ cursor:grab; }
.cv3-cell[data-equip-cell]:active{ cursor:grabbing; }

/* Selected — the cell whose detail card is open. Full brass border plus a
   ring, never background-only. */
.cv3-cell--active{
  border-color:var(--v3-brass-hi)!important;
  box-shadow:0 0 0 1px rgba(194,167,106,.30),0 0 18px -4px rgba(194,167,106,.55);
}

/* Empty slot: the label IS the content. No icon crowding, no dashes. */
.cv3-slot-short{
  font:700 9px/1 var(--k-disp); letter-spacing:.13em;
  text-transform:uppercase; color:var(--v3-ink3);
}
.cv3-slot-empty-icon{ font-size:20px; color:#26262c; line-height:1; }
.cv3-slot-icon{ font-size:34px; line-height:1; }
.cv3-cell-icon{ font-size:26px; line-height:1; }
/* Rendered item/material art inside an icon slot (MatIcon). Falls back to the
   emoji span when the registry has no art for that slug. */
.cv3-icon-art{ display:inline-grid; place-items:center; }
.cv3-cell-icon.cv3-icon-art img{ width:34px; height:34px; object-fit:contain; }
.cv3-slot-icon.cv3-icon-art img{ width:52px; height:52px; object-fit:contain; }
.cv3-cell-icon .mat-icon-img{ width:32px; height:32px; }
.cv3-slot-name,.cv3-cell-name{
  max-width:100%; font:400 10px/1.15 var(--k-sans); color:var(--v3-ink);
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
/* Item names are LONG on purpose — generation is carried by the name
   ("Gen 3 Bulwark Torso"), so this has to be designed for, not against.
   A tile showing a damaged item makes room for the condition caption instead
   of letting it sit on top of the second line of the name.

   Driven by a class the renderer stamps, not by :has(). :has() would degrade
   silently to "caption overlaps the name" on an engine without it, and a
   readability bug that only appears in some browsers is the worst kind. */
.cv3-tile--conditioned{ padding-bottom:20px; }
.cv3-dim{ color:var(--v3-ink3); font-style:italic; }

/* ── the generation field ──────────────────────────────────────────
   The ONE place saturation is allowed on this panel. Hues are read out
   of content/items.js NODE_DEFS particleColours (see
   packages/common/src/item-tiers.ts) so gear reuses the colour language
   the world teaches at the node — the green you mined tier-2 rocks for
   is the green a Gen 2 item wears.

   These are those hues at PANEL LUMINANCE, not raw. A literal #4ade80
   tile would out-shout the hero number. Hue carries the information;
   brightness is not information. */
.cv3-slot[data-gen="1"],.cv3-cell[data-gen="1"]{ background:linear-gradient(180deg,var(--v3-gen1-a),var(--v3-gen1-b)); }
.cv3-slot[data-gen="2"],.cv3-cell[data-gen="2"]{ background:linear-gradient(180deg,var(--v3-gen2-a),var(--v3-gen2-b)); }
.cv3-slot[data-gen="3"],.cv3-cell[data-gen="3"]{ background:linear-gradient(180deg,var(--v3-gen3-a),var(--v3-gen3-b)); }

/* ── the four corners ──────────────────────────────────────────────
   One fact each, and each one absent unless it has something true to
   say. Corners are aria-hidden: every value they carry is already in the
   tile's aria-label as a sentence, so a screen reader gets prose rather
   than a pile of loose numerals. */
.cv3-corner{
  position:absolute; z-index:2; pointer-events:none;
  font:400 10px/1 var(--k-mono); font-variant-numeric:tabular-nums;
}
/* top-left — generation numeral, in a notched corner plate */
.cv3-corner--gen{
  top:0; left:0; padding:3px 6px 4px 4px;
  border-bottom-right-radius:var(--v3-rad-tile);
  background:rgba(6,6,8,.72); color:var(--v3-ink);
  font-weight:700;
}
/* top-right — enchantment pips. ONE hue; the COUNT is the level. */
.cv3-corner--ench{ top:4px; right:4px; display:flex; gap:2px; }
.cv3-pip{ width:4px; height:4px; border-radius:50%; background:var(--v3-ench); display:block; }
/* bottom-left — the granted-ability key cap. 2px bottom border so it
   reads as a physical key rather than a chip. */
.cv3-corner--key{
  bottom:4px; left:4px; padding:1px 4px;
  border:1px solid #33333a; border-bottom-width:2px; border-radius:3px;
  background:rgba(6,6,8,.72); color:var(--v3-ink3); font-size:9px;
}
/* bottom-right — stack count */
.cv3-corner--qty{ bottom:4px; right:5px; color:var(--v3-ink2); font-weight:700; }
/* Zero is SHOWN, not hidden — an empty-but-committed consumable slot is a
   legal state and the player needs to see it, not guess. */
.cv3-corner--qty-empty{ color:#f59e0b; }

/* ── bottom edge: condition ────────────────────────────────────────
   Rendered ONLY for damaged and worse. Healthy items stay clean (locked
   invariant 7) — that is exactly what makes a worn piece pop out of a
   full grid. Colour is redundant: the bar's WIDTH carries the magnitude
   and the condition word is folded into the tile's aria-label. */
.cv3-cond{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  display:flex; flex-direction:column; gap:2px;
  padding:2px 4px 0;
  background:linear-gradient(180deg,rgba(6,6,8,0),rgba(6,6,8,.85) 45%);
}
/* The WORD and the ICON — not optional, not aria-only. Colour and bar length
   alone would leave a colourblind player with one channel. */
.cv3-cond-cap{
  font:700 7px/1 var(--k-disp); letter-spacing:.05em; text-align:center;
  color:currentColor; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.cv3-cond-track{ height:3px; background:rgba(0,0,0,.55); }
.cv3-cond-fill{ height:100%; background:currentColor; }
.cv3-cond--damaged { color:#fde047; }
.cv3-cond--critical{ color:#fb923c; }
.cv3-cond--low     { color:#f87171; }
.cv3-cond--broken  { color:#ef4444; }
/* Broken: warm border draws the eye to the worst state, paired with the
   bar — never colour alone. */
.cv3-tile--broken{ border-color:rgba(239,68,68,.55)!important; }
/* ITEM-REQUIREMENTS-V1 (#4267): equipped but requirements unmet. Amber,
   dimmed, and the aria-label says so. */
.cv3-slot--inactive{ border-color:rgba(251,191,36,.5)!important; opacity:.65; }
.cv3-slot--inactive:hover{ border-color:#fbbf24!important; opacity:1; }

/* ── the Details drawer ────────────────────────────────────────────
   Collapsed by default. The player opened the Inventory to see items;
   the stat sheet is what they go looking for, not what they get handed. */
.cv3-drawer{
  border:1px solid #232329; border-radius:var(--v3-rad-card); overflow:hidden;
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
}
.cv3-drawer-tab{
  width:100%; display:flex; align-items:center; gap:7px;
  padding:9px 12px; cursor:pointer; text-align:left;
  background:none; border:none; color:var(--v3-ink2);
  font:700 10px/1 var(--k-disp); letter-spacing:.13em; text-transform:uppercase;
}
.cv3-drawer-tab:hover{ color:var(--v3-ink); }
.cv3-drawer-tab:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:-2px; }
.cv3-drawer-caret{ display:inline-block; font-size:8px; color:var(--v3-brass); transition:transform .14s; }
.cv3-drawer--open .cv3-drawer-caret{ transform:rotate(90deg); }
.cv3-drawer-body{ display:none; padding:2px 12px 11px; }
.cv3-drawer--open .cv3-drawer-body{ display:block; }
@media (prefers-reduced-motion:reduce){ .cv3-drawer-caret{ transition:none; } }

.cv3-stat-group{
  margin:10px 0 5px;
  font:700 8px/1 var(--k-disp); letter-spacing:.15em;
  text-transform:uppercase; color:var(--v3-brass);
}
.cv3-stat-group:first-child{ margin-top:2px; }
.cv3-stat{
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  padding:3px 0;
}
.cv3-stat-lbl{ font:400 11px/1.3 var(--k-sans); color:var(--v3-ink2); }
.cv3-stat-right{ display:flex; align-items:baseline; gap:7px; flex:0 0 auto; }
.cv3-stat-val{
  font:400 12px/1 var(--k-mono); font-variant-numeric:tabular-nums; color:var(--v3-ink);
}
/* Gear delta. Same Wong palette and mandatory glyph as the hero well —
   with attributes removed, (total − base) IS the player's whole build. */
.cv3-stat-delta{ font:400 10px/1 var(--k-mono); font-variant-numeric:tabular-nums; }
.cv3-stat-delta--up  { color:#009E73; }
.cv3-stat-delta--down{ color:#D55E00; }

/* ── grid controls (INV-SORT-FILTER-SEARCH-01 #6559) ───────────────
   Search, category chips and sort. They sit ABOVE the grid and OUTSIDE it, so
   `.cv3-grid` stays exactly one container of cells — the unified-grid invariant
   is asserted on that element, and a control inside it would break the read.

   These replace the ordering the redesign removed with the old sections. The
   difference is who chooses it: the sections sorted the bag for the player,
   these let the player sort it. */
.cv3-controls{ flex:0 0 auto; display:flex; flex-direction:column; gap:8px; }
.cv3-search{ display:flex; }
.cv3-search-input{
  width:100%; padding:7px 10px; box-sizing:border-box;
  background:linear-gradient(180deg,var(--v3-well-a),var(--v3-well-b));
  border:1px solid #232329; border-radius:var(--v3-rad-tile);
  color:var(--v3-ink); font:400 12px/1.2 var(--k-sans);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.5);
}
.cv3-search-input::placeholder{ color:var(--v3-ink3); }
.cv3-search-input:focus{ outline:none; border-color:var(--v3-brass); }
.cv3-search-input:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:1px; }

.cv3-chips{ display:flex; flex-wrap:wrap; gap:5px; }
.cv3-chip{
  display:inline-flex; align-items:center; gap:5px;
  padding:4px 8px; cursor:pointer; border-radius:var(--v3-rad-tile);
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border:1px solid #2a2a30; color:var(--v3-ink2);
  font:700 9px/1 var(--k-disp); letter-spacing:.1em; text-transform:uppercase;
  transition:border-color .09s,color .09s;
}
.cv3-chip:hover{ border-color:var(--v3-brass-lo); color:var(--v3-ink); }
.cv3-chip:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:2px; }
/* Active: brass border AND filled, never colour alone — the chip also carries
   aria-pressed, so the state is available three ways. */
.cv3-chip--on{
  border-color:var(--v3-brass); color:var(--v3-ink);
  background:linear-gradient(180deg,#2a2418,#1a170f);
}
/* A category with nothing in it stays clickable (so the count is readable) but
   reads as inert — clicking it would produce the no-match state, and the chip
   says so in advance. */
.cv3-chip--empty{ opacity:.45; }
.cv3-chip-n{
  font:400 9px/1 var(--k-mono); font-variant-numeric:tabular-nums;
  color:var(--v3-ink3); letter-spacing:0;
}
.cv3-chip--on .cv3-chip-n{ color:var(--v3-brass-hi); }

.cv3-sort{ display:flex; }
.cv3-sort-select{
  width:100%; padding:5px 8px; cursor:pointer;
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border:1px solid #2a2a30; border-radius:var(--v3-rad-tile);
  color:var(--v3-ink2); font:700 9px/1.4 var(--k-disp);
  letter-spacing:.1em; text-transform:uppercase;
}
.cv3-sort-select:hover{ border-color:var(--v3-brass-lo); color:var(--v3-ink); }
.cv3-sort-select:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:2px; }
/* The dropdown itself renders in the OS layer, which does not inherit the
   panel's palette — set it explicitly or the options come back white. */
.cv3-sort-select option{ background:#141416; color:var(--v3-ink); }

/* ── empty states ──────────────────────────────────────────────────
   Two different facts, deliberately not one component. "Your bag is empty" is
   about the player; "No items match" is about their own filter and ships the
   way out. Spans the grid so it reads as the grid's state, not a stray note. */
.cv3-empty{
  grid-column:1 / -1; padding:28px 16px; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:6px;
}
.cv3-empty-title{
  font:700 11px/1 var(--k-disp); letter-spacing:.13em;
  text-transform:uppercase; color:var(--v3-ink2);
}
.cv3-empty-sub{ font:400 11px/1.4 var(--k-sans); color:var(--v3-ink3); max-width:32ch; }
.cv3-empty-btn{
  margin-top:4px; padding:5px 12px; cursor:pointer;
  background:linear-gradient(180deg,var(--v3-surf-a),var(--v3-surf-b));
  border:1px solid var(--v3-brass-lo); border-radius:var(--v3-rad-tile);
  color:var(--v3-brass-hi);
  font:700 9px/1 var(--k-disp); letter-spacing:.11em; text-transform:uppercase;
}
.cv3-empty-btn:hover{ border-color:var(--v3-brass-hi); color:var(--v3-ink); }
.cv3-empty-btn:focus-visible{ outline:2px solid var(--v3-brass); outline-offset:2px; }

/* ── the bag ───────────────────────────────────────────────────────
   ONE grid. No risk/protected sections, no category headers. */
.cv3-grid{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(84px,1fr)); gap:8px;
  align-content:start;
}
.cv3-cell-material .cv3-cell-name{ -webkit-line-clamp:2; }
/* Currency reads warm and never drops on death. It keeps a distinct cell
   rather than a section header — the difference is visible on the tile,
   which is where the player is already looking. */
.cv3-cell-currency{ border-color:rgba(251,191,36,.32); }
.cv3-cell-currency .cv3-corner--qty{ color:#fbbf24; }
/* Filler: a recessed well, no border-style tricks. Empty must look
   deliberate — this is the panel saying "there is room here", not
   "something failed to load". */
.cv3-cell-filler{
  cursor:default; opacity:.5;
  background:linear-gradient(180deg,#0e0e10,#0a0a0c);
  border-color:#171719; box-shadow:inset 0 2px 4px rgba(0,0,0,.5);
}
.cv3-cell-filler:hover{ border-color:#171719; background:linear-gradient(180deg,#0e0e10,#0a0a0c); }

/* ℹ info button — hidden until hover so the resting grid stays quiet. */
.cv3-info-btn{
  display:none; position:absolute; top:3px; right:3px; z-index:4;
  width:17px; height:17px; padding:0; border-radius:50%; cursor:pointer;
  background:rgba(10,10,12,.9); border:1px solid var(--v3-brass-lo);
  color:var(--v3-brass-hi); font-size:10px; line-height:15px; text-align:center;
}
.cv3-cell[data-equip-cell]:hover .cv3-info-btn{ display:block; }
.cv3-info-btn:hover{ border-color:var(--v3-brass-hi); color:var(--v3-ink); }

/* Per-cell controls. These DO something, so they survived the caption
   cull that removed PROTECTED / AT RISK / WORKS FROM BAG. */
.cv3-cell-actions{ display:flex; gap:4px; margin-top:2px; }
.cv3-use-btn,.cv3-slot-btn{
  font:700 8px/1 var(--k-disp); letter-spacing:.09em; padding:2px 5px;
  border-radius:3px; cursor:pointer; background:transparent;
}
.cv3-use-btn{ color:#22c55e; border:1px solid rgba(34,197,94,.45); }
.cv3-use-btn:hover{ background:rgba(34,197,94,.12); border-color:#22c55e; }
.cv3-use-btn:disabled{ opacity:.4; cursor:default; }
.cv3-slot-btn{ color:#f59e0b; border:1px solid rgba(245,158,11,.45); }
.cv3-slot-btn:hover{ background:rgba(245,158,11,.12); border-color:#f59e0b; }
/* Committed: filled, and the LABEL changes to "SLOTTED" — state is never
   colour-alone (WCAG 2.2 §1.4.1). */
.cv3-slot-btn--active{ background:rgba(245,158,11,.18); border-color:#f59e0b; color:#fbbf24; }

/* ── weight ────────────────────────────────────────────────────────
   A hard combat gate, not a cosmetic meter: ≥50% pauses HP/Energy
   recovery and ≥90% blocks attacks and skills, both server-enforced. It
   gets the visual weight of a resource bar for that reason. */
.cv3-weight{ flex:0 0 auto; }
.cv3-weight-lbl{
  display:flex; justify-content:space-between; margin-bottom:4px;
  font:700 9px/1 var(--k-disp); letter-spacing:.12em; text-transform:uppercase;
  color:var(--v3-ink3);
}
.cv3-weight-lbl span:last-child{ font-family:var(--k-mono); font-variant-numeric:tabular-nums; letter-spacing:0; }
.cv3-weight-track{
  position:relative; height:6px; border-radius:3px; overflow:hidden;
  background:linear-gradient(180deg,var(--v3-well-b),#08080a);
  box-shadow:inset 0 1px 3px rgba(0,0,0,.6);
}
.cv3-weight-bar{ height:100%; transition:width .25s; }
/* Permanent threshold marks at 50% and 90%, so the player sees the cliff
   coming instead of discovering it mid-fight. */
.cv3-weight-track::before,.cv3-weight-track::after{
  content:''; position:absolute; top:0; bottom:0; width:1px; background:rgba(236,233,228,.28); z-index:2;
}
.cv3-weight-track::before{ left:50%; }
.cv3-weight-track::after { left:90%; }
.cv3-weight-warn{ margin-top:4px; font:700 9px/1.3 var(--k-disp); letter-spacing:.06em; }

/* ── drag ──────────────────────────────────────────────────────────
   Valid destinations glow BRASS (this panel's interactive colour), the
   bag shows a solid ring when unequipping, and the discard overlay arms
   red only once the cursor is outside the panel. */
.cv3-slot-drop,.cv3-cell--quest-target{
  border-color:var(--v3-brass-hi)!important;
  box-shadow:0 0 10px rgba(194,167,106,.45),inset 0 0 6px rgba(194,167,106,.20);
  animation:cv3-slot-drop-pulse 1.1s ease-in-out infinite;
}
@keyframes cv3-slot-drop-pulse{
  0%,100%{ box-shadow:0 0 8px rgba(194,167,106,.35),inset 0 0 5px rgba(194,167,106,.16); }
  50%    { box-shadow:0 0 16px rgba(194,167,106,.65),inset 0 0 9px rgba(194,167,106,.30); }
}
@media (prefers-reduced-motion:reduce){
  .cv3-slot-drop,.cv3-cell--quest-target{ animation:none; }
}
.cv3-inv-drop{ outline:2px solid rgba(194,167,106,.55); outline-offset:-4px; border-radius:var(--v3-rad-card); }
.cv3-slot--equip-flash{ animation:cv3SlotEquipFlash .55s ease-out forwards; }
@keyframes cv3SlotEquipFlash{
  0%  { box-shadow:0 0 0 0 rgba(194,167,106,.75); }
  100%{ box-shadow:0 0 0 10px rgba(194,167,106,0); }
}

/* The discard zone is an INVISIBLE full-screen drop-catcher for the whole
   drag — it resolves equip vs no-op vs discard by coordinates. The red
   only ARMS once the cursor leaves the panel, so an equip-bound drag
   never screams "you are about to destroy this". */
.cv3-discard-zone{
  display:none; position:fixed; inset:0; z-index:3000;
  align-items:center; justify-content:center;
  background:transparent; border:none;
  /* Visual-only: pointer capture owns the gesture, so this layer must
     never replace the real slot/cell hit targets. */
  pointer-events:none;
}
.cv3-discard-zone--armed{ background:rgba(239,68,68,.10); border:2px solid rgba(239,68,68,.5); }
.cv3-discard-zone-label{
  display:none; padding:12px 28px; border-radius:var(--v3-rad-card);
  background:rgba(239,68,68,.85); color:#fff; pointer-events:none;
  font:800 15px/1 var(--k-disp); letter-spacing:.14em; text-transform:uppercase;
}
.cv3-discard-zone--armed .cv3-discard-zone-label{ display:block; }
.cv3-drag-ghost{
  position:fixed; left:0; top:0; z-index:3001; margin:0!important;
  pointer-events:none!important; opacity:.9; overflow:hidden;
  filter:drop-shadow(0 10px 14px rgba(0,0,0,.6));
  transform-origin:top left; will-change:transform;
}

/* ITEM-REQUIREMENTS-V1: the "you can't equip this yet" line, rendered by
   inventory.js into the equip flow. Lives here with the rest of the .cv3-*
   namespace rather than in a global sheet — one file owns the prefix. */
.cv3-req-err{ padding:3px 8px; margin:2px 0; font-size:12px; color:#ef4444; }

/* ── scrollbars ─── */
#char-panel-v3 ::-webkit-scrollbar{ width:8px; }
#char-panel-v3 ::-webkit-scrollbar-track{ background:transparent; }
#char-panel-v3 ::-webkit-scrollbar-thumb{
  background:#26262b; border-radius:8px;
  border:2px solid transparent; background-clip:padding-box;
}

/* ── narrow ─── */
@media (max-width:720px){
  #char-v3-body{ flex-direction:column; }
  #char-v3-body-rail{ flex:0 0 auto; border-right:0; border-bottom:1px solid #212126; }
  .cv3-equipment-grid{ max-width:340px; }
  .cv3-grid{ grid-template-columns:repeat(auto-fill,minmax(76px,1fr)); }
}
