/* Gym in Progress — Member App (web, mobile-first) */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; min-height: 100%; }
body {
  font-family: var(--font-sans);
  color: var(--fg1);
  background:
    radial-gradient(900px 620px at 50% -8%, rgba(34,212,236,.20), transparent 60%),
    radial-gradient(680px 520px at 92% 104%, rgba(15,116,168,.14), transparent 55%),
    #E3EEF5;
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
#root { min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center; }

.ic { display: inline-flex; line-height: 0; }
.ic svg { width: 1em; height: 1em; stroke-width: 2px; }

/* The bottom tab bar's own height, and the ONE place it is measured. Five rules used to
   hardcode it (the scroll padding, the class-detail CTA bar, the toast at two heights) and the
   toast's copy had already drifted from the other four by omitting the safe-area inset.
   The inset is inside the token: a phone with a home indicator needs the room, a browser
   without one must not be given 34px of empty bar. */
:root { --tabbar-h: calc(68px + env(safe-area-inset-bottom)); }

/* ============ responsive app shell ============ */
/* phone-width column on desktop, full-bleed on phones */
.app {
  position: relative;
  width: 100%;
  max-width: 440px;
  height: 100vh;   /* fallback for browsers without dvh */
  height: 100dvh;
  max-height: 940px;
  background: var(--bg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 40px 90px -30px rgba(11,18,29,.45), 0 0 0 1px rgba(11,18,29,.04);
  border-radius: 34px;
}
@media (max-width: 480px) {
  #root { display: block; }
  .app { max-width: none; height: 100vh; height: 100dvh; max-height: none; border-radius: 0; box-shadow: none; }
}

/* a single screen surface (the scrolling area) */
.screen { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--bg); }
.scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain; padding: 0 18px calc(var(--tabbar-h) + 12px); }
.scroll::-webkit-scrollbar { display: none; }
.scroll.with-top { padding-top: calc(14px + env(safe-area-inset-top)); }

/* ============ headers ============ */
.m-head { display: flex; align-items: center; gap: 12px; padding: 18px 2px 14px; }
.m-head .av { width: 46px; height: 46px; border-radius: 50%; background: linear-gradient(135deg,var(--aqua-400),var(--ocean-500)); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px; flex: none; cursor: pointer; }
.m-head .hi { font-size: 13px; color: var(--fg3); font-weight: 500; }
.m-head .nm { font-family: var(--font-display); font-weight: 600; font-size: 20px; letter-spacing: -.01em; }
.m-head .stack-title { font-family: var(--font-display); font-weight: 600; font-size: 20px; letter-spacing: -.01em; }
.m-head .sub { font-size: 13px; color: var(--fg3); font-weight: 500; }
.m-bell { margin-left: auto; width: 44px; height: 44px; border-radius: 14px; background: #fff; box-shadow: var(--shadow-xs); display: flex; align-items: center; justify-content: center; color: var(--fg2); font-size: 20px; position: relative; border: 0; cursor: pointer; flex: none; }
.m-bell .dot { position: absolute; top: 4px; right: 4px; min-width: 16px; height: 16px; padding: 0 4px; background: var(--danger-500); border-radius: 99px; border: 2px solid #fff; color: #fff; font-size: 9px; font-weight: 800; font-family: var(--font-sans); display: flex; align-items: center; justify-content: center; }

/* back button for pushed screens */
.backbtn { width: 42px; height: 42px; border-radius: 13px; background: #fff; box-shadow: var(--shadow-xs); border: 0; display: flex; align-items: center; justify-content: center; color: var(--fg1); cursor: pointer; flex: none; }
.backbtn:active { transform: scale(.95); }

/* ============ membership card ============ */
.mcard { border-radius: var(--mcard-r, 24px); padding: 20px; position: relative; overflow: hidden; cursor: pointer; transition: transform var(--dur-mid) var(--ease-out), box-shadow var(--dur-mid); }
.mcard:active { transform: scale(.985); }
.mcard .glow { position: absolute; right: -40px; top: -50px; width: 170px; height: 170px; border-radius: 50%; pointer-events: none; }
.mcard .row1 { display: flex; align-items: center; justify-content: space-between; position: relative; }
.mcard .brandmini { display: flex; align-items: center; gap: 8px; }
.mcard .brandmini img { width: 26px; height: 26px; border-radius: 8px; }
.mcard .brandmini span { font-family: var(--font-display); font-weight: 700; font-size: 13px; }
.mcard .plan-pill { font-size: 11px; font-weight: 800; letter-spacing: .04em; padding: 4px 11px; border-radius: 999px; }
/* A lapsed term (lapsedTerm() in components.jsx). Declared with the variant selectors' weight
   so it wins over every `.mcard.v-* .plan-pill` below on all three card styles. */
.mcard.mcard .plan-pill.is-expired { background: var(--danger-50); color: var(--danger-700); border: 1px solid var(--danger-500); }
.mcard .name { font-family: var(--font-display); font-weight: 600; font-size: 23px; margin: 22px 0 2px; position: relative; letter-spacing: -.01em; }
.mcard .meta { display: flex; gap: 22px; margin-top: 14px; position: relative; }
.mcard .meta .k { font-size: 10px; text-transform: uppercase; letter-spacing: .1em; }
.mcard .meta .v { font-family: var(--font-mono); font-weight: 600; font-size: 14px; margin-top: 3px; }
.mcard .tap { position: relative; display: flex; align-items: center; gap: 7px; margin-top: 18px; font-weight: 700; font-size: 14px; padding: 12px; border-radius: 14px; justify-content: center; }

/* --- variant: aurora (navy gradient, default) --- */
.mcard.v-aurora { color: #EAF6FA; background: linear-gradient(140deg,#0B121D 0%,#103a52 55%,#0F74A8 130%); box-shadow: 0 18px 40px rgba(11,18,29,.3); }
.mcard.v-aurora .glow { background: radial-gradient(circle, rgba(34,212,236,.55), transparent 65%); }
.mcard.v-aurora .brandmini b { color: var(--aqua-400); }
.mcard.v-aurora .plan-pill { background: rgba(34,212,236,.2); color: var(--aqua-200); border: 1px solid rgba(34,212,236,.4); }
.mcard.v-aurora .meta .k { color: #9DD0E7; }
.mcard.v-aurora .tap { background: var(--aqua-500); color: var(--fg-on-aqua); }

/* --- variant: aqua (bright, navy ink) --- */
.mcard.v-aqua { color: #07333B; background: linear-gradient(140deg,#5FE6F4 0%,#22D4EC 55%,#15ABC2 130%); box-shadow: 0 18px 40px rgba(34,212,236,.4); }
.mcard.v-aqua .glow { background: radial-gradient(circle, rgba(255,255,255,.6), transparent 65%); }
.mcard.v-aqua .brandmini b { color: #07333B; }
.mcard.v-aqua .plan-pill { background: rgba(7,51,59,.12); color: #07333B; border: 1px solid rgba(7,51,59,.18); }
.mcard.v-aqua .meta .k { color: rgba(7,51,59,.6); }
.mcard.v-aqua .tap { background: #0B121D; color: var(--aqua-300); }

/* --- variant: minimal (white, aqua accent) --- */
.mcard.v-minimal { color: var(--fg1); background: #fff; box-shadow: var(--shadow-md), inset 0 0 0 1px var(--border-subtle); }
.mcard.v-minimal .glow { background: radial-gradient(circle, rgba(34,212,236,.28), transparent 65%); }
.mcard.v-minimal .brandmini b { color: var(--aqua-600); }
.mcard.v-minimal .plan-pill { background: var(--aqua-50); color: var(--aqua-700); border: 1px solid var(--aqua-100); }
.mcard.v-minimal .meta .k { color: var(--fg3); }
.mcard.v-minimal .tap { background: var(--aqua-500); color: var(--fg-on-aqua); }

/* ============ sections ============ */
.sec-h { display: flex; align-items: center; justify-content: space-between; margin: 24px 2px 12px; }
.sec-h .t { font-family: var(--font-display); font-weight: 600; font-size: 16px; letter-spacing: -.01em; }
.sec-h .l { font-size: 12.5px; font-weight: 600; color: var(--ocean-500); cursor: pointer; background: none; border: 0; font-family: var(--font-sans); }
.sec-h .l-static { color: var(--fg3); cursor: default; }

/* ============ stat duo ============ */
/* `auto-fit`, not a hardcoded `1fr 1fr`: the Home entitlement pair drops to ONE tile for a
   pilates-only member (there is no PT for them to hold), and a fixed two-column grid would
   leave the right half as a visible hole. With two children this resolves to exactly the same
   two equal columns it always did, so nothing else that uses .duo changes. minmax(0, 1fr)
   rather than plain 1fr so a long label cannot push a column past its share. */
.duo { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 12px; }
.scard { background: #fff; border-radius: 18px; padding: 15px; box-shadow: var(--shadow-xs); }
.ic-box { width: 34px; height: 34px; border-radius: 11px; display: flex; align-items: center; justify-content: center; font-size: 18px; }
.scard .num { font-family: var(--font-mono); font-weight: 600; font-size: 24px; margin-top: 11px; }
.scard .lbl { font-size: 12px; color: var(--fg3); font-weight: 500; }
.scard .sub { font-size: 11px; color: var(--fg3); margin-top: 4px; line-height: 1.35; }
/* .scard is also rendered as a <button> (the Home package tiles), which brings its own border,
   font and centred text. Reset those rather than forking the class — the tile is the same card,
   it just happens to navigate. */
button.scard { border: 0; width: 100%; text-align: left; font-family: var(--font-sans); cursor: pointer; display: block; }
button.scard:active { transform: scale(.99); }
/* A category the member holds nothing in. Recessed rather than hidden — the tile still states
   what it is and still navigates, it just has no number to show. */
button.scard.is-empty { background: var(--surface-2); box-shadow: none; }

/* ============ class card ============ */
.clscard { background: #fff; border-radius: 18px; padding: 15px; box-shadow: var(--shadow-xs); display: flex; align-items: center; gap: 13px; margin-bottom: 10px; cursor: pointer; transition: box-shadow var(--dur-mid), transform var(--dur-fast); }
.clscard:active { transform: scale(.99); }
/* 56px, not 50px: the status chip lives under the time and "Booked" needs the
   extra width. The chip vacated the right-hand side, so the body nets out even. */
.clscard .when { text-align: center; flex: none; width: 56px; }
.clscard .when .h { font-family: var(--font-mono); font-weight: 600; font-size: 15px; color: var(--fg1); }
.clscard .when .ap { font-size: 10px; color: var(--fg3); text-transform: uppercase; }
.clscard .vbar { width: 3px; align-self: stretch; border-radius: 2px; flex: none; }
.clscard .body { flex: 1; min-width: 0; }
/* the chip + button sit beside the name, so on a 375px phone there isn't room
   for a long title — truncate rather than wrap to a second line, matching
   .cls .nm in the admin dashboard */
/* The name and its category tag share one line; only the NAME may shrink, so a long class
   title truncates instead of squeezing the tag out of existence. */
.clscard .nmrow { display: flex; align-items: center; gap: 6px; min-width: 0; }
.clscard .nm { font-weight: 700; font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.clscard .nmrow .plan-tag { font-size: 9.5px; padding: 3px 7px; }
.clscard .coach { font-size: 12px; color: var(--fg3); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.clscard .cap { font-size: 11px; color: var(--fg3); margin-top: 5px; display: flex; align-items: center; gap: 5px; }
.mini-btn { flex: none; border: 0; font-family: var(--font-sans); font-weight: 700; font-size: 12.5px; padding: 9px 15px; border-radius: 11px; cursor: pointer; transition: transform var(--dur-fast); }
.mini-btn:active { transform: scale(.95); }
/* the row only opens the detail screen, so its button stays secondary — the
   real commit action lives on the detail screen's sticky CTA */
.mb-open { background: var(--surface-2); color: var(--ocean-600); }
.mb-open:hover { background: var(--ocean-50); }

/* Whole-card state, encoded in the card's FILL for both cases so the two read
   as one system: green = yours, grey = unavailable. Fill is pre-attentive —
   caught in peripheral vision while scrolling — where a coloured drop shadow
   is too ambient to register, and a ring reads as selection/focus. Shadow is
   already spoken for in this app: --glow-aqua marks the primary CTA. */
.clscard.is-booked { background: var(--success-50); }
/* full flattens to a grey surface. --surface-3, not --surface-2: the latter is
   #F1F5F9 against a #F4F8FB page background and the card would disappear. */
.clscard.is-full { background: var(--surface-3); box-shadow: none; }
/* A 1-on-1 is YOURS, like a booked class — but not the same fact, and the fill has to say
   which without adding a third idea to a two-colour system. Aqua is the brand accent and
   already reads as "this one is about you" throughout the app, so it lands as a sibling of the
   green rather than a new category. Same tint depth as --success-50 so neither shouts over the
   other, and the two never sit adjacent asking to be compared: a private session is never
   bookable and a class is never private. */
.clscard.is-private { background: var(--aqua-50); }
/* the button's own --surface-2 fill goes muddy on all three tints */
.clscard.is-booked .mb-open,
.clscard.is-private .mb-open,
.clscard.is-full .mb-open { background: #fff; }
/* on a tinted card the fill already carries the emphasis, so the chip sheds its
   pill and reads as plain coloured text — the pill only earned its keep when it
   had to stand out on white */
.clscard.is-booked .rstat,
.clscard.is-private .rstat,
.clscard.is-full .rstat { background: none; padding-left: 0; padding-right: 0; }

/* status chip sits under the time, in its own column. No icon — the fill colour
   is the signal (green = booked, aqua = your 1-on-1, grey = full) and a glyph at
   this size only crowds it. */
.rstat { display: block; margin-top: 6px; padding: 3px 2px; border-radius: 999px; font-size: 9.5px; font-weight: 800; letter-spacing: .02em; white-space: nowrap; }
.rs-booked { background: var(--success-50); color: var(--success-700); }
.rs-private { background: var(--aqua-50); color: var(--aqua-700); }
.rs-full { background: var(--surface-2); color: var(--fg3); }

/* ============ class type card (Jenis) ============ */
.typecard { display: flex; align-items: center; gap: 12px; width: 100%; background: #fff; border: 0; border-radius: 18px; padding: 15px; box-shadow: var(--shadow-xs); margin-bottom: 10px; cursor: pointer; font-family: var(--font-sans); text-align: left; }
.typecard:active { transform: scale(.99); }
.typecard .vbar { width: 3px; align-self: stretch; border-radius: 2px; flex: none; }
.typecard .tbody { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.typecard .nm { font-weight: 700; font-size: 14.5px; color: var(--fg1); }
.typecard .meta { font-size: 12px; color: var(--fg3); margin-top: 2px; }
.typecard .n { font-size: 11.5px; color: var(--ocean-600); font-weight: 600; margin-top: 5px; }
.plan-tag { flex: none; font-size: 10.5px; font-weight: 700; padding: 4px 9px; border-radius: 999px; letter-spacing: .02em; }
.pt-gym { background: var(--ocean-50); color: var(--ocean-600); }
.pt-pilates { background: var(--aqua-50); color: var(--aqua-700); }
/* A class in BOTH buckets. Its own token pair rather than one of the two above — reusing
   .pt-gym's colour would make a combined class indistinguishable from a gym-only one at a
   glance, which is the whole thing the tag exists to say. */
.pt-both { background: var(--ocean-50); color: var(--aqua-700); }
/* A coach's DISCIPLINE (Private Trainer / Gym Instructor / Pilates Instructor). Deliberately
   quieter than .plan-tag: it appears once per row rather than alongside a name, and a coach may
   hold two, so it has to read as a line of text that happens to be tinted, not as two badges. */
.role-pill { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .01em;
  color: var(--ocean-700); background: var(--ocean-50); padding: 3px 9px; border-radius: 999px; }

/* ============ quick actions ============ */
.qa { display: grid; grid-template-columns: repeat(4,1fr); gap: 10px; }
.qa .item { background: #fff; border-radius: 16px; padding: 13px 6px; text-align: center; box-shadow: var(--shadow-xs); cursor: pointer; border: 0; font-family: var(--font-sans); transition: transform var(--dur-fast); }
.qa .item:active { transform: scale(.96); }
.qa .item .b { width: 40px; height: 40px; border-radius: 13px; margin: 0 auto 7px; display: flex; align-items: center; justify-content: center; font-size: 19px; }
.qa .item .t { font-size: 11px; font-weight: 600; color: var(--fg2); }

/* ============ weekly bars ============ */
.wk { display: flex; align-items: stretch; gap: 9px; height: 120px; padding-top: 6px; }
.wk .col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; }
/* the bar's height is a % — it needs a track with a definite height to resolve
   against, and one that excludes the day label so 100% isn't clipped by it */
.wk .track { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.wk .bar { width: 100%; border-radius: 6px 6px 3px 3px; transition: height var(--dur-slow) var(--ease-out); }
.wk .d { font-size: 10.5px; color: var(--fg3); font-weight: 600; }

/* ============ date strip ============ */
.dates { display: flex; gap: 8px; margin-bottom: 6px; overflow-x: auto; padding: 4px 0; }
.dates::-webkit-scrollbar { display: none; }
.dates .day { flex: none; width: 48px; text-align: center; padding: 10px 0; border-radius: 14px; background: #fff; box-shadow: var(--shadow-xs); cursor: pointer; border: 0; font-family: var(--font-sans); transition: transform var(--dur-fast); }
.dates .day:active { transform: scale(.95); }
.dates .day .dd { font-family: var(--font-mono); font-weight: 600; font-size: 16px; color: var(--fg1); }
.dates .day .dn { font-size: 10.5px; color: var(--fg3); text-transform: uppercase; margin-top: 2px; }
.dates .day.sel { background: var(--aqua-500); }
.dates .day.sel .dd, .dates .day.sel .dn { color: var(--fg-on-aqua); }

/* ============ generic pieces ============ */
.chip-row { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 4px; }
.chip-row::-webkit-scrollbar { display: none; }
.chip { flex: none; padding: 8px 14px; border-radius: 999px; background: #fff; box-shadow: var(--shadow-xs); font-size: 12.5px; font-weight: 600; color: var(--fg2); cursor: pointer; border: 0; font-family: var(--font-sans); }
.chip.on { background: var(--ocean-500); color: #fff; }

.cta { width: 100%; border: 0; border-radius: 16px; padding: 16px; font-family: var(--font-sans); font-weight: 700; font-size: 15px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: transform var(--dur-fast), filter var(--dur-fast); }
.cta:active { transform: scale(.98); }
.cta-primary { background: var(--fab, var(--aqua-500)); color: var(--fab-ink, var(--fg-on-aqua)); box-shadow: var(--glow-aqua); }
.cta-ocean { background: var(--ocean-500); color: #fff; }
.cta-ghost { background: #fff; color: var(--fg1); box-shadow: inset 0 0 0 1px var(--border); }
.cta-danger { background: var(--danger-50); color: var(--danger-700); }

/* list rows (profile / settings) */
.list { background: #fff; border-radius: 18px; box-shadow: var(--shadow-xs); overflow: hidden; }
.row { display: flex; align-items: center; gap: 13px; padding: 14px 16px; cursor: pointer; border: 0; background: none; width: 100%; font-family: var(--font-sans); text-align: left; transition: background var(--dur-fast); }
.row:not(:last-child) { box-shadow: inset 0 -1px 0 var(--border-subtle); }
.row:active { background: var(--surface-2); }
.row .ic-box { flex: none; }
.row .rt { flex: 1; min-width: 0; }
.row .rt .a { font-size: 14.5px; font-weight: 600; color: var(--fg1); }
.row .rt .b { font-size: 12px; color: var(--fg3); margin-top: 1px; }
.row .rv { font-size: 13px; color: var(--fg3); font-weight: 600; font-family: var(--font-mono); }

/* notification rows */
.note { display: flex; gap: 13px; padding: 15px 16px; background: #fff; border-radius: 16px; box-shadow: var(--shadow-xs); margin-bottom: 10px; position: relative; }
.note.unread { box-shadow: var(--shadow-xs), inset 3px 0 0 var(--aqua-500); }
.note .nbody { flex: 1; min-width: 0; }
.note .ntitle { font-size: 14px; font-weight: 700; color: var(--fg1); }
.note .ntext { font-size: 12.5px; color: var(--fg2); margin-top: 3px; line-height: 1.5; }
.note .ntime { font-size: 11px; color: var(--fg3); margin-top: 6px; font-family: var(--font-mono); }

/* invoice / billing */
.invoice { background: #fff; border-radius: 20px; padding: 18px; box-shadow: var(--shadow-sm); }
.invoice.due { background: linear-gradient(140deg,#0B121D,#103a52); color: #EAF6FA; box-shadow: 0 18px 40px rgba(11,18,29,.28); }
.pay-row { display: flex; align-items: center; gap: 13px; padding: 13px 0; }
.pay-row:not(:last-child) { box-shadow: inset 0 -1px 0 var(--border-subtle); }

/* generic card */
.card { background: #fff; border-radius: 18px; padding: 16px; box-shadow: var(--shadow-xs); }

/* ============ bottom tab bar ============ */
.tabbar { position: absolute; left: 0; right: 0; bottom: 0; height: var(--tabbar-h); padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); background: rgba(255,255,255,.92); backdrop-filter: blur(14px); border-top: 1px solid var(--border-subtle); display: flex; align-items: center; justify-content: space-between; z-index: 30; }
.tab { display: flex; flex-direction: column; align-items: center; gap: 3px; color: var(--fg3); font-size: 10px; font-weight: 600; cursor: pointer; background: none; border: 0; flex: 1; font-family: var(--font-sans); transition: color var(--dur-fast); }
.tab .ic { font-size: 23px; }
.tab.active { color: var(--ocean-600); }
.tab.active .ic { color: var(--aqua-600); }
.tab-scan { flex: none; }
.tab-scan .b { width: 58px; height: 58px; border-radius: 20px; background: var(--fab, var(--aqua-500)); color: var(--fab-ink, var(--fg-on-aqua)); display: flex; align-items: center; justify-content: center; font-size: 26px; box-shadow: 0 8px 22px rgba(34,212,236,calc(.18 + var(--glow-a, .5))); margin-top: -22px; transition: transform var(--dur-fast); }
.tab-scan:active .b { transform: scale(.93); }

/* ============ QR sheet ============ */
.sheet-back { position: absolute; inset: 0; background: rgba(11,18,29,.55); backdrop-filter: blur(3px); z-index: 40; display: flex; align-items: flex-end; opacity: 0; pointer-events: none; transition: opacity var(--dur-mid); }
.sheet-back.show { opacity: 1; pointer-events: auto; }
.sheet { background: #fff; width: 100%; border-radius: 28px 28px 0 0; padding: 14px 22px calc(34px + env(safe-area-inset-bottom)); transform: translateY(100%); transition: transform var(--dur-slow) var(--ease-out); }
.sheet-back.show .sheet { transform: translateY(0); }
.grab { width: 40px; height: 5px; border-radius: 99px; background: var(--neutral-200); margin: 0 auto 18px; }
.qr-wrap { background: #fff; border: 2px solid var(--neutral-100); border-radius: 22px; padding: 18px; box-shadow: var(--glow-aqua); width: max-content; margin: 6px auto 4px; transition: transform var(--dur-slow) var(--ease-spring); }
.qr { display: grid; gap: 0; }
.qr i { display: block; }

/* check-in success overlay */
.success-back { position: absolute; inset: 0; background: rgba(11,18,29,.55); backdrop-filter: blur(3px); z-index: 50; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity var(--dur-mid); }
.success-back.show { opacity: 1; pointer-events: auto; }
.success-card { background: #fff; border-radius: 26px; padding: 32px 28px; width: 280px; text-align: center; transform: scale(.85); transition: transform var(--dur-slow) var(--ease-spring); }
.success-back.show .success-card { transform: scale(1); }
.success-ring { width: 92px; height: 92px; border-radius: 50%; margin: 0 auto 18px; background: var(--success-50); display: flex; align-items: center; justify-content: center; color: var(--success-500); position: relative; }
.success-ring::after { content: ""; position: absolute; inset: -8px; border-radius: 50%; border: 2px solid var(--success-500); opacity: .25; }

/* screen transition (push) */
.screen-enter { animation: slideIn var(--dur-slow) var(--ease-out); }
@keyframes slideIn { from { transform: translateY(10px); } to { transform: translateY(0); } }
.fade-enter { animation: fadeIn var(--dur-mid) var(--ease-out); }
@keyframes fadeIn { from { transform: translateY(7px); } to { transform: translateY(0); } }

/* toast */
.toast { position: absolute; left: 50%; bottom: calc(var(--tabbar-h) + 20px); transform: translateX(-50%); background: var(--neutral-900); color: #fff; padding: 11px 17px; border-radius: 18px; font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; box-shadow: var(--shadow-lg); z-index: 45; white-space: normal; text-align: center; line-height: 1.35; pointer-events: none; transition: opacity var(--dur-mid), transform var(--dur-mid) var(--ease-out);  max-width: 90%; width: max-content; box-sizing: border-box;}

/* ============ Fitness tracker (home section + report) ============ */
.ft-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
.ft-tile { background: var(--surface-2); border-radius: 14px; padding: 13px; }
.ft-tile .k { font-size: 11px; color: var(--fg3); font-weight: 600; display: flex; align-items: center; gap: 6px; }
.ft-tile .v { font-family: var(--font-mono); font-weight: 600; font-size: 21px; color: var(--fg1); margin-top: 7px; }
.ft-tile .v small { font-size: 11px; color: var(--fg3); font-weight: 500; margin-left: 2px; }
.ft-tile .u { font-size: 11px; font-weight: 700; margin-top: 3px; }

/* parameter bar gauge */
.pbar { margin-bottom: 15px; }
.pbar:last-child { margin-bottom: 0; }
.pbar .ptop { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; gap: 10px; }
.pbar .pnm { font-size: 13px; font-weight: 600; color: var(--fg1); }
.pbar .pnm span { color: var(--fg3); font-weight: 500; font-size: 11px; }
.pbar .pval { font-family: var(--font-mono); font-weight: 600; font-size: 13px; color: var(--fg1); display: flex; align-items: center; gap: 7px; }
.ptrack { position: relative; height: 8px; border-radius: 999px; background: var(--neutral-100); }
.pnormal { position: absolute; top: 0; bottom: 0; background: var(--aqua-100); }
.pmark { position: absolute; top: 50%; width: 14px; height: 14px; border-radius: 50%; transform: translate(-50%,-50%); border: 2.5px solid #fff; box-shadow: var(--shadow-sm); }
.prange { font-family: var(--font-mono); font-size: 10.5px; color: var(--fg3); margin-top: 6px; }
.pdelta { font-size: 11px; font-weight: 800; display: inline-flex; align-items: center; gap: 2px; padding: 2px 6px; border-radius: 999px; }
.pdelta.up, .pdelta.good { background: var(--success-50); color: var(--success-700); }
.pdelta.down, .pdelta.bad { background: var(--danger-50); color: var(--danger-700); }
.pdelta.flat { background: var(--surface-2, #eef2f5); color: var(--fg3); }

/* report subject hero */
.bc-hero { border-radius: 20px; padding: 18px; color: #EAF6FA; background: linear-gradient(140deg,#0B121D,#103a52 60%,#0F74A8 140%); position: relative; overflow: hidden; }
.bc-hero .hglow { position: absolute; right: -40px; top: -50px; width: 160px; height: 160px; border-radius: 50%; background: radial-gradient(circle, rgba(34,212,236,.5), transparent 65%); }
.bc-hero .top { display: flex; align-items: center; gap: 12px; position: relative; }
.bc-hero .av { width: 46px; height: 46px; border-radius: 13px; background: linear-gradient(135deg,var(--aqua-400),var(--ocean-500)); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 16px; color: #fff; flex: none; }
.bc-hero .nm { font-family: var(--font-display); font-weight: 600; font-size: 18px; color: #fff; }
.bc-hero .mt { font-size: 12px; color: #9DD0E7; margin-top: 2px; }
.bc-hero .meta { display: flex; gap: 20px; margin-top: 16px; position: relative; flex-wrap: wrap; }
.bc-hero .meta .k { font-size: 10px; text-transform: uppercase; letter-spacing: .09em; color: #7FB6D6; }
.bc-hero .meta .v { font-family: var(--font-mono); font-weight: 600; font-size: 14px; color: #EAF6FA; margin-top: 3px; }

/* body shape matrix */
.bcmatrix { display: grid; grid-template-columns: repeat(4,1fr); gap: 5px; }
.bcm { background: var(--surface-2); border-radius: 9px; padding: 7px 3px; text-align: center; font-size: 9.5px; font-weight: 600; color: var(--fg2); min-height: 46px; display: flex; align-items: center; justify-content: center; line-height: 1.2; position: relative; }
.bcm.ideal { background: var(--aqua-50); color: var(--ocean-700); box-shadow: inset 0 0 0 1.5px var(--aqua-300); }
.bcm.current { background: linear-gradient(145deg,var(--aqua-400),var(--ocean-500)); color: #fff; box-shadow: 0 5px 13px rgba(34,212,236,.4); }
.bcm .pin { position: absolute; top: -6px; right: -6px; width: 19px; height: 19px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-sm); display: flex; align-items: center; justify-content: center; }

/* zone gauge (visceral fat) */
.zg .ztr { position: relative; height: 10px; border-radius: 999px; display: flex; overflow: visible; }
.zg .ztr .sg { height: 100%; }
.zg .ztr .sg:first-child { border-radius: 999px 0 0 999px; }
.zg .ztr .sg:last-child { border-radius: 0 999px 999px 0; }
.zg .zmk { position: absolute; top: 50%; width: 16px; height: 16px; border-radius: 50%; transform: translate(-50%,-50%); background: #fff; border: 3px solid var(--ocean-500); box-shadow: var(--shadow-sm); }
.zg .zlb { display: flex; justify-content: space-between; font-size: 10px; color: var(--fg3); margin-top: 7px; text-transform: uppercase; letter-spacing: .04em; }

/* risk ring */
.bc-ring { width: 122px; height: 122px; border-radius: 50%; margin: 4px auto 2px; display: flex; align-items: center; justify-content: center; }
.bc-ring .in { width: 88px; height: 88px; border-radius: 50%; background: #fff; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: var(--shadow-sm); }
.bc-ring .in .lv { font-family: var(--font-display); font-weight: 600; font-size: 19px; }
.bc-ring .in .sb { font-size: 9.5px; color: var(--fg3); text-transform: uppercase; letter-spacing: .08em; margin-top: 2px; }

/* segmental */
.segseg { display: flex; gap: 6px; background: var(--surface-2); border-radius: 12px; padding: 4px; margin-bottom: 16px; }
.segseg button { flex: 1; border: 0; background: none; font-family: var(--font-sans); font-weight: 600; font-size: 13px; color: var(--fg3); padding: 9px; border-radius: 9px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 6px; }
.segseg button.on { background: #fff; color: var(--ocean-700); box-shadow: var(--shadow-xs); }
.bmap { position: relative; width: 188px; height: 210px; margin: 4px auto 0; }
.bmap .part { position: absolute; }
.bm-head { width: 32px; height: 32px; border-radius: 50%; left: 50%; top: 0; transform: translateX(-50%); }
.bm-tr { width: 60px; height: 74px; border-radius: 15px; left: 50%; top: 38px; transform: translateX(-50%); }
.bm-la { width: 18px; height: 70px; border-radius: 11px; left: 40px; top: 44px; }
.bm-ra { width: 18px; height: 70px; border-radius: 11px; right: 40px; top: 44px; }
.bm-ll { width: 24px; height: 76px; border-radius: 11px; left: 70px; top: 122px; }
.bm-rl { width: 24px; height: 76px; border-radius: 11px; right: 70px; top: 122px; }
.seg-tag { position: absolute; background: #fff; border-radius: 9px; box-shadow: var(--shadow-sm); padding: 5px 8px; font-size: 10px; line-height: 1.3; white-space: nowrap; }
.seg-tag .sv { font-family: var(--font-mono); font-weight: 600; font-size: 11.5px; color: var(--fg1); }
.seg-tag .ss { font-weight: 700; font-size: 9px; }

/* composition rows */
.bcrow { display: flex; align-items: center; gap: 10px; padding: 12px 0; }
.bcrow:not(:last-child) { box-shadow: inset 0 -1px 0 var(--border-subtle); }
.bcrow .bk { flex: 1; font-size: 13.5px; font-weight: 600; color: var(--fg1); }
.bcrow .bv { font-family: var(--font-mono); font-weight: 600; font-size: 14px; color: var(--fg1); min-width: 46px; text-align: right; }
.bcrow .brn { font-family: var(--font-mono); font-size: 11px; color: var(--fg3); min-width: 74px; text-align: right; }
.bbadge { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; font-weight: 700; padding: 3px 8px; border-radius: 999px; }
.bbadge .d { width: 5px; height: 5px; border-radius: 50%; }
.bb-normal { background: var(--success-50); color: var(--success-700); } .bb-normal .d { background: var(--success-500); }
.bb-low { background: var(--warning-50); color: var(--warning-700); } .bb-low .d { background: var(--warning-500); }
.bb-high { background: var(--danger-50); color: var(--danger-700); } .bb-high .d { background: var(--danger-500); }

/* trend (member) */
.tr-block { padding: 14px 0; }
.tr-block:not(:last-child) { box-shadow: inset 0 -1px 0 var(--border-subtle); }
.tr-block .th { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 6px; }
.tr-block .tnm { font-size: 13.5px; font-weight: 600; color: var(--fg1); }
.tr-block .tnm span { font-size: 11px; color: var(--fg3); font-weight: 500; }
.tr-block .tnow { font-family: var(--font-mono); font-weight: 600; font-size: 17px; }
.tr-dates { display: grid; grid-template-columns: repeat(8,1fr); margin-top: 8px; }
.tr-dates span { font-size: 9px; color: var(--fg3); text-align: center; font-family: var(--font-mono); }

/* advice + duo cards on report */
.bc-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.advrow { display: flex; align-items: center; gap: 11px; padding: 12px 0; }
.advrow:not(:last-child) { box-shadow: inset 0 -1px 0 var(--border-subtle); }
.advrow .ab { width: 36px; height: 36px; border-radius: 11px; display: flex; align-items: center; justify-content: center; flex: none; }
.advrow .at { flex: 1; }
.advrow .at .a { font-size: 13px; font-weight: 600; color: var(--fg1); }
.advrow .at .b { font-size: 11px; color: var(--fg3); }
.advrow .amt { font-family: var(--font-mono); font-weight: 700; font-size: 16px; }

/* ============ class detail ============ */
.scroll.with-cta { padding-bottom: calc(var(--tabbar-h) + 78px); }
.cd-hero { position: relative; overflow: hidden; padding: calc(18px + env(safe-area-inset-top)) 18px 44px; color: #EAF6FA; background: linear-gradient(140deg,#0B121D 0%,#103a52 55%,#0F74A8 140%); }
.cd-hero .hglow { position: absolute; right: -50px; top: -60px; width: 200px; height: 200px; border-radius: 50%; background: radial-gradient(circle, rgba(34,212,236,.5), transparent 65%); }
.cd-back { position: relative; width: 40px; height: 40px; border-radius: 13px; border: 0; background: rgba(255,255,255,.14); backdrop-filter: blur(8px); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cd-hero-in { position: relative; margin-top: 26px; }
.cd-level { display: inline-block; font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; padding: 4px 10px; border-radius: 999px; background: rgba(34,212,236,.2); color: var(--aqua-200); border: 1px solid rgba(34,212,236,.4); }
.cd-title { font-family: var(--font-display); font-weight: 600; font-size: 27px; letter-spacing: -.02em; margin-top: 10px; line-height: 1.15; }
.cd-pad { padding: 0 18px; }
.cd-info { background: #fff; border-radius: 18px; padding: 14px 16px; box-shadow: var(--shadow-md); margin-top: -30px; position: relative; display: flex; flex-direction: column; gap: 11px; }
.cd-info .r { display: flex; align-items: center; gap: 11px; font-size: 13.5px; color: var(--fg1); font-weight: 500; }
.cd-body { font-size: 13.5px; line-height: 1.6; color: var(--fg2); margin: 0 2px; }
.cd-body.clamp { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.cd-more { border: 0; background: none; padding: 6px 2px 0; font-family: var(--font-sans); font-size: 12.5px; font-weight: 700; color: var(--ocean-500); cursor: pointer; text-decoration: underline; }
.cd-sticky { position: absolute; left: 0; right: 0; bottom: var(--tabbar-h); display: flex; align-items: center; gap: 12px; padding: 12px 18px; background: rgba(255,255,255,.94); backdrop-filter: blur(14px); border-top: 1px solid var(--border-subtle); z-index: 25; }
.cd-sticky .cap { flex: none; max-width: 42%; font-size: 12px; font-weight: 600; color: var(--fg2); line-height: 1.3; }
/* Two buttons (confirming a cancel): the caption takes its own line so neither button wraps. */
.cd-sticky.has-secondary { flex-wrap: wrap; row-gap: 8px; }
.cd-sticky.has-secondary .cap { flex: 1 1 100%; max-width: none; }
/* the toast's default position (--tabbar-h + 20px) lands inside the sticky bar's own band,
   and its z-index:45 paints over the button — lift it clear on this screen only */
.toast-raised { bottom: calc(var(--tabbar-h) + 78px + 12px); }
/* A toast reporting a FAILURE: the danger colour instead of the neutral one, so it never reads as
   the green-ticked success it used to be drawn as. */
.toast-error { background: var(--danger-700); }

/* ============ plan block ============ */
.pb-banner { display: flex; align-items: flex-start; gap: 10px; border-radius: 16px; padding: 14px; font-size: 13px; line-height: 1.45; font-weight: 500; }
.pb-banner b { font-weight: 700; }
.pb-ok { background: var(--success-50); color: var(--success-700); }
.pb-credit { background: var(--aqua-50); color: var(--aqua-800); flex-direction: column; gap: 10px; }
.pb-warn { background: var(--warning-50); color: var(--warning-700); }
.pb-none { background: var(--ocean-50); color: var(--ocean-700); }
.pb-bar { height: 7px; border-radius: 99px; background: rgba(255,255,255,.65); overflow: hidden; width: 100%; }
.pb-bar i { display: block; height: 100%; border-radius: 99px; background: linear-gradient(90deg,var(--aqua-400),var(--ocean-500)); }
.plan-opt { display: flex; gap: 12px; width: 100%; text-align: left; background: #fff; border: 1.5px solid var(--border-subtle); border-radius: 16px; padding: 14px; margin-bottom: 10px; cursor: pointer; font-family: var(--font-sans); }
.plan-opt.on { border-color: var(--aqua-500); box-shadow: 0 0 0 3px var(--ring, rgba(34,212,236,.18)); }
.plan-opt .radio { flex: none; width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--border-strong); margin-top: 2px; }
.plan-opt.on .radio { border-color: var(--aqua-500); background: radial-gradient(circle, var(--aqua-500) 42%, transparent 46%); }
.po-body { flex: 1; display: flex; flex-direction: column; gap: 5px; }
.po-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 14.5px; color: var(--fg1); }
.po-price { font-family: var(--font-mono); font-weight: 600; font-size: 13.5px; color: var(--ocean-600); }
.po-b { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fg3); font-weight: 500; }

/* ============ trainer picker ============ */
.trn-row { display: flex; gap: 10px; }
.trn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; background: #fff; border: 1.5px solid var(--border-subtle); border-radius: 16px; padding: 14px 10px; cursor: pointer; font-family: var(--font-sans); }
.trn.on { border-color: var(--aqua-500); box-shadow: 0 0 0 3px rgba(34,212,236,.18); }
.trn .av { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg,var(--aqua-400),var(--ocean-500)); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 15px; }
.trn .nm { font-size: 13px; font-weight: 700; color: var(--fg1); margin-top: 4px; }
.trn .sp { font-size: 11px; color: var(--fg3); text-align: center; line-height: 1.3; }

/* ============ studio map ============ */
.smap { padding: 16px 14px; }
.smap-front { text-align: center; font-size: 9.5px; font-weight: 800; letter-spacing: .1em; color: var(--fg3); padding-bottom: 10px; border-bottom: 2px solid var(--aqua-200); margin-bottom: 14px; }
.smap-mid { display: flex; align-items: center; gap: 10px; }
.smap-side { flex: none; width: 34px; font-size: 8.5px; font-weight: 700; color: var(--fg3); text-transform: uppercase; letter-spacing: .04em; text-align: center; line-height: 1.25; }
.smap-grid { flex: 1; display: grid; gap: 8px; }
.smap-grid i { aspect-ratio: 1.6; border-radius: 6px; border: 1.5px solid var(--aqua-200); background: #fff; }
.smap-grid i.on { background: var(--aqua-500); border-color: var(--aqua-500); }
.smap-legend { display: flex; gap: 16px; justify-content: center; margin-top: 14px; font-size: 11px; color: var(--fg3); font-weight: 600; }
.smap-legend span { display: flex; align-items: center; gap: 6px; }
.smap-legend i { width: 14px; height: 10px; border-radius: 4px; border: 1.5px solid var(--aqua-200); background: #fff; }
.smap-legend i.on { background: var(--aqua-500); border-color: var(--aqua-500); }

/* ============ bring / venue / policy ============ */
.bring { display: flex; flex-wrap: wrap; gap: 9px; }
.bchip { display: inline-flex; align-items: center; gap: 6px; background: #fff; border-radius: 12px; padding: 11px 14px; box-shadow: var(--shadow-xs); font-size: 13px; font-weight: 600; color: var(--fg1); }
.bchip .ic { color: var(--success-500); }
.vmap { display: block; width: 100%; height: 132px; }
.pol-cut { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700; color: var(--warning-700); background: var(--warning-50); border-radius: 11px; padding: 10px 12px; margin-bottom: 12px; }

/* ============ sign-in gate ============ */
/* Reached only when a server answered 401 — see screen-signin.jsx. Fills the .app frame
   rather than sitting inside .scroll: there is no tab bar behind it to leave room for. */
.signin-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 28px 22px calc(28px + env(safe-area-inset-bottom)); background: linear-gradient(170deg, var(--aqua-50), var(--surface-1) 46%); }
.signin-card { width: 100%; max-width: 380px; background: #fff; border-radius: 22px; padding: 26px 22px 20px; box-shadow: var(--shadow-md); display: flex; flex-direction: column; }
.signin-brand { display: flex; flex-direction: column; align-items: center; gap: 11px; margin-bottom: 22px; }
.signin-brand img { width: 54px; height: 54px; border-radius: 16px; box-shadow: var(--shadow-sm); }
.signin-brand span { font-family: var(--font-display); font-weight: 500; font-size: 15px; color: var(--fg2); }
.signin-brand b { color: var(--aqua-600, var(--aqua-500)); font-weight: 600; }
.signin-splash img { border-radius: 17px; }
.signin-ttl { font-family: var(--font-display); font-weight: 600; font-size: 21px; color: var(--fg1); }
.signin-sub { font-size: 13px; color: var(--fg3); margin-top: 5px; line-height: 1.5; }
/* The development OTP note. Deliberately NOT styled like an error or a success — it is neither,
   it is scaffolding, and it should read as obviously not part of the product. Full width and
   tappable because its job is to fill the code field in one touch. It can only ever render when
   the server echoed a code back, which it does only in local/testing. */
.signin-dev {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  font-size: 12.5px; font-weight: 600; line-height: 1.4;
  color: var(--fg2); background: var(--surface-2);
  border: 1px dashed var(--fg3); border-radius: 11px;
  padding: 10px 12px; margin-bottom: 14px; cursor: pointer;
}
.signin-dev b { font-family: var(--font-mono); font-size: 14px; color: var(--fg1); letter-spacing: .06em; }
.signin-dev:active { background: var(--surface-3); }

.signin-err { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--danger-700); background: var(--danger-50); border-radius: 11px; padding: 10px 12px; margin-bottom: 14px; }
/* The goodbye after a deliberate Keluar — same shape as .signin-err, in the success colours. */
.signin-bye { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--success-700); background: var(--success-50); border-radius: 11px; padding: 10px 12px; margin-bottom: 14px; }
.signin-alt { display: flex; justify-content: space-between; gap: 12px; margin-top: 14px; }
.signin-foot { text-align: center; font-size: 11.5px; color: var(--fg3); margin-top: 20px; line-height: 1.5; }
.signin-splash { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.signin-splash img { width: 54px; height: 54px; opacity: .9; animation: splash-pulse 1.4s var(--ease-out, ease-in-out) infinite; }
@keyframes splash-pulse { 0%, 100% { opacity: .45; transform: scale(.96); } 50% { opacity: 1; transform: scale(1); } }

/* the member app's only form fields */
.fld { display: block; margin: 18px 0 14px; }
.fld-l { display: block; font-size: 12px; font-weight: 700; color: var(--fg2); margin-bottom: 7px; }
.fld-i { width: 100%; box-sizing: border-box; border: 0; border-radius: 14px; background: var(--surface-2); box-shadow: inset 0 0 0 1.5px var(--border); padding: 15px 16px; font-family: var(--font-sans); font-size: 16px; font-weight: 600; color: var(--fg1); transition: box-shadow var(--dur-fast); }
.fld-i::placeholder { color: var(--fg3); font-weight: 500; }
.fld-i:focus { outline: 0; background: #fff; box-shadow: inset 0 0 0 2px var(--aqua-400); }
.fld-otp { font-family: var(--font-mono); font-size: 24px; letter-spacing: .4em; text-align: center; padding-left: 22px; }

/* THE SIGNUP FORM'S OWN CONTROLS. Organic self-registration introduced this app's FIRST
   <select> and FIRST checkbox — every other field it has ever had is a text input, which is why
   the block above calls itself "the member app's only form fields". Without these three rules a
   referral link renders a native OS dropdown and a bare browser checkbox in the middle of an
   otherwise styled card: not broken, but obviously not the same product. smoke.js cannot see
   this class of defect at all (no browser, no layout), so it has to be looked at. */

/* `appearance: none` strips the platform widget so .fld-i's box is what shows; the chevron is
   then ours to draw. Built from borders rather than a background-image SVG on purpose — a data
   URI bakes in a stroke colour, and this app has light and dark themes, so it would be invisible
   in one of them. A border triangle takes var(--fg3) and follows the theme for free. */
.fld-sel { position: relative; }
.fld-sel .fld-i { appearance: none; -webkit-appearance: none; padding-right: 42px; cursor: pointer; }
.fld-sel::after {
  content: ""; position: absolute; right: 18px; top: 50%; margin-top: -7px;
  width: 8px; height: 8px; transform: rotate(45deg); pointer-events: none;
  border-right: 2px solid var(--fg3); border-bottom: 2px solid var(--fg3); border-radius: 1px;
}

/* "(opsional)" sits inside .fld-l, which is 700-weight label text — without this it reads as
   part of the field's NAME rather than as a qualifier on it. */
.fld-opt { font-weight: 500; color: var(--fg3); }

/* The terms tick. `align-items: flex-start` because the sentence wraps to two lines on a phone
   and a centred box then floats beside the middle of the paragraph. The whole row is a <label>,
   so the text is part of the hit target — a 20px checkbox alone is under the 44px touch
   minimum. */
.signin-terms {
  display: flex; align-items: flex-start; gap: 10px; margin-top: 16px;
  font-size: 12.5px; font-weight: 500; line-height: 1.5; color: var(--fg2); cursor: pointer;
}
.signin-terms input {
  flex: none; width: 19px; height: 19px; margin: 1px 0 0; cursor: pointer;
  accent-color: var(--aqua-500);
}
/* `.lnk` was written for a <button> and the signup screen made it an <a> too ("Masuk di sini."
   in the footer). A button has no default decoration and an anchor does, so without an explicit
   `none` the same class rendered underlined in one place and not the other. `display: inline`
   for the same reason: an <a> is already inline, but stating it stops a future flex parent from
   stretching one and not the other. */
.lnk { background: 0; border: 0; padding: 4px 2px; font-family: var(--font-sans); font-size: 12.5px; font-weight: 700; color: var(--ocean-600); cursor: pointer; text-decoration: none; }
.lnk:disabled { color: var(--fg3); cursor: default; }
/* Inside the footer it is one word in a sentence, not a control of its own — so it takes the
   footer's own size rather than .lnk's 12.5px, which sat a point larger than the text around it
   and read as a rendering glitch. */
.signin-foot .lnk { font-size: inherit; padding: 0; }
.cta:disabled { filter: grayscale(.65); opacity: .55; cursor: default; }
.cta:disabled:active { transform: none; }

/* ============ monthly goal ============ */
.goal-card { display: block; width: 100%; text-align: left; border: 0; font-family: var(--font-sans); cursor: pointer; }
.goal-set { padding: 20px 16px 16px; }
.goal-steps { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.stepper { flex: none; width: 46px; height: 46px; border-radius: 15px; border: 0; background: var(--surface-2); color: var(--fg1); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform var(--dur-fast), background var(--dur-fast); }
.stepper:active { transform: scale(.94); }
.stepper:disabled { color: var(--fg3); opacity: .5; cursor: default; }
.goal-big { flex: 1; text-align: center; }
.goal-big .n { font-family: var(--font-display); font-weight: 600; font-size: 46px; line-height: 1; color: var(--fg1); }
.goal-big .u { font-size: 12px; font-weight: 600; color: var(--fg3); margin-top: 6px; }
.goal-hint { text-align: center; font-size: 12px; color: var(--fg3); margin-top: 16px; }
.goal-chips { display: flex; gap: 9px; }
.gchip { flex: 1; border: 0; border-radius: 14px; background: #fff; box-shadow: var(--shadow-xs); padding: 13px 0; font-family: var(--font-mono); font-weight: 600; font-size: 15px; color: var(--fg2); cursor: pointer; transition: background var(--dur-fast), color var(--dur-fast); }
.gchip.on { background: var(--aqua-500); color: var(--fg-on-aqua, #07333B); box-shadow: var(--glow-aqua); }

/* ============ data states (loading / empty / error) ============
   What a screen renders when it does not (yet) have data. The mock used to answer this
   question for free — every accessor fell back to a fabricated Andrew B., so a screen never
   had to tell "still loading" apart from "genuinely empty" apart from "the server refused".
   With the mock gone those are three different states, rendered by useMemberData/DataGate
   in components.jsx, and a screen that shows the same blank card for all three is the same
   conflation in a smaller place. */
.data-skel { display: flex; flex-direction: column; gap: 10px; }
.data-skel-row { height: 54px; border-radius: 14px; background: linear-gradient(100deg, var(--surface-2) 30%, var(--surface-3, #e7eef3) 50%, var(--surface-2) 70%); background-size: 220% 100%; animation: data-shimmer 1.3s ease-in-out infinite; }
@keyframes data-shimmer { 0% { background-position: 120% 0; } 100% { background-position: -20% 0; } }

.data-empty, .data-error { text-align: center; padding: 30px 18px; border-radius: 18px; background: #fff; box-shadow: var(--shadow-xs); }
.data-empty .ring, .data-error .ring { width: 46px; height: 46px; border-radius: 50%; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; background: var(--surface-2); color: var(--fg3); }
.data-error .ring.err { background: var(--danger-50); color: var(--danger-600, var(--danger-500)); }
.data-empty .ttl, .data-error .ttl { font-family: var(--font-display); font-weight: 600; font-size: 15px; color: var(--fg1); }
.data-empty .sub, .data-error .sub { font-size: 12.5px; color: var(--fg3); margin-top: 5px; line-height: 1.5; }
.data-error .retry { margin-top: 14px; }

/* Membership terms — the full-screen reader (TermsReader) and the read-then-tick block (TermsAgree). */
.terms-reader { -webkit-overflow-scrolling: touch; animation: fadeIn var(--dur-fast, .15s) ease-out; }
.terms-agree { margin-bottom: 16px; }
.terms-agree .signin-terms { margin-top: 12px; height: auto; align-items: flex-start; }
.terms-agree .signin-terms input:disabled { cursor: not-allowed; }

/* ============ Class media (class-media.jsx) ============
   The one bold move is the hero: when a class has a cover photo, the photo IS the header, under a
   scrim heavy enough at the bottom that the existing white title reads on any picture. Everything
   else — the strip, the thumbnails, the viewer — stays quiet. */
.cd-hero.has-photo { min-height: 268px; display: flex; flex-direction: column; justify-content: space-between; background: #0B121D; }
.cd-photo { position: absolute; inset: 0; background-size: cover; background-position: center; }
.cd-photo::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8,14,22,.55) 0%, rgba(8,14,22,.05) 34%, rgba(8,14,22,.35) 62%, rgba(8,14,22,.9) 100%); }
.cd-hero-top { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cd-hero.has-photo .cd-hero-in { margin-top: 64px; }
.cd-gallery { display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 13px; border-radius: 999px; border: 0;
  background: rgba(255,255,255,.16); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); color: #fff;
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 700; cursor: pointer; }
.cd-gallery:focus-visible, .cd-back:focus-visible { outline: 2px solid var(--aqua-300); outline-offset: 2px; }

.cg-strip { display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 18px; margin: 0 -18px; padding: 2px 18px 6px;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.cg-strip::-webkit-scrollbar { display: none; }
.cg-tile { position: relative; flex: none; width: 132px; height: 96px; border-radius: 14px; overflow: hidden; border: 0; padding: 0;
  background: #0B121D; scroll-snap-align: start; cursor: pointer; box-shadow: var(--shadow-xs); }
.cg-tile:first-child { width: 168px; }
.cg-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cg-tile:focus-visible { outline: 2px solid var(--ocean-500); outline-offset: 2px; }
.cg-blank { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: rgba(255,255,255,.55); }
.cg-play { position: absolute; left: 8px; bottom: 8px; display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 9px 0 7px;
  border-radius: 999px; background: rgba(8,14,22,.7); color: #fff; font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
.cg-tile.is-video:not(:has(img)) .cg-play { left: 50%; bottom: 50%; transform: translate(-50%, 50%); }

.typecard .tthumb { position: relative; flex: none; width: 52px; height: 52px; border-radius: 12px; overflow: hidden; background: #0B121D; }
.typecard .tthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.typecard .tthumb > :not(img) { position: absolute; right: 4px; bottom: 4px; color: #fff; filter: drop-shadow(0 1px 2px rgba(0,0,0,.6)); }

.mv { position: absolute; inset: 0; z-index: 90; background: #05080D; color: #fff; display: flex; flex-direction: column;
  animation: fadeIn var(--dur-mid) var(--ease-out); }
.mv-top { position: absolute; top: 0; left: 0; right: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between;
  padding: calc(14px + env(safe-area-inset-top)) 16px 14px; background: linear-gradient(180deg, rgba(5,8,13,.8), transparent); pointer-events: none; }
.mv-top > * { pointer-events: auto; }
.mv-btn { width: 40px; height: 40px; border-radius: 13px; border: 0; background: rgba(255,255,255,.14); color: #fff; display: flex;
  align-items: center; justify-content: center; cursor: pointer; }
.mv-btn:focus-visible, .mv-nav:focus-visible { outline: 2px solid var(--aqua-300); outline-offset: 2px; }
.mv-count { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: rgba(255,255,255,.85); }
.mv-track { flex: 1; display: flex; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; scrollbar-width: none;
  overscroll-behavior: contain; }
.mv-track::-webkit-scrollbar { display: none; }
.mv-slide { flex: none; width: 100%; height: 100%; scroll-snap-align: center; scroll-snap-stop: always; display: flex;
  align-items: center; justify-content: center; }
.mv-slide img, .mv-slide video { max-width: 100%; max-height: 100%; object-fit: contain; display: block; background: #000; }
.mv-slide video { width: 100%; }
.mv-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; width: 40px; height: 40px; border-radius: 50%; border: 0;
  background: rgba(255,255,255,.14); color: #fff; display: none; align-items: center; justify-content: center; cursor: pointer; }
.mv-nav.prev { left: 10px; } .mv-nav.next { right: 10px; }
.mv-nav:disabled { opacity: .25; cursor: default; }
@media (hover: hover) and (pointer: fine) { .mv-nav { display: flex; } }
.mv-cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 28px 22px calc(18px + env(safe-area-inset-bottom)); background: linear-gradient(0deg, rgba(5,8,13,.85), transparent);
  pointer-events: none; text-align: center; font-size: 13.5px; line-height: 1.45; color: rgba(255,255,255,.92); }
.mv-dots { display: flex; gap: 6px; }
.mv-dots i { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.35); transition: background var(--dur-fast); }
.mv-dots i.on { background: var(--aqua-300); }
@media (prefers-reduced-motion: reduce) { .mv { animation: none; } }

/* A class still to come on this day (Kelas › Jadwal day strip). */
.dates .day { position: relative; }
.day-dot { display: block; width: 5px; height: 5px; border-radius: 50%; margin: 3px auto 0; background: transparent; }
.day-dot.on { background: var(--ocean-500); }
.day.sel .day-dot.on { background: currentColor; }
