/* DO NOT EDIT — copied from packages/shared/src/design/css/hero-detail.css
   by `npm run tokens`. Edit the source there, regenerate, commit both.
   A vitest in packages/shared fails if this file drifts from the source. */
/* Hero detail chrome (Task 1, codex-hero-detail-parity). The single source
 * for the /app Codex's inline hero-detail panel and its overlay body, moved
 * here from apps/server/public/app/codex.css so the Electron desktop app can
 * link the exact same rules instead of duplicating them. Both `.cx-hero-panel*`
 * (the inline panel chrome) and `.hd-*` (the overlay body content — tabs,
 * ability cards, matchups, report card) came from codex.css verbatim; every
 * class name below is FROZEN from this point — both surfaces render markup
 * that targets these exact names.
 *
 * Generated for the two no-build web surfaces via `npm run tokens`
 * (apps/server/public/shared/hero-detail.css); imported directly by the
 * desktop app's build (apps/desktop/src/index.css). Edit only here. */

/* ── Inline hero detail panel (E5.1, redesigned by an owner correction in
 * E5 fix round 1) ─────────────────────────────────────────────────────────
 * Replaces the shared `.tdtt-overlay` modal for the Dice Throne hero detail
 * only (the TCG card detail below still opens through that shared
 * primitive). Owner (E5.1): "I dont want a pop up to show character
 * information, I want it to fill the entire lower screen starting at the top
 * of where you select the heroes in the carsoul." Owner (the correction):
 * "It should not move to the top it should just match its hieght... click
 * the X in the top right that brings you back to the carousel." codex.js's
 * rosterResultsHtml() renders this INSTEAD OF the carousel inside the same
 * `#cx-results` container (never both, never a separate mount node below
 * it) — so there is no margin-top pushing it down from where the carousel
 * would have started, and no scroll needed to reveal it. `min-height`
 * approximates "fill the lower screen" rather than a hard viewport calc —
 * the surrounding chrome (shell top bar, banner, nav rail) has no fixed
 * height this page-local stylesheet can read, so a `calc(100vh - Npx)`
 * would silently go wrong the moment any of those change size; a generous
 * minimum height gets the same visual result without that coupling. */
.cx-hero-panel {
  min-height: 60vh;
  border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); padding: 20px; box-sizing: border-box;
}
/* Task 5 (codex-hero-detail-parity) — owner: "I want an image of the hero
 * also in the hero details page, right now its just a name." The head band
 * grew from a bare name + close button into a portrait, an optional banner
 * backdrop, Set/Archetype/Difficulty identity pills, an overview lead-in and
 * an ability-type line (heroPanelSectionHtml() in codex.js). `position:
 * relative` + `overflow: hidden` let the banner sit absolutely behind the
 * real content; `.cx-hero-panel__head`/`.cx-hero-panel__name` keep their
 * Task 1 names and role unchanged, everything nested below is new. */
.cx-hero-panel__head {
  position: relative;
  margin-bottom: 12px;
  padding: 16px;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--bg);
  overflow: hidden;
}
.cx-hero-panel__name { margin: 0; font-family: var(--head); font-weight: 700; font-size: 1.1rem; color: var(--text); }

/* Banner art exists for only 29 of 44 heroes — no banner is the common case,
 * not the exception. Starts fully transparent so a 404 (onerror hides the
 * <img> outright, codex.js) never flashes a broken-image glyph; only once
 * the image actually LOADS does codex.js add both `.is-loaded` (fades it in)
 * and the head's own `--bannered` modifier (below, adds the legibility
 * gradient). A bannerless hero therefore renders with plain `--bg`, no empty
 * band and no gradient with nothing to darken. */
.cx-hero-panel__banner {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center 20%;
  opacity: 0; z-index: 0;
  transition: opacity .2s ease;
}
.cx-hero-panel__banner.is-loaded { opacity: .45; }
.cx-hero-panel__head--bannered::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgb(from var(--bg) r g b / .15), rgb(from var(--bg) r g b / .92) 85%);
}

.cx-hero-panel__headrow {
  position: relative; z-index: 2;
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
}
.cx-hero-panel__heroinfo { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 14px; }
.cx-hero-panel__portrait {
  flex: 0 0 auto; width: 77px; height: 77px; border-radius: 50%;
  object-fit: cover; object-position: top;
  border: 2px solid var(--border); background: var(--surface-2);
}
.cx-hero-panel__identity { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cx-hero-panel__pills { --hd-chip-h: 26px; display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
.cx-hero-panel__pills .hd-pill {
  display: inline-flex; align-items: center; min-height: var(--hd-chip-h); box-sizing: border-box;
  font-size: 12px; padding: 2px 10px;
}
/* Tier chip: the roster tile .tdtt-tier badge (same component) plus a Tier label, sized to the shared chip height. */
.cx-hero-panel__pills .tdtt-tier {
  min-width: var(--hd-chip-h); height: var(--hd-chip-h); box-sizing: border-box; font-size: .8rem;
}
.hd-tierchip {
  display: inline-flex; align-items: center; gap: 6px; min-height: var(--hd-chip-h);
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim);
}
/* Reuses `.hd-pill`/`.hd-pill--neutral` (below, Abilities tab) rather than a
 * new pill shape — this only adds the icon+label layout the difficulty pill
 * needs to sit next to its `.cx-die` (codex.js's existing complexity die). */
.cx-hero-panel__difficulty {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: var(--hd-chip-h); box-sizing: border-box;
}
/* Real overview first paragraphs run 300-713 characters (Thor 713, Shadow
 * Thief 638) — unclamped, a bannerless hero at 1024px gets roughly six lines
 * of dim text inside what's meant to be a head band, not the Overview tab's
 * own Character Analysis panel (analysisPanelHtml() in hero-detail.js, which
 * still renders the paragraph in full). Two lines keeps this a lead-in. */
.cx-hero-panel__lead {
  margin: 0; font-size: 13px; line-height: 1.5; color: var(--text-dim);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.cx-hero-panel__abilities {
  margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px;
  font-size: 14px; font-weight: 700; letter-spacing: .02em; color: var(--text-dim);
}
/* One counter: icon + label + count badge. kind = offensive | defensive | passive. */
.hd-abcount { --ab-c: var(--muted); display: inline-flex; align-items: center; gap: 6px; font-size: 14px; }
.hd-abcount--offensive { --ab-c: var(--red); }
.hd-abcount--defensive { --ab-c: var(--blue-link); }
.hd-abcount--passive { --ab-c: var(--green); }
.hd-abcount__icon { display: inline-flex; width: 16px; height: 16px; color: var(--ab-c); }
.hd-abcount__icon svg { width: 100%; height: 100%; }
.hd-abcount__label { color: var(--text-dim); }
.hd-abcount__n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 22px; padding: 0 7px; box-sizing: border-box; border-radius: 999px;
  font-family: var(--mono); font-weight: 800; font-size: 13px; line-height: 1;
  color: var(--bg); background: var(--ab-c);
}

/* An X glyph, not the word "Close" (the owner's correction: "click the X in
 * the top right") — `.cx-hero-panel__headrow`'s `justify-content:
 * space-between` puts it top-right. `aria-hidden` on the glyph plus
 * `aria-label` on the button (heroPanelSectionHtml()) keeps a real
 * accessible name, since an "×" character alone reads poorly on some screen
 * readers. */
.cx-hero-panel__close { flex: 0 0 auto; font-size: 1.2rem; line-height: 1; padding: 4px 12px; }

/* ── Hero detail overlay body (C5a) ───────────────────────────────────────
 * Content rendered by /app/hero-detail.js inside the shared `.tdtt-overlay`
 * (chrome.css) once a hero is opened from the carousel above. `.tdtt-tabs`/
 * `.tdtt-tabs__tab` (chrome.css) and `.tdtt-panel`/`.tdtt-panel__head`
 * (chrome.css) are the shared primitives; everything below is what those
 * don't already cover — the Dice Faces grid, status/companion mini-cards,
 * the type pills, and the danger (How to Counter) panel variant. Prefixed
 * `hd-` (hero detail) to keep it visually distinct from `cx-` (the roster/
 * TCG browser above it in this same file) even though both live in one
 * stylesheet per this surface's "page-local CSS goes in codex.css" rule. */
.hd-tabpanel { padding-top: 16px; }
.hd-stack { display: flex; flex-direction: column; gap: 16px; }
.hd-text { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text-dim); }
.hd-text + .hd-text { margin-top: 10px; }
.hd-text--sm { font-size: 13px; margin-top: 4px; }

/* Section headings inside the hero detail: .tdtt-panel__head is 12px by default. */
.cx-hero-panel .tdtt-panel__head, .hd-tabpanel .tdtt-panel__head { font-size: 14px; }

.hd-panel--center { text-align: center; color: var(--muted); font-size: 14px; }
.hd-panel--danger { border-color: rgb(from var(--red) r g b / .2); }
.hd-panel--danger .tdtt-panel__head { color: var(--red); }

/* ── Overview tab — Hero Identity panel (Task 10, codex_character_
 * overview.png's "HERO IDENTITY" card) ───────────────────────────────────
 * Portrait + a short label/value row list, then a lead-in paragraph
 * (`.hd-text`, already defined above). `.hd-identity__portrait` intentionally
 * does not reuse `.cx-hero-panel__portrait` (codex.js's header portrait,
 * this same file) — that class is 64px/circular for the head band; this one
 * sits inside a `.tdtt-panel` card next to a row list and reads better
 * rounded-square, matching the mockup. */
.hd-identity { display: flex; flex-direction: column; gap: 12px; }
.hd-identity__top { display: flex; align-items: flex-start; gap: 14px; }
/* Bug E fix (2026-09-17) — owner: "lets make that portrait image larger."
 * 72px -> 96px (33% bigger), shared by both surfaces since `.hd-identity__portrait`
 * is this one class. */
.hd-identity__portrait {
  flex: 0 0 auto; width: 115px; height: 115px; border-radius: 12px;
  object-fit: cover; object-position: top;
  border: 1px solid var(--border); background: var(--surface-2);
}
.hd-identity__rows { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* Bug E fix (2026-09-17) — owner: "instead of having like the 'Class' and
 * then the actual name ALL THE WAY ON THE RIGHT SIDE, lets have it just have
 * space and then the class name closer to that so its not so far dragged
 * off." `justify-content: space-between` pushed label and value to the two
 * ends of the whole panel width; dropped in favour of the row's own `gap`
 * (10px) so the value sits close after the label — left-grouped, not
 * justified. `.hd-identity__value` no longer needs `text-align: right`
 * either, now that nothing pushes it away from the text it's aligning
 * against. */
.hd-identity__row {
  display: flex; align-items: baseline; gap: 10px;
  font-size: 13px; padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.hd-identity__row:last-child { border-bottom: none; padding-bottom: 0; }
.hd-identity__label { color: var(--muted); }
.hd-identity__value { color: var(--text); font-weight: 700; }

/* Overview: Hero Identity + Dice Faces side by side >= 900px, stacked below. */
.hd-overview-row { display: grid; grid-template-columns: 1fr; gap: 16px; align-items: start; }
@media (min-width: 900px) { .hd-overview-row { grid-template-columns: 1fr 1fr; } }

/* Dice faces: 3 columns x 2 rows, each cell = image + leaflet symbol label. */
.hd-facegrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.hd-face {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px 6px;
}
.hd-face__num { font-family: var(--mono); font-size: 9px; color: var(--muted); }
.hd-face img { width: 48px; height: 48px; object-fit: contain; }
.hd-face__symbol { font-size: 12px; font-weight: 700; color: var(--cyan); text-align: center; }

.hd-minicard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.hd-minicard { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; }
.hd-minicard__head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.hd-minicard__name { font-size: 14px; font-weight: 700; color: var(--text); }
.hd-minicard__name--accent { color: var(--green); }
/* Draft Ban override (C6 fix round 1, item 7): a high ban rate is bad, not
   good, so it gets desktop's red instead of the shared green accent above.
   Per-card modifier only — `.hd-minicard`/`.hd-minicard__name--accent` stay
   unchanged for every other consumer (Pick Rate, Draft Pick, Status Effects,
   Companions). */
.hd-minicard__name--accent-neg { color: var(--red); }

.hd-pill {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 7px; border-radius: 999px; border: 1px solid transparent;
}
.hd-pill--neg { color: var(--red); border-color: rgb(from var(--red) r g b / .3); background: rgb(from var(--red) r g b / .1); }
.hd-pill--pos { color: var(--green); border-color: rgb(from var(--green) r g b / .3); background: rgb(from var(--green) r g b / .1); }
.hd-pill--neutral { color: var(--cyan); border-color: rgb(from var(--cyan) r g b / .3); background: rgb(from var(--cyan) r g b / .1); }

/* C3 — owner: "Rule sheets should not have so many lines and margin spaces
 * within margin spaces. Just have the rule sheets be side by side in the
 * center, if there are more then add those in to fill the line up." No
 * panel wrapper, no border, no per-sheet heading — just the images
 * themselves in a centered, wrapping flex row. 42 of 44 heroes ship exactly
 * 2 sheets (Druid ships 4, Wolverine 3, per f3f10c4), so the common case is
 * a clean side-by-side pair; a wrapping row fills the line with however
 * many sheets exist (1, 3, 4, ...) without a fixed column count or any
 * special-case rule for odd counts. */
.hd-rulesheet-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.hd-rulesheet { width: 100%; max-height: 60vh; object-fit: contain; border-radius: 10px; border: 0; display: block; }

/* ── Abilities tab (C5b) ───────────────────────────────────────────────────
 * Four ability kinds, per the owner's own request ("abilities list
 * ultimate, passive, defensive, offensive"). Colours reuse existing tokens
 * only, matching desktop's ABILITY_TYPE_COLORS choices where a direct
 * equivalent exists (offensive=red, ultimate=gold — desktop's own ultimate
 * treatment is bf-gold throughout) and the closest existing token where it
 * doesn't (defensive=blue-link in place of desktop's ad hoc blue-500;
 * passive=green in place of desktop's ad hoc amber-500, since gold is
 * already spoken for by ultimate and green is otherwise unused by any kind
 * pill on this tab). No new colour literal is introduced. */
.hd-pill--ultimate { color: var(--gold); border-color: rgb(from var(--gold) r g b / .3); background: rgb(from var(--gold) r g b / .1); }
.hd-pill--offensive { color: var(--red); border-color: rgb(from var(--red) r g b / .3); background: rgb(from var(--red) r g b / .1); }
.hd-pill--defensive { color: var(--blue-link); border-color: rgb(from var(--blue-link) r g b / .3); background: rgb(from var(--blue-link) r g b / .1); }
.hd-pill--passive { color: var(--green); border-color: rgb(from var(--green) r g b / .3); background: rgb(from var(--green) r g b / .1); }

/* E5.7 — owner: "The abilities also should have the tulimate up top taking
 * over to columns, and then below that the abilities should be in a grid
 * below that." `.hd-ability-list` stays a simple flex column with two
 * children: the ultimate card (a full flex-width item — spanning the whole
 * grid width in effect, since it sits outside the grid entirely) and
 * `.hd-ability-grid` beneath it, a real grid for the rest.
 *
 * Task 9 fix round 1, item E — owner directive, overriding Task 8's 2-column
 * call directly: the grid is now 3 columns (was 2). A hero has 8 abilities,
 * so that's 3 rows (2 full, 1 partial) instead of 4. */
.hd-ability-list { display: flex; flex-direction: column; gap: 10px; }
.hd-ability-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
/* Task 8 (codex_character_abilities.png mockup restyle) — denser card:
 * tighter gap/padding/radius than the E5.7 baseline so a grid of these reads
 * like the mockup's grid density rather than E5.7's plain list item.
 * `.hd-ability-list`/`.hd-ability-grid` themselves are untouched by Task 8
 * (E5.7's 10px-gap shape stays) — only the column count moved, in the fix
 * round above. */
.hd-ability {
  display: flex; gap: 10px; padding: 10px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface);
}
/* Bug B fix (2026-09-17) — owner: "The ultimates art is not showing and all
 * of them are just cut off." Every ultimate image is a WIDE BANNER (paladin
 * 580x115, barbarian 727x143, thor 773x150 — roughly 5:1), and the old rule
 * absolutely positioned it into a ~48%-wide, full-card-HEIGHT box with
 * `object-fit: cover` — for a 5:1 source that discards the overwhelming
 * majority of the image (only a ~48%-wide, ~9%-of-width-tall sliver survives
 * the crop). Redesigned as a full-width strip across the TOP of the card
 * instead: `.hd-ability--ultimate` becomes a column flex (image, then body,
 * in normal DOM order — the markup itself is unchanged, both surfaces'
 * `UltimateCard`/`ultimateCardHtml()` already render the image before the
 * body div), and the image renders at `width: 100%; height: auto` — its OWN
 * natural aspect, no crop at all. Because the body now sits BELOW the art
 * instead of behind/beside it, the old `::after` legibility gradient and the
 * body's `padding-right` reservation are no longer needed and are removed
 * outright (nothing to fade — text never overlaps the art any more). */
.hd-ability--ultimate {
  display: flex; flex-direction: column; gap: 10px;
  border-color: rgb(from var(--gold) r g b / .35); background: rgb(from var(--gold) r g b / .06);
}
.hd-ability__img { width: 60px; flex-shrink: 0; aspect-ratio: 2 / 3; object-fit: contain; background: var(--bg); border-radius: 6px; }
/* Ultimate-only strip image — full card width, natural aspect ratio (no
 * object-fit crop), rounded on all corners since it no longer bleeds to any
 * card edge. `display: block` drops the inline-image baseline gap that would
 * otherwise leave a hairline of card background under the strip. */
.hd-ability__art { display: block; width: 100%; height: auto; border-radius: 8px; }
/* C (2026-09-17) — click-to-enlarge trigger for ability art (desktop only;
 * see AbilitiesTab.tsx). A plain button reset so it doesn't disturb the
 * image's own sizing/layout rules above/below — `cursor: zoom-in` is the only
 * visual change a wrapped, unclicked image gets. */
.hd-ability__art-btn { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.hd-ability__img-btn { display: block; flex-shrink: 0; padding: 0; border: 0; background: none; cursor: zoom-in; }
.hd-ability__body { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; }
.hd-ability__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.hd-ability__name { font-size: 14px; font-weight: 700; color: var(--text); }
.hd-ability__dmg { margin-top: 2px; }

.hd-tiers { display: flex; flex-direction: column; gap: 6px; }
/* Task 8 — a tier's badge, dice and damage now read as one dense row,
 * `T1 · 3x⚔ + 1x⚔ · 5 dmg`, joined by `.hd-tier__sep` between
 * whichever segments are present (abilityTierHtml()/ultimateCardHtml()
 * filter out empty ones, so there is never a leading/trailing/doubled dot). */
.hd-tier { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.hd-tier__sep { color: var(--muted); }
.hd-tier__badge {
  font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--gold);
  background: rgb(from var(--gold) r g b / .1); border: 1px solid rgb(from var(--gold) r g b / .2);
  border-radius: 4px; padding: 1px 6px;
}
.hd-tier__name { font-size: 12px; font-weight: 700; color: var(--cyan); }
.hd-tier__dmg { font-size: 12px; font-weight: 700; color: var(--red); }

.hd-dice { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; color: var(--cyan); }
.hd-dice__plus { color: var(--muted); }
.hd-dice__icon { width: 13px; height: 13px; object-fit: contain; }
.hd-dice__symbol { color: var(--text-dim); }
/* Task 9 fix round 1, item F — a generic roll type (STRAIGHT_SMALL/
 * STRAIGHT_LARGE/DEFENSIVE_ROLL/DEFENSE_ROLL/PASSIVE) or any other symbol
 * whose per-hero image 404s (onerror adds this class) reads as a small pill
 * instead of a bare dim word standing in for a missing icon — same recipe as
 * `.hd-pill--neutral` (cyan, the existing "no more specific meaning" chip
 * color already used elsewhere in this file). */
.hd-dice__symbol--chip {
  display: inline-block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 7px; border-radius: 999px; color: var(--cyan);
  border: 1px solid rgb(from var(--cyan) r g b / .3); background: rgb(from var(--cyan) r g b / .1);
}

/* Rules text beneath the tier row — plain wrapped lines (no bullet dash),
 * closer to the mockup's flowing description than a mechanical bullet
 * list. Still one line per `effects[]` entry; nothing about the data changes,
 * only how each entry is styled. */
.hd-effects { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 2px; }
.hd-effects li { font-size: 13px; line-height: 1.5; color: var(--text-dim); }

/* ── Cards + Assets tabs (C5c) ─────────────────────────────────────────────
 * `.tdtt-cardgrid`/`.tdtt-cardtile`/`.tdtt-cardtile__body` (chrome.css) are
 * reused verbatim for General/Hero/Info card tiles — the same primitive the
 * TCG faceted browser already uses above in this file, so an image+name+
 * body-text card doesn't need a second grid/tile pair invented here.
 * `.hd-minicard`/`.hd-minicard-grid` (already defined above, for Overview's
 * Status Effects/Companions) cover the text-only fallback grids. What's
 * genuinely new: a plain image+label tile for asset galleries that have no
 * body text (tokens, dice symbols, companion art, shared components), a
 * larger single-column tile for the labelled reference images, a small
 * corner count badge for a card's copy count, and a button that LOOKS like
 * `.tdtt-panel__head` for the General Cards collapse toggle. Accent
 * variants reuse desktop's own per-section colour choices (Tokens/Info Cards
 * = gold, Dice Symbols = cyan, Companion Images = green) via the same
 * `rgb(from var(--x) r g b / .2)` recipe `.hd-panel--danger` already uses. */
/* `.tdtt-cardtile` (chrome.css :738-745) is styled `cursor: pointer` with a
   hover lift for its usual job — a clickable TCG card that opens a detail
   overlay. These General/Hero/Info Cards tiles are inert (no lightbox, see
   this file's header comment) — a hand cursor and a lift on hover for a tile
   that does nothing on click is a false affordance, so this neutralises
   both rather than reusing `.tdtt-cardtile` unmodified. */
.hd-cardtile--static { cursor: default; }
@media (hover: hover) {
  /* Static tiles opt OUT of the shared tile hover, so they must also cancel
     the glow it now casts, not just the transform. */
  .hd-cardtile--static:hover { border-color: var(--border); transform: none; box-shadow: none; }
}

.hd-panel--gold { border-color: rgb(from var(--gold) r g b / .2); }
.hd-panel--gold .tdtt-panel__head { color: var(--gold); }
.hd-panel--cyan { border-color: rgb(from var(--cyan) r g b / .2); }
.hd-panel--cyan .tdtt-panel__head { color: var(--cyan); }
.hd-panel--green { border-color: rgb(from var(--green) r g b / .2); }
.hd-panel--green .tdtt-panel__head { color: var(--green); }
/* Task 9 — Upgrade Cards panel accent, same recipe as the three above. */
.hd-panel--magenta { border-color: rgb(from var(--magenta) r g b / .2); }
.hd-panel--magenta .tdtt-panel__head { color: var(--magenta); }

/* Task 9 — the "Upgrade" pill on an upgrade card's art, reusing `.hd-pill`
 * (already defined above for Abilities/Matchups) rather than a new shape. */
.hd-pill--upgrade { color: var(--magenta); border-color: rgb(from var(--magenta) r g b / .3); background: rgb(from var(--magenta) r g b / .1); }

/* Resets button chrome so a <button> can stand in for `.tdtt-panel__head`
   (the General Cards collapse toggle) without fighting its own flex/font/
   color rules — deliberately omits `font`/`color` shorthands so the panel
   head's font-family/size/weight/letter-spacing/text-transform/color (set
   by the class alongside this one) still win. Top/left/right margins ARE
   reset explicitly below even though modern Chrome/Firefox/Safari already
   default a `<button>`'s margin to 0 — the reset is free and removes the
   reliance on that UA default. `margin-bottom` is deliberately left alone
   in this base rule: leaving it undeclared lets `.tdtt-panel__head`'s own
   `margin-bottom: 12px` (chrome.css) apply unmodified when expanded, so
   that 12px value exists in exactly one place in the codebase. It is the
   COLLAPSED state that needs an override — nothing renders under a
   collapsed toggle, so the panel head's gap must not reserve space there —
   and `.hd-toggle-btn[aria-expanded="false"]`'s `.class[attr]` selector is
   0,2,0 specificity, beating `.tdtt-panel__head`'s 0,1,0 outright, so this
   doesn't depend on codex.css loading after chrome.css the way equal-
   specificity selectors would. `line-height: inherit` matches every other
   sibling panel head, which inherits the body's line-height rather than a
   button's UA-default `normal`. */
.hd-toggle-btn {
  background: none; border: none; padding: 0; width: 100%; cursor: pointer;
  text-align: left; line-height: inherit;
  margin-top: 0; margin-left: 0; margin-right: 0;
}
.hd-toggle-btn[aria-expanded="false"] { margin-bottom: 0; }
/* E6: a section disclosure that had no hover or focus state at all. */
@media (hover: hover) {
  .hd-toggle-btn:hover { color: var(--sec-accent, var(--cyan)); }
}
.hd-toggle-btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

.hd-cardtile__count { display: block; font-family: var(--mono); font-size: 12px; color: var(--muted); }

.hd-assetgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 10px; }
.hd-asset { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.hd-asset img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: contain;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 4px;
}
.hd-asset__label {
  font-size: 11px; color: var(--muted); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}

.hd-refgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.hd-ref__label { margin: 0 0 6px; font-size: 12px; color: var(--muted); }
.hd-ref img { width: 100%; border-radius: 10px; border: 1px solid var(--border); display: block; }

/* ── Matchups tab (C5d) ────────────────────────────────────────────────────
 * `.hd-matchup-row` is the two-up layout for Strong/Weak Against. The
 * "one panel otherwise" half is handled in JS, not here: strongWeakRowHtml()
 * emits this wrapper ONLY when both sides exist and returns a lone panel
 * bare, which app-hero-detail.test.ts pins with a not.toContain check. So
 * auto-fit is NOT doing single-panel collapse — it is here for responsive
 * wrap on a narrow viewport, matching every other multi-column grid in this
 * file (hd-facegrid/hd-minicard-grid/hd-refgrid all use auto-fill/auto-fit).
 *
 * Archetype chip colours map each of desktop's five known archetype names
 * (Characters.tsx's `ARCHETYPE_COLORS`, raw Tailwind values not available on
 * this tokens-only surface) to the nearest existing token — see
 * matchupsTabHtml()'s comment in hero-detail.js for the full reasoning,
 * including why --purple (a documented duplicate of --cyan) is not used for
 * Slow Burn. hd-pill--archetype-unknown is the fallback for any other
 * archetype name the data might carry — a neutral muted border/text chip,
 * never a missing one. */
.hd-matchup-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }

.hd-pill--archetype-allin { color: var(--red); border-color: rgb(from var(--red) r g b / .3); background: rgb(from var(--red) r g b / .1); }
.hd-pill--archetype-slowburn { color: var(--magenta); border-color: rgb(from var(--magenta) r g b / .3); background: rgb(from var(--magenta) r g b / .1); }
.hd-pill--archetype-fullboard { color: var(--green); border-color: rgb(from var(--green) r g b / .3); background: rgb(from var(--green) r g b / .1); }
.hd-pill--archetype-buhbye { color: var(--orange); border-color: rgb(from var(--orange) r g b / .3); background: rgb(from var(--orange) r g b / .1); }
.hd-pill--archetype-irondial { color: var(--blue-link); border-color: rgb(from var(--blue-link) r g b / .3); background: rgb(from var(--blue-link) r g b / .1); }
.hd-pill--archetype-unknown { color: var(--muted); border-color: var(--border); background: transparent; }

/* ── Community Stats — Report Card (C6) ────────────────────────────────────
 * Reference: apps/desktop/src/routes/Characters.tsx's `CharacterRankings`
 * (the last unported piece of the desktop hero detail — see hero-detail.js's
 * header comment for the two owner rulings this stage implements). Combat
 * Performance and Draft & Pick Rates reuse `.tdtt-statrow`/`.tdtt-stat`
 * (chrome.css) and `.hd-minicard`/`.hd-minicard-grid` (already defined
 * above) verbatim — the ring and the grade badge below are the only two
 * things that genuinely need new rules.
 *
 * Grade colour mapping (gradeFor() in hero-detail.js) — desktop's raw
 * Tailwind values (bf-gold / green-400 / bf-cyan / yellow-400 / bf-orange /
 * red-400) aren't available on this tokens-only surface, and there is no
 * yellow token, so C has no direct counterpart. Measured against the dark
 * --bg surface with the dataviz skill's palette validator across four
 * candidate mappings for C:
 *   - All six pass contrast vs. surface (>=3:1) — no grade is unreadable.
 *   - --orange (D) <-> --red (F) are DeltaE 9.7 apart in normal vision
 *     (below the 15 "easily told apart" floor — 5.8 deutan / 3.0 tritan).
 *     Inherited from desktop's OWN D/F choice, not introduced here.
 *   - --blue-link for C adds a SECOND collision (vs --cyan, DeltaE 9.3).
 *   - --chrome for C fails the chroma floor outright (reads grey) and
 *     collides with cyan at DeltaE 2.4 protan.
 *   - --magenta for C introduces NO new confusable pair — the only
 *     six-token mapping that leaves D/F as the sole collision.
 * Because D and F are near-indistinguishable, the LETTER inside the badge is
 * load-bearing, not the colour — never reduce this badge to a bare coloured
 * dot; the letter must always render. */
.hd-reportcard { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }

.hd-ring { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.hd-ring__wrap { position: relative; width: 80px; height: 80px; }
/* The whole <svg> rotates -90deg (not a per-circle transform) so the arc
   starts at 12 o'clock, same as desktop's `-rotate-90` on its own <svg>. */
.hd-ring__svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.hd-ring__track { fill: none; stroke: rgb(from var(--red) r g b / .3); stroke-width: 7; }
.hd-ring__arc { fill: none; stroke: var(--green); stroke-width: 7; stroke-linecap: round; }
.hd-ring__mid {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
}
.hd-ring__pct { font-size: 13px; font-weight: 700; }
.hd-ring__pct--win { color: var(--green); }
.hd-ring__pct--loss { color: var(--red); }
.hd-ring__wr { font-size: 7px; color: var(--muted); }
.hd-ring__games { font-size: 9px; color: var(--muted); }

.hd-gradegrid { flex: 1 1 220px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; min-width: 0; }
.hd-grade {
  display: flex; align-items: center; gap: 8px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 8px;
}
.hd-grade__badge {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 8px; border: 1px solid transparent;
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
}
.hd-grade__value { margin: 0; font-size: 10px; font-weight: 700; color: var(--text); }
.hd-grade__label { margin: 2px 0 0; font-size: 7px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }

.hd-grade--s { color: var(--gold); border-color: rgb(from var(--gold) r g b / .3); background: rgb(from var(--gold) r g b / .1); }
.hd-grade--a { color: var(--green); border-color: rgb(from var(--green) r g b / .3); background: rgb(from var(--green) r g b / .1); }
.hd-grade--b { color: var(--cyan); border-color: rgb(from var(--cyan) r g b / .3); background: rgb(from var(--cyan) r g b / .1); }
.hd-grade--c { color: var(--magenta); border-color: rgb(from var(--magenta) r g b / .3); background: rgb(from var(--magenta) r g b / .1); }
.hd-grade--d { color: var(--orange); border-color: rgb(from var(--orange) r g b / .3); background: rgb(from var(--orange) r g b / .1); }
.hd-grade--f { color: var(--red); border-color: rgb(from var(--red) r g b / .3); background: rgb(from var(--red) r g b / .1); }

/* ── Tier panel (Task 9, dt-hero-tiers batch, design doc §7.3) ─────────────
 * Community Rating / Your Rating / Online win rate, hero-detail.js's
 * tierPanelHtml() — the top of the Overview tab. Deliberately a DIFFERENT
 * class prefix (`hd-cr*`/`hd-voterow`) than `.hd-tier*` just above
 * (abilityTierHtml()'s activation-dice tier rows, "T1"/"T2"/"T3") — same
 * name, unrelated meaning (CLAUDE.md's "unrelated tier meanings" gotcha);
 * reusing that prefix here would silently restyle both. The big letters and
 * the five vote buttons reuse the GLOBAL `.tdtt-tier`/`.tdtt-tier--lg` badge
 * (controls.css) — the same primitive the carousel tile badge and the header
 * pill (codex.js) already use — only the distribution bar's fill needs a
 * page-local class, since no shared primitive exists for a bar. */
.hd-crhead { display: flex; align-items: center; gap: 12px; }
.hd-crbars { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.hd-crbar { display: flex; align-items: center; gap: 8px; }
.hd-crbar__track {
  flex: 1 1 auto; height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--surface-2);
}
.hd-crbar__fill { --bar-c: var(--muted); height: 100%; border-radius: 999px; background: var(--bar-c); }
.hd-crbar__fill[data-tier="S"] { --bar-c: var(--gold); }
.hd-crbar__fill[data-tier="A"] { --bar-c: var(--orange); }
.hd-crbar__fill[data-tier="B"] { --bar-c: var(--magenta); }
.hd-crbar__fill[data-tier="C"] { --bar-c: var(--cyan); }
.hd-crbar__fill[data-tier="D"] { --bar-c: var(--muted); }
.hd-crbar__count { width: 24px; text-align: right; font-size: 11px; color: var(--muted); }

.hd-voterow { display: flex; align-items: center; gap: 8px; }
.hd-voterow__btn {
  border: none; background: none; padding: 0; cursor: pointer;
  border-radius: 12px; opacity: .6; transition: opacity .15s, box-shadow .15s;
}
@media (hover: hover) {
  .hd-voterow__btn:hover:not([disabled]) { opacity: 1; }
}
.hd-voterow__btn.on { opacity: 1; box-shadow: 0 0 0 2px var(--cyan); }
.hd-voterow__btn[disabled] { opacity: .35; cursor: not-allowed; }

/* ── Tier panels: Community / Your / Online win rate, one row >= 900px ──── */
.hd-tierrow { display: grid; grid-template-columns: 1fr; gap: 16px; align-items: start; }
@media (min-width: 900px) { .hd-tierrow { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ── Abilities grid: image-only tiles below the full-width ultimate art.
 *    Text appears only in the enlarged view. 2 cols, 3 >= 700px, 4 >= 1100px. */
.hd-ability-grid--images { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
@media (min-width: 700px) { .hd-ability-grid--images { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .hd-ability-grid--images { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.hd-ability-tile { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.hd-ability-tile img { display: block; width: 100%; height: auto; border-radius: 10px; }

/* Enlarged-view text (abilityDetailModel): name, kind chip, tiers, effects. */
.hd-abilitydetail { display: flex; flex-direction: column; gap: 10px; }
.hd-abilitydetail__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.hd-abilitydetail__name { font-size: 16px; font-weight: 700; color: var(--text); }
.hd-abilitydetail__tier { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; color: var(--text-dim); }
.hd-abilitydetail__dmg { font-size: 13px; font-weight: 700; color: var(--red); }
.hd-abilitydetail__effects { margin: 0; padding-left: 18px; font-size: 14px; line-height: 1.55; color: var(--text-dim); }
/* Upgrades section of the enlarged view: one column per upgrade (level pill, card name, CP cost,
   changed tiers/effects, then the upgrade card at a readable ~200px); columns wrap when they do not fit. */
.hd-abilitydetail__upgrades { display: flex; flex-direction: column; gap: 12px; margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--border); }
.hd-abilitydetail__uphead { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--magenta); }
.hd-abilitydetail__uplist { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px; }
.hd-abilitydetail__upgrade { display: flex; flex-direction: column; gap: 6px; flex: 1 1 200px; min-width: 0; max-width: 240px; }
.hd-abilitydetail__upline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.hd-abilitydetail__upname { font-size: 14px; font-weight: 700; color: var(--text); }
.hd-abilitydetail__upcost { font-size: 12px; font-weight: 700; color: var(--gold); }
.hd-abilitydetail__bonus { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border-left: 2px solid var(--cyan); background: var(--surface-2); border-radius: 0 6px 6px 0; }
.hd-abilitydetail__bonuslabel { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--cyan); }
.hd-abilitydetail__bonusname { font-size: 14px; font-weight: 700; color: var(--text); }
.hd-abilitydetail__upcard { width: 200px; max-width: 100%; padding: 0; overflow: hidden; border: 1px solid var(--border); border-radius: 8px; background: none; cursor: zoom-in; }
.hd-abilitydetail__upcard img { display: block; width: 100%; height: auto; }
.hd-abilitydetail__upcard:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.hd-abilitydetail__back { align-self: flex-start; }

/* ── Status Effects tab ──────────────────────────────────────────────────── */
.hd-status-list { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 900px) { .hd-status-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hd-status {
  display: flex; gap: 14px; padding: 12px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--border);
}
.hd-status__token {
  flex: 0 0 auto; width: 56px; height: 56px; object-fit: contain; border-radius: 10px;
  background: var(--bg); border: 1px solid var(--border);
}
.hd-status__body { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; }
.hd-status__head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.hd-status__name { font-size: 15px; font-weight: 700; color: var(--text); }
.hd-status__type, .hd-status__stack {
  display: inline-flex; align-items: center; min-height: 20px; box-sizing: border-box;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 8px; border-radius: 999px; border: 1px solid transparent;
}
.hd-status__type { color: var(--cyan); border-color: rgb(from var(--cyan) r g b / .3); background: rgb(from var(--cyan) r g b / .1); }
.hd-status__type--negative { color: var(--red); border-color: rgb(from var(--red) r g b / .3); background: rgb(from var(--red) r g b / .1); }
.hd-status__type--positive { color: var(--green); border-color: rgb(from var(--green) r g b / .3); background: rgb(from var(--green) r g b / .1); }
.hd-status__stack { color: var(--gold); border-color: rgb(from var(--gold) r g b / .3); background: rgb(from var(--gold) r g b / .1); }
.hd-status__text { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-dim); }
.hd-status__rules { align-self: flex-start; font-size: 13px; font-weight: 600; color: var(--cyan); text-decoration: none; background: none; border: 0; padding: 0; cursor: pointer; }
.hd-status__rules:hover { text-decoration: underline; }
.hd-status__rules:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

/* ── Extra dice (Nevermore Die, Manifest Die): image optional, faces text-only ok ── */
.hd-extradie { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.hd-extradie__img { flex: 0 0 auto; width: 120px; max-width: 40%; height: auto; border-radius: 10px; border: 1px solid var(--border); background: var(--bg); object-fit: contain; }
.hd-extradie__body { min-width: 0; flex: 1 1 220px; display: flex; flex-direction: column; gap: 8px; }
.hd-extradie__name { font-size: 15px; font-weight: 700; color: var(--text); }
.hd-extradie__text { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-dim); }
.hd-extradie__faces { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.hd-extradie__face { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 1.5; color: var(--text-dim); }
.hd-extradie__faceimg { flex: 0 0 auto; width: 32px; height: 32px; object-fit: contain; }
