/* ==========================================================================
   88M — mobile build (/m)
   A separate layout, not a narrowed desktop: drawer nav, split JOIN/LOGIN,
   provider list cards and horizontally scrolling rails.
   ========================================================================== */

:root {
  --m-header-h: 52px;

  /* Sampled off the supplied screens — a colder, more saturated blue than the
     desktop palette, on a very light blue-grey ground with white cards. */
  --m-blue:       #0b53c8;
  --m-blue-lit:   #1157d8;
  --m-blue-deep:  #0846b4;
  --m-teal:       #0fbdc0;

  /* Sampled straight off logo.png - the 88 is this green, the M this orange.
     The primary CTA pair paints in them so the buttons read as the mark. */
  --m-logo-green:       #51a37d;
  --m-logo-green-lit:   #5eb68c;
  --m-logo-green-deep:  #3f8b68;
  --m-logo-orange:      #cf5a46;
  --m-logo-orange-lit:  #dd6a54;
  --m-logo-orange-deep: #b64837;

  --m-ground:     #f4f7fc;
  --m-line:       #e7ebf2;
  --m-shadow:     0 1px 3px rgba(15, 22, 31, .05);
  --m-card:       0 2px 10px rgba(23, 43, 77, .06);
  --m-radius:     14px;

  /* Tints behind the tile icons */
  --m-tint-blue:   #e9f0ff;
  --m-tint-indigo: #eeecff;
  --m-tint-green:  #e6f6ec;
  --m-tint-purple: #f1eaff;
  --m-tint-amber:  #fff2dc;
}

/* Every traced mark paints in currentColor and sizes off its box, so one rule
   covers the tabs, the buttons and the cards alike. */
.ic-svg { width: 1em; height: 1em; display: block; flex: 0 0 auto; }

/* `clip` rather than `hidden`: hidden would make body a scrollport and break
   window scrolling / position:fixed on iOS. */
html, body { overflow-x: clip; }
body {
  background: var(--c-soft);
  padding-top: var(--m-header-h);
  max-width: 640px;
  margin-inline: auto;
}

.wrap { padding-inline: 12px; }
.block { background: #fff; }

/* --- Header -------------------------------------------------------------- */
.m-header {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 640px;
  height: var(--m-header-h);
  background: #fff;
  z-index: 50;
  display: flex;
  align-items: center;
  padding: 0 12px;
  box-shadow: 0 1px 6px rgba(15, 22, 31, .06);
}
.m-header .burger { width: 22px; gap: 4px; }
.m-header .brand-center {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
}
.m-header .brand-logo img { height: 28px; }

/* --- Drawer -------------------------------------------------------------- */
/* The mobile build is capped at 640px and centred, so the overlay layer tracks
   that column. It also clips: without that, the closed drawer would park in the
   desktop gutter beside the column instead of off-screen. */
.m-overlay {
  position: fixed;
  top: 0;
  bottom: 0;
  left: max(0px, calc(50% - 320px));
  width: min(100%, 640px);
  overflow: hidden;
  pointer-events: none;
  z-index: 70;
}
.m-scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  opacity: 0;
  transition: opacity .22s ease;
}
.m-scrim.open { opacity: 1; pointer-events: auto; }

.m-drawer {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 80%;
  max-width: 320px;
  background: #fff;
  transform: translateX(-100%);
  transition: transform .24s ease;
  overflow-y: auto;
  padding: 18px 20px 30px;
}
.m-drawer.open { transform: none; pointer-events: auto; }
.drawer-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px 8px; }
.drawer-grid a { text-align: center; font-size: 11px; color: var(--c-ink); line-height: 1.25; }
/* One box height for every kind of mark in the grid — an icon-font glyph, a
   flag disc, a traced drawing — so the labels under them sit on one line
   whatever the row happens to hold. */
.drawer-grid .ico {
  height: 28px;
  font-size: 22px;
  color: var(--c-blue-mid);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
  position: relative;
}
.drawer-grid .ico .ic-svg { width: 24px; height: 24px; }
.drawer-grid .ico .dot-new {
  position: absolute;
  top: -3px;
  left: 50%;
  margin-left: 4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--c-red-badge);
  color: #fff;
  font-size: 7px;
  display: grid;
  place-items: center;
}
.m-drawer .btn-orange { display: block; text-align: center; margin-top: 34px; height: 42px; line-height: 32px; border-radius: var(--radius); }
.drawer-grid button {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  text-align: center;
  font-size: 11px;
  color: var(--c-ink);
  line-height: 1.25;
}
.drawer-grid .ico.flag { margin: 0 auto 8px; }

/* Language pane — replaces the menu inside the same drawer */
.drawer-lang { display: none; }
.m-drawer.lang .drawer-menu { display: none; }
.m-drawer.lang .drawer-lang { display: block; }

.drawer-lang .lang-head {
  display: flex;
  align-items: center;
  padding-bottom: 14px;
  border-bottom: 2px solid #f7f7f7;
  margin-bottom: 18px;
}
.drawer-lang .lang-head .globe { font-size: 26px; color: var(--c-blue-light); line-height: 1; }
.drawer-lang .lang-head p { font-size: 17px; font-weight: 600; color: var(--c-ink); margin: 0 0 0 10px; }
.drawer-lang .lang-x {
  margin-left: auto;
  border: 0;
  background: none;
  color: var(--c-muted);
  font-size: 15px;
  padding: 4px;
}
.drawer-lang .region-name { font-size: 12px; }
.drawer-lang .lang-opt { font-size: 12px; }

/* --- Hero + primary CTAs ------------------------------------------------- */
.m-hero {
  position: relative;
  aspect-ratio: 375 / 120;
  overflow: hidden;
  touch-action: pan-y;
  user-select: none;
  outline: none;
}
.m-hero-track { display: flex; height: 100%; will-change: transform; }
.m-hero .slide { position: relative; flex: 0 0 100%; height: 100%; overflow: hidden; }
.m-hero .copy { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: #fff; z-index: 2; }
.m-hero .copy small { font-size: 8px; letter-spacing: .06em; display: block; opacity: .9; }
.m-hero .copy b { font-size: 14px; font-weight: 700; line-height: 1.15; display: block; }
.m-hero .dots { position: absolute; right: 12px; bottom: 8px; display: flex; gap: 4px; z-index: 3; }
.m-hero .dots button {
  width: 5px;
  height: 5px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: rgba(255, 255, 255, .6);
  display: block;
  transition: width .2s ease;
}
.m-hero .dots button.active { width: 14px; background: var(--c-orange); }

/* Two rounded buttons side by side rather than one flush strip, painted in the
   logo's own two colours: green to join, orange to sign in, each led by its
   own mark. */
.m-cta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  padding: 12px;
}
.m-cta a {
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  border-radius: 11px;
  color: #fff;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: .05em;
}
.m-cta .ic-svg { width: 20px; height: 20px; }
.m-cta .join {
  background: linear-gradient(135deg, var(--m-logo-green-lit), var(--m-logo-green-deep));
  box-shadow: 0 6px 14px rgba(63, 139, 104, .26);
}
.m-cta .login {
  background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep));
  box-shadow: 0 6px 14px rgba(182, 72, 55, .26);
}

/* --- Every other button ---------------------------------------------------
   The pair above was the only one drawn off the logo; the rest had each picked
   up a flat fill somewhere down the file — `--c-orange` #ff8a00 on Submit, a
   pale `#8dc4f5` inline on Add Bank, and nothing at all on Upload Receipt,
   which came out as a bare Bootstrap outline. They are one family now, taking
   their shape and their weight from the pair they sit under.

   **The colours mean the same thing they do on /web.** Orange is the mark's
   own and is the thing you came to do — Login, Submit, Save, Add, Confirm.
   Blue is the route *to* one, never the doing — Add Bank, Upload Receipt.
   Green stays with `.m-cta .join`, so registering never looks like anything
   else on the screen.

   Painted from **this build's** tokens, not /web's: `--m-blue` is a deeper,
   colder blue than the desktop's on purpose, and the corner is the pair's own
   11px rather than the 8px `var(--radius)` the shared sheet uses. Sizes stay
   where each rule already put them — a full-width 46px cashier button and a
   dialog button in a two-column grid are not the same button, only the same
   style. */
.btn-orange,
.btn-upload,
.btn-addbank,
.m-acc-submit,
.vf-ghost,
.cf-btn {
  /* The fill rides on variables, so a colour below is three lines instead of
     a repeat of this rule. Unset it is the logo orange — which makes every
     confirm right by default and leaves only exceptions to say anything. */
  --cta-a: var(--m-logo-orange-lit);
  --cta-b: var(--m-logo-orange-deep);
  --cta-glow: rgba(182, 72, 55, .26);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 0;
  border-radius: 11px;
  background: linear-gradient(135deg, var(--cta-a), var(--cta-b));
  box-shadow: 0 6px 14px var(--cta-glow);
  color: #fff;
  font-weight: 600;
  letter-spacing: .03em;
  text-align: center;
  transition: filter .15s ease;
}

.btn-orange:hover, .btn-orange:active,
.btn-upload:hover, .btn-upload:active,
.btn-addbank:hover, .btn-addbank:active,
.m-acc-submit:hover, .m-acc-submit:active,
.cf-btn:hover, .cf-btn:active {
  /* Restated: theme.css's own `.btn-orange:hover` still sets the flat
     `--c-orange` it used to be, and nothing above would replace it — the fill
     would snap back to the old colour on the first tap. */
  background: linear-gradient(135deg, var(--cta-a), var(--cta-b));
  color: #fff;
  filter: brightness(1.07);
}

/* A button that cannot be pressed still has to read as a button — Bootstrap's
   `.btn:disabled` sets a grey of its own, so the fill is restated and the
   opacity is what says it is locked. */
.btn-orange:disabled,
.btn-upload:disabled,
.btn-addbank:disabled,
.m-acc-submit:disabled {
  background: linear-gradient(135deg, var(--cta-a), var(--cta-b));
  color: #fff;
  opacity: .45;
  box-shadow: none;
  filter: none;
  cursor: not-allowed;
}

/* Blue — the routes to money, never the confirming of it. */
.btn-upload,
.btn-addbank {
  --cta-a: var(--m-blue-lit);
  --cta-b: var(--m-blue-deep);
  --cta-glow: rgba(11, 83, 200, .26);
}

/* Cancel, standing beside a confirm. Flat on purpose: two filled buttons side
   by side are two things asking to be tapped. */
.vf-ghost, .vf-ghost:disabled,
.cf-btn.ghost {
  background: #fff;
  border: 1px solid var(--c-line);
  color: var(--c-body);
  box-shadow: none;
  font-weight: 500;
}
.vf-ghost:hover, .cf-btn.ghost:hover, .cf-btn.ghost:active {
  background: var(--c-soft);
  border-color: var(--c-muted);
  color: var(--c-body);
  filter: none;
}

/* Destructive actions say so in the button, not just in the sentence. */
.cf-btn.go.danger { --cta-a: #ef2f4e; --cta-b: #c40024; --cta-glow: rgba(196, 0, 36, .26); }

/* The confirm in a member dialog. `.vf-acts .btn-orange` is two classes and
   out-ranks the fill above — it is there to beat Bootstrap's disabled grey,
   and now has to carry the gradient to do it. */
.vf-acts .btn-orange,
.vf-acts .btn-orange:disabled {
  background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep));
  color: #fff;
}

/* Stands in for the login or registration form while that switch is off. */
.service-closed {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--c-soft);
  border: 1px dashed var(--c-line);
  border-radius: var(--radius);
  padding: 18px;
  margin: 6px 0;
}
.service-closed i { font-size: 22px; color: var(--c-muted); }
.service-closed p { margin: 0; font-size: 13px; color: var(--c-body); line-height: 1.6; }

.m-ticker {
  background: #fff;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--c-body);
}
.m-ticker .ico { color: var(--c-red-badge); font-size: 13px; flex: 0 0 auto; }
.m-ticker .ticker-view { flex: 1; overflow: hidden; }
.m-ticker .ticker-track {
  display: inline-flex;
  gap: 50px;
  white-space: nowrap;
  will-change: transform;
  animation: ticker-scroll 26s linear infinite;
}
.m-ticker .ticker-msg { font-size: 12px; color: var(--c-muted); }
.m-ticker .ticker-msg a { text-decoration: underline; margin-left: 6px; }
@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .m-ticker .ticker-track { animation: none; }
}

/* --- Uploaded artwork ----------------------------------------------------- */
/* contain, not cover — the banner is uploaded at exactly this slot's shape, so
   nothing is cropped and nothing is letterboxed. */
.m-hero { background: #000; }
.m-hero .hero-img { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
/* The promotion list shows the picture at the shape it was uploaded in — a
   fixed height with object-fit: cover was cutting the top and bottom off a
   banner, while More Info showed the same file whole. `height: auto` lets the
   row grow to the picture instead of the picture being trimmed to the row. */
.m-promo-item .thumb img { width: 100%; height: auto; display: block; }
.m-promo-card .thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* Same rule as the promotion list: the banner is stored whole inside its
   own box, so a fixed height with object-fit: cover was shaving the top and
   bottom off it. Let the slot grow to the picture instead. */
.m-auth-banner img.thumb { width: 100%; height: auto; display: block; }

/* --- Promotion detail ----------------------------------------------------- */
.m-promo-detail { background: #fff; padding: 14px 14px 30px; }
.m-promo-detail img { width: 100%; border-radius: var(--radius); display: block; margin-bottom: 14px; }
.m-promo-detail h1 { font-size: 19px; font-weight: 600; margin-bottom: 8px; }
.m-promo-detail .lead { font-size: 13px; color: var(--c-body); margin-bottom: 14px; }
.m-promo-detail .rich { font-size: 13px; line-height: 1.7; color: var(--c-body); }
.m-promo-detail .rich table { border-collapse: collapse; width: 100%; margin: 12px 0; }
.m-promo-detail .rich table td, .m-promo-detail .rich table th { border: 1px solid var(--c-line); padding: 6px 9px; }
.m-promo-detail .rich img { max-width: 100%; height: auto; }
.m-promo-detail .w-100 { width: 100%; margin-top: 18px; }

/* The home page carries the bottom bar too once signed in, so it needs the
   same clearance the member pages get. */
body.has-bottom-nav { padding-bottom: 74px; }

/* Referral card — signed-in home page */
.m-referral {
  display: block;
  margin: 10px 12px 0;
  border-radius: var(--radius);
  padding: 18px 20px 22px;
  background: linear-gradient(160deg, #cfe2fb, #e5eeff 60%, #dfeafd);
  color: var(--c-ink);
}
.m-referral h4 { font-size: 16px; font-weight: 600; line-height: 1.3; margin-bottom: 10px; }
.m-referral p { font-size: 12px; color: var(--c-body); line-height: 1.5; margin: 0; }
.m-referral .code {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 14px;
  background: rgba(255, 255, 255, .7);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-size: 13px;
  color: var(--c-body);
}

/* --- Category tabs -------------------------------------------------------
   Separate cards rather than segments of one grey pill, and every tab carries
   its icon — the selected one is the only thing that changes colour. */
.cat-tabs {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 12px 12px 4px;
  overflow-x: auto;
  scrollbar-width: none;
}
.cat-tabs::-webkit-scrollbar { display: none; }

.cat-tab {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 38px;
  padding: 0 13px;
  border: 1px solid var(--m-line);
  border-radius: 11px;
  background: #fff;
  font-size: 12.5px;
  color: #26313f;
  white-space: nowrap;
  box-shadow: var(--m-shadow);
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.cat-tab .ic-svg { width: 19px; height: 19px; }
.cat-tab i { font-size: 17px; line-height: 1; }
.cat-tab.active {
  background: var(--m-blue);
  border-color: var(--m-blue);
  color: #fff;
  box-shadow: 0 6px 14px rgba(11, 83, 200, .26);
}

/* --- Category panels ----------------------------------------------------- */
.cat-panels { background: #fff; }
.cat-panel { display: none; }
.cat-panel.active { display: block; }

/* 4-up provider tiles (every category except the sportsbook) */
.prov-grid {
  display: grid;
  /* minmax(0,…) for the same reason as .m-tile-grid: a platform name is
     typed in /admin and can be any length. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  padding: 4px 12px 14px;
}
.prov-tile {
  position: relative;
  aspect-ratio: 88 / 110;
  border-radius: 6px;
  overflow: hidden;
  display: block;
}
.prov-tile .pt-logo {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 14px 4px 6px;
  background: linear-gradient(transparent, rgba(0, 0, 0, .65));
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  text-align: center;
  line-height: 1.15;
}
.prov-tile .pill-hot,
.prov-tile .pill-new {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 3;
  font-size: 9px;
  padding: 3px 6px;
  border-radius: 0 6px 0 8px;
}

/* --- Provider cards ------------------------------------------------------ */
.prov-list { background: #fff; padding: 0 12px 12px; }
.prov-card {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 10px;
  min-height: 96px;
  display: flex;
  align-items: center;
  background: var(--c-soft);
}
.prov-card .pc-art { position: absolute; inset: 0 0 0 45%; }
.prov-card .pc-body { position: relative; z-index: 2; padding: 12px 14px; }
.prov-card .pc-logo { font-size: 15px; font-weight: 800; color: var(--c-blue); letter-spacing: -.5px; }
.prov-card .pc-name { font-size: 12px; color: var(--c-body); margin: 2px 0 10px; }
.prov-card .pc-acts { display: flex; gap: 8px; }
.prov-card .pc-acts a {
  font-size: 11px;
  border-radius: var(--radius-pill);
  padding: 5px 14px;
}
/* Small inline actions keep their pill — only the colour joins the family. */
.prov-card .pc-acts .bet {
  background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep));
  color: #fff;
}
.prov-card .pc-acts .desk { border: 1px solid var(--c-line); background: #fff; color: var(--c-body); }
.prov-card .pill-hot { position: absolute; top: 8px; right: 8px; z-index: 3; }

/* --- Section headers ----------------------------------------------------- */
.m-sec { background: #fff; padding: 16px 0; margin-top: 10px; }
.m-sec-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px 12px;
}
.m-sec-head h2 { font-size: 15px; font-weight: 600; margin: 0; }
.m-sec-head .see-all { font-size: 12px; text-decoration: underline; color: var(--c-ink); }
.m-sec .rail { padding: 0 12px 4px; }

/* --- Platform cards ------------------------------------------------------
   The same artwork the desktop submenu shows, four to a row. The cards are
   portrait (260×461), so narrow columns keep a product from running down the
   screen. The fishing sheet is squarer than the rest — heights are left to the
   picture rather than forced, so no card is ever cropped. */
.plat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  background: #fff;
  padding: 12px;
}
.plat-tile img { width: 100%; height: auto; display: block; border-radius: var(--radius-sm); }
.plat-tile:active img { transform: scale(.97); }

/* --- Promo cards ---------------------------------------------------------- */
/* The picture is uploaded into a 750x300 box, so the thumb keeps that shape
   rather than a fixed height that would trim it. */
.m-promo-card {
  width: 300px;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--c-soft);
}
.m-promo-card .thumb { aspect-ratio: 5 / 2; }
.m-promo-card .body { padding: 10px; background: #fff; }
/* Two lines for every card, so the buttons in the rail line up whatever the
   operator called the promotion. */
.m-promo-card .title {
  font-size: 12px;
  line-height: 1.4;
  height: 2.8em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.m-promo-card .acts { margin-top: 8px; }
.m-promo-card .acts .btn { width: 100%; font-size: 13px; padding: 8px 0; }

/* --- Game cards ---------------------------------------------------------- */
.m-game-card { width: 152px; }
.m-game-card .thumb { width: 152px; height: 108px; border-radius: var(--radius); position: relative; }
.m-game-card .name { font-size: 12px; margin-top: 7px; display: flex; align-items: center; gap: 5px; }
.m-game-card .name b { font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.m-game-card .prov { font-size: 11px; color: var(--c-muted); }
.m-game-card .pill-rtp { margin-top: 5px; }

/* **Three across, and every column exactly a third.** `1fr` alone is
   `minmax(auto, 1fr)`, whose auto minimum is the tile's min-content width —
   which is the game's own name — so one long name from a synced catalogue used
   to widen its column and squeeze the other two. Games arrive with names
   nobody here chose, so the track has to be floored at 0. */
.m-tile-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 10px; padding: 0 12px; }
.m-tile .thumb { aspect-ratio: 1; border-radius: 16px; position: relative; }
.m-tile .thumb .pill-new { position: absolute; top: 6px; left: 6px; font-size: 9px; padding: 3px 6px; }
.m-tile .name { font-size: 11px; margin-top: 7px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.m-tile .pill-rtp { margin-top: 4px; font-size: 10px; }

/* --- Footer -------------------------------------------------------------- */
.m-footer { background: #fff; padding: 26px 16px 40px; text-align: center; }
.m-foot-links { display: flex; align-items: center; justify-content: space-between; padding: 18px 0; gap: 12px; }
.m-foot-links .aff { font-size: 14px; font-weight: 600; text-decoration: underline; }
.m-foot-block { text-align: left; padding-top: 8px; }
.m-foot-block h5 { font-size: 12px; color: var(--c-body); margin-bottom: 10px; }
.m-foot-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 22px; }
.foot-licence img { height: 44px; width: auto; display: block; }

/* --- Inner page furniture ------------------------------------------------ */
.m-page-bar {
  background: var(--c-soft);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  position: sticky;
  top: var(--m-header-h);
  z-index: 40;
}
.m-page-bar .back { font-size: 18px; color: var(--c-blue-mid); }
.m-page-bar .ttl { font-size: 15px; font-weight: 500; }
.m-page-bar .sel {
  margin-left: auto;
  border: 1px solid var(--c-line);
  border-radius: var(--radius-pill);
  background: #fff;
  font-size: 13px;
  padding: 6px 14px;
  min-width: 150px;
  color: var(--c-ink);
}

/* Platform picker — the second level of the menu on a product page. It takes
   the right-hand end of the back bar, and its panel hangs off the bar's whole
   width so a logo and a full brand name fit on every row. */
.plat-pick { margin-left: auto; position: relative; }
.pp-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius-pill);
  background: #fff;
  font-size: 13px;
  padding: 7px 14px;
  min-width: 170px;
  color: var(--c-ink);
}
.pp-trigger .pp-label { flex: 1; text-align: center; }
.pp-trigger i { font-size: 12px; color: var(--c-muted); transition: transform .15s; }
.pp-trigger[aria-expanded="true"] i { transform: rotate(180deg); }

.pp-panel {
  position: absolute;
  top: calc(100% + 10px);
  /* The bar's own width, not the viewport's — body is capped at 640px and
     centred, so 100vw would hang the panel off the side of the column. */
  right: -12px;
  width: calc(100vw - 24px);
  max-width: 616px;
  max-height: 62vh;
  overflow-y: auto;
  padding: 8px;
  background: #fff;
  border-top: 1px solid var(--c-line);
  box-shadow: 0 12px 24px rgba(0, 0, 0, .12);
  z-index: 45;
}
.pp-opt {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  border: 0;
  background: none;
  border-radius: var(--radius-pill);
  padding: 10px 16px;
  font-size: 14px;
  color: var(--c-muted);
  text-align: left;
}
/* The chosen platform has to read as chosen — this is the whole reason the
   picker is not a <select>. */
.pp-opt.active { background: var(--c-orange); color: #fff; }
.pp-logo { flex: 0 0 40px; height: 28px; display: flex; align-items: center; }
.pp-logo img {
  max-width: 100%;
  max-height: 100%;
  border-radius: 4px;
  /* One grey column: the marks are a dozen different brand colours and would
     fight both each other and the orange row. The chosen row keeps its own
     colour, because there the mark sits on orange and needs the contrast. */
  filter: grayscale(1);
}
.pp-opt.active .pp-logo img { filter: none; }

.m-filter { display: flex; gap: 10px; padding: 14px 12px; background: var(--c-soft); }
.m-filter .sort {
  border: 1px solid var(--c-line);
  border-radius: var(--radius-pill);
  background: #fff;
  font-size: 13px;
  padding: 7px 12px;
  color: var(--c-body);
  width: 140px;
}
.m-filter .search {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius-pill);
  background: #fff;
  padding: 7px 14px;
  color: var(--c-muted);
}
.m-filter .search input { border: 0; outline: none; width: 100%; font-size: 13px; }

.m-chip-row { display: flex; gap: 8px; overflow-x: auto; padding: 0 12px 14px; background: var(--c-soft); scrollbar-width: none; }
.m-chip-row::-webkit-scrollbar { display: none; }
.m-chip {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  font-size: 13px;
  color: var(--c-muted);
  padding: 7px 18px;
  border-radius: var(--radius-pill);
}
.m-chip.active { background: var(--c-orange); color: #fff; }

/* Promotions */
.m-promo-item { background: #fff; border-radius: var(--radius); margin: 10px 12px; overflow: hidden; }
/* Only the placeholder is a fixed slot: it has no picture to take a shape
   from. One with artwork is sized by the picture — see the img rule above. */
.m-promo-item .thumb.art { height: 140px; }
.m-promo-item .body { padding: 14px; }
.m-promo-item h3 { font-size: 17px; font-weight: 600; line-height: 1.3; }
.m-promo-item p { font-size: 13px; color: var(--c-body); line-height: 1.5; margin: 10px 0 16px; }
.m-promo-item .acts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.m-promo-item .acts a { text-align: center; border-radius: var(--radius-sm); padding: 9px; font-size: 14px; color: #fff; }
/* The same pair as everywhere else: blue is the reading, orange is the doing. */
.m-promo-item .acts .info { background: linear-gradient(135deg, var(--m-blue-lit), var(--m-blue-deep)); }
.m-promo-item .acts .join { background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep)); }

/* Auth */
.m-auth { background: #fff; padding: 18px 16px 40px; }
.m-auth-banner { border-radius: var(--radius); overflow: hidden; background: var(--c-soft); }
/* Only the placeholder plate needs a shape of its own; an uploaded picture
   carries its own. */
.m-auth-banner div.thumb { aspect-ratio: 5 / 2; }
.m-auth-banner .cap { text-align: center; font-size: 14px; font-weight: 600; padding: 14px; }
.m-auth h1 { font-size: 20px; font-weight: 500; margin: 24px 0 18px; }
/* --- Form fields ----------------------------------------------------------
   **This build states its own field widths; it does not inherit them.**
   `.form-control` and `.form-select` are Bootstrap's class names, and Bootstrap
   is what used to set `display: block; width: 100%` on them — so every form on
   /m depended on a vendor stylesheet arriving. When it does not, an `<input>`
   falls back to its intrinsic 20-character width and a `<select>` to whatever
   its longest option is, which is how the registration form came to show six
   boxes at four different widths with the red `*` stranded out to the right
   (reported off production, 16 Sep 2026).

   `box-sizing` is restated for the same reason: it is Bootstrap's reboot that
   sets it globally, and without it `width: 100%` plus the padding below
   overflows the very row it is meant to fill. Both declarations are exactly
   what Bootstrap declares, so nothing changes while that file is there. */
.form-control,
.form-select {
  display: block;
  width: 100%;
  box-sizing: border-box;
}

.m-field { position: relative; margin-bottom: 14px; }
.m-field .form-control, .m-field .form-select {
  height: 46px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  font-size: 14px;
  padding-inline: 14px;
}

/* The dial code and the number are one row, and it is stated here rather than
   borrowed from `d-flex`/`gap-2` for the reason above — they are Bootstrap's
   utilities too. The code keeps its own width; the number takes the rest.
   `min-width: 0` is what lets it shrink: a flex item's floor is its content. */
.m-field .d-flex { display: flex; gap: 8px; }
.m-field .d-flex .form-select { flex: 0 0 110px; }
.m-field .d-flex .form-control { flex: 1 1 auto; min-width: 0; }

.m-field .req { position: absolute; top: -6px; right: 2px; color: var(--c-red); font-size: 14px; }
.m-auth .btn-orange { width: 100%; height: 46px; border-radius: var(--radius); font-size: 15px; margin-top: 8px; }
.m-auth .meta { text-align: center; font-size: 13px; color: var(--c-body); margin-top: 16px; line-height: 1.9; }
.m-auth .meta a { color: var(--c-blue-light); text-decoration: underline; }
.m-auth .meta .create { color: var(--c-orange); text-decoration: none; }

/* --- VIP -----------------------------------------------------------------
   The same ground, header and footer as every other mobile page — white cards
   on the light ground, with gold as the only thing that marks it out. A phone
   cannot hold a five-column table, so the comparison is turned on its side:
   the chips pick a tier and each row shows that tier's cell. Every cell is in
   the markup — with the script off the first tier stays visible and the page
   still reads.

   Gold as text on white is too pale to read, so headings and bands use a
   darker draw of the same colour. */
.m-vip { --c-gold-ink: #96702a; }

.m-vip-btn {
  display: inline-block;
  background: linear-gradient(135deg, #d4ac63, #b8893c);
  color: #1a1206;
  font-size: 14px;
  font-weight: 600;
  padding: 9px 26px;
  border-radius: var(--radius-pill);
}
.m-vip-btn:hover, .m-vip-btn:active { color: #1a1206; }
.m-vip-btn.big { padding: 11px 40px; font-size: 15px; }

.m-vip-hero {
  padding: 32px 18px 0;
  text-align: center;
  background:
    radial-gradient(ellipse 420px 260px at 50% 20%, rgba(200, 162, 86, .2), transparent 70%),
    linear-gradient(180deg, #fdfbf6, var(--c-soft));
  border-bottom: 1px solid var(--m-line);
}
/* Full width, and it is the hero's own bottom edge — the padding above stops
   at the button, so the picture is not a card floating in a gap.

   **Not the banner's own shape, unlike /web.** The artwork is a desktop
   banner: its left half is the quiet marble the desktop page writes its
   copy over, and here the copy is already above the picture, so drawing it
   whole would spend half a phone's width on an empty cream field. The box
   is squarer than the picture and anchored right, which crops that field
   away and leaves the couple and the club sign. */
.m-vip-hero-img {
  display: block;
  /* `max-width` has to go with it: theme.css caps every image at 100% of its
     box, which is the padded box, and the point here is to escape it. */
  width: calc(100% + 36px);
  max-width: none;
  margin: 26px -18px 0;
  aspect-ratio: 5 / 3;
  height: auto;
  object-fit: cover;
  object-position: right center;
}
.m-vip-hero .eyebrow {
  display: block;
  font-size: 10.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--c-gold-ink);
  margin-bottom: 10px;
}
.m-vip-hero h1 { font-size: 30px; font-weight: 700; line-height: 1.15; margin: 0 0 12px; color: var(--c-ink); }
.m-vip-hero p { font-size: 13.5px; line-height: 1.7; color: var(--c-body); margin: 0 0 20px; }

/* The tab strip is the page's own navigation, so it stays reachable. */
.m-vip-tabs {
  position: sticky;
  top: var(--m-header-h);
  z-index: 15;
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 10px 12px;
  background: rgba(255, 255, 255, .95);
  border-bottom: 1px solid var(--m-line);
  scrollbar-width: none;
}
.m-vip-tabs::-webkit-scrollbar { display: none; }
.m-vip-tabs a {
  flex: 0 0 auto;
  font-size: 13px;
  color: var(--c-body);
  padding: 7px 16px;
  border-radius: var(--radius-pill);
  background: var(--c-soft);
}

.m-vip-sec { padding: 26px 0 0; scroll-margin-top: calc(var(--m-header-h) + 52px); }
.m-vip-h2 {
  font-size: 17px;
  font-weight: 600;
  color: var(--c-gold-ink);
  text-align: center;
  margin: 0 12px 20px;
}
.m-vip-h2 small {
  display: block;
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--c-muted);
  margin-top: 6px;
}

.m-vip-card {
  margin: 26px 12px 0;
  padding: 20px 18px;
  background: #fff;
  border: 1px solid var(--m-line);
  border-radius: var(--m-radius);
  box-shadow: var(--m-card);
  scroll-margin-top: calc(var(--m-header-h) + 52px);
}
.m-vip-card h2, .m-vip-card h3 { font-size: 16px; font-weight: 600; color: var(--c-gold-ink); margin: 0 0 12px; }
.m-vip-card > p { font-size: 13.5px; line-height: 1.8; color: var(--c-body); margin: 0 0 14px; }
.m-vip-art {
  display: block;
  width: 100%;
  aspect-ratio: 458 / 150;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius-sm);
}
.m-vip-card details { border-bottom: 1px solid var(--m-line); }
.m-vip-card details:last-of-type { border-bottom: 0; }
.m-vip-card summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 0;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--c-ink);
}
.m-vip-card summary::-webkit-details-marker { display: none; }
.m-vip-card summary .chev { margin-left: auto; font-size: 11px; color: var(--c-muted); transition: transform .18s; }
.m-vip-card details[open] summary .chev { transform: rotate(180deg); }
.m-vip-card details p { font-size: 13px; line-height: 1.8; color: var(--c-body); margin: 0 0 14px; }
.m-vip-steps { margin: 0; padding-left: 18px; }
.m-vip-steps li { font-size: 13px; line-height: 1.9; color: var(--c-body); }
.m-vip-fine { font-size: 11.5px; line-height: 1.8; color: var(--c-muted); margin: 14px 12px 0; }
.m-vip-card .m-vip-fine { margin: 12px 0 0; }

/* Medal strip. Sideways rather than shrunk: five medals squeezed into 390px
   are five medals nobody can read. The hairline ring is what stops a pale
   tier — the colours are picked in /admin — dissolving into the ground, and
   the names print in ink for the same reason. */
.m-vip-medals {
  list-style: none;
  margin: 0;
  padding: 4px 12px 6px;
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scrollbar-width: none;
}
.m-vip-medals::-webkit-scrollbar { display: none; }
.m-vip-medals li {
  flex: 0 0 118px;
  display: grid;
  justify-items: center;
  gap: 7px;
  text-align: center;
}
.m-vip-medal {
  width: 76px;
  height: 76px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 26px;
  color: rgba(0, 0, 0, .3);
  background:
    radial-gradient(circle at 32% 28%, rgba(255, 255, 255, .45) 0%, transparent 46%),
    conic-gradient(from 210deg, var(--c), rgba(255, 255, 255, .5), var(--c), rgba(0, 0, 0, .22), var(--c));
  box-shadow:
    0 5px 14px rgba(15, 22, 31, .16),
    inset 0 0 0 4px rgba(255, 255, 255, .35),
    0 0 0 1px rgba(15, 22, 31, .08);
  object-fit: contain;
}
/* Same as the desktop strip: the artwork has its own rim, so the drawn plate
   underneath it has to go, and the shadow follows the cut-out circle. */
img.m-vip-medal {
  background: none;
  box-shadow: none;
  /* A badge, not a disc — see the same rule on /web. */
  border-radius: 0;
  object-fit: contain;
  filter: drop-shadow(0 4px 10px rgba(15, 22, 31, .28));
}
.m-vip-medals b { font-size: 13px; font-weight: 500; color: var(--c-ink); }
.m-vip-medals em { font-style: normal; font-size: 15px; font-weight: 600; color: var(--c-ink); line-height: 1.3; }

/* Tier chips and the one-tier-at-a-time list */
.m-vip-pick {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 0 12px 14px;
  scrollbar-width: none;
}
.m-vip-pick::-webkit-scrollbar { display: none; }
.m-vip-pick button {
  flex: 0 0 auto;
  border: 1px solid var(--m-line);
  background: #fff;
  color: var(--c-body);
  font-size: 13px;
  font-weight: 500;
  padding: 7px 18px;
  border-radius: var(--radius-pill);
}
/* The chosen tier is marked in ink on a gold wash: a tier's own colour is a
   medal colour, and pale ones would leave the chip looking unpicked. */
.m-vip-pick button.on {
  border-color: var(--c-gold);
  color: var(--c-gold-ink);
  background: rgba(200, 162, 86, .14);
}

.m-vip-list { margin: 0 12px; }
.m-vip-list .band {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--c-gold-ink);
  padding: 22px 0 8px;
}
.m-vip-list .line {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--m-line);
}
.m-vip-list .line.sub dt { padding-left: 14px; }
.m-vip-list .line.sub dt::before { content: "–"; margin-left: -12px; padding-right: 5px; color: var(--c-faint); }
.m-vip-list dt { flex: 1 1 auto; font-size: 13px; color: var(--c-body); }
.m-vip-list dt small { display: block; font-size: 11px; color: var(--c-muted); margin-top: 1px; }
.m-vip-list dd { flex: 0 0 auto; margin: 0; font-size: 14px; font-weight: 600; color: var(--c-ink); text-align: right; }
/* One column visible at a time; the chips move the `on`. */
.m-vip-list .cell { display: none; }
.m-vip-list .cell.on { display: inline; }
.m-vip-list .cell .bi-check-lg { color: var(--c-green); font-size: 17px; }
.m-vip-list .cell .off { font-style: normal; color: var(--c-faint); }

.m-vip-cta {
  margin-top: 34px;
  padding: 38px 18px;
  text-align: center;
  background:
    radial-gradient(ellipse 420px 200px at 50% 100%, rgba(200, 162, 86, .22), transparent 70%),
    var(--c-soft);
  border-top: 1px solid var(--m-line);
}
.m-vip-cta h2 { font-size: 20px; font-weight: 600; margin: 0 0 10px; color: var(--c-ink); }
.m-vip-cta p { font-size: 13.5px; color: var(--c-body); margin: 0 0 20px; }

/* --- Member area ---------------------------------------------------------
   No site header and no drawer here — a back bar on top, a fixed bottom nav,
   and the menu itself rendered as tile grids. */
.m-acc-body { background: var(--m-ground); padding-top: 0; padding-bottom: 78px; }

.m-acc-bar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--m-ground);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
}
.m-acc-bar .back { font-size: 19px; color: var(--m-blue); line-height: 1; }
.m-acc-bar .ttl { font-size: 19px; font-weight: 600; letter-spacing: -.01em; }
.m-acc-bar .dots { margin-left: auto; border: 0; background: none; color: var(--c-faint); font-size: 16px; }

.m-acc-main { padding-bottom: 20px; }

/* Fixed bottom nav — the selected entry gets a tinted plate behind it rather
   than a colour change, which is what the supplied screens do. */
.m-bottom {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 640px;
  background: #fff;
  border-top: 1px solid var(--m-line);
  display: grid;
  /* Five since 優惠活動 joined the bar. **minmax(0, 1fr) and not 1fr**: a bare
     1fr floors at the content width, so the longest label — Instant
     Rebate — would push the track wider than a fifth and shove the row
     off a 390px screen. */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  padding: 7px 4px calc(9px + env(safe-area-inset-bottom));
  z-index: 45;
}
.m-bottom a {
  text-align: center;
  /* A fifth of 390px is 78px. The label wraps to two lines at this size
     rather than being cut, and the bar is one height for all five
     because every anchor is a grid item of the same row. */
  font-size: 9.5px;
  color: var(--m-blue);
  line-height: 1.3;
  border-radius: 12px;
  padding: 5px 0 4px;
}
.m-bottom a i { display: block; font-size: 21px; margin-bottom: 3px; }
.m-bottom a .ic-svg { width: 22px; height: 22px; margin: 0 auto 3px; }
.m-bottom a.active { background: var(--m-tint-blue); color: var(--m-blue-deep); font-weight: 600; }

/* --- Cards ----------------------------------------------------------------
   Every block in the member area is a white card floating on the ground, so
   the radius, the margin and the shadow are set once here. */
.m-acc-user,
.m-acc-verify,
.m-acc-tile,
.m-acc-card,
.m-signout,
.m-wallet,
.m-rb-card,
.m-rb-note {
  background: #fff;
  border-radius: var(--m-radius);
  box-shadow: var(--m-card);
  margin: 12px;
}

/* User row */
.m-acc-user {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  margin-top: 0;
}
.m-acc-user .av {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: linear-gradient(150deg, #f9c465, #e79a2b);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 24px;
  flex: 0 0 46px;
}
.m-acc-user .who { min-width: 0; }
.m-acc-user .nm {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 17px;
  font-weight: 600;
  color: var(--c-ink);
}
.m-acc-user .nm i { font-size: 14px; color: #2f80f0; }
.m-acc-user .vip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 5px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--m-tint-blue);
  color: #3663c4;
  font-size: 11px;
}
.m-acc-user .tier,
.m-acc-user .pts {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  padding: 5px 11px;
  color: #fff;
  flex: 0 0 auto;
}
.m-acc-user .tier { margin-left: auto; background: #4a5566; }
.m-acc-user .pts { background: linear-gradient(to right, #e0a92a, #f5cf5c); color: #4a3403; }

/* --- Wallet card ---------------------------------------------------------
   Measured off the supplied screen (sample/photo_2026-08-25_14-23-22.jpg) and
   written back as fractions of the card's width, so the whole thing lands at
   the same proportions here as it has there.

   The plate is the top sheet of a small stack: a grey sheet peeking out low on
   the left, a blue one down the right, the near-white plate over both. Inside
   the plate a double hairline runs top-right to bottom-left with a lighter
   panel behind the Deposit pill, a fine dot triangle sits beside the seam, and
   88M is stamped faintly along the bottom.

   **It paints in `--m-blue`, the blue the category chips carry.** The card had
   a darker green of its own until 3 Sep 2026, when the operator asked for that
   blue instead — figure, pill and sheet together, which left nothing green on
   it worth keeping the `--m-money-green` tokens for. */
.m-wallet { overflow: hidden; padding-bottom: 0; }

.m-wallet-head {
  display: flex;
  align-items: center;
  padding: 11px 15px 9px 16px;
}
.m-wallet .lbl {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  line-height: 1;
  font-weight: 600;
  color: var(--c-ink);
}
.m-wallet .eye {
  border: 0;
  background: none;
  padding: 0;
  line-height: 1;
  font-size: 13px;
  color: var(--c-muted);
}
.m-wallet .refresh {
  margin-left: auto;
  border: 0;
  background: none;
  padding: 0;
  line-height: 1;
  font-size: 15px;
  color: var(--m-blue);
}

/* The stack. It only positions — the two sheets are its pseudo-elements and
   the plate is the child, so the sheets can sit outside the plate's rounding
   without giving up the clipping the plate's own decoration needs. */
.m-wallet-stack {
  position: relative;
  margin: 0 13px 8px;
}
/* Grey sheet, low and to the left */
.m-wallet-stack::before {
  content: '';
  position: absolute;
  left: -7px;
  right: 24px;
  top: 12px;
  bottom: -2px;
  border-radius: 12px;
  background: #ededed;
  box-shadow: 0 2px 6px rgba(23, 43, 77, .10);
}
/* Blue sheet, down the right */
.m-wallet-stack::after {
  content: '';
  position: absolute;
  right: -5px;
  top: 3px;
  bottom: 2px;
  width: 44px;
  border-radius: 12px;
  background: linear-gradient(180deg,
    var(--m-blue-deep) 0%, var(--m-blue-lit) 38%, #608ed7 72%, #d7e2f4 100%);
  box-shadow: 0 2px 6px rgba(11, 60, 150, .18);
}

/* The plate: figure on the left, the one action on the right. */
.m-wallet-top {
  position: relative;
  z-index: 1;
  overflow: hidden;
  padding: 13px 12px 13px 13px;
  border-radius: 12px;
  background:
    /* 88M, stamped low and very faint, the way a watermark sits under print */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='132' height='48'%3E%3Ctext x='0' y='40' font-family='Poppins,Arial,sans-serif' font-size='46' font-weight='800' fill='%23172b4d' fill-opacity='0.022'%3E88M%3C/text%3E%3C/svg%3E")
      30% 100% / 66px 24px no-repeat,
    linear-gradient(180deg, #f8f9f9, #f1f2f2);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .9),
    0 3px 8px rgba(23, 43, 77, .12);
  display: flex;
  align-items: center;
  gap: 10px;
}
/* One angled gradient carries both hairlines and the lighter panel beyond
   them. 120deg puts the seam 30° off vertical leaning left, and the stops are
   fractions of the gradient axis, so the angle holds at any card width. */
.m-wallet-top::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(120deg,
    transparent 0 58.4%,
    rgba(23, 43, 77, .07) 58.4% 58.6%,
    transparent 58.6% 59.7%,
    rgba(23, 43, 77, .07) 59.7% 59.9%,
    rgba(255, 255, 255, .5) 59.9% 100%);
}
/* The dot field: a fine tile clipped to the triangle the artwork has. */
.m-wallet-top::after {
  content: '';
  position: absolute;
  left: 51%;
  top: 6%;
  width: 26px;
  height: 54px;
  pointer-events: none;
  background: radial-gradient(circle, rgba(23, 43, 77, .14) .7px, transparent .9px) 0 0 / 5.8px 5.8px;
  clip-path: polygon(0 0, 100% 0, 40% 100%);
}
.m-wallet-top .main { position: relative; flex: 1; min-width: 0; }

.m-wallet .bal { display: flex; align-items: baseline; gap: 9px; }
.m-wallet .bal .cur { font-size: 10px; color: var(--c-muted); }
.m-wallet .bal b {
  font-size: 28px;
  font-weight: 700;
  color: var(--m-blue);
  letter-spacing: -.02em;
  line-height: 1.15;
}
.m-wallet .sub {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 2px;
  font-size: 10px;
  line-height: 1.35;
  color: var(--c-muted);
}
/* 禮碼錢包 — the card under the wallet. Same white card, same gutter and
   shadow as everything on the ground; the coin in the logo's gold on a warm
   plate so it reads as a different kind of money from the blue figure above
   it, and the figure in ink rather than blue because it cannot be taken out.
   Pulled up under the wallet so the two read as one pair.

   **Not `.m-chip`** — that is the category pill in `.m-chip-row`, and this
   block, being the later of the two, was quietly repainting every filter
   row on the product pages as a white shadowed card with this card's own
   padding and negative margins. */
.m-chip-wallet {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: -4px 12px 12px;
  padding: 12px 14px 12px 12px;
  background: #fff;
  border-radius: var(--m-radius);
  box-shadow: var(--m-card);
}
.m-chip-wallet .ic {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  background: linear-gradient(160deg, #fbf3dc, #f3e2b1);
  color: #b48a2c;
  font-size: 19px;
}
.m-chip-wallet .main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.m-chip-wallet .lbl { font-size: 12px; font-weight: 600; line-height: 1; color: var(--c-ink); }
.m-chip-wallet .bal { display: flex; align-items: baseline; gap: 6px; }
.m-chip-wallet .bal .cur { font-size: 10px; color: var(--c-muted); }
.m-chip-wallet .bal b { font-size: 20px; font-weight: 700; color: var(--c-ink); letter-spacing: -.02em; line-height: 1.15; }
.m-chip-wallet .sub { font-size: 10px; line-height: 1.35; color: var(--c-muted); }
.m-chip-wallet .info {
  flex: 0 0 auto;
  border: 0;
  background: none;
  padding: 4px;
  line-height: 1;
  font-size: 16px;
  color: var(--c-muted);
}
/* The popup's own title — the invitation dialog has none on show. */
.rf-title { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0 0 10px; font-size: 16px; font-weight: 700; color: var(--c-ink); }
.rf-title .bi { color: #b48a2c; }

/* The pill sits in a white socket: that is the ring in the first shadow, which
   is why the plate's right padding has to stay clear of it. */
.m-wallet .dep {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 17px 0 11px;
  border-radius: var(--radius-pill);
  background: linear-gradient(155deg,
    var(--m-blue-lit) 0%, var(--m-blue) 52%, var(--m-blue-deep) 100%);
  box-shadow:
    0 0 0 6px #fff,
    0 5px 11px rgba(8, 70, 180, .26),
    inset 0 1px 0 rgba(255, 255, 255, .24);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
}
.m-wallet .dep .ic-svg { width: 16px; height: 16px; }
/* Not `.mark` — Bootstrap owns that class and paints it a highlighter yellow. */
.m-wallet .dep .dep-shield { width: 21px; height: 21px; }

/* Collapsible shortcut row. Both it and the disclosure sit closer than the
   raw numbers suggest — the stack already carries 8px under the plate. */
.m-wallet-apps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  padding: 10px 12px 4px;
}
.m-wallet-apps[hidden] { display: none; }
.m-wallet-apps a { text-align: center; font-size: 12px; color: var(--c-ink); }
.m-wallet-apps .ic { position: relative; display: inline-block; font-size: 26px; color: var(--m-blue); line-height: 1; }
.m-wallet-apps .badge-n {
  position: absolute;
  top: -4px;
  right: -12px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--c-red-badge);
  color: #fff;
  font-size: 10px;
  font-style: normal;
  line-height: 17px;
}
.m-wallet-apps .lb { display: block; margin-top: 8px; }

/* Hairline above it rather than a coloured bar — it is a disclosure, not an
   action. */
.m-wallet-more {
  width: 100%;
  border: 0;
  border-top: 1px solid var(--m-line);
  background: none;
  color: var(--m-blue);
  font-size: 13px;
  margin-top: 6px;
  padding: 13px 8px;
}
.m-wallet-more i { transition: transform .2s ease; display: inline-block; }
.m-wallet-more[aria-expanded="true"] i { transform: rotate(180deg); }

.m-acc-verify {
  padding: 16px;
  display: flex;
  align-items: center;
}
.m-acc-verify .t { font-size: 14px; font-weight: 500; }
.m-acc-verify .icons { margin-left: auto; display: flex; gap: 12px; }
/* Buttons where there is still something to ask, a plain span where there is
   not: the bank mark stops being pressable once an account is on file. Both
   have to look the same, so both are styled here. */
.m-acc-verify .icons > button,
.m-acc-verify .icons > span {
  position: relative;
  width: 36px;
  height: 36px;
  border-radius: 9px;
  border: 0;
  padding: 0;
  background: var(--m-tint-blue);
  display: grid;
  place-items: center;
  font-size: 16px;
  color: var(--m-blue);
  cursor: pointer;
}
.m-acc-verify .icons > button:active { background: var(--c-line); }
.m-acc-verify .icons .done::after {
  content: '\f26e';
  font-family: 'bootstrap-icons';
  position: absolute;
  right: -4px;
  bottom: -3px;
  font-size: 11px;
  color: var(--c-green);
  background: #fff;
  border-radius: 50%;
  line-height: 1;
}

/* --- Tile grid -----------------------------------------------------------
   Each tile is its own card now: a tinted glyph, the name with a chevron, and
   one line saying what the page is for. `minmax(0, …)` because the name is a
   translated string and the description wraps under it — bare `1fr` columns
   would be pushed apart by the longest word in whichever language is on. */
.m-acc-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  padding: 0 12px;
  margin-top: 12px;
}
.m-acc-tile {
  margin: 0;
  padding: 14px 12px 16px;
  color: var(--c-ink);
  min-width: 0;
}
.m-acc-tile .ic {
  position: relative;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  font-size: 19px;
  line-height: 1;
  margin-bottom: 12px;
}
/* The client's member marks are drawn at their own colours, so the plate is
   all the tint does here; a Bootstrap glyph in one of these still takes the
   `color`. */
.m-acc-tile .ic .ic-svg { width: 24px; height: 24px; }
.m-acc-tile .ic.tint-blue   { background: var(--m-tint-blue);   color: #2f6fe0; }
.m-acc-tile .ic.tint-indigo { background: var(--m-tint-indigo); color: #5a52d6; }
.m-acc-tile .ic.tint-green  { background: var(--m-tint-green);  color: #1f9d55; }
.m-acc-tile .ic.tint-purple { background: var(--m-tint-purple); color: #7a45d1; }
.m-acc-tile .ic.tint-amber  { background: var(--m-tint-amber);  color: #cc8a12; }
.m-acc-tile .ic .dot {
  position: absolute;
  top: -3px;
  right: -3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--c-red-badge);
  border: 2px solid #fff;
}
.m-acc-tile .hd {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.25;
}
.m-acc-tile .hd .lb { min-width: 0; }
.m-acc-tile .hd i { margin-left: auto; font-size: 11px; color: var(--c-faint); }
.m-acc-tile .ds {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--c-muted);
}

/* Content blocks reused by the inner member pages */
.m-acc-card { padding: 16px 14px 20px; }
.m-acc-card h3 { font-size: 15px; font-weight: 600; margin-bottom: 14px; }
.m-acc-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 12px; }
.m-acc-tabs a {
  text-align: center;
  padding: 11px;
  border-radius: var(--radius-sm);
  background: #b9bfc6;
  color: #fff;
  font-size: 14px;
}
.m-acc-tabs a.active { background: linear-gradient(to right, #095ebd, #04a9c2); }
.m-acc-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.m-acc-row .k { font-size: 13px; color: var(--c-muted); flex: 0 0 42%; }
.m-acc-row .v { font-size: 13px; }
.m-acc-row .edit { margin-left: auto; color: var(--c-body); font-size: 14px; }
/* Sits where the pencil would be on a field that can no longer be changed. */
.m-acc-row .acc-locked { margin-left: auto; color: var(--c-faint); font-size: 12px; }
.m-acc-sub { font-size: 15px; font-weight: 600; margin: 6px 0 4px; }
.m-bank {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--c-soft);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.m-bank b { font-size: 13px; font-weight: 500; }
.m-bank span { display: block; font-size: 12px; color: var(--c-muted); }
/* The bin on a saved withdrawal account. The form is only there to carry the
   row's id, so it takes no space of its own — the button is the control. */
.m-bank-del { margin-left: auto; line-height: 0; }
.m-bank .row-del {
  border: 0;
  background: none;
  color: var(--c-faint);
  font-size: 15px;
  padding: 6px;
}
.m-bank .row-del:hover, .m-bank .row-del:active { color: var(--c-red); }

.m-opt-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.m-opt {
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  background: var(--c-soft);
  padding: 14px 6px 10px;
  text-align: center;
  font-size: 12px;
  color: var(--c-body);
  position: relative;
}
.m-opt i { display: block; font-size: 22px; color: var(--c-blue-mid); margin-bottom: 6px; }
.m-opt.active { border-color: var(--c-orange); border-width: 2px; background: #fff; }

.m-stat-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.m-stat { background: var(--c-soft); border-radius: var(--radius); padding: 12px; }
.m-stat .t { font-size: 12px; color: var(--c-body); display: flex; align-items: center; gap: 6px; }
.m-stat .n { font-size: 18px; color: var(--c-blue-light); margin-top: 6px; }
.m-stat .n span { font-size: 11px; color: var(--c-muted); }

.m-acc-field { margin-bottom: 14px; }
.m-acc-field label { display: block; font-size: 13px; color: var(--c-muted); margin-bottom: 6px; }
.m-acc-field label i { color: var(--c-red); font-style: normal; }
.m-acc-field .form-control, .m-acc-field .form-select {
  height: 44px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  font-size: 14px;
}
/* 交易记录's From / To pair. `d-grid`/`gap-2` are Bootstrap utilities, and this
   is a form, so the row is stated here for the same reason the field widths
   are — see the note above .m-field. Scoped to the card rather than declared as
   a global utility: this build reclaims what its own forms need, not Bootstrap. */
.m-acc-card .d-grid { display: grid; gap: 8px; }
/* Size only — the fill and the shape come from the button family. */
.m-acc-submit { width: 100%; height: 46px; font-size: 15px; }

/* Scrollable tables on the narrow canvas.
   `.m-table-scroll` carries the whole table's styling on /m — .acc-table has no
   rules of its own here — so a table that fits keeps the wrapper and drops the
   floor instead: `.fits` is for the three-column tables (Instant Rebate), while
   520px is the width the four- and five-column ones need. */
.m-table-scroll { overflow-x: auto; }
.m-table-scroll .acc-table { min-width: 520px; }
.m-table-scroll.fits .acc-table { min-width: 0; }
.m-table-scroll .acc-table thead th,
.m-table-scroll .acc-table td { font-size: 12px; padding: 10px 8px; white-space: nowrap; }

.m-msg-tabs { display: flex; gap: 6px; overflow-x: auto; padding: 12px; scrollbar-width: none; }
.m-msg-tabs::-webkit-scrollbar { display: none; }
.m-msg-tabs a {
  flex: 0 0 auto;
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  background: #b9bfc6;
  color: #fff;
  font-size: 13px;
  white-space: nowrap;
}
.m-msg-tabs a.active { background: linear-gradient(to right, #095ebd, #04a9c2); }
.m-msg-item { background: #fff; padding: 14px; border-bottom: 1px solid var(--c-line); display: flex; gap: 12px; }
.m-msg-item .s { font-size: 13px; line-height: 1.4; }
.m-msg-item.unread .s { font-weight: 600; }
.m-msg-item.read .s { color: var(--c-faint); }
.m-msg-item .d { font-size: 11px; color: var(--c-faint); margin-top: 6px; display: block; }
.m-msg-item .acts { margin-left: auto; display: flex; gap: 12px; color: var(--c-faint); font-size: 14px; }

/* Floating tweaks for the narrow canvas. On a page with the bottom bar the
   bubble has to clear it — parked at 18px it sits on top of the last tab. */
.float-chat { width: 46px; height: 46px; font-size: 18px; }
.has-bottom-nav .float-chat,
.m-acc-body .float-chat { bottom: calc(88px + env(safe-area-inset-bottom)); }

.m-signout {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 16px;
  font-size: 14px;
  color: var(--c-body);
}

/* --- Instant rebate -------------------------------------------------------
   One claim card carrying the figure and the button, a note under it, then the
   records — with an empty state rather than a bare line of text. */
.m-rb-card { padding: 18px 18px 0; }
.m-rb-top { display: flex; align-items: flex-start; gap: 10px; }
.m-rb-top .main { flex: 1; min-width: 0; }

.m-rb-lbl { display: flex; align-items: center; gap: 9px; font-size: 15px; font-weight: 600; }
.m-rb-lbl .ic {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 10px;
  background: var(--m-tint-blue);
  color: var(--m-blue);
  display: grid;
  place-items: center;
}
.m-rb-lbl .ic .ic-svg { width: 19px; height: 19px; }
.m-rb-lbl > i { font-size: 13px; color: var(--c-faint); }

.m-rb-amt { display: flex; align-items: baseline; gap: 8px; margin: 16px 0 14px; }
.m-rb-amt .cur { font-size: 15px; color: var(--c-muted); }
.m-rb-amt b {
  font-size: 40px;
  font-weight: 700;
  color: var(--m-blue);
  letter-spacing: -.02em;
  line-height: 1;
}

.m-rb-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: #f1f4f9;
  color: var(--c-body);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-size: 12px;
}
.m-rb-chip .ic-svg { width: 14px; height: 14px; color: var(--c-muted); }

.m-rb-art { flex: 0 0 92px; display: block; }
.m-rb-art .ic-svg { width: 92px; height: 92px; }

.m-rb-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  border-top: 1px solid var(--m-line);
  margin-top: 18px;
  padding: 15px 0 18px;
}
.m-rb-foot p {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 12px;
  color: var(--c-body);
  line-height: 1.45;
}
.m-rb-foot .ic-svg { width: 16px; height: 16px; color: var(--m-blue); }
.m-rb-claim {
  border: 0;
  border-radius: 10px;
  padding: 11px 20px;
  font-size: 13px;
  font-weight: 600;
  background: linear-gradient(135deg, var(--m-blue-lit), var(--m-blue-deep));
  color: #fff;
  box-shadow: 0 6px 14px rgba(10, 78, 200, .26);
  white-space: nowrap;
}
/* Nothing to claim is a state, not a failure — grey, and it says so. */
.m-rb-claim[disabled] { background: #eceff4; color: var(--c-faint); box-shadow: none; }

.m-rb-note {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: #f1f6fe;
  box-shadow: none;
}
.m-rb-note .ic {
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  border-radius: 9px;
  background: #fff;
  color: #3b7bf0;
  display: grid;
  place-items: center;
}
.m-rb-note .ic .ic-svg { width: 18px; height: 18px; }
.m-rb-note .tx { flex: 1; min-width: 0; }
.m-rb-note .tx b { display: block; font-size: 13px; font-weight: 600; }
.m-rb-note .tx em { display: block; font-style: normal; margin-top: 3px; font-size: 12px; color: var(--c-muted); line-height: 1.45; }
.m-rb-note > i { color: var(--c-faint); font-size: 12px; }

.m-rb-head {
  position: relative;
  margin: 24px 12px 0;
  padding-bottom: 10px;
  font-size: 17px;
  font-weight: 600;
}
.m-rb-head::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 2px;
  width: 28px;
  height: 3px;
  border-radius: 3px;
  background: var(--m-blue);
}

.m-rb-empty { text-align: center; padding: 24px 10px 28px; }
.m-rb-empty .pic { display: block; }
.m-rb-empty .pic .ic-svg { width: 96px; height: 96px; margin: 0 auto 14px; }
.m-rb-empty b { display: block; font-size: 15px; font-weight: 600; }
.m-rb-empty p { margin: 6px 0 18px; font-size: 12.5px; color: var(--c-muted); }
.m-rb-empty .go {
  display: inline-flex;
  align-items: center;
  height: 42px;
  padding: 0 30px;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--m-blue-lit), var(--m-blue-deep));
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  box-shadow: 0 6px 14px rgba(10, 78, 200, .26);
}
/* --- Verification popups --------------------------------------------------
   Native <dialog>: the backdrop, Esc and focus trapping are the browser's job,
   so this file only has to dress the box. */
.vf {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  width: min(420px, calc(100vw - 32px));
  box-shadow: 0 24px 60px rgba(9, 30, 66, .25);
}
.vf::backdrop { background: rgba(6, 20, 44, .55); }
.vf-box { padding: 26px 24px 22px; position: relative; }
.vf-box h3 { font-size: 18px; font-weight: 600; margin: 0 0 6px; padding-right: 24px; }
.vf-lead { font-size: 13px; color: var(--c-muted); line-height: 1.6; margin: 0 0 18px; }

.vf-x {
  position: absolute;
  top: 16px;
  right: 16px;
  border: 0;
  background: none;
  font-size: 15px;
  color: var(--c-muted);
  cursor: pointer;
  line-height: 1;
}
.vf-x:hover { color: var(--c-ink); }

.vf-lbl { display: block; font-size: 13px; color: var(--c-body); margin: 0 0 6px; }
.vf-box input, .vf-box select {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  background: #fff;
  font-size: 14px;
  margin-bottom: 16px;
}
.vf-box input[readonly] { background: var(--c-soft); color: var(--c-muted); }
.vf-row { display: flex; gap: 8px; align-items: start; }
.vf-row input, .vf-row select { flex: 1; margin-bottom: 0; }
.vf-row { margin-bottom: 16px; }
.vf-send {
  flex: 0 0 auto;
  padding: 9px 20px;
  border: 0;
  border-radius: var(--radius);
  background: var(--c-blue-mid);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.vf-help { font-size: 12px; color: var(--c-muted); margin: -6px 0 16px; line-height: 1.6; }
.vf-help a { color: var(--c-orange); }

/* Says why the buttons do nothing. Without it a dead Submit reads as a bug. */
.vf-locked {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: 12px;
  line-height: 1.6;
  color: #8a6d1f;
  background: #fdf6e3;
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 18px;
}
.vf-locked i { margin-top: 2px; }

.vf-acts { display: flex; gap: 10px; }
.vf-acts .btn { flex: 1; justify-content: center; padding: 10px; font-size: 14px; }
.vf-acts .btn:disabled, .vf-send:disabled { opacity: .5; cursor: not-allowed; }
/* Date-of-birth popup ------------------------------------------------------ */
.vf-dob { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 10px; }
.vf-dob select { margin-bottom: 18px; }
.vf-err {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: 13px;
  line-height: 1.6;
  color: var(--c-red);
  background: #fdecec;
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 16px;
}

/* What finishing the bank popup is worth, said inside the popup. The same
   shape as .vf-err and .vf-locked beside it, in the colour of a reward rather
   than a warning. */
.vf-prize {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: 12.5px;
  line-height: 1.6;
  color: #1b7a42;
  background: #e8f6ed;
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 18px;
}
.vf-prize i { margin-top: 2px; }

/* --- 實名認證彩金 popup ------------------------------------------------------
   The free 18: offered, then paid. The same <dialog> furniture as the
   verification popups above, arranged differently because this one is an offer
   rather than a form — centred, the figure first, and it opens itself. */
.vb {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  width: min(360px, calc(100vw - 32px));
  box-shadow: 0 24px 60px rgba(9, 30, 66, .25);
}
.vb::backdrop { background: rgba(6, 20, 44, .6); }
.vb-box { position: relative; padding: 30px 24px 22px; text-align: center; }
.vb-box h3 { font-size: 17px; font-weight: 600; margin: 0 0 8px; padding: 0 14px; }
.vb-lead { font-size: 13px; color: var(--c-muted); line-height: 1.65; margin: 0 0 20px; }

.vb-x {
  position: absolute;
  top: 14px;
  right: 14px;
  border: 0;
  background: none;
  font-size: 14px;
  color: var(--c-muted);
  cursor: pointer;
  line-height: 1;
}
.vb-x:hover { color: var(--c-ink); }

/* The mark, and the figure under it. A bonus is a number before it is a
   sentence, so the number is the biggest thing in the box. */
.vb-mark {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  margin: 0 auto 14px;
  font-size: 24px;
  color: #fff;
}
.vb-mark.bad { background: var(--c-red); }

.vb-amt {
  font-size: 30px;
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 10px;
}

.vb-acts { display: flex; gap: 10px; margin: 0; }
.vb-acts .btn { flex: 1; justify-content: center; padding: 11px; font-size: 14px; }
.vb-mark { background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep)); }
.vb-amt {
  background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Bootstrap's `.btn:disabled` rule outranks `.btn-orange`, exactly as it does
   in .vf-acts — restate the fill so the button always reads as one. */
.vb-acts .btn-orange {
  background: linear-gradient(135deg, var(--m-logo-orange-lit), var(--m-logo-orange-deep));
  color: #fff;
}
/* The pencil is a <button> on a field that opens a popup; it has to shed the
   browser's button chrome to keep matching the <a> pencils beside it. */
.m-acc-row button.edit { border: 0; padding: 0; cursor: pointer; }

/* Confirmation above the profile card after a save. */
.acc-note {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  border-radius: var(--radius);
  padding: 11px 15px;
  /* Same gutter the cards below it sit in, so a save confirmation lines up
     with the card it is confirming. */
  margin: 12px 12px 0;
}
.acc-note.ok  { background: #e8f6ed; color: #1b7a42; }
.acc-note.err { background: #fdecec; color: #b3243d; }

/* Deposit: the steps that only appear once something is chosen ------------- */
.dep-hint {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--c-muted);
  background: var(--c-soft);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 16px 0 0;
}
.dep-hint i { font-size: 18px; }
.dep-amount-hint { font-size: 12px; color: var(--c-red); margin: 6px 0 0; }
/* IDR: the box is typed in thousands — "1 = 1,000 IDR" beside the label. */
.dep-unit { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; font-style: normal; color: var(--c-orange, #f5a623); border: 1px solid currentColor; vertical-align: middle; }
/* The tiles are labels now, so the radio inside must not take up space. */
.opt-card { cursor: pointer; }
.opt-card .bank-logo, .m-opt .bank-logo { width: 48px; height: 48px; object-fit: contain; }
.dep-sender { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.dep-add {
  width: 44px;
  height: 44px;
  border: 1px dashed var(--c-line);
  border-radius: var(--radius);
  display: grid;
  place-items: center;
  font-size: 20px;
  color: var(--c-muted);
}
.dep-add:hover { border-color: var(--c-blue-mid); color: var(--c-blue-mid); }
/* The sender tiles are .m-opt here, not .opt-card, so they need the same
   three-across sizing the picks grid gives its own. */
.dep-sender .m-opt { flex: 1 1 28%; }
.m-opt .wd-acct { display: block; font-size: 11px; color: var(--c-faint); margin-top: 2px; }
/* Without scripting the whole form is shown rather than a dead end. */

/* Deposit amount shortcuts ------------------------------------------------
   These had no mobile rules at all and were falling back to the browser's own
   button chrome. A row that scrolls sideways rather than wrapping: five chips
   never fit across a phone, and a wrapped second row pushes the form about. */
.chip-row {
  display: flex;
  gap: 8px;
  margin: 10px 0 4px;
  overflow-x: auto;
  scrollbar-width: none;
  /* The row is edge-to-edge inside a padded card, so the last chip can be
     scrolled fully clear of the edge instead of sitting under it. */
  scroll-padding-inline: 4px;
}
.chip-row::-webkit-scrollbar { display: none; }
.amt-chip {
  flex: 0 0 auto;
  min-width: 72px;
  height: 40px;
  padding: 0 14px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  background: #fff;
  color: var(--c-body);
  font-size: 14px;
  font-weight: 500;
}
.amt-chip:active { background: var(--c-soft); border-color: var(--c-blue-mid); color: var(--c-blue-mid); }
/* Deposit Bank Account — the account a Bank Transfer is paid into ---------- */
.pay-to { display: grid; gap: 10px; margin-bottom: 4px; }
.pay-field { position: relative; }
.pay-field .form-control { padding-right: 44px; background: var(--c-soft); }
.pay-copy {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  color: var(--c-blue-mid);
  font-size: 16px;
  cursor: pointer;
}
.pay-copy:hover { color: var(--c-blue-light); }
/* A tick for a moment, so a silent clipboard write is visibly a write. */
.pay-copy.copied { color: var(--c-green); }
.pay-copy.copied i::before { content: '\f26e'; }

/* --- Confirm dialog -------------------------------------------------------
   Replaces the browser's confirm(): that one cannot be styled, announces the
   origin, and reads like a security warning rather than a question about the
   thing the reader just clicked. */
.cf {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  width: min(380px, calc(100vw - 32px));
  box-shadow: 0 24px 60px rgba(9, 30, 66, .28);
}
.cf::backdrop { background: rgba(6, 20, 44, .55); }
.cf-box { padding: 28px 26px 22px; text-align: center; }
.cf-icon {
  width: 54px;
  height: 54px;
  margin: 0 auto 16px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 26px;
  background: #fff4e5;
  color: var(--c-orange);
}
.cf-box h3 { font-size: 17px; font-weight: 600; margin: 0 0 8px; }
.cf-box p { font-size: 14px; color: var(--c-muted); line-height: 1.6; margin: 0 0 22px; }
.cf-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* Size only — the two fills and the ghost are up with the button family. */
.cf-btn { padding: 11px 10px; font-size: 14px; cursor: pointer; }

/* Withdraw: a saved account tile carries its number under the bank name. */
.opt-card .wd-acct { display: block; font-size: 11px; color: var(--c-faint); margin-top: 2px; }
/* The two filled-in fields are shown, not typed into. */
.acc-filter input[readonly] { background: var(--c-soft); color: var(--c-muted); }
/* The saved-account table's logo, at row height. */
.bank-logo.sm { width: 26px; height: 26px; object-fit: contain; vertical-align: middle; margin-right: 8px; }

/* Receipt upload — optional, and the chosen filename is echoed by hand
   because the real input is hidden inside a label. */
.upload-note .opt, .text-muted-2 .opt { color: var(--c-faint); }
.dep-file { font-size: 12px; color: var(--c-muted); word-break: break-all; }

/* Referral invitation popup ------------------------------------------------
   Opened by the code chip on the referral card. Wide and quiet: the whole
   point is a block of text somebody is about to copy into a chat. */
.rf {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  width: min(620px, calc(100vw - 60px));
  box-shadow: 0 24px 60px rgba(9, 30, 66, .25);
  /* The close button hangs off the corner, and a dialog scrolls by default —
     without this it earns a scrollbar instead of letting the button out. */
  overflow: visible;
}
.rf::backdrop { background: rgba(6, 20, 44, .55); }
.rf-box { padding: 40px 38px 30px; position: relative; text-align: center; }
.rf-x {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: #fff;
  color: var(--c-muted);
  box-shadow: 0 4px 14px rgba(9, 30, 66, .18);
}
.rf-msg { font-size: 16px; color: var(--c-ink); line-height: 1.7; margin: 0 0 22px; }
/* The link wraps rather than stretching the dialog — a referral code is short
   but the host in front of it does not have to be. */
.rf-link {
  display: block;
  font-size: 15px;
  color: var(--c-blue-light);
  word-break: break-all;
  margin-bottom: 26px;
}
.rf-acts { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }
.rf-copy, .rf-ok {
  border: 0;
  border-radius: var(--radius-sm);
  height: 44px;
  padding: 0 34px;
  font-size: 15px;
}
.rf-copy { background: var(--c-soft); color: var(--c-body); }
.rf-copy.copied { background: #e8f6ed; color: #1b7a42; }
.rf-ok { background: var(--c-blue-mid); color: #fff; }

/* The code chip is a button now, so it has to give back what a <button>
   takes away — it was an <a> when the card only linked somewhere. */
.acc-referral .code, .m-referral .code { width: 100%; border: 0; text-align: left; font: inherit; cursor: pointer; }

/* Message reader ------------------------------------------------------------
   Wide and plain: the body is written by an operator and can be long, so the
   dialog gets out of its way and scrolls rather than clipping. */
.msgv {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  width: min(760px, calc(100vw - 32px));
  max-height: calc(100vh - 60px);
  box-shadow: 0 24px 60px rgba(9, 30, 66, .25);
  overflow: visible;
}
.msgv::backdrop { background: rgba(6, 20, 44, .55); }
.msgv-box {
  position: relative;
  padding: 28px 32px 30px;
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  border-radius: var(--radius-lg);
}
.msgv-x {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #fff;
  color: var(--c-muted);
  box-shadow: 0 4px 14px rgba(9, 30, 66, .18);
  display: grid;
  place-items: center;
  /* The scrolling box is painted after it in the DOM and would swallow the
     clicks otherwise. */
  z-index: 1;
}
.msgv-box h3 { font-size: 18px; font-weight: 600; line-height: 1.5; margin: 0 0 6px; padding-right: 20px; }
.msgv-date { font-size: 12px; color: var(--c-faint); padding-bottom: 16px; border-bottom: 1px solid var(--c-line); }
/* The body is the operator's own markup, so it is given room rather than a
   house style — only the things that would break the dialog are constrained. */
.msgv-body { font-size: 15px; line-height: 1.75; color: var(--c-ink); padding-top: 20px; }
.msgv-body p { margin: 0 0 14px; }
.msgv-body a { color: var(--c-blue-light); text-decoration: underline; }
.msgv-body img { max-width: 100%; height: auto; }
.msgv-body table { width: 100%; border-collapse: collapse; }
.msgv-body td, .msgv-body th { border: 1px solid var(--c-line); padding: 6px 10px; }
.msgv-body hr { border: 0; border-top: 1px solid var(--c-line); margin: 22px 0; }
/* The row is a link plus a bin; the link takes the space so the whole row is
   tappable rather than just the subject line. */
.m-msg-item .m-msg-open { flex: 1; min-width: 0; color: inherit; }
.m-msg-item .acts button { border: 0; background: none; padding: 0; color: inherit; font-size: 14px; }
.m-msg-foot {
  background: #fff;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  color: var(--c-body);
  border-bottom: 1px solid var(--c-line);
}
.m-msg-foot label { display: flex; align-items: center; gap: 8px; margin: 0; }
.m-msg-foot .right { margin-left: auto; display: flex; gap: 16px; }
.m-msg-foot button {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--c-muted);
}
.acc-empty { text-align: center; color: var(--c-faint); font-size: 13px; padding: 30px 0; }
/* --- Referral -------------------------------------------------------------
   `.ref-status` and `.copy-field` are the desktop build's class names and this
   page has always used them, but /m carries its own stylesheet and never had
   rules for either — the status rows ran together and the code sat in a bare
   input. They are defined here in /m's own tokens rather than borrowed. */
.ref-status { background: var(--c-soft); border-radius: var(--radius-sm); padding: 2px 14px; }
.ref-status div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  font-size: 13px;
  color: var(--c-muted);
}
.ref-status div + div { border-top: 1px solid var(--m-line); }

.copy-field {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--c-soft);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
}
.copy-field input { border: 0; background: none; outline: none; flex: 1; min-width: 0; font-size: 13px; color: var(--c-body); }
/* The clipboard is a real button — it is the only thing on the row that does
   anything, and on a touch canvas it needs a target, not a 15px glyph. The old
   rule dressed a bare <i> in a pointer cursor and nothing happened. */
.copy-field .cf-copy {
  border: 0;
  background: none;
  padding: 4px;
  margin: -4px -4px -4px 0;
  line-height: 1;
  font-size: 16px;
  color: var(--c-blue-light);
  cursor: pointer;
}
/* A tick for a moment, so a silent clipboard write is visibly a write. */
.copy-field .cf-copy.copied { color: var(--c-green); }
.copy-field .cf-copy.copied i::before { content: '\f26e'; }

/* 推薦詞 — the invitation as one copyable block, the same box as the desktop
   page. On this canvas the button is always under the message and full width:
   a 390px row cannot hold a paragraph and a pill side by side. `pre-wrap`
   because the operator's own wording in /admin may carry line breaks and they
   are part of the message. */
.ref-msg-box {
  background: var(--c-soft);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}
.ref-msg-box .rmb-text {
  font-size: 13px;
  line-height: 1.7;
  color: var(--c-body);
  white-space: pre-wrap;
}
/* The link is its own line and wraps anywhere: a referral link is one long
   unbroken word, and left to itself it would run out of the box. */
.ref-msg-box .rmb-link {
  display: block;
  margin-top: 8px;
  color: var(--c-blue-light);
  overflow-wrap: anywhere;
}
.ref-msg-box .rmb-copy {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-top: 12px;
  border: 1px solid var(--c-line);
  background: #fff;
  border-radius: var(--radius-pill);
  padding: 10px 16px;
  font-size: 13px;
  color: var(--c-body);
}
/* The same tick the clipboard buttons give, so a silent write is visibly one. */
.ref-msg-box .rmb-copy.copied { border-color: var(--c-green); background: #e8f6ed; color: #1b7a42; }
.ref-msg-box .rmb-copy.copied i::before { content: '\f26e'; }
/* A ladder is a list on this canvas, not the desktop page's table: 390px
   cannot hold a table without a sideways scroll, and a rung is only ever two
   facts — what the friend does, and what you get. */
.ref-rungs { border: 1px solid var(--m-line); border-radius: var(--radius); overflow: hidden; }
.ref-rungs .hd,
.ref-rungs .ln { display: flex; justify-content: space-between; gap: 12px; padding: 10px 13px; font-size: 13px; }
.ref-rungs .hd { background: var(--c-soft); font-size: 12px; color: var(--c-muted); }
.ref-rungs .ln { border-top: 1px solid var(--m-line); color: var(--c-body); }
.ref-rungs .ln b { font-weight: 600; }
/* A level is three facts, so the first column takes the room the others do
   not need; the member's own row and the milestones already passed are
   marked, as on the desktop page. */
.ref-rungs .hd > :first-child,
.ref-rungs .ln > :first-child { flex: 1; text-align: left; }
.ref-rungs .ln.is-me { background: #eef4ff; color: var(--c-ink); font-weight: 600; }
.ref-rungs .ln.is-done { color: var(--c-faint); text-decoration: line-through; }

/* The agent level: a badge, the rate it earns, and what the next one takes. */
.ref-level { display: flex; flex-direction: column; gap: 6px; margin: 0 0 12px; }
.ref-level b { font-size: 14px; color: var(--c-ink); }
.ref-level > span:last-child { font-size: 12px; color: var(--c-muted); }
.ref-badge {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 600;
  background: var(--c-soft);
  color: var(--c-body);
}
.ref-badge.lv2 { background: #eceff3; color: #5b6673; }
.ref-badge.lv3 { background: #fbf1d6; color: #96702a; }
.ref-badge.lv4 { background: #e4f0ff; color: #1d5fbf; }
/* 超級代理 — given, not climbed, so it is the one badge off the ladder's pale
   scale. Same dark-and-gold as /web's. */
.ref-badge.lv5 { background: linear-gradient(135deg, #1c2533, #33405a); color: #f0d9a0; }
/* Their one figure, where the two rung lists would have been. */
.ref-super {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 16px;
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  background: linear-gradient(135deg, #f7f9fc, #eef2f8);
}
.ref-super em { font-style: normal; font-size: 12px; color: var(--c-muted); }
.ref-super b { font-size: 30px; line-height: 1.1; color: var(--c-blue-light); }
.ref-super > span:last-child { font-size: 12px; color: var(--c-muted); }
/* My Bonus: the chip wallet's figure under the total earned. */
.ref-earned em { display: flex; align-items: center; gap: 5px; margin-top: 8px; font-style: normal; font-size: 12px; color: var(--c-muted); }
.ref-earned em .bi { color: var(--c-gold); }
.ref-earned em span { font-weight: 600; color: var(--c-ink); }

/* The total above the bonus record. */
.ref-earned { background: var(--c-soft); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 12px; }
.ref-earned .t { font-size: 13px; color: var(--c-muted); }
.ref-earned b { display: block; margin-top: 4px; font-size: 22px; color: var(--c-blue-light); }
.ref-earned b span { font-size: 12px; color: var(--c-muted); }

/* The small print under the ladders and the bonus record. */
.ref-note { font-size: 12px; color: var(--c-faint); line-height: 1.7; margin: 12px 0 0; }
/* The one line on the page that is a *condition* rather than an invitation, so
   it is set apart from the prose above it. Flex, not a bullet, so the wrapped
   lines run under the text and not under the icon — which matters more here,
   where seven platform names take four lines. */
.ref-limit {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--c-soft);
  color: var(--c-muted);
}
.ref-limit i { color: var(--c-blue-mid); font-size: 13px; line-height: 1.6; }
/* 推薦首充彩金 — the rule in one line, and the 領取 button beside it while a
   friend's first deposit is waiting. Wraps under the text on a narrow card. */
.ref-first {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: var(--c-soft);
}
.ref-first > i { color: var(--c-orange); font-size: 20px; }
.ref-first .rf-txt { flex: 1 1 180px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ref-first .rf-txt b { font-size: 13px; color: var(--c-body); }
.ref-first .rf-txt span { font-size: 12px; color: var(--c-muted); line-height: 1.6; }
.ref-first form { margin: 0; }
.ref-first .btn { white-space: nowrap; padding: 7px 16px; font-size: 13px; }

/* The count sits inside a card whose own padding is zeroed by the list, so it
   carries its own. */
.acc-count { text-align: center; font-size: 12px; color: var(--c-muted); padding: 14px; background: #fff; }

/* Game tiles that enter a game are links, and must not read as one. The
   platform select and the chip row above them only filter this grid. */
.m-tile.play, .m-game-card.play { display: block; text-decoration: none; color: inherit; }
.m-tile.play:active .thumb, .m-game-card.play:active .thumb { filter: brightness(1.08); }

/* A tile with the platform's own picture instead of a gradient plate.

   **`contain`, not `cover`: a game's artwork is never cropped.** The two shapes
   in the catalogue are the vendors' 4:3 (480x360, what GAME_IMAGE_BOX still
   names) and square — an operator's own upload is kept at whatever size it
   arrived, so both are in the same grid, and this grid's tile is square. `cover`
   filled the tile by cutting the picture, which took a third off the width of
   every live-casino photo.

   **No ground of its own**, so a picture with transparency keeps it and the
   band a mismatched shape leaves is the tile's own background rather than a
   plate drawn behind the artwork. */
.thumb.has-img { overflow: hidden; }
.thumb.has-img img { width: 100%; height: 100%; object-fit: contain; display: block; border-radius: inherit; }

/* Filtered out by the platform or category bar. */
[data-game-grid] > [hidden], [data-top-rail] > [hidden] { display: none; }
.grid-empty { padding: 36px 12px; font-size: 13px; }

/* A platform under maintenance in the picker. */
.pp-opt .pp-maint {
  margin-left: auto;
  font-style: normal;
  font-size: 11px;
  color: var(--c-muted);
}
.pp-opt.active .pp-maint { color: rgba(255, 255, 255, .85); }

/* Balance refresh ----------------------------------------------------------
   The wallet card already had its own refresh button, styled by
   `.m-wallet .refresh`; this is for the ones that stand beside a figure
   elsewhere — the withdraw tile — and carries no look of its own so the rules
   already written for `.n i` and the rest size and colour the icon. */
.bal-sync {
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  line-height: 1;
  vertical-align: baseline;
  cursor: pointer;
}

/* The figure's wrapper is only somewhere for the script to write, so it takes
   the bold back off — the wallet card's own big figure says `font-weight: 700`
   in `.m-wallet .bal b`, which is more specific than this and wins. */
b[data-bal] { font-weight: inherit; }

/* Turning while the request is in flight, dimmed while the cooldown runs. */
[data-bal-sync][data-busy] i { display: inline-block; animation: bal-spin .8s linear infinite; }
[data-bal-sync]:disabled { opacity: .4; cursor: default; }
@keyframes bal-spin { to { transform: rotate(360deg); } }

/* Transaction History cards ------------------------------------------------
   The cashier and promotion histories are six columns wide; on a phone that is
   a horizontal scroll nobody reads, so each row is a small card with its
   figures stacked. The betting summary stays a table — four columns fit. */
.m-hist {
  border: 1px solid var(--m-line);
  border-radius: var(--radius);
  padding: 12px 13px;
  margin-bottom: 10px;
}
.m-hist .hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--m-line);
}
.m-hist .hd b { font-size: 14px; font-weight: 600; }
.m-hist .hd span { font-size: 12px; color: var(--c-muted); }
.m-hist .ln {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  padding: 3px 0;
}
.m-hist .ln span { color: var(--c-muted); }
.m-hist .ln b { font-weight: 500; text-align: right; }
.m-hist .ln small { font-weight: 400; }
/* The row under the list is a total, not another transaction. */
.m-hist.total { background: var(--c-soft); border-color: transparent; }
.m-hist.total .ln b { font-weight: 600; }

/* Opening a game -----------------------------------------------------------
   Covers the gap between tapping a card and the platform handing back a URL —
   see the handler in the script for why that gap is half a second to a second
   before the game's own domain even starts loading.

   **It has to actually block.** A translucent sheet that let taps through
   would look like feedback and change nothing, which was the whole complaint:
   the second tap starts a second launch. `inset: 0` over everything, and the
   body loses its scroll so the list underneath cannot be moved either. */
.game-loading {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: grid;
  place-items: center;
  background: rgba(8, 16, 30, .72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  /* The one thing a player may still do is leave. */
  cursor: wait;
}
.game-loading[hidden] { display: none; }
body.game-loading-on { overflow: hidden; }

.game-loading .gl-box {
  text-align: center;
  padding: 0 28px;
  max-width: 320px;
}

/* A ring rather than a graphic: no image to load at the exact moment the
   network is already busy fetching a game. */
.game-loading .gl-spin {
  width: 46px;
  height: 46px;
  margin: 0 auto 18px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, .22);
  border-top-color: #fff;
  animation: gl-spin .8s linear infinite;
}
@keyframes gl-spin { to { transform: rotate(360deg); } }

.game-loading .gl-name {
  margin: 0 0 6px;
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  line-height: 1.35;
}
/* No game name on the card — the hint carries it alone rather than leaving a
   gap where a title would have been. */
.game-loading .gl-name:empty { display: none; }

.game-loading .gl-hint {
  margin: 0;
  font-size: 13px;
  color: rgba(255, 255, 255, .72);
  line-height: 1.5;
}

/* A player who has asked their browser not to animate still needs to see that
   something is happening, so the ring stays and only stops turning. */
@media (prefers-reduced-motion: reduce) {
  .game-loading .gl-spin { animation: none; border-top-color: rgba(255, 255, 255, .8); }
}

/* --- Play or Demo ---------------------------------------------------------
   The same ground as the loading overlay above and deliberately so: they are
   two steps of one action, and a player who has just tapped a card should see
   the page dim once, not twice in two different colours. The cursor is the
   difference — this one is asking a question, so it is not `wait`. */
.game-pick {
  position: fixed;
  inset: 0;
  z-index: 3010;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(8, 16, 30, .72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.game-pick[hidden] { display: none; }
body.game-pick-on { overflow: hidden; }

.game-pick .gp-box {
  position: relative;
  width: 100%;
  max-width: 330px;
  padding: 22px 22px 22px;
  border-radius: var(--radius-lg);
  background: #fff;
  box-shadow: 0 24px 60px rgba(6, 12, 24, .4);
  text-align: center;
}
/* The game's own picture, at the shape the grids draw it. `object-fit`
   rather than a fixed height: the catalogue's artwork is 4:3 from some
   vendors and square from others, and a letterboxed tile reads as a
   loading error. */
.game-pick .gp-art {
  width: 100%;
  aspect-ratio: 4 / 3;
  margin: 0 0 20px;
  border-radius: var(--radius);
  overflow: hidden;
}
/* The ground shows only while the picture loads — and **not** when the
   plate is on, or this rule's two classes would out-specify `.art`'s one
   and every game with no artwork would be a grey box. */
.game-pick .gp-art:not(.art) { background: var(--c-soft); }
.game-pick .gp-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* A game with no artwork keeps the plate its card wears — see gpPaint(). */
.game-pick .gp-art.art::after { font-size: 13px; }

/* Two equal buttons, side by side: neither choice is the small print. Play
   carries the site's own call-to-action colour and Demo is the quiet one — the
   difference in weight is the only thing saying which is the real game. */
.game-pick .gp-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.game-pick .gp-btn {
  display: block;
  padding: 12px 10px;
  border-radius: var(--radius-pill);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  transition: filter .15s, background .15s, color .15s;
}
/* The logo's own orange, declared here the way every other confirm button
   on this site declares it — the `--cta-*` pair is set on the rule that
   uses it, not inherited from :root. */
.game-pick .gp-play {
  --cta-a: var(--m-logo-orange-lit);
  --cta-b: var(--m-logo-orange-deep);
  background: linear-gradient(135deg, var(--cta-a), var(--cta-b));
  box-shadow: 0 4px 10px rgba(182, 72, 55, .26);
  color: #fff;
}
.game-pick .gp-play:hover { color: #fff; filter: brightness(1.08); }
.game-pick .gp-demo {
  background: var(--c-soft);
  color: var(--c-ink);
  box-shadow: inset 0 0 0 1px var(--c-line);
}
.game-pick .gp-demo:hover { background: #e9edf3; color: var(--c-ink); }

/* The X sits **on** the picture, so it cannot be a grey glyph on white — the
   artwork behind it is a different colour on every game. A dark disc reads
   over all of them. */
.game-pick .gp-x {
  position: absolute;
  top: 30px;
  right: 30px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(10, 16, 28, .55);
  color: #fff;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s;
}
.game-pick .gp-x:hover { background: rgba(10, 16, 28, .8); color: #fff; }

/* --- Registration, in two steps --------------------------------------------
   The account is not created until the second panel has been filled in, so the
   two are one form and one POST — see inc/register-post.php. The panels are
   plain divs the script toggles with `hidden`; **without JavaScript both are
   visible and the single Create Account button submits all of it**, which is
   the whole reason nothing here hides a panel by class.

   `[hidden]` is spelled out because a `display` rule further up the file would
   otherwise win against the browser's own default for the attribute. */
[data-reg-panel][hidden] { display: none; }

.reg-steps {
  display: flex;
  gap: 10px;
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  counter-reset: none;
}
.reg-steps li {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--c-muted);
  padding-top: 9px;
  border-top: 3px solid var(--c-line);
}
.reg-steps li em {
  flex: none;
  width: 20px; height: 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--c-line);
  color: #fff;
  font-style: normal;
  font-size: 11px;
  font-weight: 600;
}
/* Both dots are lit on the second panel — the first step is done, not
   abandoned. `.now` is the one being filled in. */
.reg-steps li.on { border-top-color: var(--c-orange); color: var(--c-body); }
.reg-steps li.on em { background: var(--c-orange); }
.reg-steps li.now { font-weight: 600; }

/* 實名制. Amber rather than red: it is a condition of the form, not an error
   in it, and the member has not done anything wrong yet. */
.reg-note {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 4px 0 0;
  padding: 11px 12px;
  border: 1px solid #f0c98a;
  border-radius: var(--radius-sm, 6px);
  background: #fff8ec;
  color: #7a4c07;
  font-size: 12.5px;
  line-height: 1.65;
}
.reg-note i { flex: none; font-size: 14px; line-height: 1.35; }

/* 上一步 is a way back, not a second thing to press — it reads as text under
   the button that finishes the form. */
.reg-back {
  display: block;
  width: 100%;
  margin-top: 10px;
  background: none;
  border: 0;
  color: var(--c-muted);
  font-size: 13px;
  text-decoration: underline;
}
.reg-back:hover { color: var(--c-body); }

/* The registration account on 提款資料: a lock where the 移除 button is on
   every other row. 實名制 — see sql/migrate-member-bank-locked.sql. */
.bank-locked { color: var(--c-muted); font-size: 14px; display: inline-flex; align-items: center; padding: 4px; }

/* --- 分享邀請詞 --------------------------------------------------------------
   The 分享 button beside Copy, and the menu behind it: Copy, then the six
   networks. **On this canvas it is the way the invitation will actually be
   sent** — a phone is where WhatsApp is, and copying meant leaving the site,
   finding the friend and pasting.

   A menu rather than six buttons on the page: the invitation goes to one
   friend in one app, and 390px has no room to offer six at once anyway. */
.rmb-acts { display: flex; gap: 10px; margin-top: 12px; }
/* Two halves of one row. `min-width: 0` or a long translated label stretches
   its half and pushes the other off the card. */
.rmb-acts > * { flex: 1 1 0; min-width: 0; }
/* The button already carries the full-width rule from `.ref-msg-box .rmb-copy`;
   inside the row it is a half, and the margin belongs to the row now. */
.ref-msg-box .rmb-acts .rmb-copy { width: 100%; margin-top: 0; }

.shr { position: relative; }
.shr-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  border: 1px solid var(--c-line);
  background: #fff;
  border-radius: var(--radius-pill);
  padding: 10px 16px;
  font-size: 13px;
  color: var(--c-body);
}
.shr.open .shr-toggle { border-color: var(--c-blue-light); color: var(--c-blue-mid); background: var(--c-soft); }

/* Closed by `display:none` rather than `hidden` on the element, so one class on
   `.shr` opens and closes it and the script never touches the menu itself.

   **Right-aligned and dropping upward.** The button sits low in a card near the
   foot of a long page; a menu falling out of the bottom lands under the sticky
   tab bar, where the last two entries cannot be reached. */
.shr-menu {
  display: none;
  position: absolute;
  z-index: 60;
  bottom: calc(100% + 8px);
  right: 0;
  min-width: 184px;
  padding: 6px;
  background: #fff;
  border: 1px solid var(--m-line);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(23, 43, 77, .18);
}
.shr.open .shr-menu { display: block; }

.shr-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  font-size: 13.5px;
  color: var(--c-ink);
  text-decoration: none;
  text-align: left;
}
.shr-item:active { background: var(--c-soft); }
/* One column of icons: the labels have to start on the same x or the menu
   reads as a pile rather than a list. */
.shr-ico { flex: none; width: 18px; font-size: 16px; text-align: center; }
/* The tick `[data-copy]` gives every other copy on the site. */
.shr-item.copied { background: #e8f6ed; color: #1b7a42; }
.shr-item.copied .shr-ico { color: #1b7a42; }
.shr-item.copied .shr-ico::before { content: '\f26e'; }

/* Inside the invite popup the row is Copy, 分享 and OK, and the menu drops
   upward there too — the dialog ends just under the button row. */
.rf-acts .shr { flex: 0 0 auto; }
.rf-acts .shr-menu { right: auto; left: 50%; transform: translateX(-50%); }
.rf-acts .shr-toggle { width: auto; height: 44px; border-radius: var(--radius-sm); padding: 0 18px; }

/* --- 首頁彈窗 -----------------------------------------------------------
   The same popup as /web, in this build's own tokens and at a phone's width.
   Managed per country and per language — see admin/home-popup.php.

   The <dialog> is only the frame: .hp-box is the card and the cross sits under
   it on the backdrop, so nothing is drawn over the artwork. */
.hp {
  border: 0;
  padding: 0;
  background: none;
  overflow: visible;
  width: min(420px, calc(100vw - 28px));
}
.hp::backdrop { background: rgba(8, 24, 52, .7); }

/* **The frame**, as on /web: the artwork sits inset in white rather than
   bleeding to the card's edge. Tighter here, because a phone has less of a card
   to give away. */
.hp-box {
  background: #fff;
  border-radius: var(--m-radius);
  box-shadow: 0 18px 44px rgba(9, 30, 66, .3);
  padding: 12px;
}

/* **The slideshow**, as on /web: one entry rotates nothing, two or more run on
   the same carousel script the hero uses, and the wording is inside its own
   slide so it can never come apart from its picture. A phone gets the swipe for
   free — the script is pointer-driven.

   `.hp-view` is the window the track slides behind and carries the picture's
   radius; the dots sit under it in the flow, never over the artwork. */
.hp-slider { position: relative; }
.hp-view { overflow: hidden; border-radius: 8px; }
.hp-track { display: flex; will-change: transform; }
/* `min-width: 0` beside the basis — a bare `flex: 0 0 100%` floors at the
   content width, and one long unbroken line of wording would push its slide
   wider than a 390px card. */
.hp-slide { flex: 0 0 100%; min-width: 0; }

.hp-dots {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin: 12px 0 2px;
}
.hp-dots button {
  width: 6px;
  height: 6px;
  border: 0;
  border-radius: 6px;
  padding: 0;
  background: rgba(9, 30, 66, .22);
  transition: width .2s ease;
}
.hp-dots button.active { width: 16px; background: var(--c-orange); }

/* **The whole picture, always.** media_store() already scaled the upload into
   its 1500×600 box and padded the rest, so `height: auto` draws all of it.
   No `object-fit: cover` anywhere near this — nothing may be cropped.

   `overflow: hidden` is here and not on the card: the radius that has to clip
   something is the picture's — and with a slideshow it is `.hp-view` above that
   clips, so this radius only has to match it. */
.hp-art { display: block; border-radius: 8px; overflow: hidden; }
.hp-art img { display: block; width: 100%; height: auto; }

/* Dark and semi-bold, like /web — and `margin` not padding, so a popup with no
   wording closes up to an even frame on all four sides. */
.hp-text {
  margin: 16px 5px 6px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.6;
  color: #17233a;
  text-align: center;
}

.hp-x {
  display: block;
  margin: 14px auto 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .45);
  background: rgba(255, 255, 255, .12);
  color: #fff;
  font-size: 13px;
  line-height: 1;
}

/* --- Referral QR ---------------------------------------------------------
   The same block /web draws; the box is set from the caller (--qr-size on the
   wrapper), so this file only names the tokens the mobile canvas needs. Level
   H reserves ~30% of the QR and the logo covers ~22% of it — inside the
   reserve, and the code still scans.
--- */
.ref-qr-wrap { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 14px; }
.ref-qr-hint { font-size: 12px; color: var(--m-ink-3, #6b7280); margin: 0; text-align: center; }
.ref-qr { display: inline-flex; flex-direction: column; align-items: center; gap: 10px; }
.ref-qr-box { position: relative; width: var(--qr-size, 180px); height: var(--qr-size, 180px);
  background: #fff; border-radius: 14px; padding: 10px; box-sizing: border-box;
  box-shadow: 0 4px 14px rgba(15, 23, 42, .10); border: 1px solid var(--m-line, #e6e9ef); }
.ref-qr-box canvas { width: 100% !important; height: 100% !important; display: block; border-radius: 8px; background: #fff; }
.ref-qr-logo { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 22%; height: 22%; padding: 4px; background: #fff; border-radius: 10px;
  box-shadow: 0 1px 4px rgba(15, 23, 42, .18); box-sizing: border-box; object-fit: contain; }
.ref-qr-dl { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: 999px;
  border: 1px solid var(--m-line, #e6e9ef); background: #fff; color: var(--m-ink-2, #475569);
  font-size: 12px; cursor: pointer; }
.ref-qr-dl:active { background: #f3f4f6; }
/* The QR inside the invite popup. */
.rf-qr { display: flex; justify-content: center; margin: 0 0 16px; }
.rf-qr .ref-qr-box { --qr-size: 170px; width: 170px; height: 170px; }
