/* Shop page only. Same rule as the shell: no colour, no duration, no radius
 * that is not a token. */

.phead {
  padding: var(--s-6) var(--s-6) var(--s-5);
  border-bottom: 1px solid var(--ln);
  position: relative; overflow: hidden;
  background: radial-gradient(560px circle at var(--mx, 50%) var(--my, 0%), var(--spot), transparent 62%);
}
.phead > * { position: relative; z-index: 2; }
.phead h1 { margin: 0 0 7px; font-size: var(--t-h2); }
.phead .sub { margin: 0; color: var(--mut); font-size: var(--t-sm); max-width: 58ch; }
.hint { color: var(--mut); font-size: var(--t-sm); max-width: 56ch; margin: 0 0 var(--s-4); }

/* ── all access ─────────────────────────────────────────────────────────── */
.plan { padding: var(--s-6); border-bottom: 1px solid var(--ln); }
.durs { display: flex; gap: var(--s-2); flex-wrap: wrap; margin: 0 0 var(--s-5); min-height: 38px; }
.durs button {
  appearance: none; cursor: pointer; font: inherit; font-size: var(--t-xs);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--ln); border-radius: var(--rad-soft);
  background: transparent; color: var(--mut);
  transition: color var(--t-tint) ease, border-color var(--t-tint) ease, box-shadow var(--t-move) var(--ease);
}
.durs button:hover { color: var(--fg); border-color: var(--mut); }
.durs button[aria-pressed="true"] { color: var(--acc-txt); border-color: var(--acc); box-shadow: 0 0 24px -12px var(--acc); }

/* The button sits WITH the price, not at the far end of the perks list.
 *
 * This was `1fr auto` with `align-items: end`, so the price and the five perk
 * lines filled the left column and the single most important control on the shop
 * was pushed to the bottom-right corner, level with the last perk and about
 * eight hundred pixels from the figure it buys. Half the section was empty and
 * the call to action was stranded in it.
 *
 * A price panel is one thing: what it costs, what it includes, and the button.
 * The column it lives in is capped so a 1180px measure does not stretch a price
 * and a list of five short lines across the whole window, which is the same
 * "everything is too big" problem the max-width on main was added to fix. */
.pricebox {
  display: grid;
  grid-template-columns: minmax(0, 34rem);
  gap: var(--s-5);
  align-items: start;
}
.pricebox > div:last-child { justify-self: start; }
.pricebox .btn { min-width: 15rem; justify-content: center; }
/* Full width on a phone. A 240px button floating in a 294px column reads as
   tentative, and this is the one control the page is for. */
@media (max-width: 620px) {
  .pricebox .btn { width: 100%; min-width: 0; }
}

.amt {
  font-size: clamp(36px, 6.4vw, 58px); line-height: 1; font-weight: 600;
  letter-spacing: -0.03em; display: flex; align-items: baseline; gap: var(--s-2);
  font-variant-numeric: tabular-nums;
}
.amt u { text-decoration: none; font-size: 0.34em; font-weight: 400; color: var(--mut); letter-spacing: -0.01em; }
.tier { color: var(--acc-txt); margin-bottom: 9px; }
.perday { font-size: var(--t-sm); color: var(--mut); margin-top: var(--s-3); font-family: var(--mono); }
.perday i { font-style: normal; color: var(--acc-txt); }

/* What the tier includes.
 *
 * The card carried a tier name and a price and never said what the name buys,
 * so "Diamond" was a word next to a number. The last row is deliberately a
 * cross rather than being left off: a limit somebody discovers after paying
 * costs far more than one they read before. */
.perks { list-style: none; margin: var(--s-5) 0 0; padding: 0; display: grid; gap: 7px; }
.perks li {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s-2);
  align-items: start; font-size: var(--t-sm); line-height: var(--lh-body);
}
.perks .ic { width: 15px; height: 15px; color: var(--ok); margin-top: 3px; }
.perks li.off { color: var(--mut); }
.perks li.off .ic { color: var(--mut); }

.pay { display: flex; gap: 7px; flex-wrap: wrap; margin-top: var(--s-4); }
.pay span {
  font-size: var(--t-xs); color: var(--mut);
  border: 1px solid var(--ln); padding: 6px var(--s-3); border-radius: var(--rad-soft);
  transition: color var(--t-tint) ease, border-color var(--t-tint) ease;
}
.pay span:hover { color: var(--fg); border-color: var(--mut); }
.pay span.off { opacity: 0.38; }

/* ── the bundles ────────────────────────────────────────────────────────────
 *
 * A section of cards, not a single strip. The strip drew ONE bundle, so a shop
 * with three packs advertised one and hid the rest, and it summarised the
 * contents as a comma-separated run of titles, which is read as one grey line
 * and skipped. A pack is worth exactly what is in it, so what is in it is the
 * body of the card. */
.bundle { padding: var(--s-6); border-bottom: 1px solid var(--ln); }
.bhead { margin-bottom: var(--s-5); }
.bhead h2 { margin: var(--s-1) 0 0; font-size: var(--t-h2); }

.bgrid {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr));
}

.bcard {
  display: flex; flex-direction: column; gap: var(--s-3);
  padding: var(--s-5);
  background: var(--sur);
  border: 1px solid var(--ln);
  border-radius: var(--rad-large);
  position: relative;
  transition: border-color var(--t-tint) var(--ease), transform var(--t-move) var(--ease);
}
.bcard:hover { border-color: var(--edge); transform: translateY(-2px); }
.bcard h3 { margin: 0; font-size: var(--t-h3); font-weight: 600; }
.bcount { margin: 0; color: var(--mut); font-size: var(--t-label); letter-spacing: var(--track-label); text-transform: uppercase; }
.bdesc { margin: 0; color: var(--mut); font-size: var(--t-sm); }

/* One row per game, with its art. Somebody deciding on a pack is checking
   whether the two they actually want are in it. */
.bgames { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.bgames li {
  display: flex; align-items: center; gap: var(--s-3);
  min-width: 0; font-size: var(--t-sm);
}
.bgames img, .bgames i {
  width: 26px; height: 26px; flex: none;
  border-radius: var(--rad-soft); object-fit: cover;
  background: var(--inp); border: 1px solid var(--ln);
}
.bgames i {
  display: grid; place-items: center; font-style: normal;
  font-weight: 600; font-size: var(--t-xs); color: var(--mut);
}
.bgames span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bmore span { color: var(--mut); font-size: var(--t-xs); }

/* Pinned to the bottom, and STACKED rather than wrapped.
 *
 * It was a flex row that wrapped when it ran out of width, so a pack with a
 * longer price put its button on a second line while the pack beside it kept
 * both on one. Two cards of the same height then had their buttons at different
 * heights, which is the specific kind of small misalignment that reads as
 * carelessness. A row that always breaks is aligned; a row that breaks sometimes
 * is not. */
.bfoot {
  margin-top: auto; padding-top: var(--s-4);
  display: grid; gap: var(--s-3); justify-items: stretch;
}
.bfoot .btn { width: 100%; justify-content: center; }
.bprice .amt { font-size: var(--t-h2); font-weight: 600; letter-spacing: var(--track-display); }
.save {
  align-self: flex-start;
  padding: 0.1em 0.5em; border-radius: var(--rad-pill);
  background: var(--acc); color: var(--on-acc);
  font-size: var(--t-label); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
}
.was {
  text-decoration: line-through; color: var(--mut); font-size: var(--t-sm); font-weight: 400;
  margin-left: var(--s-2); font-family: var(--mono); letter-spacing: 0;
}
@media (prefers-reduced-motion: reduce) {
  .bcard { transition: none; }
  .bcard:hover { transform: none; }
}

/* ── game cards ─────────────────────────────────────────────────────────── */
.count { font-size: var(--t-xs); color: var(--mut); font-family: var(--mono); padding: var(--s-4) var(--s-6) 0; }
.grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: var(--s-4); padding: var(--s-5) var(--s-6) var(--s-6);
}

/* The source art is square, so the card is square and nothing is cropped.
   Detail sits on the picture behind a scrim rather than in a panel below it. */
.card {
  padding: 0; aspect-ratio: 1; position: relative; overflow: hidden;
  display: flex; flex-direction: column; justify-content: flex-end;
  border: 1px solid var(--ln); border-radius: var(--rad-large); background: var(--inp);
}
.card .art {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: saturate(0.88) brightness(0.7);
  transition: transform 0.62s var(--ease), filter var(--t-move) ease;
}
.card:hover .art { transform: scale(1.055); filter: saturate(1.04) brightness(0.9); }
.card .art.none { display: grid; place-items: center; color: var(--rank); font-size: 44px; font-weight: 600; }
.veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, var(--scrim-top) 0%, transparent 24%, var(--scrim-mid) 56%, var(--scrim-base) 100%);
  transition: opacity var(--t-move) ease;
}
.card:hover .veil { opacity: 0.9; }
.card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px; z-index: 3;
  background: var(--acc); box-shadow: 0 0 14px -1px var(--acc);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-move) var(--ease);
}
.card:hover::before { transform: scaleX(1); }
.cbody { position: relative; z-index: 2; padding: 0 var(--s-4) var(--s-4); transition: transform var(--t-move) var(--ease); }
.card:hover .cbody { transform: translateY(-2px); }
.card h4 { margin: 0 0 3px; font-size: var(--t-body); font-weight: 600; line-height: 1.3; letter-spacing: -0.015em; text-shadow: 0 1px 12px var(--scrim-base); }
.card .price { font-size: 26px; font-weight: 600; display: block; letter-spacing: -0.03em; margin-top: 6px; text-shadow: 0 1px 14px var(--scrim-base); }
.card .stk { font-size: 11.5px; color: var(--card-meta); font-family: var(--mono); margin-top: 3px; display: block; text-shadow: 0 1px 10px var(--scrim-base); }
.card .go { margin-top: var(--s-3); width: 100%; }

/* Two states stacked in one control: at rest it offers to show you the game,
   on approach it commits to selling it. The label rolls over rather than
   swapping, so the change reads as one object turning rather than two. */
.btn.morph { position: relative; overflow: hidden; }
.btn.morph .ph { display: inline-flex; align-items: center; justify-content: center; gap: 7px; transition: transform var(--t-move) var(--ease), opacity var(--t-tint) ease; }
.btn.morph .ph.b { position: absolute; inset: 0; transform: translateY(112%); opacity: 0; }
.card:hover .btn.morph, .card:focus-within .btn.morph {
  background: var(--acc); color: var(--on-acc); border-color: var(--acc);
  box-shadow: inset 0 1px 0 var(--on-acc-sheen-hover), 0 0 28px -8px var(--acc);
}
.card:hover .btn.morph .ph.a, .card:focus-within .btn.morph .ph.a { transform: translateY(-112%); opacity: 0; }
.card:hover .btn.morph .ph.b, .card:focus-within .btn.morph .ph.b { transform: none; opacity: 1; }

/* Where a checkout failure reports itself: next to the button that failed,
   never in a corner the customer is not looking at. */
.checkout-msg { font-size: var(--t-sm); color: var(--mut); margin: var(--s-3) 0 0; }
.checkout-msg[data-kind="bad"], .checkout-msg[data-kind="warn"] { color: var(--warn); }
.checkout-msg:empty { display: none; }
.msg-lead { font-weight: 600; color: var(--fg); }
.msg-next { color: var(--mut); }

/* The real charge, stated beside a converted price.
 *
 * Prices follow the reader's language, and create-checkout.ts charges in baht and
 * only in baht. So a converted figure is an approximation of a charge that will
 * not match it, and the place to say so is next to the number, not in the small
 * print at the bottom. */
.charged {
  margin-top: var(--s-3); padding-top: var(--s-3);
  border-top: 1px solid var(--ln);
  color: var(--mut); font-size: var(--t-xs); line-height: var(--lh-body);
}
.charged b { color: var(--fg); font-weight: 500; }
