/* WINGED — deck builder. Kept in its own stylesheet so the builder can be
   dropped in without touching the game shell's CSS. Matches the gold-on-slate
   language of the board. */

.dbwrap{position:fixed;inset:0;z-index:150;display:flex;align-items:center;justify-content:center;
  padding:18px;background:rgba(3,5,8,.9);backdrop-filter:blur(5px)}
.dbbox{display:flex;flex-direction:column;width:min(1180px,100%);height:min(760px,100%);
  border-radius:14px;overflow:hidden;
  background:linear-gradient(180deg,rgba(28,40,50,.98),rgba(13,20,27,.99));
  border:2px solid var(--gold-d);box-shadow:0 26px 74px rgba(0,0,0,.92),inset 0 1px 0 rgba(255,255,255,.08)}

/* The right padding is the `?` button's seat, not decoration: `.dbsub` runs the
   full width of the header and would otherwise wrap underneath it. */
.dbhead{padding:16px 62px 12px 20px;border-bottom:1px solid rgba(224,185,95,.2);flex:none;position:relative}
.dbhead h2{margin:0 0 4px;font-size:22px;letter-spacing:2px;font-weight:normal;
  background:linear-gradient(180deg,var(--gold-l),var(--gold) 50%,#a57d2c);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.dbsub{font-size:12px;opacity:.7;line-height:1.5}
.dbsub b{color:var(--gold)}

/* ---------------- the mini tutorial ----------------
   Auto-shown the first time the builder is opened, and reachable from `?`
   for ever after. deck.js holds the steps and the reasoning; this is the look.

   IT IS DELIBERATELY THE MATCH TUTORIAL'S VOCABULARY — gold on slate, the step
   counter in small caps, the pulsing dot beside "waiting for you" — because a
   player arrives here straight out of LEARN TO PLAY and a second visual
   language for the same idea is how a client starts to look assembled from
   parts (the note over the PvP lobby below says the same thing). It is not the
   same CSS: `#tutbox` lives at z-index 130 in the shell and this screen is at
   150, so reusing the element would render it behind the builder. */

.dbhelpbtn{position:absolute;top:12px;right:14px;width:30px;height:30px;padding:0;
  border-radius:50%;font-size:15px;line-height:1;
  background:linear-gradient(180deg,rgba(47,63,75,.9),rgba(20,29,36,.92));
  color:var(--gold-l);border:1px solid rgba(224,185,95,.42);text-shadow:none}
.dbhelpbtn:hover{border-color:var(--gold);color:var(--gold)}

/* pointer-events:none is load-bearing, not tidiness. Step 2 asks the player to
   click a card while its own text is on screen, and an overlay that ate the
   click would make the step impossible to satisfy. The match tutorial has no
   overlay element at all — it dims with a 9999px spread shadow, which also
   blocks nothing — so this preserves that property rather than inventing one. */
.dbhelpdim{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:rgba(4,7,10,.72);animation:dbhelpfade .22s ease}
@keyframes dbhelpfade{from{opacity:0}to{opacity:1}}

/* THE RING IS DRAWN INWARD, and that is the whole reason it is an `outline`
   with a negative offset rather than the shell's `.spot` box-shadow. `.dbbox`
   is `overflow:hidden` (line 8), so anything painted outside a spotlit panel is
   clipped away at the panel's edge — and every target here IS a panel flush
   against one. It is the same trap AGENTS.md records for the board, where
   `.field`'s `overflow:hidden` clips the spotlight, stated there as "may only
   highlight coarse containers". */
.dbspot{position:relative;z-index:3;outline:2px solid var(--gold);outline-offset:-2px;
  box-shadow:inset 0 0 40px rgba(224,185,95,.16);animation:dbspotp 2.2s ease-in-out infinite}
@keyframes dbspotp{0%,100%{outline-color:rgba(224,185,95,.5)}50%{outline-color:rgba(224,185,95,1)}}

/* 300px, and the 22 pixels it gave up buy the whole step-2 placement.
   At 1280×720 the pool ends at x=960, which leaves 306px of clear space to the
   right of it — so a 322px box did not fit beside the panel it points at and
   `place()` had to park it INSIDE, over the cheapest cards, in the one step
   that says "click a card now". At 300 it sits beside the pool and covers none
   of it. Narrower windows still fall back to the inside placement, which is why
   that branch stays.

   ONE WIDTH, AT EVERY SIZE, AND NO CLAMP. It used to carry
   `max-width:calc(100vw - 20px)`, which is a trap rather than a safety net: a
   browser tab that is not being painted reports a zero viewport, `100vw`
   resolves to 0, the max-width beats the width, and the box lays out 38px wide
   and 1114px tall — which `place()` then dutifully positions off-screen.
   Measured in exactly that state. A percentage would do the same, because it
   resolves against `.dbwrap`, which is `inset:0` on that same zero viewport.
   Nothing is needed: `index.html` calls the shell responsive to 390px, 300
   leaves 90px there, and `place()` clamps the box 10px inside either edge. */
.dbhelp{position:absolute;z-index:4;width:300px;
  padding:15px 17px;border-radius:12px;
  background:linear-gradient(180deg,rgba(30,45,56,.99),rgba(14,22,29,.99));
  border:2px solid var(--gold-d);
  box-shadow:0 20px 52px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.08)}
.dbhelpstep{font-size:9px;letter-spacing:2.5px;opacity:.5;margin-bottom:3px}
.dbhelp h4{margin:0 0 6px;font-size:16px;color:var(--gold-l);font-weight:normal}
.dbhelp p{margin:0 0 12px;font-size:12px;line-height:1.55;opacity:.94}
.dbhelp p b{color:var(--gold)}
.dbhelp p i{font-style:italic;opacity:.8}
.dbhelpfoot{display:flex;gap:8px;align-items:center}
.dbhelpwait{font-size:11px;color:var(--gold);display:flex;align-items:center;gap:6px}
.dbhelpwait::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--gold);
  animation:dbwpulse 1.1s ease-in-out infinite}
@keyframes dbwpulse{0%,100%{opacity:.25;transform:scale(.8)}50%{opacity:1;transform:scale(1.15)}}
.dbhelpskip{margin-left:auto;background:none;border:0;color:var(--cream);opacity:.42;
  font-size:10.5px;padding:3px 5px;box-shadow:none;text-shadow:none;font-weight:normal}
.dbhelpskip:hover{opacity:.85}

/* ---------------- the deck rail ---------------- */
/* One deck per faction used to be the whole model, so there was nothing to
   list. Now there are up to 12 and you need to see them to switch. */
.dbdecks{flex:none;display:flex;gap:8px;align-items:center;overflow-x:auto;
  padding:10px 14px;border-bottom:1px solid rgba(224,185,95,.16);
  background:rgba(6,10,14,.5)}
.dbdecks::-webkit-scrollbar{height:6px}
.dbdecks::-webkit-scrollbar-thumb{background:#3a4b57;border-radius:4px}

.dbtile{position:relative;flex:none;width:104px;border-radius:9px;overflow:hidden;cursor:pointer;
  background:linear-gradient(180deg,#1b262e,#0e151b);border:1px solid rgba(224,185,95,.18);
  transition:transform .13s,border-color .13s,box-shadow .13s}
.dbtile:hover{transform:translateY(-2px);border-color:var(--gold)}
.dbtile.on{border-color:var(--gold);box-shadow:0 0 0 1px rgba(224,185,95,.55),0 6px 16px rgba(0,0,0,.6)}
.dbtileart{height:52px;background:#0e151b center/cover no-repeat}
.dbtilename{font-size:10.5px;padding:4px 6px 5px;line-height:1.25;min-height:30px;
  display:flex;align-items:center;justify-content:center;text-align:center;
  color:var(--gold-l);background:linear-gradient(180deg,#4a3a17,#241b09);
  overflow:hidden;word-break:break-word}
.dbtiledel{position:absolute;top:3px;right:3px;width:18px;height:18px;line-height:1;
  border:none;border-radius:50%;cursor:pointer;font-size:13px;opacity:0;transition:opacity .13s;
  background:rgba(6,10,14,.85);color:#e0574a}
.dbtile:hover .dbtiledel{opacity:1}
.dbtiledel:hover{background:#e0574a;color:#fff}

.dbtileadd{flex:none;width:104px;height:82px;border-radius:9px;cursor:pointer;font-size:19px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  background:rgba(255,255,255,.03);border:1px dashed rgba(224,185,95,.35);color:var(--gold-l)}
.dbtileadd span{font-size:10.5px;letter-spacing:.3px}
.dbtileadd:hover:not(.off){background:rgba(224,185,95,.1);border-color:var(--gold)}
.dbtileadd.off{opacity:.35;cursor:not-allowed}
.dbtilecount{flex:none;margin-left:auto;padding-left:12px;font-size:11px;opacity:.55;white-space:nowrap}

/* ---------------- deck identity: name + box ---------------- */
.dbident{display:flex;gap:10px;padding:12px 12px 10px;border-bottom:1px solid rgba(224,185,95,.14);position:relative}
.dbboxart{flex:none;width:56px;height:56px;border-radius:8px;cursor:pointer;
  background:#0e151b center/cover no-repeat;border:1px solid rgba(224,185,95,.35);
  box-shadow:0 3px 10px rgba(0,0,0,.6)}
.dbboxart:hover{border-color:var(--gold)}
.dbidentr{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.dbnamelabel{font-size:10px;letter-spacing:1px;text-transform:uppercase;opacity:.5}
/* 16px floor — iOS Safari zooms the page on focus below that (issue #200).
   The coarse-pointer block below already grew this to a 44px tap target and
   left the zoom trigger untouched; the two defects are not the same. */
.dbnameinput{width:100%;padding:6px 8px;border-radius:6px;font-family:inherit;font-size:16px;
  color:var(--cream);background:rgba(6,10,14,.7);border:1px solid rgba(224,185,95,.3)}
.dbnameinput:focus{outline:none;border-color:var(--gold);background:rgba(6,10,14,.9)}
.dbnamehint{font-size:10px;opacity:.5;min-height:12px}

.dbboxmenu{position:absolute;top:70px;left:12px;z-index:5;display:grid;gap:5px;
  grid-template-columns:repeat(4,1fr);padding:8px;border-radius:9px;width:244px;
  background:rgba(10,16,21,.98);border:1px solid var(--gold-d);box-shadow:0 14px 34px rgba(0,0,0,.85)}
.dbboxopt{padding:0;border:1px solid transparent;border-radius:6px;cursor:pointer;background:none;
  display:flex;flex-direction:column;align-items:center;gap:2px}
.dbboxopt span{display:block;width:100%;height:40px;border-radius:5px;background:#0e151b center/cover no-repeat}
.dbboxopt em{font-size:9px;font-style:normal;opacity:.75;color:var(--cream)}
.dbboxopt:hover{border-color:var(--gold)}
.dbboxopt.on{border-color:var(--gold);box-shadow:0 0 0 1px rgba(224,185,95,.5)}

.dbmain{flex:1 1 auto;display:grid;grid-template-columns:minmax(0,1fr) 268px;gap:0;min-height:0}
/* `grid-auto-rows:max-content` is the load-bearing declaration, and it took two
   attempts to find that out.
   The pool is the `minmax(0,1fr)` row of a height-constrained `.dbmain`, so its
   IMPLICIT ROWS size themselves to the container's leftover height rather than
   to the tiles. Every card is then squashed — measured at 23px on a phone
   against a 107px tile — and `overflow:hidden` on `.dbcard` clips away the name
   and the stat line, leaving a wall of unlabelled art you cannot pick from.
   Worse, the scroll extent is computed from those collapsed tracks, so the real
   content cannot even be scrolled back.
   The first fix was `align-items:start`, on the theory that items stretching to
   their row was the cause. It is not: the ROW is wrong, not the item's fit
   inside it, and a QA pass found the pool still collapsing on a phone and on
   any laptop window under ~730px tall — with this comment sitting above it
   claiming the bug was fixed. Sizing the ROWS to their content is what actually
   works. `align-items:start` is kept because it is correct on its own terms. */
.dbpool{overflow-y:auto;padding:14px;display:grid;gap:10px;align-content:start;align-items:start;
  grid-auto-rows:max-content;
  grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
.dbpool::-webkit-scrollbar{width:7px}
.dbpool::-webkit-scrollbar-thumb{background:#3a4b57;border-radius:4px}

.dbcard{position:relative;border-radius:9px;overflow:hidden;cursor:pointer;user-select:none;
  background:linear-gradient(180deg,#1b262e,#0e151b);border:1px solid rgba(224,185,95,.16);
  transition:transform .13s,border-color .13s,box-shadow .13s}
.dbcard:hover{transform:translateY(-3px);border-color:var(--gold);box-shadow:0 8px 20px rgba(0,0,0,.7)}
.dbcard.in{border-color:rgba(111,207,107,.8);box-shadow:0 0 0 1px rgba(111,207,107,.35)}
.dbcard.full{border-color:var(--gold);box-shadow:0 0 0 1px rgba(224,185,95,.5)}
.dbart{height:74px;background:#0e151b center 38%/cover no-repeat}
.dbcard.in .dbart{filter:none}
.dbcard:not(.in) .dbart{filter:grayscale(.45) brightness(.72)}
.dbname{font-size:9px;text-align:center;padding:4px 5px 2px;line-height:1.2;min-height:26px;
  display:flex;align-items:center;justify-content:center;color:#f0e7d4}
.dbstat{font-size:9px;text-align:center;padding:0 4px 6px;opacity:.62;letter-spacing:.4px}
.dbcost{position:absolute;top:4px;left:4px;width:21px;height:21px;border-radius:50%;
  display:grid;place-items:center;font-size:11.5px;font-weight:bold;color:#fff;
  background:radial-gradient(circle at 35% 30%,#7fd0f5,#175a7d);box-shadow:0 2px 6px rgba(0,0,0,.85)}
.dbn{position:absolute;top:4px;right:4px;min-width:21px;height:21px;border-radius:11px;padding:0 5px;
  display:grid;place-items:center;font-size:11.5px;font-weight:bold;color:#1c1608;
  background:linear-gradient(180deg,var(--gold-l),var(--gold));box-shadow:0 2px 6px rgba(0,0,0,.85)}

/* ---------------- a printing of a card you already have ----------------
   A premium printing is its own tile, sitting immediately after the standard
   one (deck.js, `sorted`). It must read as THE SAME CARD in a different frame,
   never as a different card: same art, same crop, same name, same numbers, and
   the only additions are the border colour and one ribbon naming the
   treatment. `--r-fullart` / `--r-secret` are menu.css's, so this tile is the
   colour the Collection and the pack opening already taught. */
.dbcard.prem{border-color:color-mix(in srgb,var(--pc) 55%,transparent)}
.dbcard.prem:hover{border-color:var(--pc)}
.dbcard.p-fullart{--pc:var(--r-fullart)}
.dbcard.p-secret{--pc:var(--r-secret)}
.dbcard.prem.in{box-shadow:0 0 0 1px color-mix(in srgb,var(--pc) 45%,transparent),0 0 12px color-mix(in srgb,var(--pc) 22%,transparent)}
.dbprem{position:absolute;top:57px;left:0;right:0;text-align:center;font-size:7.5px;letter-spacing:1.2px;
  font-weight:bold;color:#100a18;padding:1px 0;
  background:linear-gradient(90deg,transparent,var(--pc) 20%,#fff 50%,var(--pc) 80%,transparent)}
.dbrow.prem .n em{font-style:normal;color:var(--pc)}
.dbrow.p-fullart{--pc:var(--r-fullart)}
.dbrow.p-secret{--pc:var(--r-secret)}

/* ---------------- a card you have not earned yet ----------------
   GREY, PRESENT, COUNTABLE — never absent and never a padlock. The padlock
   already means one thing in this game, "access you do not have yet": it sits on
   the DECK and COLLECTION buttons while signed out and on a Skyhold you have not
   reached. A card is not access, it is a thing you have or have not, and
   collapsing the two would import a shop's vocabulary into a game with no shop.

   The desaturation is the house style, not a new idea: a card in hand you cannot
   afford is already dimmed and labelled `needs 3✦` in a match — present,
   readable, obviously yours-later. Take that; leave the padlock alone.

   The NAME stays at full contrast. It is what the tile is for: you cannot want a
   card whose name you cannot read. Only the art and the frame recede, and the
   stat line becomes the way to earn it — one row, same height, so the grid never
   reflows as a collection fills up. */
.dbcard.locked{cursor:default;opacity:.62;border-color:rgba(224,185,95,.09);
  background:linear-gradient(180deg,#141c22,#0b1116)}
.dbcard.locked:hover{transform:none;border-color:rgba(224,185,95,.3);box-shadow:none}
.dbcard.locked .dbart{filter:grayscale(1) brightness(.5);opacity:.55}
.dbcard.locked .dbcost{filter:grayscale(1);opacity:.75}
.dbcard.locked .dbname{opacity:.95}
.dbsrc{color:var(--gold-l);opacity:.8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* The honest counter in the header. It replaces nothing — the old subtitle is
   still there — it just stops the builder implying the pool is yours. */
.dbown{display:block;margin-top:4px;color:var(--gold-l);opacity:.9}

/* A saved deck holding cards this account has not earned. Marked, never deleted
   and never silently repaired. */
.dbtile.owed{border-color:rgba(224,87,74,.55)}
.dbtileowed{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
  display:grid;place-items:center;font-size:12px;font-weight:bold;color:#1c1608;cursor:help;
  background:linear-gradient(180deg,#f0a25a,#e0574a);box-shadow:0 2px 6px rgba(0,0,0,.85)}

.dbside{border-left:1px solid rgba(224,185,95,.18);display:flex;flex-direction:column;min-height:0;
  background:rgba(0,0,0,.24)}
.dbcount{padding:12px 14px 8px;font-size:12.5px;flex:none}
.dbcount b{font-size:19px}
.dbcount b.ok{color:var(--good)}
.dbcount b.bad{color:var(--even)}

.dbcurve{display:flex;align-items:flex-end;gap:5px;padding:4px 14px 10px;flex:none;position:relative;height:78px}
.dbcurvelabel{position:absolute;top:-2px;left:14px;font-size:9px;letter-spacing:2px;opacity:.45}
.dbbar{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:2px}
.dbbar i{display:block;width:100%;border-radius:2px;min-height:2px;
  background:linear-gradient(180deg,var(--gold),#8a6a25)}
.dbbar u{font-size:9px;opacity:.5;text-decoration:none}
.dbbar b{position:absolute;display:none}

.dblist{flex:1 1 auto;overflow-y:auto;padding:6px 10px 12px;min-height:0}
.dblist::-webkit-scrollbar{width:6px}
.dblist::-webkit-scrollbar-thumb{background:#3a4b57;border-radius:3px}
.dbrow{display:flex;align-items:center;gap:8px;padding:4px 7px;border-radius:6px;font-size:11.5px;
  cursor:pointer;background:rgba(255,255,255,.03);margin-bottom:3px}
.dbrow:hover{background:rgba(224,87,74,.18)}
.dbrow .c{flex:none;width:18px;height:18px;border-radius:50%;display:grid;place-items:center;
  font-size:10px;font-weight:bold;color:#fff;background:radial-gradient(circle at 35% 30%,#7fd0f5,#175a7d)}
.dbrow .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dbrow .x{flex:none;opacity:.75;font-weight:bold;color:var(--gold-l)}
.dbempty{font-size:11.5px;opacity:.45;padding:10px 6px;line-height:1.5}

.dbfoot{flex:none;display:flex;align-items:center;gap:10px;padding:12px 18px;
  border-top:1px solid rgba(224,185,95,.2)}
.dbwarn{flex:1;font-size:11.5px;color:var(--even);text-align:right}
.dbsave:disabled{opacity:.35}

@media (max-width:820px){
  .dbwrap{padding:0}
  .dbbox{width:100%;height:100%;border-radius:0;border-width:0}
  .dbmain{grid-template-columns:1fr;grid-template-rows:minmax(0,1fr) auto}
  .dbside{border-left:0;border-top:1px solid rgba(224,185,95,.18);max-height:38vh}
  .dbpool{grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:8px;padding:10px}
  .dbart{height:60px}
  /* The right value is the `?` seat again — restating the whole shorthand here
     would silently drop it, which is how the desktop rule's own comment stops
     being true on a phone. */
  .dbhead{padding:12px 62px 9px 14px}
  .dbhead h2{font-size:18px}
  .dbcurve{height:64px}
  /* The rail scrolls sideways on a phone rather than stealing height from the
     pool, which is the part you are actually working in. */
  .dbdecks{padding:8px 10px}
  .dbtile,.dbtileadd{width:84px}
  .dbtileart{height:42px}
  .dbtileadd{height:70px}
  .dbtilecount{display:none}
  .dbboxmenu{grid-template-columns:repeat(3,1fr);width:190px}
}

/* =========================================================================
   THE FLIGHT PICKER  (#flight)
   The screen PLAY opens. The shell (.screen/.shead/.sbody) comes from
   menu.css; everything here is scoped under #flight so it cannot reach the
   match, the builder or the collection.

   PHONE FIRST, on purpose. The control this replaces (#p1deck) sits below the
   whole board inside the side panel, so on a 375px screen the deck choice was
   unreachable — you got Stormcrest and no way to ask for anything else. Every
   rule below is written so the two faction cards and the four flight tiles are
   readable and tappable at 375px without a horizontal scrollbar.
   ========================================================================= */

/* Opaque, unlike the collection: this screen opens OVER the title menu, and at
   the shell's default .97 alpha the menu buttons read straight through it. */
#flight { background: #04070a; }
/* NOTE: #flightbody IS the .sbody element, not a child of one — the campaign
   screen has a .sbody wrapper AND a #cmpbody child, and copying that shape
   while collapsing it to one node is how the first cut of this screen lost its
   padding: `#flight .sbody{padding:0}` (1-1-0) beat `#flightbody{padding:...}`
   (1-0-0) on the same element, and every line sat flush against the left edge
   of a 375px screen. The scroll behaviour comes from .screen .sbody in
   menu.css; only the padding is restated here. */
#flightbody { padding: 16px 18px 30px; }
#flightbody::-webkit-scrollbar { width: 8px; }
#flightbody::-webkit-scrollbar-thumb { background: #3a4b57; border-radius: 4px; }

.flwrap { max-width: 1020px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; }

.flhead { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.flhead h3 { margin: 0; font-size: 15px; letter-spacing: 3px; font-weight: normal; color: var(--gold-l); }
.flhead p { margin: 0; flex: 1 1 260px; min-width: 0; font-size: 12.5px; line-height: 1.55; opacity: .78; }

/* the first-run nudge toward the campaign */
.flnudge { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 14px; border-radius: 11px; font-size: 12.5px; line-height: 1.55;
  background: linear-gradient(180deg, rgba(28,40,26,.85), rgba(12,20,14,.9));
  border: 1px solid rgba(111,207,107,.32); }
.flnudge-tx { flex: 1 1 240px; min-width: 0; }
.flnudge b { color: #9fe39a; font-weight: normal; }
.flnudge button { flex: none; margin-left: auto; }

/* ---- level 1: the two factions ---- */
.flsides { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.flside { display: flex; gap: 14px; align-items: center; text-align: left;
  padding: 14px; border-radius: 13px; cursor: pointer; color: var(--cream); font-family: inherit;
  background: linear-gradient(180deg, rgba(16,24,31,.9), rgba(9,14,19,.94));
  border: 1px solid rgba(224,185,95,.2); box-shadow: 0 6px 22px rgba(0,0,0,.5);
  transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease; }
.flside:hover { transform: translateY(-3px); border-color: var(--gold);
  box-shadow: 0 0 26px rgba(224,185,95,.2), 0 6px 22px rgba(0,0,0,.6); }
.flside-box { width: 84px; height: 110px; flex: none; border-radius: 8px;
  background: #10181f center/cover no-repeat; border: 1px solid var(--gold-d);
  box-shadow: 0 5px 16px rgba(0,0,0,.7); }
.flside-tx { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.flside-tx b { font-size: 16px; color: var(--gold-l); font-weight: normal; letter-spacing: 1px; }
.flside-tx i { font-style: normal; font-size: 12.5px; line-height: 1.5; opacity: .8; }
.flside-tx em { font-style: normal; font-size: 11px; opacity: .5; }

/* ---- level 2: the four flights of one faction ---- */
.flgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 13px; }
.fl { display: flex; gap: 12px; align-items: flex-start; text-align: left;
  padding: 12px; border-radius: 12px; cursor: pointer; color: var(--cream); font-family: inherit;
  background: linear-gradient(180deg, rgba(16,24,31,.9), rgba(9,14,19,.94));
  border: 1px solid rgba(224,185,95,.18); box-shadow: 0 5px 18px rgba(0,0,0,.5);
  transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease; }
.fl:hover:not(:disabled) { transform: translateY(-3px); border-color: var(--gold);
  box-shadow: 0 0 24px rgba(224,185,95,.2), 0 5px 18px rgba(0,0,0,.6); }
/* A locked flight is DIMMED, not hidden. It is the only place in the client
   that says where the other 50 cards are, so it has to stay legible. */
.fl.locked { cursor: default; opacity: .62; filter: grayscale(.45); border-style: dashed; }
.fl-box { position: relative; width: 62px; height: 82px; flex: none; border-radius: 7px;
  background: #0f171e center/cover no-repeat; border: 1px solid rgba(224,185,95,.32);
  box-shadow: 0 4px 12px rgba(0,0,0,.7); }
.fl.locked .fl-box { filter: grayscale(.8) brightness(.7); }
.fl-lock { position: absolute; inset: 0; display: grid; place-items: center; font-size: 20px;
  text-shadow: 0 2px 6px rgba(0,0,0,.9); }
.fl-tx { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.fl-tx b { font-size: 14.5px; color: var(--gold-l); font-weight: normal; letter-spacing: .8px; }
.fl-tx i { font-style: normal; font-size: 12.5px; color: var(--cream); opacity: .92; }
.fl-tx em { font-style: normal; font-size: 11.5px; line-height: 1.45; opacity: .62; }
.fl-new { align-self: flex-start; margin-top: 2px; padding: 2px 8px; border-radius: 20px;
  font-size: 9.5px; letter-spacing: 1.1px; text-transform: uppercase; white-space: nowrap;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  color: #0b1016; background: linear-gradient(180deg, #9fe39a, #4fa64a); }
.fl-owed { font-size: 10.5px; letter-spacing: .4px; opacity: .78; color: var(--gold-l); }

.flfoot { display: flex; justify-content: center; padding-top: 4px; }

/* ---- the friend-match lobby (#pvp) ----
   Built from the flight screen's vocabulary rather than a new one: the same
   card shape, the same gold, the same widths. A second visual language for the
   newest mode is how a client starts to look assembled from parts. */
#pvpbody { padding: 16px 18px 30px; }
.pvpwrap { max-width: 720px; margin: 0 auto; display: flex; flex-direction: column; gap: 15px; }
.pvpwrap h3 { margin: 0 0 6px; font-size: 15px; letter-spacing: 3px; font-weight: normal; color: var(--gold-l); }
.pvpwrap p { margin: 0; font-size: 12.5px; line-height: 1.55; opacity: .78; }
.pvpcard { padding: 15px 16px; border-radius: 13px;
  background: linear-gradient(180deg, rgba(16,24,31,.9), rgba(9,14,19,.94));
  border: 1px solid rgba(224,185,95,.2); box-shadow: 0 6px 22px rgba(0,0,0,.5); }
.pvprow { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
/* 16px floor — was 15px, one pixel under Safari's zoom threshold (issue #200). */
.pvprow input { flex: 1 1 180px; min-width: 0; padding: 10px 12px; border-radius: 9px;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 16px; letter-spacing: 3px;
  text-transform: uppercase; color: var(--cream);
  background: rgba(6,10,14,.8); border: 1px solid rgba(224,185,95,.28); }
/* ONE COLUMN PER SIDE. `repeat(2, …)` was the literal that matched the literal
   in ui.js, so the day the picker started deriving its tiles this would have
   laid three of them out 2-then-1. `--n` is set from SIDES.length by paintPvp();
   the fallback keeps the rule readable on its own. */
.pvpsides { display: grid; grid-template-columns: repeat(var(--n, 2), minmax(0, 1fr));
  gap: 10px; margin-top: 10px; }
.pvpside { display: flex; gap: 10px; align-items: center; text-align: left; padding: 11px 12px;
  border-radius: 11px; cursor: pointer; color: var(--cream); font-family: inherit; font-size: 13px;
  background: rgba(9,14,19,.8); border: 1px solid rgba(224,185,95,.18); }
.pvpside.on { border-color: var(--gold); box-shadow: 0 0 20px rgba(224,185,95,.18); }
/* A side that exists and is not yours yet. Legible, not hidden — it is telling
   the player the faction is there and what wins it. */
.pvpside.off { cursor: not-allowed; opacity: .45; border-style: dashed; }
.pvpside.off em { opacity: .85; }
.pvpside .ic { font-size: 19px; }
.pvpside em { display: block; font-style: normal; font-size: 11px; opacity: .6; margin-top: 2px; }
/* The invite code is read aloud and typed by hand, so it is the largest thing on
   the screen and it is monospaced. Eight characters from an alphabet with no
   0/O and no 1/I — server/src/routes/pvp.js mints it that way for this reason. */
.pvpcode { margin-top: 12px; padding: 14px; border-radius: 11px; text-align: center;
  background: rgba(6,10,14,.85); border: 1px dashed rgba(224,185,95,.4); }
.pvpcode b { display: block; font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 32px; letter-spacing: 6px; color: var(--gold-l); font-weight: normal; }
.pvpcode span { display: block; margin-top: 7px; font-size: 11.5px; opacity: .62; letter-spacing: .6px; }
.pvpnote { font-size: 12px; line-height: 1.5; padding: 9px 12px; border-radius: 9px;
  background: rgba(40,18,18,.7); border: 1px solid rgba(214,110,96,.4); color: #ffcfc6; }
.pvpwait { display: flex; align-items: center; gap: 10px; margin-top: 12px; font-size: 12.5px; opacity: .82; }

@media (max-width: 720px) {
  #flightbody { padding: 12px 12px 24px; }
  .flsides { grid-template-columns: 1fr; gap: 11px; }
  .flgrid { grid-template-columns: 1fr; gap: 10px; }
  .flside-box { width: 66px; height: 88px; }
  .flside-tx b { font-size: 15px; }
  .fl-box { width: 54px; height: 72px; }
  .flnudge button { margin-left: 0; }
  #pvpbody { padding: 12px 12px 24px; }
  .pvpsides { grid-template-columns: 1fr; }
  .pvpcode b { font-size: 27px; letter-spacing: 4px; }
}

/* Same query as the tap-target block in index.html, for the same reason: a
   768px iPad is a touch device and width alone does not say so. The deck name
   is the one field in the builder you type into, and 32.5px is a mouse target.
   Every other control went to 44px there; this is the one the shell's
   `select,input[…]` rule cannot reach, because it is an input[type=text]. */
@media (pointer: coarse), (max-width: 1100px) {
  .dbnameinput { min-height: 44px; padding: 9px 10px; }
  /* Same reason, and the shell cannot reach this one either: index.html sizes
     `button.sm` to 44px and the `?` carries no `.sm` — it is a 30px circle by
     design on a mouse. `.dbhelpskip` is the shell's opt-out shape (no border,
     no background), so it gets the height without the padding. */
  .dbhelpbtn { width: 44px; height: 44px; font-size: 18px; top: 9px; right: 10px; }
  .dbhelpskip { min-height: 44px; }
}
