/* ToonData: page-specific styles.
   Built on top of /assets/site.css (shared Zindigon design system).
   Reuses --bg, --bg-elev, --line, --fg, --accent, --serif, --sans, --mono, --r etc. */

/* Hero background image, held fixed in the viewport so all page content
   scrolls in front of it instead of the image scrolling with the page.
   This is done with a fixed, full-viewport ::before layer behind
   everything rather than `background-attachment: fixed` directly on
   body — plain "fixed" backgrounds are notoriously unreliable on mobile
   Safari (they can fail to paint at all, especially combined with this
   site's overflow-x:hidden body), while a fixed pseudo-element renders
   consistently everywhere and gives the same "stays put" effect. */
/* Sticky-footer layout, scoped to this page rather than touching the
   shared site.css: html/body there only set height:100%, nothing pins the
   footer to the bottom of the viewport or stops it moving as content
   above it changes height. On a static page that's invisible, but
   toondata's #result area fills in over several stages after first paint
   (character render, then a feats-loading skeleton, then the real feat
   counts, then the category tree fetched separately afterward) — every
   stage change pushed the footer down by however much taller the new
   content was. Cloudflare's Core Web Vitals report caught this directly:
   the site-footer element scored the maximum possible CLS (1.0) on six
   separate toondata sessions. Making body a column flex container with
   main allowed to grow doesn't stop #result from changing height, but it
   does mean the footer only ever sits right after whatever main's real
   height is at any given moment — it can't be shorter than the viewport,
   so short pages don't get a footer floating mid-screen either. */
body.toondata {
  background: var(--bg);
  position: relative;
  z-index: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body.toondata > main {
  flex: 1 0 auto;
}
body.toondata::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(rgba(10, 10, 12, 0.72), rgba(10, 10, 12, 0.90)),
    url("superhero-background.jpg") center / cover no-repeat;
}

.td-lookup-form {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 20px;
}
.td-lookup-form .input { flex: 1; min-width: 200px; }

/* Character / League mode toggle above the search box. */
.td-mode-tabs {
  display: flex;
  gap: 6px;
  margin-top: 28px;
  border-bottom: 1px solid var(--line);
}
.td-mode-tab {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  background: none;
  border: none;
  padding: 10px 4px 12px;
  margin-right: 18px;
  cursor: pointer;
  position: relative;
}
.td-mode-tab:hover { color: var(--fg-soft); }
.td-mode-tab.active { color: var(--fg); }
.td-mode-tab.active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--accent);
}

.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* Compare mode — two independent "find a character" mini-forms side by
   side, each with its own match picker, plus a Compare button that only
   lights up once both sides have resolved to one specific character. */
.td-compare-form {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.td-compare-side .td-lookup-form { margin-top: 0; }
.td-compare-selected {
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-soft);
  background: var(--bg-elev);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-sm);
  padding: 8px 12px;
}
.td-compare-selected strong { font-family: var(--sans); color: var(--fg); font-weight: 500; }
.td-compare-selected button {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--fg-mute);
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.td-compare-selected button:hover { color: var(--fg); }
#compareGoBtn { align-self: flex-start; }

.status-line {
  margin-top: 16px;
  padding: 12px 14px;
  border-radius: var(--r-sm);
  font-family: var(--mono);
  font-size: 12px;
  border: 1px solid var(--line);
  background: var(--bg-elev);
  color: var(--fg-soft);
  display: none;
}
.status-line.show { display: block; }
.status-line.error { border-color: rgba(220,80,80,0.4); color: #ff8a8a; }
.status-line.warn  { border-color: var(--accent-line); color: var(--fg); }

.td-match-list { margin-top: 14px; display: grid; gap: 10px; }
.td-match-item {
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px 16px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  transition: border-color 0.15s;
}
.td-match-item:hover { border-color: var(--accent-line); }

.td-result { margin-top: 40px; display: none; }
.td-result.show { display: block; }

.td-columns {
  display: grid;
  /* 60/40 — gear needs the extra room for its own internal paperdoll +
     detail-panel split (see .td-gear-area below); minmax(0, …) keeps a
     long-wrapping equipment name or description from stretching this
     outer grid column past its share. */
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 28px;
  align-items: start;
}
@media (max-width: 880px) {
  .td-columns { grid-template-columns: 1fr; }
  /* Gear stacks above stats on narrow screens (source order, gear is
     div:nth-child(1)) — the selectable paperdoll is the primary way to
     drill into an item's info, so it gets the top slot instead of being
     pushed below CR/SP/health/etc. This used to reorder stats first (back
     when gear was just a scannable stat-adjacent chip row); the
     interactive paperdoll+detail-panel redesign is worth surfacing
     first now, so that override was removed. */
}

/* Character identity header — full-width, sits above the two-column layout
   as the transition from the search bar into the results. */
.td-identity {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 16px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 20px;
  margin-bottom: 28px;
}
.td-name {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 34px;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.td-identity-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.td-tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px 5px 11px;
}
.td-role-tag {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  color: var(--fg);
  font-weight: 600;
  padding: 5px 13px;
}
.td-tag strong {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 12px;
  color: var(--fg);
  text-transform: none;
  letter-spacing: 0;
  margin-left: 5px;
}
.td-identity-icons {
  display: flex;
  gap: 8px;
}
.td-icon-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 7px 11px;
  background: var(--bg-elev);
}
.td-icon-chip svg {
  width: 13px;
  height: 13px;
  flex: none;
}
.td-icon-chip svg path {
  stroke: var(--accent);
  stroke-width: 1.6;
  fill: none;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
.td-icon-chip span {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-soft);
}

/* Hero stats — Combat Rating and Skill Points are the headline numbers */
.td-hero-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-bottom: 14px;
}
.td-hero-stat {
  position: relative;
  padding: 20px 20px 18px;
  border: 1px solid var(--accent-line);
  border-radius: var(--r);
  background:
    radial-gradient(120% 140% at 50% 0%, var(--accent-soft) 0%, transparent 65%),
    var(--bg-elev);
  overflow: hidden;
}
.td-hero-stat::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--accent);
  opacity: 0.7;
}
.td-hero-stat .v {
  font-family: var(--serif);
  font-size: 42px;
  line-height: 1;
  color: var(--fg);
}
.td-hero-stat .k {
  margin-top: 9px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
@media (max-width: 420px) {
  .td-hero-stat .v { font-size: 32px; }
}

/* Level + PvP CR — visibly secondary to the hero stats above */
.td-secondary-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
  margin-bottom: 22px;
}
.td-secondary-stats > div {
  padding: 10px 14px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.td-secondary-stats .k {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.td-secondary-stats .v {
  font-family: var(--sans);
  font-weight: 500;
  margin-top: 3px;
}
.td-secondary-stats .v.td-level { font-size: 15px; color: var(--fg-soft); }
.td-secondary-stats .v.td-pvp   { font-size: 13px; color: var(--fg-mute); }

.td-kv {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
  margin-bottom: 22px;
}
.td-kv > div {
  padding: 14px 16px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.td-kv .k {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.td-kv .v {
  font-family: var(--serif);
  font-size: 18px;
  margin-top: 4px;
}

.td-rows {
  border-top: 1px solid var(--line);
  margin-bottom: 22px;
}
.td-rows .td-row {
  display: flex;
  justify-content: space-between;
  padding: 10px 2px;
  border-bottom: 1px solid var(--line);
}
.td-rows .td-row .k {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.td-rows .td-row .v {
  font-family: var(--serif);
  font-size: 16px;
  color: var(--fg);
}

.td-section-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin: 0 0 14px;
}

/* Paperdoll — silhouette with equipped-slot chips flanking it.
   Slots 0-7 down the left, 8-14 down the right, each column evenly
   spaced top to bottom. Slot -> body part isn't mapped yet, so this is
   just even spacing for now, not anatomically placed. */
.td-paperdoll {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: stretch;
  gap: 8px;
  margin-bottom: 24px;
}
.td-paperdoll-figure {
  position: relative;
  border-radius: var(--r);
  overflow: hidden;
  /* The silhouette PNG is mostly transparent, so it needs something behind
     it to actually read as a shape against the page's near-black surface.
     A soft mint radial glow (matching the hero-stat cards) plus a faint
     accent border does that without introducing a new visual language. */
  background:
    radial-gradient(75% 75% at 50% 38%, var(--accent-soft) 0%, transparent 72%),
    var(--bg-elev);
  border: 1px solid var(--accent-line);
  min-height: 380px;
}
.td-paperdoll-figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
.td-paperdoll-slots {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 8px 0;
}
.td-slot-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.td-slot-chip .n {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.td-slot-chip .v {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 4px 8px;
  background: var(--bg-elev);
  white-space: nowrap;
  /* Recovered item names run much longer than a raw item_id ever did —
     capped and truncated so a long name can't stretch this "auto" grid
     column and unbalance the paperdoll figure next to it. Full name is
     still available via the title attribute on hover. */
  max-width: 108px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.td-slot-chip .v.empty { color: var(--fg-faint); }

/* Below ~600px there isn't room to flank the figure with two columns of
   chips, so the figure moves to the top (shorter, so it doesn't eat the
   screen) and the two slot columns sit side by side underneath it,
   wrapping as needed. The figure stays visible — it just changes position
   — rather than disappearing on small screens. */
@media (max-width: 600px) {
  .td-paperdoll {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "figure figure"
      "left   right";
    gap: 12px;
  }
  .td-paperdoll-figure {
    grid-area: figure;
    min-height: 220px;
  }
  .td-paperdoll-slots:nth-child(1) { grid-area: left; }
  .td-paperdoll-slots:nth-child(3) { grid-area: right; }
  .td-paperdoll-slots {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    padding: 0;
  }
}

.td-gear-details summary.td-section-label,
.td-extra-slots summary.td-section-label {
  cursor: pointer;
  margin: 0;
  list-style: none;
}
.td-gear-details summary.td-section-label::-webkit-details-marker,
.td-extra-slots summary.td-section-label::-webkit-details-marker { display: none; }
.td-gear-details summary.td-section-label::before,
.td-extra-slots summary.td-section-label::before {
  content: "▸";
  display: inline-block;
  margin-right: 6px;
  color: var(--accent);
  transition: transform 0.15s;
}
.td-gear-details[open] summary.td-section-label::before,
.td-extra-slots[open] summary.td-section-label::before { transform: rotate(90deg); }
.td-gear-details .td-gear-table { margin-top: 14px; }

/* Slots outside the known 0-14 range (weapon/offhand/trinket/stat-mod,
   etc.) — kept out of the fixed flanking columns so they can't stretch
   the paperdoll, but still surfaced here instead of only existing in the
   full items table below. */
.td-extra-slots { margin-bottom: 20px; }
.td-extra-slots-grid {
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.td-gear-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.td-gear-table th, .td-gear-table td {
  text-align: left;
  padding: 9px 10px;
  border-bottom: 1px solid var(--line);
}
.td-gear-table th {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  font-weight: 500;
}
.td-gear-table td { color: var(--fg-soft); font-family: var(--mono); font-size: 12px; }
.td-gear-table tr:last-child td { border-bottom: none; }
.td-slot-id { color: var(--fg-faint); }

/* ---------------------------------------------------------------------
   Equipped Gear (single-character view only): a selectable paperdoll +
   a detail panel beside it, replacing the old hover-only chip. See
   slotButtonHtml/gearDetailHtml/wireGearPanel in app.js. Internally
   ~55% paperdoll / ~45% detail; the OUTER 60/40 gear/stats split lives
   on .td-columns above. Compare's own simpler hover-tooltip paperdoll
   is untouched and still uses .td-paperdoll/.td-slot-chip/.td-gear-table
   exactly as before — everything below is new, additive classes, not a
   replacement of what Compare depends on.
   --------------------------------------------------------------------- */
.td-gear-area {
  display: flex;
  align-items: flex-start;
  gap: 20px;
}
.td-paperdoll-wrap { flex: 1 1 55%; min-width: 0; }
.td-gear-detail {
  flex: 1 1 45%;
  min-width: 0;
  align-self: stretch;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg-elev);
  padding: 18px 20px;
}

/* Below ~700px there isn't room for the paperdoll and the detail panel
   side by side without either feeling cramped, so the panel drops below
   the paperdoll (full width) instead of squeezing both — this is
   independent of the OUTER 880px breakpoint that stacks gear above
   stats, so a "smaller desktop/tablet" width where gear already has the
   full page width to itself can still show paperdoll+detail side by
   side, per spec. */
@media (max-width: 700px) {
  .td-gear-area { flex-direction: column; }
  .td-paperdoll-wrap, .td-gear-detail { flex-basis: auto; width: 100%; }
}

/* The decorative border frame (gear-frame.png) with the character
   silhouette centered in its open top, and every slot button absolutely
   positioned at the exact percentage coordinates its own artwork opening
   sits at (see GEAR_FRAME_SLOTS in app.js — those percentages were read
   directly off the source image). Deliberately its own class names
   (td-gear-frame*, not td-paperdoll*) — sharing Compare's actual
   .td-paperdoll/.td-paperdoll-slots/.td-paperdoll-figure classes would
   silently couple this redesign to Compare's simpler chip-based paperdoll
   (a change to one could reflow the other), which the "don't touch
   Compare" scope for this task rules out.
   A fixed aspect-ratio keeps every button's percentage position landing
   inside its own frame opening at any render width — this is the reason
   the whole thing can be one responsive layout instead of needing its own
   extra breakpoints the way the old flanking-columns paperdoll did. */
.td-gear-frame {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  aspect-ratio: 1024 / 1536;
}
.td-gear-frame-border {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Purely decorative chrome — the interactive buttons sit on top of it. */
  pointer-events: none;
}
.td-gear-frame-silhouette {
  position: absolute;
  left: 21%;
  top: 2%;
  width: 58%;
  height: 74%;
  object-fit: contain;
  object-position: center top;
  opacity: 0.9;
  pointer-events: none;
}

/* Slot buttons — compact, game-like equipment-slot controls. Icon on
   top, slot name below it, then either a state dot (identified/
   unidentified) or the word "Empty" — never a raw item name or ID. */
.td-slot-btn {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  min-height: 62px;
  padding: 8px 4px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-elev);
  text-align: center;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.td-slot-btn:hover { border-color: var(--accent-line); }
.td-slot-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.td-slot-icon { color: var(--fg-mute); }
.td-slot-icon svg { width: 18px; height: 18px; display: block; }
/* Icon paths are drawn in a 64-unit viewBox (see SLOT_ICON_PATHS in app.js)
   — stroke-width scaled up from the old 16-unit glyph set's 1.3 to keep
   the same visual weight at this render size (~2.2 was the icon set's own
   authored stroke-width in that same 64-unit space). */
.td-slot-icon svg * { stroke: currentColor; stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; }

.td-slot-name {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Identified: a small FILLED mint dot. Unidentified: a hollow gray ring —
   a different shape, not just a different color, so the distinction
   still reads without relying on color alone. Deliberately not a
   checkmark, so it's never confused with feat completion elsewhere on
   the page. */
.td-slot-state-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.4px solid var(--fg-faint);
  background: none;
}
.td-slot-btn.is-identified .td-slot-state-dot { border-color: #4caf7d; background: #4caf7d; }
.td-slot-btn.is-identified .td-slot-icon,
.td-slot-btn.is-identified .td-slot-name { color: #8fe0b8; }
.td-slot-state-text {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.td-slot-btn.is-empty { opacity: 0.62; }

.td-slot-btn.is-selected {
  border-color: var(--accent);
  background:
    radial-gradient(120% 140% at 50% 0%, var(--accent-soft) 0%, transparent 70%),
    var(--bg-elev);
  box-shadow: 0 0 0 1px var(--accent-line);
}
.td-slot-btn.is-selected .td-slot-icon { color: var(--accent); }

/* Compact variant — used only for the buttons sitting directly on the
   border frame's own artwork openings (see .td-gear-frame above), where
   there's no room for a background/border/visible name without visually
   fighting the frame's own drawn octagon. The frame's artwork supplies the
   "slot" visual; these buttons are just the interactive icon+state layer
   floating inside each opening, absolutely positioned by inline
   left/top % (set from GEAR_FRAME_SLOTS in app.js). */
.td-slot-btn-compact {
  position: absolute;
  width: 9%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  min-height: 0;
  padding: 0;
  border: none;
  background: none;
  border-radius: 50%;
  justify-content: center;
}
.td-slot-btn-compact:hover { border-color: transparent; filter: brightness(1.35); }
.td-slot-btn-compact.is-selected {
  border: none;
  background: none;
  box-shadow: none;
  filter: drop-shadow(0 0 5px var(--accent));
}
.td-slot-btn-compact .td-slot-icon svg { width: 100%; height: 100%; }
.td-slot-btn-compact .td-slot-icon { width: 62%; height: 62%; }
/* No room for the "Empty" text label at this size — the state still
   reads through the faded icon (below) and is still exposed to screen
   readers via aria-label regardless of what's visually shown. */
.td-slot-btn-compact .td-slot-state-text { display: none; }
.td-slot-btn-compact .td-slot-state-dot {
  position: absolute;
  right: 2%;
  bottom: 2%;
  width: 22%;
  height: 22%;
  border-width: 1.5px;
}
.td-slot-btn-compact.is-empty { opacity: 1; }
.td-slot-btn-compact.is-empty .td-slot-icon { opacity: 0.4; }

/* Untracked (Consumable/Soder, Artifacts) — visually distinct from a real
   empty/unidentified slot so it never reads as "confirmed no item here":
   a faded icon inside an always-visible dashed ring, no state dot at all,
   since there's no real identified/unidentified/empty data behind it. */
.td-slot-btn.is-untracked { cursor: help; }
.td-slot-btn.is-untracked .td-slot-icon { opacity: 0.4; }
.td-slot-btn.is-untracked::after {
  content: "";
  position: absolute;
  inset: 4%;
  border: 1.5px dashed var(--fg-faint);
  border-radius: 50%;
  pointer-events: none;
}
.td-slot-btn.is-untracked.is-selected {
  filter: drop-shadow(0 0 4px var(--fg-mute));
}
.td-gear-detail-title-untracked { color: var(--fg-mute); }

/* Additional Equipment — same slot-button language, wrapped into a
   collapsed strip so extra trinket/artifact-style slots (or any future
   slot id) don't crowd the primary 14-slot paperdoll. */
.td-additional-gear { margin-top: 16px; }
.td-additional-gear-grid {
  margin-top: 12px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
}
.td-additional-gear-grid .td-slot-btn { min-height: 58px; }

/* Detail panel content */
.td-gear-detail-slot {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin-bottom: 6px;
}
.td-gear-detail-title, .td-gear-detail-name {
  font-family: var(--serif);
  font-size: 19px;
  color: var(--fg);
  margin: 0 0 6px;
  overflow-wrap: break-word;
}
.td-gear-detail-title-unidentified { color: var(--fg-soft); }
.td-gear-elite-tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8fe0b8;
  border: 1px solid rgba(76,175,125,0.45);
  background: rgba(76,175,125,0.10);
  border-radius: 999px;
  padding: 2px 8px;
  vertical-align: middle;
}
.td-gear-detail-note {
  font-size: 13px;
  color: var(--fg-mute);
  line-height: 1.5;
  margin: 0 0 10px;
}
.td-gear-detail-desc {
  font-size: 13px;
  font-style: italic;
  color: var(--fg-soft);
  line-height: 1.5;
  margin: 0 0 14px;
}
.td-gear-detail-field {
  padding: 8px 0;
  border-top: 1px solid var(--line);
}
.td-gear-detail-field .k {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.td-gear-detail-field .v {
  margin-top: 3px;
  font-size: 13px;
  color: var(--fg);
  line-height: 1.5;
  overflow-wrap: break-word;
}

/* Socketed mod(s) get their own visibly separated block — a distinct
   surface and left accent border — so a mod's name/description can never
   be mistaken for the equipment's own description above it. */
.td-gear-mods {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.td-gear-mods-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin: 0 0 8px;
}
.td-gear-mod {
  border-left: 2px solid var(--accent-line);
  background: var(--accent-soft);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: 8px 12px;
  margin-bottom: 8px;
}
.td-gear-mod:last-child { margin-bottom: 0; }
.td-gear-mod-name { font-size: 13px; color: var(--fg); font-weight: 500; }
.td-gear-mod-tier { color: var(--fg-mute); font-weight: 400; font-size: 11px; }
.td-gear-mod-effect { margin-top: 4px; font-size: 12px; color: var(--fg-soft); line-height: 1.5; }
.td-gear-mod-meta { margin-top: 3px; font-size: 11px; color: var(--fg-mute); }

/* Technical Details — collapsed by default, same disclosure-triangle
   pattern used elsewhere on the site; this is the ONLY place a raw item
   ID or augment ID ever shows. */
.td-gear-technical { margin-top: 14px; }
.td-gear-technical summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fg-mute);
}
.td-gear-technical summary::-webkit-details-marker { display: none; }
.td-gear-technical summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 6px;
  color: var(--accent);
  transition: transform 0.15s;
}
.td-gear-technical[open] summary::before { transform: rotate(90deg); }
.td-gear-technical-body { margin-top: 8px; }
.td-gear-technical-body .td-gear-detail-field:first-child { border-top: none; padding-top: 0; }

.td-gear-summary-counts {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--fg-soft);
  margin-top: 4px;
}

/* Small screens: same tightening the rest of the page does. */
@media (max-width: 560px) {
  .td-gear-detail { padding: 14px 16px; }
  .td-slot-btn { min-height: 54px; }
}


/* League roster — same table language as the gear table, but rows are
   clickable through to that member's own character view. */
.td-roster-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.td-roster-table th, .td-roster-table td {
  text-align: left;
  padding: 11px 10px;
  border-bottom: 1px solid var(--line);
}
.td-roster-table th {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  font-weight: 500;
}
.td-roster-table td { color: var(--fg-soft); font-family: var(--sans); font-size: 13px; }
.td-roster-table td:first-child { color: var(--fg); font-weight: 500; }
.td-roster-table tr:last-child td { border-bottom: none; }
.td-roster-table th.sortable {
  cursor: pointer;
  user-select: none;
}
.td-roster-table th.sortable:hover { color: var(--fg-soft); }
.td-roster-table th.sortable::after {
  content: "";
  display: inline-block;
  width: 8px;
  margin-left: 5px;
}
.td-roster-table th.sortable.sorted-asc::after,
.td-roster-table th.sortable.sorted-desc::after {
  color: var(--accent);
}
.td-roster-table th.sortable.sorted-asc::after { content: "▲"; }
.td-roster-table th.sortable.sorted-desc::after { content: "▼"; }
.td-roster-row { cursor: pointer; transition: background 0.15s; }
.td-roster-row:hover, .td-roster-row:focus-visible { background: var(--accent-soft); outline: none; }
.td-roster-note {
  margin-top: 14px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
}

/* Roster checkbox column — narrow, and doesn't trigger the row's
   click-through to a profile (that's guarded in JS too). */
.td-roster-select-th, .td-roster-select-td {
  width: 34px;
  text-align: center !important;
}
.td-roster-checkbox { cursor: pointer; }
.td-roster-checkbox:disabled { cursor: not-allowed; opacity: 0.35; }
.td-roster-actions { margin-top: 16px; }

/* Comparison view — two equal columns wherever a stat, gear, or feat block
   needs to sit side by side. Stacks to one column on narrow screens, same
   breakpoint as the character view's own two-column layout. */
.td-compare-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  align-items: start;
}
@media (max-width: 880px) {
  .td-compare-columns { grid-template-columns: 1fr; }
}
.td-compare-side-label {
  font-family: var(--serif);
  font-size: 20px;
  margin: 0 0 14px;
}
.td-compare-identity-row { margin-bottom: 28px; }

.td-compare-stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.td-compare-stats-table th, .td-compare-stats-table td {
  text-align: left;
  padding: 10px 10px;
  border-bottom: 1px solid var(--line);
}
.td-compare-stats-table th {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  font-weight: 500;
}
.td-compare-stats-table th:first-child { visibility: hidden; }
.td-compare-stats-table td:first-child {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.td-compare-stats-table td:not(:first-child) {
  font-family: var(--serif);
  font-size: 16px;
  color: var(--fg);
}
.td-compare-stats-table td.td-stat-better { color: var(--accent); }
.td-compare-stats-table tr:last-child td { border-bottom: none; }

/* Loading skeleton — used wherever a piece of the page (feats, league) is
   still being fetched in the background after the rest has already
   rendered. A slow, subtle opacity pulse reads as "still working" without
   being distracting. */
.td-loading {
  color: var(--fg-faint) !important;
  animation: td-pulse 1.4s ease-in-out infinite;
}
@keyframes td-pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 0.9; }
}

.td-feat-summary { display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px; }
.td-feat-summary .kv-single .k {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.10em;
  text-transform: uppercase; color: var(--fg-mute);
}
.td-feat-summary .kv-single .v {
  font-family: var(--serif); font-size: 26px; margin-top: 4px;
}
.td-feat-of-total {
  font-family: var(--mono);
  font-size: 14px;
  color: var(--fg-mute);
}

.td-feat-ids {
  margin-top: 12px;
  max-height: 200px;
  overflow-y: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.td-feat-ids span {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  padding: 4px 9px;
  border-radius: 999px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--line);
  color: var(--fg-mute);
}
/* Feats with a recovered name (attached server-side by the Worker — see
   enrichFeatNames in toondata-census-worker.js) are styled a bit brighter
   than the plain "#12345" chips so it's obvious at a glance which ones
   have a real name and which are still just an ID. */
.td-feat-ids span.td-feat-named {
  font-family: var(--sans);
  letter-spacing: normal;
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.16);
  color: var(--fg-soft);
}

.td-limitations {
  font-size: 13px;
  color: var(--fg-soft);
  line-height: 1.7;
  padding-left: 18px;
}
.td-limitations li { margin-bottom: 6px; }

/* Feats menu — a DCUO-style browser (main category -> subcategory -> feat
   list) built from the Worker's /feat-catalog endpoint. Main categories
   and subcategories are plain buttons + hidden panels (not <details>)
   because the completion/selection rules here are more than simple
   independent expand-collapse: only one subcategory's feat list is ever
   open across the WHOLE tree at once (see wireFeatMenuEvents in app.js),
   which <details> has no native way to express across separate elements. */
.td-feat-categories {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Cloudflare's Core Web Vitals report flagged the site-footer with a
     maximum (1.0) CLS score on several toondata sessions. Root cause: this
     placeholder ("Loading feat categories…") is a single short line, then
     getFeatCatalog()/loadFeatCategoryTreeInto() replace it with the full
     tree — often several hundred pixels taller — well after the rest of
     the page has already painted, so everything below it (including the
     footer) visibly jumps down. A fixed min-height on the placeholder
     state closes most of that gap without needing to know the real tree's
     height in advance; it's a no-op once real content exceeds it. */
  min-height: 160px;
}

/* ---------------------------------------------------------------------
   Feats: two-column browser. Left column (.td-feat-nav) lists every main
   category (name, compact total, progress bar) plus, for whichever one is
   expanded, its subcategories (name + compact total) underneath; picking
   a subcategory — or Unmapped Feats, which has no subcategory step of its
   own — fills the right column (.td-feat-results) with that group's feat
   cards. Only one main category is ever expanded and only one
   subcategory/Unmapped Feats is ever selected at a time (app.js keeps
   both single-valued, never a set), so there's only ever one feat list
   showing anywhere in the browser.
   Below 860px the two columns become three single-column drill-down
   "screens" (categories -> subcategories -> results) driven by the root's
   [data-view] attribute — see the media query near the bottom of this
   block. That attribute is only ever derived from the same expand/select
   state the desktop layout uses, never tracked separately, so the two
   layouts can't drift out of sync with each other.
   --------------------------------------------------------------------- */
.td-feat-browser { display: flex; align-items: flex-start; gap: 18px; }

.td-feat-nav {
  flex: 0 0 300px;
  min-width: 0;
  max-height: 78vh;
  overflow-y: auto;
  position: sticky;
  top: 16px;
  padding-right: 4px;
}
.td-feat-nav-maincats { display: flex; flex-direction: column; gap: 8px; }

/* Main categories are the visually dominant tier: bordered card-like
   rows with the largest type and most padding in the nav. */
.td-feat-nav-maincat {
  border: 1px solid var(--line);
  border-radius: var(--r, 10px);
  background: rgba(255,255,255,0.02);
  overflow: hidden;
}
/* Green means "everything this category represents is complete" — never
   a partial state — so it's reserved for the main category itself, not
   inherited by anything still inside an incomplete subcategory. */
.td-feat-nav-maincat.is-complete {
  border-color: rgba(76,175,125,0.5);
  background: rgba(76,175,125,0.06);
}
.td-feat-nav-maincat.is-expanded {
  border-color: var(--accent);
  background: rgba(255,255,255,0.045);
}
.td-feat-nav-maincat.is-selected { border-color: #6fe0b0; }

.td-feat-nav-maincat-header {
  all: unset;
  cursor: pointer;
  width: 100%;
  box-sizing: border-box;
  padding: 13px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--serif);
  font-size: 16px;
  color: var(--fg);
}
.td-feat-expand-icon {
  display: inline-block;
  color: var(--accent);
  transition: transform 0.15s;
  font-family: var(--sans);
  font-size: 13px;
  flex-shrink: 0;
}
.td-feat-nav-maincat.is-expanded .td-feat-expand-icon { transform: rotate(90deg); }
.td-feat-nav-maincat-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.td-feat-nav-maincat.is-complete .td-feat-nav-maincat-name { color: #8fe0b8; }
.td-feat-nav-maincat-count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg-mute);
  white-space: nowrap;
  flex-shrink: 0;
}
.td-feat-progress-bar {
  height: 6px;
  border-radius: 999px;
  background: rgba(255,255,255,0.06);
  overflow: hidden;
}
.td-feat-nav-maincat > .td-feat-progress-bar { margin: 0 16px 12px; width: calc(100% - 32px); }
.td-feat-progress-bar-sm { height: 4px; }
.td-feat-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.2s;
}
.td-feat-progress-fill.is-complete { background: #4caf7d; }

/* Subcategories are a deliberately lighter, smaller navigation tier
   nested under the expanded main category — indented, a darker surface
   color, no progress bar at all (just the compact count) so this tier
   never gets mistaken for the main-category tier above it. */
.td-feat-nav-subcats {
  padding: 4px 10px 12px 22px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: rgba(0,0,0,0.12);
}
.td-feat-nav-subcat {
  all: unset;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  width: 100%;
  padding: 7px 10px;
  border-radius: 7px;
  border-left: 2px solid transparent;
  font-family: var(--sans);
  font-size: 12px;
  color: var(--fg-soft);
}
.td-feat-nav-subcat:hover { background: rgba(255,255,255,0.04); }
.td-feat-nav-subcat.is-complete { color: #8fe0b8; }
/* The selected subcategory's clear, color-plus-border state (never color
   alone — the left border and count color shift too). */
.td-feat-nav-subcat.is-selected {
  border-left-color: #6fe0b0;
  background: rgba(76,175,125,0.10);
  color: #cdf5e2;
}
.td-feat-nav-subcat-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.td-feat-nav-subcat-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--fg-faint);
  white-space: nowrap;
  flex-shrink: 0;
}
.td-feat-nav-subcat.is-selected .td-feat-nav-subcat-count { color: #9fe8c4; }

/* Only shown in the mobile drill-down (see the 860px media query) — on
   desktop a main category collapses by clicking its own header again, so
   no back control is needed there. */
.td-feat-mobile-back {
  all: unset;
  display: none;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  padding: 4px 2px 10px;
}

/* ---- Right column: selected subcategory / Unmapped Feats results ---- */
.td-feat-results { flex: 1 1 auto; min-width: 0; }
.td-feat-results-placeholder {
  font-size: 13px;
  color: var(--fg-mute);
  font-style: italic;
  padding: 20px 4px;
}
.td-feat-breadcrumb {
  font-family: var(--serif);
  font-size: 16px;
  color: var(--fg);
  margin-bottom: 8px;
}
.td-feat-breadcrumb-sep { color: var(--fg-faint); margin: 0 6px; }
.td-feat-results-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-bottom: 12px;
}
.td-feat-results-count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg-mute);
  white-space: nowrap;
}
.td-feat-results-header > .td-feat-progress-bar { flex: 1 1 160px; }
.td-feat-completed-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0 2px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-faint);
}
.td-feat-completed-divider::before, .td-feat-completed-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}

.td-feat-unmapped-counts {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--fg-soft);
  margin-bottom: 8px;
}
.td-feat-unmapped-counts strong { color: var(--fg); font-family: var(--mono); }
.td-feat-search-row { margin-bottom: 10px; }
.td-feat-unmapped-search {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: rgba(255,255,255,0.03);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 13px;
}
.td-feat-unmapped-search:focus { outline: none; border-color: var(--accent); }

.td-feat-list {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Unmapped Feats has no subcategory step — its list sits directly under
   the main category header, which stays outside this scroll container so
   it's always visible while the (potentially very long) list scrolls. */
.td-feat-unmapped-note {
  font-size: 12px;
  color: var(--fg-mute);
  line-height: 1.5;
  margin: 0 0 10px;
  font-style: italic;
}
.td-feat-unmapped-scroll {
  max-height: 420px;
  overflow-y: auto;
  padding-right: 4px;
}

/* Sits below the scroll container (outside it, so it's reachable without
   scrolling to the bottom) and only renders while more unmapped feats
   remain to be paged in — see UNMAPPED_PAGE_SIZE in app.js. */
.td-feat-load-more {
  display: block;
  width: 100%;
  margin-top: 10px;
  font-size: 13px;
}

/* One row per feat — name + stars/points + status on the first line,
   description (when the recovered data has one) or the raw feat_id (for
   Unmapped Feats) underneath. Only two visual states, per spec: unfinished
   (the site's existing dark/black feat styling) and completed (green,
   with a checkmark) — "in progress" still gets its own status label, just
   not its own color, so it stays legible without a third visual tier. */
.td-feat-row {
  border-left: 3px solid var(--line);
  background: rgba(255,255,255,0.015);
  border-radius: 0 6px 6px 0;
  padding: 8px 12px;
}
.td-feat-row-unfinished { border-left-color: var(--line); }
.td-feat-row-complete {
  border-left-color: #4caf7d;
  background: rgba(76,175,125,0.06);
}

.td-feat-row-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.td-feat-check {
  color: #4caf7d;
  font-weight: 700;
  font-size: 13px;
  line-height: 1;
}
.td-feat-row-name {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--fg);
  font-weight: 500;
}
.td-feat-row-complete .td-feat-row-name { color: #8fe0b8; }
.td-feat-row-points {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--fg-faint);
}
.td-feat-row-desc {
  margin-top: 4px;
  font-size: 12px;
  color: var(--fg-mute);
  line-height: 1.5;
}
.td-feat-row-id {
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-faint);
}
.td-feat-row-status {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--fg-faint);
  white-space: nowrap;
}
.td-feat-row-complete .td-feat-row-status {
  color: #8fe0b8;
  border-color: rgba(76,175,125,0.45);
  background: rgba(76,175,125,0.10);
}

.td-feat-stars { display: inline-flex; align-items: center; gap: 2px; }
.td-feat-star { width: 13px; height: 13px; display: block; }

/* ≥861px: side-by-side two-column browser, both columns always visible.
   ≤860px: a single-column drill-down through the same hierarchy — only
   one "screen" (category list / expanded subcategories / results) shows
   at a time, switched purely by the root's [data-view] attribute (see
   featBrowserView() in app.js — it's derived, never a separately tracked
   flag, so it can't drift out of sync with the desktop layout's own
   expand/select state). The back controls that appear here are the same
   .td-feat-mobile-back buttons already rendered (but hidden) on desktop. */
@media (max-width: 860px) {
  .td-feat-browser { display: block; }
  .td-feat-nav { position: static; max-height: none; overflow: visible; padding-right: 0; }
  .td-feat-mobile-back { display: block; }

  .td-feat-browser[data-view="categories"] .td-feat-results { display: none; }

  .td-feat-browser[data-view="subcats"] .td-feat-results { display: none; }
  .td-feat-browser[data-view="subcats"] .td-feat-nav-maincat-header { display: none; }
  .td-feat-browser[data-view="subcats"] .td-feat-nav-maincat:not(.is-expanded) { display: none; }
  .td-feat-browser[data-view="subcats"] .td-feat-nav-maincat.is-expanded { border: none; background: none; }
  .td-feat-browser[data-view="subcats"] .td-feat-nav-subcats { padding: 4px 4px 12px; background: none; }

  .td-feat-browser[data-view="results"] .td-feat-nav { display: none; }
}

/* Small screens: the header rows already wrap (flex-wrap above), this
   just tightens padding/font-size so the progress bar's own wrapped line
   and the touch targets stay comfortable rather than cramped. */
@media (max-width: 560px) {
  .td-feat-nav-maincat-header { padding: 11px 12px; font-size: 15px; }
  .td-feat-nav-maincat-count, .td-feat-nav-subcat-count { font-size: 10px; }
  .td-feat-row-status { margin-left: 0; }
}

/* Combat Rating calculator — a standalone tool (no search involved), so it
   sits in the same spot the other three modes' forms occupy, matching
   their spacing and card language rather than introducing a new one. */
.td-cr-calc { margin-top: 20px; }

.td-cr-fillall {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  background: var(--bg-elev);
  border: 1px solid var(--accent-line);
  border-radius: var(--r);
  padding: 14px 16px;
}
.td-cr-fillall label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
  white-space: nowrap;
}
.td-cr-fillall .input { max-width: 140px; }

.td-cr-grid {
  margin-top: 20px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
@media (max-width: 560px) {
  .td-cr-grid { grid-template-columns: 1fr; }
}
.td-cr-slot {
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px;
}
.td-cr-slot label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}
.td-cr-slot-name {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--fg-soft);
}
.td-cr-slot-weight {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--fg-faint);
}
.td-cr-input { width: 100%; }

/* Skill Points leaderboard + "still cached" sidebar. The sidebar is
   deliberately the quiet element here — smaller type, muted color, no
   card border — so the leaderboard table stays the visual focus and the
   recent list reads as a minor convenience next to it, not a competing
   feature. */
.td-leaderboard { margin-top: 20px; }
.td-lb-layout {
  display: grid;
  grid-template-columns: 1fr 220px;
  gap: 20px;
  align-items: start;
}
@media (max-width: 720px) {
  .td-lb-layout { grid-template-columns: 1fr; }
}
.td-lb-recent {
  padding-top: 4px;
}
.td-lb-recent-title {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-bottom: 8px;
}
.td-lb-recent-empty {
  font-size: 12px;
  color: var(--fg-faint);
}
.td-lb-recent-item {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  font-size: 12px;
}
.td-lb-recent-item:last-child { border-bottom: none; }
.td-lb-recent-item:hover, .td-lb-recent-item:focus-visible { color: var(--fg-soft); outline: none; }
.td-lb-recent-name {
  color: var(--fg-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.td-lb-recent-sp {
  color: var(--fg-faint);
  font-family: var(--mono);
  white-space: nowrap;
}
