/* Cardboard Oracle — decision-look chassis (dl-*).
   1:1 port of arena-harvest/decision-site/standards/standards.css.
   Every selector is prefixed dl- so nothing here can collide with the
   existing .rules-* arena classes or styles.css. Values are copied
   verbatim from standards.css — do not "improve" a number here without
   updating the source chassis too.
   The page-level resets in standards.css (the universal box-sizing rule,
   and the html/body layout rules) are NOT ported: they are global and
   unscoped, and would either collide with or duplicate styles.css's own
   reset. */

/* ===== tokens ===== */
:root {
  --dl-gold:        #d9b76a;
  --dl-gold-bright: #e8cd8f;
  --dl-gold-dim:    rgba(217,183,106,0.55);
  --dl-gold-faint:  rgba(217,183,106,0.28);
  --dl-ink:         #efe9dd;
  --dl-ink-dim:     #cfc8b8;
  --dl-bg:          #0b0b0c;
  --dl-panel:       #141416;
  --dl-panel2:      #17171a;

  /* Card sizing is ONE token. Pickers scroll below --dl-card-min; they
     never shrink cards to fit, because unreadable art defeats the point
     of a card-object picker. */
  /* The source-card panel is exactly as wide as a full-height card, so
     the card fills the box edge to edge with no letterbox. */
  --dl-modal-h:  700px;

  --dl-card-w:   180px;
  --dl-card-min: 150px;
  --dl-card-gap: 22px;
}

/* ===== chassis (modal, panel, title, footer) ===== */
.dl-modal {
  display: flex; width: 1240px; height: var(--dl-modal-h);
  background: linear-gradient(160deg, #141416 0%, #0e0e10 100%);
  border: 1.5px solid var(--dl-gold-dim); border-radius: 18px;
  box-shadow: 0 24px 90px rgba(0,0,0,0.75);
  color: var(--dl-ink); overflow: hidden; position: relative;
}

/* The source-card panel is present IF AND ONLY IF the ask has a source
   card. Cleanup discard has no source card, so it has no panel — that is
   the rule, not an omission. */
/* L1 amendment (2026-08-23): the panel itself still spans the full modal
   height edge to edge (that comes from .dl-modal's default cross-axis
   stretch on this flex item, untouched below) — this flex context centres
   the card WITHIN that full height, so a content column taller than the
   card (e.g. colors_multi) never reads as the card being pinned to the top. */
/* Census #15 (owner ruling 2026-08-27, "honor FULL-BLEED, fix the root
   cause"): the pane's width is NOT a token derived from the nominal 700px
   chassis — it is the card's own 488:680 aspect applied to whatever height
   the chassis actually rendered at. A box that always matches the card's
   ratio is a box object-fit:cover fills edge-to-edge without ever cropping,
   at every viewport, so the name and mana cost can't be shaved off. This is
   the geometry the legacy standard pane already ships (styles.css
   `.rules-choice-box.has-source-panel > .dl-card-panel`, owner reference
   2026-08-25) — the two chassis now agree. */
.dl-card-panel {
  flex: 0 0 auto; aspect-ratio: 488 / 680; align-self: stretch;
  display: flex;
  align-items: center; justify-content: center;
  background: #0a0a0b;
  border-right: 1px solid rgba(255,255,255,0.08);
  padding: 0;
}
.dl-card-panel .dl-frame {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
}
.dl-card-panel img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; border-radius: 0;
}

.dl-content {
  flex: 1; min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  padding: 40px 44px 36px 40px;
}

.dl-close {
  position: absolute; top: 24px; right: 28px;
  background: none; border: none; color: var(--dl-gold); opacity: 0.85;
  font-size: 26px; line-height: 1; cursor: pointer; font-family: inherit;
}
.dl-close:hover { opacity: 1; }

/* Title reserves two lines on every screen so the picker never shifts
   vertically between one decision and the next. */
.dl-title {
  color: var(--dl-gold); font-size: 42px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  line-height: 1.08; min-height: 2.16em; text-wrap: balance;
  padding-right: 44px;
}
/* The sub-line is the INSTRUCTION. It never reprints the oracle text
   that the card panel is already showing.
   R8-P11 (owner ruling, adaptive note sizing): the modal itself is a fixed
   1240px chassis, so this was pinned at 19px on every desktop screen no
   matter how much room there was. clamp() grows it modestly on wider
   viewports without touching the mobile-shell override below. */
.dl-sub { font-size: clamp(19px, 1.35vw, 22px); line-height: 1.4; color: #d8d2c4; margin-top: 4px; }

/* Centred by AUTO MARGINS, never justify-content:center (2026-08-27 full
   sweep). `justify-content: center` on a scroll container splits an overflow
   symmetrically — and a scroll container can only ever scroll DOWN from 0, so
   the top half of that overflow is permanently unreachable. That is the bug
   behind every one of the per-case `justify-content: flex-start` patches
   further down this file (.dl-tier, library-search, many-opts, faces, and the
   <=460px shell), and it kept coming back on new surfaces: at 1024x768,
   ability_pick's option list ran 512px inside a 248px body, putting the whole
   FIRST option 132px above the scrollable region — visible but unclickable.
   Auto margins do the same centring when there IS free space and collapse to
   zero when there is not, so nothing is ever hidden above the scroll origin.
   first-child/last-child (not `> *`) so multiple children stay a centred
   GROUP instead of being spread apart by shared free space. */
.dl-body { flex: 1; display: flex; flex-direction: column; justify-content: flex-start; min-height: 0; }
.dl-body > :first-child { margin-block-start: auto; }
.dl-body > :last-child { margin-block-end: auto; }

/* L1: Cancel · readout · primary is ONE row. flex-wrap stays off at this
   width (the narrow-viewport blocks below re-enable it).
   B7b residual: the OLD order shrank the readout first (flex-shrink:4, to
   0px) and the secondary second (FAIL TO FIND ellipsizing to "FAIL T…")
   while the primary's 280px min-width never gave an inch — at 1024x768 that
   forced the readout to vanish and the secondary to truncate to cover a gap
   the primary alone could have absorbed. New order: the primary shrinks
   FIRST (flex-shrink:20, min-width 160px — still its own content's floor,
   just not 280px's fixed one), the secondary only ellipsizes once the
   primary is already at that floor (flex-shrink:1, unchanged), and the
   readout never shrinks at all (flex-shrink:0) — it shows its natural width
   on one row or not at all, never a half-empty one. */
.dl-foot { display: flex; align-items: center; gap: 20px; margin-top: 22px; flex-wrap: nowrap; }
.dl-count {
  font-size: 16px; color: var(--dl-ink-dim); letter-spacing: 0.06em;
  margin-left: auto; flex: 0 0 auto; min-width: 0; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dl-foot > .dl-btn-cancel {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dl-foot > .dl-btn-confirm, .dl-foot > .dl-btn-next {
  flex: 0 20 auto; min-width: 160px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dl-btn {
  font-family: inherit; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; font-size: 19px; border-radius: 10px;
  cursor: pointer; padding: 17px 36px;
}
.dl-btn-cancel {
  background: rgba(255,255,255,0.03); border: 1.5px solid rgba(217,183,106,0.4);
  color: #e6dfd0;
}
.dl-btn-cancel:hover { border-color: var(--dl-gold-dim); }

/* Filled gold COMMITS the action. Nothing else is allowed to be filled gold. */
.dl-btn-confirm {
  border: 1px solid #a8813d; color: #241300; min-width: 280px;
  background: linear-gradient(180deg, #d9b26a 0%, #c69a4e 55%, #b9893f 100%);
  box-shadow: 0 4px 18px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.35);
}
.dl-btn-confirm:hover { filter: brightness(1.06); }
.dl-btn-confirm:disabled, .dl-btn-confirm[disabled] {
  background: linear-gradient(180deg, #4a453c 0%, #3b382f 100%);
  border-color: #55503f; color: #8d8879; box-shadow: none; cursor: not-allowed;
}
/* Outlined gold ADVANCES to a further prompt — it does not commit. */
.dl-btn-next {
  border: 1.5px solid var(--dl-gold-bright); color: var(--dl-gold-bright);
  background: rgba(217,183,106,0.07); min-width: 280px;
}
.dl-btn-next::after { content: " \203A"; font-size: 22px; }
.dl-btn-next:hover { background: rgba(217,183,106,0.14); }
/* RULING 1: the primary is disabled at 0 chosen. .dl-btn-confirm already had
   this state; the outlined twin needs the same one or a select-then-confirm
   ask that advances looks live before anything is picked. */
.dl-btn-next:disabled, .dl-btn-next[disabled] {
  border-color: #55503f; color: #8d8879;
  background: rgba(255,255,255,0.02); cursor: not-allowed;
}
.dl-btn-next:disabled:hover { background: rgba(255,255,255,0.02); }

/* Keyboard focus is visibly NOT the selected state. */
.dl-opt-card:focus-visible, .dl-opt-row:focus-visible, .dl-btn:focus-visible, .dl-pop-row:focus-visible {
  outline: 3px solid #7fb6ff; outline-offset: 3px;
}

/* Mana symbols — official Scryfall artwork, same source the app uses. */
.dl-mana-symbol { width: 1em; height: 1em; vertical-align: -0.13em; }
.dl-opt-desc .dl-mana-symbol { width: 21px; height: 21px; }
.dl-sep { margin: 0 10px; color: var(--dl-gold-dim); }
/* RULING 3b: a COLOR option leads with its real mana symbol; the color name
   stays the row title. renderScryfallManaSymbols (frontend/app.js) emits the
   shared un-prefixed .mana-symbol class, so size that one here rather than
   fork the helper. */
.dl-mana-lead { display: inline-flex; vertical-align: -0.16em; margin-right: 14px; }
.dl-mana-lead .mana-symbol, .dl-mana-lead .dl-mana-symbol { width: 27px; height: 27px; }
/* R8-P9 (owner ruling 2026-08-26): the row-icon slot, for rows whose natural
   glyph is chip-icon art rather than a mana symbol (keywords, counter types,
   permanent types — frontend/assets/chip-icons, the battlefield chips' own
   set). Geometry is the owner's canon sheet's (keyword-choice-standard-
   layout.html .opt-icon): a 46px gold-ringed disc in a left column, glyph
   masked to gold so one art file serves every surface. Absolute rather than a
   flex column because .dl-opt-row carries an inline display:block; the row's
   left padding below opens the column for it. Rows with no shipped glyph emit
   NOTHING — there is deliberately no default icon. */
.dl-row-icon {
  position: absolute; left: 26px; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%;
  border: 1.5px solid rgba(217,183,106,0.5);
  background: rgba(217,183,106,0.08);
}
.dl-row-icon::before {
  content: ""; position: absolute; inset: 9px;
  background: var(--dl-gold-bright);
  -webkit-mask: var(--ic) center/contain no-repeat;
  mask: var(--ic) center/contain no-repeat;
  /* R8-P18: the row-icons set fills small dots/solid masses with
     fill="currentColor" (default black). Default mask MODE is luminance in
     some engines, which reads opaque black as near-zero luminance and drops
     those details. Force alpha mode so any opaque pixel counts, regardless
     of its resolved color. */
  -webkit-mask-mode: alpha;
  mask-mode: alpha;
}

/* ===== option rows ===== */
/* Selection mark — ONE component, two anchors. */
.dl-check {
  position: absolute; width: 34px; height: 34px; border-radius: 50%;
  background: linear-gradient(180deg, var(--dl-gold-bright) 0%, #c69a4e 100%);
  color: #241300; border: 1px solid #a8813d;
  box-shadow: 0 2px 12px rgba(0,0,0,0.55);
  display: none; align-items: center; justify-content: center;
  font-size: 19px; font-weight: 700; line-height: 1;
}
.dl-opt-row.selected .dl-check, .dl-opt-card.selected .dl-check { display: flex; }
/* The live arena marks a chosen candidate with .is-picked on the element that
   carries the binding attribute (its delegated click handler owns that class);
   .selected is the static chassis mark. Same component, same look. */
.is-picked > .dl-opt-card .dl-check,
.dl-opt-card.is-picked .dl-check { display: flex; }
.is-picked > .dl-opt-card img, .dl-opt-card.is-picked img {
  box-shadow: 0 0 0 3px var(--dl-gold-bright), 0 0 28px rgba(217,183,106,0.4);
}
/* L3 says ONE mark. The legacy chooser tile carries a second one — a ::after
   check badge (frontend/styles.css .rules-choice-card.is-picked::after) — and
   because _DL_BARE's inline `all:unset` strips the button's own
   position:relative, that badge escapes its tile and lands in the MODAL's
   top-left corner. The pseudo-element is unreachable from the inline reset, so
   cancel it here; the chassis tile wears .dl-check instead. */
.dl-modal .rules-choice-card.is-picked::after { content: none; }
.dl-thumb { position: relative; display: block; }
.dl-opt-card .dl-check { top: -13px; right: -13px; }             /* card objects: corner */
.dl-opt-row  .dl-check { top: 50%; right: 26px; transform: translateY(-50%); }

.dl-options { display: flex; flex-direction: column; gap: 20px; }
.dl-opt-row {
  position: relative; text-align: left; cursor: pointer; font-family: inherit;
  background: var(--dl-panel2); border: 1.5px solid rgba(217,183,106,0.35);
  border-radius: 12px; color: var(--dl-ink); padding: 24px 84px 24px 28px;
}
.dl-opt-row:hover { border-color: var(--dl-gold-dim); background: #1b1b1f; }
.dl-opt-row.selected {
  border-color: var(--dl-gold-bright);
  box-shadow: 0 0 0 1px rgba(232,205,143,0.45), 0 0 26px rgba(217,183,106,0.22),
              inset 0 0 26px rgba(217,183,106,0.05);
}
.dl-opt-title { font-size: 25px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; }
/* R8-P11 (owner ruling, adaptive note sizing): same clamp() treatment as
   .dl-sub — the per-row description is the other note-tier text in this
   chassis. Mobile-shell override below is untouched. */
.dl-opt-desc  { font-size: clamp(18px, 1.25vw, 21px); color: var(--dl-ink-dim); margin-top: 9px; }
/* A row with no description (colors_multi's five glyph+title rows) is the
   same 78px-tall box as a row that carries one — measured against
   .dl-body's real available height (colors_multi @1280x900: 393.6px; the
   options block wants 470px), five of those overflow enough that
   .dl-body's centered overflow:auto makes the top row unreachably clipped
   (justify-content:center clips symmetrically, but only the bottom half of
   the overflow is ever scrollable). Compact rows only when there's no
   description to make room for, so a row that DOES carry one (return_mode_
   cancel, look 01) is untouched — this must stay pixel-identical. */
.dl-opt-row:not(:has(.dl-opt-desc)) { padding: 12px 84px 12px 28px; }
/* R8-P9: an iconed row opens a left column for the disc (26 + 46 + 20), the
   mirror of .dl-check's 84px right gutter. MUST stay after the no-desc
   compaction rule above — same (0,2,0) specificity, so source order decides,
   and a no-desc iconed row (permanent types) needs both. */
.dl-opt-row:has(.dl-row-icon) { padding-left: 92px; }
/* G7 v2 — a name-card browse row (SEEN FROM OPPONENTS) carries a small art
   thumb beside the title; a plain type row (creature-type tiers) has none. */
.dl-opt-row-thumb { display: flex; align-items: center; gap: 14px; padding: 14px 84px 14px 18px; }
.dl-opt-thumb { width: 40px; height: 40px; border-radius: 6px; object-fit: cover; flex: 0 0 auto; }
.dl-opt-row-thumb .dl-opt-title { font-size: 18px; }
/* G7 v2 — a tiered browse list (338-type vocabulary, the full name index)
   is routinely far taller than .dl-body, unlike every other chassis body
   before it. .dl-body's base rule centers its children (see the .dl-opt-row
   compaction comment above for the identical bug) — for content this tall
   that clips the TOP tier's rows outside the scrollable area entirely
   (Chrome's default scroll position for centered flex overflow only ever
   exposes the bottom half). Anchor to the top instead so overflow-y:auto's
   default scroll position (0) starts exactly where "In Your Deck" /
   "Seen From Opponents" begins. */
.dl-body:has(.dl-tier),
.rules-choice-library-search .dl-body,
/* R5 leg 1 fix: a 5+ option modal-ability mode list (spell_abilities_multi,
   Mishra-class charms) is exactly the "routinely far taller than .dl-body"
   case above, just for option ROWS instead of browse tiles -- same fix. */
.rules-choice-many-opts .dl-body,
/* 12-case review fix (card_state, Room doors): face tiles carry the face's
   full rules text below the art — LOOK 04 makes that text the basis for the
   pick — so a wordy face plus a multi-line title overflows .dl-body, and
   centering leaves the overflow's top half unreachable. Same fix. */
.rules-choice-faces .dl-body { justify-content: flex-start; }
/* These four opt OUT of the auto-margin centring the base .dl-body rule now
   uses: they want a hard top anchor even when the content DOES fit, which is
   what they shipped with. (When it does not fit the two are identical — an
   auto margin has no free space to take.) */
.dl-body:has(.dl-tier) > *,
.rules-choice-library-search .dl-body > *,
.rules-choice-many-opts .dl-body > *,
.rules-choice-faces .dl-body > * { margin-block: 0; }
/* …and, per the G7 v2 lesson below (scrolling/clipping never fixes what a
   too-tall child paints under the footer), shrink the FACE TILES themselves
   so art + name + the face's full rules text fit the body at 1280x900. */
.rules-choice-faces { --dl-card-w: 150px; --dl-card-min: 110px; }
/* Panel-less face picks (owner ruling 2026-08-24) center their tiles in the
   full-width body; `safe` keeps an overflowing strip anchored at its start. */
.rules-choice-faces .dl-picker {
  height: 100%; min-height: 0; justify-content: safe center;
}
/* Census #13 (owner ruling 2026-08-27) — the MDFC / split FACE PICKER. Same
   panel-less face chassis, but its tiles carry no rules text (the faces are
   whole cards, read from the art), so they are the big reference tiles of
   decision-site/face-pick-own-surface.html rather than the 150px text-tile
   size .rules-choice-faces uses. The 268px reference is a CEILING, not a
   fixed size: L10's measured --dl-card-grow (the picker calls _dlFitPickers
   itself — it renders outside _rulesShow) shrinks the art on a short body so
   the caption under it never scrolls out of sight. */
.rules-choice-face-pick { --dl-card-w: 268px; --dl-card-min: 130px; }
.rules-choice-face-pick .dl-picker { gap: 52px; }
.rules-choice-face-pick .rules-cardface { max-width: 268px; }
/* The caption's mana pips are IMGs inside .dl-opt-card, which styles its
   card art as a full-width block with the selection ring on it. Keep the
   pips inline text-sized glyphs, unringed. */
.rules-choice-face-pick .dl-opt-card img.mana-symbol,
.rules-choice-face-pick .dl-opt-card.selected img.mana-symbol {
  display: inline-block; width: 0.95em; height: 0.95em; margin: 0 1px;
  vertical-align: -0.08em; border-radius: 0; box-shadow: none;
  transform: none;
}
/* G7 v2 — measured regression (ux-sweep P4 hit-test audit, library_search_
   fail_to_find): a full --dl-card-w tile is 252px tall; with the added
   filter/type-chip row eating into .dl-body's height above it, the tile's
   own bottom landed 3px BELOW the footer's top (632px vs 629px) — a
   hairline paint overlap between a card and FAIL TO FIND, invisible at a
   glance but a real physical hit-test failure (the same click could
   resolve to either control). NOTE: capping/scrolling the .dl-picker
   CONTAINER does not fix this — overflow clipping changes what's visible,
   never the overflowing child's own getBoundingClientRect(), which is
   exactly what a real click (and this audit) resolves against. Shrink the
   TILES themselves instead, via the same --dl-card-w/--dl-card-min token
   every other picker already sizes from (L10's own "shrink to the legible
   floor" rule) — smaller width keeps the aspect ratio, so height drops
   with it, clearing real headroom instead of hiding an unclipped overlap. */
.rules-choice-library-search {
  --dl-card-w: 130px;
  --dl-card-min: 110px;
}
/* G7 v2 — gold serif section caption above each browse tier (IN YOUR DECK /
   ON THE BATTLEFIELD / ALL TYPES / SEEN FROM OPPONENTS). */
.dl-tier { margin-bottom: 20px; }
.dl-tier-h {
  font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dl-gold); margin: 0 0 10px; font-weight: 700;
}
.dl-tier .dl-options { gap: 10px; }

/* ===== card / face / player tiles ===== */
/* L10 (owner 2026-08-23) "content fills the body": the strip OWNS the body's
   spare height instead of sitting in it as a fixed-size band. --dl-tile-w is
   the whole sizing rule and every tile emitter reads it:
     few tiles  → the per-tile share of the strip is huge, so the growth cap
                  wins and tiles grow until the card is as tall as the body
                  allows (--dl-card-grow, measured per render in app.js —
                  CSS cannot see how many lines the title wrapped to);
     many tiles → the share falls below the legible floor, --dl-card-min wins,
                  and the strip scrolls with the gold bar + edge fades.
   --dl-card-w stays the fallback for any tile rendered before that pass. */
/* align-items stays flex-start: the tiles are all one card tall now, and a
   face tile with three lines of rules text under it must not push its ART out
   of line with its neighbours'. The BODY centres the strip (it always did). */
.dl-picker {
  display: flex; align-items: flex-start; gap: var(--dl-card-gap);
  overflow-x: auto; overflow-y: visible;
  padding: 20px 2px 16px;
  --dl-share: calc((100% - (var(--dl-count, 1) - 1) * var(--dl-card-gap))
                   / var(--dl-count, 1));
  --dl-tile-w: clamp(var(--dl-card-min), var(--dl-share),
                     var(--dl-card-grow, var(--dl-card-w)));
}
/* Library-order is one continuous ordered run, so the cards read as a single
   track when packed tight — the wide 22px inter-card gap suits partitioned
   pickers, not this one. */
.rules-liborder .dl-picker { --dl-card-gap: 8px; }

/* Trigger-order — vertical drag column (Arena layout). The strip wraps ONE
   ordered group; that group is a column-reverse flex run so the ord-1 tile
   paints at the TOP while sel.order / commit stay in DOM order (only the paint
   direction and the drag geometry go vertical). Each ability tile reflows from
   the stacked mini-card to a full-width row. */
.dl-tray.is-vertical .dl-tray-strip.is-vertical {
  flex-direction: column; align-items: stretch;
  overflow-x: hidden; overflow-y: auto; max-height: 52vh; padding: 6px 2px;
}
.dl-tray.is-vertical .dl-tray-half {
  display: flex; flex-direction: column-reverse; gap: 12px; width: 100%;
}
.dl-tray.is-vertical .dl-tray-item { width: 100% !important; flex: 0 0 auto; }
.dl-tray.is-vertical .dl-ab {
  display: flex; flex-direction: row; align-items: center; gap: 14px;
  text-align: left; padding: 10px 14px;
}
.dl-tray.is-vertical .dl-ab-art {
  width: 110px; height: 64px; object-fit: cover; border-radius: 6px; flex: 0 0 auto;
}
.dl-tray.is-vertical .dl-ab-txt { flex: 1 1 auto; text-align: left; }
.dl-tray.is-vertical .dl-ord-badge {
  position: static; margin-left: auto; flex: 0 0 auto;
  font-size: 18px; min-width: 34px; text-align: center;
}
.dl-tray-ends.is-axis {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0 4px 10px; gap: 8px;
}
.dl-tray-ends.is-axis .dl-tray-end {
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  opacity: .7; background: none; border: none;
}
/* Edge fade so a clipped card reads as "scroll me", not as a render bug. */
.dl-picker.is-scrollable {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 44px,
                      #000 calc(100% - 44px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 44px,
                      #000 calc(100% - 44px), transparent 100%);
}
/* A real proportional scrollbar — its width states how much is off-screen. */
.dl-picker::-webkit-scrollbar { height: 9px; }
.dl-picker::-webkit-scrollbar-track { background: rgba(255,255,255,0.06); border-radius: 99px; }
.dl-picker::-webkit-scrollbar-thumb {
  background: linear-gradient(90deg, #c69a4e 0%, var(--dl-gold-bright) 100%);
  border-radius: 99px;
}

.dl-opt-card {
  position: relative; flex: 0 0 auto; cursor: pointer;
  background: none; border: none; padding: 0; font-family: inherit;
  border-radius: 12px;
}
/* The TILE carries the L10 width (--dl-tile-w, set by its emitter); the art
   fills whatever the tile is. min-width would fight the clamp's own floor. */
.dl-opt-card img {
  display: block; width: 100%;
  border-radius: 9px; box-shadow: 0 6px 20px rgba(0,0,0,0.55);
  transition: transform 0.12s ease;
}
.dl-opt-card:hover img { transform: translateY(-4px); }
.dl-opt-card.selected img {
  box-shadow: 0 0 0 3px var(--dl-gold-bright), 0 0 28px rgba(217,183,106,0.4);
}
/* Player tiles: the label is the player, the sub-label their commander. */
.dl-opt-label {
  font-size: 15px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dl-gold); margin-bottom: 8px; text-align: center;
}
.dl-opt-sublabel {
  font-size: 13px; color: var(--dl-ink-dim); margin-top: 8px; text-align: center;
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* R3 mulligan tuck: order = tap order, stated once there is an order to state
   (2+ picked) rather than shown per-tile — L3 keeps the check disc as the
   ONLY selection mark. */
.rules-bottom-order-hint {
  margin-bottom: 16px; font-size: 13.5px; letter-spacing: 0.05em;
  color: var(--dl-ink-dim); text-align: center; font-style: italic;
}

/* ===== L10 grouping: interchangeable candidates are ONE tile ===== */
/* Two card edges peeking out behind the face say "there are more of these"
   before the badge is even read. They are pseudo-elements on the thumb so no
   emitter has to invent extra nodes; isolation keeps the negative z-index
   inside the thumb instead of dropping it behind the modal's own background. */
.dl-opt-card.is-stack .dl-thumb { isolation: isolate; }
.dl-opt-card.is-stack .dl-thumb::before,
.dl-opt-card.is-stack .dl-thumb::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: 9px; background: #131316;
  border: 1px solid rgba(217,183,106,0.4);
  box-shadow: 0 6px 20px rgba(0,0,0,0.55);
}
.dl-opt-card.is-stack .dl-thumb::before { transform: translate(11px, 11px); }
.dl-opt-card.is-stack .dl-thumb::after  { transform: translate(5px, 5px); }
/* The badge is the group's live state: "×5" until a member is chosen, then
   "2 of ×5". Gold, and diagonally opposite the L3 check disc so a selected
   stack shows both marks. It sits INSIDE the art: .dl-picker is a horizontal
   scroller (overflow-x:auto, which computes overflow-y to auto as well), so
   the check disc's negative corner offset only survives on the axis the strip
   pads — a badge hung off the left edge of the first tile gets clipped. */
.dl-stack-badge {
  position: absolute; top: 9px; left: 9px; z-index: 2;
  min-width: 34px; height: 34px; padding: 0 10px; border-radius: 99px;
  background: linear-gradient(180deg, var(--dl-gold-bright) 0%, #c69a4e 100%);
  border: 1px solid #a8813d; color: #241300;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 700; letter-spacing: 0.04em; line-height: 1;
  white-space: nowrap; box-shadow: 0 2px 12px rgba(0,0,0,0.55);
}
/* On a TEXT row there is no corner to hang it off — it rides the title. */
.dl-opt-title .dl-stack-badge {
  position: static; display: inline-flex; vertical-align: middle;
  margin-left: 12px; height: 28px; font-size: 14px;
}
/* Look 08 (L6) player tiles: the player's commander card, name captioned
   ABOVE it (not below, unlike .dl-opt-label). Separate classes from
   .dl-opt-card/.dl-opt-label so this look's markup shares no selector with
   the card/face-tile work landing in this file alongside it. */
.dl-player-tile {
  position: relative; flex: 0 0 auto; cursor: pointer;
  background: none; border: none; padding: 0; font-family: inherit;
  border-radius: 12px; display: flex; flex-direction: column; align-items: center;
}
/* The tile button carries the shared inline reset `all:unset;display:block`
   (_DL_BARE in app.js), which always wins over this file's
   `.dl-player-tile{display:flex}` — inline beats any class selector. That
   silently drops the intended flex column, and .dl-player-cap (a bare
   `<span>`, UA-default `display:inline`) then has its margin-bottom ignored
   entirely: vertical margins on inline boxes don't add layout space. The
   caption and the thumb below it end up flush, zero gap, so the selected
   check disc (`top:-13px` off .dl-thumb, right below the caption) lands in
   the same pixel band as the caption text instead of clear above it. Make
   the caption a real block so its margin actually pushes the thumb down. */
.dl-player-cap {
  display: block;
  font-size: 15px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dl-gold); margin-bottom: 16px; text-align: center;
}
.dl-player-tile img {
  display: block; width: 100%;
  border-radius: 9px; box-shadow: 0 6px 20px rgba(0,0,0,0.55);
  transition: transform 0.12s ease;
}
.dl-player-tile:hover img { transform: translateY(-4px); }
.dl-player-tile.selected img {
  box-shadow: 0 0 0 3px var(--dl-gold-bright), 0 0 28px rgba(217,183,106,0.4);
}
.dl-player-tile .dl-check { top: -13px; right: -13px; }
.dl-player-tile.selected .dl-check { display: flex; }

/* ===== number picker ===== */
.dl-num-row { display: flex; align-items: center; justify-content: center; gap: 22px; }
.dl-step {
  font-family: inherit; font-size: 26px; font-weight: 700; color: var(--dl-ink);
  background: var(--dl-panel2); border: 1.5px solid rgba(217,183,106,0.35);
  border-radius: 10px; padding: 14px 26px; cursor: pointer; min-width: 86px;
}
.dl-step:hover { border-color: var(--dl-gold-dim); background: #1b1b1f; }
.dl-num-value {
  font-size: 84px; font-weight: 700; color: var(--dl-gold-bright); line-height: 1;
  min-width: 150px; text-align: center;
}
.dl-slider { margin-top: 26px; }
.dl-slider input {
  width: 100%; accent-color: var(--dl-gold); height: 6px;
}
.dl-ticks {
  display: flex; justify-content: space-between; margin-top: 8px;
  font-size: 15px; color: var(--dl-ink-dim);
}
.dl-bound {
  margin-top: 18px; text-align: center; font-size: 17px;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--dl-gold);
}

/* ===== anchored action menu ===== */
.dl-anchor { position: relative; display: flex; flex-direction: column; align-items: center; gap: 0; }
.dl-anchor.below { flex-direction: column-reverse; }   /* battlefield: menu under the card */

/* L2: a decision never appears in sans. The modal chassis carries the serif
   inline (_DL_FONT in frontend/app.js); the anchored menu cannot — it mounts
   on document.body inside the deliberately unstyled .pt-menu/.rules-menu
   shell, so its rows inherited the app's Inter. Set it here, on the dl side,
   so the .pt-menu skin every other menu shares stays untouched. */
/* L11 (owner 2026-08-23): the anchored menu SIZES TO ITS CONTENT — never a
   fixed wide box. fit-content inside a min/max clamp, so a two-row rider menu
   is as narrow as its longest row while a long ability list still stops at the
   640px the cost-chip cap below is measured against; rows wrap only past the
   max. The viewport cap keeps it on screen on a phone. */
.dl-pop {
  font-family: Georgia, "Times New Roman", serif;
  width: fit-content;
  min-width: 220px;
  max-width: min(640px, calc(100vw - 24px));
  position: relative;
  background: linear-gradient(180deg, #1b1712 0%, #121114 100%);
  border: 1.5px solid var(--dl-gold-dim); border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.7);
  overflow: hidden;
}
.dl-pop-row {
  display: grid; grid-template-columns: 46px 1fr auto;
  gap: 18px; align-items: center; width: 100%;
  padding: 18px 20px; background: none; border: none;
  font-family: inherit; color: var(--dl-ink); text-align: left; cursor: pointer;
}
.dl-pop-row + .dl-pop-row { border-top: 1px solid rgba(255,255,255,0.07); }
.dl-pop-row:hover { background: rgba(217,183,106,0.07); }
.dl-pop-row.primary { background: rgba(217,183,106,0.10); }
/* Left column = WHAT IT DOES. Right column = WHAT IT COSTS. One encoding each. */
.dl-pop-icon {
  width: 46px; height: 46px; border-radius: 11px;
  border: 1.5px solid var(--dl-gold-dim); color: var(--dl-gold-bright);
  display: flex; align-items: center; justify-content: center; font-size: 22px;
}
/* Wrapping is the MAX-width behaviour, not the default: a row keeps its words
   on one line while the menu can still grow. */
.dl-pop-label { font-size: 21px; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.dl-pop-cost { display: flex; align-items: center; gap: 6px; }
.dl-pop-cost .dl-mana-symbol { width: 27px; height: 27px; }
/* renderScryfallManaSymbols (frontend/app.js) emits the un-prefixed generic
   ".mana-symbol" class shared by every other pip context in the app — size
   it the same as the (never-emitted-here) .dl-mana-symbol name above rather
   than fork the helper just to rename its output class. */
.dl-pop-cost .mana-symbol { width: 27px; height: 27px; }
/* Non-mana costs get a chip in the SAME slot — never a second icon system. */
/* The chip states the COST, it does not get to eat the row: .dl-pop tops out
   at 640px, of which 518px is shared by the label and cost columns, so a
   230px cap leaves the label the ≥55% it needs to keep whole words on a
   line. The full clause stays reachable as the chip's title. */
.dl-cost-chip {
  font-size: 15px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--dl-gold-bright); border: 1px solid var(--dl-gold-faint);
  border-radius: 99px; padding: 6px 14px; white-space: nowrap;
  max-width: 230px; overflow: hidden; text-overflow: ellipsis;
}
.dl-pop-cancel { border-top: 2px solid var(--dl-gold-faint) !important; font-style: italic; color: var(--dl-ink-dim); }
.dl-pop-cancel .dl-pop-label { font-weight: 400; font-size: 20px; }

.dl-tail { width: 26px; height: 13px; margin: -1px 0; position: relative; z-index: 2; }
.dl-tail svg { display: block; }

.dl-anchor-card { position: relative; }
.dl-anchor-card img {
  /* R8-P8: reserve the card's layout box BEFORE the image loads. Without an
     aspect-ratio the <img> measures 0px tall, so _rulesCardActionMenu's
     on-screen clamp (which reads getBoundingClientRect right after append)
     under-measured the popout by ~419px and every battlefield menu dropped
     its action rows below the fold — invisible and unclickable. */
  display: block; width: 300px; aspect-ratio: 745 / 1040; border-radius: 12px;
  box-shadow: 0 0 0 3px var(--dl-gold-bright), 0 14px 44px rgba(0,0,0,0.65);
}
/* Same "this card has actions" badge on hand cards and battlefield cards. */
.dl-badge-actions {
  position: absolute; top: -14px; left: -14px;
  width: 42px; height: 42px; border-radius: 50%;
  background: linear-gradient(180deg, var(--dl-gold-bright) 0%, #c69a4e 100%);
  border: 1px solid #a8813d; color: #241300;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; box-shadow: 0 3px 14px rgba(0,0,0,0.6);
}
/* The card frame clips to its rounded corners (frontend/styles.css .cob-v3
   .pt-card{overflow:hidden}); the chip battlefield renderer already opts
   back out (.pt-card.pt-chip{overflow:visible}) but the full-card hand
   renderer does not, which would silently clip this badge's intentional
   corner-straddle. Scope the escape hatch to only cards that carry it. */
.pt-card:has(> .dl-badge-actions) { overflow: visible; }

/* ===== command ask ===== */
/* New block, not in standards.css — LOOK_RULES.md rule L7: one serif hint
   line stating the consequence, above exactly two buttons (filled gold
   default/affirmative, outlined the other). Lives in the sidebar panel
   under History, never floats. Buttons reuse .dl-btn-confirm/.dl-btn-cancel. */
.dl-ask {
  border: 1px solid var(--dl-gold-faint);
  border-radius: 12px;
  padding: 1.25rem;
  background: var(--dl-panel);
}
/* L2: the panel's own caption is serif gold, never sans app chrome. */
.dl-ask-cap {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 0.78rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dl-gold); text-align: center; margin-bottom: 0.6rem;
}
.dl-ask-hint {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.1rem;
  text-align: center;
  color: var(--dl-ink);
}
.dl-ask-hint b, .dl-ask-hint strong {
  color: var(--dl-gold);
}
.dl-ask-btns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 1rem;
}
/* An ask button is the decision, not rail chrome, so it does not take part in
   the rail's compaction. Two things shrink it there and both have to lose:
   the tablet skin (`.cob-v3.cob-tablet-compact .cob-rail-mid
   .cob-decision-box .rules-act`, 11px) and — see arena-harvest/LOOK_REVIEW.md,
   the 1024x768 blank-button defect — a stray inline `font-size: 0px` that
   lands on both buttons after render and erases their labels outright.
   `!important` is what beats an inline declaration; nothing else does. */
.dl-ask .dl-btn-confirm, .dl-ask .dl-btn-cancel { font-size: 0.95rem !important; }
/* L2 again: the ask buttons and the chooser-context header above them are
   decision copy, so they are serif too. The buttons carry .rules-act, which
   the rail re-skins at `.cob-v3 .cob-rail .rules-act` specificity — and the
   same inline write above can reach them — so they need the same !important
   the size floor does. The context block (SOURCE / card name / Full card
   rules / "Cascaded from …") is plain inheritance, so a rule is enough. */
.dl-ask .dl-btn-confirm, .dl-ask .dl-btn-cancel {
  font-family: Georgia, "Times New Roman", serif !important;
}
.rules-may .rules-choice-context, .rules-may .rules-cascade-from,
.rules-mulligan-modal .rules-choice-context {
  font-family: Georgia, "Times New Roman", serif;
}

/* ===== G5 ordering tray (LOOK_RULES G5 APPROVED 2026-08-23) ============= */
/* ONE horizontal tray whose two ENDS are labelled — position along that single
   axis IS the answer (BOTTOM↔TOP / GRAVEYARD↔LIBRARY / LAST↔FIRST). There is
   no second box: the divider inside the strip is where one end's cards stop
   and the other's begin, and the end LABELS are themselves tap/drop targets
   (drag primary, tap secondary: tap a card then an end label).
   The strip IS a .dl-picker, so L10 sizing (grow to --dl-card-w with few
   items, shrink to --dl-card-min and scroll with many, gold bar + edge fades)
   comes for free from the rules above — nothing is re-implemented here. */
.dl-tray {
  display: flex; flex-direction: column; gap: 10px;
  min-height: 0; width: 100%;
}
.dl-tray-strip {
  background: rgba(255,255,255,0.028);
  border: 1px solid rgba(217,183,106,0.18); border-radius: 14px;
  /* Tiles line up by their ART, not by their centres: a trigger with three
     lines of text must not push its neighbour's art out of line. */
  padding: 18px 16px 14px; align-items: flex-start;
  /* The two ends hug their labels and the divider sits between them, so the
     tray LOOKS like the axis it is. A one-ended tray (triggers, library
     order) spreads its tiles across the same span instead. */
  justify-content: space-between;
  /* L10 for a tray: few items grow to the chassis card size and stop there
     (the mockup's 180px), many shrink to --dl-card-min and the strip scrolls.
     The measured grow cap the other pickers use would let three cards balloon
     past a real card's width. */
  --dl-card-grow: var(--dl-card-w);
}
/* One end's cards. A real box (the drag engine hit-tests group rects), sized
   to its contents so the STRIP is what overflows and scrolls. */
.dl-tray-half {
  display: flex; align-items: flex-start; gap: var(--dl-card-gap);
  flex: 0 0 auto; min-width: 0;
}
.dl-tray-strip > .dl-tray-half:only-child {
  flex: 1 1 auto; justify-content: space-evenly;
}
.dl-tray-half:empty { min-width: 54px; min-height: 90px; border-radius: 10px; }
.dl-tray-half.is-drop-hover { background: rgba(217,183,106,0.08); }
.dl-tray-strip:empty::after {
  content: "nothing to order"; color: var(--dl-ink-dim); font-size: 15px;
}
/* The split: everything left of it goes to the LEFT end, everything right of
   it to the RIGHT end. It is a flex child (it has to sit between tiles in
   flow) but never a tile — [data-dl-nocount] keeps it out of the L10 count. */
.dl-tray-split {
  flex: 0 0 auto; align-self: stretch; width: 3px; min-height: 90px;
  border-radius: 99px; background: linear-gradient(180deg,
    rgba(217,183,106,0) 0%, var(--dl-gold) 30%, var(--dl-gold) 70%,
    rgba(217,183,106,0) 100%);
}
.dl-tray-ends {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px;
}
.dl-tray-end {
  background: none; border: 1px solid transparent; border-radius: 10px;
  padding: 6px 14px; cursor: pointer; font-family: inherit; text-align: left;
  color: var(--dl-gold); font-size: 17px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.dl-tray-end[data-end="right"] { text-align: right; }
.dl-tray-end:hover, .dl-tray-end.is-armed {
  border-color: var(--dl-gold-dim); background: rgba(217,183,106,0.10);
}
.dl-tray-end small {
  display: block; font-size: 13px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: none; color: var(--dl-ink-dim); margin-top: 3px;
}
/* Grip dots on a tray card: "this one moves" before anything is dragged. */
.dl-tray-grip {
  position: absolute; top: 7px; left: 50%; transform: translateX(-50%);
  z-index: 2; width: 34px; height: 15px; border-radius: 99px;
  background: rgba(12,12,14,0.82); border: 1px solid rgba(217,183,106,0.4);
  display: flex; align-items: center; justify-content: center; gap: 3px;
}
.dl-tray-grip i {
  width: 3px; height: 3px; border-radius: 99px; background: var(--dl-gold-dim);
}
/* The order number. Distinct from .dl-stack-badge (the ×N group count) — they
   sit on opposite corners so a grouped tile can show both. */
.dl-ord-badge {
  position: absolute; top: 9px; right: 9px; z-index: 3;
  min-width: 30px; height: 30px; padding: 0 8px; border-radius: 99px;
  background: rgba(12,12,14,0.9); border: 1.5px solid var(--dl-gold);
  color: var(--dl-gold-bright);
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 700; line-height: 1;
}
/* A tray tile that is currently picked (tap step 1 of tap-then-tap-an-end,
   or the first half of a two-card swap). */
.dl-tray-item.is-armed .dl-thumb, .dl-tray-item.is-armed .dl-ab {
  outline: 2px solid var(--dl-gold-bright); outline-offset: 3px;
  border-radius: 11px;
}

/* --- mini ability card (a TRIGGER is not a card object; it is one ability) --
   Art crop on top, the trigger's own text below, tinted in the source card's
   frame colour — the only thing that tells four stacked triggers apart at a
   glance. --dl-frame is set per frame letter; everything else is shared. */
.dl-ab {
  position: relative; display: flex; flex-direction: column;
  border-radius: 11px; overflow: hidden;
  border: 1px solid rgba(217,183,106,0.34);
  background: #121215; box-shadow: 0 6px 20px rgba(0,0,0,0.55);
  --dl-frame: #b9b3a6;
}
.dl-ab-W { --dl-frame: #efe4c4; }
.dl-ab-U { --dl-frame: #79b6e8; }
.dl-ab-B { --dl-frame: #b39ad6; }
.dl-ab-R { --dl-frame: #ec8b6d; }
.dl-ab-G { --dl-frame: #7fc48b; }
.dl-ab-M { --dl-frame: var(--dl-gold-bright); }
.dl-ab-C { --dl-frame: #c4c8cc; }
.dl-ab-art {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  background: #0d0d0f;
}
.dl-ab-txt {
  padding: 9px 11px 11px; text-align: left;
  border-top: 1px solid color-mix(in srgb, var(--dl-frame) 45%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--dl-frame) 13%, #121215) 0%, #121215 100%);
}
.dl-ab-name {
  display: block; color: var(--dl-frame);
  font-size: 13px; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; line-height: 1.25; margin-bottom: 5px;
}
/* R8-P11 (owner ruling, adaptive note sizing): this is the note line on the
   card-strip/tray chassis (order_triggers, discard-from-strip, etc.) — same
   clamp() treatment, same untouched compact override below. */
.dl-ab-text {
  display: block; color: var(--dl-ink);
  font-size: clamp(14px, 1vw, 16px); line-height: 1.32;
}
/* L10 grouping on an ability card. .dl-ab must keep overflow:hidden (it clips
   the art to the card's corners), which would clip pseudo-element edges — so
   the two cards peeking out behind are drawn as offset box-shadow layers
   instead, which nothing clips and which cost no extra nodes. */
.dl-ab.is-stack {
  box-shadow:
    5px 5px 0 -1px #121215, 5px 5px 0 0 rgba(217,183,106,0.34),
    11px 11px 0 -1px #121215, 11px 11px 0 0 rgba(217,183,106,0.34),
    0 6px 20px rgba(0,0,0,0.55);
}
.dl-tray-item:hover .dl-ab { transform: translateY(-4px); }
.dl-ab { transition: transform 0.12s ease; }

/* The trigger tray's footer carries TWO controls in the primary slot: the
   "keep this order" toggle (quiet, default off) and the filled-gold commit. */
.dl-modal .rules-order-foot {
  display: flex; align-items: center; gap: 22px; margin-left: 0;
}
.dl-modal .rules-order-remember { font-family: inherit; }

/* ===== responsive ===== */
/* S1 narrow: the modal must never exceed the viewport, on any screen. */
.dl-modal {
  max-height: min(var(--dl-modal-h), 90vh);
  max-width: 96vw;
}
/* Host fit, once the modal is capped above: the head and foot hold their size
   and the BODY is what scrolls, or .dl-modal's overflow:hidden clips the
   instruction line and the footer.
   The pane needs nothing here any more. It used to re-derive a fixed
   --dl-panel-w and then clamp it at 40%, which made the box NARROWER than the
   card on a short viewport — object-fit:cover answered that by cropping the
   name and mana cost off the left edge, and the standing patch was
   object-fit:contain (letterbox), in direct conflict with the full-bleed
   ruling. The pane now carries the card's own aspect-ratio (see the chassis
   block above), so it tracks the ACTUAL rendered height at every viewport and
   cover fills it exactly — no crop, no letterbox, no override needed. */
.dl-body { overflow-y: auto; }
/* B7 G7 fix: this used to also be unconditional (no @media around it), which
   let .dl-foot wrap at ANY width — a long secondary (FAIL TO FIND) plus the
   readout plus a 280px-min primary is ~680px against a ~654px content column
   at 1240px modal width, close enough to trip it into three stacked rows
   (library_search_fail_to_find). Wrapping belongs to the narrow/short cases
   below only; the base rule (line ~103) shrinks the readout first instead. */

/* S1 narrow (rule): a WIDE-but-SHORT landscape phone (844x390) never hits the
   780px width rule but is just as tight on the vertical axis a title reserving
   2.16em eats it. Hide the card panel on short height too, not just narrow
   width. */
@media (max-width: 780px), (max-height: 460px) {
  .dl-card-panel { display: none; }
  .dl-content { width: 100%; }
  .dl-foot { flex-wrap: wrap; }
}

/* ===== mobile shell (S1 narrow, landscape phone) ===== */
/* At <=460px tall the base chassis (42px title reserving 2 lines, full-size
   rows/buttons) doesn't fit even with the card panel gone above: return_mode_
   cancel's title alone ran 136px tall, leaving .dl-body zero height and
   clipping every option off the bottom of the (correctly capped, 90vh) modal.
   Collapse the chrome instead: one-line ellipsized title/sub, tighter rows,
   44px-min buttons (touch target floor) at a smaller footprint. */
@media (max-height: 460px) {
  .dl-title {
    font-size: 22px; min-height: 0; line-height: 1.2;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .dl-sub {
    font-size: 14px; margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .dl-content { padding: 14px 40px 10px 14px; }
  /* .dl-body's base rule centers its content (justify-content:center) so a
     picker/option list too tall for the shrunk box overflows EQUALLY above
     and below — Chrome's default scroll position for centered flex overflow
     only exposes the bottom half, permanently hiding the top row(s) (see the
     .dl-opt-row compaction comment above for the same class of bug). Anchor
     to the top instead so overflow-y:auto's default scroll position (0)
     starts exactly where the content starts — nothing is hidden before the
     player scrolls. */
  .dl-body { justify-content: flex-start; }
  .dl-body > * { margin-block: 0; }   /* same opt-out as the four above */
  /* colors_multi's 5 no-desc rows need every spare pixel to fit under a
     shrunk body without scrolling — measured against .dl-body's real
     available height at 844x390 (203px): 5 rows at the wider 8px padding/
     gap ran 262px, clipping the last two under the pinned footer. */
  .dl-options { gap: 3px; }
  .dl-opt-row { padding: 10px 60px 10px 14px; }
  /* R8-P10 bugfix: 4px top/bottom measured 27px tall at 14px title/1.2
     line-height (some_type_card's 5 permanent-type rows) — 1px under the
     32x28 hit-test floor the layout gate enforces, failing ALL 5 rows even
     though every other physical-input check (paint, containment, scroll
     reachability) passed. 5px clears it at 29px; colors_multi (the case that
     originally drove 4px here) has since moved to the mana-card chassis and
     no longer renders through .dl-opt-row, so nothing else needed the tighter
     value. */
  .dl-opt-row:not(:has(.dl-opt-desc)) { padding: 5px 60px 5px 14px; }
  .dl-opt-title { font-size: 14px; }
  .dl-opt-desc { font-size: 13px; margin-top: 4px; }
  /* the row icon rides the compacted row, not the 46px canon disc — a
     no-desc row is only ~22px of content tall here. */
  .dl-row-icon { left: 12px; width: 26px; height: 26px; }
  .dl-row-icon::before { inset: 5px; }
  .dl-opt-row:has(.dl-row-icon) { padding-left: 48px; }
  .dl-btn { padding: 10px 20px; min-height: 44px; font-size: 14px; box-sizing: border-box; }
  .dl-foot { margin-top: 10px; gap: 10px; }
  .dl-close { top: 10px; right: 10px; font-size: 20px; }
  /* G5: the tray's own chrome shrinks with everything else — the strip keeps
     the cards, the end labels keep the axis, the padding goes. */
  .dl-tray { gap: 6px; }
  .dl-tray-strip { padding: 8px 8px 6px; }
  .dl-tray-end { font-size: 13px; padding: 2px 8px; }
  .dl-tray-end small { font-size: 11px; margin-top: 1px; }
  .dl-ab-text { font-size: 12px; }
  .dl-ab-name { font-size: 11px; }
}

/* discard_to_hand_size class of bug: the RAIL "native choice" panel (hand
   picks that stay in place, .rules-decision.rules-native-choice, rendered by
   _rulesNativeChoiceControls in the .rules-mobile-decision-host aside) is a
   DIFFERENT widget from the .dl-modal popout chassis above, but shares the
   same S1 narrow-viewport failure mode: .rules-decision is display:flex with
   flex-wrap:wrap (styles.css) but gets flex-direction:column from
   .cob-rail-card .rules-decision — wrap on a COLUMN axis means "start a new
   COLUMN to the right" once content is taller than the box, not "wrap to a
   new row". In the narrow aside that silently shoves .rules-acts (and its
   confirm button) into an invisible second column past the right edge of the
   184px-wide panel — a control that's present, painted, and completely
   unreachable. Force one column; the panel's own overflow-y:auto (mobile.css)
   already makes the excess height scroll instead. */
.rules-mobile-decision-host .rules-decision { flex-wrap: nowrap; }

/* return_mode_cancel class of bug: the .dl-modal popout escapes its narrow
   aside host visually via position:absolute;inset:0 (relative to the fixed
   .rules-mobile-shell), so on a short viewport it extends down over the
   .rules-mobile-hand-shelf grid row below — but .rules-mobile-decision-host
   is itself a GRID ITEM with z-index:15 (mobile.css), and per the grid
   spec that gives it a real stacking context even though it's position:static.
   The popout's own z-index:120 is trapped INSIDE that context: it wins
   against siblings inside the aside, but the aside-vs-hand-shelf comparison
   happens one level up at 15 vs the shelf's z-index:30, so the shelf paints
   over the popout and swallows its clicks/taps. Lift the aside above the
   shelf only while it is actually hosting a popout. */
.rules-mobile-decision-host:has(.rules-choice) { z-index: 40; }

/* colors_multi class of bug: the dl-chassis footer's primary/cancel buttons
   carry .rules-act too (shared class the delegated click handler binds to),
   and since the .dl-modal popout is a DOM descendant of .rules-mobile-
   decision-host (see the flex-wrap fix above for why), they also match
   mobile.css's `.rules-mobile-shell .rules-mobile-decision-host .rules-act
   { width: 100%; ... }` (0,3,0) — a rule written for the small rail-only
   native-choice buttons. Forced to 100% of the popout's ~750px-wide footer,
   the primary button can no longer sit beside the n-of-m readout, wraps onto
   its own line, and the now-taller footer overlaps the last option row. Give
   the dl-chassis buttons back their own (min-width-driven) size — matching
   the offending selector's 3-class specificity so this actually wins. */
.rules-mobile-shell .rules-mobile-decision-host .dl-btn {
  width: auto; min-width: 0; margin-top: 0; white-space: nowrap;
}

/* ===== G1 · on-board multi-select (L9 colour semantics) =====
   L9: GOLD ring = "there is something you can do here" (playable card,
   activatable ability); RED ring = a COMBAT role (selected attacker,
   assigned blocker, incoming attacker). Combat tint wins while assigned;
   the legal-action classes stay intact so gold returns after combat.
   The chassis check disc (L3) is NEVER used on the board — the mark is the
   ring plus a role badge (⚔ / 🛡) on the existing .dl-badge-actions anchor.
   Defined ONCE here: the manual-mana-payment build reuses the same classes.
   Card role badges and A2 colors are shared with phones; the surrounding
   desktop combat controls keep their composition-specific rules. */
:root {
  --dl-red:        #e0564a;
  --dl-red-bright: #ff8a7a;
  --dl-red-faint:  rgba(224,86,74,0.55);
}

.cob-v3:not(.rules-mobile-shell) .pt-card.dl-ring-gold {
  box-shadow: 0 0 0 2px var(--dl-gold), 0 0 18px 3px rgba(217,183,106,0.5),
              0 6px 16px rgba(0,0,0,0.7);
}

/* The badge and the cue straddle the card's edge, and `.cob-v3 .pt-card`
   (0,3,0) clips to the rounded frame — it out-specifies the chip renderer's
   own `overflow: visible`, so a corner mark silently renders as a sliced pill.
   `:has()` takes the specificity of its argument, which puts this at (0,4,0)
   and lets exactly the marked cards opt back out. */
.cob-v3 .pt-card:has(> .dl-badge-role),
.cob-v3 .pt-card:has(> .dl-role-cue),
.cob-v3 .pt-card:has(> .dl-unblocked),
.cob-v3 .pt-card:has(> .dl-defender-tag),
.cob-v3 .pt-card:has(> .dl-attack-targets) { overflow: visible; }

/* The role badge — same corner as .dl-badge-actions, sized off the chip's
   own --w so it reads on a 78px opponent chip and a 99px one alike. */
.cob-v3:not(.rules-mobile-shell) .dl-badge-role,
.cob-v3.rules-mobile-shell .dl-badge-role {
  position: absolute; top: -9px; left: -9px; z-index: 6;
  box-sizing: border-box; flex: 0 0 auto;
  width: max(20px, calc(var(--w, 92px) * 0.24));
  height: max(20px, calc(var(--w, 92px) * 0.24));
  border-radius: 50%;
  background: linear-gradient(180deg, var(--dl-red-bright) 0%, #b53a2e 100%);
  border: 1px solid #8d2c22; color: #2a0906;
  display: flex; align-items: center; justify-content: center;
  font-size: max(10px, calc(var(--w, 92px) * 0.13)); line-height: 1;
  box-shadow: 0 3px 12px rgba(0,0,0,0.65); pointer-events: none;
}
.cob-v3:not(.rules-mobile-shell) .dl-badge-role i,
.cob-v3.rules-mobile-shell .dl-badge-role i { font-size: inherit; }
/* Phone ranks scroll and clip at their edges. Keep the same non-interactive
   badge inside the card rather than changing the rank's geometry. */
.cob-v3.rules-mobile-shell .dl-badge-role { top: 2px; left: 2px; }
/* Selection gains a combat identity, not the legacy attack lift/rotation. */
.cob-v3.rules-mobile-shell .pt-card.rules-attacking:not(.pt-tapped):not(.tapped) {
  transform: none;
}

/* L2: the affordance cue is something the GAME says, so it is serif, not
   sans chrome. It hangs off the card like the mockup's pill. */
.cob-v3:not(.rules-mobile-shell) .dl-role-cue,
.cob-v3:not(.rules-mobile-shell) .dl-unblocked {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: -18px; z-index: 6; pointer-events: none; white-space: nowrap;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 9.5px; letter-spacing: 0.09em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 99px;
  background: rgba(10,10,11,0.92);
}
.cob-v3:not(.rules-mobile-shell) .dl-role-cue {
  color: var(--dl-red-bright); border: 1px solid var(--dl-red-faint);
}
/* "Can block" is DEFENSIVE — calm steel-blue, not the aggressive combat red,
   so it doesn't read as a threat/attacker (esp. beside "Summoning Sickness",
   which is rules-legal to block through). */
.cob-v3:not(.rules-mobile-shell) .dl-role-cue.dl-role-block {
  color: #9cc4f0; border-color: rgba(140,194,255,0.42);
}
/* An attacker nothing is blocking — states the consequence, not the state. */
.cob-v3:not(.rules-mobile-shell) .dl-unblocked {
  top: -18px; bottom: auto;
  color: #ffd9d2; background: rgba(120,24,16,0.94);
  border: 1px solid var(--dl-red);
}

/* Summoning sickness during a declare step: dimmed, with the ⏳ the mockup
   uses. The banner element already exists on every chip (.csick). */
.cob-v3:not(.rules-mobile-shell):has(.rules-attack-panel) .you-board .pt-card.pt-sick {
  filter: saturate(0.4) brightness(0.62); opacity: 0.7;
}
.cob-v3:not(.rules-mobile-shell):has(.rules-attack-panel) .you-board .pt-card.pt-sick .csick::before {
  content: "⏳ ";
}

/* Eligible-but-unpicked = a FAINT red glow (L9: combat is red, so the old
   gold "ready" pulse said the wrong thing). Overrides styles.css's
   cob-atk-ready by naming a different animation — a keyframed box-shadow
   beats any static declaration, so it has to be replaced, not out-specified. */
.cob-v3:not(.rules-mobile-shell) .pt-card.rules-can-attack {
  animation-name: dl-combat-ready;
}
.cob-v3:not(.rules-mobile-shell) .you-board .pt-card.rules-can-block {
  box-shadow: 0 0 0 1.5px rgba(140,194,255,0.5),
              0 0 10px rgba(120,170,235,0.35), 0 8px 18px rgba(0,0,0,0.7);
}
@keyframes dl-combat-ready {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(224,86,74,0.5),
               0 0 8px rgba(224,86,74,0.28), 0 8px 18px rgba(0,0,0,0.7); }
  50%      { box-shadow: 0 0 0 2px rgba(224,86,74,0.85),
               0 0 18px rgba(224,86,74,0.55), 0 8px 18px rgba(0,0,0,0.7); }
}

/* Defending players/permanents are board-native tap targets. During split
   attacks every defender in use shows its assigned attacker count. */
.cob-v3:not(.rules-mobile-shell) .cob-player.dl-defender,
.cob-v3:not(.rules-mobile-shell) .pt-card.dl-defender {
  position: relative;
  box-shadow: 0 0 0 2px var(--dl-red), 0 0 22px 4px rgba(224,86,74,0.45),
              0 8px 22px rgba(0,0,0,0.65);
  border-color: var(--dl-red);
}
.cob-v3:not(.rules-mobile-shell) [data-atk-def] { cursor: pointer; }
.cob-v3:not(.rules-mobile-shell) [data-atk-def]:not(.dl-defender) {
  box-shadow: 0 0 0 1.5px var(--dl-red-faint);
}
/* The identity box's own frame (.cob-rail-id) clips to 3px of headroom, which
   sliced the tag in half. Un-clip only the box that is actually carrying it. */
.cob-v3:not(.rules-mobile-shell) .cob-rail-id:has(.dl-defender-tag) {
  overflow: visible;
}
.cob-v3:not(.rules-mobile-shell) .dl-defender-tag {
  position: absolute; top: -9px; right: 10px; z-index: 7;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase;
  color: #2a0906; background: var(--dl-red-bright);
  border: 1px solid #8d2c22; border-radius: 99px; padding: 2px 9px;
  pointer-events: none;
}
.cob-v3:not(.rules-mobile-shell) .dl-attack-targets {
  position: absolute; left: 50%; bottom: -10px; z-index: 9;
  max-width: calc(100% + 32px); transform: translateX(-50%);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 3px 8px; border: 1px solid #8d2c22; border-radius: 999px;
  background: #180b09ee; color: var(--dl-red-bright);
  box-shadow: 0 5px 12px #000a; cursor: pointer;
  font: 800 9px/1.1 Inter, sans-serif;
}

/* Z1: the ask panel is the ONLY commit surface for a board selection. Same
   .dl-ask chassis as the mulligan/confirm asks — the combat family has to
   read as the same object, so this block only adds the rail's spacing. */
/* The commit surface remains fixed while all creature selection lives on board. */
.cob-v3 .cob-decision-box .dl-ask-combat {
  margin-top: 0.5rem; flex: 0 0 auto; padding: 0.6rem 0.7rem;
}
.cob-v3 .cob-decision-box .dl-ask-combat .dl-ask-cap { margin-bottom: 0.35rem; }
.cob-v3 .cob-decision-box .dl-ask-combat .dl-ask-btns { margin-top: 0.55rem; }
.cob-v3 .cob-decision-box .dl-ask-combat .dl-ask-extra { margin-top: 0.4rem; }
/* S1 disabled-primary: the chassis already has the state, but the rail
   re-skins .rules-act at 3 classes, which outranks .dl-btn-confirm:disabled
   and left a 0-of-n primary looking live. Match the rail's specificity. */
.cob-v3 .cob-decision-box .dl-ask .dl-btn-confirm[disabled] {
  background: linear-gradient(180deg, #4a453c 0%, #3b382f 100%);
  border-color: #55503f; color: #8d8879; box-shadow: none;
  cursor: not-allowed;
}
.cob-v3 .cob-decision-box .dl-ask-combat .dl-ask-extra .rules-act {
  width: 100%; justify-content: center;
}

/* ===== G4 · manual mana payment (LOOK_RULES "G4 APPROVED (v2)") =====
   Every untapped source wears a chassis-style chip over its lower third with
   the REAL mana symbols it produces; a dual shows one pip per colour and each
   pip is its own tap target. A source tapped for this payment keeps the chip,
   dimmed, with the paid pip ticked. NO floating board labels — the words live
   in the sidebar ask panel (Z1), and the LAST PIP COMMITS (no PAY confirm).
   Scoped away from the phone composition, which keeps its own look. */
.cob-v3:not(.rules-mobile-shell) .dl-mana-chip {
  position: absolute; bottom: 9%; z-index: 6;
  /* Centred in the card's VISIBLE strip: a back-rank row that has to overlap
     publishes how much its right-hand neighbour covers (--cob-ov, set by
     _cobFit), and a chip centred on the geometric middle of a 74%-covered
     land is a chip nobody can see or tap. */
  left: calc(50% - var(--cob-ov, 0px) / 2);
  transform: translateX(-50%);
  display: flex; align-items: center;
  gap: max(3px, calc(var(--w, 92px) * 0.045));
  padding: max(3px, calc(var(--w, 92px) * 0.045))
           max(4px, calc(var(--w, 92px) * 0.06));
  border-radius: max(6px, calc(var(--w, 92px) * 0.08));
  background: linear-gradient(180deg, #1b1712 0%, #121114 100%);
  border: 1.5px solid var(--dl-gold-dim);
  box-shadow: 0 6px 18px rgba(0,0,0,0.6);
}
/* Already tapped for this cost: same chip, dimmed, with the paid pip ticked. */
.cob-v3:not(.rules-mobile-shell) .dl-mana-chip.is-paid { opacity: 0.55; }

.cob-v3:not(.rules-mobile-shell) .dl-mana-pip {
  position: relative; display: block; line-height: 0;
  width: max(16px, calc(var(--w, 92px) * 0.20));
  height: max(16px, calc(var(--w, 92px) * 0.20));
  border-radius: 50%; cursor: pointer;
  transition: transform .12s, box-shadow .12s;
}
.cob-v3:not(.rules-mobile-shell) .dl-mana-pip img {
  width: 100%; height: 100%; display: block;
}
.cob-v3:not(.rules-mobile-shell) .dl-mana-pip:hover {
  transform: scale(1.16);
  box-shadow: 0 0 0 2px var(--dl-gold-bright);
}
/* The colour this source was tapped for. */
.cob-v3:not(.rules-mobile-shell) .dl-mana-pip.is-chosen {
  box-shadow: 0 0 0 2px var(--dl-gold-bright), 0 0 10px rgba(217,183,106,0.6);
}
.cob-v3:not(.rules-mobile-shell) .dl-mana-tick {
  position: absolute; right: -5px; bottom: -5px;
  width: max(11px, calc(var(--w, 92px) * 0.115));
  height: max(11px, calc(var(--w, 92px) * 0.115));
  border-radius: 50%; line-height: 1; pointer-events: none;
  background: linear-gradient(180deg, var(--dl-gold-bright) 0%, #c69a4e 100%);
  border: 1px solid #a8813d; color: #241300;
  display: flex; align-items: center; justify-content: center;
  font-size: max(7px, calc(var(--w, 92px) * 0.075)); font-weight: 700;
}
/* A source that makes more than one mana per tap ("×2"). */
.cob-v3:not(.rules-mobile-shell) .dl-mana-amt {
  font-family: Georgia, "Times New Roman", serif;
  font-size: max(8px, calc(var(--w, 92px) * 0.10));
  color: var(--dl-gold-bright); padding-left: 1px;
}

/* L9: a mana source is ACTIONABLE, so its ring is GOLD — never combat red.
   Faint while it waits, full once it has been tapped for this cost. */
.cob-v3:not(.rules-mobile-shell) .pt-card:has(> .dl-mana-chip) {
  box-shadow: 0 0 0 1.5px var(--dl-gold-faint),
              0 0 10px rgba(217,183,106,0.28), 0 8px 18px rgba(0,0,0,0.7);
}
.cob-v3:not(.rules-mobile-shell) .pt-card:has(> .dl-mana-chip.is-paid) {
  box-shadow: 0 0 0 2px var(--dl-gold), 0 0 18px 3px rgba(217,183,106,0.5),
              0 6px 16px rgba(0,0,0,0.7);
}

/* Z1: the rail ask panel is the only commit surface. Same .dl-ask chassis as
   the combat family — the cost readout is the one extra part. */
.cob-v3 .cob-decision-box .dl-ask-mana {
  margin-top: 0.5rem; flex: 0 0 auto; padding: 0.6rem 0.7rem;
}
.cob-v3 .cob-decision-box .dl-ask-mana .dl-ask-cap { margin-bottom: 0.45rem; }
.cob-v3 .cob-decision-box .dl-ask-mana .dl-ask-btns { margin-top: 0.6rem; }
.dl-mana-readout {
  display: flex; align-items: center; justify-content: center;
  gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--dl-gold-faint); border-radius: 99px;
  padding: 6px 14px; background: #0f0f11;
}
.dl-mana-cost { display: flex; align-items: center; gap: 6px; }
.dl-cost-pip { display: block; line-height: 0; }
.dl-cost-pip img { width: 24px; height: 24px; display: block; }
/* Paid pips DIM — the readout empties as the cost fills. */
.dl-cost-pip.is-paid { opacity: 0.32; }
.dl-mana-sep { width: 1px; height: 20px; background: var(--dl-gold-faint); }
.dl-mana-count {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dl-gold);
}
/* The "Paying for <card>" context header sheds the old cost line + numbered
   instructions — both now live in the ask panel below it. */
.cob-v3 .cob-decision-box .rules-mana-for-card { flex: 0 0 auto; }

/* ===== G2 · board single target + G1 · non-combat board multi-select =====
   L9: RED is combat-ONLY, so every other board job — a legal target, a
   sacrifice, a tap-cost, proliferate, convoke — rings GOLD and wears a gold
   role badge. The chassis check disc (L3) is never used on the board.
   These rules re-colour the arena's existing candidate hooks
   (.rules-can-target / .is-picked, painted inline by the card renderers) and
   out-specify styles.css's older vitality-GREEN treatment by one class.
   Scoped away from the phone composition, which keeps its current look. */
.cob-v3:not(.rules-mobile-shell) .pt-card.rules-can-target,
.cob-v3:not(.rules-mobile-shell) .cob-stack-card.rules-can-target {
  cursor: pointer;
  box-shadow: 0 0 0 2px var(--dl-gold), 0 0 15px 3px rgba(217,183,106,0.42),
              0 8px 18px rgba(0,0,0,0.7);
}
.cob-v3:not(.rules-mobile-shell) .pt-card.rules-can-target:hover {
  box-shadow: 0 0 0 2.5px var(--dl-gold-bright),
              0 0 24px 5px rgba(217,183,106,0.6), 0 8px 18px rgba(0,0,0,0.7);
}
/* Chosen (a multi ask). The mark is the ring + the badge the painter adds —
   styles.css's square gold `outline` was the disc's cousin and has to go. */
.cob-v3:not(.rules-mobile-shell) .pt-card.rules-can-target.is-picked,
.cob-v3:not(.rules-mobile-shell) .cob-player.rules-can-target.is-picked {
  outline: none;
  box-shadow: 0 0 0 2.5px var(--dl-gold-bright), 0 0 0 5px rgba(217,183,106,0.32),
              0 0 22px 4px rgba(217,183,106,0.5), 0 8px 20px rgba(0,0,0,0.7);
}
.cob-v3:not(.rules-mobile-shell) .cob-player.rules-can-target {
  cursor: pointer; border-color: var(--dl-gold);
  box-shadow: 0 0 0 2px var(--dl-gold), 0 0 20px 4px rgba(217,183,106,0.4),
              0 8px 22px rgba(0,0,0,0.65);
}
.cob-v3:not(.rules-mobile-shell) .cob-player.rules-can-target:hover {
  box-shadow: 0 0 0 3px var(--dl-gold-bright),
              0 0 28px 6px rgba(217,183,106,0.55), 0 8px 22px rgba(0,0,0,0.65);
}

/* The gold half of the role badge / cue pair (the red half is the combat
   block above; geometry is shared, only the palette differs). */
.cob-v3:not(.rules-mobile-shell) .dl-badge-role.is-gold,
.cob-v3.rules-mobile-shell .dl-badge-role.is-gold {
  background: linear-gradient(180deg, var(--dl-gold-bright) 0%, #a8813a 100%);
  border-color: #7d5f26; color: #2b1f06;
}
.cob-v3:not(.rules-mobile-shell) .dl-role-cue.is-gold {
  color: var(--dl-gold-bright); border-color: var(--dl-gold-faint);
}
/* G2's consequence tag: what THIS candidate gets, on the candidate, above it
   — the same words the arc is carrying and the sidebar sentence restates. */
.cob-v3:not(.rules-mobile-shell) .dl-target-tag {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: -18px; z-index: 8; pointer-events: none; white-space: nowrap;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 9.5px; letter-spacing: 0.09em; text-transform: uppercase;
  padding: 2px 9px; border-radius: 99px;
  color: #2b1f06; background: var(--dl-gold-bright);
  border: 1px solid #7d5f26;
}
/* On a player identity the tag sits where the DEFENDER tag does — the box is
   wide, and a centred pill would collide with the life number. */
.cob-v3:not(.rules-mobile-shell) .cob-player .dl-target-tag {
  left: auto; right: 10px; transform: none; top: -9px;
  letter-spacing: 0.12em;
}
/* Same clipping story as the combat marks: `.cob-v3 .pt-card` (0,3,0) clips to
   the rounded frame, so exactly the tagged objects opt back out. */
.cob-v3 .pt-card:has(> .dl-target-tag),
.cob-v3 .cob-player:has(> .dl-target-tag),
.cob-v3 .cob-rail-id:has(.dl-target-tag) { overflow: visible; }

/* G2: everything ILLEGAL recedes while a board pick is open, so the lit
   candidates are the only thing the eye lands on. Cards that are still
   playable keep their own gold — dimming them would lie about what is legal. */
.cob-v3.rules-board-pick:not(.rules-mobile-shell) .cob-bf
  .pt-card:not(.rules-can-target):not(.rules-playable):not([data-mana-src]) {
  filter: saturate(0.35) brightness(0.6); opacity: 0.62;
}

/* Z1 rail ask. Same .dl-ask chassis as the combat + mulligan asks (they must
   read as one object); this block only adds the rail's own spacing, exactly
   like .dl-ask-combat above it. */
.cob-v3 .cob-decision-box .dl-ask-board {
  margin: 0; flex: 0 0 auto; padding: 0.7rem 0.8rem;
}
.cob-v3 .cob-decision-box .dl-ask-board .dl-ask-cap { margin-bottom: 0.4rem; }
.cob-v3 .cob-decision-box .dl-ask-board .dl-ask-hint { font-size: 1rem; }
.cob-v3 .cob-decision-box .dl-ask-board .dl-ask-btns { margin-top: 0.7rem; }
/* One button (a mandatory multi ask, or G2's Cancel-only single pick) fills
   the row rather than sitting in half of a two-column grid. */
.dl-ask-btns.is-one { grid-template-columns: 1fr; }
/* Secondary lines: the "where to tap" nudge (shown only until something is
   picked) and the n-selected readout the transport contract reads. */
.cob-v3 .cob-decision-box .dl-ask-board .dl-ask-sub,
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-status {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
  text-align: center; color: var(--dl-ink-dim); margin-top: 0.35rem;
}
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source,
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-consequence {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 0.78rem; line-height: 1.45; text-align: center;
  color: var(--dl-ink-dim); margin-top: 0.45rem;
}
/* B7 G2 fix: the source line carries a small art thumbnail. It reads as
   identity context either way, but it also doubles as _rulesDrawTargetArc's
   fallback anchor ([data-arc-source-anchor]) when the asking card isn't
   findable on the board/hand/stack — so the arc has somewhere honest to
   start instead of just not being drawn. */
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source {
  display: flex; align-items: center; gap: 8px; text-align: left;
  margin-top: 0.55rem; opacity: 0.85;
}
/* Owner ruling (POST-R8): the source is subordinate to the action line, so the
   thumbnail shrinks and its border mutes — it stays the arc anchor, just quiet. */
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source-art {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 5px;
  object-fit: cover; border: 1px solid var(--dl-gold-faint);
}
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source-text {
  min-width: 0;
}
/* "Asked by <source>" — an attribution cue, not a gold headline. */
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source-by {
  display: block; font-size: 0.68rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--dl-ink-dim);
}
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source-by b {
  color: var(--dl-ink-dim); font-weight: 600;
}
/* The source's own rules text is reference, not the ask — small and dim so the
   action line above it is unmistakably the dominant element. */
.cob-v3 .cob-decision-box .dl-ask-board .rules-native-choice-source-rules {
  display: block; font-size: 0.68rem; line-height: 1.35;
  color: var(--dl-ink-dim); opacity: 0.75; margin-top: 1px;
}
/* The rail re-skins .rules-act at 3 classes, which outranks the chassis
   buttons — the same fight .dl-ask-combat already documents. */
.cob-v3 .cob-decision-box .dl-ask-board .rules-act.dl-btn {
  width: 100%; justify-content: center;
}
/* Convoke/improvise keep G4's own .dl-ask-mana panel (this integration), so
   .dl-ask-board never carries .rules-mana-acts or a .dl-ask-extra row — the
   overrides those needed are gone with them. */
/* The target arc must land ON the object it points at. The floating rail is
   z-80, and the arena's shared arrow layer is z-30, so a PLAYER target's
   arrowhead was clipped at the rail's left edge — the one arc that has to
   cross it. Only the target layer is lifted: the combat beams belong to G1.
   Still below the .rules-choice popout overlay (z-120), which must cover it. */
.cob-v3 .rules-arrows-tgt { z-index: 90; }

/* ===== G1c: the enlist pairing on the board =====================
   LOOK_RULES.md "R2 APPROVED + L11". The helper picked at declare time reads
   as TAPPED with a thin red line back to its attacker (the beam SVG), and the
   attacker carries its boosted power as "P+H". L9 palette: red is the combat
   role, gold is "you can do something with this" (the CAN ENLIST cue reuses
   .dl-role-cue.is-gold, already defined above). */
.cob-v3 .pt-card:has(> .dl-enlist-pt),
.cob-v3 .pt-card:has(> .dl-enlist-tap) { overflow: visible; }
.cob-v3:not(.rules-mobile-shell) .pt-card.rules-enlist-helper {
  filter: saturate(0.75) brightness(0.82);
  transform: rotate(7deg);
}
.cob-v3:not(.rules-mobile-shell) .dl-enlist-tap {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: -18px; z-index: 6; pointer-events: none; white-space: nowrap;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 9.5px; letter-spacing: 0.09em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 99px;
  background: rgba(10,10,11,0.92);
  color: var(--dl-red-bright); border: 1px solid var(--dl-red-faint);
}
/* The boosted power, stated the way the mockup states it ("2+3"): the
   attacker's own power plus the helper's, not a silent new number. */
.cob-v3:not(.rules-mobile-shell) .dl-enlist-pt {
  /* B8c residue: -9px clipped the card's rotated name plate by a few px at
     the corner. A few more px of clearance, nothing else. */
  position: absolute; top: -15px; right: -9px; z-index: 6; pointer-events: none;
  font-family: Georgia, "Times New Roman", serif; font-weight: 700;
  font-size: max(10px, calc(var(--w, 92px) * 0.13)); line-height: 1;
  padding: 3px 7px; border-radius: 99px;
  color: #ffd9d2; background: rgba(120,24,16,0.94);
  border: 1px solid var(--dl-red);
  box-shadow: 0 3px 12px rgba(0,0,0,0.65);
}
/* #5 honesty (G_REVIEW B8): the pairing is a client-side guess Forge never
   confirmed — dashed gold, not the solid red "this already happened" look
   the tapped/boosted states above use for real board state. */
.cob-v3:not(.rules-mobile-shell) .dl-enlist-tap.is-pending {
  color: var(--dl-gold-bright); border-style: dashed; border-color: var(--dl-gold-dim);
}
.cob-v3:not(.rules-mobile-shell) .dl-enlist-pt.is-pending {
  color: var(--dl-gold-bright); background: rgba(56,44,10,0.92);
  border-style: dashed; border-color: var(--dl-gold-dim);
}

/* Gate #48 sibling: an action-menu row may state its own consequence
   (the combat riders do). Opt-in — rows without .dl-pop-desc are unchanged. */
.dl-pop-desc {
  display: block;
  margin-top: 3px;
  font-family: var(--dl-body, inherit);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: 0;
  text-transform: none;
  color: rgba(232, 224, 208, 0.72);
  white-space: normal;
}

/* A2: one soft inward action glow above the art, independent of grounding
   shadows and sibling stacking. Solo's always-affordable flag is not a legal
   action; only its opt-in rules-assist cue receives gold. */
:root {
  --card-action-glow: inset 0 0 0 3px #ffd67c,
    inset 0 0 16px 4px #ffd67566, inset 0 0 42px 12px #efa42b66;
}
/* The approved title tint is a separate, persistent action cue alongside A2. */
:is(.cob-v3, .rules-zoneview, .codex-table)
  .pt-card:is(.rules-playable, .playable, .pt-rules-assist, .dl-ring-gold) > :is(.cob-hand-label, .chead),
.cob-v3:not(.pt-solo) .pt-card.pt-affordable > :is(.cob-hand-label, .chead) {
  background: linear-gradient(110deg, #59441b, #292216);
  box-shadow: inset 3px 0 #f2c665, inset 0 2px #f2c665;
}
:is(.cob-v3, .rules-zoneview, .codex-table)
  :is(.pt-card, .land-card, .token-card, .commander-card):is(.rules-playable, .playable, .pt-rules-assist, .dl-ring-gold):not(.cob-stack-target-glow)::before,
.cob-v3:not(.pt-solo) .pt-card.pt-affordable:not(.cob-stack-target-glow)::before,
.cob-v3 .cob-railpile:is(.rules-playable, .can-cast):not(.cob-stack-target-glow) > .cob-railpile-face::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--card-action-glow);
  pointer-events: none;
  z-index: 5;
}
/* Replace the old outer gold haze. Targeting keeps its own meaning, and the
   combat rules below temporarily replace gold with the approved red A2. */
:is(.cob-v3, .rules-zoneview, .codex-table)
  :is(.pt-card, .land-card, .token-card, .commander-card):is(.rules-playable, .playable, .pt-rules-assist, .dl-ring-gold):not(.dl-ring-red, .dl-defender, .rules-attacking, .rules-attacking-me, .rules-blocking, .rules-block-assigned, .rules-block-covered):not(.cob-stack-target-glow, .rules-can-attack, .rules-can-block, .rules-can-target, .rules-block-selected),
.cob-v3:not(.pt-solo) .cob-hand .pt-card.pt-affordable,
.cob-v3 .cob-railpile:is(.rules-playable, .can-cast):not(.cob-stack-target-glow) {
  box-shadow: var(--shadow-card, -3px 5px 9px #000a);
}

/* Approved red A2: the same inward geometry as gold, with the red title
   tint. Cover selected/incoming attackers, assigned blockers and attacked
   permanents; player identity boxes retain their Defender label/frame.
   Keep the glow above art and below role badges, without changing hit areas,
   card stacking or the shared, rotation-corrected grounding shadow. */
:root {
  --card-combat-glow: inset 0 0 0 3px #ff796b,
    inset 0 0 16px 4px #ff766866, inset 0 0 42px 12px #e13e3266;
}
.cob-v3:not(.rules-mobile-shell) .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered):not(.cob-stack-target-glow),
.cob-v3.rules-mobile-shell .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered):not(.cob-stack-target-glow) {
  box-shadow: var(--shadow-card, -8px 14px 24px #0008);
}
.cob-v3:not(.rules-mobile-shell) .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered):not(.cob-stack-target-glow)::before,
.cob-v3.rules-mobile-shell .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered):not(.cob-stack-target-glow)::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--card-combat-glow);
  pointer-events: none;
  z-index: 5;
}
.cob-v3:not(.rules-mobile-shell) .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered) > :is(.cob-hand-label, .chead),
.cob-v3.rules-mobile-shell .pt-card:is(.dl-ring-red, .dl-defender,
  .rules-attacking, .rules-attacking-me, .rules-blocking,
  .rules-block-assigned, .rules-block-covered) > :is(.cob-hand-label, .chead) {
  color: #fff1ed;
  background: linear-gradient(110deg, #5d2721, #2b1816);
  border-color: #be655d88;
  box-shadow: inset 3px 0 #ed7568, inset 0 2px #ed7568;
}
