/* === VoxRoster redesign (2026-07) — token layer + base defaults + shared shell.
   Loaded AFTER roster.css/w40k.css so the reworked --rb-* + new tokens win via the cascade;
   --src-* and all component rules in roster.css/w40k.css are preserved. === */

/* ---- tokens/colors.css ---- */
/* ============================================================
   Fight Tracker / VoxRoster — Colors
   Grimdark-Imperial palette. NIGHT (default) is the product's charcoal +
   parchment-gold world; DAY is a fully-crafted aged-parchment theme —
   warm paper, ink, ochre-gold, wax-red — not a naive inversion.
   Semantic aliases at the bottom are what components reference.
   ============================================================ */

:root {
    /* --- Base surfaces (warm near-black charcoal) --- */
    --rb-bg:               #121110;
    --rb-bg-panel:         #1c1a17;
    --rb-bg-panel-raised:  #242019;
    --rb-bg-deep:          #0d0b07;

    /* --- Borders --- */
    --rb-border:           #3a332a;
    --rb-border-strong:    #5a4d38;

    /* --- Text --- */
    --rb-text:             #e8e2d4;
    --rb-text-muted:       #a89f8c;
    --rb-text-faint:       #9a8d64; /* was #8a7c52: 4.5:1 borderline at the small sizes it's used at; now 5.7:1 */

    /* --- Parchment-gold (primary accent) --- */
    --rb-gold:             #c9a227;
    --rb-gold-bright:      #e8c34a;

    /* --- Blood-red (danger / destroy) --- */
    --rb-red:              #8b2020;
    --rb-red-bright:       #c73838;

    /* --- Support accents (Tracker board) --- */
    --rb-green:            #3f7a4f;
    --rb-green-bright:     #7fc47f;
    --rb-steel:            #7aa2c9;
    --rb-steel-bright:     #a8c8e6;
    --rb-danger-soft:      #d16b6b;
    --rb-gold-play:        #d4af4e;
    --rb-gold-play-bright: #f2c869;

    /* --- Tints (theme-aware translucent fills) --- */
    --tint-gold-06:        rgba(201, 162, 39, 0.06);
    --tint-gold-12:        rgba(201, 162, 39, 0.12);
    --tint-gold-25:        rgba(201, 162, 39, 0.25);
    --tint-red-10:         rgba(199, 56, 56, 0.10);
    --tint-ink-05:         rgba(255, 255, 255, 0.05);   /* zebra / hairline overlay */
    --tint-ink-08:         rgba(255, 255, 255, 0.08);

    /* ============================================================
       Semantic aliases — reference THESE in components.
       ============================================================ */
    --surface-page:        var(--rb-bg);
    --surface-panel:       var(--rb-bg-panel);
    --surface-raised:      var(--rb-bg-panel-raised);
    --surface-board:       var(--rb-bg-deep);

    --border-hairline:     var(--rb-border);
    --border-control:      var(--rb-border-strong);

    --text-body:           var(--rb-text);
    --text-muted:          var(--rb-text-muted);
    --text-faint:          var(--rb-text-faint);
    --text-heading:        var(--rb-gold-bright);

    --accent:              var(--rb-gold);
    --accent-strong:       var(--rb-gold-bright);
    --accent-react:        var(--rb-steel);
    --accent-react-strong: var(--rb-steel-bright);

    --intent-danger:       var(--rb-red);
    --intent-danger-strong:var(--rb-red-bright);
    --intent-success:      var(--rb-green);
    --intent-success-strong:var(--rb-green-bright);

    --on-accent:           #1a1710;
    --link:                var(--rb-gold);
    --link-hover:          var(--rb-gold-bright);

    /* Active-side row fills (Tracker) */
    --fill-attacker:       #3a2f10;
    --fill-defender:       #12283a;
}

/* ============================================================
   DAY — aged parchment. Crafted: warm layered paper, soft ink,
   ochre-gold that actually contrasts, wax-seal red. Shadows are
   allowed to carry depth here (see effects.css).
   ============================================================ */
:root[data-theme="light"] {
    --rb-bg:               #e9dfc6;   /* table parchment */
    --rb-bg-panel:         #f3ecda;   /* sheet */
    --rb-bg-panel-raised:  #faf5e8;   /* raised sheet / header */
    --rb-bg-deep:          #ddd0af;   /* board backdrop */

    --rb-border:           #d4c49c;
    --rb-border-strong:    #a68c58;

    --rb-text:             #2b2416;
    --rb-text-muted:       #6d5e42;
    --rb-text-faint:       #6f6140; /* was #94815a: 3.1:1 on parchment (AA fail); now 5.0:1 */

    --rb-gold:             #8f6e0f;   /* ochre — legible on paper */
    --rb-gold-bright:      #6e540a;   /* hover darkens on light */

    --rb-red:              #9a2424;
    --rb-red-bright:       #b02a2a;

    --rb-green:            #2e6f47;
    --rb-green-bright:     #256b40;
    --rb-steel:            #3d648e;
    --rb-steel-bright:     #2f527a;
    --rb-danger-soft:      #a33d3d;
    --rb-gold-play:        #8f6e0f;
    --rb-gold-play-bright: #6e540a;

    --tint-gold-06:        rgba(143, 110, 15, 0.07);
    --tint-gold-12:        rgba(143, 110, 15, 0.13);
    --tint-gold-25:        rgba(143, 110, 15, 0.26);
    --tint-red-10:         rgba(154, 36, 36, 0.10);
    --tint-ink-05:         rgba(64, 50, 22, 0.06);
    --tint-ink-08:         rgba(64, 50, 22, 0.10);

    --on-accent:           #faf5e8;

    --fill-attacker:       #efe2b6;
    --fill-defender:       #dbe4ea;
}

/* ---- tokens/typography.css ---- */
/* ============================================================
   Fight Tracker — Typography
   The product ships NO webfonts: it leans on the OS UI sans for prose
   and the OS monospace for all numbers (points, wounds, dice, stats).
   The mono / tabular-nums pairing is the signature — every game number
   is monospaced and tabular so columns of stats line up.
   ============================================================ */

:root {
    /* --- Families (verbatim from source) --- */
    --font-sans: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, Menlo, Consolas, "Liberation Mono", monospace;

    /* --- Sizes (rem; base 16px) --- */
    --fs-hero:    2.4rem;   /* codex hub hero */
    --fs-title:   1.6rem;   /* page titles, roster name */
    --fs-h2:      1.3rem;   /* datasheet / duel unit name */
    --fs-h3:      1.15rem;  /* panel-adjacent headings */
    --fs-lg:      1.05rem;  /* card links */
    --fs-body:    0.9rem;   /* default body */
    --fs-sm:      0.85rem;  /* secondary body */
    --fs-xs:      0.8rem;   /* labels */
    --fs-2xs:     0.72rem;  /* eyebrow / stat-key labels */
    --fs-3xs:     0.62rem;  /* keyword chips, pins */

    /* --- Weights --- */
    --fw-normal:  400;  /* @kind font */
    --fw-medium:  600;  /* @kind font */
    --fw-bold:    700;  /* @kind font */
    --fw-black:   800;  /* @kind font */

    /* --- Line heights --- */
    --lh-tight:   1.2;   /* @kind font */
    --lh-snug:    1.45;  /* @kind font */
    --lh-body:    1.6;   /* @kind font */

    /* --- Letter-spacing (uppercase labels are tracked out) --- */
    --ls-tight:   0.01em;
    --ls-label:   0.05em;   /* uppercase section labels */
    --ls-eyebrow: 0.08em;   /* eyebrows / stat keys */
    --ls-wide:    0.14em;   /* .lbl in the Tracker */

    /* --- Semantic roles --- */
    --text-numeric: var(--font-mono);   /* always pair with tabular-nums */
}

/* Uppercase-label convention used across the whole product: gold, small,
   tracked-out, bold. Applied as a utility so cards + kits can reuse it. */
.ft-label {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: var(--ls-eyebrow);
    color: var(--text-muted);
    font-weight: var(--fw-bold);
}

/* Every game number: monospaced + tabular so stat columns align. */
.ft-num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* ---- tokens/spacing.css ---- */
/* ============================================================
   Fight Tracker — Spacing
   The source uses a rem scale (--rb-space-1..8). Reproduced verbatim,
   with semantic aliases layered on top.
   ============================================================ */

:root {
    --rb-space-1: 0.25rem;   /*  4px */
    --rb-space-2: 0.5rem;    /*  8px */
    --rb-space-3: 0.75rem;   /* 12px */
    --rb-space-4: 1rem;      /* 16px */
    --rb-space-6: 1.5rem;    /* 24px */
    --rb-space-8: 2rem;      /* 32px */

    /* Semantic */
    --gap-inline:  var(--rb-space-2);   /* chips, buttons in a row */
    --gap-stack:   var(--rb-space-3);   /* stacked fields */
    --gap-section: var(--rb-space-6);   /* between panels */
    --pad-control: var(--rb-space-2) var(--rb-space-4);   /* button padding */
    --pad-panel:   var(--rb-space-4);
    --pad-panel-head: var(--rb-space-3) var(--rb-space-4);

    /* Layout rails (from the builder's 3-column grid) */
    --rail-left:  20rem;
    --rail-right: 18rem;
    --content-max: 92rem;
}

/* ---- tokens/effects.css ---- */
/* ============================================================
   Fight Tracker / VoxRoster — Effects
   Radii, borders, elevation, glow, and the motion system.
   NIGHT: thin borders carry structure; glow is the reward for interaction.
   DAY: soft warm paper shadows carry depth (borders alone look flat on paper).
   ============================================================ */

:root {
    /* --- Radius --- */
    --rb-radius:     6px;
    --radius-sm:     4px;
    --radius-xs:     3px;
    --radius-lg:     10px;   /* hero cards / doorways */
    --radius-pill:   999px;

    /* --- Border widths --- */
    --bw-hair:  1px;
    --bw-emph:  2px;
    --bw-side:  3px;

    /* --- Elevation (theme-aware) --- */
    --shadow-card:   none;                                        /* @kind other */
    --shadow-lift:   0 6px 18px rgba(0, 0, 0, 0.45);              /* @kind other */
    --shadow-pop:    0 8px 26px rgba(0, 0, 0, 0.6), 0 0 0 1px #000 inset;  /* @kind other */
    --shadow-drawer: 0 6px 20px rgba(0, 0, 0, 0.5);               /* @kind other */

    /* --- Glow (interaction reward; night only — day zeroes it) --- */
    --glow-gold:     0 0 0 1px rgba(201, 162, 39, 0.35), 0 0 20px rgba(201, 162, 39, 0.18);  /* @kind other */
    --glow-gold-soft:0 0 14px rgba(201, 162, 39, 0.12);           /* @kind other */
    --glow-red:      0 0 0 1px rgba(199, 56, 56, 0.4), 0 0 18px rgba(199, 56, 56, 0.2);      /* @kind other */
    --glow-steel:    0 0 0 1px rgba(122, 162, 201, 0.35), 0 0 18px rgba(122, 162, 201, 0.16); /* @kind other */

    /* --- Focus ring --- */
    --focus-ring: var(--rb-gold);

    /* --- Page wash --- */
    --page-wash:
        radial-gradient(1100px 500px at 18% -8%, rgba(201, 162, 39, 0.055) 0%, transparent 60%),
        radial-gradient(900px 480px at 85% 108%, rgba(139, 32, 32, 0.05) 0%, transparent 55%); /* @kind other */

    /* --- Inset "active" bars --- */
    --inset-gold:  inset 3px 0 0 var(--rb-gold);   /* @kind other */
    --inset-steel: inset 3px 0 0 var(--rb-steel);  /* @kind other */

    /* --- Motion: fast + mechanical base, one springy ease for arrivals --- */
    --dur-fast:   0.08s;   /* @kind other */
    --dur-base:   0.14s;   /* @kind other */
    --dur-slow:   0.22s;   /* @kind other */
    --dur-enter:  0.36s;   /* @kind other */
    --ease:       ease;                                /* @kind other */
    --ease-out:   cubic-bezier(0.2, 0.7, 0.2, 1);      /* @kind other */
    --ease-spring:cubic-bezier(0.34, 1.4, 0.44, 1);    /* @kind other */
    --press-scale: 0.95;   /* @kind other */
}

:root[data-theme="light"] {
    /* Paper depth: warm, soft, layered. */
    --shadow-card:   0 1px 2px rgba(92, 72, 32, 0.10), 0 4px 14px rgba(92, 72, 32, 0.08);   /* @kind other */
    --shadow-lift:   0 2px 6px rgba(92, 72, 32, 0.14), 0 12px 28px rgba(92, 72, 32, 0.14);  /* @kind other */
    --shadow-pop:    0 10px 30px rgba(70, 54, 22, 0.28), 0 0 0 1px rgba(140, 115, 60, 0.25);/* @kind other */
    --shadow-drawer: 0 8px 24px rgba(92, 72, 32, 0.2);   /* @kind other */

    /* No neon on paper — glow becomes a crisp ochre outline. */
    --glow-gold:     0 0 0 2px rgba(143, 110, 15, 0.3);  /* @kind other */
    --glow-gold-soft:none;                                /* @kind other */
    --glow-red:      0 0 0 2px rgba(154, 36, 36, 0.3);   /* @kind other */
    --glow-steel:    0 0 0 2px rgba(61, 100, 142, 0.3);  /* @kind other */

    --page-wash:
        radial-gradient(1100px 520px at 18% -8%, rgba(143, 110, 15, 0.07) 0%, transparent 60%),
        radial-gradient(900px 480px at 85% 108%, rgba(154, 36, 36, 0.05) 0%, transparent 55%),
        linear-gradient(rgba(255, 251, 240, 0.55), rgba(233, 223, 198, 0)); /* @kind other */
}

/* ---- base.css (element defaults) ---- */
/* ============================================================
   Fight Tracker / VoxRoster — Base
   Element defaults. Theme-aware, motion-aware.
   ============================================================ */

* { box-sizing: border-box; }
html, body { margin: 0; }
html { color-scheme: dark; scrollbar-color: var(--rb-border-strong) transparent; }
:root[data-theme="light"] { color-scheme: light; }

body {
    background: var(--surface-page);
    background-image: var(--page-wash);
    background-attachment: fixed;
    color: var(--text-body);
    font-family: var(--font-sans);
    line-height: var(--lh-body);
    -webkit-font-smoothing: antialiased;
    transition: background-color var(--dur-slow) var(--ease), color var(--dur-slow) var(--ease);
}

a       { color: var(--link); text-decoration: none; transition: color var(--dur-base) var(--ease); }
a:hover { color: var(--link-hover); text-decoration: underline; }
a:visited { color: var(--link); }

select,
input[type="text"],
input[type="number"],
input[type="search"] {
    background: var(--surface-page);
    color: var(--text-body);
    border: var(--bw-hair) solid var(--border-control);
    border-radius: var(--rb-radius);
    padding: var(--rb-space-2) var(--rb-space-3);
    font-size: var(--fs-body);
    font-family: inherit;
    transition: border-color var(--dur-base) var(--ease), box-shadow var(--dur-base) var(--ease);
}
select:focus, input:focus { outline: none; border-color: var(--focus-ring); box-shadow: var(--glow-gold); }

/* Keyboard focus — visible gold ring everywhere. */
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--radius-xs); }

h1, h2, h3 { color: var(--text-heading); margin: 0 0 var(--rb-space-3); text-wrap: balance; }
p { text-wrap: pretty; }

::selection { background: var(--tint-gold-25); color: var(--rb-text); }

/* Thin themed scrollbars in overflow panes. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border-control); border-radius: 5px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---- shell.css (vx-* shared classes) ---- */
/* ============================================================
   VoxRoster — Shell & Interaction system (shared by all UI kits)
   Topnav, footer, hero, doorways, cards, tabs, ripple, reveals.
   Link AFTER styles.css.
   ============================================================ */

/* ---------- App shell ---------- */
.vx-shell { max-width: 92rem; margin: 0 auto; padding: 1.25rem clamp(1rem, 3vw, 2rem) 2.5rem; }
.vx-topnav { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  border-bottom: 2px solid var(--border-control); padding: 0.4rem 0 0.9rem; margin-bottom: 1.4rem; }
.vx-brand { display: inline-flex; align-items: center; gap: 0.55rem; color: var(--text-heading);
  font-weight: 800; font-size: 1.12rem; letter-spacing: 0.09em; text-decoration: none; }
.vx-brand:hover { text-decoration: none; color: var(--text-heading); }
.vx-brand img { width: 30px; height: 30px; transition: transform var(--dur-slow) var(--ease-spring); }
.vx-brand:hover img { transform: rotate(45deg) scale(1.08); }
.vx-beta { font-size: 0.58rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-faint); border: 1px solid var(--border-hairline); border-radius: var(--radius-pill);
  padding: 0.08rem 0.45rem; margin-left: 0.1rem; align-self: flex-start; }
.vx-navlinks { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; margin-left: auto; }
.vx-navlinks > a { position: relative; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em;
  font-size: 0.8rem; font-weight: 700; padding: 0.5rem 0.75rem; border-radius: var(--rb-radius); cursor: pointer; }
.vx-navlinks > a:hover { color: var(--text-body); background: var(--tint-ink-05); text-decoration: none; }
.vx-navlinks > a::after { content: ""; position: absolute; left: 0.75rem; right: 0.75rem; bottom: 0.22rem;
  height: 2px; background: var(--accent); border-radius: 1px; transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-slow) var(--ease-out); }
.vx-navlinks > a:hover::after, .vx-navlinks > a.on::after { transform: scaleX(1); }
.vx-navlinks > a.on { color: var(--text-heading); }
.vx-navsearch input { min-width: 9rem; padding: 0.4rem 0.7rem; font-size: 0.85rem; border-radius: var(--radius-pill); }
.vx-footer { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem;
  margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--border-hairline);
  color: var(--text-muted); font-size: 0.8rem; }

/* Animated theme toggle */
.vx-theme { position: relative; width: 2.1rem; height: 2.1rem; border-radius: 50%; cursor: pointer;
  background: transparent; border: 1px solid var(--border-control); color: var(--accent);
  font-size: 1rem; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
  transition: transform var(--dur-slow) var(--ease-spring), box-shadow var(--dur-base) var(--ease),
    border-color var(--dur-base) var(--ease); overflow: hidden; }
.vx-theme:hover { border-color: var(--accent); box-shadow: var(--glow-gold-soft); }
.vx-theme:active { transform: scale(0.88); }
.vx-theme .g { display: inline-block; transition: transform var(--dur-enter) var(--ease-spring), opacity var(--dur-slow) var(--ease); }
.vx-theme.flip .g { transform: rotate(360deg); }

/* ---------- Press ripple (attach class vx-ripple; JS in vx.js) ---------- */
.vx-ripple { position: relative; overflow: hidden; }
.vx-ripple .vx-rip { position: absolute; border-radius: 50%; pointer-events: none;
  background: currentColor; opacity: 0.22; transform: scale(0);
  animation: vx-rip var(--dur-enter) var(--ease-out) forwards; }
@keyframes vx-rip { to { transform: scale(2.6); opacity: 0; } }

/* ---------- Entrance reveals ---------- */
@keyframes vx-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes vx-fade { from { opacity: 0; } to { opacity: 1; } }
.vx-rise { animation: vx-rise var(--dur-enter) var(--ease-out) both; }
.vx-rise-1 { animation-delay: 0.05s; } .vx-rise-2 { animation-delay: 0.12s; }
.vx-rise-3 { animation-delay: 0.19s; } .vx-rise-4 { animation-delay: 0.26s; }

/* ---------- Hero (index) ---------- */
.vx-hero { position: relative; text-align: center; padding: clamp(2.2rem, 7vw, 4.5rem) 1rem clamp(1.6rem, 4vw, 2.8rem);
  isolation: isolate; }
.vx-hero::before { content: ""; position: absolute; inset: -1.4rem 0 0; z-index: -1;
  background: radial-gradient(620px 260px at 50% 0%, var(--tint-gold-12), transparent 70%); pointer-events: none; }
.vx-hero-mark { width: clamp(56px, 9vw, 84px); height: auto; margin-bottom: 1rem;
  filter: drop-shadow(0 6px 22px rgba(201, 162, 39, 0.25)); animation: vx-rise var(--dur-enter) var(--ease-out) both; }
.vx-hero-title { font-size: clamp(2.1rem, 6vw, 3.4rem); letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 800; margin: 0 0 0.5rem; color: var(--text-heading);
  background: linear-gradient(105deg, var(--rb-gold-bright), var(--rb-gold) 45%, var(--rb-gold-bright) 90%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
:root[data-theme="light"] .vx-hero-title { background: none; -webkit-text-fill-color: currentColor; color: var(--rb-gold-bright); }
.vx-hero-sub { color: var(--text-muted); font-size: clamp(0.95rem, 2vw, 1.1rem); margin: 0; }
.vx-hero-rule { width: 120px; margin: 1.1rem auto 0; height: 2px; border: 0;
  background: linear-gradient(90deg, transparent, var(--accent), transparent); }

/* ---------- Doorways (index) ---------- */
.vx-doorways { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin-top: 1.6rem; }
.vx-doorway { position: relative; display: flex; flex-direction: column; gap: 0.5rem; padding: 1.5rem 1.4rem 1.4rem;
  background: var(--surface-panel); border: 1px solid var(--border-hairline); border-radius: var(--radius-lg);
  color: var(--text-body); overflow: hidden; cursor: pointer; box-shadow: var(--shadow-card);
  transition: transform var(--dur-slow) var(--ease-out), border-color var(--dur-slow) var(--ease),
    box-shadow var(--dur-slow) var(--ease); }
.vx-doorway::before { content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  transform: scaleX(0); transition: transform var(--dur-enter) var(--ease-out); }
.vx-doorway::after { content: ""; position: absolute; top: 0; bottom: 0; left: -70%; width: 45%;
  background: linear-gradient(105deg, transparent, var(--tint-ink-08), transparent);
  transform: skewX(-18deg); transition: left 0.55s var(--ease-out); pointer-events: none; }
.vx-doorway:hover { transform: translateY(-4px); border-color: var(--border-control);
  box-shadow: var(--shadow-lift), var(--glow-gold-soft); text-decoration: none; color: var(--text-body); }
.vx-doorway:hover::before { transform: scaleX(1); }
.vx-doorway:hover::after { left: 130%; }
.vx-doorway:active { transform: translateY(-1px) scale(0.99); }
.vx-doorway-icon { font-size: 1.7rem; color: var(--accent); line-height: 1;
  transition: transform var(--dur-slow) var(--ease-spring); }
.vx-doorway:hover .vx-doorway-icon { transform: translateY(-2px) scale(1.12); }
.vx-doorway-title { font-size: 1.12rem; font-weight: 800; color: var(--text-heading); }
.vx-doorway-desc { font-size: 0.86rem; color: var(--text-muted); line-height: 1.5; }
.vx-doorway-cta { margin-top: auto; padding-top: 0.6rem; font-size: 0.74rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--accent);
  display: inline-flex; align-items: center; gap: 0.3rem; }
.vx-doorway-cta::after { content: "»"; transition: transform var(--dur-slow) var(--ease-spring); }
.vx-doorway:hover .vx-doorway-cta::after { transform: translateX(4px); }

/* ---------- Tabs (faction pages) ---------- */
.vx-tabs { display: flex; gap: 0.25rem; flex-wrap: wrap; border-bottom: 1px solid var(--border-hairline); margin-bottom: 1.2rem; }
.vx-tab { position: relative; padding: 0.55rem 0.9rem; font-size: 0.85rem; font-weight: 700; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer; background: none; border: none; font-family: inherit;
  border-radius: var(--rb-radius) var(--rb-radius) 0 0; transition: color var(--dur-base) var(--ease), background var(--dur-base) var(--ease); }
.vx-tab:hover { color: var(--text-body); background: var(--tint-ink-05); }
.vx-tab::after { content: ""; position: absolute; left: 0.4rem; right: 0.4rem; bottom: -1px; height: 2px;
  background: var(--accent); transform: scaleX(0); transition: transform var(--dur-slow) var(--ease-out); }
.vx-tab.on { color: var(--text-heading); }
.vx-tab.on::after { transform: scaleX(1); }
.vx-tab-count { font-size: 0.68rem; color: var(--text-faint); margin-left: 0.35rem;
  border: 1px solid var(--border-hairline); border-radius: var(--radius-pill); padding: 0 0.4rem; }

/* ---------- Card grid (datasheet cards) ---------- */
.vx-cardgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.6rem; }
.vx-gridcard { position: relative; display: block; padding: 0.8rem 0.95rem; background: var(--surface-panel);
  border: 1px solid var(--border-hairline); border-radius: var(--rb-radius); color: var(--text-body);
  font-size: 0.92rem; font-weight: 600; box-shadow: var(--shadow-card); overflow: hidden;
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease),
    box-shadow var(--dur-base) var(--ease), background var(--dur-base) var(--ease); }
.vx-gridcard:hover { transform: translateY(-2px); border-color: var(--accent); background: var(--surface-raised);
  box-shadow: var(--shadow-lift), var(--glow-gold-soft); text-decoration: none; color: var(--text-heading); }
.vx-gridcard:active { transform: none; }

/* ---------- Panels with lift-on-hover option ---------- */
.vx-lift { transition: transform var(--dur-slow) var(--ease-out), box-shadow var(--dur-slow) var(--ease),
  border-color var(--dur-slow) var(--ease); }
.vx-lift:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }

/* ---------- Modal / drawer sheets ---------- */
.vx-sheet { background: var(--surface-panel); border: 1px solid var(--border-control);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-pop); }
@keyframes vx-sheet-in { from { opacity: 0; transform: translateY(16px) scale(0.98); } to { opacity: 1; transform: none; } }
.vx-sheet-in { animation: vx-sheet-in var(--dur-enter) var(--ease-spring) both; }

/* ---------- Attention: object enter / exit / deny ---------- */
@keyframes vx-pop-in { 0% { opacity: 0; transform: scale(0.55); } 65% { transform: scale(1.07); } 100% { opacity: 1; transform: none; } }
.vx-pop-in { animation: vx-pop-in 0.32s var(--ease-spring) both; }
/* Same spring-pop, but for an element that also needs a fixed rotate() held throughout -- animating
   `transform` overrides the WHOLE property, so a separately-declared static rotate()/translate()
   gets silently dropped for the animation's duration (and after, under `both`'s held end frame).
   Used by the datasheet checkbox tick, which must stay rotated 45deg the entire time it pops in. */
@keyframes vx-pop-in-rot45 { 0% { opacity: 0; transform: scale(0.55) rotate(45deg); }
  65% { transform: scale(1.07) rotate(45deg); } 100% { opacity: 1; transform: rotate(45deg); } }
@keyframes vx-pop-out { to { opacity: 0; transform: scale(0.55); } }
.vx-pop-out { animation: vx-pop-out 0.22s var(--ease) both; }
@keyframes vx-shake { 20%, 60% { transform: translateX(-3px); } 40%, 80% { transform: translateX(3px); } }
.vx-shake { display: inline-block; animation: vx-shake 0.3s var(--ease); }
@keyframes vx-attn { from { box-shadow: 0 0 0 0 var(--tint-gold-25); } to { box-shadow: 0 0 0 12px transparent; } }
.vx-attn { animation: vx-attn 0.5s var(--ease-out); }
/* Rest state for options removed by a cap or binary choice */
.vx-dim { opacity: 0.38; filter: grayscale(0.6); transition: opacity var(--dur-slow) var(--ease), filter var(--dur-slow) var(--ease); }

/* ---------- Responsive shell ---------- */
@media (max-width: 720px) {
  .vx-topnav { gap: 0.5rem; }
  .vx-navsearch { order: 10; width: 100%; }
  .vx-navsearch input { width: 100%; }
  .vx-shell { padding-bottom: 4.5rem; }
}


/* ---- integration: apply the design page-wash to the app body classes (they set their own
   background-image which would otherwise win over base.css's element-level rule). ---- */
.rb-body, .w40k-body { background-image: var(--page-wash); background-attachment: fixed; }

/* ---- integration bridges: honor the app's existing active-state classes on the vx shell ---- */
.vx-navlinks > a.w40k-nav-active { color: var(--text-heading); }
.vx-navlinks > a.w40k-nav-active::after { transform: scaleX(1); }
.vx-tab.w40k-tab-active { color: var(--text-heading); }
.vx-tab.w40k-tab-active::after { transform: scaleX(1); }

/* ============================================================
   Custom radio / checkbox controls (datasheet + builder wargear)
   Native input is hidden; the control is drawn. Radio dot springs
   in; checkbox fills gold with an SVG check drawn by dashoffset.
   ============================================================ */
/* Block radio list (choose-one weapon/crest slots) */
.rb2-radio { position: relative; display: flex; gap: .5rem; align-items: flex-start;
  padding: .34rem .55rem .34rem 1.95rem; border-radius: 6px; cursor: pointer; opacity: .55;
  transition: opacity var(--dur-base) var(--ease), background var(--dur-fast) var(--ease); }
.rb2-radio:hover { background: var(--tint-gold-06); opacity: 1; }
.rb2-radio input[type=radio] { position: absolute; opacity: 0; width: 0; height: 0; }
.rb2-radio::before { content: ''; position: absolute; left: .6rem; top: .5rem; width: 15px; height: 15px;
  box-sizing: border-box; border-radius: 50%; border: 2px solid var(--border-control);
  transition: border-color var(--dur-base) var(--ease); }
.rb2-radio::after { content: ''; position: absolute; left: calc(.6rem + 4px); top: calc(.5rem + 4px);
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent); transform: scale(0);
  transition: transform var(--dur-base) var(--ease-spring); }
.rb2-radio.rb2-radio-on { opacity: 1; }
.rb2-radio.rb2-radio-on::before { border-color: var(--accent); }
.rb2-radio.rb2-radio-on::after { transform: scale(1); }
.rb2-radio.rb2-radio-on .rb2-radio-name { color: var(--accent); font-weight: 600; }

/* Inline radio pills (compact choose-one on a model's option line) */
.rb2-iopt { position: relative; display: inline-flex; align-items: center; gap: .3rem;
  padding: .1rem .55rem; border: 1px solid var(--border-control); border-radius: var(--radius-pill, 999px);
  cursor: pointer; font-size: .82rem; opacity: .7;
  transition: opacity var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
.rb2-iopt:hover { border-color: var(--accent); opacity: 1; }
.rb2-iopt input[type=radio] { position: absolute; opacity: 0; width: 0; height: 0; }
.rb2-iopt.rb2-iopt-on { opacity: 1; border-color: var(--accent); background: var(--tint-gold-12);
  color: var(--accent); font-weight: 600; }

/* Drawn checkbox: hidden native + gold-filling box with an SVG tick */
.rb2-check, .rb2-wargear-grp label:has(> input[type=checkbox]) { position: relative; display: inline-flex;
  align-items: center; gap: .45rem; padding-left: 1.5rem; cursor: pointer; opacity: .78;
  transition: opacity var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.rb2-check input[type=checkbox], .rb2-wargear-grp label > input[type=checkbox] { position: absolute; opacity: 0; width: 0; height: 0; }
.rb2-check::before, .rb2-wargear-grp label:has(> input[type=checkbox])::before { content: ''; position: absolute;
  left: 0; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; box-sizing: border-box;
  border-radius: 3px; border: 2px solid var(--border-control); background: transparent;
  transition: background var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease); }
.rb2-check:has(input:checked), .rb2-wargear-grp label:has(> input[type=checkbox]:checked) { opacity: 1; color: var(--accent); font-weight: 600; }
.rb2-check:has(input:checked)::before, .rb2-wargear-grp label:has(> input[type=checkbox]:checked)::before { background: var(--accent); border-color: var(--accent); }
.rb2-check::after, .rb2-wargear-grp label:has(> input[type=checkbox])::after { content: ''; position: absolute;
  left: 4px; top: 50%; width: 7px; height: 4px; border-left: 2px solid var(--surface-page);
  border-bottom: 2px solid var(--surface-page); transform: translateY(-70%) rotate(-45deg) scale(0);
  transition: transform var(--dur-base) var(--ease-spring); }
.rb2-check:has(input:checked)::after, .rb2-wargear-grp label:has(> input[type=checkbox]:checked)::after { transform: translateY(-70%) rotate(-45deg) scale(1); }

/* An option ruled out by a reached cap / binary choice rests grey and refuses the click */
.rb2-iopt.vx-dim, .rb2-radio.vx-dim { pointer-events: none; }

/* Datasheet wargear pickers: bare <input> in a table cell -> draw directly on the input.
   Radio = ring + springing dot; checkbox = gold-filling box with a cut-out tick. */
.w40k-wgpick { appearance: none; -webkit-appearance: none; position: relative; box-sizing: border-box;
  width: 16px; height: 16px; margin: 0 .35rem 0 0; vertical-align: middle; cursor: pointer;
  border: 2px solid var(--rb-border-strong, var(--border-control)); background: transparent;
  transition: border-color var(--dur-fast, .1s) var(--ease, ease), background var(--dur-fast, .1s) var(--ease, ease); }
.w40k-wgpick[type=radio] { border-radius: 50%; }
.w40k-wgpick[type=checkbox] { border-radius: 4px; }
.w40k-wgpick:hover { border-color: var(--rb-gold, var(--accent)); }
.w40k-wgpick:focus-visible { outline: 2px solid var(--rb-gold, var(--accent)); outline-offset: 2px; }
.w40k-wgpick:checked { border-color: var(--rb-gold, var(--accent)); }
.w40k-wgpick[type=radio]:checked::after { content: ''; position: absolute; left: 4px; top: 4px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--rb-gold, var(--accent));
  animation: vx-pop-in .3s var(--ease-spring) both; }
.w40k-wgpick[type=checkbox]:checked { background: var(--rb-gold, var(--accent)); }
.w40k-wgpick[type=checkbox]:checked::after { content: ''; position: absolute; left: 4px; top: 1px;
  width: 5px; height: 9px; border: solid var(--surface-page, #12100a); border-width: 0 2px 2px 0;
  animation: vx-pop-in-rot45 .3s var(--ease-spring) both; }
/* the whole option row dims when its weapon is not currently equipped */
.w40k-wargear-table tr.w40k-w-off .w40k-wg-name { opacity: .62; }

/* ---------- Builder "Filter units" panel: Legends + Crossover + Saved ----------
   A labelled, roomy panel (was a cramped single wrapping row). Its own tint + hairline set it
   apart from the search box above and the picker below; the chips sit in a grid so all four have
   air and stay on tidy rows at the drawer's width. */
.rb2-browser-filters { margin: .5rem var(--rb-space-4, 1rem) .35rem; padding: .55rem .65rem;
  background: var(--tint-ink-05); border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md, 8px); }
.rb2-filters-label { display: block; font-size: .62rem; text-transform: uppercase; letter-spacing: .09em;
  font-weight: 700; color: var(--text-muted); margin-bottom: .45rem; }
.rb2-filter-chips { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem; }
.rb2-filter-chip { display: inline-flex; align-items: center; gap: .4rem; font-size: .74rem;
  text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); cursor: pointer;
  padding: .28rem .55rem; border: 1px solid var(--border-control); border-radius: var(--radius-pill, 999px);
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
.rb2-filter-chip:hover { border-color: var(--accent); color: var(--text-body); }
.rb2-filter-chip:has(input:checked) { border-color: var(--accent); color: var(--accent); }
.rb2-filter-chip input { accent-color: var(--accent); margin: 0; }
.rb2-legends-tag, .rb2-saved-tag { font-size: .58rem; text-transform: uppercase; letter-spacing: .05em;
  font-weight: 700; color: var(--text-faint); border: 1px solid var(--border-hairline); border-radius: 3px;
  padding: 0 .3rem; vertical-align: middle; }
.rb2-saved-tag { color: var(--accent); border-color: var(--accent); }
/* Browse facets hide their rows only while BROWSING: a typed name means "find this unit", so
   during a search (.rb2-facets-off, set by rb2Search) the facets yield and an off-by-default
   Legends/Allied toggle can't make an exact name match look like a missing datasheet. Rows keep
   their [Legends] tag; clearing the box restores the facet view. Same rule as the index nav. */
.rb2-hide-legends:not(.rb2-facets-off) .rb2-row-legends { display: none !important; }
.rb2-hide-allies:not(.rb2-facets-off) .rb2-browser-ally { display: none !important; }
.rb2-hide-generic:not(.rb2-facets-off) .rb2-row-generic { display: none !important; }
.rb2-hide-custom:not(.rb2-facets-off) .rb2-row-custom { display: none !important; }
/* 'Saved' = keep only datasheets already in your army (data-saved set by the view). */
.rb2-only-saved .rb2-browser-row:not([data-saved]) { display: none !important; }
/* While a name search filters your saved army, drop the list chrome so the matches read cleanly. */
.rb2-army-filtered .rb2-summary,
.rb2-army-filtered .rb2-legal,
.rb2-army-filtered .rb2-warnings,
.rb2-army-filtered .rb2-empty { display: none; }

/* ============================================================
   Builder (roster_detail) — budget track, unit-row motion, remove-×.
   Targets the app's rb2-* classes; loaded after roster.css so these win.
   ============================================================ */
.rb2-summary { box-shadow: var(--shadow-card); }
.rb2-pts { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.rb2-pts-track { flex-basis: 100%; height: 4px; border-radius: 2px; background: var(--tint-ink-05); overflow: hidden; margin-top: .5rem; }
.rb2-pts-fill { height: 100%; border-radius: 2px; max-width: 100%;
  background: linear-gradient(90deg, var(--rb-gold), var(--rb-gold-bright));
  transition: width var(--dur-enter) var(--ease-out), background var(--dur-base) var(--ease); }
.rb2-pts-fill.near { background: linear-gradient(90deg, var(--rb-amber, #c98a1f), var(--rb-amber-bright, #e0a53a)); }
.rb2-pts-fill.over { background: linear-gradient(90deg, var(--rb-red), var(--rb-red-bright)); }
.rb2-unit { box-shadow: var(--shadow-card);
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease), box-shadow var(--dur-base) var(--ease); }
.rb2-unit:hover { transform: translateX(3px); box-shadow: var(--shadow-lift); }
.rb2-unit.rb2-unit-selected { background: var(--tint-gold-12); }
.rb2-x button { transition: color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease); }
.rb2-x button:hover { color: var(--intent-danger-strong); transform: scale(1.25); }
.rb2-list, .rb2-centre { box-shadow: var(--shadow-card); }
.rb2-unit-pts { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.rb2-drag-handle { cursor: grab; color: var(--text-faint); font-size: .9rem; line-height: 1;
  padding: 0 .3rem; user-select: none; touch-action: none; }
.rb2-drag-handle:active { cursor: grabbing; }
.rb2-unit.rb2-dragging { opacity: .45; }

/* ============================================================
   Design pass (2026-07) — cohere the builder into the gold-steel
   terminal aesthetic. CSS-only, structure-preserving: no DOM
   moves, no renamed hooks, no overlays. Loaded last so these win.
   ============================================================ */

/* ---- ONE button box for every variant --------------------------------
   The base .rb-btn had `border:none` while .rb-btn-ghost has 1px, so a
   solid button rendered 2px shorter than a ghost one, and an <a> button
   (Export) sat at a different height than a <button> (Save). Give the base
   a 1px *transparent* border + a shared line-height and render every
   variant as the same inline-flex box, then re-assert the ghost/danger
   accents AFTER (equal specificity -> later wins). Result: Save, Export,
   +, and danger buttons are all the same height/padding/radius/font. ---- */
.rb-btn { display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  border: 1px solid transparent; line-height: 1.15; vertical-align: middle;
  text-decoration: none; white-space: nowrap;
  transition: background var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease),
    color var(--dur-base) var(--ease); }
.rb-btn:hover { text-decoration: none; }
.rb-btn-ghost { border-color: var(--border-control); }
.rb-btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.rb-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ---- Toolbar reads as a page header: hairline base + a little air ---- */
/* Command-bar chrome (height, sticky, gold rule) lives in roster.css -- vx only sets the
   action cluster's alignment. The old hairline + padding-bottom belonged to the two-row
   toolbar and would fight the bar's 2px gold rule. */
.rb2-name-actions { display: flex; gap: var(--rb-space-2); margin-left: auto;
  align-items: center; flex-wrap: wrap; }

/* ---- Roster name = chrome-less editable title; frames on hover/focus
   with a 1px border that's transparent at rest, so NO layout shift. ---- */
.rb2-namebar .rb2-name-input { background: transparent; border: 1px solid transparent;
  border-radius: var(--radius-sm); padding: .18rem .4rem;
  transition: border-color var(--dur-base) var(--ease), background var(--dur-base) var(--ease); }
.rb2-namebar .rb2-name-input:hover { border-color: var(--border-hairline); }
.rb2-namebar .rb2-name-input:focus { background: var(--surface-page); border-color: var(--accent);
  box-shadow: none; outline: none; }

/* ---- Points readouts (DP / Enh / units) as consistent quiet stat pills;
   red pill when a budget is blown. ---- */
.rb2-summary-stats { gap: var(--rb-space-2); flex-wrap: wrap; }
.rb2-stat { display: inline-flex; align-items: baseline; gap: .3rem;
  background: var(--tint-ink-05); border: 1px solid var(--border-hairline);
  border-radius: var(--radius-pill); padding: .12rem .6rem; font-size: var(--fs-xs); }
.rb2-stat b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.rb2-stat.rb2-over { border-color: var(--intent-danger-strong); background: var(--tint-red-10); }
.rb2-stat.rb2-over b { color: var(--intent-danger-strong); }

/* ---- Points-budget track: a touch taller + a hairline so it reads as a
   gauge, not a stray line. (fill colours already handled above.) ---- */
.rb2-pts-track { height: 6px; border: 1px solid var(--border-hairline); }

/* ---- Hairline under section heads for vertical rhythm ---- */
.rb2-section-head { border-bottom: 1px solid var(--border-hairline);
  padding-bottom: var(--rb-space-1); }

/* ---- A bit more breathing room between army-list unit rows ---- */
.rb2-list .rb2-unit { margin-bottom: var(--rb-space-2); }

/* ---- Roster-index New / Import starters get the same card depth as the
   roster cards. ---- */
.rbf-starter { box-shadow: var(--shadow-card); }

/* ============================================================
   Builder CENTRE -> adopt the INDEX datasheet's own header chrome
   (2026-07). The selected-unit fragment (_entry_state.html) reuses
   the index's .w40k-datasheet / .w40k-ds-header / .w40k-ds-section /
   .w40k-ds-pts markup. The header's points BANDS are the index's own
   size selector -- in the builder each band is wrapped in its own
   rb-ajax form (display:contents so the button stays the flex item)
   that POSTs entry_size, so choosing a size persists to the entry. */
.rb2-size-band { display: contents; }

/* Fold / Unfold toggle (compactable units): a quiet ghost pill next to the unit name. Folded =
   the combined index tables (leader inline); unfolded = per-model blocks. */
.rb2-fold-btn { margin-left: var(--rb-space-2); font: inherit; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--ls-label); font-weight: var(--fw-bold);
  color: var(--text-muted); background: transparent; border: 1px solid var(--border-control);
  border-radius: var(--radius-pill); padding: .1rem .55rem; cursor: pointer; white-space: nowrap;
  transition: border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease),
    background var(--dur-base) var(--ease); }
.rb2-fold-btn:hover { border-color: var(--accent); color: var(--accent); }
.rb2-fold-btn[aria-pressed="true"] { border-color: var(--accent); color: var(--accent);
  background: var(--tint-gold-12); }

/* ---- Public share control: the checkbox reveals a read-only, selectable
   link. .rb2-check draws the box + tick (defined above). ---- */
.rb2-public { display: inline-flex; align-items: center; gap: var(--rb-space-2); flex-wrap: wrap; }
.rb2-public-toggle { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); }
/* Public toggle button (sits next to Export): gold-filled when the roster is public, ghost when private. */
.rb2-public-btn.is-public,
.rb2-public-btn.is-public:hover {
  background: var(--rb-gold);
  border-color: var(--rb-gold);
  color: #1a1a1a;
}
/* (Builder count-field parity now lives in encyclopedia/w40k.css -- .w40k-wcount matches the index's
   .w40k-wgcount exactly, and the stale roster.css override was removed -- so no vx.css patch needed.) */

/* ============================================================
   Builder LEFT COLUMN as an app rail (2026-08): your army on top,
   the faction's whole catalogue browsable underneath, both filtered
   by one search box. The column is the page's scroll region, so the
   rows have to be dense enough to read a 2000pt list at a glance.
   ============================================================ */

/* Denser list rows: name + points on one line, one muted sub-line under it.
   Every row in a full list was three lines tall before. */
.rb2-list .rb2-unit { margin-bottom: 3px; }
.rb2-unit-open { padding: .3rem var(--rb-space-2); gap: .35rem 
  var(--rb-space-2); font-size: .88rem; line-height: 1.25; }
.rb2-unit-name { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.rb2-unit-sub { flex: 1 0 100%; font-size: .72rem; line-height: 1.2; }
.rb2-unit-row { gap: 2px; padding-right: var(--rb-space-2); }
.rb2-drag-handle { padding-left: 4px; opacity: .5; font-size: .8rem; cursor: grab; }
.rb2-unit-pts { font-size: .85rem; }
.rb2-enh-chip { font-size: .66rem; }

/* '+' on a list row = another copy of that unit; sits beside the remove '×'. */
.rb2-dup { display: inline; }
.rb2-dup button { background: none; border: none; color: var(--text-muted);
  cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 3px; }
.rb2-dup button:hover { color: var(--accent); }

/* The catalogue: always visible under your army, whole row clickable to add. */
.rb2-browser-head { margin: var(--rb-space-4) var(--rb-space-1) var(--rb-space-1); }
.rb2-browser-row { cursor: pointer; }
.rb2-browser-row:hover .rb2-browser-name { color: var(--accent); }

/* Collapsible card section (Match Up): the heading IS the toggle. */
.rb2-sec-h { display: flex; align-items: center; }
.rb2-sec-toggle { background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  display: inline-flex; align-items: center; gap: .4rem; }
.rb2-sec-toggle::before { content: '▾'; font-size: .7em; color: var(--accent);
  transition: transform var(--dur-base) var(--ease); }
.rb2-sec-toggle[aria-expanded="false"]::before { transform: rotate(-90deg); }
.rb2-sec-toggle:hover { color: var(--accent); }
