/* locker-grid.css - the LEFT side of the Locker: the toolbar over the grid, the
   cells in it, and the stack of copies drawn behind a card.

   Its own file because locker.css is 88 lines from its budget and this is a
   different subject: locker.css is the drawer, the copy rows and the melt ladder,
   this is what you look at before you open one.

   LOADED BEFORE locker.css, on purpose. The `#ownedGrid` rules here are the base,
   and locker.css overrides them inside media queries for the small screens. A media
   query adds no specificity, so loading this file second would silently beat every
   one of those and hand a 320px phone the desktop grid. */

/* ---- The toolbar ----
   It lives in the panel's HEADING band. Above the grid, inside the column the
   cards page against, it cost a 44px row plus a 12px gap of paging height, which
   at 390x844 is one whole row of skins. Beside the title it costs the difference
   between a 25px heading and a 44px control, and on the narrow screens where it
   has to take its own line it takes the LEDE's line: a sentence telling you to tap
   a skin is worth less than the search field, and fit.css already retires ledes
   first on the shapes with no height (`.tab-head p` in the landscape block).

   The wrap does the layout. `flex: 1 1 320px` says "beside the title if 320px is
   there, on your own line if not", which is one declaration instead of a
   breakpoint, and it answers zoom the same way it answers a narrow phone. */
#skinsPanel .tab-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
#skinsPanel .tab-head h2 { flex: 0 0 auto; }
#lockerLede { flex: 1 0 100%; margin-top: 0; }
/* Capped and pushed right. Left to grow it ran 910px of search field across a
   1280px header, which reads as the page's main event rather than as a tool for
   finding one card among fourteen. */
.locker-tools { display: flex; align-items: center; gap: 8px; flex: 1 1 320px; min-width: 0;
  max-width: 520px; margin-left: auto; }
/* Narrow, or short. Either way the sentence goes and the field stays. */
@media (max-width: 620px), (max-height: 700px) { #lockerLede { display: none; } }
/* On a phone the toolbar shares the title's line rather than taking one of its
   own. Measured at 320x568: on its own line the heading band is 77px and the
   drawer ends 10px past the panel, sharing it the band is 44px and everything
   fits with room over. The basis is what decides that, so it drops to what will
   actually sit beside a 101px title in 296px of width. */
@media (max-width: 620px) { .locker-tools { flex-basis: 140px; } }

.lk-find {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 7px;
  padding: 0 11px; min-height: 44px;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,.16);
  background: rgba(255,255,255,.05);
}
.lk-find:focus-within { border-color: rgba(127,227,255,.55); }
.lk-find-icon { opacity: .55; font-size: .85rem; }
#lockerSearch {
  flex: 1 1 auto; min-width: 0; min-height: 42px;
  border: 0; background: none; color: inherit; font: inherit; font-size: .88rem;
}
/* The UA's own clear affordance. Left in place because it is the control people
   already know, but sized up: it ships at about 12px, and the rest of this screen
   holds a 44px floor. */
#lockerSearch::-webkit-search-cancel-button { cursor: pointer; }

.lk-sort {
  display: flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0 13px; white-space: nowrap;
  border-radius: 10px; border: 1px solid rgba(255,255,255,.16);
  background: rgba(255,255,255,.06); color: inherit;
  font: inherit; font-size: .84rem; font-weight: 700; cursor: pointer;
  transition: border-color var(--t-quick) var(--e-standard),
              background var(--t-quick) var(--e-standard);
}
.lk-sort:hover { border-color: rgba(255,255,255,.4); background: rgba(255,255,255,.1); }
.lk-sort:active { transform: scale(.965); transition-duration: var(--t-instant); }
.lk-dir { font-size: 1rem; opacity: .75; }
/* Only says anything while a search is narrowing the grid. */
.lk-tally { font-size: .76rem; opacity: .6; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* A search with no hits. Not the empty-locker art: somebody who owns forty skins
   and mistyped one of them is not an empty locker, and telling them they own
   nothing is a worse answer than telling them nothing. */
.locker-empty .lk-clear {
  margin-left: 4px; min-height: 44px; padding: 6px 12px;
  border-radius: 9px; border: 1px solid rgba(255,255,255,.22);
  background: rgba(255,255,255,.07); color: inherit; font: inherit;
  font-size: .82rem; font-weight: 700; cursor: pointer;
}
.locker-empty .lk-clear:hover { border-color: rgba(255,255,255,.45); }

/* ---- The grid ----
   Columns come from the container, not from a breakpoint. clamp() is what makes
   browser zoom a non-event: zooming to 200% halves the CSS-pixel width, which this
   reads as a narrower container and answers with fewer, same-sized cards. A fixed
   pixel track would have kept the card size and dropped the last column off the
   panel instead.

   align-content: start is the half-empty last page. Without it a page holding one
   row of a three-row grid stretches that row down the whole column, and the end of
   a collection reads as a rendering fault. */
#ownedGrid {
  grid-template-columns: repeat(auto-fill, minmax(clamp(96px, 11vw, 148px), 1fr));
  align-content: start;
}

/* ---- One cell, and the copies behind it ----
   The stack is drawn by the SLOT, never by the card. A card cannot show its own
   stack: `.skin-card.shimmer` sets overflow: hidden, so anything peeking out from
   behind a Green would be cut off, and both :hover and :active transform the card,
   which makes it a stacking context and flips a negative-z child in front of it
   halfway through a press.

   The card is inset by exactly what the stack sticks out by, so a stacked cell
   occupies the same box an unstacked one does. Letting it bleed into the grid gap
   instead would push the right-hand column a few pixels outside the panel, which is
   the one thing tools/checks/ui-rules.mjs exists to catch. */
.locker-slot { position: relative; display: flex; min-width: 0; --lk-off: 5px; }
.locker-slot > .skin-card { flex: 1 1 auto; min-width: 0; }
.locker-slot.stacked > .skin-card { margin: 0 var(--lk-off) var(--lk-off) 0; }

.lk-stack { position: absolute; inset: 0; pointer-events: none; }
.lk-stack::before, .lk-stack::after {
  content: ""; position: absolute;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,.13);
  background: rgba(255,255,255,.045);
}
/* Each layer is the card's own box, shifted. Deliberately NOT rarity-tinted: the
   card carries the rarity frame already, and a second tinted edge behind it reads
   as a glow rather than as another copy. */
.lk-stack::before { inset: calc(var(--lk-off) / 2); }
.lk-stack::after { inset: var(--lk-off) 0 0 var(--lk-off); }
/* Two copies is one card behind, three or more is two. The pile stops growing
   there: a stack that tracks the count turns into a chart nobody can read, and the
   exact number is on the badge, which is the half of this that has to be precise. */
.locker-slot.s1 .lk-stack::before { display: none; }

/* The ×N badge sits INSIDE the shop's existing .skin-card, which is
   position:relative already, so it pins to a corner without changing its box.
   The "⋯" that used to sit in the opposite corner is GONE, and so are its
   twelve lines of styling: it hid the stats behind a control almost nobody
   found, and the whole card opens the drawer now (see renderLocker). */
.locker-count {
  position: absolute; top: 6px; right: 6px;
  padding: 1px 7px; border-radius: 999px;
  font-size: .72rem; font-weight: 700; font-variant-numeric: tabular-nums;
  background: rgba(0,0,0,.62); color: #fff;
  border: 1px solid rgba(255,255,255,.28);
  pointer-events: none;
}
.locker-empty { opacity: .7; padding: 18px; text-align: center; }

/* Three across, not two. `.skins-grid` is a fixed two-column grid, which is right
   for the Shop's big priced cards and wrong for the Locker on a phone: with the
   drawer taking its share of a 390x844 body the grid gets one row, so two columns
   meant fourteen skins arrived as SEVEN pages of two. Same card, same size, one
   more per row, five pages. */
@media (max-width: 520px) and (orientation: portrait) {
  #ownedGrid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
  /* The toolbar costs a row, and this is where it is paid for. Measured at
     390x844: the grid track is 336px, a 120px card row plus the 12px gap plus the
     48px page bar plus the 44px toolbar is 21px more than that, so a phone that
     had two rows of skins dropped to one and fourteen skins became five pages.
     The card gives up padding, not artwork: the swatch goes 46 -> 38, the card
     goes about 120 -> 100, and two rows fit again. Anything that shrank the art
     instead would be trading the one thing a locker is for. */
  #ownedGrid .skin-card { padding: 17px 6px 7px; gap: 3px; }
  #ownedGrid .swatch { width: 38px; height: 38px; }
  /* The row is as tall as its TALLEST card, and a two-line name is 10px taller
     than a one-line one, so "Amethyst Cataphract" decides how many rows the page
     gets. At 13px that row was 115px against 227px of room and the second row
     missed by six pixels, which is a whole row of skins lost to one long name.
     Tightening the line brings the wrapped card to 108 and both rows fit. The
     name still wraps freely: nothing here truncates it. */
  #ownedGrid .skin-card-name { font-size: 12px; line-height: 1.12; }
  /* The badge drops below the rarity pill instead of sitting beside it. The pill
     is centred and never wraps, so on a 108px card "GOLD · 7%" runs under a
     top-right badge and loses its last characters to an opaque chip: the drop rate
     was being covered by the copy count. There is no width where both fit on one
     line, so they take different lines and both stay whole. */
  .locker-count { top: 21px; right: 5px; }
}

/* The open card's second-tap hint. Cyan, which is the shell's own "this is a
   control" colour (--focus), so it reads as an instruction rather than as the
   quiet grey status line it replaces. */
#ownedGrid .skin-card-sub.hint { color: #7fe3ff; }

/* Spoken instead of the badge. "×3" is read out as "3" by some screen readers and
   skipped by others, and how many copies you hold is why this card looks different
   from the one beside it. */
.lk-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ---- Rarity shelves (touch only) --------------------------------------------
   Direction A of docs/design/mockups, picked off three. Rarity is what a player
   already sorts by in their head, and a flat scroll of forty cards offers no
   landmark to scroll TO; a shelf per tier turns "where is my Gold one" into a
   glance. Desktop keeps the wall, and none of this applies there. */
body.touch-device #ownedGrid { display: block; }
.lk-shelf { padding: 2px 0 6px; }
.lk-shelf-head {
  display: flex; align-items: baseline; gap: 7px; padding: 0 2px 6px;
  font-size: 12.5px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
  color: var(--rc);
}
.lk-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--rc); flex: 0 0 auto; }
.lk-shelf-sub {
  margin-left: auto; font-size: 11px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: #9aa3c7; font-variant-numeric: tabular-nums;
}
/* The rail. `data-hscroll` in the markup is the explicit opt-in the commit gate
   looks for, because sideways scrolling is a layout bug everywhere else in this
   shell. scroll-snap so a flick lands on a card rather than between two. */
.lk-rail {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 6px;
  scroll-snap-type: x proximity; overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
.lk-rail > .locker-slot { flex: 0 0 auto; width: 84px; scroll-snap-align: start; }

/* ---- Two-face skins: the flip on a Locker card (js/skin-flip.js) ---------
   Beat one is the rim. It comes up over 140ms on hover or focus and then HOLDS,
   never pulsed, because a light that repeats is the one thing the motion rules
   forbid. The flip drives the same variable inline, so the press takes it
   straight to full without a second animation arguing with this one. */
.sf-rim {
  position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  box-shadow: inset 0 0 0 2px var(--sf-rim, #fff), inset 0 0 12px var(--sf-rim, #fff);
  opacity: var(--sf-rim-a, 0); transition: opacity 140ms ease-out;
}
.locker-slot:has(.sf-flip:hover) .swatch, .locker-slot:has(.sf-flip:focus-visible) .swatch { --sf-rim-a: 0.55; }
.sf-face-b { opacity: 0; }
/* The seam is WIDTH-CORRECTED by the driver as the coin narrows under it, so the
   hang reads as a blade of light rather than as an empty frame. */
.sf-seam {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; transform: translateX(-50%);
  pointer-events: none; opacity: 0; border-radius: 2px;
  background: linear-gradient(to bottom, transparent, #fff 18%, #fff 82%, transparent);
  box-shadow: 0 0 10px var(--sf-rim, #fff), 0 0 22px var(--sf-rim, #fff);
}
/* 44x44 of hit area with a 26px dot inside it: the target is the rule, the dot is
   what a 84px card can spare. The compression is beat two, and it is the visible
   half of the wind-up. */
.sf-flip {
  position: absolute; right: 0; bottom: 0; z-index: 2; width: 44px; height: 44px;
  padding: 0; border: 0; background: none; font-size: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.sf-flip::before {
  content: "\21C4"; font-size: 13px; width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 50%;
  background: rgba(12, 16, 32, 0.82); color: #dfe6ff;
  transition: transform 90ms ease-out;
}
.sf-flip:active::before { transform: scale(0.88); }
.sf-flip:disabled { cursor: default; }
.sf-flip:disabled::before { opacity: 0.35; }
