/* Member + staff area. Builds on style.css tokens. */

[hidden] { display: none !important; }
.member-main { padding-top: calc(var(--header-h) + 48px); min-height: 100svh; }   /* the login or account box appears after a moment; the footer waits below the screen instead of jumping (22 Oct 2026) */
.loading { color: var(--ivory-3); font-size: 14px; }
.small { font-size: 12px; }
.hint { margin: 0; font-size: 12px; color: var(--ivory-3); line-height: 1.6; }

/* ---------- Panels & messages ---------- */
.panel {
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--bg-2), var(--bg));
  padding: clamp(18px, 2.6vw, 30px);
  min-width: 0;
}
.msg { padding: 12px 14px; font-size: 14px; line-height: 1.55; border: 1px solid var(--line-2); border-left-width: 3px; margin-bottom: 16px; }
.msg--err { border-color: var(--crimson); background: var(--crimson-wash-2); }
.msg--ok { border-color: #3f8f5a; background: rgba(63, 143, 90, 0.12); }
.msg--info { border-color: var(--ivory-3); background: rgba(239, 230, 214, 0.05); }
.msg a { text-decoration: underline; }

/* ---------- Forms ---------- */
.form { display: grid; gap: 16px; align-content: start; }
.form--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.form--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.form .full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form--2, .form--3 { grid-template-columns: 1fr; } .form--3 .field[style] { grid-column: auto !important; } }

.field { display: grid; gap: 7px; align-content: start; }   /* a message under the neighbour doesn't push this field down */
.field > span:first-child { font-size: 12px; color: var(--ivory-2); letter-spacing: 0.04em; }
.field input, .field select, .search input {
  width: 100%;
  height: 50px;
  padding: 0 16px;
  background: rgba(10, 6, 7, 0.6);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  color: var(--ivory);
  font: inherit;
  font-size: 16px;               /* stops iOS zooming into inputs */
  transition: border-color 150ms, background 150ms, box-shadow 150ms;
}
.field input::placeholder, .search input::placeholder { color: var(--ivory-3); }
/* focus: a 2px ring (border + 1px shadow) fades in within 150ms, so the listening field is always clear */
.field input:focus, .field select:focus, .search input:focus { outline: none; border-color: var(--crimson-hi); box-shadow: 0 0 0 1px var(--crimson-hi); background-color: rgba(10, 6, 7, 0.9); }
.field input[type="date"] { color-scheme: dark; }
.field select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ivory-2) 50%), linear-gradient(-45deg, transparent 50%, var(--ivory-2) 50%); background-position: calc(100% - 20px) 22px, calc(100% - 15px) 22px; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 36px; }
.field select option { background: var(--bg-2); color: var(--ivory); }

/* Six looks per field (owner, 2 Oct 2026; checks in auth.js):
   default  hairline border, example text faint
   focus    red 2px ring (above)
   filled   the answer in full ink; an unchosen list stays faint like example text
   error    red border and ring, and the fix written under the field with a warning icon
   right    green-tinted border and a green check inside the field
   disabled dashed border, faded text and label, no fill */
:root { --ok-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%233ecf7a'%3E%3Cpath d='M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'/%3E%3C/svg%3E"); }
.field select:has(option[value=""]:checked) { color: var(--ivory-3); }
:is(.field, .search) :is(input, select)[aria-invalid="true"], .check input[aria-invalid="true"] { border-color: var(--crimson-hi); outline: 1px solid var(--crimson-hi); outline-offset: 0; }
:is(.field, .search) :is(input, select).is-ok:not(:focus) { border-color: color-mix(in srgb, var(--green) 60%, var(--line-2)); }
:is(.field, .search) input.is-ok { padding-right: 48px; background-image: var(--ok-icon); background-repeat: no-repeat; background-position: right 16px center; background-size: 20px; }
.field select.is-ok {
  padding-right: 64px;
  background-image: var(--ok-icon), linear-gradient(45deg, transparent 50%, var(--ivory-2) 50%), linear-gradient(-45deg, transparent 50%, var(--ivory-2) 50%);
  background-position: right 36px center, calc(100% - 20px) 22px, calc(100% - 15px) 22px; background-size: 20px, 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.field :is(input, select):disabled { color: var(--ivory-3); border-style: dashed; background-color: transparent; cursor: not-allowed; }
.field:has(:is(input, select):disabled) > span:first-child { color: var(--ivory-3); }
.check:has(input:disabled) { opacity: 0.55; cursor: not-allowed; }
.field-msg { display: flex; gap: 6px; align-items: flex-start; font-size: 13px; line-height: 1.45; color: var(--crimson-hi); }
.field-msg:empty { display: none; }
.field-msg .ico { flex: none; width: 16px; height: 16px; margin-top: 1px; }
.check .field-msg { grid-column: 2; }
/* the rules under a field: hollow circle to do, green check done; when the field is wrong the missing ones turn red */
.field-reqs { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 13px; line-height: 1.45; color: var(--ivory-3); }
.field-reqs .req { display: inline-flex; align-items: center; gap: 6px; color: var(--ivory-3); transition: color 150ms; }   /* grey until typed: not the red .req star */
.field-reqs .ico { display: none; width: 16px; height: 16px; }
.field-reqs .req .ico:first-child { display: block; }
.field-reqs .req.is-met { color: var(--green); }
.field-reqs .req.is-met .ico:first-child { display: none; }
.field-reqs .req.is-met .ico:nth-child(2) { display: block; }
.field-reqs.is-wrong .req:not(.is-met) { color: var(--crimson-hi); }
.field-reqs.is-wrong .req:not(.is-met) .ico:first-child { display: none; }
.field-reqs.is-wrong .req:not(.is-met) .ico:nth-child(3) { display: block; }
.search ~ :is(.field-msg, .field-reqs) { margin-top: 8px; }

.check { display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: start; font-size: var(--fs-small); color: var(--ivory-2); line-height: 1.55; cursor: pointer; }
.check input { width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--crimson); }
.check a { color: var(--ivory); text-decoration: underline; }

.field-more summary { cursor: pointer; font-size: var(--fs-small); color: var(--ivory-2); list-style: none; }
.field-more summary::before { content: "+ "; color: var(--crimson-hi); }
.field-more[open] summary::before { content: "− "; }
.field-more[open] { display: grid; gap: 12px; }

.btn--block { width: 100%; justify-content: center; }
.btn--sm { min-height: 40px; padding: 0 14px; font-size: 12px; }
.btn:disabled { opacity: 0.5; pointer-events: none; }
.text-btn { font-size: var(--fs-small); color: var(--ivory-2); text-decoration: underline; text-underline-offset: 3px; justify-self: start; }
@media (hover: hover) and (pointer: fine) { .text-btn:hover { color: var(--ivory); } }
.is-busy { opacity: 0.7; }

/* segmented tabs */
.seg { display: flex; border: 1px solid var(--line-2); }
.seg button {
  flex: 1; min-height: 46px; padding: 0 16px;
  font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ivory-3); transition: background 150ms, color 150ms;
}
.seg button [lang="zh"] { font-size: 14px; letter-spacing: 0.1em; }
.seg button + button { border-left: 1px solid var(--line-2); }
.seg button[aria-selected="true"] { background: var(--ivory); color: var(--bg); }

/* ---------- Log in / sign up ---------- */
.member-grid { align-items: start; }
.auth-intro { display: grid; gap: 22px; }
@media (max-width: 900px) { .auth-panel { order: -1; } }   /* returning members reach the form first */
/* Login cards (member + staff), owner's choice B, 2 Oct 2026: one job, get them in.
   One big title and one red button; everything else is a link · 8px grid: 56px fields, 16 between, 32 before
   the button · labels above, placeholders only as examples · card 24px, fields 12px, button stays a pill
   · hairline edge, surface lighter at the top, soft shadow below (the panel's) */
.panel.auth-panel {
  display: grid; gap: 24px; padding: 32px; border-radius: 24px;
  background: var(--glass-clear); -webkit-backdrop-filter: var(--glass-clear-blur); backdrop-filter: var(--glass-clear-blur);   /* very light glass, like the Rooms box (owner, 22 Oct 2026) */
}
.panel.auth-panel :is(h2, .h2, .lead, .muted, .field > span, .text-btn, p) { text-shadow: var(--glass-clear-shadow); }
@media (max-width: 480px) { .panel.auth-panel { padding: 24px; } }
.auth-head { display: grid; gap: 8px; }
.auth-title { margin: 0; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; font-size: 32px; line-height: 1; text-transform: uppercase; color: var(--ivory); }
.auth-title [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; letter-spacing: 0.04em; }
.auth-sub { margin: 0; font-size: 15px; color: var(--ivory-2); }
.auth-panel .form { gap: 16px; }
.auth-panel .field { gap: 8px; }
.auth-panel .field > span { font-size: 13px; }
.auth-panel .field input { height: 56px; }
.auth-panel .btn--solid[type="submit"] { margin-top: 16px; }
.auth-panel .text-btn { font-size: 14px; color: var(--crimson-hi); }
.auth-links { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 16px; font-size: 14px; color: var(--ivory-3); }

.perks { border-top: 1px solid var(--line); padding-top: 18px; }
.perks ul { list-style: none; margin: 0; padding: 0; display: grid; }
.perks li { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); font-size: var(--fs-body); }
.perks b { font-size: 18px; white-space: nowrap; }
.perks b small { font-size: 12px; color: var(--ivory-3); }

/* ---------- Account ---------- */
.acct-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 24px; }   /* greeting and Log out on one line, centred (owner, 22 Oct 2026) */
.acct-head .h2 { margin: 0; min-width: 0; display: grid; gap: 6px; }
.hello-k { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.hello-name { font-size: clamp(20px, 5.6vw, 44px); line-height: 1.1; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }   /* a long name wraps to a second line, never more */
.acct-head #logout { flex: none; min-height: 44px; padding: 0 18px; font-size: 12px; }
.dash { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 18px; align-items: stretch; }
@media (max-width: 820px) {
  .dash { grid-template-columns: 1fr; grid-auto-rows: 1fr; }   /* phones: the points box is as tall as the card (owner, 22 Oct 2026) */
  /* the card and balance grow (equally) to fill the first screen, so Rewards waits for a scroll without a big empty gap */
  #view-account .dash { min-height: calc(100svh - 280px); row-gap: 24px; }   /* a little shorter (owner, 22 Oct 2026); the room goes to the gaps */
  #view-account .acct-head { margin-bottom: 44px; }
  #rw-sec { margin-top: 74px; }
}

.member-card--live { max-width: none; width: 100%; }
.member-card .mc-qr { width: clamp(84px, 20vw, 118px); background: #fff; padding: 6px; border-radius: 6px; }
.member-card .mc-qr svg { display: block; width: 100%; height: auto; }
.member-card .mc-name { font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: clamp(16px, 2.4vw, 22px); letter-spacing: 0.04em; text-transform: uppercase; }
.member-card .mc-no { margin-top: 4px; }

.balance { display: grid; align-content: center; gap: 8px; }
.balance .k { font-family: var(--f-display); font-stretch: 112%; font-weight: 800; font-size: var(--fs-label); color: var(--ivory-2); letter-spacing: var(--track-label); text-transform: uppercase; }
.balance .big { font-size: clamp(56px, 8vw, 96px); line-height: 0.95; color: var(--green); }

.acct-sec { margin-top: clamp(40px, 6vw, 72px); display: grid; gap: 16px; }
.acct-sec .h3 { margin: 0; }

/* Rewards / History / Profile: folded bars (the .fold-* base is in style.css); open, each is one bordered box */
.acct-folds { margin-top: clamp(40px, 6vw, 72px); display: grid; gap: 12px; }
.fold-h { margin: 0; font: inherit; }
.acct-fold .fold-body { padding: clamp(16px, 2.2vw, 24px); border: 1px solid var(--line); border-top: 0; background: rgba(18, 10, 12, 0.6); }
.acct-fold .hist { border-top: 0; }
.acct-fold .hist-row:last-child { border-bottom: 0; }

.code-sm { font-size: var(--fs-body); letter-spacing: 0.12em; color: var(--ivory); }

.reward-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
.reward { border: 1px solid var(--line); background: var(--bg-2); display: grid; grid-template-rows: auto 1fr; }
.reward img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.rw-body { padding: 16px; display: grid; gap: 8px; align-content: start; }
.rw-body h3 { margin: 0; font-size: 18px; }
.rw-body p { margin: 0; font-size: var(--fs-small); }
.rw-foot { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 6px; }
.rw-foot .cost { font-size: 22px; }
.rw-foot .cost small { font-size: 12px; color: var(--ivory-3); }

.hist { display: grid; border-top: 1px solid var(--line); }
.hist-row { display: grid; grid-template-columns: 150px 1fr auto; gap: 14px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.hist-row .d { font-size: 12px; color: var(--ivory-3); }
.hist-row .r small { display: block; font-size: 12px; color: var(--ivory-3); margin-top: 2px; }
.hist-row .v { text-align: right; }
.hist-row .v.num { font-size: 18px; }
.hist-row .plus { color: var(--green); }
.hist-row .minus { color: var(--crimson-hi); }
.hist-row .act { display: flex; gap: 12px; align-items: center; justify-content: end; flex-wrap: wrap; }
@media (max-width: 520px) {
  .hist-row { grid-template-columns: 1fr auto; }
  .hist-row .d { grid-column: 1 / -1; }
}

/* ---------- Staff console ---------- */
.staff-grid { display: grid; grid-template-columns: minmax(260px, 0.8fr) minmax(0, 1.6fr); gap: 18px; align-items: start; }
@media (max-width: 900px) { .staff-grid { grid-template-columns: 1fr; } }
.search { display: flex; gap: 8px; }
.search input { flex: 1; min-width: 0; }
.search .btn { min-height: 50px; }
.results { display: grid; margin-top: 14px; max-height: 420px; overflow-y: auto; }
.result { display: grid; gap: 2px; text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--line); transition: background 120ms; }
.result:focus-visible { background: var(--crimson-wash); }
@media (hover: hover) and (pointer: fine) { .result:hover { background: var(--crimson-wash); } }
.result small { color: var(--ivory-3); font-size: 12px; }

#member-panel { display: grid; gap: 26px; }
.mp-head { display: flex; justify-content: space-between; gap: 18px; align-items: start; flex-wrap: wrap; }
/* member details: read at a glance across the counter */
.mp-meta { margin-top: 10px; display: grid; gap: 6px; }
.mp-meta p { margin: 0; display: flex; flex-wrap: wrap; gap: 4px 0; font-size: 17px; line-height: 1.45; color: var(--ivory); }
.mp-meta p + p { font-size: 16px; color: var(--ivory-2); }
.mp-meta span, .mp-meta b { overflow-wrap: anywhere; }
.mp-meta p > * + *::before { content: "·"; margin: 0 10px; color: var(--ivory-3); }
@media (max-width: 640px) {
  .mp-meta p { flex-direction: column; gap: 2px; }      /* one detail per line on phones */
  .mp-meta p > * + *::before { content: none; }
}
.mp-id b { font-family: var(--f-display); font-stretch: 112%; font-weight: 700; letter-spacing: 0.08em; }
.req { font-style: normal; color: var(--crimson-hi); }
.visit-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.visit-grid > .field:first-child { grid-column: 1 / -1; }   /* room names + prices need the full width */
@media (max-width: 640px) { .visit-grid { grid-template-columns: 1fr; } }
.mp-bal { display: flex; align-items: baseline; gap: 8px; }
.mp-bal .big { font-size: 48px; line-height: 1; color: var(--green); }
.mp-bal small { color: var(--ivory-2); font-size: 14px; }
.mp-title { margin: 0 0 10px; font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-2); font-family: var(--f-display); font-stretch: 112%; }
.mp-title [lang="zh"] { font-size: 14px; }
.mp-visit { padding: 18px; border: 1px solid var(--line); background: rgba(10, 6, 7, 0.4); }
.mp-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.mp-row b { color: var(--ivory); font-size: 18px; }

/* Staff console is used all shift: no marketing chrome, no grain, no decorative wipes */
.staff-page .nav, .staff-page .call-btn, .staff-page .member-btn, .staff-page .burger { display: none; }
/* of the footer, only the outlined GP PLUS wordmark stays */
.staff-page .foot-grid, .staff-page .foot-base { display: none; }
.staff-page .site-footer { border-top: 0; padding-block: 0 28px; background: transparent; }
.staff-page #view-login .panel, .staff-page #view-denied .panel { margin-inline: auto; }
.who { margin: 10px 0 0; font-size: var(--fs-small); color: var(--ivory-2); }
.staff-page::after { display: none; }
.staff-page .btn::before, .staff-page .rim { display: none; }   /* no rim light in the console */
.staff-page .btn { transition: color 120ms, border-color 120ms, background-color 120ms, transform 160ms var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .staff-page .btn:hover { color: var(--ivory); border-color: var(--ivory-2); background-color: rgba(239, 230, 214, 0.08); }
  .staff-page .btn--solid:hover { background-color: var(--crimson-hi); border-color: var(--crimson-hi); }
}

/* ---------- Member area hover effects ---------- */
.panel.spot { position: relative; overflow: hidden; transition: border-color 200ms; }
.panel.spot > * { position: relative; }
@media (hover: hover) and (pointer: fine) {
  .panel.spot:hover { border-color: var(--line-2); }
}
.member-card--live { transition: transform 300ms var(--ease), box-shadow 300ms var(--ease); will-change: transform; }
.member-card--live::before { transition: background-position 300ms var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .member-card--live:hover { box-shadow: 0 44px 90px rgba(40, 4, 8, 0.7); }
}

/* Gold foil (card-foil.js). The canvas replaces the CSS sheen; without WebGL2 the sheen stays. */
.member-card .mc-foil { position: absolute; inset: 0; z-index: -1; display: block; width: 100%; height: 100%; }
.member-card.has-foil::before { display: none; }
.member-card--live.has-foil { transition: box-shadow 300ms var(--ease); }   /* the script eases the tilt itself */
.member-card.has-foil .mc-name, .member-card.has-foil .mc-no { text-shadow: 0 1px 10px rgba(10, 6, 7, 0.7); }

/* ---------- Staff console: a working tool, so the chrome stays compact ---------- */
.console-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px 16px; margin-bottom: 24px; }   /* the sun/moon stays beside the title; on a phone the title takes two lines */
.console-bar > div:first-child { min-width: 0; }
/* the page title is the biggest heading on the page, above the 24px card titles (owner, 2 Oct 2026) */
.console-title { margin: 0; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; font-size: clamp(28px, 4vw, 40px); line-height: 1; letter-spacing: 0.01em; text-transform: uppercase; }
.console-title [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; letter-spacing: 0.06em; }
.console-bar .who { margin: 10px 0 0; overflow-wrap: anywhere; }
.console-actions { margin-left: auto; display: flex; align-items: center; }   /* the sun / moon switch, right of the title */

/* Section tabs and Log out live in the floating bar, styled and hovering like the public site's nav
   (owner, 2 Oct 2026). staff.js moves them there; they show only once someone is in the console. */
body:not(.in-console) .site-header :is(.tabs, [data-logout]) { display: none; }
.staff-page .site-header .wrap { gap: 40px; }
.staff-page .site-header .header-tools { margin-left: auto; }   /* sections start after the logo; language + Log out on the right */
.tabs { display: flex; gap: 26px; }
.tabs button {
  position: relative; flex: none; min-height: 44px;
  font-family: var(--f-display); font-stretch: 112%; font-weight: 500; font-size: 12px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ivory-2); white-space: nowrap; transition: color 200ms;
}
.tabs button [lang="zh"] { font-size: 14px; letter-spacing: 0.1em; }
.tabs button::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 8px; height: 1px; background: var(--crimson-hi);
  transform: scaleX(0); transform-origin: right; transition: transform 250ms var(--ease);
}
.tabs button[aria-selected="true"] { color: var(--ivory); }
.tabs button[aria-selected="true"]::after { transform: scaleX(1); transform-origin: left; }
@media (hover: hover) and (pointer: fine) {
  .tabs button:hover { color: var(--ivory); }
  .tabs button:hover::after { transform: scaleX(1); transform-origin: left; }
}
.staff-page.in-console .member-main { padding-top: 132px; }
@media (max-width: 1240px) {   /* narrower: the tabs take a second row inside the same bar, scrolling sideways if needed */
  .staff-page .site-header { height: auto; }
  .staff-page .site-header .wrap { flex-wrap: wrap; row-gap: 0; padding-block: 10px 2px; }
  .tabs { order: 3; width: 100%; margin: 0; overflow-x: auto; scrollbar-width: none; border-top: 1px solid var(--glass-edge); }
  .tabs::-webkit-scrollbar { display: none; }
  .staff-page.in-console .member-main { padding-top: 176px; }
}
.tabs .ico, [data-logout] .ico { display: none; }
/* Phones: the sections become an app-style bar at the bottom (icon over a short label); the top bar keeps
   the logo, EN/中文 and a round Log out (owner, 19 Oct 2026) */
@media (max-width: 640px) {
  .staff-page .site-header { height: 60px; }
  .staff-page .site-header .wrap { flex-wrap: nowrap; gap: 8px; padding-block: 0; }
  .staff-page.in-console .member-main { padding-top: 100px; padding-bottom: calc(112px + env(safe-area-inset-bottom, 0px)); }
  body:not(.in-console) > .tabs { display: none; }
  .staff-page > .tabs {
    position: fixed; z-index: 50; inset: auto 8px calc(8px + env(safe-area-inset-bottom, 0px)); width: auto; margin: 0;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0; padding: 6px; overflow: visible;
    border: 1px solid var(--glass-edge); border-radius: 22px; color: var(--ivory);
    background: var(--glass-strong); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    box-shadow: var(--glass-rim), var(--shadow-1);
  }
  .staff-page > .tabs button {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
    min-width: 0; min-height: 58px; border-radius: 16px;
    font-size: 10px; letter-spacing: 0.05em; transition: color 200ms, background-color 200ms;
  }
  .staff-page > .tabs button [lang="zh"] { font-size: 12px; letter-spacing: 0.04em; }
  .staff-page > .tabs .ico { display: block; width: 24px; height: 24px; }
  .staff-page > .tabs button::after { left: 50%; right: auto; width: 18px; margin-left: -9px; top: 3px; bottom: auto; height: 2px; border-radius: 2px; }
  .staff-page > .tabs button[aria-selected="true"] { background: var(--glass-soft); }
  .staff-page > .tabs button[aria-selected="true"] .ico { color: var(--crimson-hi); }
  .staff-page .site-header [data-logout] { width: 44px; min-height: 44px; padding: 0; justify-content: center; }
  .staff-page .site-header [data-logout] .ico { display: block; width: 20px; height: 20px; }
  .staff-page .site-header [data-logout] .lo-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
[data-theme="light"] .staff-page > .tabs {   /* the same deep ink as the top bar */
  --ivory: #efe6d6; --ivory-2: rgba(239, 230, 214, 0.72); --crimson-hi: #e8474a; --glass-edge: rgba(255, 255, 255, 0.1); --glass-soft: rgba(255, 255, 255, 0.08);
  --glass-rim: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  background: #1d2433;
}

.panel-title { margin: 0 0 14px; font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 13px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory); }
.form > .panel-title { margin: 0; }
.panel-title [lang="zh"] { font-size: 15px; letter-spacing: 0.08em; }
.search input::-webkit-search-cancel-button, .search input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
/* the code is what staff read out: make it the loudest thing in the row */
.hist-row .d.code-sm { font-family: var(--f-display); font-stretch: 125%; font-weight: 700; font-size: 18px; letter-spacing: 0.14em; color: var(--ivory); }
.hist-row .d.pts { font-size: 16px; color: var(--ivory); }
.hist-row .d.pts small { font-size: 11px; color: var(--ivory-3); letter-spacing: 0.06em; }

/* ---------- Reports (admin) ---------- */
.seg button[aria-pressed="true"] { background: var(--ivory); color: var(--bg); }
.rp-bar {   /* the download button always sits on the right */
  display: grid; grid-template-columns: auto 1fr auto; grid-template-areas: "period nav csv";
  gap: 12px 18px; align-items: center;
}
.dl { grid-area: csv; justify-self: end; position: relative; }
.rp-period { grid-area: period; justify-self: start; }
.rp-period button { flex: 0 0 auto; min-width: 60px; min-height: 40px; padding: 0 14px; font-size: 11px; }
.rp-nav { grid-area: nav; display: flex; align-items: center; justify-content: center; gap: 10px 14px; flex-wrap: wrap; }
@media (max-width: 1100px) {
  .rp-bar { grid-template-columns: 1fr auto; grid-template-areas: "period csv" "nav nav"; }
  .rp-nav { justify-content: flex-start; }
}
.rp-label { font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 18px; min-width: 15ch; text-align: center; }   /* fixed width: arrows don't jump */
.rp-step:disabled { opacity: 0.35; }
.rp-note { margin: 12px 0 26px; max-width: 80ch; }
#rp-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 40px; transition: opacity 150ms; }   /* wide tables scroll inside, never stretch the page */
#rp-body.is-loading { opacity: 0.45; pointer-events: none; }

.rp-sec { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; min-width: 0; align-content: start; }   /* a wide table scrolls inside its box */
.rp-sec-head { display: flex; justify-content: space-between; align-items: center; gap: 8px 16px; flex-wrap: wrap; min-height: 32px; }
.rp-h { margin: 0; font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 13px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory); }
.rp-h [lang="zh"] { font-size: 15px; letter-spacing: 0.08em; }
.rp-aside, .rp-count { margin: 0; font-size: 13px; color: var(--ivory-3); }
.rp-empty { margin: 0; padding: 18px 20px; border: 1px dashed var(--line-2); color: var(--ivory-2); font-size: 14px; }

/* what a manager should look at */
.rp-alert { border-left: 2px solid var(--crimson-hi); background: linear-gradient(90deg, rgba(186, 27, 29, 0.16), rgba(186, 27, 29, 0.03)); padding: 16px 22px; display: grid; gap: 6px; }
.rp-alert.is-clear { border-left-color: var(--green); background: linear-gradient(90deg, rgba(62, 207, 122, 0.08), transparent); }
.rp-alert ul { list-style: none; margin: 0; padding: 0; display: grid; }
.rp-alert li { display: grid; grid-template-columns: minmax(3ch, auto) 1fr auto; gap: 16px; align-items: baseline; padding: 9px 0; border-top: 1px solid var(--line); font-size: 15px; }
.rp-alert li:first-child { border-top: 0; }
.rp-alert li b { font-size: 20px; color: var(--crimson-hi); text-align: right; }
.rp-alert .text-btn { color: var(--ivory); min-height: 32px; }

/* headline numbers: one strip, the takings lead */
.rp-kpis { display: grid; grid-template-columns: minmax(0, 1.9fr) repeat(4, minmax(0, 1fr)); border: 1px solid var(--line); background: var(--bg-2); }
.rp-kpi { padding: 20px 22px; display: grid; gap: 6px; align-content: start; border-left: 1px solid var(--line); min-width: 0; }
.rp-kpi:first-child { border-left: 0; }
.rp-kpi .k { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: 11px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-kpi .k [lang="zh"] { font-size: 13px; letter-spacing: 0.08em; }
.rp-kpi .v { font-size: clamp(22px, 2.1vw, 30px); line-height: 1.1; white-space: nowrap; }
.rp-kpi--hero .v { font-size: clamp(30px, 3.3vw, 46px); }
.rp-kpi .cur { font-size: 0.42em; margin-right: 0.35em; vertical-align: 0.6em; opacity: 0.75; }
.rp-kpi .s { font-size: 12px; color: var(--ivory-3); display: grid; gap: 4px; }
.rp-kpi .plus { color: var(--green); }
.rp-kpi .minus { color: var(--crimson-hi); }
.rp-delta b { font-weight: 700; color: var(--ivory-2); }
.rp-delta.up b { color: var(--green); }
.rp-delta.down b { color: var(--crimson-hi); }
@media (max-width: 1100px) {
  .rp-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rp-kpi--hero { grid-column: 1 / -1; }
  .rp-kpi { border-left: 0; border-top: 1px solid var(--line); }
  .rp-kpi:first-child { border-top: 0; }
  .rp-kpi:nth-child(odd):not(:first-child) { border-left: 1px solid var(--line); }
}

/* spending chart: plain bars, the value appears on hover or focus */
.rp-chart { position: relative; border: 1px solid var(--line); background: var(--bg-2); padding: 26px 18px 12px; }
.rp-cols { position: relative; display: flex; gap: 3px; height: 210px; }
.rp-cols::before {   /* top, middle and base lines */
  content: ""; position: absolute; left: 0; right: 0; top: 0; bottom: 24px; pointer-events: none;
  border-top: 1px dashed var(--line); border-bottom: 1px solid var(--line-2);
  background: linear-gradient(var(--line), var(--line)) 0 50% / 100% 1px no-repeat;
}
.rp-col { flex: 1 1 0; min-width: 0; display: grid; grid-template-rows: 1fr 24px; padding: 0; cursor: pointer; }
.rp-col-plot { position: relative; display: block; }
.rp-col-bar { position: absolute; left: 14%; right: 14%; bottom: 0; height: var(--h); min-height: 2px; background: var(--crimson); transition: background 150ms; }
.rp-col.is-empty .rp-col-bar { display: none; }
.rp-col-x { align-self: end; font-size: 11px; line-height: 1; color: var(--ivory-3); text-align: center; white-space: nowrap; }
.rp-col.is-today .rp-col-x { color: var(--ivory); font-weight: 700; }
.rp-tip {
  position: absolute; left: 50%; bottom: min(calc(var(--h) + 8px), calc(100% - 46px)); transform: translateX(-50%); z-index: 2;
  display: grid; gap: 2px; padding: 8px 10px; background: var(--ivory); color: var(--bg); font-size: 12px; white-space: nowrap;
  pointer-events: none; opacity: 0; transition: opacity 120ms;
}
.rp-tip b { font-family: var(--f-display); font-stretch: 112%; font-size: 14px; }
.rp-col.is-start .rp-tip { left: 0; transform: none; }
.rp-col.is-end .rp-tip { left: auto; right: 0; transform: none; }
.rp-col:focus-visible { outline: none; }
.rp-col:focus-visible .rp-col-bar { background: var(--ivory); }
.rp-col:focus-visible .rp-tip { opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  .rp-col:hover .rp-col-bar { background: var(--crimson-hi); }
  .rp-col:hover .rp-tip { opacity: 1; }
}
.rp-chart-empty { position: absolute; inset: 0 0 36px; display: grid; place-items: center; margin: 0; font-size: 14px; color: var(--ivory-2); pointer-events: none; }
@media (max-width: 640px) { .rp-cols { gap: 1px; height: 170px; } .rp-chart { padding: 22px 10px 10px; } }

/* tables */
.rp-table { overflow-x: auto; border: 1px solid var(--line); background: var(--bg-2); }
.rp-table table { width: 100%; border-collapse: collapse; font-size: 14px; font-variant-numeric: tabular-nums; }
.rp-table th, .rp-table td { padding: 10px 14px; border-bottom: 1px solid var(--line); text-align: right; white-space: nowrap; }
.rp-table th:first-child, .rp-table td:first-child, .rp-table .l { text-align: left; }
.rp-table th {
  position: sticky; top: 0; z-index: 1; background: var(--bg-3); vertical-align: bottom; line-height: 1.35;
  font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ivory-3);
}
.rp-table th [lang="zh"] { font-size: 13px; letter-spacing: 0.06em; }
.rp-table--wrap th { white-space: normal; min-width: 84px; }   /* long headings wrap instead of pushing the table off the edge */
.rp-table tbody tr:last-child td { border-bottom: 0; }
.rp-table tr.is-zero td { color: var(--ivory-3); }
.rp-table tfoot td { font-weight: 700; background: var(--bg-3); border-top: 1px solid var(--line-2); border-bottom: 0; }
.rp-table small { font-size: 12px; }
.rp-table .plus { color: var(--green); }
.rp-table .minus { color: var(--crimson-hi); }
.rp-table tr.is-flag td { background: rgba(186, 27, 29, 0.12); }
@media (hover: hover) and (pointer: fine) { .rp-table tbody tr:not(.is-flag):hover td { background: rgba(239, 230, 214, 0.035); } }
.rp-wrap { white-space: normal !important; min-width: 200px; }
.rp-who span { display: block; }
.rp-who small { display: block; margin-top: 2px; font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ivory-3); }
.rp-share { min-width: 150px; }
.rp-share-track { position: relative; display: inline-block; width: 88px; height: 4px; margin-right: 10px; vertical-align: middle; background: var(--line); }
.rp-share-track span { position: absolute; inset: 0 auto 0 0; background: var(--crimson-hi); }
.rp-drill { color: var(--ivory); text-decoration: underline; text-decoration-color: var(--line-2); text-underline-offset: 3px; padding: 4px 0; }
@media (hover: hover) and (pointer: fine) { .rp-drill:hover { text-decoration-color: var(--crimson-hi); } }
.rp-flag { display: inline-block; margin-left: 6px; padding: 1px 6px; border: 1px solid var(--crimson-hi); color: var(--crimson-hi); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; vertical-align: 1px; }
.rp-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: 40px 24px; align-items: start; }
@media (max-width: 1100px) { .rp-pair { grid-template-columns: minmax(0, 1fr); } }

.rp-toggle { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; font-size: 13px; color: var(--ivory-2); cursor: pointer; }
.rp-toggle input { width: 16px; height: 16px; margin: 0; accent-color: var(--crimson); }
.rp-filter { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--ivory-2); }
.rp-filter select {
  min-width: 220px; height: 40px; padding: 0 36px 0 12px; appearance: none; cursor: pointer;
  background-color: rgba(10, 6, 7, 0.6);
  background-image: linear-gradient(45deg, transparent 50%, var(--ivory-2) 50%), linear-gradient(-45deg, transparent 50%, var(--ivory-2) 50%);
  background-position: calc(100% - 20px) 17px, calc(100% - 15px) 17px; background-size: 5px 5px; background-repeat: no-repeat;
  border: 1px solid var(--line-2); border-radius: var(--radius); color: var(--ivory); font: inherit; font-size: 14px;
}
.rp-filter select option { background: var(--bg-2); }
@media (max-width: 640px) {
  .rp-bar { grid-template-columns: 1fr; grid-template-areas: "period" "nav" "csv"; }
  .rp-period { display: grid; grid-template-columns: repeat(4, 1fr); width: 100%; justify-self: stretch; }
  .rp-period button { min-width: 0; padding: 0 6px; }
  .rp-nav { width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }   /* the period name gets its own line */
  .rp-label { grid-column: 1 / -1; order: -1; min-width: 0; }
  .rp-step { justify-content: center; }
  #rp-today { grid-column: 1 / -1; justify-self: center; }
  .rp-alert { padding: 14px 16px; }
  .rp-alert li { grid-template-columns: auto 1fr; }
  .rp-alert li .text-btn { grid-column: 2; justify-self: start; }
  .rp-kpi { padding: 16px; }
  .rp-filter { width: 100%; }
  .rp-filter select { flex: 1; min-width: 0; }
}
#visit-preview { color: var(--ivory-2); }
#visit-preview .earn { color: var(--green); font-size: 22px; }
@media (prefers-reduced-motion: reduce) {
  .tabs button::after, #rp-body, .rp-tip, .rp-col-bar { transition: none; }
}

/* ---------- Amount fields ---------- */
.field input.amount { font-variant-numeric: tabular-nums; letter-spacing: 0.02em; text-align: right; }   /* amounts line up on the right, like a receipt */
.field input.amount[readonly] { background: rgba(239, 230, 214, 0.04); border-style: dashed; color: var(--ivory); cursor: default; }
.field input.amount[readonly]:focus { border-color: var(--line-2); background: rgba(239, 230, 214, 0.04); }
.signed { display: flex; }
.signed input { flex: 1; min-width: 0; border-left: 0 !important; }
.sign-seg { display: flex; flex: none; border: 1px solid var(--line-2); }
.sign-seg label { position: relative; display: grid; place-items: center; width: 46px; cursor: pointer; font-size: 20px; color: var(--ivory-3); transition: background 150ms, color 150ms; }
.sign-seg label + label { border-left: 1px solid var(--line-2); }
.sign-seg input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.sign-seg label:has(input[value="1"]:checked) { background: rgba(62, 207, 122, 0.14); color: var(--green); }
.sign-seg label:has(input[value="-1"]:checked) { background: var(--crimson-wash-2); color: var(--crimson-hi); }
.sign-seg label:has(input:focus-visible) { outline: 2px solid var(--crimson-hi); outline-offset: -2px; }

/* ---------- Numbered pages under a list ---------- */
.pager { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 12px; }
.pager button {
  min-width: 36px; height: 36px; padding: 0 8px; border: 1px solid var(--line-2);
  font-size: 13px; font-variant-numeric: tabular-nums; color: var(--ivory-2); transition: background 150ms, color 150ms, border-color 150ms;
}
.pager button[aria-current="page"] { background: var(--ivory); border-color: var(--ivory); color: var(--bg); font-weight: 700; }
.pager button:disabled { opacity: 0.3; cursor: default; }
@media (hover: hover) and (pointer: fine) { .pager button:not(:disabled):not([aria-current]):hover { border-color: var(--ivory-2); color: var(--ivory); } }
.pager button:first-child, .pager button:nth-last-child(2) { font-size: 20px; line-height: 1; }   /* ‹ › */
.pager-gap { padding: 0 2px; color: var(--ivory-3); }
.pager-info { margin-left: auto; font-size: 12px; color: var(--ivory-3); }
.nw { white-space: nowrap; }
@media (max-width: 600px) { .pager { gap: 4px; } .pager-info { flex-basis: 100%; margin-left: 0; } }   /* keeps 44px touch targets */
@media (pointer: coarse) { .pager button { min-width: 44px; height: 44px; } }

/* ---------- Download menu ---------- */
#rp-csv { gap: 10px; }
.dl-caret { width: 12px; height: 12px; transition: transform 150ms; }
#rp-csv[aria-expanded="true"] .dl-caret { transform: rotate(180deg); }
.dl-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; min-width: 260px;
  display: grid; background: var(--bg-3); border: 1px solid var(--line-2); box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
}
.dl-menu button { display: grid; gap: 2px; padding: 12px 16px; text-align: left; transition: background 120ms; }
.dl-menu button + button { border-top: 1px solid var(--line); }
.dl-menu b { font-family: var(--f-display); font-stretch: 112%; font-size: 14px; letter-spacing: 0.06em; }
.dl-menu small { font-size: 12px; color: var(--ivory-3); }
.dl-menu button:focus-visible { outline: none; background: rgba(239, 230, 214, 0.08); }
@media (hover: hover) and (pointer: fine) { .dl-menu button:hover { background: rgba(239, 230, 214, 0.06); } }
@media (max-width: 640px) { .dl-menu { left: auto; right: 0; min-width: min(300px, calc(100vw - 32px)); } }

/* ---------- Printing the console directly ---------- */
@media print {   /* the tidy printable report comes from Reports → Download → PDF */
  body.staff-page > * { display: none !important; }
  body.staff-page::before { content: "To print a report, open Reports and choose Download → PDF.  如需打印报表，请在“报表”中选择“下载 → PDF”。"; display: block; padding: 20mm; font: 12pt sans-serif; color: #000; }
}

/* ---------- Settings: two panels of identical size on shared rows ----------
   Rows: title · description · two fields · button · list. Each panel uses the
   parent's rows (subgrid), so every row lines up across both panels. */
.settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(5, auto); gap: 0 18px; align-items: stretch; }
.set-panel { grid-row: span 5; display: grid; grid-template-rows: subgrid; row-gap: 16px; align-content: start; }
.set-panel > .panel-title { margin: 0; }
.set-hint { margin: 0; font-size: 13px; line-height: 1.6; color: var(--ivory-2); max-width: 60ch; }
.set-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; }   /* a message under one field doesn't lift it */
.set-form { display: contents; }   /* its fields and button sit on the panel's shared rows */
.set-actions { display: grid; grid-template-columns: auto 1fr; gap: 10px 14px; align-items: center; align-content: start; }
.set-status { margin: 0; font-size: 13px; color: var(--ivory-3); line-height: 1.5; }
.set-msg { grid-column: 1 / -1; }
.set-msg .msg { margin: 0; }
.set-list { display: grid; gap: 8px; align-content: start; padding-top: 14px; border-top: 1px solid var(--line); }
.set-list .mp-title { margin: 0; }
.set-list .hist { max-height: 300px; overflow-y: auto; border-top: 0; }
.set-list .hist-row { grid-template-columns: 130px 1fr auto; }
.staff-page :is(.btn, .text-btn):disabled { opacity: 0.38; pointer-events: none; }   /* off until the form has its answers (staff.js syncBtns) */
@supports not (grid-template-rows: subgrid) {
  .settings-grid { grid-template-rows: none; }
  .set-panel { grid-row: auto; display: flex; flex-direction: column; gap: 16px; }
  .set-list { margin-top: auto; }
}
@media (max-width: 900px) {
  .settings-grid { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; gap: 18px; }
  .set-panel { grid-row: auto; grid-template-rows: none; }
}
@media (max-width: 560px) {
  .set-fields { grid-template-columns: minmax(0, 1fr); }
  .set-actions { grid-template-columns: minmax(0, 1fr); }
  .set-list .hist-row { grid-template-columns: 1fr auto; }
}

/* ---------- Staff console cards (Members, Reward codes, Rewards, Settings): the login card's six decisions
   (owner, 2 Oct 2026) · a big title per card · one red button per card, everything else a link · 8px grid:
   56px fields, 8 label-to-field, 16 between, 32 before the main button · placeholders only as examples
   · 24px cards with a lighter top, 12px fields · focus ring, press, spinner then check are site-wide ---------- */
.staff-page .tab .panel {
  padding: 32px; border-radius: 24px;
  background: linear-gradient(180deg, rgba(255, 244, 234, 0.05), rgba(255, 244, 234, 0) 40%), var(--glass);
}
.staff-page .tab .panel-title { font-size: 24px; font-weight: 800; font-stretch: 125%; letter-spacing: 0.01em; line-height: 1.1; }
.staff-page .tab .panel-title [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; font-size: 24px; letter-spacing: 0.04em; }
.staff-page .tab .field { gap: 8px; }
.staff-page .tab .field > span { font-size: 13px; }
.staff-page .tab :is(.field input, .field select, .search input) { height: 56px; }
.staff-page .tab .search .btn { min-height: 56px; }
.staff-page .tab .search { flex-wrap: wrap; }   /* the field keeps its room: the button drops below in a narrow card */
.staff-page .tab .search input { flex: 1 1 140px; }   /* stays beside the button down to 360px phones */
.staff-page .tab :is(.visit-grid, .form--3, .set-fields) { gap: 16px; }
.staff-page .tab form > :has(> .btn--solid[type="submit"]) { margin-top: 16px; }   /* 16 gap + 16 = 32 before the main button */
.staff-page .tab .btn-row { align-items: center; gap: 16px; }
.staff-page .tab .text-btn { font-size: 14px; color: var(--crimson-hi); }
.staff-page .tab .new-member { margin-top: 4px; }
/* Every button in the console follows the button rules (audit, 2 Oct 2026): nothing under 44px to tap, regular
   buttons 48px like the red ones, red buttons keep a 13px label even when small. No rim-light loop in the console. */
.staff-page .btn { min-height: 48px; }
.staff-page .btn--sm { min-height: 44px; padding: 0 18px; }
.staff-page .btn--sm.btn--solid { font-size: 13px; }
.staff-page .rp-period button { min-height: 44px; font-size: 12px; }
.staff-page .text-btn { display: inline-flex; align-items: center; gap: 0.3em; min-height: 44px; }   /* gap keeps the space in "+ Add a new member" */
@media (pointer: coarse) {   /* touch: each switch button itself is a 44px target */
  .staff-page :is(.lang-toggle, .theme-toggle) { height: 46px; padding: 0; }
  .staff-page .theme-toggle button { width: 44px; }
}
@media (max-width: 640px) { .staff-page .tab .panel { padding: 24px; } }

/* ==========================================================
   Soft glass: member area and staff console
   (tokens and the public-site layer are in style.css)
   ========================================================== */
.panel {
  border-color: var(--glass-edge); border-radius: var(--r-lg);
  background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-rim), var(--shadow-2);
}
.msg { border-left-width: 1px; border-radius: var(--r-sm); }
.seg { border-color: var(--glass-edge); border-radius: var(--r-pill); padding: 3px; gap: 3px; background: var(--glass-soft); }
.seg button { border-radius: var(--r-pill); }
.seg button + button { border-left: 0; }
.member-card .mc-qr { border-radius: 10px; }
.acct-fold .fold-body {
  border-color: var(--glass-edge); border-radius: 0 0 var(--r-lg) var(--r-lg);
  background: var(--glass); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
}
.reward { border-color: var(--glass-edge); border-radius: var(--r-md); overflow: hidden; background: var(--glass-soft); box-shadow: var(--shadow-1); }
.result { border-radius: var(--r-sm); }
.mp-visit { border-color: var(--glass-edge); border-radius: var(--r-md); background: var(--glass-soft); }
.signed .sign-seg { border-radius: var(--r-sm) 0 0 var(--r-sm); overflow: hidden; }
.signed input { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.pager button { border-color: var(--glass-edge); border-radius: 10px; }
.dl-menu {
  border-color: var(--glass-edge); border-radius: var(--r-md); overflow: hidden;
  background: var(--glass-strong); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: var(--shadow-2);
}


/* reports */
.rp-alert { border-left: 0; border: 1px solid rgba(232, 71, 74, 0.3); border-radius: var(--r-md); }
.rp-alert.is-clear { border-color: rgba(62, 207, 122, 0.3); }
.rp-kpis { border-color: var(--glass-edge); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-1); }
.rp-chart { border-color: var(--glass-edge); border-radius: var(--r-lg); box-shadow: var(--shadow-1); }
.rp-col-bar { border-radius: 4px 4px 1px 1px; }
.rp-tip { border-radius: 10px; box-shadow: var(--shadow-1); }
.rp-table { border-color: var(--glass-edge); border-radius: var(--r-lg); box-shadow: var(--shadow-1); }
.rp-empty { border-radius: var(--r-md); }
.rp-flag { border-radius: var(--r-pill); padding: 1px 8px; }
.rp-share-track, .rp-share-track span { border-radius: var(--r-pill); }

/* The console is open all shift: same shapes and shadows, but no blur (nothing moves behind it).
   Two looks, chosen with the sun / moon above Log out (staff.js): Dim (below, also the default
   without a choice) and Light. Both lift the small print for reading at a bright or dim counter. */
/* Night (default), owner 2 Oct 2026: back to the public site's Palace Black. Text tiers measured on the cards:
   ivory 15.6:1, soft 10.1:1, faint 6.4:1; field borders ~3:1; crimson chart bars 3:1, green 9.6:1 */
.staff-page {
  --glass-blur: none;
  --bg: #0a0607; --bg-2: #130c0e; --bg-3: #1b1214;
  --ivory-2: rgba(239, 230, 214, 0.8); --ivory-3: rgba(239, 230, 214, 0.62);
  --line: rgba(239, 230, 214, 0.12); --line-2: rgba(239, 230, 214, 0.34);
  --glass: #130c0e; --glass-strong: #160e10; --glass-edge: rgba(239, 230, 214, 0.12);
  --tint: rgba(239, 230, 214, 0.06);   /* hover and quiet fills */
  background: var(--bg); color: var(--ivory);
}
.staff-page .rp-kpis, .staff-page .rp-chart, .staff-page .rp-table { background: var(--bg-2); }
.staff-page .hint, .staff-page .rp-aside, .staff-page .rp-count, .staff-page .set-status, .staff-page .pager-info { font-size: 13px; }
.staff-page .rp-kpi .k, .staff-page .rp-table th, .staff-page .result small, .staff-page .hist-row .d { font-size: 12px; }
.staff-page .rp-kpi .k { white-space: normal; overflow: visible; }   /* at 12px a long label wraps instead of being cut off */
@media (hover: hover) and (pointer: fine) {
  .staff-page .btn:hover { background-color: var(--tint); }
  .staff-page .rp-table tbody tr:not(.is-flag):hover td { background: var(--tint); }
  .staff-page .dl-menu button:hover { background: var(--tint); }
}
.staff-page .dl-menu button:focus-visible { background: var(--tint); }

/* Light ("bright mood"), owner's reference 2 Oct 2026: a soft cool canvas, white cards with very soft cool shadows,
   deep ink text, crimson kept as the one accent. Measured on the cards: ink 15.1:1, text 2 7.5:1, text 3 5.2:1,
   crimson 6.4:1, green 5.2:1 (all still above 4.5:1 on the canvas); field borders ~3:1. The bar is deep ink. */
[data-theme="light"] .staff-page {
  --bg: #eef0f4; --bg-2: #fcfcfd; --bg-3: #f4f6f9;
  --ivory: #1d2433; --ivory-2: #4b5366; --ivory-3: #636b7c;
  --line: #e2e6ec; --line-2: #959dac;
  --glass: #fcfcfd; --glass-strong: #fcfcfd; --glass-soft: rgba(29, 36, 51, 0.04); --glass-edge: #e2e6ec;
  --glass-rim: inset 0 1px 0 #ffffff;
  --shadow-1: 0 1px 2px rgba(29, 36, 51, 0.05), 0 8px 20px -12px rgba(29, 36, 51, 0.14);
  --shadow-2: 0 1px 3px rgba(29, 36, 51, 0.05), 0 24px 48px -28px rgba(29, 36, 51, 0.22);
  --crimson-hi: #b3261e; --green: #167a4a;
  --crimson-wash: rgba(179, 38, 30, 0.06); --crimson-wash-2: rgba(179, 38, 30, 0.09);
  --tint: rgba(29, 36, 51, 0.045);
  --btn-track: rgba(29, 36, 51, 0.08); --btn-lip: rgba(255, 255, 255, 0.9);   /* a soft groove, not a black ring */
  --btn-drop: 0 8px 24px rgba(120, 20, 24, 0.22);
  --ok-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256' fill='%23167a4a'%3E%3Cpath d='M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'/%3E%3C/svg%3E");
}
[data-theme="light"] { color-scheme: light; scrollbar-color: rgba(29, 36, 51, 0.3) #eef0f4; }
[data-theme="light"] .staff-page .site-header {   /* deep ink bar: it keeps the dark-theme text colours */
  --ivory: #efe6d6; --ivory-2: rgba(239, 230, 214, 0.72); --ivory-3: rgba(239, 230, 214, 0.58);
  --bg: #1d2433; --crimson-hi: #e8474a;
  --glass-edge: rgba(255, 255, 255, 0.1); --glass-soft: rgba(255, 255, 255, 0.06);
  --glass-rim: inset 0 1px 0 rgba(255, 255, 255, 0.06);   /* the cards' white top line drew a second outline on the dark bar */
  background: #1d2433; color: var(--ivory);
}
/* flat white cards like the reference (no warm top tint), light numerals in the report like its stats row */
[data-theme="light"] .staff-page :is(.tab .panel, .panel.auth-panel) { background: var(--glass); }
[data-theme="light"] .staff-page .rp-kpi .v { font-weight: 500; font-stretch: 100%; letter-spacing: -0.01em; }
[data-theme="light"] .staff-page .btn--solid { color: #fff; }
[data-theme="light"] .staff-page .field input, [data-theme="light"] .staff-page .field select,
[data-theme="light"] .staff-page .search input, [data-theme="light"] .staff-page .rp-filter select { background-color: #fff; }
[data-theme="light"] .staff-page .field input:focus, [data-theme="light"] .staff-page .field select:focus, [data-theme="light"] .staff-page .search input:focus { background-color: #fff; }
[data-theme="light"] .staff-page .field select option, [data-theme="light"] .staff-page .rp-filter select option { background: #fff; }
[data-theme="light"] .staff-page .field input[type="date"] { color-scheme: light; }
[data-theme="light"] .staff-page .field :is(input, select):disabled { background-color: var(--tint); }
[data-theme="light"] .staff-page .field input.amount[readonly] { background: var(--tint); }
[data-theme="light"] .staff-page .msg--info { background: var(--tint); }
[data-theme="light"] .staff-page .rp-alert { background: #fdf3f2; }
[data-theme="light"] .staff-page .rp-alert.is-clear { background: #eef8f2; }
[data-theme="light"] .staff-page .foot-word { -webkit-text-stroke-color: rgba(29, 36, 51, 0.1); }

.theme-toggle { display: inline-flex; gap: 2px; padding: 3px; height: 40px; border: 1px solid var(--glass-edge); border-radius: var(--r-pill); background: var(--glass-soft); }
.theme-toggle button { display: grid; place-items: center; width: 40px; border-radius: var(--r-pill); color: var(--ivory-3); transition: background-color 150ms, color 150ms; }
.theme-toggle button .ico { width: 18px; height: 18px; }
.theme-toggle button[aria-pressed="true"] { background: var(--ivory); color: var(--bg); }
@media (hover: hover) and (pointer: fine) { .theme-toggle button:not([aria-pressed="true"]):hover { color: var(--ivory); } }
@media (pointer: coarse) { .theme-toggle { height: 44px; } .theme-toggle button { width: 44px; } }
.staff-page .btn:not(.btn--solid) { -webkit-backdrop-filter: none; backdrop-filter: none; }

/* ---------- Password show / hide (auth.js) ---------- */
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 56px; }
.pw-wrap input.is-ok { padding-right: 88px; background-position: right 56px center; }
.pw-toggle {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%); display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 10px; color: var(--ivory-3); transition: color 150ms;
}
.pw-toggle .ico { width: 20px; height: 20px; }
.pw-toggle .ico + .ico, .pw-toggle[aria-pressed="true"] .ico:first-child { display: none; }
.pw-toggle[aria-pressed="true"] .ico + .ico { display: block; }
.pw-toggle:focus-visible { outline: 2px solid var(--crimson-hi); outline-offset: -2px; }

/* ---------- Confirm before something that can't be undone (GP.confirm in auth.js) ---------- */
dialog.confirm {
  width: min(440px, calc(100vw - 32px)); padding: 28px; border: 1px solid var(--glass-edge); border-radius: 24px;
  background: var(--glass-strong); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  color: var(--ivory); box-shadow: var(--shadow-2);
}
.staff-page dialog.confirm { background: var(--bg-2); -webkit-backdrop-filter: none; backdrop-filter: none; }   /* no blur in the console */
dialog.confirm::backdrop { background: rgba(0, 0, 0, 0.55); }
.confirm-title { margin: 0 0 10px; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; font-size: 22px; line-height: 1.15; text-transform: uppercase; }
.confirm-title [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; letter-spacing: 0.04em; }
.confirm-text { margin: 0 0 24px; font-size: 15px; line-height: 1.6; color: var(--ivory-2); overflow-wrap: anywhere; }
.confirm-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 12px; }

@media (hover: hover) and (pointer: fine) {
  .pw-toggle:hover { color: var(--ivory); }
  .auth-panel .text-btn:hover, .staff-page .tab .text-btn:hover { color: var(--ivory); }   /* the red links: turn ivory on hover */
}

/* First paint (member.html head guesses from a saved login): the login box shows at once for visitors,
   "Loading…" for returning members; member.js then shows the real view and drops the guess. */
html:not(.has-session) #loading { display: none; }
.has-session #view-auth { display: none; }

/* ---------- Rewards: two lists, pick several, redeem once (19 Oct 2026) ---------- */
.rw-switch { display: inline-flex; gap: 4px; padding: 4px; border: 1px solid var(--glass-edge); border-radius: var(--r-pill); background: var(--glass-soft); }
.rw-switch button {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 20px; border-radius: var(--r-pill); white-space: nowrap;
  font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: 13px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ivory-2); transition: background 150ms, color 150ms;
}
.rw-switch button[aria-pressed="true"] { background: var(--ivory); color: var(--bg); }
@media (hover: hover) and (pointer: fine) { .rw-switch button[aria-pressed="false"]:hover { color: var(--ivory); } }
.rw-count:empty { display: none; }
.rw-count { min-width: 22px; height: 22px; padding: 0 6px; border-radius: var(--r-pill); display: grid; place-items: center; background: var(--crimson); color: var(--ivory); font-size: 12px; letter-spacing: 0; }
.rw-how { margin: 12px 0 16px; }
.reward.is-picked { border-color: var(--crimson-hi); box-shadow: 0 0 0 1px var(--crimson-hi) inset; }
.qty { display: inline-flex; align-items: center; border: 1px solid var(--line-2); border-radius: var(--r-pill); }
.qty button { width: 44px; height: 44px; display: grid; place-items: center; font-size: 22px; line-height: 1; color: var(--ivory); border-radius: var(--r-pill); }
.qty button:disabled { color: var(--ivory-3); opacity: 0.45; cursor: not-allowed; }
.qty output { min-width: 26px; text-align: center; font-size: 18px; }
.basket {
  position: sticky; bottom: 12px; z-index: 5; margin-top: 18px;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 20px;
  padding: 14px 16px 14px 22px; border: 1px solid var(--glass-edge); border-radius: var(--r-lg);
  background: var(--glass-strong); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: var(--shadow-2);
}
.basket-sum { display: grid; gap: 2px; }
.basket-sum b { font-family: var(--f-display); font-stretch: 112%; font-size: 17px; color: var(--ivory); }
.basket-sum > [lang] { display: grid; gap: 2px; }
.basket-sum > [lang] > span { font-size: var(--fs-small); color: var(--ivory-2); }
.basket-act { display: flex; align-items: center; gap: 16px; }
.has-basket .float-actions { display: none; }   /* the running total sits where the floating buttons were */
@media (max-width: 520px) {
  .basket-act { width: 100%; justify-content: space-between; }
  .basket-act .btn { flex: 1; }
  .rw-switch { display: flex; }
  .rw-switch button { flex: 1; justify-content: center; padding: 0 12px; letter-spacing: 0.08em; }
}
.spent { margin: 0; font-size: var(--fs-small); color: var(--ivory-2); }
.spent b { color: var(--ivory); font-weight: 600; }

/* ---------- Reward ticket: a pass with a tear line; the QR holds only the 6-character code ---------- */
.tickets { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 340px)); gap: 18px; }
.ticket {
  --cut: 170px; --notch: 14px;
  border: 1px solid rgba(232, 71, 74, 0.35); border-radius: var(--r-lg);
  background: linear-gradient(170deg, var(--crimson-deep) 0%, #23090c 46%, #140a0c 100%); box-shadow: var(--shadow-2);
  -webkit-mask: radial-gradient(circle var(--notch) at 0 var(--cut), #0000 97%, #000) left / 51% 100% no-repeat,
                radial-gradient(circle var(--notch) at 100% var(--cut), #0000 97%, #000) right / 51% 100% no-repeat;
          mask: radial-gradient(circle var(--notch) at 0 var(--cut), #0000 97%, #000) left / 51% 100% no-repeat,
                radial-gradient(circle var(--notch) at 100% var(--cut), #0000 97%, #000) right / 51% 100% no-repeat;
}
.tk-top { position: relative; display: grid; gap: 12px; padding: 20px 22px 24px; }
.tk-top::after { content: ""; position: absolute; left: 22px; right: 22px; bottom: 0; border-top: 2px dashed rgba(239, 230, 214, 0.28); }
.tk-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px 10px; }   /* on a narrow voucher the Ready tag drops under the brand instead of overlapping it */
.tk-brand { display: flex; align-items: center; gap: 9px; font-family: var(--f-display); font-stretch: 125%; font-weight: 700; font-size: 10px; letter-spacing: 0.12em; color: var(--ivory); white-space: nowrap; }
.tk-brand img { width: 44px; height: auto; }   /* bigger logo on the vouchers (owner, 22 Oct 2026; 20 -> 30 -> 44px) */
.tb-ticket .tk-brand { font-size: 12px; }
.tb-ticket .tk-brand img { width: 50px; }   /* the voucher opened big: a bigger logo still */
.tk-chip { white-space: nowrap; font-family: var(--f-display); font-weight: 700; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--green); border: 1px solid rgba(62, 207, 122, 0.4); background: rgba(62, 207, 122, 0.08); padding: 5px 9px; border-radius: var(--r-pill); }
.tk-kind { margin-top: 6px; font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--crimson-hi); }
.tk-name { margin: 0; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; text-transform: uppercase; font-size: clamp(22px, 6vw, 28px); line-height: 1.05; color: var(--ivory); }
.tk-name [lang="zh"] { font-family: var(--f-zh); font-weight: 700; letter-spacing: 0.04em; }
.tk-bottom { display: grid; justify-items: center; gap: 12px; padding: 22px; }
.tk-qr { width: 168px; padding: 8px; background: #fff; border-radius: 10px; }
.tk-qr svg { display: block; width: 100%; height: auto; }
.tk-code { font-size: 30px; font-weight: 800; letter-spacing: 0.2em; line-height: 1; color: var(--ivory); }
.tk-hint { margin: 0; font-size: 13px; color: var(--ivory-2); text-align: center; }
.tk-facts { width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding-top: 14px; border-top: 1px solid var(--line); }
.tk-facts .k { display: block; font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.tk-facts b { display: block; margin-top: 3px; font-size: 14px; font-weight: 600; color: var(--ivory); }
.tk-facts .r { text-align: right; }

/* ---------- Member spending chart (same bars as the admin report) ---------- */
.spend-chart { margin-top: 12px; border-radius: var(--r-lg); border-color: var(--glass-edge); }
.spend-chart .rp-col { cursor: pointer; }
.spend-chart .rp-col.is-on .rp-tip { opacity: 1; }
.spend-chart .rp-col.is-on .rp-col-bar { background: var(--crimson-hi); }
.spend-note { margin: 12px 0 0; font-size: var(--fs-small); color: var(--ivory-2); }
.spend-note b { color: var(--ivory); }
@media print { .ticket { -webkit-mask: none; mask: none; } }

/* ---------- Staff: scan a member card or reward ticket with the phone camera ---------- */
.scan-btn { margin-top: 12px; }
dialog.scanner { width: min(420px, calc(100vw - 32px)); }
.scan-view { position: relative; margin: 6px 0 14px; aspect-ratio: 1; border-radius: var(--r-md); overflow: hidden; background: #000; }
.scan-view video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-frame { position: absolute; inset: 18%; border: 3px solid rgba(255, 255, 255, 0.85); border-radius: 14px; box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.35); }
.scan-status.is-err { color: var(--crimson-hi); }
.basket-act .btn { justify-content: center; }

/* ---------- Member area on phones (owner, 19 Oct 2026): open sections, a swipe fan of rewards ---------- */
.acct-sec .sec-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 16px; margin: 0; }
.sec-sum { font-size: var(--fs-small); color: var(--ivory-2); }
.sec-sum:empty { display: none; }
.rw-how { margin: 0; }
.fan-wrap { min-width: 0; }   /* the swipe row scrolls inside the page, never widens it */
#spend { display: grid; gap: 14px; }
.fan-wrap { position: relative; margin: 0 calc(var(--gutter) * -1); }
#rw-sec .rw-switch { justify-self: start; }
.fan {
  --cw: min(70vw, 300px);
  position: relative; display: flex; gap: 0; overflow: auto hidden; overscroll-behavior-x: contain; touch-action: pan-x pan-y;   /* sideways only: up/down always scrolls the page */
  scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: 26px calc(50% - var(--cw) / 2 + 10px) 70px;   /* +10px undoes the first and last card's tuck-in, so they sit dead centre */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);   /* cards fade at the edges, never cut */
}
.fan::-webkit-scrollbar { display: none; }
.fan .reward {
  flex: 0 0 var(--cw); margin: 0 -10px; scroll-snap-align: center;   /* neighbours tuck in behind */
  display: flex; flex-direction: column; gap: 10px; min-height: 330px; padding: 22px;   /* points, bar and button sit at the bottom of every card */
  border: 1px solid rgba(232, 71, 74, 0.32); border-radius: var(--r-lg);
  background: linear-gradient(165deg, var(--crimson-deep) 0%, #23090c 55%, #140a0c 100%); box-shadow: var(--shadow-2);
  /* tilt around the card's own centre: Chrome snaps to the tilted outline, and a pivot far below shifted it
     sideways, so the swiped-to card stopped ~18px left of centre (owner, 20 Oct 2026). The sink keeps the fan curve. */
  transform-origin: 50% 50%;
  transform: translateY(calc(var(--ad, 0) * 14px)) rotate(calc(var(--d, 0) * 3.5deg)) scale(calc(1 - var(--ad, 0) * 0.05));
  opacity: calc(1 - var(--ad, 0) * 0.3);
  will-change: transform;
}
.fan .reward.is-picked { border-color: var(--crimson-hi); box-shadow: 0 0 0 1px var(--crimson-hi) inset, var(--shadow-2); }
.fan .reward.is-short .rw-cost b { color: var(--ivory-2); }
.rw-kind { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--crimson-hi); }
.rw-name { margin: 0; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; text-transform: uppercase; font-size: clamp(22px, 6vw, 28px); line-height: 1.05; color: var(--ivory); }
.rw-name [lang="zh"] { font-family: var(--f-zh); font-weight: 700; letter-spacing: 0.04em; }
.rw-desc { margin: 0; font-size: var(--fs-small); color: var(--ivory-2); }
.rw-cost { margin-top: auto; padding-top: 8px; }
.rw-cost b { font-size: 34px; line-height: 1; color: var(--ivory); }
.rw-cost small { font-size: 13px; color: var(--ivory-3); }
.rw-meter { height: 6px; border-radius: var(--r-pill); background: rgba(239, 230, 214, 0.1); overflow: hidden; }
.rw-meter i { display: block; height: 100%; width: calc(var(--p) * 100%); border-radius: inherit; background: var(--green); }
.reward.is-short .rw-meter i { background: var(--crimson-hi); }
.rw-state { margin: 0; font-size: 13px; color: var(--ivory-2); }
.rw-act { margin-top: 4px; display: flex; justify-content: center; }
.rw-act .btn:disabled { opacity: 0.38; cursor: not-allowed; }
.fan-arrow { position: absolute; top: 50%; z-index: 20; width: 48px; height: 48px; margin-top: -24px; border-radius: 50%; border: 1px solid var(--glass-edge); background: var(--glass-strong); color: var(--ivory); display: grid; place-items: center; }
.fan-arrow::before { content: ""; width: 10px; height: 10px; border: solid currentColor; border-width: 2px 2px 0 0; transform: rotate(45deg); margin-right: 4px; }
.fan-prev { left: max(8px, calc(50% - var(--cw, 320px) / 2 - 64px)); }
.fan-prev::before { transform: rotate(-135deg); margin: 0 0 0 4px; }
.fan-next { right: max(8px, calc(50% - var(--cw, 320px) / 2 - 64px)); }
.fan-arrow:disabled { opacity: 0.3; cursor: default; }
@media (hover: none) { .fan-arrow { display: none; } }
.fan-dots { display: flex; justify-content: center; gap: 8px; margin-top: -40px; }
.fan-dots:empty { display: none; }
.fan-dots i { width: 8px; height: 8px; border-radius: 50%; background: rgba(239, 230, 214, 0.25); cursor: pointer; transition: width 200ms var(--ease), background 200ms; }
.fan-dots i.is-on { width: 22px; border-radius: var(--r-pill); background: var(--ivory); }
@media (prefers-reduced-motion: reduce) { .fan .reward { transform: none; opacity: 1; } .fan { scroll-behavior: auto; } }

.hs-pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 4px; font-size: var(--fs-small); color: var(--ivory-2); }
.hs-pager .text-btn { min-height: 44px; }
.hs-pager .text-btn:disabled { opacity: 0.35; cursor: default; text-decoration: none; }
@media (max-width: 560px) {
  .hist-row { grid-template-columns: 1fr auto; gap: 2px 12px; }
  .hist-row .d { grid-column: 1 / -1; }
}

.sp-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
@media (max-width: 640px) { .sp-tiles { grid-template-columns: 1fr 1fr; } }
.sp-tile { display: grid; gap: 6px; padding: 14px 16px; border: 1px solid var(--glass-edge); border-radius: var(--r-md); background: var(--glass); box-shadow: var(--glass-rim); }
.sp-tile .k { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.sp-tile b { font-size: clamp(17px, 4.6vw, 22px); color: var(--ivory); white-space: nowrap; }
.sp-fav { margin: 0; font-size: var(--fs-small); color: var(--ivory-2); }
.sp-fav b { color: var(--ivory); }
.sp-chart { display: grid; grid-template-columns: repeat(12, 1fr); gap: 4px; height: 190px; padding: 16px 12px 10px; border: 1px solid var(--glass-edge); border-radius: var(--r-lg); background: var(--bg-2); }
.sp-col { display: grid; grid-template-rows: 1fr 22px; min-width: 0; padding: 0; border-radius: 8px; cursor: pointer; }
.sp-plot { position: relative; display: block; }
.sp-bar { position: absolute; left: 12%; right: 12%; bottom: 0; height: var(--h); border-radius: 5px 5px 2px 2px; background: rgba(186, 27, 29, 0.55); transition: background 150ms, height 300ms var(--ease); }
.sp-col.is-empty .sp-bar { height: 2px; background: var(--line-2); }
.sp-col.is-on .sp-bar { background: var(--crimson-hi); box-shadow: 0 0 18px rgba(232, 71, 74, 0.45); }
.sp-x { align-self: end; text-align: center; font-size: 11px; line-height: 1; color: var(--ivory-3); }
.sp-col.is-on .sp-x { color: var(--ivory); font-weight: 700; }
.sp-col:focus-visible { outline: 2px solid var(--ivory); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .sp-col:not(.is-on):hover .sp-bar { background: var(--crimson); } }
.sp-month { display: grid; gap: 8px; padding: 16px 18px; border: 1px solid var(--glass-edge); border-radius: var(--r-lg); background: var(--glass); }
.sp-month-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.sp-month-head b { font-family: var(--f-display); font-stretch: 112%; font-size: 16px; color: var(--ivory); white-space: nowrap; }
.sp-month-head .num { font-size: 18px; color: var(--green); white-space: nowrap; }
.sp-month .hist { border-top: 1px solid var(--line); }
.sp-month .hist-row:last-child { border-bottom: 0; }

/* ---------- Running points, ticket fan, the locked Redeem box (owner, 19 Oct 2026) ---------- */
.bal-note { font-size: var(--fs-small); color: var(--crimson-hi); }
.rw-points { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.rw-points .k { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.rw-points b { font-size: 30px; line-height: 1; color: var(--green); }
.rw-of { font-size: var(--fs-small); color: var(--ivory-2); }
.fan-wrap.is-single .fan-arrow { display: none; }
.tickets-fan { --cw: min(74vw, 320px); }   /* the neighbours peek in evenly on both sides */
.fan .ticket {
  flex: 0 0 var(--cw); margin: 0 -10px; scroll-snap-align: center;
  transform-origin: 50% 50%;   /* own-centre tilt, so snapping lands dead centre (see .fan .reward) */
  transform: translateY(calc(var(--ad, 0) * 14px)) rotate(calc(var(--d, 0) * 3deg)) scale(calc(1 - var(--ad, 0) * 0.05));
  opacity: calc(1 - var(--ad, 0) * 0.3);
}
.fan .ticket.is-new { animation: ticketIn 450ms var(--ease) both; animation-delay: calc(var(--n, 0) * 60ms); }
@keyframes ticketIn { from { translate: 0 24px; scale: 0.96; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .fan .ticket { transform: none; opacity: 1; } .fan .ticket.is-new { animation: none; } }

html.is-locked, html.is-locked body { overflow: hidden; }   /* nothing scrolls behind the Redeem box */
dialog.redeem-box {
  width: min(440px, calc(100vw - 24px)); max-height: calc(100dvh - 24px); overflow: auto; padding: 0 24px 24px;
  border: 1px solid var(--glass-edge); border-radius: 28px; background: var(--glass-strong);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); color: var(--ivory); box-shadow: var(--shadow-2);
  animation: rbIn 260ms var(--ease) both;
}
dialog.redeem-box::backdrop { background: rgba(8, 4, 5, 0.82); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); animation: rbFade 300ms ease both; }
@keyframes rbIn { from { opacity: 0; translate: 0 12px; scale: 0.98; } }
@keyframes rbFade { from { opacity: 0; } }
.rb-fan { position: relative; height: 176px; margin: 0 -24px 6px; display: grid; place-items: end center; overflow: hidden; }
.rb-stub {
  position: absolute; bottom: 10px; width: 168px; min-height: 124px; padding: 14px; overflow-wrap: anywhere; display: grid; align-content: start; gap: 6px; text-align: left;
  border: 1px solid rgba(232, 71, 74, 0.4); border-radius: 16px; background: linear-gradient(165deg, var(--crimson-deep), #1a0a0d); box-shadow: var(--shadow-2);
  transform-origin: 50% 140%; transform: translateX(calc(var(--k) * 60px)) rotate(calc(var(--k) * 6deg));
  animation: stubIn 380ms var(--ease) both; animation-delay: calc(80ms + var(--i) * 50ms);
}
.rb-stub b { font-family: var(--f-display); font-stretch: 112%; font-weight: 800; text-transform: uppercase; font-size: 14px; line-height: 1.15; color: var(--ivory); }
.rb-stub b [lang="zh"] { font-family: var(--f-zh); font-weight: 700; }
.rb-stub i { position: absolute; bottom: 10px; left: 12px; font-style: normal; font-size: 13px; padding: 3px 8px; border-radius: var(--r-pill); background: var(--crimson); color: var(--ivory); }
.rb-stub .tk-kind { margin: 0; font-size: 10px; }
@keyframes stubIn { from { opacity: 0; translate: 0 16px; } }
.rb-title { margin: 6px 0 14px; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; font-size: 24px; line-height: 1.1; text-transform: uppercase; }
.rb-title [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; }
.rb-lines { margin: 0; display: grid; }
.rb-lines > div { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.rb-lines dt { font-size: 15px; color: var(--ivory-2); }
.rb-lines dd { margin: 0; white-space: nowrap; font-size: 15px; color: var(--ivory); }
.rb-total dt, .rb-total dd { color: var(--ivory); font-weight: 700; font-size: 17px; }
.rb-bal { border-bottom: 0 !important; }
.rb-bal dd b { color: var(--green); font-size: 19px; }
.rb-note { margin: 12px 0 20px; font-size: 14px; line-height: 1.6; color: var(--ivory-2); }
.rb-msg { margin: -8px 0 16px; font-size: 14px; color: var(--crimson-hi); }
.rb-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.rb-actions .btn { justify-content: center; white-space: nowrap; padding-inline: 12px; }
.redeem-box.is-done .rb-lines, .redeem-box.is-done .rb-note, .redeem-box.is-done .rb-actions { opacity: 0.35; transition: opacity 300ms; }
.redeem-box.is-done .rb-stub { animation: stubUp 400ms var(--ease) both; }
.redeem-box.is-done .rb-title { color: var(--green); }
@keyframes stubUp { to { translate: 0 -8px; } }
@media (prefers-reduced-motion: reduce) { dialog.redeem-box, dialog.redeem-box::backdrop, .rb-stub, .redeem-box.is-done .rb-stub { animation: none; } }

/* ---------- Rewards tabs incl. My tickets; the Add box; a ticket shown big (owner, 19 Oct 2026) ---------- */
#rw-sec .rw-switch button { padding: 0 16px; }
@media (max-width: 520px) { #rw-sec .rw-switch button { padding: 0 8px; font-size: 12px; letter-spacing: 0.06em; gap: 6px; } }
.rw-count--tk { background: var(--green); color: var(--bg); }
#rw-pane, #tk-pane { display: grid; gap: 16px; min-width: 0; }
#rw-pane[hidden], #tk-pane[hidden] { display: none; }
.fan .ticket { cursor: pointer; }
.fan .ticket:focus-visible { outline: 2px solid var(--ivory); outline-offset: 4px; }
.fan .empty { margin: 0 auto; flex: 0 0 var(--cw); }

.pk-card {
  display: grid; gap: 8px; padding: 20px; margin-bottom: 16px; border: 1px solid rgba(232, 71, 74, 0.4); border-radius: var(--r-lg);
  background: linear-gradient(165deg, var(--crimson-deep) 0%, #23090c 60%, #140a0c 100%); box-shadow: var(--shadow-2);
  transform-origin: 50% 60%;
}
.pk-card .rw-cost { margin: 0; padding: 0; }
.pk-qty { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 4px; }
.pk-qty .k { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: var(--fs-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.pk-qty .qty output { min-width: 40px; font-size: 22px; }
.pk-of { font-size: 14px; color: var(--ivory-3); }
#pk-left { font-size: 24px; }
.pk-remove { display: block; margin: 14px auto 0; min-height: 44px; }


/* ---------- Picked cards and a slimmer basket bar (owner, 19 Oct 2026) ---------- */
.fan .reward { position: relative; }
.rw-badge { position: absolute; top: 18px; right: 18px; padding: 4px 10px; border-radius: var(--r-pill); background: var(--green); color: var(--bg); font-size: 13px; font-weight: 800; }
.fan .reward.is-picked .rw-state { color: var(--green); font-weight: 600; }
.basket { flex-wrap: nowrap; gap: 12px; padding: 10px 10px 10px 18px; border-radius: var(--r-pill); }
.basket-sum b { font-size: 15px; white-space: nowrap; }
.basket-sum > [lang] > span { font-size: 13px; }
.basket-act { gap: 4px; flex: none; }
.basket-act .text-btn { min-height: 44px; padding: 0 10px; font-size: 13px; }
.basket-act .btn { min-height: 46px; padding: 0 22px; }
@media (max-width: 520px) { .basket-act { width: auto; } .basket-act .btn { flex: none; } }
@media (max-width: 360px) { .basket { padding-left: 14px; } .basket-act .text-btn { padding: 0 6px; } .basket-act .btn { padding: 0 16px; } }

.acct-sec, .acct-fold { scroll-margin-top: calc(var(--header-h) + 24px); }   /* jumps land below the floating header */

/* ---------- One type scale for reward cards, tickets and their boxes (owner, 19 Oct 2026) ----------
   label 11px · name 20px (17px when long) · body 13px · points 30px · code 28px · facts 14px */
.rw-kind, .tk-kind { font-size: 11px; letter-spacing: 0.14em; }
.rw-name, .tk-name { font-size: 20px; line-height: 1.15; letter-spacing: 0.01em; }
.rw-name.is-long, .tk-name.is-long, .tb-name.is-long { font-size: 17px; }
.rw-name [lang="zh"], .tk-name [lang="zh"] { font-size: 19px; letter-spacing: 0.06em; }
.fan .reward { min-height: 300px; padding: 20px; gap: 8px; }
.rw-desc { font-size: 13px; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.rw-cost b { font-size: 30px; }
.rw-cost small { font-size: 12px; }
.rw-state { font-size: 12.5px; }
.rw-badge { top: 16px; right: 16px; font-size: 12px; }
.tk-top { gap: 10px; padding: 18px 20px 22px; }
.tk-top::after { left: 20px; right: 20px; }
.tk-bottom { gap: 10px; padding: 20px; }
.tk-qr { width: 156px; }
.tk-code { font-size: 28px; }
.tk-hint { font-size: 12.5px; }
.tk-facts .k { font-size: 11px; }
.tk-facts b { font-size: 14px; }
.pk-card .rw-name { font-size: 22px; }
.pk-card .rw-name.is-long { font-size: 18px; }
.rb-stub b { font-size: 13px; }

/* entrances that CSS can do on its own */
.basket:not([hidden]) { animation: basketIn 280ms var(--ease) both; }
@keyframes basketIn { from { opacity: 0; translate: 0 16px; } }
.bal-note:not([hidden]) { animation: noteIn 240ms ease both; }
@keyframes noteIn { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .basket:not([hidden]), .bal-note:not([hidden]) { animation: none; } }

/* ---------- "RM50" over "Cash Voucher"; the Add box lined up on one grid (owner, 19 Oct 2026) ---------- */
.is-split { display: grid; gap: 2px; }
.nm-big { display: block; font-size: 40px; line-height: 1; letter-spacing: 0.01em; text-transform: none; }
.nm-sub { display: block; font-family: var(--f-body); font-stretch: 100%; font-weight: 600; font-size: 15px; letter-spacing: 0.02em; text-transform: none; color: var(--ivory-2); }
.nm-sub [lang="zh"] { font-family: var(--f-zh); }
.tk-name.is-split .nm-big { font-size: 34px; }
.pk-card .is-split .nm-big { font-size: 46px; }
.pk-card .is-split .nm-sub { font-size: 16px; }
.rb-stub b.is-split .nm-big { font-size: 22px; }
.rb-stub b.is-split .nm-sub { font-size: 12px; }

dialog.pick-box { padding: 20px 20px 22px; }   /* the card keeps the same gap from the top as from the sides */
.pk-card { margin-bottom: 8px; padding: 20px 20px 16px; gap: 8px; }
.pk-each { display: flex; justify-content: space-between; align-items: baseline; margin-top: 8px; padding-top: 12px; border-top: 1px solid rgba(239, 230, 214, 0.14); }
.pk-each span { font-family: var(--f-display); font-stretch: 112%; font-weight: 600; font-size: 11px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory-3); }
.pk-each b { font-size: 20px; color: var(--ivory); }
.pk-lines > div { align-items: center; min-height: 56px; padding: 6px 20px; }   /* words and numbers line up with the card's above */
.pk-lines dt { font-size: 14px; }
.pk-lines dd { font-size: 16px; }
.pk-lines .qty { border-color: var(--line-2); }
.pk-lines .qty output { min-width: 36px; font-size: 20px; }
.pk-lines #pk-left { font-size: 22px; color: var(--green); }
.pk-lines .pk-of { font-size: 13px; color: var(--ivory-3); }
.pick-box .rb-actions { margin-top: 16px; }
.pick-box #pk-msg { margin: 8px 4px 0; }

/* ---------- A ticket shown big for staff (owner, 19 Oct 2026): the ticket itself, larger, on a dark page ---------- */
dialog.ticket-box {
  width: min(360px, calc(100vw - 32px)); max-height: calc(100dvh - 16px); overflow: visible; padding: 0; border: 0; background: transparent; color: var(--ivory);
  display: grid; justify-items: center; gap: 14px; text-align: left; animation: rbIn 280ms var(--ease) both;
}
dialog.ticket-box:not([open]) { display: none; }
dialog.ticket-box::backdrop { background: rgba(6, 3, 4, 0.9); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); animation: rbFade 240ms ease both; }
.tb-ticket {
  --cut: 150px;
  width: 100%; border: 1px solid rgba(232, 71, 74, 0.4); border-radius: 28px;
  background: linear-gradient(170deg, #6b0e13 0%, #2a090c 42%, #150a0c 100%); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  -webkit-mask: radial-gradient(circle 15px at 0 var(--cut), #0000 97%, #000) left / 51% 100% no-repeat, radial-gradient(circle 15px at 100% var(--cut), #0000 97%, #000) right / 51% 100% no-repeat;
          mask: radial-gradient(circle 15px at 0 var(--cut), #0000 97%, #000) left / 51% 100% no-repeat, radial-gradient(circle 15px at 100% var(--cut), #0000 97%, #000) right / 51% 100% no-repeat;
}
.tb-top { position: relative; display: grid; gap: 10px; padding: 20px 22px 24px; }
.tb-top::after { content: ""; position: absolute; left: 24px; right: 24px; bottom: 0; border-top: 2px dashed rgba(255, 240, 230, 0.28); }
.tb-top .tk-kind { margin: 6px 0 0; }
.tb-name { margin: 0; font-family: var(--f-display); font-stretch: 125%; font-weight: 800; text-transform: uppercase; font-size: 24px; line-height: 1.1; color: #fff3e8; }
.tb-name.is-split .nm-big { font-size: 48px; color: #fff3e8; }
.tb-name.is-split .nm-sub { font-size: 16px; }
.tb-name.is-split { justify-items: start; }
.tb-chip { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; padding: 6px 11px; border-radius: var(--r-pill); border: 1px solid rgba(62, 207, 122, 0.45); background: rgba(62, 207, 122, 0.1); color: var(--green); font-family: var(--f-display); font-weight: 700; font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; }
.tb-chip i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.tb-bottom { display: grid; justify-items: center; gap: 12px; padding: 24px 22px 20px; }
.tb-qrwrap { position: relative; width: min(66vw, 236px); }
.tb-qr { width: 100%; margin: 0; padding: 14px; background: #fff; border-radius: 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); transition: opacity 300ms, filter 300ms; }
.tb-code { font-size: 30px; letter-spacing: 0.24em; margin-right: -0.24em; color: #fff3e8; }
.tb-status { display: inline-flex; align-items: center; gap: 8px; margin: 0; padding: 7px 14px; border-radius: var(--r-pill); background: rgba(255, 244, 234, 0.06); font-size: 13px; color: var(--ivory-2); }
.tb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 0 rgba(62, 207, 122, 0.6); animation: tbPulse 1.6s ease-out infinite; }
@keyframes tbPulse { to { box-shadow: 0 0 0 9px rgba(62, 207, 122, 0); } }
.tb-bottom .tk-facts { margin-top: 4px; }
.tb-tip { margin: 0; font-size: 12px; color: var(--ivory-3); text-align: center; }
.tb-close { min-height: 48px; padding: 0 34px; border-radius: var(--r-pill); border: 1px solid rgba(239, 230, 214, 0.24); outline: 2px solid var(--crimson-hi); outline-offset: 3px; color: var(--ivory); font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 13px; letter-spacing: var(--track-action); text-transform: uppercase; }
@media (hover: hover) and (pointer: fine) { .tb-close:hover { border-color: var(--ivory); } }
.tb-stamp {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%; rotate: -10deg; padding: 10px 18px; border: 3px solid currentColor; border-radius: 12px;
  font-family: var(--f-display); font-stretch: 125%; font-weight: 800; font-size: 26px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--green);
  background: rgba(10, 6, 7, 0.82); opacity: 0; pointer-events: none;
}
.ticket-box.is-collected .tb-stamp, .ticket-box.is-cancelled .tb-stamp { opacity: 1; animation: tbStamp 420ms var(--ease) both; }
.ticket-box.is-cancelled .tb-stamp { color: var(--crimson-hi); }
@keyframes tbStamp { from { opacity: 0; scale: 1.6; } }
.ticket-box.is-collected .tb-qr, .ticket-box.is-cancelled .tb-qr { opacity: 0.35; filter: grayscale(1); }
.ticket-box.is-collected .tb-chip { background: var(--green); color: var(--bg); border-color: var(--green); }
.ticket-box.is-collected .tb-status { color: var(--green); font-weight: 600; }
.ticket-box.is-cancelled .tb-chip, .ticket-box.is-cancelled .tb-status { color: var(--crimson-hi); border-color: rgba(232, 71, 74, 0.45); background: rgba(232, 71, 74, 0.1); }
.ticket-box.is-collected .tb-dot, .ticket-box.is-cancelled .tb-dot { animation: none; }
.ticket-box.is-cancelled .tb-dot { background: var(--crimson-hi); }
@media (prefers-reduced-motion: reduce) { dialog.ticket-box, .tb-dot, .tb-stamp { animation: none !important; } }
/* the voucher opened big, a little shorter (owner, 22 Oct 2026): smaller QR and tighter spacing */
.tb-top { padding: 16px 20px 18px; gap: 8px; }
.tb-name.is-split .nm-big { font-size: 40px; }
.tb-bottom { gap: 10px; padding: 16px 20px 16px; }
.tb-qrwrap { width: min(54vw, 200px); }
.tb-qr { padding: 12px; }
.tb-code { font-size: 24px; }
/* the member card, opened big (tap the card): one tall card, the palace-door picture dimmed and softly blurred behind
   the logo, a large QR, the name and the member number (owner, 22 Oct 2026) */
.cb-card { position: relative; isolation: isolate; overflow: hidden; width: 100%; min-height: min(560px, calc(100dvh - 120px)); display: grid; justify-items: center; align-content: space-between; gap: 18px;
  padding: 24px 22px 22px; border: 1px solid rgba(232, 71, 74, 0.4); border-radius: 28px; background: #150a0c; box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08); }
.cb-card::before { content: ""; position: absolute; inset: 0; z-index: -1; background: url("../img/art/member-card-1120.webp") center / cover; }   /* full strength, sharp (owner, 22 Oct 2026) */
.cb-logo { justify-self: start; width: auto; height: 92px; }
.cb-id { display: grid; justify-items: center; gap: 6px; }
.cb-name { font-family: var(--f-display); font-stretch: 125%; font-weight: 800; text-transform: uppercase; font-size: 18px; letter-spacing: 0.04em; color: #fff3e8; text-align: center; text-shadow: 0 1px 8px rgba(10, 6, 7, 0.85); }
.card-box .tb-code { font-size: 20px; text-shadow: 0 1px 8px rgba(10, 6, 7, 0.85); }
.card-box .tb-tip { color: var(--ivory-2); text-shadow: 0 1px 8px rgba(10, 6, 7, 0.85); }
.member-card--live[role="button"] { cursor: pointer; }
.tb-qr svg { display: block; width: 100%; height: auto; }
.tb-name [lang="zh"] { font-family: var(--f-zh-display); font-weight: 900; }

/* ---------- Staff member panel: Rewards waiting to collect and History fold (owner, 19 Oct 2026) ---------- */
.mp-fold { border-top: 1px solid var(--line); }
.mp-fold > summary { display: flex; align-items: center; gap: 10px; min-height: 52px; cursor: pointer; list-style: none; }
.mp-fold > summary::-webkit-details-marker { display: none; }
.mp-fold > summary .mp-title { margin: 0; }
.mp-fold > summary::after { content: ""; margin-left: auto; width: 9px; height: 9px; border: solid var(--ivory-2); border-width: 0 2px 2px 0; rotate: 45deg; translate: 0 -3px; transition: rotate 200ms var(--ease), translate 200ms var(--ease); }
.mp-fold[open] > summary::after { rotate: 225deg; translate: 0 3px; }
.mp-fold[open] > summary { margin-bottom: 6px; }
.mp-fold > form:last-child { padding-bottom: 16px; }   /* Edit member details: room under Save details */
.mp-count:not(:empty) { min-width: 26px; height: 24px; padding: 0 8px; border-radius: var(--r-pill); display: inline-grid; place-items: center; background: var(--crimson-wash-2); color: var(--crimson-hi); font-size: 13px; }
.panel-title .mp-count { vertical-align: middle; font-size: 14px; }
@media (prefers-reduced-motion: reduce) { .mp-fold > summary::after { transition: none; } }

/* ---------- Reports: Points activity with type chips (owner, 19 Oct 2026) ---------- */
.rp-sub { margin: -4px 0 12px; font-size: var(--fs-small); color: var(--ivory-2); }
.rp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.rp-chips button { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 14px; border-radius: var(--r-pill); border: 1px solid var(--line-2); background: var(--bg-2); color: var(--ivory-2); font-size: 13px; font-weight: 600; transition: background 150ms, color 150ms, border-color 150ms; }
.rp-chips button b { font-size: 12px; color: var(--ivory-3); }
.rp-chips button[aria-pressed="true"] { background: var(--ivory); border-color: var(--ivory); color: var(--bg); }
.rp-chips button[aria-pressed="true"] b { color: var(--bg); }
@media (hover: hover) and (pointer: fine) { .rp-chips button[aria-pressed="false"]:hover { border-color: var(--ivory-2); color: var(--ivory); } }
.rp-to { margin: 4px 0 0; }
.rp-to .text-btn { min-height: 44px; }
#code-result:not(:empty) { margin-top: 16px; }   /* the result never touches Scan ticket */
#code-result .msg { margin-bottom: 0; }
.pick-box .rb-msg { padding-inline: 20px; }   /* same left edge as the rows */
/* History: Points | Tickets (owner, 19 Oct 2026) */
.hs-switch { margin-bottom: 12px; }
.tk-hist.is-used small { color: var(--green); }
.tk-hist.is-cancelled .v { text-decoration: line-through; color: var(--ivory-3); }
/* Points activity: a divider row opens each type, and each type keeps its own colour stripe (owner, 19 Oct 2026) */
.rp-table tr.rp-divider th {
  position: static; padding: 18px 14px 8px; text-align: left; background: var(--bg-3, var(--bg-2)); border-bottom: 1px solid var(--line-2);
  font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 12px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory);
}
.rp-table tr.rp-divider th b { margin-left: 10px; padding: 2px 8px; border-radius: var(--r-pill); background: var(--glass-soft); font-size: 12px; color: var(--ivory-2); }
.rp-table tr[class*="k-"] > :first-child { box-shadow: inset 3px 0 0 var(--k, var(--line-2)); }
.rp-table .k-visit { --k: var(--green); }
.rp-table .k-redeem { --k: #c9a227; }
.rp-table .k-used { --k: #4a8fd8; }
.rp-table .k-refund { --k: var(--ivory-3); }
.rp-table .k-adjust { --k: var(--crimson-hi); }
.rp-table .k-rate { --k: var(--ivory-2); }
.rw-group { display: flex; align-items: center; gap: 8px; margin: 0; padding: 20px 0 8px; border-bottom: 1px solid var(--line-2);
  font-family: var(--f-display); font-stretch: 112%; font-weight: 700; font-size: 12px; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ivory); }
.rw-group:first-child { padding-top: 0; }
.staff-page .site-header .brand-text { display: none; }   /* the console bar shows the logo only, and it isn't a link (site.js) */
.staff-page .site-header .brand { cursor: default; }
.hs-hint { margin: 0 0 8px; }
.tk-done { color: var(--green); }
.mp-code-q { margin-top: 4px; }
.tb-top .tb-chip { justify-self: start; margin-top: 4px; }   /* the type and state sit on their own line under the logo */
/* the arrows float above the neighbouring cards: a lift shadow, hover and keyboard focus (owner, 20 Oct 2026) */
.fan-arrow { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5); cursor: pointer; transition: background-color 200ms, color 200ms, opacity 200ms; }
.fan-arrow:focus-visible { outline: 2px solid var(--ivory); outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .fan-arrow:not(:disabled):hover { background: var(--ivory); color: var(--bg); } }
/* Ticket rows on phones: code and details get the full width, Redeem / Cancel sit on their own line below (owner, 20 Oct 2026) */
@media (max-width: 640px) {
  .hist-row:has(.code-sm) { grid-template-columns: 1fr; gap: 6px; }
  .hist-row:has(.code-sm) .act { justify-content: flex-start; margin-top: 4px; }
  .hist-row:has(.code-sm) .v .muted { text-align: left; }
  .hist-row:has(.code-sm) .v { text-align: left; }
}
@media (pointer: coarse) { .rp-chips button { min-height: 44px; } }   /* touch: 44px chips (audit, 20 Oct 2026) */
/* Phones (owner's iPhone test, 20 Oct 2026) */
/* the basket goes to two rows: what's in it on top, Clear and a full-width Get tickets underneath */
@media (max-width: 480px) {
  .basket { flex-wrap: wrap; border-radius: 24px; padding: 14px 14px 12px 18px; row-gap: 10px; }
  .basket-sum { flex: 1 1 100%; }
  .basket-act { flex: 1 1 100%; width: 100%; justify-content: space-between; }
  .basket-act .btn { flex: 1; }
}
/* iPhone Safari draws date boxes at their own width and centres the date: make them a normal field */
.field input[type="date"] { -webkit-appearance: none; appearance: none; display: block; min-width: 0; max-width: 100%; text-align: left; line-height: 48px; }
.field input[type="date"]::-webkit-date-and-time-value { text-align: left; }
/* the hint under a search box, with its Cancel on the right (owner, 20 Oct 2026) */
.search-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 4px; }
.search-foot .hint { flex: 1; min-width: 0; }
.search-foot .text-btn { flex: none; }
/* a Clear link beside a form's main button, and beside the Tickets to redeem box (owner, 20 Oct 2026) */
.mp-acts { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.mp-acts .text-btn, .mp-code-q .text-btn { padding: 0 8px; }
.staff-page .tab .search.mp-code-q { align-items: center; flex-wrap: nowrap; }
.mp-code-q .text-btn { flex: none; }

/* Staff console bars (owner, 20 Oct 2026): the top bar and the phone's bottom tab bar slide away while
   scrolling down and come back on the first scroll up (site.js sets body.bars-hidden with the header) */
.staff-page > .tabs { transition: transform 260ms var(--ease); }
.staff-page.bars-hidden > .tabs { transform: translateY(calc(100% + 16px + env(safe-area-inset-bottom, 0px))); }
/* Back to top sits just above the bottom tab bar, and drops to the corner when the bar slides away */
.staff-page .float-actions { transition: transform 260ms var(--ease); }
@media (max-width: 640px) { .staff-page:not(.bars-hidden) .float-actions { transform: translateY(-86px); } }
@media (prefers-reduced-motion: reduce) { .staff-page > .tabs, .staff-page .float-actions { transition: none; } }
.staff-page .float-top { background: var(--glass-strong); }
.mp-acts-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Beer left over (owner, 20 Oct 2026): cans kept for a member and taken out on a later visit */
.beer-now { margin: 0 0 12px; color: var(--ivory-2); }
.beer-now b { color: var(--ivory); font-size: 18px; }
.beer-seg { border-radius: var(--r-sm); overflow: hidden; min-height: 48px; }
.beer-seg label { flex: 1; width: auto; padding: 0 12px; font-size: 14px; }
.beer-seg label:has(input[value="keep"]:checked) { background: rgba(62, 207, 122, 0.14); color: var(--green); }
.beer-seg label:has(input[value="take"]:checked) { background: var(--crimson-wash-2); color: var(--crimson-hi); }
.beer-seg label:has(input:disabled) { opacity: 0.4; cursor: not-allowed; }
.beer-date { margin: 0; line-height: 48px; color: var(--ivory-2); }
/* Settings: Clear sits right after the main button */
.set-actions .mp-acts { margin-left: 0; }
.rp-h4 { margin: 22px 0 10px; font-size: 13px; font-weight: 600; color: var(--ivory-2); }
/* the one-time link code for a counter member's online sign-up: big enough to read out or copy */
.link-code { display: inline-block; margin: 4px 0; font-size: 20px; letter-spacing: 0.08em; user-select: all; }

/* Voucher artwork (Higgsfield, owner, 22 Oct 2026): gold silk for cash vouchers, red stage light for room packages.
   Only the background changes; a light dark wash and a soft shadow keep every line of text as readable as before. */
.ticket--cash, .ticket--room { background-size: cover; background-position: center; }
.ticket--cash { background-image: linear-gradient(rgba(10, 6, 7, 0.28), rgba(10, 6, 7, 0.42)), url("../img/art/voucher-cash-720.webp"); }
.ticket--room { background-image: linear-gradient(rgba(10, 6, 7, 0.28), rgba(10, 6, 7, 0.42)), url("../img/art/voucher-room-720.webp"); }
:is(.ticket--cash, .ticket--room) :is(.tk-brand, .tk-kind, .tk-name, .tb-name, .tk-code, .tb-code, .tk-hint, .tb-status, .tk-facts) { text-shadow: 0 1px 8px rgba(10, 6, 7, 0.85); }

/* Member card artwork (owner, 22 Oct 2026): palace doors opening onto a stage; big logo top left, name bottom left, QR bottom right */
.member-card--art { background: linear-gradient(90deg, rgba(10, 6, 7, 0.35), transparent 55%), url("../img/art/member-card-1120.webp") center / cover;
  grid-template-columns: 1fr auto; grid-template-rows: 1fr auto; align-content: stretch; column-gap: 16px; }
.member-card--art .mc-logo { grid-column: 1; grid-row: 1; height: clamp(84px, 24vw, 150px); width: auto; align-self: start; filter: drop-shadow(0 0 10px rgba(10, 6, 7, 0.8)); }
.member-card--art .mc-id { grid-column: 1; grid-row: 2; align-self: end; }
.member-card--art .mc-qr { grid-column: 2; grid-row: 2; align-self: end; }
.member-card--art :is(.mc-name, .mc-no) { text-shadow: 0 1px 10px rgba(10, 6, 7, 0.85); }

/* Reward cards to swap points for (owner, 22 Oct 2026): the same artwork as the vouchers they become, kept faint under a dark wash */
.reward.reward--cash, .reward.reward--room { background-size: cover; background-position: center; }   /* .reward twice: beats the fan's .fan .reward background */
.reward.reward--cash { background-image: linear-gradient(rgba(10, 6, 7, 0.7), rgba(10, 6, 7, 0.86)), url("../img/art/voucher-cash-720.webp"); }
.reward.reward--room { background-image: linear-gradient(rgba(10, 6, 7, 0.7), rgba(10, 6, 7, 0.86)), url("../img/art/voucher-room-720.webp"); }
:is(.reward--cash, .reward--room) :is(.rw-kind, .rw-name, .rw-desc, .rw-cost, .rw-state) { text-shadow: 0 1px 8px rgba(10, 6, 7, 0.85); }

/* Glossy vouchers (owner, 22 Oct 2026): a lacquer shine on top and a slow light sweep now and then, both under the text
   and the QR (z-index -1 inside the voucher), so scanning is never affected. The sweep moves by transform only. */
.ticket, .tb-ticket { position: relative; isolation: isolate; }
.ticket::before, .tb-ticket::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; border-radius: inherit;
  background: linear-gradient(165deg, rgba(255, 248, 238, 0.2) 0%, rgba(255, 248, 238, 0.06) 22%, transparent 42%),
              linear-gradient(0deg, rgba(255, 248, 238, 0.05), transparent 30%);
}
.ticket::after, .tb-ticket::after {
  content: ""; position: absolute; top: -10%; bottom: -10%; left: -70%; width: 55%; z-index: -1; pointer-events: none;
  background: linear-gradient(105deg, transparent 20%, rgba(255, 240, 220, 0.2) 45%, rgba(255, 250, 240, 0.42) 50%, rgba(255, 240, 220, 0.2) 55%, transparent 80%);
  transform: translateX(0) skewX(-12deg); animation: gloss 7s ease-in-out 1.2s infinite;
}
/* phones: the vouchers in the list keep the still shine; only a voucher opened big sweeps. Every sweep inside a masked voucher
   made the phone redraw it each frame (lag), and a layer per voucher ran an iPhone out of graphics memory (owner, 22 Oct 2026) */
@media (hover: none) { .ticket::after { animation: none; display: none; } }
.tickets .ticket:nth-child(2n)::after { animation-delay: 2.6s; }
@keyframes gloss { 0%, 62% { transform: translateX(0) skewX(-12deg); } 100% { transform: translateX(420%) skewX(-12deg); } }
@media (prefers-reduced-motion: reduce) { .ticket::after, .tb-ticket::after { animation: none; display: none; } }

/* Glossy member card too (owner, 22 Oct 2026): the same lacquer highlight and slow sweep as the vouchers, under the logo,
   name and QR; the mouse sheen (--sheen, member.js) stays on computers */
.member-card--art::before {
  background: linear-gradient(165deg, rgba(255, 248, 238, 0.18) 0%, rgba(255, 248, 238, 0.05) 22%, transparent 42%),
              linear-gradient(115deg, transparent 30%, rgba(239, 230, 214, 0.14) 45%, transparent 60%);
  background-size: 100% 100%, 250% 100%; background-position: 0 0, var(--sheen, 100%) 0;
}
.member-card--art::after {
  content: ""; position: absolute; top: -10%; bottom: -10%; left: -70%; width: 45%; z-index: -1; pointer-events: none;
  background: linear-gradient(105deg, transparent 20%, rgba(255, 240, 220, 0.2) 45%, rgba(255, 250, 240, 0.4) 50%, rgba(255, 240, 220, 0.2) 55%, transparent 80%);
  transform: translateX(0) skewX(-12deg); animation: gloss 8s ease-in-out 2s infinite;
}
@media (prefers-reduced-motion: reduce) { .member-card--art::after { animation: none; display: none; } }

/* Account folds, open (owner's iPhone, 22 Oct 2026): the bar's lower corners square off and the panel joins flush under it.
   The phone rule that keeps an open bar fully rounded (style.css) is for the Rooms rate table only. The focus ring sits
   inside the bar, so it never overlaps the panel. */
.acct-fold.is-open .fold-bar { border-radius: var(--r-lg) var(--r-lg) 0 0; box-shadow: var(--glass-rim); }
.acct-fold .fold-bar:focus-visible { outline-offset: -3px; }
.acct-fold .fold-body { margin-top: -1px; }
