/* public/shell.css — extracted from landing.html .topnav block, class-renamed */

#appShell {
  display: flex;
  align-items: center;
  padding: 0.7rem 2rem;
  background: #111827;
  border-bottom: 1px solid #1F2937;
  position: sticky;
  top: 0;
  z-index: 200;
  font-family: 'Inter', system-ui, sans-serif;
}

.shell-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 800;
  font-size: 1.15rem;
  color: #F8FAFC;
  text-decoration: none;
}

/* BETA badge — sits beside the shop name on every page the shell draws.
   Amber rather than the accent colour: it is a notice, not a feature, and it must not read as
   part of the shop's branding. A sibling of .shell-brand and not a child, because updateBrand()
   replaces that element's innerHTML the moment branding resolves and would wipe it. */
.shell-beta {
  display: inline-flex;
  align-items: center;
  padding: 0.12rem 0.45rem;
  margin-left: 0.1rem;
  border: 1px solid #B45309;
  border-radius: 4px;
  background: rgba(180, 83, 9, 0.18);
  color: #FBBF24;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: help;
}

.shell-tabs {
  display: flex;
  gap: 1.2rem;
  margin-left: 2rem;
}
.shell-tab {
  color: #94A3B8;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.3rem 0;
  border-bottom: 2px solid transparent;
  transition: all 0.2s;
  text-decoration: none;
}
.shell-tab:hover, .shell-tab.active {
  color: #F8FAFC;
  border-color: #6366F1;
}

.shell-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.shell-cart-btn {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 1rem;
  background: #1E293B;
  border: 1px solid #334155;
  border-radius: 8px;
  color: #F8FAFC;
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.2s;
  text-decoration: none;
}
.shell-cart-btn:hover { background: #334155; }
.shell-cart-count {
  background: #6366F1;
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.15rem 0.4rem;
  border-radius: 10px;
  min-width: 1.2rem;
  text-align: center;
}

/* Sized to match the cart and My Orders beside it, not to its own taste.
   Measured at 1204px wide before this: cart 34px tall with 8px corners, Sign In 31px with 7px,
   sitting a pixel lower. Three controls in one slot, three sizes — small enough that nobody
   could name it and visible enough that Henry circled it. The avatar is already 34px. */
.shell-sign-btn {
  padding: 0.4rem 1rem;
  background: #6366F1;
  color: #fff;
  border: 1px solid transparent;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.82rem;
  line-height: 1.25;
  cursor: pointer;
  transition: background 0.2s;
  text-decoration: none;
}
.shell-sign-btn:hover { background: #4F46E5; }

/* Account slot — fixed width to avoid reflow on sign-in -> avatar swap */
.shell-account {
  position: relative;
  min-width: 7rem;
  display: flex;
  justify-content: flex-end;
}

.shell-user-btn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid #334155;
  background: #1E293B;
  color: #F8FAFC;
  cursor: pointer;
  overflow: hidden;
  padding: 0;
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.shell-avatar { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* Dropdown */
.shell-dropdown {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  min-width: 200px;
  background: #1E293B;
  border: 1px solid #334155;
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  z-index: 300;
  padding: 0.5rem 0;
}
.shell-dropdown[hidden] { display: none; }
.shell-dropdown-header { padding: 0.8rem 1rem; }
.shell-dropdown-name { color: #F8FAFC; font-weight: 600; font-size: 0.9rem; }
.shell-dropdown-role { color: #94A3B8; font-size: 0.75rem; }
.shell-dropdown-sep { border: none; border-top: 1px solid rgba(255,255,255,0.1); margin: 0.3rem 0; }
.shell-dropdown-section { padding: 0.3rem 1rem; color: #64748B; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; }
.shell-dropdown-item {
  display: block;
  width: 100%;
  padding: 0.5rem 1rem;
  background: none;
  border: none;
  color: #F8FAFC;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
  text-decoration: none;
}
.shell-dropdown-item:hover { background: rgba(255,255,255,0.05); }
.shell-logout { color: #ef5350; }

/* The one place the shell changes shape, and it sat on exactly the width an iPad is.
 *
 * A tablet in portrait is 768 CSS pixels, so the whole class of device landed on the phone side
 * of this and lost the game switcher — on a screen with room to spare. Measured at 768 with the
 * tabs shown: the bar reports no overflow at all, so hiding them was never a space decision.
 *
 * 640 is where the room actually runs out. Below it the brand, the search box and the cart
 * genuinely need the whole width, and the games are still reachable from the tiles on the front
 * page. Above it the tabs stay, which is every tablet in either orientation. */
@media (max-width: 640px) {
  .shell-tabs { display: none; }
}

@media (max-width: 768px) {
  #appShell { padding: 0.7rem 1rem; }
}

/* ── Cart drawer ─────────────────────────────────────────────────────────────
   Slides in over the current page. The cart is global state, so it is reachable
   from any storefront page rather than living at the bottom of the market. */

.cart-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0.25s ease;
  z-index: 400;
}
.cart-drawer-backdrop.open { opacity: 1; visibility: visible; }

.cart-drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: min(400px, 100vw);
  background: #111827;
  border-left: 1px solid #1F2937;
  box-shadow: -12px 0 40px rgba(0, 0, 0, 0.5);
  transform: translateX(100%);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 401;
  display: flex;
  flex-direction: column;
  font-family: 'Inter', system-ui, sans-serif;
}
.cart-drawer.open { transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .cart-drawer, .cart-drawer-backdrop { transition: none; }
}

.cart-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.2rem;
  border-bottom: 1px solid #1F2937;
  flex-shrink: 0;
}
.cart-drawer-title { color: #F8FAFC; font-weight: 700; font-size: 1rem; }
.cart-drawer-close {
  background: none; border: none; color: #94A3B8;
  font-size: 1rem; cursor: pointer; padding: 0.2rem 0.4rem; border-radius: 6px;
}
.cart-drawer-close:hover { background: #1E293B; color: #F8FAFC; }

.cart-drawer-body { flex: 1; overflow-y: auto; padding: 0.6rem; }
.cart-drawer-empty { color: #64748B; text-align: center; padding: 3rem 1rem; font-size: 0.9rem; }

.cart-drawer-line {
  display: flex; gap: 0.7rem; align-items: center;
  padding: 0.7rem 0.6rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.cart-drawer-line img, .cart-drawer-noimg {
  width: 44px; height: 61px; object-fit: cover; border-radius: 4px;
  background: #1E293B; flex-shrink: 0;
}
.cart-drawer-info { flex: 1; min-width: 0; }
.cart-drawer-name {
  color: #F8FAFC; font-size: 0.82rem; font-weight: 600; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cart-drawer-meta {
  color: #64748B; font-size: 0.68rem; margin-top: 0.15rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cart-drawer-price { color: #10B981; font-size: 0.82rem; font-weight: 700; margin-top: 0.25rem; }

.cart-drawer-qty { display: flex; align-items: center; gap: 0.25rem; flex-shrink: 0; }
.cart-drawer-qty button {
  width: 24px; height: 24px; border-radius: 5px;
  background: #1E293B; border: 1px solid #334155; color: #F8FAFC;
  font-size: 0.8rem; cursor: pointer; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.cart-drawer-qty button:hover:not(:disabled) { background: #334155; }
.cart-drawer-qty button:disabled { opacity: 0.35; cursor: not-allowed; }
.cart-drawer-qty span { color: #F8FAFC; font-size: 0.8rem; min-width: 1.3rem; text-align: center; }
.cart-drawer-rm { border-color: transparent !important; color: #ef5350 !important; }
.cart-drawer-rm:hover { background: rgba(239, 83, 80, 0.12) !important; }

.cart-drawer-foot {
  padding: 1rem 1.2rem;
  border-top: 1px solid #1F2937;
  flex-shrink: 0;
}
.cart-drawer-total {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 0.8rem;
}
.cart-drawer-total span { color: #94A3B8; font-size: 0.85rem; }
.cart-drawer-total strong { color: #10B981; font-size: 1.2rem; font-weight: 800; }
.cart-drawer-checkout {
  width: 100%; padding: 0.7rem; background: #6366F1; color: #fff;
  border: none; border-radius: 8px; font-weight: 700; font-size: 0.9rem; cursor: pointer;
  transition: background 0.2s;
}
.cart-drawer-checkout:hover:not(:disabled) { background: #4F46E5; }
.cart-drawer-checkout:disabled { opacity: 0.4; cursor: not-allowed; }
.cart-drawer-clear {
  width: 100%; margin-top: 0.5rem; padding: 0.5rem;
  background: none; border: none; color: #64748B; font-size: 0.78rem; cursor: pointer;
}
.cart-drawer-clear:hover { color: #ef5350; }

/* ── Foil shimmer for storefront cards ───────────────────────────────────────
   The market's version wraps the image in .foil-shimmer-wrap, but the landing
   card uses an absolutely-positioned image inside a padding-ratio box, so a
   wrapping inline-block would collapse the layout. Here the effect is painted
   on the existing .img-wrap instead, which is already position:relative.

   The gradient duplicates the one in style.css. That file is design-locked, so
   it cannot be moved out; the keyframes are renamed to avoid colliding when
   both stylesheets load on the same page. Fold them together when the design
   lock is retired. */

.card .img-wrap.is-foil::after {
  content: '';
  position: absolute;
  inset: 0;
  background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><defs><linearGradient id="g" x1="0%25" y1="0%25" x2="100%25" y2="100%25"><stop offset="0%25" style="stop-color:rgb(255,0,128);stop-opacity:0.3"/><stop offset="16%25" style="stop-color:rgb(255,200,0);stop-opacity:0.3"/><stop offset="33%25" style="stop-color:rgb(0,255,100);stop-opacity:0.3"/><stop offset="50%25" style="stop-color:rgb(0,150,255);stop-opacity:0.3"/><stop offset="66%25" style="stop-color:rgb(128,0,255);stop-opacity:0.3"/><stop offset="83%25" style="stop-color:rgb(255,100,200);stop-opacity:0.3"/><stop offset="100%25" style="stop-color:rgb(255,200,0);stop-opacity:0.3"/></linearGradient></defs><rect fill="url(%23g)" width="200" height="200"/></svg>');
  background-size: 300% 300%;
  mix-blend-mode: color-dodge;
  opacity: 0.55;
  z-index: 1;
  pointer-events: none;
  animation: shellFoilGradient 4s ease infinite;
}

@keyframes shellFoilGradient {
  0%   { background-position: 0% 0%; }
  25%  { background-position: 100% 0%; }
  50%  { background-position: 100% 100%; }
  75%  { background-position: 0% 100%; }
  100% { background-position: 0% 0%; }
}

@media (prefers-reduced-motion: reduce) {
  .card .img-wrap.is-foil::after { animation: none; }
}

/* Surge foil — the WebGL overlay in surge-foil.js targets this class via a
   MutationObserver. The landing card's .img-wrap is already position:relative with a
   fixed aspect box, so it needs no extra geometry; the .is-foil shimmer stays underneath
   as the fallback when Three.js fails or is blocked. */
.card .img-wrap.surge-foil-wrap { overflow: hidden; }

/* ─── CAROUSEL ───────────────────────────────────────────────────────────────
   Styles for public/lib/carousel.js. They live here rather than in a page's
   <style> block because two pages use them — landing.html and game.html — and
   because public/style.css is design-locked (steering/contracts.md §4).

   The cell width is set inline by the script, not here: how many fit is a
   measurement, and CSS cannot express "five, or fewer, but never under 120px". */
.carousel {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.carousel-viewport {
  flex: 1;
  overflow: hidden;
}
.carousel-track {
  display: flex;
  /* transition is set by the script so prefers-reduced-motion can drop it */
  will-change: transform;
}
.carousel-cell {
  /* flex-basis and max-width are assigned per item by carousel.js */
  padding: 0 0.5rem;
  box-sizing: border-box;
}
.carousel-arrow {
  flex: 0 0 auto;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  border: 1px solid #334155;
  background: #1E293B;
  color: #F8FAFC;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.carousel-arrow:hover:not(:disabled) {
  background: #6366F1;
  border-color: #6366F1;
}
.carousel-arrow:focus-visible {
  outline: 2px solid #818CF8;
  outline-offset: 2px;
}
.carousel-arrow:disabled {
  opacity: 0.3;
  cursor: default;
}
@media (max-width: 768px) {
  .carousel-arrow { width: 1.9rem; height: 1.9rem; font-size: 1rem; }
  .carousel-cell { padding: 0 0.3rem; }
}
@media (prefers-reduced-motion: reduce) {
  .carousel-track { transition: none !important; }
}

/* ── Search in the global bar ────────────────────────────────────────────────
   Search used to exist only in the landing page's hero, so from the market, a game page or
   My Orders there was no way to search without going home first. In the bar it is on every
   storefront page, and the hero is freed for what the shop wants to advertise.

   It is the flexible element between the game tabs and the account controls, so .shell-right
   no longer needs margin-left:auto to push itself over. */
.shell-search {
  flex: 1 1 auto;
  max-width: 420px;
  margin-left: 2rem;
  display: flex;
  align-items: center;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 8px;
  overflow: hidden;
}
.shell-search input {
  flex: 1;
  min-width: 0;
  padding: 0.45rem 0.7rem;
  background: transparent;
  border: none;
  color: #F8FAFC;
  font-size: 0.82rem;
}
.shell-search input::placeholder { color: #64748B; }
.shell-search input:focus { outline: none; }
.shell-search button {
  padding: 0.45rem 0.8rem;
  background: #6366F1;
  border: none;
  color: #fff;
  cursor: pointer;
  font-size: 0.82rem;
}
.shell-search button:hover { background: #4F46E5; }

/* The account controls sit at the right edge of the bar.
 *
 * They had `margin-left: 1rem`, on the reasoning — written in the comment above `.shell-search`
 * — that the flexible search element would absorb the spare width and push them over. It
 * cannot: `.shell-search` is capped at `max-width: 420px`, so past that width it stops growing
 * and nobody claims the rest. All the slack collected at the far right and the whole bar sat
 * bunched against the left, while every other row on the page spanned the full width.
 *
 * `auto` gives the leftover space to this margin instead, which is what the mobile rule below
 * has always done. The search keeps its cap: a search field stretched across a 2560px monitor
 * is not an improvement. */
#appShell .shell-right { margin-left: auto; }

@media (max-width: 768px) {
  /* Search keeps its place on a phone — it is the main way to find anything there, and the
     game tabs are already hidden at this width. It wraps to its own line rather than being
     squeezed to nothing beside the cart. */
  #appShell { flex-wrap: wrap; }
  .shell-search { order: 3; flex-basis: 100%; margin: 0.6rem 0 0; max-width: none; }
  .shell-search input { min-height: 40px; }
  /* `margin-left: auto` is the base rule now, so it is not repeated here. It used to differ:
     the desktop bar had 1rem and only the phone pushed the controls to the edge. */
}

/* ── Autocomplete under the bar's search ─────────────────────────────────────
   The dropdown is absolutely positioned at top:100% of its wrapper, so the search box must
   not clip it. `overflow: hidden` above was there to round the ends of a flex row, and it
   sheared the suggestions down to a sliver inside the box — landing.html had already learned
   this and solved it the same way. The rounding moves onto the end children instead.

   Suggestion rows carry a thumbnail, a name, a set line and a price, which do not fit in a
   420px bar. The list is allowed to be wider than the input it hangs from, and to grow to the
   right, since the box sits left of centre in the bar. */
.shell-search { overflow: visible; }
.shell-search input { border-radius: 8px 0 0 8px; }
.shell-search button { border-radius: 0 8px 8px 0; }

.shell-search .ac-dropdown {
  min-width: 380px;
  width: max-content;
  max-width: min(560px, calc(100vw - 2rem));
  border-radius: 8px;
  border-top: 1px solid rgba(255,255,255,0.2);
  margin-top: 0.35rem;
  /* Above the sticky bar's own stacking context (#appShell is z-index 200). */
  z-index: 300;
}

@media (max-width: 768px) {
  /* Full width on a phone: the bar is already wrapped, and a 380px minimum would overflow
     a 375px viewport. */
  .shell-search .ac-dropdown { min-width: 0; width: 100%; max-width: none; }
}

/* ═══ TOUCH TARGETS ═══
 *
 * The same 44px floor style.css has carried since the storefront was built — copied here
 * because the pages that load shell.css do not load style.css, so every control on
 * /landing.html and /g/:game was exempt from it. Those are the two pages a customer meets
 * first and browses on a phone: the category tabs, the trend tabs, the "Browse All" links,
 * the game and set tiles, and the Add button on every card.
 *
 * The selector list is this page family's own controls, not style.css's, because the two sets
 * share almost no class names — copying that rule verbatim would have looked like a fix and
 * matched nothing. `.add-btn` appears in both and is deliberately repeated.
 *
 * 44px is the figure both Apple and Google publish as the minimum comfortable target, and
 * `pointer: coarse` is what distinguishes a finger from a mouse — a desktop visitor keeps the
 * compact layout.
 */
@media (hover: none) and (pointer: coarse) {
  button,
  .cta,
  .tab,
  .trend-tab,
  .section-link,
  .add-btn,
  .game-tile,
  .set-tile {
    min-height: 44px;
    min-width: 44px;
  }

  /* Links laid out inline need the height to actually apply. */
  a.cta,
  a.section-link {
    display: inline-flex;
    align-items: center;
  }

  input,
  select,
  textarea {
    font-size: 16px; /* Below 16px iOS zooms the whole page on focus. */
    min-height: 44px;
  }

  /* The global search sets its own 40px above; a finger gets the full target. */
  .shell-search input { min-height: 44px; }
}

/* ═══ MY ORDERS ═══
 *
 * Sits beside the cart and matches it, because they are the same kind of thing to a shopper:
 * one is what they are about to buy, the other is what they already bought. It lived two
 * clicks inside the avatar menu, which is a strange place for the question customers ask most
 * often after paying.
 *
 * Below 640px the label drops and the icon carries it, so the bar does not wrap on a phone —
 * the same treatment the cart count already gets.
 */
.shell-orders-btn {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 1rem;
  background: #1E293B;
  border: 1px solid #334155;
  border-radius: 8px;
  color: #F8FAFC;
  font-size: 0.82rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.2s;
}
.shell-orders-btn:hover { background: #334155; }

/* `display: flex` above beats the browser's own `[hidden] { display: none }`, so without this
   the button is visible to a signed-out visitor — the attribute is set and does nothing. The
   same trap `.shell-dropdown[hidden]` above exists to avoid. */
.shell-orders-btn[hidden] { display: none; }

@media (max-width: 640px) {
  .shell-orders-label { display: none; }
  .shell-orders-btn { padding: 0.4rem 0.7rem; }
}

/* ─── PER-GAME ROWS IN THE MARKET ────────────────────────────────────────────
   The market's per-game rows became carousels, and the tiles stopped lining up: the
   price and the Add button sat at a different height in every card, because a card
   with a variant line ("0001 - Borderless") or a FOIL badge is taller than one without.

   The flat grid never had this — a CSS grid stretches its items, so every tile in a row
   is the height of the tallest and `.card-item .card-condition { margin-top: auto }` in
   style.css has slack to push the bottom block down with. A flex track stretches the
   *cell*, but the tile inside it is a block box sized by its own content, so that slack
   was never there.

   Two lines give it back. Scoped to `.game-row` on purpose: the landing page and the game
   pages put a different tile in their carousels, and neither was asked to change.

   Here rather than in style.css because that file is design-locked, which is also why the
   carousel's own styles live in this file (see the CAROUSEL block above). */
.game-row .carousel-cell {
  display: flex;
}
.game-row .carousel-cell > .card-item {
  flex: 1 1 auto;
  min-width: 0;
}
