/* ============================================================
   A reseller's storefront.

   Deliberately its own stylesheet with nothing of our own site in it. This
   page belongs to the reseller: their name, their colour, their contact
   details, and no route back to our own prices.

   One colour drives the page. The reseller picks it, and js/reseller-shop.js
   turns it into a small set (--brand-ink always readable on white,
   --brand-deep for the dark end of a gradient) before anything is drawn, so
   a pale colour chosen for its look can never leave white text unreadable.
   Network colours get the same treatment per card (--net and friends).

   Light only, as before: the shop has no theme switch, and every surface
   paints its own background so a phone in dark mode still shows it as
   designed.
   ============================================================ */
:root {
  --brand: #0164ce;
  --brand-ink: #0164ce;
  --brand-deep: #0059c4;
  --brand-soft: #e7f0fb;
  --brand-ring: rgba(1, 100, 206, 0.2);

  --ink: #16181d;
  --soft: #5d6470;
  --faint: #8a919c;
  --line: #e4e7eb;
  --line-strong: #d2d7de;
  --page: #f4f6f8;
  --card: #fff;

  --green: #127a3e;
  --green-bg: #e7f6ed;
  --red: #b42318;
  --red-bg: #fdecea;
  --amber: #8f5500;
  --amber-bg: #fff3d6;
  --blue: #0059c4;
  --blue-bg: #e7f0fb;
  --star: #e0a100;

  --radius: 16px;
  --shadow: 0 1px 2px rgba(22, 24, 29, 0.05), 0 8px 24px rgba(22, 24, 29, 0.07);
  --figure: "Space Grotesk", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
/* This page carries no site-wide reset, and .sheet sets display:flex, which
   would otherwise beat the hidden attribute and leave the buy sheet open on
   load. Everything here toggles visibility with el.hidden, so this one rule
   has to win. */
[hidden] { display: none !important; }

/* A page even 1px wider than the screen makes iPhone Safari shrink it. */
html, body { overflow-x: clip; }
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
body.is-locked { overflow: hidden; }
body.has-pending { padding-bottom: 88px; }

img { max-width: 100%; }
h1, h2, h3 { letter-spacing: -0.01em; }
.mono { font-family: var(--mono); }
.sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- loading, closed and offline ---- */
.state {
  min-height: 72vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 40px 16px; color: var(--soft);
}
.state h1 { margin: 16px 0 6px; font-size: 21px; color: var(--ink); }
.state p { margin: 0 0 18px; max-width: 34ch; }
.state__icon {
  width: 68px; height: 68px; border-radius: 50%; display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--line); color: var(--soft);
}
.state__icon svg { width: 30px; height: 30px; }
.spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--brand-ink);
  animation: shop-spin 0.8s linear infinite; margin-bottom: 14px;
}
@keyframes shop-spin { to { transform: rotate(360deg); } }

/* ---- the bar at the top of the shop ----
   The seller's mark and name stay in view as the customer reads. On a wide
   screen the sections sit beside them; on a phone one button opens them. */
:root { --bar: 58px; }
.bar {
  position: sticky; top: 0; z-index: 30; color: #fff;
  background: var(--brand-ink);
  padding-top: env(safe-area-inset-top, 0px);
}
.bar__inner {
  max-width: 1080px; margin: 0 auto; height: var(--bar);
  display: flex; align-items: center; gap: 12px; padding: 0 12px;
}
.bar__brand {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  display: flex; align-items: center; gap: 10px; min-width: 0; padding: 4px; margin-right: auto;
  color: inherit; border-radius: 12px;
}
.bar__ring {
  width: 34px; height: 34px; flex: none; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.18); border: 1px solid rgba(255, 255, 255, 0.45);
}
.bar__ring img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bar__initials { font-family: var(--figure); font-size: 14px; font-weight: 700; line-height: 1; }
.bar__name {
  font-size: 16.5px; font-weight: 700; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bar__links { display: none; }
.bar__links button {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  color: rgba(255, 255, 255, 0.86); font-size: 14.5px; font-weight: 600;
  padding: 8px 10px; border-radius: 10px;
}
.bar__links button:hover { color: #fff; background: rgba(255, 255, 255, 0.14); }
.bar__links button.is-on { color: #fff; background: rgba(255, 255, 255, 0.2); }
.bar__menu {
  appearance: none; -webkit-appearance: none; background: none; cursor: pointer;
  width: 42px; height: 42px; flex: none; display: grid; place-items: center;
  border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.4); color: #fff;
}
.bar__menu svg { width: 20px; height: 20px; }
.bar__brand:focus-visible, .bar__menu:focus-visible, .bar__links button:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
}

/* ---- the menu a phone opens ---- */
.menu {
  position: fixed; inset: 0; z-index: 60; display: flex; justify-content: flex-end;
  background: rgba(16, 24, 40, 0.5);
}
.menu__box {
  width: min(320px, 86vw); background: #fff; color: var(--ink);
  padding: 16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: 6px; overflow-y: auto;
  box-shadow: -18px 0 40px rgba(16, 24, 40, 0.18);
}
.menu__head { display: flex; align-items: center; gap: 12px; padding-bottom: 10px; }
.menu__head strong { font-size: 16px; min-width: 0; overflow-wrap: anywhere; }
.menu__close {
  appearance: none; -webkit-appearance: none; background: var(--page); border: 0; cursor: pointer;
  margin-left: auto; width: 40px; height: 40px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--soft);
}
.menu__close svg { width: 18px; height: 18px; }
.menu__list { list-style: none; margin: 0; padding: 8px 0; display: grid; gap: 2px; border-top: 1px solid var(--line); }
.menu__list button {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  width: 100%; min-height: 48px; padding: 10px 12px; border-radius: 12px;
  display: flex; align-items: center; gap: 10px; text-align: left;
  font-size: 16px; font-weight: 600; color: var(--ink);
}
.menu__list button svg { width: 20px; height: 20px; flex: none; color: var(--soft); }
.menu__list button.is-on { background: var(--brand-soft); color: var(--brand-ink); }
.menu__list button.is-on svg { color: var(--brand-ink); }
.menu__talk { display: grid; gap: 8px; padding-top: 12px; border-top: 1px solid var(--line); }
.menu__talk a {
  min-height: 48px; padding: 10px 12px; border-radius: 12px; text-decoration: none;
  display: flex; align-items: center; gap: 10px;
  font-size: 15.5px; font-weight: 600; color: var(--brand-ink); background: var(--brand-soft);
}
.menu__talk a svg { width: 18px; height: 18px; flex: none; }

/* ---- the front of the shop ----
   The name is the headline, the line under it is the seller's own, and the
   two buttons go where most customers are heading. */
.hero {
  position: relative; overflow: hidden; color: #fff;
  background: linear-gradient(135deg, var(--brand-ink) 0%, var(--brand-deep) 100%);
  padding: 40px 16px 44px;
}
.hero::before, .hero::after {
  content: ""; position: absolute; border-radius: 50%; pointer-events: none;
  background: rgba(255, 255, 255, 0.08);
}
.hero::before { width: 320px; height: 320px; top: -170px; right: -100px; }
.hero::after { width: 240px; height: 240px; bottom: -150px; left: -80px; background: rgba(255, 255, 255, 0.06); }
.hero__inner {
  position: relative; z-index: 1; max-width: 1080px; margin: 0 auto;
  display: flex; align-items: center; gap: 32px;
}
.hero__text { min-width: 0; flex: 1 1 auto; }
.hero__name {
  margin: 0; font-weight: 750; line-height: 1.04; letter-spacing: -0.03em;
  font-size: clamp(34px, 9vw, 60px); overflow-wrap: anywhere;
}
.hero__desc {
  margin: 14px 0 0; max-width: 42ch; white-space: pre-line; overflow-wrap: anywhere;
  font-size: 16px; line-height: 1.55; color: rgba(255, 255, 255, 0.88);
}
.hero__notice {
  display: inline-flex; align-items: flex-start; gap: 8px; margin: 14px 0 0; padding: 8px 14px;
  border-radius: 999px; font-size: 14.5px; font-weight: 600; overflow-wrap: anywhere;
  background: rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.38);
}
.hero__notice svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.hero__acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.hero__btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 10px 20px; border-radius: 14px;
  font-size: 15.5px; font-weight: 700;
  background: rgba(255, 255, 255, 0.14); border: 1px solid rgba(255, 255, 255, 0.45); color: #fff;
  transition: transform 0.15s ease, background-color 0.15s ease;
}
.hero__btn svg { width: 18px; height: 18px; flex: none; }
.hero__btn:hover { transform: translateY(-1px); background: rgba(255, 255, 255, 0.24); }
.hero__btn--go { background: #fff; color: var(--brand-ink); border-color: #fff; }
.hero__btn--go:hover { background: #fff; }
.hero__btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.hero__art { flex: none; display: none; }
/* The scene has depth rather than a flat drawing: the phone sits back, the
   mark sits in front of it, and both move a little, which is what makes it
   read as one object seen at an angle. */
.scene { position: relative; width: 300px; height: 250px; perspective: 900px; }
.scene__svg {
  width: 100%; height: 100%; display: block;
  transform: rotateY(-16deg) rotateX(6deg); transform-origin: 60% 50%;
  filter: drop-shadow(0 22px 34px rgba(0, 0, 0, 0.22));
  animation: shop-float 7s ease-in-out infinite;
}
.scene__wave { opacity: 0.22; animation: shop-wave 2.6s ease-in-out infinite; }
.scene__wave:nth-of-type(2) { animation-delay: 0.32s; }
.scene__wave:nth-of-type(3) { animation-delay: 0.64s; }
.scene__dot { animation: shop-drift 5.5s ease-in-out infinite; }
.scene__dot:nth-of-type(2) { animation-delay: 0.7s; animation-duration: 6.4s; }
.scene__dot:nth-of-type(3) { animation-delay: 1.3s; animation-duration: 7.1s; }
.hero__ring {
  position: absolute; left: 6px; bottom: 6px;
  width: 112px; height: 112px; padding: 5px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.2); border: 1.5px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.24);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  animation: shop-float 7s ease-in-out infinite reverse;
}
.hero__ring img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; background: #fff; }
.hero__ring .head__initials { font-size: 34px; }

@keyframes shop-float {
  0%, 100% { transform: rotateY(-16deg) rotateX(6deg) translateY(0); }
  50% { transform: rotateY(-16deg) rotateX(6deg) translateY(-10px); }
}
@keyframes shop-wave {
  0%, 100% { opacity: 0.18; }
  40% { opacity: 0.85; }
}
@keyframes shop-drift {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-6px, -9px); }
}

/* The page arrives once: the name, then the line under it, then the buttons.
   Nothing repeats, so the shop is still after a second. */
.hero__name, .hero__desc, .hero__acts, .head__links { animation: shop-rise 0.5s ease-out both; }
.hero__desc { animation-delay: 0.06s; }
.hero__acts { animation-delay: 0.12s; }
.head__links { animation-delay: 0.18s; }
@keyframes shop-rise {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .net, .bundle, [role="tabpanel"].is-fresh { animation: none !important; }
  .net, .net::before, .net__count, .tab svg, .tab::after, .bundle, .bundle__buy { transition: none !important; }
  .net.is-picked::after { display: none; }
  .scene__svg, .hero__ring, .scene__wave, .scene__dot,
  .hero__name, .hero__desc, .hero__acts, .head__links { animation: none; }
  .scene__wave { opacity: 0.5; }
}
.head__initials { font-family: var(--figure); font-size: 52px; font-weight: 700; line-height: 1; }
.head__links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.pill {
  display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 7px 15px;
  border-radius: 999px; text-decoration: none; color: #fff;
  font-size: 14.5px; font-weight: 600;
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.38);
  transition: background-color 0.15s ease;
}
.pill svg { width: 17px; height: 17px; flex: none; }
/* The share pill is a button among links, so it needs the bits a button
   does not inherit. */
button.pill { appearance: none; -webkit-appearance: none; cursor: pointer; font-family: inherit; }
.pill:hover { background: rgba(255, 255, 255, 0.26); }
.pill:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (min-width: 900px) {
  .bar__links { display: flex; align-items: center; gap: 2px; }
  .bar__menu { display: none; }
  /* The bar already lists the sections on a wide screen, so the strip under
     the header would say the same thing twice. */
  .tabs { display: none; }
  .hero { padding: 72px 24px 78px; }
  .hero__art { display: block; }
  .hero__ring { left: 0; bottom: 0; }
}

/* ---- the shop's scrolling message line ---- */
.ticker { background: #fff; border-bottom: 2px solid var(--brand); color: var(--brand-deep); }
.ticker__viewport {
  overflow: hidden; padding: 9px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}
.ticker__track {
  display: flex; width: max-content;
  animation: shop-ticker var(--ticker-time, 30s) linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__group { display: flex; align-items: center; flex: none; white-space: nowrap; }
.ticker__item { font-size: 14.5px; font-weight: 600; }
.ticker__dot {
  width: 5px; height: 5px; margin: 0 18px; flex: none; border-radius: 50%;
  background: var(--brand-ink); opacity: 0.6;
}
/* The two copies are identical, so moving by half the track lands exactly
   where it started and the loop has no visible jump. */
@keyframes shop-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.ticker.is-still .ticker__viewport { -webkit-mask-image: none; mask-image: none; padding: 9px 16px; }
.ticker.is-still .ticker__track { animation: none; width: auto; justify-content: center; }
.ticker.is-still .ticker__group { flex: 1 1 auto; min-width: 0; white-space: normal; flex-wrap: wrap; justify-content: center; row-gap: 4px; text-align: center; }
.ticker.is-still .ticker__dot:last-child { display: none; }

/* ---- tabs ---- */
.tabs-anchor { height: 0; }
.tabs {
  /* Under the bar, which is sticky too, so both stay in view together. */
  position: sticky; top: calc(var(--bar) + env(safe-area-inset-top, 0px)); z-index: 20;
  background: rgba(255, 255, 255, 0.97);
  -webkit-backdrop-filter: saturate(1.4) blur(10px);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}
.tabs__inner { max-width: 1080px; margin: 0 auto; display: flex; padding: 0 6px; }
.tab {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  position: relative; flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 10px 4px 9px; color: var(--soft);
  font-size: 13px; font-weight: 600; white-space: nowrap;
  transition: color 0.15s ease;
}
/* The icon leads the change: it lifts and grows a little as its tab becomes
   the live one, so the eye follows the move rather than noticing the colour
   afterwards. (4 Oct 2026, on the owner's word.) */
.tab svg {
  width: 21px; height: 21px; flex: none;
  transition: transform .34s cubic-bezier(.22, 1.3, .36, 1), opacity .2s ease;
}
.tab:active svg { transform: scale(.9); }
/* The underline grows out of the middle instead of appearing whole, which
   is what makes moving between tabs read as one thing sliding rather than
   two things blinking. */
.tab::after {
  content: ""; position: absolute; left: 16%; right: 16%; bottom: -1px; height: 3px;
  border-radius: 3px 3px 0 0; background: var(--brand-ink);
  transform: scaleX(0); transform-origin: center;
  transition: transform .3s cubic-bezier(.22, .9, .3, 1);
}
.tab:hover { color: var(--ink); }
.tab:hover svg { transform: translateY(-1px); }
.tab.is-on { color: var(--brand-ink); }
.tab.is-on svg { transform: translateY(-2px) scale(1.08); }
.tab.is-on::after { transform: scaleX(1); }
/* The panel the tab opens arrives rather than appearing. Set on the section
   by js/reseller-shop.js each time a tab is chosen. */
[role="tabpanel"].is-fresh { animation: panelIn .36s cubic-bezier(.22, .8, .3, 1) both; }
@keyframes panelIn { from { opacity: 0; transform: translateY(10px); } }
.tab:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: -4px; border-radius: 8px; }

.wrap { max-width: 1080px; margin: 0 auto; padding: 18px 16px 48px; }
.narrow { max-width: 640px; margin: 0 auto; }
.mid { max-width: 820px; margin: 0 auto; }

.notice {
  display: flex; align-items: flex-start; gap: 10px; margin: 0 0 16px; padding: 12px 14px;
  border-radius: 12px; background: var(--amber-bg); color: var(--amber);
  border: 1px solid #f0dcaa; font-size: 14.5px; overflow-wrap: anywhere;
}
.notice svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }

/* ---- network pills ---- */
/* ---- the networks ----
   One row of pills, one of them on. The motion here was added 4 Oct 2026 on
   the owner's word, and all of it is doing a job: the pills arrive in order
   so the eye lands on the first one, the chosen pill lifts and its colour
   fills from the left rather than snapping, its count bumps so you can see
   which number just became the live one, and a press gives way under the
   finger. Transform and opacity only, so none of it costs a layout. */
.nets {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px;
  perspective: 600px;
}
.net {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 8px; min-height: 42px; padding: 7px 10px 7px 16px;
  border-radius: 999px; background: #fff; color: var(--net-ink);
  border: 1.5px solid var(--net); font-size: 15px; font-weight: 650;
  transition:
    color .2s ease,
    transform .32s cubic-bezier(.22, 1.2, .36, 1),
    box-shadow .32s ease,
    border-color .2s ease;
  animation: netIn .42s cubic-bezier(.22, .8, .3, 1) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
/* The fill, which is what the colour change actually is: a layer under the
   text that grows from the left when the pill is chosen and shrinks back
   when it is not. A background-color cannot be animated this way. */
.net::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--net);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .34s cubic-bezier(.22, .8, .3, 1);
}
@keyframes netIn { from { opacity: 0; transform: translateY(8px) scale(.96); } }
.net__count {
  display: inline-grid; place-items: center; min-width: 24px; height: 24px; padding: 0 7px;
  border-radius: 999px; font-size: 12.5px; font-weight: 700;
  background: var(--net-soft); color: var(--net-ink);
  transition: background-color .2s ease, color .2s ease, transform .3s cubic-bezier(.22, 1.4, .36, 1);
}
.net:hover { background: var(--net-soft); }
.net:active { transform: scale(.96); }
.net.is-on {
  color: var(--net-on);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px -14px var(--net-deep);
}
.net.is-on::before { transform: scaleX(1); }
.net.is-on .net__count { background: rgba(255, 255, 255, 0.3); color: inherit; transform: scale(1.08); }
/* A ring that leaves the pill once, on the tap that chose it. Added by
   js/reseller-shop.js and removed when it finishes, so it can play again. */
.net.is-picked::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit;
  border: 2px solid var(--net);
  animation: netRing .55s ease-out forwards;
  pointer-events: none;
}
@keyframes netRing {
  from { opacity: .7; transform: scale(1); }
  to { opacity: 0; transform: scale(1.22); }
}
.net:focus-visible { outline: 2px solid var(--net-ink); outline-offset: 2px; }

/* ---- find a size ----
   Added 4 Oct 2026. A shop with twenty sizes on one network is a list a
   customer scrolls twice and then messages the seller about instead. The
   box only appears once there are enough of them to be worth it. */
.find {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 10px; padding: 0 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(22, 24, 29, .04);
}
.find[hidden] { display: none; }
.find svg { width: 17px; height: 17px; flex: none; color: var(--soft); }
.find input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font: inherit; font-size: 16px; color: var(--ink);
  padding: 11px 0;
}
.find input::-webkit-search-cancel-button { display: none; }
.find button {
  appearance: none; border: 0; background: none; cursor: pointer;
  font-size: 22px; line-height: 1; color: var(--soft); padding: 2px 2px 4px;
}
.find button:hover { color: var(--ink); }
.empty--find { padding: 18px 12px; }
.linkish {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit;
  color: var(--brand-ink); font-weight: 700; text-decoration: underline;
}

/* ---- the bundles, which are the whole point of the page ---- */
/* Four across on a phone and more on wider screens. Three was the owner's
   first call and he changed it on 4 Oct 2026 looking at his own shop: the
   cards were still too big, and a customer should take in a whole network's
   sizes in one look rather than scroll past a few giant tiles. Everything
   inside the card is sized against the card itself below, or four columns
   only produces four cramped ones. */
.grid { display: grid; gap: 7px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.bundle {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden; cursor: pointer;
  /* They arrive in order, a few milliseconds apart, so a switch of network
     reads as a new set landing rather than as a flicker. Capped at the
     tenth, or the last card of twenty would be most of a second late. */
  animation: bundleIn .34s cubic-bezier(.22, .8, .3, 1) both;
  animation-delay: calc(min(var(--i, 0), 9) * 32ms);
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
@keyframes bundleIn { from { opacity: 0; transform: translateY(10px) scale(.97); } }
.bundle:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.bundle:active { transform: scale(.98); }
.bundle__buy { transition: transform .18s ease, filter .18s ease; }
.bundle:hover .bundle__buy { filter: brightness(1.04); }
.bundle__buy:active { transform: scale(.97); }
/* A fixed band. The uploaded network logos are complete branded tiles with
   their own background, so the picture fills the band; if the picture set
   the size, a 1600px square would decide how tall every card is. The
   network colour behind is the fallback for a network with no picture. */
/* Four by three, like the panel the owner's resellers know: a square logo
   tile cropped to this still shows the whole oval. The size comes from the
   card's width, never from the image, so a 1600px tile cannot stretch it. */
.bundle__band {
  position: relative; flex: none; aspect-ratio: 4 / 3; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--net), var(--net-deep));
}
.bundle__band img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.bundle__mark {
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  border: 2px solid currentColor; background: rgba(255, 255, 255, 0.14); color: var(--net-on);
  font-family: var(--figure); font-size: 12px; font-weight: 700; padding: 0 2px;
  overflow: hidden; white-space: nowrap;
}
/* Five or six letters (a network with no logo text of its own) need a
   smaller size to stay inside the circle. */
.bundle__mark--long { font-size: 10px; padding: 0; letter-spacing: -0.02em; }
.bundle__body { display: flex; flex-direction: column; flex: 1; padding: 8px; }
.bundle__size {
  margin: 0; font-family: var(--figure); font-size: 17px; font-weight: 700; line-height: 1.15;
  color: var(--net-ink); overflow-wrap: anywhere;
}
.bundle__val { margin: 1px 0 0; font-size: 11.5px; color: var(--soft); }
.bundle__price {
  margin: auto 0 8px; padding-top: 5px;
  font-family: var(--figure); font-size: 13.5px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.bundle__buy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 100%; min-height: 32px; border: 0; border-radius: 8px;
  font-size: 13px; font-weight: 700; color: var(--net-on);
  background: linear-gradient(135deg, var(--net), var(--net-deep));
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1);
}
.bundle__buy:focus-visible { outline: 3px solid var(--net-ink); outline-offset: 2px; }

/* ---- checker cards ---- */
/* Drawn here and never from a picture: a coloured band with the exam's name
   on it. WASSCE takes the shop's own colour; BECE and anything else get a
   fixed colour of their own, each dark enough to carry white text. */
.vhead { margin: 0 0 14px; }
.vhead h2 { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.3; }
.vhead p { margin: 4px 0 0; max-width: 60ch; color: var(--soft); font-size: 14.5px; }
.vgrid { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.voucher {
  --v: var(--brand-ink); --v-deep: var(--brand-deep);
  display: flex; flex-direction: column; min-width: 0; overflow: hidden; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.voucher--bece { --v: #0f7a6c; --v-deep: #0a5a50; }
.voucher--other { --v: #475467; --v-deep: #344054; }
.voucher:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.voucher.is-out { cursor: default; }
.voucher.is-out:hover { transform: none; box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04); border-color: var(--line); }
.voucher__band {
  position: relative; flex: none; aspect-ratio: 16 / 10; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between; padding: 12px;
  color: #fff; background: linear-gradient(135deg, var(--v), var(--v-deep));
}
/* Two soft circles, like the header, so a band is not a flat block. */
.voucher__band::before, .voucher__band::after {
  content: ""; position: absolute; border-radius: 50%; pointer-events: none; background: rgba(255, 255, 255, 0.1);
}
.voucher__band::before { width: 120px; height: 120px; top: -58px; right: -34px; }
.voucher__band::after { width: 84px; height: 84px; bottom: -48px; left: -26px; background: rgba(255, 255, 255, 0.07); }
.voucher.is-out .voucher__band { filter: grayscale(0.7); opacity: 0.75; }
.voucher__icon {
  position: relative; width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.35);
}
.voucher__icon svg { width: 22px; height: 22px; }
.voucher__exam {
  position: relative; font-family: var(--figure); font-size: 22px; font-weight: 700; line-height: 1.1;
  letter-spacing: 0.02em; overflow-wrap: anywhere;
}
.voucher__body { display: flex; flex-direction: column; flex: 1; padding: 10px; }
.voucher__name { margin: 0; font-size: 15px; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.voucher__note { margin: 2px 0 0; font-size: 12.5px; color: var(--soft); }
.voucher__price {
  margin: auto 0 8px; padding-top: 8px;
  font-family: var(--figure); font-size: 17px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.voucher__buy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 100%; min-height: 40px; border: 0; border-radius: 9px;
  font-size: 14px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--v), var(--v-deep));
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.12);
}
.voucher__buy:focus-visible { outline: 3px solid var(--v); outline-offset: 2px; }
.voucher__soldout {
  display: grid; place-items: center; min-height: 40px; border-radius: 9px;
  background: var(--page); border: 1px dashed var(--line-strong);
  color: var(--soft); font-size: 14px; font-weight: 700;
}

.empty {
  display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center;
  padding: 48px 16px; color: var(--soft);
  background: var(--card); border: 1px dashed var(--line-strong); border-radius: var(--radius);
}
.empty svg { width: 34px; height: 34px; color: var(--faint); }
.empty p { margin: 0; max-width: 32ch; }

/* ---- cards and forms ---- */
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px; box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
}
.card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin: 0 0 4px; }
.card__head h2 { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.3; }
.card__lead { margin: 0 0 4px; color: var(--soft); font-size: 14.5px; }
.fee { margin: 0; display: flex; flex-direction: column; align-items: flex-end; flex: none; text-align: right; }
.fee small { font-size: 12.5px; color: var(--soft); font-weight: 500; }
.fee span { font-family: var(--figure); font-size: 21px; font-weight: 700; color: var(--brand-ink); line-height: 1.15; }

.fields { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: 16px; }
.field { margin-top: 14px; min-width: 0; }
.lbl { display: block; margin: 0 0 6px; font-size: 14px; font-weight: 600; color: var(--ink); }
.req { color: var(--red); margin-left: 2px; }
.input {
  display: block; width: 100%; min-height: 48px; margin: 0;
  font: inherit; font-size: 16px; /* 16px keeps iPhone from zooming */
  padding: 11px 13px; border-radius: 11px;
  border: 1px solid var(--line-strong); background: #fff; color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
textarea.input { min-height: 96px; resize: vertical; line-height: 1.5; }
.input::placeholder { color: var(--faint); }
.input:focus { outline: none; border-color: var(--brand-ink); box-shadow: 0 0 0 3px var(--brand-ring); }
.input[aria-invalid="true"] { border-color: var(--red); }
.input.mono { font-family: var(--mono); letter-spacing: 0.02em; }
.hint { margin: 6px 0 0; font-size: 13px; color: var(--soft); }
.sub { margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--line); font-size: 16px; font-weight: 700; }
.sub + .hint { margin-top: 2px; }
.muted { color: var(--soft); font-size: 14.5px; margin: 12px 0 0; }

.err, .ok { margin: 14px 0 0; padding: 10px 12px; border-radius: 10px; font-size: 14.5px; }
.err { background: var(--red-bg); color: var(--red); }
.ok { background: var(--green-bg); color: var(--green); }

.btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: 50px; margin-top: 18px; padding: 12px 18px;
  border: 0; border-radius: 12px; text-decoration: none;
  font-size: 16px; font-weight: 700; line-height: 1.2;
}
.btn svg { width: 19px; height: 19px; flex: none; }
.btn--solid {
  color: #fff; background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep));
  box-shadow: 0 6px 16px var(--brand-ring);
}
.btn--ghost { color: var(--ink); background: #fff; border: 1px solid var(--line-strong); }
.btn--ghost:hover { background: var(--page); }
.btn--auto { width: auto; }
.btn:disabled { opacity: 0.55; cursor: default; box-shadow: none; }
.btn:focus-visible { outline: 3px solid var(--brand-ring); outline-offset: 2px; }
.linkish {
  appearance: none; background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  color: var(--brand-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px;
}

.inline { display: flex; gap: 8px; }
.inline .input { flex: 1; min-width: 0; }
.inline .btn { flex: none; width: auto; margin-top: 0; min-height: 48px; padding: 10px 16px; }

/* How many checker cards: a number between two round buttons. */
.qty { display: flex; align-items: stretch; gap: 8px; max-width: 220px; }
.qty__btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 48px; min-height: 48px; flex: none; border-radius: 11px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong); background: #fff; color: var(--brand-ink);
}
.qty__btn svg { width: 20px; height: 20px; }
.qty__btn:hover { background: var(--brand-soft); }
.qty__btn:disabled { opacity: 0.45; cursor: default; background: #fff; }
.qty__btn:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
.qty .qty__input { flex: 1; min-width: 0; text-align: center; font-family: var(--figure); font-weight: 700; }

/* ---- how to pay: radio cards ---- */
.pay { margin-top: 20px; }
.pay__title { margin: 0 0 10px; font-size: 15.5px; font-weight: 700; }
.pay-cards { display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }
.pay-card { position: relative; display: block; cursor: pointer; }
.pay-card input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.pay-card__box {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border: 1.5px solid var(--line-strong); border-radius: 13px; background: #fff;
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.pay-card:hover .pay-card__box { border-color: var(--brand); }
.pay-card input:checked + .pay-card__box {
  border-color: var(--brand-ink); background: var(--brand-soft); box-shadow: 0 0 0 3px var(--brand-ring);
}
.pay-card input:focus-visible + .pay-card__box { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
.pay-card__badge {
  width: 44px; height: 44px; flex: none; border-radius: 12px; display: grid; place-items: center;
  background: var(--badge, var(--brand-ink)); color: var(--badge-on, #fff);
}
.pay-card__badge svg { width: 22px; height: 22px; }
.pay-card__main { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.35; }
.pay-card__main strong { font-size: 15px; }
.pay-card__main span { font-size: 13.5px; color: var(--soft); overflow-wrap: anywhere; }
.pay-card__main .pay-card__num { font-family: var(--figure); font-weight: 600; color: var(--ink); letter-spacing: 0.02em; }
.pay-card__tick {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  border: 2px solid var(--line-strong); background: #fff;
}
.pay-card input:checked + .pay-card__box .pay-card__tick {
  border-color: var(--brand-ink); background: var(--brand-ink); box-shadow: inset 0 0 0 4px #fff;
}
.pay-empty {
  display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: 12px;
  background: var(--amber-bg); color: var(--amber); font-size: 14.5px;
}
.pay-empty svg { width: 20px; height: 20px; flex: none; margin-top: 2px; }
.pay-empty strong { display: block; }
/* On a line of its own under the message, only as wide as its label. */
.pay-empty .pay-retry {
  display: flex; width: fit-content; margin-top: 10px; min-height: 42px; padding: 8px 16px; font-size: 15px;
}

/* ---- the popup ---- */
.sheet {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(17, 19, 24, 0.55);
  animation: shop-fade 0.18s ease;
}
.sheet__box {
  position: relative; width: 100%; max-width: 500px;
  max-height: 92vh; max-height: 92dvh; overflow-y: auto; overscroll-behavior: contain;
  background: #fff; border-radius: 20px 20px 0 0;
  padding: 20px 16px calc(22px + env(safe-area-inset-bottom, 0px));
  animation: shop-rise 0.22s ease;
}
.sheet__box:focus { outline: none; }
@keyframes shop-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes shop-rise { from { transform: translateY(24px); opacity: 0.6; } to { transform: none; opacity: 1; } }
.sheet__x {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  width: 38px; height: 38px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--page); color: var(--soft);
}
.sheet__x svg { width: 18px; height: 18px; }
.sheet__x:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
/* Locked while an order is being placed, and it should look it. */
.sheet__x:disabled { opacity: 0.45; cursor: default; }
.sheet__title { margin: 2px 48px 14px 0; font-size: 19px; font-weight: 700; }

.pick {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border-radius: 14px; background: var(--page); border: 1px solid var(--line);
}
.pick__tile {
  width: 54px; height: 54px; flex: none; border-radius: 12px; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--net, var(--brand-ink)), var(--net-deep, var(--brand-deep)));
  color: var(--net-on, #fff); font-family: var(--figure); font-size: 14px; font-weight: 700;
}
.pick__tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pick__tile svg { width: 26px; height: 26px; }
.pick__main { flex: 1; min-width: 0; }
.pick__main strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.pick__main span { display: block; font-size: 13.5px; color: var(--soft); overflow-wrap: anywhere; }
.pick__price { font-family: var(--figure); font-size: 18px; font-weight: 700; white-space: nowrap; }
/* A checker card's tile takes its exam's colour, as on the card itself. */
.pick__tile--wassce { background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep)); color: #fff; }
.pick__tile--bece { background: linear-gradient(135deg, #0f7a6c, #0a5a50); color: #fff; }
.pick__tile--other { background: linear-gradient(135deg, #475467, #344054); color: #fff; }

/* ---- pay to ---- */
.payto__lead { margin: 16px 0 10px; font-size: 14.5px; color: var(--soft); }
.payto { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.payto__amount {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 14px 16px;
  color: #fff; background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep));
}
.payto__amount span { font-size: 14px; opacity: 0.92; }
.payto__amount strong { font-family: var(--figure); font-size: 24px; line-height: 1.1; white-space: nowrap; }
.payto__rows { margin: 0; padding: 2px 16px; }
.payto__row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.payto__row:last-child { border-bottom: 0; }
.payto__row dt { flex: none; font-size: 13.5px; color: var(--soft); }
.payto__row dd {
  margin: 0; min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
  text-align: right; font-size: 15px; font-weight: 600; overflow-wrap: anywhere;
}
.payto__num { font-family: var(--figure); letter-spacing: 0.03em; }
.payto__note {
  margin: 0; padding: 12px 16px; border-top: 1px solid var(--line);
  background: var(--amber-bg); color: var(--amber); font-size: 14px; overflow-wrap: anywhere;
}
.copy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  min-height: 34px; padding: 5px 10px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: #fff; color: var(--brand-ink);
  font-size: 13px; font-weight: 700;
}
.copy svg { width: 15px; height: 15px; }
.copy:hover { background: var(--brand-soft); }
.copy.is-done { color: var(--green); border-color: #bfe3cc; background: var(--green-bg); }
.copy:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }

/* ---- paying online ---- */
/* The amount and the reference sit in the same panel as Pay on Confirmation,
   so both ways to pay read as one checkout. A prompt gateway adds the two
   fields above; a card gateway adds nothing and only opens its own page. */
#step-online .payto { margin-top: 16px; }
#step-online .btn--ghost { margin-top: 10px; }
select.input { padding-right: 34px; cursor: pointer; background-color: #fff; }
.wait {
  display: flex; align-items: center; gap: 12px; margin-top: 16px; padding: 12px 14px;
  border-radius: 12px; background: var(--blue-bg); color: var(--blue);
}
.wait .spinner {
  width: 26px; height: 26px; flex: none; margin: 0; border-width: 2.5px;
  border-color: rgba(1, 100, 206, 0.25); border-top-color: var(--blue);
}
.wait__text { min-width: 0; font-size: 14.5px; }
.wait__text strong { display: block; }

/* ---- proof of payment ---- */
.proof { margin-top: 20px; padding-top: 18px; border-top: 1px dashed var(--line-strong); }
.proof h3 { margin: 0 0 2px; font-size: 17px; font-weight: 700; }
.proof > .hint { margin-top: 0; }
.drop {
  position: relative; display: flex; align-items: center; gap: 12px; padding: 12px 14px; cursor: pointer;
  border: 1.5px dashed var(--line-strong); border-radius: 13px; background: var(--page);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.drop:hover, .drop:focus-within { border-color: var(--brand-ink); background: var(--brand-soft); }
.drop.is-invalid { border-color: var(--red); }
.drop__icon {
  width: 48px; height: 48px; flex: none; overflow: hidden; border-radius: 11px;
  display: grid; place-items: center; background: #fff; color: var(--brand-ink); border: 1px solid var(--line);
}
.drop__icon svg { width: 24px; height: 24px; }
.drop__icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.drop__text { min-width: 0; }
.drop__text strong { display: block; font-size: 15px; overflow-wrap: anywhere; }
.drop__text span { display: block; font-size: 13px; color: var(--soft); }

/* ---- done ---- */
.done { text-align: center; padding: 10px 0 0; }
.done__icon {
  width: 68px; height: 68px; margin: 6px auto 14px; border-radius: 50%;
  display: grid; place-items: center; background: var(--green-bg); color: var(--green);
}
.done__icon svg { width: 36px; height: 36px; }
.done h2 { margin: 0 0 6px; font-size: 20px; }
.done p { margin: 0 auto; max-width: 36ch; color: var(--soft); font-size: 15px; }
.done__ref {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 10px;
  margin: 18px 0 0; padding: 12px; border-radius: 12px; background: var(--page); border: 1px solid var(--line);
}
.done__ref span { font-size: 13.5px; color: var(--soft); }
.done__ref strong { font-size: 16px; overflow-wrap: anywhere; }
.done p.done__keep { margin-top: 10px; font-size: 14px; }
.done__keep:empty { display: none; }
.done .btn--ghost { margin-top: 10px; }

/* ---- order status ---- */
.result { margin-top: 14px; }
.result__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding-bottom: 14px; border-bottom: 1px solid var(--line);
}
.result__kind { display: block; font-size: 13px; color: var(--soft); }
.result__head strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.result__note { margin: 12px 0 0; font-size: 14.5px; color: var(--soft); }
.result__note--bad { color: var(--red); }
.result__note--good { color: var(--green); font-weight: 600; }
/* The line that says the card is watching for the payment by itself. The dot
   keeps it honest: it only pulses while a check is actually running. */
.result__live { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.result__live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--blue-600, #0164ce); animation: result-live 1.6s ease-in-out infinite; }
@keyframes result-live { 0%, 100% { opacity: 0.25; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .result__live::before { animation: none; opacity: 0.9; } }
.rows { margin: 4px 0 0; }
.rows > div { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.rows > div:last-child { border-bottom: 0; padding-bottom: 0; }
.rows dt { color: var(--soft); font-size: 14px; flex: none; }
.rows dd { margin: 0; font-weight: 600; text-align: right; overflow-wrap: anywhere; min-width: 0; }

.status {
  display: inline-flex; align-items: center; gap: 6px; flex: none; white-space: nowrap;
  padding: 5px 11px; border-radius: 999px; font-size: 13px; font-weight: 700;
}
.status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.status--green { background: var(--green-bg); color: var(--green); }
.status--red { background: var(--red-bg); color: var(--red); }
.status--amber { background: var(--amber-bg); color: var(--amber); }
.status--blue { background: var(--blue-bg); color: var(--blue); }

/* ---- free code ---- */
.opt { margin-left: 8px; font-size: 12.5px; font-weight: 500; color: var(--soft); }
/* Capitals as they are typed, spaced a little so look-alike characters are
   easier to read back off a message. */
.code-input { text-transform: uppercase; letter-spacing: 0.08em; }
.code-input::placeholder { letter-spacing: 0.02em; }
.code-confirm .pick { margin-top: 12px; }
.code-confirm .hint { margin-top: 12px; font-size: 14px; }
.code-out.result { margin-top: 0; }
.code-out .hint { margin-top: 14px; }
.code-confirm .btn--ghost,
.code-out .btn--ghost { margin-top: 10px; }
/* Focus lands on these cards when they appear, for screen readers; the
   buttons inside keep their own outline. */
.code-confirm:focus,
.code-out:focus { outline: none; }
.rows dd.rows__ref { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px 8px; }

/* ---- a checker card order's codes, under Order status ---- */
.vcodes-form, .vcodes { margin-top: 20px; padding-top: 18px; border-top: 1px dashed var(--line-strong); }
.vcodes-form h3, .vcodes h3 { margin: 0 0 2px; font-size: 17px; font-weight: 700; }
.vcodes-form > .hint { margin-top: 0; }
.vcodes__list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.vcode { padding: 4px 12px; border-radius: 13px; background: var(--page); border: 1px solid var(--line); }
.vcode__num { display: block; padding-top: 8px; font-size: 13px; font-weight: 600; color: var(--soft); }
.vcode__row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.vcode__row:last-child { border-bottom: 0; }
.vcode__lbl { flex: none; width: 50px; font-size: 13.5px; color: var(--soft); }
/* Big enough to read off to someone, and one tap selects all of it. */
.vcode__val {
  flex: 1; min-width: 0; font-size: 16px; font-weight: 700; letter-spacing: 0.05em;
  overflow-wrap: anywhere; -webkit-user-select: all; user-select: all;
}
.vcodes__safe {
  display: flex; align-items: flex-start; gap: 10px; margin: 14px 0 0; padding: 12px 14px; border-radius: 12px;
  background: var(--amber-bg); color: var(--amber); border: 1px solid #f0dcaa; font-size: 14px;
}
.vcodes__safe svg { width: 19px; height: 19px; flex: none; margin-top: 1px; }

/* Five tabs. On a small phone the words get smaller and may take two
   lines, or the longest label runs out of its tab; just past the phone
   layout the tabs sit a little closer. */
@media (max-width: 479px) {
  .tabs__inner.is-crowded { padding: 0 2px; }
  .tabs__inner.is-crowded .tab {
    font-size: 12px; line-height: 1.15; white-space: normal; text-align: center;
    padding-left: 2px; padding-right: 2px;
  }
}
@media (min-width: 640px) and (max-width: 767px) {
  .tabs__inner.is-crowded .tab { padding-left: 10px; padding-right: 10px; }
}
/* Six tabs, with Checker cards. On phones every label may take two lines;
   past the phone layout six labels side by side would run off the edge
   until 900px, so the phone layout of words under icons holds till then. */
@media (max-width: 639px) {
  .tabs__inner.is-packed { padding: 0 2px; }
  .tabs__inner.is-packed .tab {
    white-space: normal; text-align: center; line-height: 1.15;
    padding-left: 2px; padding-right: 2px;
  }
}
@media (min-width: 480px) and (max-width: 639px) {
  .tabs__inner.is-packed .tab { font-size: 12.5px; }
}
@media (max-width: 379px) {
  .tabs__inner.is-packed .tab { font-size: 11.5px; }
  .tabs__inner.is-packed .tab svg { width: 19px; height: 19px; }
}
@media (min-width: 640px) and (max-width: 899px) {
  .tabs__inner.is-packed { justify-content: flex-start; gap: 0; }
  .tabs__inner.is-packed .tab {
    flex: 1 1 0; flex-direction: column; gap: 3px; padding: 10px 4px 9px; font-size: 13.5px;
  }
  .tabs__inner.is-packed .tab svg { width: 21px; height: 21px; }
  .tabs__inner.is-packed .tab::after { left: 16%; right: 16%; }
}

/* ---- reviews ---- */
.reviews { display: grid; gap: 14px; grid-template-columns: minmax(0, 1fr); }
.rev-sum { display: flex; align-items: center; gap: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--line); }
.rev-sum__num { font-family: var(--figure); font-size: 42px; font-weight: 700; line-height: 1; }
.rev-sum__side { display: flex; flex-direction: column; gap: 3px; font-size: 14px; color: var(--soft); }
.rev-sum__side strong { color: var(--ink); font-size: 17px; }
.stars { display: inline-flex; gap: 2px; color: var(--star); }
.stars svg { width: 17px; height: 17px; }
.stars .is-off { color: var(--line-strong); }
.rev { display: flex; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.rev:last-child { border-bottom: 0; padding-bottom: 0; }
.rev__avatar {
  width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-ink); font-weight: 700;
}
.rev__main { flex: 1; min-width: 0; }
.rev__top { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 2px 10px; }
.rev__name { font-weight: 650; font-size: 15px; overflow-wrap: anywhere; }
.rev__date { font-size: 12.5px; color: var(--faint); }
.rev__body { margin: 4px 0 0; font-size: 14.5px; color: #353a43; white-space: pre-line; overflow-wrap: anywhere; }

/* Five radio buttons drawn as stars. They sit in the page highest first
   and are shown lowest first, so "this one and every one before it" is a
   plain sibling selector. */
.stars-pick { display: flex; flex-direction: row-reverse; justify-content: flex-end; gap: 2px; }
.stars-pick input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.stars-pick label {
  display: grid; place-items: center; padding: 4px; border-radius: 8px; cursor: pointer;
  color: var(--line-strong); transition: color 0.12s ease;
}
.stars-pick label svg { width: 32px; height: 32px; }
.stars-pick input:checked ~ label { color: var(--star); }
.stars-pick:hover input ~ label { color: var(--line-strong); }
.stars-pick:hover label:hover,
.stars-pick:hover label:hover ~ label { color: var(--star); }
.stars-pick input:focus-visible + label { outline: 2px solid var(--brand-ink); outline-offset: 1px; }
.stars-pick__text { margin: 4px 0 0; font-size: 13.5px; color: var(--soft); }

/* ---- an order still waiting for payment ---- */
.pending {
  position: fixed; left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); z-index: 40;
  max-width: 560px; margin: 0 auto;
  display: flex; align-items: center; gap: 10px; padding: 10px 10px 10px 16px;
  background: #fff; border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 14px 36px rgba(22, 24, 29, 0.2);
}
.pending__text { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.pending__text span { font-size: 13px; color: var(--soft); }
.pending__text strong { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pending .btn { margin: 0; width: auto; flex: none; min-height: 42px; padding: 8px 14px; font-size: 14.5px; }
.icon-btn {
  width: 36px; height: 36px; flex: none; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--page); color: var(--soft);
}
.icon-btn svg { width: 16px; height: 16px; }

/* ---- the foot of the shop ----
   Where the customer is told again what is on sale here and how to reach
   the seller. Built as a column on a phone and as four across once there is
   width for it. */
.foot {
  padding: 28px 16px calc(28px + env(safe-area-inset-bottom, 0px));
  color: var(--soft); font-size: 14px;
  border-top: 1px solid var(--line); background: #fff; overflow-wrap: anywhere;
}
.foot__grid { display: grid; gap: 22px; max-width: 1040px; margin: 0 auto; }
.foot__who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.foot__ring {
  width: 44px; height: 44px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-ink); overflow: hidden;
  border: 1px solid var(--brand-ring);
}
.foot__ring img { width: 100%; height: 100%; object-fit: cover; }
.foot__initials { font-family: var(--figure); font-weight: 700; font-size: 16px; }
.foot__whotext { min-width: 0; }
.foot__whotext strong { display: block; color: var(--ink); font-size: 16px; font-weight: 700; }
.foot__whotext p { margin: 2px 0 0; font-size: 13.5px; color: var(--soft); }
.foot__h {
  margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--ink);
}
.foot__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.foot__list a {
  color: var(--soft); text-decoration: none; font-size: 14px; font-weight: 600;
  display: inline-block; padding: 3px 0; min-height: 28px;
}
.foot__list a:hover, .foot__list a:focus-visible { color: var(--brand-ink); text-decoration: underline; }
.foot__base {
  margin: 22px auto 0; max-width: 1040px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--faint); text-align: center;
}
@media (min-width: 720px) {
  .foot { padding-left: 24px; padding-right: 24px; }
  .foot__grid { grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 28px; align-items: start; }
}

/* ---- the card at four across ----
   An 80px card cannot carry 17px figures and a 32px button: the size runs
   onto two lines and the price lands under the fold of the card. These are
   the same elements at the scale four columns leaves them. */
@media (max-width: 599px) {
  .bundle__body { padding: 6px; }
  .bundle__size { font-size: 14px; line-height: 1.1; }
  .bundle__val { font-size: 10px; }
  .bundle__price { font-size: 12px; margin-bottom: 6px; padding-top: 3px; }
  .bundle__buy { min-height: 30px; font-size: 11.5px; border-radius: 7px; }
  .bundle__mark { width: 34px; height: 34px; font-size: 10px; border-width: 1.5px; }
  .bundle__mark--long { font-size: 8.5px; }
}

/* ---- very narrow phones: three across, or the sizes stop fitting ---- */
@media (max-width: 359px) {
  .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
}

/* ---- wider screens ---- */
@media (min-width: 620px) {
  .sheet { align-items: center; padding: 24px; }
  .sheet__box { border-radius: 20px; padding: 24px; }
  .fields--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fields--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pay-cards--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card { padding: 24px; }
}
@media (min-width: 640px) {
  .head { padding-top: calc(46px + env(safe-area-inset-top, 0px)); padding-bottom: 40px; }
  .head__ring { width: 108px; height: 108px; }
  .head__name { font-size: 32px; }
  .head__desc { font-size: 16px; }
  .tabs__inner { justify-content: center; gap: 4px; }
  .tab { flex: 0 0 auto; flex-direction: row; gap: 8px; padding: 15px 18px 14px; font-size: 15px; }
  .tab svg { width: 19px; height: 19px; }
  .tab::after { left: 12px; right: 12px; }
}
@media (min-width: 720px) {
  .wrap { padding: 26px 24px 64px; }
  .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
  .bundle__body { padding: 10px; }
  .bundle__size { font-size: 20px; }
  .bundle__price { font-size: 15px; }
  .bundle__buy { min-height: 36px; font-size: 14px; }
  .vgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
  .voucher__body { padding: 12px; }
  .voucher__exam { font-size: 26px; }
}
@media (min-width: 900px) {
  .reviews { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); align-items: start; gap: 20px; }
  .reviews__form { position: sticky; top: 80px; }
}
@media (min-width: 980px) {
  .grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .vgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .bundle:hover, .voucher:hover { transform: none; }
}

/* ---- announcements ----
   What this shop is telling its own customers. The shop's own colour, the
   shop's own words, and nothing from anywhere else. A notice stays until the
   seller takes it down; only a popup can be put away, and only on this
   device, because a shop's customer has no account to remember it against.
   Nothing is drawn when there is nothing to say. */
.anns { display: grid; gap: 10px; margin: 0 0 16px; }
.anns[hidden] { display: none; }
.ann {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--line); border-left: 3px solid var(--line-strong);
  border-radius: 12px; background: var(--card); box-shadow: var(--shadow);
}
.ann--care { border-left-color: var(--amber); }
.ann--important { border-left-color: var(--brand-ink); }
.ann__icon { flex: none; width: 20px; height: 20px; margin-top: 2px; color: var(--brand-ink); }
.ann__icon svg { width: 20px; height: 20px; display: block; }
.ann--care .ann__icon { color: var(--amber); }
.ann__text { flex: 1 1 auto; min-width: 0; }
.ann__flag { display: block; margin-bottom: 2px; font-size: 12px; font-weight: 700; color: var(--amber); }
.ann--important .ann__flag { color: var(--brand-ink); }
.ann__title { display: block; font-size: 14.5px; font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.ann__body { margin: 3px 0 0; font-size: 14px; line-height: 1.55; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }

.annpop {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(22, 24, 29, 0.5);
}
.annpop[hidden] { display: none; }
.annpop__box { width: 100%; max-width: 400px; padding: 20px; background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow); }
.annpop__flag { display: block; margin-bottom: 4px; font-size: 12px; font-weight: 700; color: var(--amber); }
.annpop--important .annpop__flag { color: var(--brand-ink); }
.annpop__title { margin: 0 0 6px; font-size: 17px; color: var(--ink); overflow-wrap: anywhere; }
.annpop__body { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }
.annpop__foot { display: flex; justify-content: flex-end; margin-top: 16px; }
.annpop__ok {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit; font-size: 15px; font-weight: 600;
  min-height: 44px; padding: 0 18px; border: 0; border-radius: 999px; background: var(--brand); color: #fff;
}

/* ---- An order taking longer, on Order status ---------------------------
   The question to the customer who paid, in the shop's own colour, with
   Continue and wait and Request refund side by side on a desk and stacked
   on a phone. Fields are 16px and buttons 50px (.input, .btn). */
.wip { margin-top: 20px; padding: 16px; border-radius: 14px; background: var(--brand-soft); border: 1px solid var(--brand-ring); overflow-wrap: anywhere; }
.wip h3 { margin: 0; font-size: 16.5px; font-weight: 700; line-height: 1.45; color: var(--ink); }
.wip .hint { margin-top: 8px; }
.wip__btns { display: flex; flex-wrap: wrap; gap: 0 10px; }
.wip__btns .btn { flex: 1 1 180px; width: auto; min-width: 0; }
.wip__refund { margin-top: 4px; }
textarea.input.wip__why { min-height: 72px; }

/* ---- The five seconds before an order is placed -------------------------
   Nothing has been sent while this is on screen: the clock counts down, the
   rows are what is about to be bought, and Cancel order calls it off. It is
   built by js/reseller-shop.js under the button that was pressed, in the
   shop's own colour. Fields stay 16px and the button 50px (.btn). */
.hold {
  margin-top: 16px; padding: 16px; border-radius: 14px;
  background: var(--brand-soft); border: 1px solid var(--brand-ring); overflow-wrap: anywhere;
}
.hold__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.hold__head strong { font-size: 15.5px; font-weight: 700; color: var(--ink); }
.hold__clock {
  min-width: 58px; min-height: 32px; padding: 3px 12px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--card); border: 1px solid var(--line);
  font-family: var(--figure); font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink);
}
.hold__clock.is-urgent { color: var(--amber); border-color: var(--amber); }
.hold.is-going .hold__clock { color: var(--soft); }
.hold__lead { margin: 9px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--soft); }
.hold__facts { margin: 12px 0 0; display: grid; gap: 7px; }
.hold__facts > div {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 16px;
}
.hold__facts dt { font-size: 13.5px; color: var(--soft); }
.hold__facts dd {
  margin: 0; font-size: 15px; font-weight: 700; color: var(--ink);
  text-align: right; overflow-wrap: anywhere;
}
.hold__acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
.hold__acts .btn { flex: 1 1 170px; width: auto; min-width: 0; margin: 0; }
.hold__note { flex: 1 1 200px; font-size: 13.5px; line-height: 1.55; color: var(--soft); }

/* ---- About ----
   The seller's own details, read by a customer deciding whether to buy from
   a link someone sent them. */
.about { display: grid; gap: 18px; }
.about__card {
  display: flex; align-items: center; gap: 16px; padding: 20px;
  border-radius: var(--radius); background: var(--brand-soft); border: 1px solid var(--brand-ring);
}
.about__mark {
  width: 64px; height: 64px; flex: none; border-radius: 16px; overflow: hidden;
  display: grid; place-items: center; background: var(--brand-ink); color: #fff;
}
.about__mark img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about__initials { font-family: var(--figure); font-size: 24px; font-weight: 700; line-height: 1; }
.about__who { min-width: 0; }
.about__who h2 { margin: 0; font-size: 22px; font-weight: 750; overflow-wrap: anywhere; }
.about__who p { margin: 6px 0 0; font-size: 14.5px; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }
.about__h { margin: 4px 0 0; font-size: 16px; font-weight: 700; }
.about__list {
  margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); overflow: hidden;
}
.about__row { display: flex; gap: 12px; padding: 14px 16px; border-top: 1px solid var(--line); }
.about__row:first-child { border-top: 0; }
.about__row > svg { width: 20px; height: 20px; flex: none; margin-top: 2px; color: var(--soft); }
.about__row dt { font-size: 12.5px; font-weight: 600; color: var(--soft); }
.about__row dd { margin: 2px 0 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
.about__row dd a { color: var(--brand-ink); text-decoration: none; }
.about__row dd a:hover { text-decoration: underline; }
.about__ask {
  display: grid; gap: 14px; padding: 18px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--card);
}
.about__askwords h3 { margin: 0; font-size: 16px; font-weight: 700; }
.about__askwords p { margin: 4px 0 0; font-size: 14.5px; color: var(--soft); }
.about__askacts { display: flex; flex-wrap: wrap; gap: 10px; }
.about__askacts .btn { flex: 1 1 140px; margin: 0; }
@media (min-width: 620px) {
  .about__ask { grid-template-columns: 1fr auto; align-items: center; }
  .about__askacts .btn { flex: 0 0 auto; }
}

/* ---- checking a number ---- */
.ck_note { margin: 16px 0 0; font-size: 15px; font-weight: 600; }
.ck_counts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.ck_chip {
  padding: 6px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
  background: var(--page); color: var(--soft); border: 1px solid var(--line);
}
.ck_chip.is-ok { background: var(--green-bg); color: var(--green); border-color: transparent; }
.ck_chip.is-warn { background: var(--amber-bg); color: var(--amber); border-color: transparent; }
.ck_lists { display: grid; gap: 12px; margin-top: 14px; }
.ck_list { border: 1px solid var(--line); border-radius: 12px; padding: 14px; }
.ck_list.is-ok { border-color: #bde8cc; background: #f6fcf8; }
.ck_list.is-warn { border-color: #f4dfa0; background: #fffdf6; }
.ck_list h3 { margin: 0 0 8px; font-size: 14px; }
.ck_list ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.ck_list li { font-family: var(--mono); font-size: 14.5px; }
.ck_list p { margin: 0; font-size: 13.5px; color: var(--soft); }
#ck_send { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
@media (min-width: 620px) { .ck_lists { grid-template-columns: 1fr 1fr; } }

/* ---- chat with the shop ----
   A button in the corner, and a panel above it. On a phone the panel fills
   the screen, because anything smaller is unusable with a keyboard up. */
.chatfab {
  position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); z-index: 55;
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; gap: 8px; min-height: 50px; padding: 12px 18px;
  border: 0; border-radius: 999px; color: #fff; background: var(--brand-ink);
  font-size: 15px; font-weight: 700;
  box-shadow: 0 10px 26px rgba(16, 24, 40, 0.28);
}
.chatfab svg { width: 20px; height: 20px; flex: none; }
.chatfab__dot {
  position: absolute; top: 6px; right: 8px; width: 11px; height: 11px; border-radius: 50%;
  background: #f04438; border: 2px solid #fff;
}
body.has-pending .chatfab { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }

.chatbox {
  position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
  background: var(--card);
}
.chatbox__head {
  display: flex; align-items: center; gap: 12px;
  padding: calc(14px + env(safe-area-inset-top, 0px)) 16px 14px;
  background: var(--brand-ink); color: #fff;
}
.chatbox__who { min-width: 0; }
.chatbox__who strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.chatbox__who span { font-size: 13px; opacity: 0.85; }
.chatbox__close {
  appearance: none; -webkit-appearance: none; cursor: pointer; margin-left: auto;
  width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.18); border: 1px solid rgba(255, 255, 255, 0.4); color: #fff;
}
.chatbox__close svg { width: 18px; height: 18px; }
.chatbox__intro { padding: 18px 16px; overflow-y: auto; }
.chatbox__intro p { margin: 0 0 14px; font-size: 14.5px; color: var(--soft); }
.chatbox__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.chatbox__msgs {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px;
  display: flex; flex-direction: column; gap: 10px; background: var(--page);
}
.chatmsg { max-width: 80%; align-self: flex-start; }
.chatmsg p {
  margin: 0; padding: 10px 14px; border-radius: 16px 16px 16px 4px;
  background: #fff; border: 1px solid var(--line); font-size: 15px; overflow-wrap: anywhere;
}
.chatmsg span { display: block; margin-top: 4px; font-size: 11.5px; color: var(--faint); }
.chatmsg.is-mine { align-self: flex-end; }
.chatmsg.is-mine p { background: var(--brand-ink); border-color: transparent; color: #fff; border-radius: 16px 16px 4px 16px; }
.chatmsg.is-mine span { text-align: right; }
/* Somebody is writing back. Three dots in a bubble on the shop's side,
   shown only while the server says a reply really is coming: the owner,
   11 Oct 2026, wanted "a live texting back animation to see that someone
   is texting back", and a box that shows dots when nothing is coming is
   worse than one that shows nothing at all. */
.chatmsg--typing p.chat-dots {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 13px 14px; min-height: 42px;
}
.chat-dots i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--soft, #8b9099); display: block;
  animation: chat-dot 1.2s ease-in-out infinite;
}
.chat-dots i:nth-child(2) { animation-delay: .18s; }
.chat-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes chat-dot {
  0%, 60%, 100% { transform: translateY(0); opacity: .45; }
  30% { transform: translateY(-4px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-dots i { animation: none; opacity: .6; }
}

/* Ending a chat, and clearing it off this device. The shopkeeper has had
   "Mark finished" all along; the customer had nothing but an X that hides
   the box. */
.chatbox__more {
  appearance: none;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: .85;
  /* The first auto takes the free space, so this goes right and the X
     sits beside it rather than both fighting for the same gap. */
  margin-left: auto;
}

.chatbox__more svg { width: 18px; height: 18px; }
.chatbox__more:hover { background: rgb(255 255 255 / 18%); opacity: 1; }

.chatbox__menu {
  position: absolute;
  /* Under the header, which is taller on a phone with a notch. */
  top: calc(56px + env(safe-area-inset-top, 0px));
  right: 46px;
  z-index: 5;
  min-width: 220px;
  padding: 6px;
  border-radius: 14px;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e6e9ef);
  box-shadow: 0 18px 40px -18px rgb(11 18 32 / 35%);
  color: var(--ink, #0b1220);
}

.chatbox__menu button {
  appearance: none;
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14.5px;
  padding: 11px 12px;
  min-height: 44px;
  border-radius: 10px;
  cursor: pointer;
  color: inherit;
}

.chatbox__menu button:hover { background: var(--page, #f3f5f9); }

/* The honest part, said where the choice is made and not in a help page. */
.chatbox__menunote {
  margin: 4px 2px 2px;
  padding: 0 10px 6px;
  font-size: 12px;
  color: var(--soft, #6b7280);
}

.chatbox__closed { margin: 0; padding: 10px 16px; font-size: 13.5px; color: var(--soft); background: var(--page); }
.chatbox__write {
  display: flex; align-items: flex-end; gap: 10px; padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line); background: var(--card);
}
.chatbox__write textarea {
  flex: 1 1 auto; min-height: 48px; max-height: 140px; resize: none; overflow-y: auto;
  padding: 13px 14px; border-radius: 14px; border: 1px solid var(--line-strong);
  font: inherit; font-size: 16px; line-height: 1.3;
}
.chatbox__send {
  appearance: none; -webkit-appearance: none; cursor: pointer; flex: none;
  width: 48px; height: 48px; border-radius: 50%; border: 0; display: grid; place-items: center;
  background: var(--brand-ink); color: #fff;
}
.chatbox__send svg { width: 20px; height: 20px; }
.chatbox .err { margin: 0; padding: 0 16px 12px; }

@media (min-width: 620px) {
  .chatbox {
    inset: auto 16px 86px auto; width: 380px; height: min(560px, calc(100vh - 140px));
    border-radius: 18px; overflow: hidden; box-shadow: 0 24px 60px rgba(16, 24, 40, 0.28);
    border: 1px solid var(--line);
  }
  .chatbox__head { padding: 14px 16px; }
}

/* ---- what a chat message can carry ---- */
.chatmsg.has-piece p { margin-top: 6px; }
.chatmsg__sticker { display: block; width: 92px; height: 92px; color: var(--brand-ink); }
.chatmsg.is-mine .chatmsg__sticker { color: var(--brand-deep); margin-left: auto; }
.chatmsg__sticker svg { width: 100%; height: 100%; }
.chatmsg__pic { display: block; }
.chatmsg__pic img { display: block; max-width: 220px; border-radius: 14px; }
.chatmsg__audio { width: 220px; max-width: 100%; }
.chatmsg__video { max-width: 220px; border-radius: 14px; display: block; }
.chatmsg__file {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 14px;
  background: #fff; border: 1px solid var(--line); text-decoration: none; color: var(--ink); font-size: 14.5px;
}
.chatmsg__file small { margin-left: auto; color: var(--brand-ink); font-weight: 700; }
.chatbox__staged { margin: 0; padding: 8px 16px; font-size: 13px; color: var(--soft); background: var(--page); }
.chatbox__stickers {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px;
  padding: 10px 16px; border-top: 1px solid var(--line); background: var(--card);
}
.chatbox__stickers button {
  appearance: none; -webkit-appearance: none; cursor: pointer; border: 0; background: none;
  padding: 6px; border-radius: 10px; color: var(--brand-ink);
}
.chatbox__stickers button:hover { background: var(--brand-soft); }
.chatbox__stickers svg { width: 100%; height: auto; display: block; }
.chatbox__icon {
  appearance: none; -webkit-appearance: none; cursor: pointer; flex: none;
  width: 42px; height: 42px; border-radius: 50%; border: 1px solid var(--line-strong);
  display: grid; place-items: center; background: var(--card); color: var(--soft);
}
.chatbox__icon svg { width: 19px; height: 19px; }
.chatbox__icon.is-on { background: var(--red-bg); color: var(--red); border-color: transparent; }
@media (max-width: 380px) { .chatbox__stickers { grid-template-columns: repeat(4, 1fr); } }

/* ---- one of the shop's own pages ---- */
.policy {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 22px;
}
.policy h2 { margin: 0 0 6px; font-size: 22px; font-weight: 750; }
.policy h3 { margin: 22px 0 6px; font-size: 16px; font-weight: 700; }
.policy p { margin: 0 0 12px; font-size: 15.5px; line-height: 1.65; color: var(--soft); overflow-wrap: anywhere; }
.policy p:last-child { margin-bottom: 0; }
.policy__wait { color: var(--faint); }
.policy__when { margin: 10px 0 0; font-size: 13px; color: var(--faint); }
.policy__more { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; margin: 16px 0 0; font-size: 14px; color: var(--soft); }
.policy__more a { color: var(--brand-ink); text-decoration: none; font-weight: 600; margin-right: 12px; }
.policy__more a:hover { text-decoration: underline; }

/* Shop-chat: a message the shop edited or took back. */
.chatmsg.is-deleted p.chatmsg__gone { background: transparent; border: 1px dashed var(--line, #d7dbe3); color: var(--faint, #667085); font-style: italic; }
.chatmsg.is-mine.is-deleted p.chatmsg__gone { background: transparent; color: rgba(255,255,255,.85); border-color: rgba(255,255,255,.5); }

/* ---- Background media behind the shop hero (migrate-194) ----------------
   The owner sets a default for every shop; a reseller may choose their own.
   A picture or a clip sits behind the name and buttons, with a scrim over it
   so the white text keeps its contrast on any image. ---- */
.hero { position: relative; isolation: isolate; overflow: hidden; }
.hero__inner { position: relative; z-index: 2; }
.hero__media { position: absolute; inset: 0; z-index: 0; background-size: cover; background-position: center; pointer-events: none; }
.hero__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The scrim keeps the brand colour present and the copy readable. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(4, 22, 52, 0.35) 0%, rgba(4, 22, 52, 0.62) 100%),
    radial-gradient(120% 90% at 50% 30%, rgba(4, 22, 52, 0) 35%, rgba(4, 22, 52, 0.5) 100%);
}

/* ---- Hero background controls -------------------------------------------
   Hold the picture, or turn the sound on. Small and out of the way at the
   corner of the hero, never over the shop's name or its buttons. ---- */
.hero__ctrls {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 3;
  display: flex;
  gap: 8px;
}
/* Icons, not words (owner, 9 Oct 2026). Round, 38px, which is still a
   comfortable tap and about a third of the room the worded pills took
   over the shop's own name. The words live in aria-label and title. */
.hero__ctrl {
  appearance: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(8, 24, 54, 0.45);
  color: #fff;
  border-radius: 999px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.hero__ctrl svg { width: 18px; height: 18px; fill: currentColor; }
.hero__ctrl:hover { background: rgba(8, 24, 54, 0.62); }
.hero__ctrl:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (max-width: 560px) {
  .hero__ctrls { right: 10px; bottom: 10px; gap: 6px; }
  /* 36px is still over the 32px a thumb needs, and two of them take less
     width than one of the old worded buttons. */
  .hero__ctrl { width: 36px; height: 36px; }
  .hero__ctrl svg { width: 17px; height: 17px; }
}

/* A checker card with no codes in stock is still on sale: the order waits
   for the next load. The customer is told before paying, not after. */
.voucher__wait { margin: 0 0 8px; font-size: 11.5px; line-height: 1.35; color: var(--muted, #64748b); }

/* One background fading into the next, for a shop's own pictures and
   clips. The new one is laid over the old and brought up, so the brand
   colour never flashes through between two of them. */
.hero__media { transition: opacity .42s ease; }
.hero__media--in { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .hero__media { transition: none; }
  .hero__media--in { opacity: 1; }
}

/* ---- buy again, sorting and the two tags --------------------------------
   Added 8 Oct 2026. A shop with thirty bundles was one long scroll with
   nothing to steer by, and a returning customer had to find the same
   bundle and type the same number again. */
.again { margin: 0 0 14px; }
.again__lead { margin: 0 0 8px; font-size: 12.5px; font-weight: 700; letter-spacing: .01em; color: var(--muted, #64748b); }
.again__row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; }
.again__row::-webkit-scrollbar { height: 0; }
.again__pick {
  flex: 0 0 auto; display: grid; gap: 2px; text-align: left; cursor: pointer;
  padding: 10px 14px; border-radius: 14px; border: 1px solid var(--line, rgba(15, 23, 42, .12));
  background: #fff; color: inherit; font: inherit; min-width: 150px;
}
.again__pick:hover { border-color: var(--brand, #0164ce); }
.again__what { font-weight: 700; font-size: 14px; }
.again__to { font-size: 12px; color: var(--muted, #64748b); font-variant-numeric: tabular-nums; }
.again__price { font-size: 13px; font-weight: 700; color: var(--brand, #0164ce); }

.sorter { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 13px; }
.sorter label { color: var(--muted, #64748b); }
.sorter select { padding: 7px 10px; border-radius: 10px; border: 1px solid var(--line, rgba(15, 23, 42, .12)); background: #fff; font: inherit; }

.bundle__band { position: relative; }
.bundle__tag {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 3px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 800;
  letter-spacing: .02em; background: rgba(255, 255, 255, .94); color: #0f172a;
  box-shadow: 0 2px 8px rgba(15, 23, 42, .18);
}
.bundle__tag--hot { color: #b45309; }
.bundle__tag--value { color: #047857; }

/* The way out of the form for somebody who will not pay online. */
.btn--wa { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 10px; background: #25d366; color: #05331a; border: 0; }
.btn--wa svg { width: 18px; height: 18px; }
.btn--wa:hover { filter: brightness(1.04); }

/* The page held still while a sheet or a menu is open. overflow: hidden
   alone is ignored by iOS Safari, which is why the shop behind an order
   sheet still scrolled under the thumb. See js/scroll-lock.js. */
body.scroll-locked { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }
body:not(.scroll-locked) { top: 0 !important; }

/* ============================================================
   Thumbs, 9 Oct 2026.

   Measured every button on a 390px phone: the Buy button on each bundle
   was 67x30 and the two hero controls were 36x36, against the 44px both
   Apple and Google ask for. Thirty four of them on one page, and the Buy
   button is the whole point of the page.

   The buttons are not redrawn. Each grows an invisible hit area to 44,
   so the shop looks exactly as the reseller set it up and the thumb
   stops missing. Only where the pointer is a finger.
   ============================================================ */
@media (pointer: coarse) {
  .bundle__buy,
  .hero__ctrl,
  .voucher__buy { position: relative; }

  .bundle__buy::after,
  .hero__ctrl::after,
  .voucher__buy::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    min-width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    background: none;
    border: 0;
  }
}

/* ---- finding a shop from the bare address ----
   The domain on its own names no shop, so this is the one useful thing
   that page can offer. Nothing is listed until two letters are typed: it
   answers a name somebody already knows, it is not a directory. */
.findshop {
  display: flex;
  gap: 8px;
  width: min(420px, 100%);
  margin: 18px auto 0;
}

.findshop input {
  flex: 1 1 auto;
  min-width: 0;
  /* 16px, or iOS zooms the whole page the moment it is tapped. */
  font: inherit;
  font-size: 16px;
  padding: 13px 15px;
  border-radius: 14px;
  border: 1px solid var(--line-strong, #d6dbe3);
  background: var(--card, #fff);
  color: inherit;
}

.findshop input:focus {
  outline: none;
  border-color: var(--brand-ink, #0164ce);
}

/* .btn in this stylesheet is a full-width block with a top margin, which
   is right under a form and wrong beside an input: it took the row and
   left the box a stub. The same trap as the chat Send button. */
.findshop .btn {
  flex: 0 0 auto;
  width: auto;
  margin-top: 0;
  min-height: 48px;
  padding: 0 22px;
}

/* This stylesheet has no visually-hidden of its own, so the label for the
   search box was simply on screen. */
.findshop .visually-hidden,
.state .visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.findshop__note { margin: 10px 0 0; font-size: 13.5px; color: var(--soft, #6b7280); }

.findshop__list {
  list-style: none;
  margin: 12px auto 0;
  padding: 0;
  width: min(420px, 100%);
  display: grid;
  gap: 8px;
}

.findshop__hit {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: 14px;
  border: 1px solid var(--line, #e6e9ef);
  background: var(--card, #fff);
  text-decoration: none;
  color: inherit;
  font-weight: 600;
  text-align: left;
}

.findshop__hit:hover { border-color: var(--brand-ink, #0164ce); }

.findshop__hit img,
.findshop__letter {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  object-fit: cover;
  flex: 0 0 auto;
}

.findshop__letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-soft, #e8f1fd);
  color: var(--brand-ink, #0164ce);
  font-weight: 700;
}
