/*
 * VultureHUB store — components.
 *
 * Read this as five regions, in the order a shopper meets them: the header, the hero, the left
 * rail, the package grid, and the things that sit on top (cart drawer, package dialog, sale
 * popup). Anything shared — buttons, fields, badges — is at the end.
 */

/* ============================================================ layout */

.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.page > main { flex: 1; }

/*
 * Products on the left, rail on the right.
 *
 * The rail used to run down the left edge carrying the category list, which put navigation
 * ahead of the catalogue. Now that categories are tabs across the top, what is left in the rail
 * is a fundraising bar, a recent-purchases feed and a support link, and none of those should
 * stand between the page edge and the thing being sold.
 *
 * Products come first in the DOM too, so tab order and a screen reader reach a package before
 * the fundraising panel.
 */
.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--rail);
  gap: 26px;
  align-items: start;
  padding: 24px 0 64px;
}

@media (max-width: 1080px) {
  .layout {
    display: flex;
    flex-direction: column;
    /*
     * Explicitly stretch. The desktop grid sets `align-items: start` so the sticky rail hugs
     * the top, and that value carries into this flex column, where it means "size every row to
     * its content" and lets a wide child set the width of the whole page.
     */
    align-items: stretch;
    gap: 18px;
    padding-top: 18px;
  }
  /* A flex item defaults to `min-width: auto`, meaning "never shrink below your content". Any
     child wider than the screen would otherwise grow the column and give the page sideways
     scroll instead of scrolling inside itself. */
  .layout-main, .rail { min-width: 0; }
}

/* ============================================================ masthead */

/*
 * A full-bleed band of artwork with the store's identity standing on it.
 *
 * Nothing in here is boxed. The band runs edge to edge, the art runs behind everything, and the
 * only structure is a dark wash heavy enough to keep white text at contrast over any picture
 * somebody uploads. Content is what sits in a column; the masthead is the page's ground.
 */
.top {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 22px 0 74px;
  min-height: 400px;
  display: flex;
  flex-direction: column;
  background: var(--void-deep);
}
.top-art {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 42%;
}
/*
 * Three washes, and each is doing one job.
 *
 * A flat pass takes any artwork down far enough that white text holds contrast on it. A radial
 * pass darkens the middle, and only the middle, because that is where the logo and both labels
 * sit — the alternative is dimming the whole picture to protect one band of text, which is how
 * a hero ends up looking like a grey rectangle with a logo on it. The bottom fade lands the
 * band on the page colour so there is no seam under it.
 */
.top::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(58% 86% at 50% 48%, rgba(9, 10, 15, 0.72), transparent 74%),
    linear-gradient(to bottom, rgba(9, 10, 15, 0.5) 0%, rgba(9, 10, 15, 0.24) 34%, rgba(9, 10, 15, 0.55) 78%, var(--void) 100%);
}

/* ---- the nav row: two links and the account, and nothing else */

.top-row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 40px;
}
.top-home {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.72);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.6);
}
.top-home:hover { color: #fff; }

/*
 * The store button.
 *
 * The apex serves the news and the store together, so the one thing a reader must always be
 * able to find is the way into the catalogue. It sits where "Home" sits on a store page —
 * one slot, always pointing at wherever you are not — and it is a filled button rather than a
 * text link because it is the only thing in the chrome anybody is looking for.
 */
.top-shop {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--violet);
  color: #fff;
  font-weight: 700;
  font-size: 0.9rem;
  white-space: nowrap;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
  transition: background var(--fast), transform var(--fast);
}
.top-shop:hover { background: var(--violet-bright); transform: translateY(-1px); }

.top-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ---- the identity row: address, logo, Discord */

.top-main {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 30px;
  /* Centres the identity row in whatever height is left under the nav row. */
  flex: 1;
  align-content: center;
  margin-top: 26px;
}

.top-logo { justify-self: center; display: block; }
.top-logo img {
  display: block;
  width: auto;
  max-width: min(420px, 40vw);
  max-height: 190px;
  filter: drop-shadow(0 12px 34px rgba(0, 0, 0, 0.65));
}

/*
 * The two flanking calls to action are mirror images: the address reads inward from the left,
 * Discord reads outward from the right, and both point at the logo between them.
 */
.top-cta {
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.top-cta.play { justify-self: end; text-align: right; }
.top-cta.discord { justify-self: start; cursor: pointer; }

.top-cta-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.top-cta-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.32rem;
  letter-spacing: -0.02em;
  color: #fff;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.7);
}
.top-cta-sub {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.66);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.7);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.top-cta:hover .top-cta-sub { color: rgba(255, 255, 255, 0.9); }

/* The orb. Solid, round and large enough to be the thing a thumb goes for. */
.top-orb {
  position: relative;
  display: grid;
  place-items: center;
  width: 58px; height: 58px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--violet);
  color: #fff;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  transition: transform var(--fast), background var(--fast);
}
.top-cta:hover .top-orb { transform: scale(1.06); background: var(--violet-bright); }
.top-orb.discord { background: #5865f2; }
.top-cta.discord:hover .top-orb { background: #6b77f5; }

/* The live player count, on the button that copies the address. */
.top-orb-badge {
  position: absolute;
  top: -5px; left: -12px;
  min-width: 26px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ok);
  color: #06210f;
  font-size: 0.72rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.5);
}

@media (max-width: 900px) {
  .top { padding: 16px 0 42px; }
  .top-main {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 22px;
    margin-top: 24px;
  }
  .top-cta.play, .top-cta.discord { justify-self: center; }
  .top-cta.play { order: 2; }
  .top-logo { order: 1; }
  .top-cta.discord { order: 3; }
  .top-logo img { max-width: 74vw; max-height: 140px; }
  .top-cta-title { font-size: 1.15rem; }
  .top-orb { width: 50px; height: 50px; }
}

/*
 * Search.
 *
 * On the category tab row rather than behind an icon. A store's search is used by people who
 * already know what they want and are impatient about it; hiding it behind a click to save
 * 200px is a trade against exactly those shoppers.
 */
.tab-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  max-width: 260px;
  margin-left: auto;
  padding-left: 10px;
}
.tab-search .ic {
  position: absolute;
  left: 11px;
  display: grid;
  place-items: center;
  color: var(--text-mute);
  pointer-events: none;
}
/* Typed against the attribute so this beats the generic `input[type=search]` rule further
   down the file — equal specificity there, and it comes later, so it was winning on padding
   and dropping the text under the magnifier. */
.tab-search input[type='search'] {
  width: 100%;
  padding: 8px 14px 8px 36px;
  font-size: 0.88rem;
  border-radius: 999px;
  background: var(--surface-2);
  border-color: var(--line);
}
.tab-search input[type='search']:focus { background: var(--void-deep); }
.tab-search input::-webkit-search-cancel-button { filter: invert(0.6); }


.menu-search {
  position: relative;
  display: flex;
  align-items: center;
  margin-bottom: 14px;
}
.menu-search .ic {
  position: absolute;
  left: 12px;
  display: grid;
  place-items: center;
  color: var(--text-mute);
  pointer-events: none;
}
.menu-search input[type='search'] { padding-left: 36px; border-radius: 999px; }

.search-page {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
}
.search-page .ic {
  position: absolute;
  left: 14px;
  display: grid;
  place-items: center;
  color: var(--text-mute);
  pointer-events: none;
}
.search-page input[type='search'] {
  flex: 1;
  padding: 13px 16px 13px 44px;
  font-size: 1rem;
  border-radius: 999px;
}

/* Currency and language pickers: a native select, restyled, because a custom dropdown here
   would be a lot of JavaScript to reproduce something the browser already does well. */
.picker {
  position: relative;
  display: flex;
  align-items: center;
}
.top-tools .picker select {
  border-color: rgba(255, 255, 255, 0.18);
  background: rgba(9, 10, 15, 0.4);
  backdrop-filter: blur(8px);
  color: rgba(255, 255, 255, 0.85);
}
.top-tools .picker select:hover { border-color: rgba(255, 255, 255, 0.4); color: #fff; }
.picker select {
  appearance: none;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-dim);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 7px 26px 7px 11px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--fast), color var(--fast);
}
.picker select:hover { border-color: var(--violet); color: var(--text); }
.picker::after {
  content: '';
  position: absolute;
  right: 10px;
  width: 6px; height: 6px;
  border-right: 2px solid var(--text-mute);
  border-bottom: 2px solid var(--text-mute);
  transform: translateY(-2px) rotate(45deg);
  pointer-events: none;
}
/* Below 900px the pickers move into the menu sheet, where there is room for a label. */
@media (max-width: 900px) { .picker { display: none; } }

/* The hamburger. Hidden on desktop, where everything already fits in the bar. */
.menu-btn {
  display: none;
  width: 38px; height: 38px;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-dim);
  cursor: pointer;
}
.menu-btn:hover { border-color: var(--violet); color: var(--text); }
@media (max-width: 900px) {
  .menu-btn { display: grid; }
  .tab-search { max-width: none; padding-left: 0; }
  .cart-btn { padding: 9px 12px; }
}

/* The player chip: head, name, edition. The single most-copied element on these stores, and
   the one that tells a shopper the site knows who it is delivering to. */
.chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 15px 5px 6px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font: inherit;
  color: var(--text);
  transition: border-color var(--fast), background var(--fast);
}
.chip:hover { border-color: var(--violet-bright); background: var(--surface-3); }
.chip > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
}
.chip img {
  width: 30px; height: 30px;
  border-radius: 6px;
  image-rendering: pixelated;
  background: var(--surface-3);
}
.chip-name {
  font-weight: 700;
  font-size: 0.87rem;
  line-height: 1.1;
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chip-label {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  line-height: 1;
}

/* Cart button, with the count as a crystal-lit badge. */
.cart-btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--grad-brand);
  color: #fff;
  font: inherit;
  font-weight: 700;
  font-size: 0.87rem;
  cursor: pointer;
  transition: background var(--fast);
}
.cart-btn:hover { background: var(--grad-brand-hot); }
.cart-count {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.32);
  font-size: 0.72rem;
  font-weight: 800;
}
.cart-btn[data-count='0'] .cart-count { display: none; }

/*
 * The cart once the masthead has scrolled off.
 *
 * Bottom-right rather than top, because the top of a scrolled page is where a shopper looks for
 * the thing they are reading and the bottom-right is where a thumb already is. Revealed by the
 * client, never rendered on top of the button it replaces.
 */
.cart-fab {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 55;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 13px 19px;
  border: none;
  border-radius: 999px;
  background: var(--violet);
  color: #fff;
  font: inherit;
  font-weight: 700;
  font-size: 0.9rem;
  cursor: pointer;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
  transition: background var(--fast);
}
.cart-fab:hover { background: var(--violet-bright); }
.cart-fab[hidden] { display: none; }
.cart-fab[data-count='0'] { display: none; }
@media (max-width: 640px) { .cart-fab { right: 14px; bottom: 14px; } }

/* ============================================================ hero */

/*
 * The account bar.
 *
 * A full-width row directly above the first thing anyone clicks. Signed out it is coloured and
 * carries an arrow, because at that point it is the most useful thing on the page: a shopper
 * who fills a basket before naming an account has to be sent back for it. Signed in it drops to
 * a quiet confirmation with the skin head doing the work.
 */
.who-bar {
  display: flex;
  align-items: stretch;
  gap: 12px;
  margin: 26px 0 0;
}

.who {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 12px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}
.who:hover { border-color: var(--violet); background: var(--surface-2); }
.who img {
  width: 44px; height: 44px;
  flex-shrink: 0;
  border-radius: 9px;
  image-rendering: pixelated;
  background: var(--surface-3);
}
.who-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.who-label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.who-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.who-go {
  margin-left: auto;
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-dim);
  transition: background var(--fast), color var(--fast), transform var(--fast);
}
.who:hover .who-go { background: var(--violet); color: #fff; transform: translateX(2px); }

/* Signed out, it is the call to action. */
.who-bar.out .who {
  border-color: var(--violet);
  background: linear-gradient(100deg, var(--violet-wash), var(--surface) 70%);
}
.who-bar.out .who-label { color: var(--violet-text); }
.who-bar.out .who-go { background: var(--violet); color: #fff; }

.who-support {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
  padding: 0 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  font-weight: 700;
  font-size: 0.9rem;
  white-space: nowrap;
  transition: border-color var(--fast), color var(--fast);
}
.who-support:hover { border-color: var(--line-2); color: var(--text); }

/* The basket lives here too, beside the account it will be delivered to. */
.who-bar .cart-btn {
  flex-shrink: 0;
  padding: 0 22px;
  border-radius: var(--radius);
}

@media (max-width: 560px) {
  .who-bar { flex-wrap: wrap; }
  .who { flex-basis: 100%; }
  .who-support { flex: 1; justify-content: center; padding: 13px 18px; }
  .who-bar .cart-btn { flex: 1; justify-content: center; padding: 13px 18px; }
}

/*
 * The one line the masthead does not carry.
 *
 * A gamemode's tagline is the sentence that tells a shopper what they are buying into, and it
 * has no place on the artwork above — which is already carrying a logo, an address and two
 * counts. It leads the content instead, at the size of a caption.
 */
.scope-line {
  margin: 24px 0 0;
  max-width: 62ch;
  color: var(--text-dim);
  font-size: 1rem;
}

/* ============================================================ server picker */

/*
 * The gamemode picker.
 *
 * The artwork here is the thing to get right. Every gamemode logo is a square illustration on
 * transparency with the name built into it, so the old card cropped it to 16:9 and sliced the
 * lettering off the top and bottom. These show the logo whole, contained in a panel tinted with
 * the gamemode's own accent, which is what the art was drawn for.
 *
 * `--gm` is set per card from the server's accent and falls back to the house violet, so a
 * gamemode with no colour set still looks deliberate rather than broken.
 */
.gms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 18px;
  margin-top: 24px;
}
/*
 * One gamemode is the normal state of a new network, and `auto-fit` gives a single card the
 * whole row — which, against a 16:10 art panel, made one tile 800px tall. A lone card gets a
 * card's width instead.
 */
.gms:has(> a:only-child) { grid-template-columns: minmax(0, 420px); }

.gm {
  --gm: var(--violet-bright);
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  transition: border-color var(--fast), transform var(--fast);
}
.gm:hover { border-color: var(--gm); transform: translateY(-2px); }

/*
 * The logo panel. `contain` rather than `cover`, and padded, so nothing is ever clipped
 * whatever shape somebody uploads later.
 */
.gm-art {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  /* Belt and braces for the rule above, and for any browser without `:has`. */
  max-height: 280px;
  padding: 18px;
  background:
    radial-gradient(120% 90% at 50% 15%, color-mix(in srgb, var(--gm) 26%, transparent), transparent 70%),
    var(--surface-2);
  border-bottom: 1px solid var(--line-soft);
}
.gm-art img {
  max-width: 84%;
  max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.55));
  transition: transform var(--slow);
}
.gm:hover .gm-art img { transform: scale(1.05); }

.gm-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 5px;
  padding: 15px 17px 16px;
}
.gm-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: -0.015em;
  color: #fff;
}
/* Grows, so the meta row sits on one baseline across the whole grid however long a tagline is. */
.gm-tag {
  flex: 1;
  color: var(--text-mute);
  font-size: 0.86rem;
  line-height: 1.45;
  margin-bottom: 6px;
}
.gm-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.74rem;
  font-weight: 700;
}
.gm-live {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-mute);
}
.gm-live .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.gm-live.on { color: var(--ok); }
.gm-pkgs {
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-dim);
}

/* The chevron only appears on hover: three of them sitting there permanently is three arrows
   pointing at nothing in particular. */
.gm-go {
  position: absolute;
  top: 14px; right: 14px;
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--gm);
  color: #fff;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--fast), transform var(--fast);
}
.gm:hover .gm-go { opacity: 1; transform: none; }

/* ============================================================ left rail */

.rail {
  display: flex;
  flex-direction: column;
  gap: 16px;
  position: sticky;
  top: 20px;
}
@media (max-width: 1080px) {
  /*
   * This block sits after `.rail { display: flex }` on purpose. A media query adds no
   * specificity, so an earlier `display: contents` would lose to the later plain rule.
   */
  .rail { position: static; display: contents; }
}

.panel {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
/*
 * A panel header is a label, not a banner.
 *
 * Every one of these used to carry an accent icon, uppercase type and 0.09em of letterspacing —
 * three decorative decisions on a box containing two words, repeated five times down the rail.
 */
.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 15px;
  border-bottom: 1px solid var(--line-soft);
  font-weight: 650;
  font-size: 0.84rem;
  color: var(--text-dim);
}
.panel-head .ic { color: var(--text-mute); display: grid; place-items: center; }
.panel-body { padding: 13px 15px; }
.panel-body.flush { padding: 7px; }


/*
 * Category tabs.
 *
 * Sticky, because a shopper eight packages down a grid still needs to move sideways, and
 * because the alternative is scrolling back to the top for every category. Scrolls inside
 * itself on a narrow screen rather than wrapping to three rows.
 */
.cattabs-wrap {
  position: sticky;
  top: 0;
  z-index: 40;
  margin-top: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.cattabs {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px;
  overflow-x: auto;
  scrollbar-width: none;
}
/* The search is not a tab and must not scroll away with them. */
@media (min-width: 901px) { .cattabs { overflow: visible; flex-wrap: wrap; } }
.cattabs::-webkit-scrollbar { display: none; }
.cattab {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-weight: 600;
  font-size: 0.9rem;
  white-space: nowrap;
  transition: background var(--fast), color var(--fast);
}
.cattab:hover { background: var(--surface-2); color: var(--text); }
.cattab.on { background: var(--violet); color: #fff; }
.cattab img, .cattab .cattab-ic {
  width: 22px; height: 22px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  object-fit: contain;
  color: var(--violet-text);
  image-rendering: pixelated;
}
.cattab.on img, .cattab.on .cattab-ic { color: #fff; }
.cattab-n {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-mute);
  background: var(--surface-3);
  border-radius: 999px;
  padding: 1px 7px;
}
.cattab.on .cattab-n { background: rgba(0, 0, 0, 0.28); color: #fff; }

/* Children of the open tab only. Every category's children at once is how four gamemodes
   become forty links. */
.subtabs {
  display: flex;
  gap: 6px;
  padding: 9px 12px;
  overflow-x: auto;
  scrollbar-width: none;
  border-top: 1px solid var(--line-soft);
  background: var(--surface-2);
}
.subtabs::-webkit-scrollbar { display: none; }
.subtab {
  flex-shrink: 0;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--text-mute);
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
}
.subtab:hover { color: var(--text); border-color: var(--line-2); }
.subtab.on { background: var(--violet-wash); border-color: var(--violet); color: #fff; }

/*
 * The gamemode strip.
 *
 * Small logos rather than plain text: a player recognises the Lifesteal Atlantis artwork faster
 * than they read its name, and it is the same art they picked from on the landing page.
 */
.gmtabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
}
.gmtabs::-webkit-scrollbar { display: none; }
/* Only when it follows a banner; leading the page it inherits the masthead's own gap. */
.hero + .gmtabs { margin-top: 20px; }
.gmtab {
  --gm: var(--violet-bright);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  padding: 8px 15px 8px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-dim);
  font-weight: 700;
  font-size: 0.87rem;
  white-space: nowrap;
  transition: border-color var(--fast), color var(--fast), background var(--fast);
}
.gmtab img {
  width: 30px; height: 30px;
  object-fit: contain;
  flex-shrink: 0;
  opacity: 0.72;
  transition: opacity var(--fast);
}
.gmtab:hover { color: var(--text); border-color: var(--line-2); }
.gmtab:hover img { opacity: 1; }
.gmtab.on {
  color: #fff;
  border-color: var(--gm);
  background: color-mix(in srgb, var(--gm) 14%, var(--surface));
}
.gmtab.on img { opacity: 1; }

.donator {
  display: flex;
  align-items: center;
  gap: 13px;
}
.donator img {
  width: 46px; height: 46px;
  border-radius: 9px;
  image-rendering: pixelated;
  box-shadow: 0 0 0 2px var(--gold);
}
.donator-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1rem;
  color: #fff;
}
.donator-amt {
  font-size: 0.8rem;
  font-weight: 700;
  background: var(--grad-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.goal-bar {
  height: 9px;
  border-radius: 999px;
  background: var(--surface-3);
  overflow: hidden;
  margin: 9px 0 7px;
}
.goal-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--violet-bright);
  transition: width 600ms var(--ease);
}
.goal-meta {
  display: flex;
  justify-content: space-between;
  font-size: 0.78rem;
  color: var(--text-mute);
}
.goal-meta b { color: var(--text); }

/* ============================================================ package grid */

.section-head {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  margin: 30px 0 16px;
}
.section-head:first-child { margin-top: 0; }
.section-head h2 { color: #fff; }
.section-head p {
  color: var(--text-mute);
  font-size: 0.9rem;
  margin: 0;
}
.section-head .spacer { flex: 1; }

.grid {
  display: grid;
  gap: 18px;
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
}
@media (max-width: 1240px) { .grid { grid-template-columns: repeat(min(var(--cols, 3), 3), minmax(0, 1fr)); } }
@media (max-width: 900px)  { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .grid { grid-template-columns: 1fr; } }

/*
 * The package card.
 *
 * Image, name, one line of description, price, two actions. The hover lifts it and lights the
 * crystal edge — the only place cyan appears at this size, so it reads as "this one" rather
 * than decoration.
 */
/*
 * Card hover is deliberately restrained.
 *
 * A category page shows a dozen of these and a shopper's pointer crosses most of them on the
 * way to the one they want. A 5px lift over 240ms, with the artwork scaling at the same time,
 * turns that journey into a wave — it reads as the page being busy rather than as feedback.
 * Two pixels in 120ms is enough to say "this one", and gets out of the way.
 */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  overflow: hidden;
  transition: transform var(--fast), border-color var(--fast), box-shadow var(--fast);
}
.card:hover {
  transform: translateY(-2px);
  border-color: var(--violet-bright);
  box-shadow: var(--glow-violet);
}
/* A gradient hairline along the top edge, revealed on hover. */
.card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--grad-crystal);
  opacity: 0;
  transition: opacity var(--med);
  z-index: 2;
}
.card:hover::before { opacity: 1; }

/*
 * The art well.
 *
 * Shorter than it was — at 4:3 the picture was sixty per cent of the card and the words were
 * an afterthought, which is the wrong balance for something a shopper is deciding about rather
 * than admiring. The tint comes from the card's accent, so a ladder of three ranks does not
 * read as three identical crowns on three identical purple squares.
 */
.card-art {
  position: relative;
  aspect-ratio: 16 / 10;
  /*
   * A flat plate.
   *
   * Every card used to carry its own radial glow behind the artwork, which meant a grid of
   * twelve packages was twelve competing light sources. The icons are rendered with their own
   * soft light baked in; the plate's job is to sit still behind them.
   */
  background: var(--surface-2);
  overflow: hidden;
}
.card-art img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 14px;
  filter: drop-shadow(0 6px 18px rgba(4, 0, 14, 0.6));
}
.card-art.empty {
  display: grid;
  place-items: center;
  color: var(--violet-bright);
  opacity: 0.4;
}

/*
 * `flex: 1 0 auto` — grow to fill, but never shrink below the content.
 *
 * With the default `flex-shrink: 1`, a card sharing a grid row with a shorter sibling had its
 * body squashed to the row height and the last perk clipped by `overflow: hidden` on the card.
 * The symptom was a line of text sliced in half by the price rule, on the narrow layout only,
 * where the wrapping made the two cards disagree about how tall they needed to be.
 */
.card-body {
  padding: 14px 16px 14px;
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
}
.card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.02rem;
  color: #fff;
  line-height: 1.25;
}
.card-sub {
  font-size: 0.83rem;
  color: var(--text-mute);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/*
 * The footer wraps as a unit.
 *
 * "Add to cart" is 11 characters; "Añadir al carrito" is 17 and "Adicionar ao carrinho" is 21.
 * Sized to the English label, the Spanish one runs off the edge of the card — which is exactly
 * the kind of thing that only shows up once the site is live in another language. Grouping the
 * two buttons lets them drop to their own row together rather than one of them being clipped.
 */
/*
 * Price on its own line, actions beneath it. Always.
 *
 * This used to be one wrapping row, which meant the layout depended on how wide the price
 * happened to be: "CA$5.99" fitted beside both buttons and "CA$19.99" did not, so two cards in
 * the same row ended up with their prices at different heights and the grid looked broken. Any
 * layout that changes shape with the length of a number will do that in some currency.
 */
.card-foot {
  padding: 13px 16px 15px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 11px;
  border-top: 1px solid var(--line-soft);
  background: var(--void-deep);
}
.card-actions {
  display: flex;
  gap: 8px;
  min-width: 0;
}
/* Details takes what it needs; the primary action takes the rest. */
.card-actions .btn { flex-shrink: 0; }
.card-actions .btn:last-child { flex: 1; justify-content: center; }
.price {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}
.price-row { display: flex; align-items: baseline; gap: 8px; }
.price b {
  font-family: var(--font-display);
  font-size: 1.42rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  /* Tabular figures so a column of prices lines up on the decimal. */
  font-variant-numeric: tabular-nums;
  background: var(--grad-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.price s {
  font-size: 0.82rem;
  color: var(--text-mute);
  text-decoration-color: var(--danger);
  font-variant-numeric: tabular-nums;
}


/* Social proof, off by default. Quiet enough that it reads as a fact rather than a boast. */
.sold {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-mute);
  margin-top: 2px;
}

/* Ribbons: sale, popular, new. Sale is the only one allowed magenta. */
.ribbon {
  position: absolute;
  top: 11px; left: 11px;
  z-index: 2;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #fff;
  background: var(--grad-brand);
  box-shadow: var(--shadow-sm);
}
.ribbon.sale { background: var(--grad-sale); }
.ribbon.cyan { background: var(--grad-crystal); color: #04141a; }
.ribbon.gold { background: var(--grad-gold); color: #2a1a00; }
.ribbon.right { left: auto; right: 11px; }

.soldout {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  background: rgba(6, 1, 15, 0.78);
  backdrop-filter: blur(2px);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--danger);
}

/* List mode: no image, one row per package. For long catalogues of keys and currency. */
.list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  transition: background var(--fast);
}
.row + .row { border-top: 1px solid var(--line-soft); }
.row:hover { background: var(--surface-2); }
.row-ic {
  width: 34px; height: 34px;
  border-radius: 7px;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--surface-3);
  image-rendering: pixelated;
}
.row-main { flex: 1; min-width: 0; }
.row-name {
  font-weight: 700;
  color: #fff;
  font-size: 0.95rem;
}
.row-sub {
  font-size: 0.8rem;
  color: var(--text-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row .price { align-items: flex-end; text-align: right; }
.row .price b { font-size: 1.05rem; }
@media (max-width: 560px) {
  .row { flex-wrap: wrap; }
  .row-main { flex-basis: 100%; order: 1; }
}

/* Compact mode: a dense grid of small tiles, for crate keys and cosmetics. */
/*
 * `auto-fit`, not `auto-fill`.
 *
 * With `auto-fill` a category of four keys laid out four 150px tiles against the left edge and
 * left two thirds of the page empty — the grid kept the empty columns. `auto-fit` collapses
 * them so the tiles grow to fill the row, which is what a shelf with four things on it should
 * look like.
 */
.compact {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(172px, 1fr));
  gap: 14px;
}
.tile {
  position: relative;
  padding: 14px 12px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: transform var(--fast), border-color var(--fast);
}
.tile:hover { transform: translateY(-2px); border-color: var(--cyan); }
.tile img {
  width: 76px; height: 76px;
  margin: 0 auto 11px;
  object-fit: contain;
}
/* The whole tile opens the details, so it says so on hover rather than looking inert. */
.tile::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  background: var(--grad-crystal);
  opacity: 0;
  transition: opacity var(--fast);
}
.tile:hover::after { opacity: 1; }
.tile-name {
  font-weight: 700;
  font-size: 0.85rem;
  color: #fff;
  line-height: 1.25;
  margin-bottom: 5px;
}
.tile .price b { font-size: 0.95rem; }

/* ============================================================ empty + notices */

.empty {
  padding: 56px 24px;
  text-align: center;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--text-mute);
}
.empty svg { margin: 0 auto 14px; opacity: 0.4; color: var(--violet-bright); }

.notice {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-2);
  font-size: 0.9rem;
}
.notice.warn { border-color: rgba(255, 180, 84, 0.4); background: rgba(255, 180, 84, 0.08); }
.notice.danger { border-color: rgba(255, 93, 115, 0.4); background: rgba(255, 93, 115, 0.08); }
.notice.ok { border-color: rgba(61, 220, 151, 0.4); background: rgba(61, 220, 151, 0.08); }
.notice.info { border-color: rgba(12, 204, 216, 0.4); background: rgba(12, 204, 216, 0.08); }
.notice .ic { flex-shrink: 0; margin-top: 1px; }

/* ============================================================ buttons + fields */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 17px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--grad-brand);
  color: #fff;
  font: inherit;
  font-weight: 700;
  font-size: 0.88rem;
  cursor: pointer;
  white-space: nowrap;
  /*
   * No coloured glow and no lift.
   *
   * A purple halo under every button — and there are three on each card — was most of what
   * made a category page feel like it was humming. A solid fill that gets one step lighter on
   * hover says "pressable" perfectly well.
   */
  transition: background var(--fast), border-color var(--fast);
}
.btn:hover { background: var(--grad-brand-hot); color: #fff; }
.btn:active { background: var(--violet-deep); }
.btn[disabled], .btn.disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.btn.ghost {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--text-dim);
  box-shadow: none;
}
.btn.ghost:hover { background: var(--surface-3); border-color: var(--violet); color: var(--text); }
.btn.crystal { background: var(--grad-crystal); color: #06232a; }
.btn.crystal:hover { background: var(--cyan-bright); color: #06232a; }
.btn.wide { width: 100%; }
.btn.lg { padding: 12px 24px; font-size: 0.96rem; }
.btn.sm { padding: 6px 12px; font-size: 0.8rem; }
.btn.danger { background: var(--danger); color: #2b0a0f; }

/* The added-to-cart flash. Swapping the label alone is too quiet to notice. */
.btn.added { background: var(--ok); color: #04231a; }

.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 15px; }
.field > label {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.02em;
}
.field .hint { font-size: 0.78rem; color: var(--text-mute); }
.field .err { font-size: 0.8rem; color: var(--danger); font-weight: 600; }

input[type='text'], input[type='email'], input[type='number'], input[type='password'],
input[type='url'], input[type='datetime-local'], input[type='search'], select, textarea {
  width: 100%;
  padding: 10px 13px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--void-deep);
  color: var(--text);
  font: inherit;
  font-size: 0.92rem;
  transition: border-color var(--fast), box-shadow var(--fast);
}
input::placeholder, textarea::placeholder { color: var(--text-mute); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--violet-bright);
  box-shadow: 0 0 0 3px rgba(180, 36, 252, 0.18);
}
textarea { resize: vertical; min-height: 96px; line-height: 1.55; }
select { cursor: pointer; }

/* A checkbox that is a real input, restyled — so it keeps its keyboard behaviour. */
.check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--text-dim);
  user-select: none;
}
.check input {
  appearance: none;
  flex-shrink: 0;
  width: 19px; height: 19px;
  margin: 1px 0 0;
  border-radius: 5px;
  border: 1px solid var(--line);
  background: var(--void-deep);
  cursor: pointer;
  transition: background var(--fast), border-color var(--fast);
  position: relative;
}
.check input:checked {
  background: var(--grad-brand);
  border-color: var(--violet-bright);
}
.check input:checked::after {
  content: '';
  position: absolute;
  left: 6px; top: 2px;
  width: 5px; height: 10px;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(42deg);
}
.check input:focus-visible { outline: 2px solid var(--cyan-bright); outline-offset: 2px; }
.check b { color: var(--text); font-weight: 700; }

/* Quantity stepper. */
.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--void-deep);
}
.qty button {
  width: 32px; height: 34px;
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: 1.1rem;
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.qty button:hover { background: var(--surface-3); color: #fff; }
.qty input {
  width: 46px;
  border: none;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  border-radius: 0;
  text-align: center;
  padding: 8px 0;
  font-weight: 700;
  background: transparent;
}
.qty input:focus { box-shadow: none; }
.qty input::-webkit-outer-spin-button,
.qty input::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* ============================================================ dialogs */

/*
 * Everything modal is a <dialog>.
 *
 * The browser gives focus trapping, Escape, inertness of the page behind, and the top layer
 * for free. Reimplementing those in JavaScript is a well-known source of accessibility bugs
 * and there is no reason to here.
 */
dialog {
  border: none;
  padding: 0;
  background: transparent;
  color: var(--text);
  max-width: min(680px, calc(100vw - 32px));
  width: 100%;
  max-height: calc(100vh - 60px);
}
dialog::backdrop {
  background: rgba(4, 0, 12, 0.74);
  backdrop-filter: blur(5px);
}
.sheet {
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  max-height: calc(100vh - 60px);
  display: flex;
  flex-direction: column;
}
.sheet-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 17px 20px;
  border-bottom: 1px solid var(--line-soft);
}
.sheet-head h2 { font-size: 1.15rem; color: #fff; }
.sheet-head .x {
  margin-left: auto;
  width: 32px; height: 32px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 8px;
  background: var(--surface-3);
  color: var(--text-dim);
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  transition: background var(--fast), color var(--fast);
}
.sheet-head .x:hover { background: var(--danger); color: #fff; }
.sheet-body { padding: 20px; overflow-y: auto; }
.sheet-foot {
  padding: 15px 20px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--void-deep);
}
.sheet-foot .spacer { flex: 1; }

/* Package detail dialog: art on the left, everything else on the right. */
.pkg-detail { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 22px; }
@media (max-width: 620px) { .pkg-detail { grid-template-columns: 1fr; } }
.pkg-art {
  border-radius: var(--radius);
  background: radial-gradient(110% 80% at 50% 0%, rgba(132, 24, 204, 0.3), var(--surface-3));
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: 16px;
}
.pkg-art img { object-fit: contain; max-height: 100%; }
.pkg-price {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 14px;
}
.pkg-price b {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 800;
  background: var(--grad-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.pkg-price s { color: var(--text-mute); font-size: 1rem; }

/* Staff-written description. Constrained so a wall of pasted markup still looks like the site. */
.rich { font-size: 0.93rem; color: var(--text-dim); line-height: 1.65; }
.rich h2, .rich h3, .rich h4 { color: #fff; margin: 1.1em 0 0.45em; font-size: 1rem; }
.rich ul, .rich ol { margin: 0 0 0.9em; padding-left: 1.3em; }
.rich li { margin-bottom: 0.3em; }
.rich li::marker { color: var(--violet-bright); }
.rich strong, .rich b { color: var(--text); }
.rich a { text-decoration: underline; text-underline-offset: 2px; }
.rich img { border-radius: var(--radius-sm); margin: 0.7em 0; }
.rich code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--void-deep);
  color: var(--cyan-bright);
}
.rich blockquote {
  margin: 0.8em 0;
  padding: 2px 0 2px 14px;
  border-left: 3px solid var(--violet);
  color: var(--text-mute);
}
.rich table { width: 100%; border-collapse: collapse; margin: 0.8em 0; font-size: 0.88rem; }
.rich th, .rich td { padding: 7px 10px; border: 1px solid var(--line); text-align: left; }
.rich th { background: var(--surface-3); color: var(--text); }

.meta-list { display: flex; flex-direction: column; gap: 7px; margin: 15px 0; }
.meta-item {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 0.85rem;
  color: var(--text-dim);
}
.meta-item .ic { color: var(--cyan); flex-shrink: 0; }

/* ============================================================ cart drawer */

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(430px, 100vw);
  z-index: 90;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform var(--med);
  visibility: hidden;
}
.drawer.open { transform: translateX(0); visibility: visible; }
.scrim {
  position: fixed;
  inset: 0;
  z-index: 89;
  background: rgba(4, 0, 12, 0.68);
  backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--med), visibility var(--med);
}
.scrim.on { opacity: 1; visibility: visible; }

.drawer-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line-soft);
}
.drawer-head h2 { font-size: 1.1rem; color: #fff; }
.drawer-body { flex: 1; overflow-y: auto; padding: 12px 20px; }
.drawer-foot {
  padding: 16px 20px 20px;
  border-top: 1px solid var(--line-soft);
  background: var(--void-deep);
}

.cart-line {
  display: flex;
  gap: 12px;
  padding: 13px 0;
}
.cart-line + .cart-line { border-top: 1px solid var(--line-soft); }
.cart-line img {
  width: 52px; height: 52px;
  border-radius: 8px;
  object-fit: contain;
  background: var(--surface-3);
  padding: 5px;
  flex-shrink: 0;
  image-rendering: pixelated;
}
/* Name and price on one line. They answer the same question — "what is this and what does it
   cost" — and splitting them across two rows left a hole where the second half should be. */
.cart-line-main { flex: 1; min-width: 0; }
.cart-line-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.cart-line-name {
  font-weight: 700;
  font-size: 0.9rem;
  color: #fff;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cart-line-gift {
  font-size: 0.76rem;
  color: var(--cyan);
  font-weight: 600;
}
.cart-line-controls {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 7px;
}
.cart-line-controls:empty { display: none; }
.cart-line-price {
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--gold-soft);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.link-danger {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.78rem;
  color: var(--text-mute);
  cursor: pointer;
  transition: color var(--fast);
}
.link-danger:hover { color: var(--danger); }

.totals { display: flex; flex-direction: column; gap: 7px; margin-bottom: 14px; }
.total-row {
  display: flex;
  justify-content: space-between;
  font-size: 0.88rem;
  color: var(--text-dim);
}
.total-row.discount { color: var(--ok); }
.total-row.grand {
  padding-top: 10px;
  margin-top: 3px;
  border-top: 1px solid var(--line);
  font-size: 1.06rem;
  font-weight: 800;
  color: #fff;
}
.total-row.grand b {
  font-family: var(--font-display);
  background: var(--grad-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.coupon-row { display: flex; gap: 8px; margin-bottom: 13px; }
.coupon-row input { flex: 1; text-transform: uppercase; }
.coupon-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(61, 220, 151, 0.14);
  border: 1px solid rgba(61, 220, 151, 0.4);
  color: var(--ok);
  font-size: 0.8rem;
  font-weight: 700;
  margin-bottom: 13px;
}

/* ============================================================ sale popup + banner */

/*
 * Announcements.
 *
 * Three shapes for the same table. A popup interrupts, a banner sits under the header, and a
 * ribbon runs edge to edge above everything. Management chooses; the difference is only where
 * it is drawn.
 */
.promo-sheet {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
.promo-art {
  width: 100%;
  aspect-ratio: 2 / 1;
  object-fit: cover;
}
.promo-body { padding: 24px 26px 26px; text-align: center; }
.promo-kicker {
  display: inline-block;
  padding: 5px 13px;
  border-radius: 999px;
  background: var(--grad-sale);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  margin-bottom: 13px;
}
.promo-body h2 { font-size: 1.7rem; color: #fff; margin-bottom: 9px; }
.promo-body .rich { max-width: 46ch; margin: 0 auto 18px; }
.promo-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* Countdown — four blocks, monospaced digits so they do not jitter as they tick. */
.countdown {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin: 16px 0 4px;
}
.cd-unit {
  min-width: 62px;
  padding: 9px 4px;
  border-radius: var(--radius-sm);
  background: var(--void-deep);
  border: 1px solid var(--line);
}
.cd-num {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  font-weight: 700;
  color: #fff;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.cd-lbl {
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-top: 4px;
}

.ribbon-bar {
  position: relative;
  z-index: 61;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 9px 20px;
  font-size: 0.87rem;
  font-weight: 600;
  color: #fff;
  background: var(--grad-sale);
  text-align: center;
}
.ribbon-bar.warning { background: var(--warn); color: #2a1a00; }
.ribbon-bar.info { background: var(--grad-crystal); color: #04141a; }
.ribbon-bar.success { background: var(--ok); color: #04231a; }
.ribbon-bar a { color: inherit; text-decoration: underline; font-weight: 800; }
.ribbon-bar .x {
  position: absolute;
  right: 12px;
  background: none;
  border: none;
  color: inherit;
  opacity: 0.75;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 2px 6px;
}
.ribbon-bar .x:hover { opacity: 1; }

.promo-banner {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 16px 20px;
  margin: 20px 0 0;
  border-radius: var(--radius);
  border: 1px solid var(--violet);
  background:
    linear-gradient(115deg, rgba(180, 36, 252, 0.2), rgba(12, 204, 216, 0.08) 62%, transparent),
    var(--surface-2);
}
.promo-banner img {
  width: 74px; height: 74px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  flex-shrink: 0;
}
.promo-banner-main { flex: 1; min-width: 0; }
.promo-banner h3 { color: #fff; margin-bottom: 3px; }
.promo-banner .rich { font-size: 0.86rem; }
@media (max-width: 640px) {
  .promo-banner { flex-direction: column; text-align: center; }
}

/* ============================================================ checkout + receipt */

.checkout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  gap: 26px;
  align-items: start;
  padding: 26px 0 64px;
}
@media (max-width: 980px) { .checkout { grid-template-columns: 1fr; } }

.pay-methods { display: flex; flex-direction: column; gap: 11px; }
.pay-method {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 15px 17px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}
.pay-method:hover { border-color: var(--violet); }
.pay-method input { appearance: none; position: absolute; }
.pay-method .radio {
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 2px solid var(--line);
  flex-shrink: 0;
  display: grid;
  place-items: center;
  transition: border-color var(--fast);
}
.pay-method input:checked ~ .radio { border-color: var(--violet-bright); }
.pay-method input:checked ~ .radio::after {
  content: '';
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--grad-brand);
}
.pay-method:has(input:checked) {
  border-color: var(--violet-bright);
  background: var(--violet-wash);
  box-shadow: var(--glow-violet);
}
.pay-method-main { flex: 1; }
.pay-method-name { font-weight: 700; color: #fff; font-size: 0.95rem; }
.pay-method-sub { font-size: 0.8rem; color: var(--text-mute); }
.pay-logos { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
.pay-logos span {
  padding: 3px 7px;
  border-radius: 4px;
  background: var(--surface-3);
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--text-mute);
}

.summary-card {
  position: sticky;
  top: 20px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  overflow: hidden;
}
.summary-lines { padding: 6px 18px; }
.summary-line {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 0;
  font-size: 0.88rem;
}
.summary-line + .summary-line { border-top: 1px solid var(--line-soft); }
.summary-line img {
  width: 38px; height: 38px;
  border-radius: 7px;
  object-fit: contain;
  background: var(--surface-3);
  padding: 4px;
  image-rendering: pixelated;
}
.summary-line .qty-tag {
  font-size: 0.75rem;
  color: var(--text-mute);
  font-weight: 700;
}
.summary-line .amt { margin-left: auto; font-weight: 700; color: var(--text); }

.receipt {
  max-width: 700px;
  margin: 40px auto 80px;
  text-align: center;
}
.receipt-mark {
  width: 84px; height: 84px;
  margin: 0 auto 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(61, 220, 151, 0.14);
  border: 2px solid var(--ok);
  color: var(--ok);
}
.receipt-mark.pending {
  background: rgba(255, 180, 84, 0.14);
  border-color: var(--warn);
  color: var(--warn);
}
.receipt-mark.failed {
  background: rgba(255, 93, 115, 0.14);
  border-color: var(--danger);
  color: var(--danger);
  box-shadow: none;
}
.receipt-ref {
  display: inline-block;
  margin: 14px 0 22px;
  padding: 7px 18px;
  border-radius: 999px;
  border: 1px dashed var(--line);
  background: var(--void-deep);
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--cyan-bright);
}
.receipt-items {
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  margin: 24px 0;
}

/* ============================================================ tables (order history) */

.table { width: 100%; border-collapse: collapse; font-size: 0.89rem; }
.table th {
  text-align: left;
  padding: 11px 14px;
  background: var(--surface-3);
  color: var(--text-dim);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.table td { padding: 12px 14px; border-top: 1px solid var(--line-soft); }
.table tbody tr:hover { background: var(--surface-2); }

.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.pill.paid { background: rgba(61, 220, 151, 0.16); color: var(--ok); }
.pill.pending { background: rgba(255, 180, 84, 0.16); color: var(--warn); }
.pill.failed, .pill.cancelled { background: rgba(127, 111, 158, 0.16); color: var(--text-mute); }
.pill.refunded { background: rgba(12, 204, 216, 0.16); color: var(--cyan); }
.pill.chargeback { background: rgba(255, 93, 115, 0.16); color: var(--danger); }

/* ============================================================ login dialog */

.login-editions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 16px;
}
.edition {
  position: relative;
  padding: 14px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
  cursor: pointer;
  text-align: center;
  transition: border-color var(--fast), background var(--fast);
}
.edition input { appearance: none; position: absolute; inset: 0; margin: 0; cursor: pointer; }
.edition:has(input:checked) {
  border-color: var(--violet-bright);
  background: var(--violet-wash);
  box-shadow: var(--glow-violet);
}
.edition .ed-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.98rem;
  color: #fff;
}
.edition .ed-sub { font-size: 0.75rem; color: var(--text-mute); }

/* The live preview of the name that will actually be used, dot and all. */
.name-preview {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--radius);
  background: var(--void-deep);
  border: 1px solid var(--line);
  margin-bottom: 15px;
}
.name-preview img {
  width: 40px; height: 40px;
  border-radius: 7px;
  image-rendering: pixelated;
  background: var(--surface-3);
}
.name-preview-main { min-width: 0; }
.name-preview-name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 1rem;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
}
.name-preview-note { font-size: 0.76rem; color: var(--text-mute); }
.name-preview .dot-hl { color: var(--cyan-bright); }

/* ============================================================ footer */

.footer {
  margin-top: auto;
  border-top: 1px solid var(--line-soft);
  background: rgba(6, 1, 15, 0.6);
  padding: 40px 0 30px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(3, 1fr);
  gap: 32px;
  margin-bottom: 30px;
}
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; } }
.footer h4 {
  font-size: 0.76rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 13px;
}
.footer-links { display: flex; flex-direction: column; gap: 8px; }
.footer-links a { color: var(--text-dim); font-size: 0.88rem; }
.footer-links a:hover { color: var(--cyan-bright); }
.footer-about p { font-size: 0.87rem; color: var(--text-mute); max-width: 40ch; }
.footer-bottom {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 22px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.8rem;
  color: var(--text-mute);
}
.footer-bottom .spacer { flex: 1; }
.pay-badges { display: flex; gap: 7px; flex-wrap: wrap; }
.pay-badges span {
  padding: 4px 9px;
  border-radius: 5px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--text-mute);
}

/* ============================================================ toasts */

.toasts {
  position: fixed;
  bottom: 20px; right: 20px;
  z-index: 120;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 13px 17px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  font-size: 0.9rem;
  font-weight: 600;
  pointer-events: auto;
  animation: toast-in 260ms var(--ease);
  max-width: 360px;
}
.toast.ok { border-color: rgba(61, 220, 151, 0.5); }
.toast.err { border-color: rgba(255, 93, 115, 0.5); }
.toast .ic { flex-shrink: 0; }
.toast.ok .ic { color: var(--ok); }
.toast.err .ic { color: var(--danger); }
.toast.leaving { animation: toast-out 200ms var(--ease) forwards; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
@keyframes toast-out {
  to { opacity: 0; transform: translateX(20px); }
}

/* ============================================================ misc */

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.83rem;
  color: var(--text-mute);
  margin-bottom: 6px;
}
.breadcrumb a { color: var(--text-mute); }
.breadcrumb a:hover { color: var(--cyan-bright); }
.breadcrumb .sep { opacity: 0.5; }

.stack { display: flex; flex-direction: column; gap: 16px; }
.row-gap { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.mt { margin-top: 20px; }
.muted { color: var(--text-mute); }
.center { text-align: center; }


/* ============================================================ menu sheet (mobile) */

/*
 * Everything the header cannot fit below 900px. A sheet rather than a dropdown, because a
 * dropdown this tall ends up scrolling behind a phone's address bar.
 */
.menu-sheet {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(340px, 88vw);
  z-index: 95;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform var(--med);
  visibility: hidden;
}
.menu-sheet.open { transform: translateX(0); visibility: visible; }

.menu-identity {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px;
  margin-bottom: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--void-deep);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.menu-identity:hover { border-color: var(--violet-bright); }
.menu-identity img {
  width: 42px; height: 42px;
  border-radius: 9px;
  image-rendering: pixelated;
  background: var(--surface-3);
  flex-shrink: 0;
}
.menu-identity > span { min-width: 0; flex: 1; }
.menu-identity-name {
  display: block;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.menu-identity-sub {
  display: block;
  font-size: 0.78rem;
  color: var(--text-mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.menu-chev { color: var(--text-mute); flex-shrink: 0; }

.menu-links { display: flex; flex-direction: column; gap: 2px; }
.menu-links a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-weight: 600;
}
.menu-links a:hover { background: var(--surface-3); color: #fff; }
.menu-links a svg { color: var(--violet-bright); flex-shrink: 0; }

.menu-prefs {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.picker.wide { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.picker.wide label {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.picker.wide select { width: 100%; padding: 10px 13px; font-size: 0.92rem; }
.picker.wide::after { display: none; }

/* ============================================================ narrow-screen sizing */

@media (max-width: 640px) {
  .shell { padding: 0 16px; }

  .hero { min-height: 172px; margin-top: 16px; border-radius: var(--radius); }
  .hero-body { padding: 22px 18px 20px; }
  .hero p { font-size: 0.92rem; }

  .section-head { margin: 24px 0 12px; flex-wrap: wrap; gap: 8px; }
  .section-head .btn { margin-left: auto; }

  /*
   * Two across, not one. A single column turns a category of six into six screens of
   * scrolling, and the shopper never sees that a cheaper option exists.
   */
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .card-art { aspect-ratio: 1; }
  .card-art img { padding: 10px; }
  /* A bottom padding, not zero. This override predates the perk list, when the body ended
     with the summary and the footer's own padding did the spacing — with three lines of perks
     under it, the last one sat flush against the price rule. */
  .card-body { padding: 11px 12px 12px; }
  .card-title { font-size: 0.92rem; }
  .card-sub { font-size: 0.78rem; -webkit-line-clamp: 2; }
  .card-foot { padding: 11px 12px 13px; gap: 10px; }
  /* Details is secondary on a phone; tapping the card itself opens it. */
  .card-actions .btn.ghost { display: none; }
  .price b { font-size: 1.08rem; }

  .compact { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 10px; }
  .tile img { width: 44px; height: 44px; }

  .drawer, .menu-sheet { width: 100vw; }
  .sheet-body { padding: 16px; }
  .sheet-foot { padding: 12px 16px; }
  .pkg-detail { gap: 16px; }

  .footer { padding: 30px 0 24px; }
  .footer-grid { gap: 24px; margin-bottom: 22px; }

  .receipt { margin: 24px auto 50px; }
  .receipt-mark { width: 68px; height: 68px; }
}

/* A card is a target on touch, not just a container. */
@media (hover: none) {
  .card { cursor: pointer; }
  .card:hover { transform: none; }
  .card:active { transform: scale(0.985); }
}


/* ============================================================ landing page */

/*
 * A full-bleed hero, rather than the rounded card the inner pages use.
 *
 * This is the first thing a shopper sees and the only page where the store is introducing
 * itself rather than selling one shelf. Everything below it is a card; letting this one run to
 * the edges is what makes the difference read as deliberate.
 */

/*
 * The three things a first-time buyer is actually worried about.
 *
 * Will it arrive, is my card safe, and will anyone answer if it goes wrong. Each gets a card
 * and a sentence, because the answer is the reassurance — three bare labels in a row were
 * decoration that happened to be worded like a promise.
 *
 * This rule used to declare `margin` and `padding` twice, and the later `0` silently won, which
 * stripped the padding off the container and flattened the whole thing into a thin bordered
 * strip with three words floating in the middle of it. One declaration per property here.
 */
/*
 * Three across, or one down. Never two and an orphan.
 *
 * `auto-fit` with a minimum width gives 2 + 1 in the middle of the range, and a lone card on a
 * second row reads as something that failed to load rather than as the third of three.
 */
.trust {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  margin: 52px 0 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 900px) { .trust { grid-template-columns: minmax(0, 1fr); } }
.trust li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
/* The icon gets a tinted disc so it reads as a mark rather than a stray glyph beside text. */
.trust-ic {
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  flex-shrink: 0;
  border-radius: 12px;
  background: var(--violet-wash);
  color: var(--violet-text);
}
.trust-text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.trust-text b {
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.01em;
}
.trust-text span {
  color: var(--text-mute);
  font-size: 0.87rem;
  line-height: 1.55;
}




/*
 * FAQ, as native <details>.
 *
 * The browser supplies open/close, keyboard handling and screen-reader semantics; a hand-built
 * accordion would reimplement all three slightly worse. It also keeps the answers in the page
 * for a search engine, which is where a fair share of "is this store legit" traffic starts.
 */
/*
 * Two columns once there is room.
 *
 * Ten questions stacked in one column is seven hundred pixels of near-identical rows, and the
 * answers most people want are the first two — which end up at the top of a very long list
 * nobody scrolls. Two columns halves the height and puts twice as many questions in view.
 */
.faq {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
@media (min-width: 900px) {
  .faq {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1px;
    background: var(--line-soft);
  }
  .faq details { background: var(--surface); }
  /* The border between rows has to come from the grid gap here, not from a sibling selector —
     `details + details` follows source order, which no longer matches visual order. */
  .faq details { border-bottom: 1px solid var(--line-soft); }
  .faq details:last-child, .faq details:nth-last-child(2):nth-child(odd) { border-bottom: none; }
}
@media (max-width: 899px) {
  .faq details + details { border-top: 1px solid var(--line-soft); }
}
.faq summary {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  cursor: pointer;
  font-weight: 600;
  color: var(--text);
  list-style: none;
  transition: background var(--fast), color var(--fast);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { background: var(--surface-2); color: #fff; }
.faq summary > span:first-child { flex: 1; }
.faq-mark {
  display: grid;
  place-items: center;
  color: var(--text-mute);
  transform: rotate(90deg);
  transition: transform var(--fast), color var(--fast);
  flex-shrink: 0;
}
.faq details[open] .faq-mark { transform: rotate(-90deg); color: var(--violet-bright); }
.faq details[open] summary { color: #fff; }
.faq .rich { padding: 0 18px 18px; font-size: 0.9rem; }

@media (max-width: 640px) {
  .trust { margin-top: 36px; gap: 10px; }
  .trust li { padding: 16px 18px; gap: 12px; }
  .trust-ic { width: 38px; height: 38px; }
  .faq summary { padding: 14px 15px; font-size: 0.92rem; }
  .faq .rich { padding: 0 15px 15px; }
}

/* A featured card carries its server, because the same rank name exists on several. */
.featured-cell { display: flex; flex-direction: column; gap: 8px; }
.featured-cell .card { flex: 1; }
.featured-where {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.featured-where img { border-radius: 4px; object-fit: cover; }
.featured-where:hover { color: var(--cyan-bright); }


/* ============================================================ card detail */

/*
 * What you actually get, on the card.
 *
 * Three lines lifted from the description. A shopper comparing two ranks can do it without
 * opening either — which is the whole job of a card in a grid.
 */
.card-perks {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
}
.card-perks li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--text-dim);
}
.card-perks svg {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--card-accent, var(--violet-bright));
}

/* Term and scarcity, as quiet chips rather than shouted badges. */
.card-term {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 2px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface-3);
  font-size: 0.71rem;
  font-weight: 600;
  color: var(--text-mute);
}
.card-term.low {
  background: rgba(255, 180, 84, 0.12);
  color: var(--warn);
}
.card-term svg { flex-shrink: 0; }

.sold.upgrade { display: inline-flex; align-items: center; gap: 4px; color: var(--cyan); }

/*
 * Accented cards.
 *
 * The accent comes from the ribbon a staff member already chose, so "Most popular" and "Best
 * value" are visibly the ones being pointed at — a permanent hairline at the top, a tinted art
 * well, and a ring that survives when the pointer is elsewhere. A card with no ribbon stays
 * neutral, because an accent every card wears is a texture rather than a signal.
 */
.card.accent { border-color: color-mix(in srgb, var(--card-accent) 45%, var(--line)); }
.card.accent::before { opacity: 1; background: var(--card-accent); }
.card.accent:hover { border-color: var(--card-accent); box-shadow: 0 0 0 1px var(--card-accent); }

.card.accent-violet { --card-accent: var(--violet-bright); --card-glow: rgba(180, 36, 252, 0.30); }
.card.accent-gold   { --card-accent: var(--gold);          --card-glow: rgba(245, 179, 42, 0.26); }
.card.accent-cyan   { --card-accent: var(--cyan);          --card-glow: rgba(12, 204, 216, 0.26); }
.card.accent-sale   { --card-accent: var(--magenta);       --card-glow: rgba(252, 96, 252, 0.26); }

/* ============================================================ rail, rebalanced */

/*
 * The rail is furniture, and it should look like it. Everything a shopper is here to navigate
 * is in the tabs above the products now, so these panels step back: lighter heads, smaller
 * type, no accent.
 */
.rail .panel { background: var(--surface); }
.rail .panel-head {
  padding: 11px 15px;
  font-size: 0.72rem;
  color: var(--text-mute);
}
.rail .panel-head .ic { opacity: 0.7; }
.rail .panel-body { padding: 12px 15px; font-size: 0.88rem; }



/* ============================================================ cart drawer */

/*
 * Who the basket is for, at the top of it.
 *
 * The single most expensive mistake a shopper can make here is buying for the wrong account —
 * a rank delivered to a misspelled name is a refund, an apology and a manual re-grant. Putting
 * the name and face above the items, with a way to change it, costs one row and prevents that.
 */
.drawer-who {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--void-deep);
  color: inherit;
}
.drawer-who:hover { background: var(--surface-2); }
.drawer-who img {
  width: 30px; height: 30px;
  border-radius: 7px;
  image-rendering: pixelated;
  background: var(--surface-3);
  flex-shrink: 0;
}
.drawer-who > span { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.drawer-who-label {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.drawer-who-name {
  font-weight: 700;
  color: #fff;
  font-size: 0.92rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.drawer-who-change {
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--cyan);
  flex-shrink: 0;
}

/* Remove is a control at the edge, not a word under the product name. */
.cart-line { position: relative; padding-right: 30px; }
.cart-line-x {
  position: absolute;
  top: 14px; right: 0;
  width: 24px; height: 24px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 7px;
  background: none;
  color: var(--text-mute);
  cursor: pointer;
  transition: background var(--fast), color var(--fast);
}
.cart-line-x:hover { background: rgba(255, 93, 115, 0.16); color: var(--danger); }

.drawer-trust {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin: 12px 0 0;
  font-size: 0.76rem;
  color: var(--text-mute);
}
.drawer-trust svg { flex-shrink: 0; }

/* ============================================================ checkout reassurance */

/*
 * Three lines under the pay button.
 *
 * A first-time buyer on a Minecraft store is weighing "is this a scam" against "I want the
 * rank", and they are weighing it at exactly this moment. Answering the three questions they
 * have — where the card details go, how long delivery takes, what proof they get — belongs
 * here rather than in a FAQ they will not scroll back to.
 */
.next-steps {
  list-style: none;
  counter-reset: step;
  margin: 18px 0 0;
  padding: 16px 18px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.next-steps li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 0.87rem;
  color: var(--text-dim);
  line-height: 1.5;
}
.next-steps li > span {
  flex-shrink: 0;
  width: 22px; height: 22px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--violet-wash);
  border: 1px solid var(--line);
  color: var(--violet-text);
  font-family: var(--font-display);
  font-size: 0.76rem;
  font-weight: 800;
}

/* ============================================================ news */

/*
 * The news section.
 *
 * Ghost's reading layout, and for Ghost's reason: an article is a single column of text with
 * nothing beside it. No sidebar, no related rail running alongside the paragraphs, no share
 * buttons floating over the margin. Everything next to body text is competing with it.
 *
 * The measure is 68 characters. Shorter than the store's content column on purpose — the store
 * is scanned, an article is read, and a line long enough to lose your place on is the single
 * most common reason a long post is not finished.
 */
.news-head {
  max-width: 60ch;
  margin: 34px 0 26px;
}
.news-head h1 {
  font-size: clamp(2rem, 4.2vw, 2.9rem);
  margin-bottom: 10px;
}
.news-head > p {
  color: var(--text-dim);
  font-size: 1.05rem;
}
.news-eyebrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.news-eyebrow a { color: var(--violet-text); }
.news-eyebrow a:hover { color: #fff; }
.news-eyebrow .sep { opacity: 0.4; }

/* ---- topics */

.news-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 28px;
}
.news-tag {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.84rem;
  font-weight: 600;
}
.news-tag:hover { border-color: var(--violet); color: var(--text); }
.news-tag.on { background: var(--violet); border-color: var(--violet); color: #fff; }
.news-tag span {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-mute);
}
.news-tag.on span { color: rgba(255, 255, 255, 0.7); }

/* ---- the lead story */

.news-lead {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 0;
  overflow: hidden;
  margin-bottom: 30px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: inherit;
  transition: border-color var(--fast);
}
.news-lead:hover { border-color: var(--violet); }
.news-lead-art { display: block; overflow: hidden; background: var(--surface-2); }
.news-lead-art img {
  display: block;
  width: 100%; height: 100%;
  min-height: 260px;
  object-fit: cover;
  transition: transform var(--slow);
}
.news-lead:hover .news-lead-art img { transform: scale(1.03); }
.news-lead-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding: 34px 34px 32px;
}
.news-lead-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 2.4vw, 1.95rem);
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: #fff;
}
.news-lead-excerpt { color: var(--text-dim); font-size: 1rem; line-height: 1.6; }
.news-more {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 4px;
  color: var(--violet-text);
  font-weight: 700;
  font-size: 0.9rem;
}
@media (max-width: 860px) {
  .news-lead { grid-template-columns: 1fr; }
  .news-lead-art img { min-height: 200px; }
  .news-lead-body { padding: 24px 22px; }
}

/* ---- the grid */

.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 22px;
}
.news-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: inherit;
  transition: border-color var(--fast), transform var(--fast);
}
.news-card:hover { border-color: var(--violet); transform: translateY(-2px); }
.news-card-art { display: block; overflow: hidden; background: var(--surface-2); }
.news-card-art img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transition: transform var(--slow);
}
.news-card:hover .news-card-art img { transform: scale(1.04); }
.news-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 9px;
  padding: 18px 19px 20px;
}
.news-card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.12rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: #fff;
}
/* Grows so every card's meta row sits on one baseline however long the excerpt runs. */
.news-card-excerpt {
  flex: 1;
  color: var(--text-mute);
  font-size: 0.9rem;
  line-height: 1.55;
}

.news-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-mute);
}
.news-badge {
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--violet-wash);
  color: var(--violet-text);
  font-weight: 700;
}

.news-pager {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 34px;
}
.news-related { margin-top: 56px; }
.news-related h2 { margin-bottom: 18px; font-size: 1.35rem; }

/* ---- the article */

.article {
  max-width: 68ch;
  margin: 30px auto 0;
}
.article-head { margin-bottom: 26px; }
.article-head h1 {
  font-size: clamp(1.9rem, 4.4vw, 3rem);
  line-height: 1.12;
  margin-bottom: 14px;
}
/* The standfirst: one sentence, larger than body text, that says why this is worth reading. */
.article-standfirst {
  font-size: 1.16rem;
  line-height: 1.6;
  color: var(--text-dim);
  margin-bottom: 18px;
}
.article-byline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.86rem;
  color: var(--text-mute);
}
.article-byline .sep { opacity: 0.45; }

.article-cover { margin: 0 0 32px; }
.article-cover img {
  display: block;
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--line);
}
.article-cover figcaption {
  margin-top: 10px;
  font-size: 0.82rem;
  color: var(--text-mute);
  text-align: center;
}

/*
 * Body text.
 *
 * 1.15rem at 1.75 line height. Both are larger than the rest of the site, because this is the
 * only place on it where somebody reads more than two sentences in a row.
 */
.article-body {
  font-size: 1.15rem;
  line-height: 1.75;
  color: var(--text-dim);
}
.article-body > * + * { margin-top: 1.35em; }
.article-body h2,
.article-body h3,
.article-body h4 {
  color: #fff;
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  line-height: 1.25;
  /* Space above a heading, not below it: a heading belongs to the text that follows. */
  margin-top: 2.2em;
  margin-bottom: -0.5em;
}
.article-body h2 { font-size: 1.6rem; }
.article-body h3 { font-size: 1.3rem; }
.article-body h4 { font-size: 1.1rem; }
.article-body strong { color: var(--text); font-weight: 700; }
.article-body a { color: var(--violet-text); text-decoration: underline; text-underline-offset: 3px; }
.article-body a:hover { color: #fff; }
.article-body ul,
.article-body ol { padding-left: 1.4em; }
.article-body li + li { margin-top: 0.5em; }
.article-body li::marker { color: var(--violet-text); }
.article-body blockquote {
  padding: 4px 0 4px 22px;
  border-left: 3px solid var(--violet);
  color: var(--text);
  font-style: italic;
}
.article-body code {
  padding: 2px 7px;
  border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.88em;
  color: var(--cyan-bright);
}
.article-body pre {
  padding: 16px 18px;
  border-radius: var(--radius);
  background: var(--void-deep);
  border: 1px solid var(--line);
  overflow-x: auto;
}
.article-body pre code {
  padding: 0;
  border: none;
  background: none;
  color: var(--text);
  font-size: 0.9rem;
}
.article-body hr {
  border: none;
  border-top: 1px solid var(--line);
  margin: 2.4em 0;
}
.article-body img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  /* Every image in an article opens full size, so it has to look like it will. */
  cursor: zoom-in;
}
.article-body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.article-body th,
.article-body td {
  padding: 9px 12px;
  border: 1px solid var(--line);
  text-align: left;
}
.article-body th { background: var(--surface-2); color: var(--text); }

.article-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--line-soft);
}

/* The one piece of selling in the article, at the end, where it has been earned. */
.article-cta {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 32px;
  padding: 22px 24px;
  border: 1px solid var(--violet);
  border-radius: var(--radius);
  background: linear-gradient(100deg, var(--violet-wash), var(--surface) 72%);
}
.article-cta div { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 200px; }
.article-cta strong { color: #fff; font-size: 1.05rem; }
.article-cta span { color: var(--text-dim); font-size: 0.92rem; }

/*
 * The lightbox.
 *
 * A dialog rather than a hand-built overlay: the browser gives the top layer, the focus trap,
 * the backdrop and Escape to close, and every one of those is a thing a hand-rolled version
 * gets subtly wrong.
 */
.zoom {
  padding: 0;
  border: none;
  background: none;
  max-width: 100vw;
  max-height: 100vh;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}
.zoom::backdrop { background: rgba(6, 7, 11, 0.92); }
.zoom-stage {
  display: grid;
  place-items: center;
  width: 100%; height: 100%;
  overflow: auto;
  cursor: zoom-in;
}
.zoom img {
  display: block;
  max-width: 94vw;
  max-height: 92vh;
  width: auto; height: auto;
  transition: max-width var(--fast), max-height var(--fast);
}
/* Zoomed in: the image is allowed past the viewport and the stage scrolls. */
.zoom.in .zoom-stage { cursor: zoom-out; place-items: start center; }
.zoom.in img { max-width: none; max-height: none; }
.zoom-close {
  position: fixed;
  top: 18px; right: 18px;
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  background: rgba(9, 10, 15, 0.7);
  backdrop-filter: blur(8px);
  color: #fff;
  cursor: pointer;
}
.zoom-close:hover { background: rgba(9, 10, 15, 0.95); border-color: rgba(255, 255, 255, 0.45); }
.zoom-hint {
  position: fixed;
  bottom: 20px; left: 50%;
  transform: translateX(-50%);
  padding: 7px 15px;
  border-radius: 999px;
  background: rgba(9, 10, 15, 0.7);
  backdrop-filter: blur(8px);
  color: rgba(255, 255, 255, 0.75);
  font-size: 0.8rem;
  font-weight: 600;
  pointer-events: none;
}

@media (max-width: 640px) {
  .article { margin-top: 20px; }
  .article-body { font-size: 1.06rem; line-height: 1.7; }
  .news-head { margin: 24px 0 20px; }
}
