/* SaveEZ Mobile PWA - Professional Ultra-Premium Design System */

:root {
  /* Dark Canvas Theme */
  --bg-dark: #0A0B0E;
  --bg-surface: #121419;
  --bg-card-dark: #191C23;
  --bg-card-light: #FFFFFF;
  
  /* Gold & Metallic Palette - sampled from 24K bullion, not a framework
     swatch: warmer and slightly desaturated so large fills read as metal. */
  --gold-primary: #E0B142;
  --gold-secondary: #B8862F;
  --gold-light: #F2D785;
  --gold-metallic: linear-gradient(135deg, #F2D785 0%, #E0B142 50%, #A97722 100%);
  --gold-card-glow: 0 10px 30px -5px rgba(224, 177, 66, 0.25);
  
  
  /* Neutral Text Colors - warm neutrals, not slate; a blue-cast grey fights
     gold wherever the two meet. Contrast on --bg-dark stays above 7:1. */
  --text-main: #FFFFFF;
  --text-sub: #E6E1D7;
  --text-muted: #86857F;
  --text-gold: #EFC75E;
  --text-dark: #1A160F;
  
  /* Status Badges */
  --success: #2F9E6E;
  --success-bg: rgba(47, 158, 110, 0.15);
  --warning: #D9922B;
  --warning-bg: rgba(217, 146, 43, 0.15);
  --danger: #D2453C;
  --danger-bg: rgba(210, 69, 60, 0.15);

  /* Layout Spacing & Borders */
  --nav-height: 68px;
  --header-height: 66px;
  --radius-sm: 12px;
  --radius-md: 18px;
  --radius-lg: 26px;
  --border-gold: rgba(224, 177, 66, 0.35);
  --border-glass: rgba(255, 255, 255, 0.08);

  /* Typography */
  --font-family: 'Public Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-display: 'Bricolage Grotesque', 'Public Sans', system-ui, sans-serif;

  /* EZ Deals accent, shared with the website. Gold stays for gold products. */
  --deal: #CBF34A;
  --deal-dim: #9BBE2E;
  --deal-glow: rgba(203, 243, 74, .18);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

body {
  font-family: var(--font-family);
  background-color: #030405;
  color: var(--text-main);
  overflow-x: hidden;
  user-select: none;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  -webkit-font-smoothing: antialiased;
}

/* Mobile App Shell Frame */
#app-shell {
  width: 100%;
  max-width: 460px;
  min-height: 100vh;
  background-color: var(--bg-dark);
  position: relative;
  display: flex;
  flex-direction: column;
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.95);
  padding-top: var(--header-height);
  padding-bottom: calc(var(--nav-height) + 16px);
}

/* Top App Header with Metallic Gold Gradient */
.app-header {
  position: fixed;
  top: 0;
  width: 100%;
  max-width: 460px;
  height: var(--header-height);
  background: rgba(20, 23, 28, .95);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 18px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
  transition: background 0.3s ease;
}

.app-header.dark-header {
  background: rgba(20, 23, 28, 0.95);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border-glass);
}

/* "This screen is about gold", said with a hairline rather than a gradient. */
.app-header.gold-header { border-bottom-color: rgba(224, 177, 66, .32); }
.app-header.gold-header .header-title { color: var(--gold-primary); }

.header-btn {
  background: rgba(0, 0, 0, 0.08);
  border: none;
  color: #1A160F;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  transition: transform 0.15s ease, background 0.15s ease;
}

.header-btn:active {
  transform: scale(0.92);
}

.dark-header .header-btn {
  background: rgba(255, 255, 255, 0.12);
  color: #FFFFFF;
}

.header-title {
  font-size: 1.15rem;
  font-weight: 800;
  color: #1A160F;
  display: flex;
  align-items: center;
  gap: 6px;
  letter-spacing: -0.2px;
}

.dark-header .header-title {
  color: #FFFFFF;
}

.wave-emoji {
  display: inline-block;
  animation: wave 2.5s infinite;
  transform-origin: 70% 70%;
}

@keyframes wave {
  0%, 100% { transform: rotate(0deg); }
  10% { transform: rotate(14deg); }
  20% { transform: rotate(-8deg); }
  30% { transform: rotate(14deg); }
  40% { transform: rotate(-4deg); }
  50% { transform: rotate(10deg); }
  60% { transform: rotate(0deg); }
}

.notification-badge-btn {
  width: 40px;
  height: 40px;
  background: rgba(255, 255, 255, .12);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  position: relative;
  transition: transform 0.15s ease;
}

.notification-badge-btn:active {
  transform: scale(0.92);
}

.notification-badge-btn svg {
  stroke: #FFFFFF;
}

.notification-dot {
  position: absolute;
  top: 9px;
  right: 10px;
  width: 8px;
  height: 8px;
  background: var(--danger);
  border-radius: 50%;
  border: 1.5px solid #FFFFFF;
}

/* Slide-Out Side Navigation Drawer */
.side-drawer-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(6px);
  z-index: 300;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.side-drawer-overlay.active {
  opacity: 1;
  pointer-events: auto;
}

.side-drawer {
  width: 84%;
  max-width: 350px;
  height: 100%;
  background: var(--bg-surface);
  position: absolute;
  top: 0;
  left: 0;
  transform: translateX(-100%);
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow-y: auto;
  border-right: 1px solid var(--border-glass);
}

.side-drawer-overlay.active .side-drawer {
  transform: translateX(0);
}

.drawer-user-card {
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-md);
  padding: 18px;
  background: var(--bg-card-dark);
  margin-bottom: 20px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

.user-avatar-circle {
  width: 44px;
  height: 44px;
  background: var(--gold-metallic);
  color: #1A160F;
  font-size: 1.2rem;
  font-weight: 800;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.link-gold {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--gold-primary);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s ease;
}

.link-gold:hover {
  color: var(--gold-light);
}

.drawer-nav-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.drawer-nav-item {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--text-sub);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}

.drawer-nav-item:active {
  background: rgba(255, 255, 255, 0.06);
  transform: translateX(4px);
}

.nav-icon-bg {
  width: 36px;
  height: 36px;
  /* Neutral on purpose. These are wayfinding, not features, and colouring
     them gold implied the whole drawer belonged to the metal. */
  background: rgba(255, 255, 255, 0.06);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.nav-icon-bg svg {
  stroke: var(--text-sub);
  width: 18px;
  height: 18px;
}

.nav-icon-bg.danger-bg {
  background: var(--danger-bg);
}

.drawer-footer {
  text-align: center;
  padding-top: 20px;
  border-top: 1px solid var(--border-glass);
}

.drawer-footer .brand-lockup {
  display: block;
  width: 116px; height: auto;
  margin: 0 auto 10px;
  opacity: .9;
}

.drawer-footer .footer-info {
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.4;
}

.drawer-footer .footer-links {
  font-size: 0.7rem;
  color: var(--text-muted);
  margin-top: 6px;
}

.drawer-footer .version-tag {
  font-size: 0.7rem;
  font-weight: 600;
  /* A build number is not a feature; it does not need an accent colour. */
  color: var(--text-muted);
  opacity: .7;
  margin-top: 8px;
}

/* Dynamic View Container */
#view-container {
  flex: 1;
  padding: 16px;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

/* 4 Primary Bottom Navigation Tabs */
.bottom-nav {
  position: fixed;
  bottom: 0;
  width: 100%;
  max-width: 460px;
  height: var(--nav-height);
  background: rgba(20, 23, 28, 0.95);
  backdrop-filter: blur(16px);
  border-top: 1px solid var(--border-glass);
  z-index: 100;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 700;
  text-decoration: none;
  width: 25%;
  height: 100%;
  transition: color 0.2s ease;
  cursor: pointer;
}

.nav-icon-wrapper {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  transition: transform 0.2s ease, background 0.2s ease;
}

.nav-item svg {
  width: 20px;
  height: 20px;
  stroke: var(--text-muted);
  fill: none;
  stroke-width: 2;
  transition: stroke 0.2s ease;
}

.nav-item.active {
  color: var(--deal);
}

.nav-item.active .nav-icon-wrapper {
  background: rgba(203, 243, 74, 0.14);
  transform: translateY(-2px);
}

.nav-item.active svg {
  stroke: var(--gold-primary);
}

.card-dark-gold {
  background: var(--bg-card-dark);
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-md);
  padding: 18px;
  margin-bottom: 16px;
  box-shadow: var(--gold-card-glow);
}

/* CTA Buttons */
.btn-gold-solid {
  width: 100%;
  padding: 15px;
  background: var(--gold-metallic);
  border: none;
  border-radius: var(--radius-sm);
  color: #1A160F;
  font-weight: 800;
  font-size: 0.95rem;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(224, 177, 66, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  letter-spacing: -0.1px;
}

.btn-gold-solid:active {
  transform: scale(0.98);
  box-shadow: 0 2px 8px rgba(224, 177, 66, 0.2);
}

.btn-gold-outline {
  width: 100%;
  padding: 15px;
  background: transparent;
  border: 1.5px solid var(--gold-primary);
  border-radius: var(--radius-sm);
  color: var(--gold-primary);
  font-weight: 800;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 0.15s ease;
}

.btn-gold-outline:active {
  background: rgba(224, 177, 66, 0.1);
}

/* Sub-Tab Bar */
.sub-tab-bar {
  display: flex;
  background: var(--bg-surface);
  border: 1px solid var(--border-glass);
  border-radius: 30px;
  padding: 4px;
  margin-bottom: 16px;
}

.sub-tab {
  /* min-height alone would leave the label top-aligned in the taller box */
  display: flex; align-items: center; justify-content: center;
  min-height: 44px;
  flex: 1;
  text-align: center;
  padding: 10px 0;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-muted);
  border-radius: 26px;
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.sub-tab.active {
  background: var(--deal);
  color: #10160A;
  box-shadow: 0 4px 12px rgba(203, 243, 74, 0.28);
}

.sub-tab-gold.active {
  background: var(--gold-metallic);
  color: #1A160F;
  box-shadow: 0 4px 12px rgba(224, 177, 66, 0.3);
}

/* Category Filter Pills */
.filter-pills {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 10px;
  margin-bottom: 14px;
  scrollbar-width: none;
}

.filter-pill {
  padding: 8px 18px;
  border-radius: 20px;
  border: 1px solid var(--border-glass);
  background: var(--bg-surface);
  color: var(--text-sub);
  font-size: 0.8rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition: all 0.15s ease;
}

.filter-pill.active {
  background: var(--gold-primary);
  border-color: var(--gold-primary);
  color: var(--text-dark);
}

/* Status Badges */
.status-pill {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 0.72rem;
  font-weight: 800;
}

.status-pill.success {
  background: var(--success-bg);
  color: var(--success);
  border: 1px solid rgba(16, 185, 129, 0.3);
}

.status-pill.pending {
  background: var(--warning-bg);
  color: var(--warning);
  border: 1px solid rgba(245, 158, 11, 0.3);
}

.status-pill.danger {
  background: var(--danger-bg);
  color: var(--danger);
  border: 1px solid rgba(239, 68, 68, 0.3);
}

/* Transaction List Card */
.txn-card {
  background: var(--bg-card-dark);
  border-radius: var(--radius-md);
  padding: 16px;
  margin-bottom: 12px;
  display: flex;
  gap: 14px;
  align-items: center;
  border: 1px solid var(--border-glass);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}

/* Form Controls */
.form-input {
  width: 100%;
  padding: 13px 16px;
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: #FFFFFF;
  font-family: var(--font-family);
  font-size: 0.95rem;
  outline: none;
  transition: border-color 0.2s ease;
}

.form-input:focus {
  border-color: var(--gold-primary);
}

/* Modal Bottom Sheet */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(8px);
  z-index: 400;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.modal-overlay.active {
  opacity: 1;
  pointer-events: auto;
}

.modal-sheet {
  width: 100%;
  max-width: 460px;
  background: var(--bg-surface);
  border-radius: 28px 28px 0 0;
  padding: 26px 20px;
  border-top: 1px solid var(--border-gold);
  transform: translateY(100%);
  transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.6);
}

.modal-overlay.active .modal-sheet {
  transform: translateY(0);
}

.sheet-handle {
  width: 44px;
  height: 5px;
  background: var(--text-muted);
  border-radius: 3px;
  margin: 0 auto 18px auto;
  opacity: 0.5;
}

/* --------------------------------------------- Terms and Privacy, in-app --
   The documents are written for the website, so they arrive as headings,
   paragraphs and lists with no styling of their own. They are given the app's
   type here rather than the site's.

   The body scrolls inside the sheet rather than the sheet scrolling, so the
   document's name stays put above it - these are long, and losing the title
   on the second screenful is disorienting. Scrollbars are hidden app-wide, so
   the foot of the box is faded to say there is more. */
.policy {
  max-height: 62vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 2px;
  font-size: .88rem;
  line-height: 1.65;
  color: var(--text-sub);
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(180deg, #000 calc(100% - 28px), transparent);
}
.policy--loading { color: var(--text-muted); padding: 8px 0 24px; }
.policy-close {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 13px;
  font-size: .9rem;
  font-weight: 700;
  border: 1px solid var(--border-glass);
  border-radius: 999px;
}
.policy > :first-child { margin-top: 0; }
.policy > :last-child { margin-bottom: 26px; }   /* clears the fade */
.policy p { margin: 0 0 12px; }
.policy h2,
.policy h3 {
  margin: 22px 0 8px;
  font-family: var(--font-display);
  font-size: .98rem;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.3;
  color: var(--text-main);
}
.policy ul,
.policy ol { margin: 0 0 12px; padding-left: 20px; }
.policy li { margin-bottom: 6px; }
.policy hr {
  border: 0;
  border-top: 1px solid var(--border-glass);
  margin: 18px 0;
}
.policy a {
  color: var(--gold-primary);
  border-bottom: 1px solid rgba(224, 177, 66, .35);
  word-break: break-word;
}
.policy strong, .policy b { color: var(--text-main); font-weight: 700; }
.policy table { width: 100%; border-collapse: collapse; margin: 0 0 14px; }
.policy th, .policy td {
  border: 1px solid var(--border-glass);
  padding: 7px 9px;
  text-align: left;
  font-size: .82rem;
}

/* ==========================================================================
   GOLD - the vault
   The last screen still wearing the previous light theme, and most of what
   it showed could not be acted on: daily-savings chips whose button had no
   endpoint behind it, three "Quick guides" with chevrons and no handler, a
   rose-pink piggy bank in a gold product, and the website's footer.

   What is here instead is what you hold, the two things you do with it, what
   is still outstanding before money can reach a bank, and every remaining
   capability as a numbered row - the same index the website uses for the
   same list.
   ========================================================================== */
/* What you hold, first thing on the screen. The bars are the only
   illustration in the app, which is what makes them worth having: this is the
   figure a customer opens the app to see. */
.savings {
  position: relative;
  overflow: hidden;
  padding: 16px 16px 15px;
  margin-bottom: 12px;
  text-align: center;
  background:
    linear-gradient(170deg, rgba(224, 177, 66, .16), rgba(224, 177, 66, .03) 58%),
    var(--bg-card-dark);
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-md);
}
.savings__glow {
  position: absolute; top: -70px; left: 50%;
  width: 260px; height: 200px; transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(224, 177, 66, .3) 0%, rgba(224, 177, 66, .06) 45%, transparent 72%);
}
.savings__bars { position: relative; display: block; margin: 0 auto 2px; }
.savings__label {
  position: relative;
  margin: 0 0 6px;
  font-size: .82rem; font-weight: 600;
  color: var(--text-sub);
}
.savings__row {
  position: relative;
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.savings__value {
  font-family: var(--font-display);
  font-size: 2.1rem; font-weight: 700;
  letter-spacing: -.03em; line-height: 1.05;
  color: var(--text-main);
}
.savings__delta {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 4px 9px;
  font-size: .78rem; font-weight: 700;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}
.savings__delta.is-up { color: var(--success); border: 1px solid rgba(47, 158, 110, .45); }
/* A loss is stated, not hidden and not coloured like an alarm - gold falls,
   and a saver buying a little each month is not in trouble because of it. */
.savings__delta.is-down { color: var(--text-muted); border: 1px solid var(--border-glass); }
.savings__grams {
  position: relative;
  margin: 7px 0 0;
  font-size: .95rem; color: var(--text-muted);
}
.savings__grams strong { color: var(--text-sub); font-weight: 700; }
.savings__basis {
  position: relative;
  margin: 5px 0 0;
  font-size: .74rem; color: var(--text-muted);
}
/* Held nothing yet: the badge keeps its place with a dash rather than
   vanishing, so the card does not change shape the moment it has a value. */
.savings__delta.is-blank {
  color: var(--text-muted);
  border: 1px solid var(--border-glass);
  padding: 4px 12px;
}

/* The rate, folded into the same card, divided from the holding above it. */
.savings__rate {
  position: relative;
  margin: 14px -16px -15px;
  padding: 11px 16px 12px;
  border-top: 1px solid var(--border-glass);
  background: rgba(0, 0, 0, .22);
  text-align: left;
}
.savings__rate-top {
  display: flex; align-items: baseline; gap: 10px;
}
.savings__rate-label {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .66rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted);
}
.savings__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold-primary);
  box-shadow: 0 0 0 0 rgba(224, 177, 66, .55);
  animation: rate-pulse 2.4s ease-out infinite;
  flex-shrink: 0;
}
.savings__rate-now {
  font-size: .95rem; font-weight: 700;
  color: var(--gold-primary);
}
.savings__rate .gold-hero__delta { margin-left: auto; }
/* Bleeds to the card edges: the line is the surface, not a picture on it. */
.savings__rate .gold-hero__spark { height: 34px; margin: 6px -16px 4px; }
/* How long the price on screen has left. Augmont replaces the block every
   three minutes, so this is a real countdown, not a spinner. */
.savings__rate-timer {
  margin-left: auto;
  padding: 2px 8px;
  font-size: .66rem; font-weight: 700;
  color: var(--gold-primary);
  background: rgba(224, 177, 66, .12);
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}
.savings__rate-timer.is-stale { color: var(--text-muted); background: rgba(255, 255, 255, .06); }

.savings__rate-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin: 2px 0 8px;
}

/* How far back the line goes. Every point is a rate the app recorded; the
   log starts in August 2022. */
.rate-range { display: flex; gap: 4px; }
.rate-range__btn {
  padding: 4px 9px;
  background: transparent;
  border: 1px solid var(--border-glass);
  border-radius: 999px;
  font-family: inherit;
  font-size: .68rem; font-weight: 700;
  color: var(--text-muted);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.rate-range__btn.is-on {
  color: var(--gold-primary);
  border-color: rgba(224, 177, 66, .45);
  background: rgba(224, 177, 66, .1);
}
.gold-hero__spark.is-loading { opacity: .35; }
.gold-hero__spark { transition: opacity .2s ease; }

.savings__purity {
  margin: 0;
  font-size: .7rem; color: var(--text-muted);
}

/* The rate card leads with the price now, not the holding, so its figure
   takes the gold the holding used to have. */

.gold-hero__delta {
  font-size: .72rem; font-weight: 700;
  color: var(--success);
  font-variant-numeric: tabular-nums;
}
/* Shown either way. A month where gold fell is still the truth. */
.gold-hero__delta.is-down { color: var(--text-muted); }
.gold-hero__delta span { font-weight: 600; color: var(--text-muted); margin-left: 3px; }

.gold-hero__value {
  display: block; margin-top: 5px;
  font-family: var(--font-display);
  font-size: 2rem; font-weight: 700;
  letter-spacing: -.025em; line-height: 1.05;
  color: var(--gold-primary);
}
/* Nothing bought yet: the space says what to do with it instead of showing
   a zero three times over. */
/* Bleeds to the card edges: the line is the surface, not a picture on it. */
.gold-hero__spark { height: 40px; margin: 10px -16px 8px; }
.gold-hero__spark svg { display: block; width: 100%; height: 100%; }

.gold-hero__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold-primary);
  box-shadow: 0 0 0 0 rgba(224, 177, 66, .55);
  animation: rate-pulse 2.4s ease-out infinite;
  flex-shrink: 0;
}

/* The two things anyone comes to this screen to do. Buy is filled because it
   is the one most people want; selling is deliberate, so it is outlined. */
.gold-do {
  display: grid; grid-template-columns: 1.6fr 1fr; gap: 10px;
  margin-bottom: 16px;
}
.gold-do .btn-gold-solid { margin: 0; }
.btn-gold-ghost {
  width: 100%;
  padding: 15px;
  background: transparent;
  border: 1.5px solid var(--border-gold);
  border-radius: 999px;
  color: var(--gold-primary);
  font-family: var(--font-family);
  font-size: 1rem; font-weight: 800;
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease, transform .15s ease;
}
.btn-gold-ghost:active:not(:disabled) { transform: translateY(1px); }
.btn-gold-ghost:disabled { opacity: .4; cursor: default; }
@media (hover: hover) {
  .btn-gold-ghost:hover:not(:disabled) {
    background: rgba(224, 177, 66, .08);
    border-color: var(--gold-primary);
  }
}

/* What is outstanding before a sale can reach a bank. Shown only while
   something is - a checklist of things already done is noise. */
.gold-ready {
  padding: 4px 0 6px;
  margin-bottom: 18px;
  border: 1px solid rgba(224, 177, 66, .22);
  border-radius: var(--radius-md);
  background: rgba(224, 177, 66, .04);
}
.gold-ready__head {
  margin: 10px 14px 4px;
  font-family: var(--font-family);
  font-size: .68rem; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--gold-primary);
}
.gold-ready__row {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 11px 14px;
  background: none; border: 0;
  font-family: inherit; text-align: left;
  cursor: pointer;
}
.gold-ready__ring {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(224, 177, 66, .5);
  flex-shrink: 0;
}
.gold-ready__main { flex: 1; min-width: 0; }
.gold-ready__main strong {
  display: block;
  font-size: .9rem; font-weight: 700; color: var(--text-main);
}
.gold-ready__main span {
  display: block; margin-top: 1px;
  font-size: .74rem; color: var(--text-muted);
}
.gold-ready__cta {
  font-size: .8rem; font-weight: 800;
  color: var(--gold-primary);
  flex-shrink: 0;
}

/* What else the gold does. A numbered list read like a page of a document;
   these are things you tap, so they look like things you tap. Each card
   carries its own state where one is known. */
.gold-more__head {
  margin: 0 0 10px;
  font-family: var(--font-family);
  font-size: .68rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted);
}
.gold-more {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 9px; margin-bottom: 18px;
}
.gold-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  min-width: 0;
  padding: 13px 12px 12px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .04), rgba(255, 255, 255, .01)),
    var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 15px;
  font-family: inherit; text-align: left;
  cursor: pointer;
  transition: transform .18s cubic-bezier(.2, .7, .3, 1),
              border-color .18s ease, box-shadow .18s ease;
}
.gold-card:active { transform: translateY(1px) scale(.985); }
@media (hover: hover) {
  .gold-card:hover {
    border-color: rgba(224, 177, 66, .32);
    transform: translateY(-2px);
    box-shadow: 0 14px 24px -18px rgba(0, 0, 0, .95);
  }
}
.gold-card__icon {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-bottom: 7px;
  border-radius: 11px;
  background: rgba(224, 177, 66, .12);
  color: var(--gold-primary);
  flex-shrink: 0;
}
.gold-card__title {
  font-size: .9rem; font-weight: 700; color: var(--text-main);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.gold-card__note {
  font-size: .73rem; line-height: 1.35; color: var(--text-muted);
}
/* A card whose state is worth noticing - a running SIP, an open deposit. */
.gold-card--live .gold-card__icon {
  background: rgba(224, 177, 66, .2);
}
.gold-card--live .gold-card__note { color: var(--gold-primary); }

.gold-fact {
  margin: 0 0 8px;
  font-size: .75rem; line-height: 1.6;
  color: var(--text-muted);
}
.gold-fact strong { color: var(--text-sub); font-weight: 700; }

/* ------------------------------------------------------------ buying gold --
   The amount used to live in four chips on the Gold screen whose companion
   button could not save anything. It belongs to the purchase. */
.buy-flow__label {
  display: block; margin-bottom: 10px;
  font-size: .8rem; color: var(--text-muted);
}
.buy-flow__amount {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px;
  background: var(--bg-card-dark);
  border: 1.5px solid rgba(255, 255, 255, .1);
  border-radius: 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.buy-flow__amount:focus-within {
  border-color: var(--gold-primary);
  box-shadow: 0 0 0 3px rgba(224, 177, 66, .12);
}
.buy-flow__cur {
  font-family: var(--font-display);
  font-size: 1.5rem; font-weight: 700;
  color: var(--text-muted);
}
.buy-flow__amount input {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: none;
  font-family: var(--font-display);
  font-size: 1.6rem; font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text-main);
  font-variant-numeric: tabular-nums;
}
.buy-flow__quick {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 8px; margin: 12px 0 16px;
}
.buy-flow__chip {
  padding: 9px 0;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 999px;
  color: var(--text-sub);
  font-family: inherit;
  font-size: .8rem; font-weight: 700;
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}
.buy-flow__chip:active { border-color: var(--gold-primary); color: var(--gold-primary); }
/* Augmont's three-minute price lock, counted down. The block returned with
   the rates IS the lock, so this window is real: pay inside it and the
   purchase is made at this price. */
.buy-freeze {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 10px;
  padding: 9px 12px;
  background: rgba(224, 177, 66, .1);
  border: 1px solid rgba(224, 177, 66, .28);
  border-radius: 12px;
  font-size: .78rem; color: var(--text-muted);
  transition: background .2s ease, border-color .2s ease;
}
.buy-freeze__live {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .64rem; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--gold-primary);
}
.buy-freeze__live::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold-primary);
  box-shadow: 0 0 0 0 rgba(224, 177, 66, .55);
  animation: rate-pulse 2.4s ease-out infinite;
}
.buy-freeze__rate { font-weight: 700; color: var(--text-main); }
.buy-freeze__left { margin-left: auto; }
.buy-freeze__left strong { color: var(--gold-primary); font-weight: 700; }

/* Run out: the price on screen is no longer one anybody will honour. */
.buy-freeze.is-stale {
  background: rgba(210, 69, 60, .08);
  border-color: rgba(210, 69, 60, .3);
}
.buy-freeze__live.is-off { color: var(--danger); }
.buy-freeze__live.is-off::before { background: var(--danger); animation: none; }

/* GST comes off the amount before any gold is bought, so the breakdown shows
   it rather than leaving the customer to wonder why the weight is short. */
.buy-flow__split {
  padding: 11px 12px 6px;
  margin-bottom: 4px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 13px;
}
.buy-flow__quote {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 7px;
  font-size: .84rem; color: var(--text-muted);
}
.buy-flow__quote strong { font-size: 1rem; color: var(--gold-primary); font-weight: 700; }
.buy-flow__quote--quiet strong { font-size: .88rem; color: var(--text-sub); }
.buy-flow__quote--total {
  padding-top: 8px;
  border-top: 1px solid var(--border-glass);
}
.buy-flow__quote--total span { color: var(--text-sub); font-weight: 600; }
.buy-flow__note {
  margin: 12px 0 14px;
  font-size: .76rem; line-height: 1.5;
  color: var(--text-muted);
}

/* The drawer is grouped now that it is only the account and the app. */
.drawer-group {
  margin: 16px 0 6px;
  padding: 0 4px;
  font-size: .64rem; font-weight: 800;
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--text-muted);
}
.drawer-group:first-child { margin-top: 4px; }
.footer-links a {
  color: var(--text-sub);
  border-bottom: 1px solid rgba(255, 255, 255, .2);
}

/* --------------------------------------------------------- paying by UPI --
   The server asks PhonePe for UPI_QR, so this is the whole of the payment:
   a QR for a desktop, a upi:// link for a phone, and nothing else offered. */
.upi-pay { text-align: center; }
.upi-pay__amount {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-size: 1.7rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--gold-primary);
}
/* White plate: a QR has to be dark-on-light to scan reliably. */
.upi-pay__qr {
  display: inline-block;
  padding: 10px;
  background: #FFFFFF;
  border-radius: 14px;
  line-height: 0;
}
.upi-pay__qr img { display: block; width: 180px; height: 180px; }
.upi-pay__hint {
  margin: 10px 0 16px;
  font-size: .8rem; color: var(--text-muted);
}
.upi-pay__open { display: block; text-decoration: none; text-align: center; }
.upi-pay__apps {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; margin-top: 10px;
}
.upi-pay__app {
  padding: 10px 4px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 999px;
  font-size: .8rem; font-weight: 700;
  color: var(--text-sub); text-decoration: none;
}
.upi-pay__app:active { border-color: var(--gold-primary); color: var(--gold-primary); }
.upi-pay__note {
  margin: 10px 0 0;
  font-size: .72rem; line-height: 1.5; color: var(--text-muted);
}
/* On a phone the QR is secondary - you cannot scan your own screen - so it
   folds away behind a line rather than taking the top of the sheet. */
.upi-pay__more { margin-top: 14px; }
.upi-pay__more summary {
  display: inline-block;
  padding: 8px 4px;
  font-size: .8rem; font-weight: 700;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
}
.upi-pay__more summary::-webkit-details-marker { display: none; }
.upi-pay__more summary::after { content: ' ›'; }
.upi-pay__more[open] summary::after { content: ''; }
.upi-pay__more[open] summary { color: var(--text-sub); }

.upi-pay__state {
  margin: 16px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border-glass);
  font-size: .82rem; color: var(--text-muted);
}

/* A plan row can now be acted on, so it has a third column. */
.plan-row__main { flex: 1; min-width: 0; }
.plan-row__go {
  flex-shrink: 0;
  margin-left: 12px;
  padding: 8px 16px;
  background: transparent;
  border: 1.5px solid var(--border-glass);
  border-radius: 999px;
  font-family: inherit;
  font-size: .82rem; font-weight: 700;
  color: var(--text-sub);
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease, background .18s ease;
}
.plan-row__go.is-buy { border-color: rgba(203, 243, 74, .4); color: var(--deal); }
.plan-row__go:active { transform: translateY(1px); }
.plan-row__go:disabled { opacity: .5; cursor: default; }
@media (hover: hover) {
  .plan-row__go:hover:not(:disabled) { background: rgba(255, 255, 255, .04); }
  .plan-row__go.is-buy:hover:not(:disabled) { background: rgba(203, 243, 74, .08); }
}
.plan-row__on {
  flex-shrink: 0;
  margin-left: 12px;
  padding: 8px 14px;
  font-size: .76rem; font-weight: 800;
  letter-spacing: .04em;
  color: var(--success);
}
.plan-lock {
  margin: 12px 0 0;
  padding: 12px 14px;
  background: rgba(224, 177, 66, .06);
  border: 1px solid rgba(224, 177, 66, .2);
  border-radius: var(--radius-md);
  font-size: .8rem; line-height: 1.55;
  color: var(--text-muted);
}
.plan-lock strong { color: var(--gold-primary); font-weight: 700; }

/* ------------------------------------------------- who stands behind it --
   The foot of the Gold screen. A customer handing over money for metal they
   will never touch is entitled to know who is holding it, so this is stated
   at the end of the screen rather than tucked into a policy page. */
.trust {
  padding: 26px 0 6px;
  margin-top: 8px;
  border-top: 1px solid var(--border-glass);
  text-align: center;
}
.trust__head {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 1.45rem; font-weight: 700;
  letter-spacing: -.02em; line-height: 1.2;
  color: var(--text-main);
}
.trust__head span { color: var(--gold-primary); }
.trust__sub {
  margin: 0 8px 22px;
  font-size: .82rem; line-height: 1.55;
  color: var(--text-muted);
}

.trust__row {
  display: grid; grid-template-columns: repeat(3, 1fr);
  align-items: stretch;
  margin-bottom: 26px;
}
.trust__cell {
  display: flex; flex-direction: column; align-items: center;
  padding: 0 6px;
  min-width: 0;
}
.trust__cell + .trust__cell { border-left: 1px solid var(--border-glass); }
.trust__role {
  margin-bottom: 9px;
  font-size: .68rem; font-weight: 600;
  color: var(--text-sub);
}
/* Each mark on its own white chip.
   Two of the three are navy on white and would all but disappear against this
   card, and neither a regulator's mark nor an insurer's seal is ours to
   recolour - so the background comes to them instead. */
.trust__mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%; max-width: 100%; height: 40px;
  margin: 0 auto;
}
.trust__mark.is-wide { padding: 0 2px; }

/*
 * Greyed out: no chip, the marks themselves rendered in one light tone.
 *
 * A plain silhouette would not work - brightness(0) invert(1) fills every
 * opaque pixel, which would close the cut-out inside the BIS triangle and
 * flatten the Oriental seal into a disc. Inverting instead keeps the tonal
 * relationships: the navy and blue lift to light grey while the white that
 * sits INSIDE each mark drops to near-black and disappears against the card,
 * so the shapes read exactly as they are drawn.
 */
.trust__logo {
  display: block;
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  filter: invert(1) grayscale(1) brightness(1.25) contrast(1.15);
  opacity: .82;
}
.trust__name {
  font-size: .92rem; font-weight: 700;
  line-height: 1.25;
  letter-spacing: .02em;
  color: var(--text-main);
  opacity: .88;
}

.trust__tag {
  margin: 0 0 2px;
  font-size: 1.05rem; font-weight: 700;
  color: var(--gold-primary);
}
.trust__line {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 1.85rem; font-weight: 700;
  letter-spacing: -.03em;
  color: var(--gold-primary);
}
.trust__made {
  margin: 0;
  font-size: .76rem; color: var(--text-muted);
}

/* ------------------------------------------------------ closing an account --
   The one irreversible thing on the Account screen. Set apart at the end,
   stated plainly, and coloured as what it is - not dressed up as a settings
   row that could be tapped by accident. */
.danger {
  margin-top: 34px;
  padding: 18px 16px 16px;
  border: 1px solid rgba(210, 69, 60, .28);
  border-radius: var(--radius-md);
  background: rgba(210, 69, 60, .05);
}
.danger__head {
  margin: 0 0 6px;
  font-family: var(--font-family);
  font-size: .95rem; font-weight: 700;
  color: var(--text-main);
}
.danger__note {
  margin: 0 0 14px;
  font-size: .8rem; line-height: 1.55;
  color: var(--text-muted);
}
.danger__btn {
  width: 100%;
  padding: 13px;
  background: transparent;
  border: 1.5px solid rgba(210, 69, 60, .45);
  border-radius: 999px;
  font-family: inherit;
  font-size: .92rem; font-weight: 700;
  color: var(--danger);
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.danger__btn:active { background: rgba(210, 69, 60, .12); }
.danger__btn:disabled { opacity: .55; cursor: default; }

.danger-flow p { margin: 0 0 12px; font-size: .9rem; line-height: 1.6; color: var(--text-sub); }
.danger-flow__quiet { font-size: .8rem !important; color: var(--text-muted) !important; }
/* The confirmation that the one hard condition is met. Stated before the
   consequences, because it is the reason the reader is allowed to see them. */
/* How much of the balance is still inside the 48-hour holding period. Stated
   under the "available to sell" figure so the smaller number has a reason
   beside it rather than looking like a mistake. */
.sell-locked {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: -6px 0 16px;
  padding: 9px 11px;
  border-radius: 10px;
  background: rgba(224, 177, 66, .09);
  border: 1px solid rgba(224, 177, 66, .2);
  color: var(--text-sub);
  font-size: .78rem;
  line-height: 1.5;
}
.sell-locked svg { flex-shrink: 0; margin-top: 2px; color: var(--gold-primary); }
.sell-locked .num { color: var(--gold-primary); font-weight: 800; }

.danger-flow__ok {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--success-bg);
  color: var(--success);
  font-size: .84rem;
  font-weight: 600;
  line-height: 1.5;
}
.danger-flow__ok svg { flex-shrink: 0; margin-top: 2px; }

/* Why the account cannot be closed yet. */
.danger-flow__list--stop li { color: var(--text-sub); }
.danger-flow__list--stop li::marker { color: var(--danger); }

.danger-flow__list {
  margin: 0 0 12px; padding-left: 18px;
  font-size: .88rem; line-height: 1.7; color: var(--text-sub);
}
.danger-flow__reason { width: 100%; margin-bottom: 16px; resize: vertical; }
.danger-flow .btn-quiet { width: 100%; margin-top: 10px; }
.danger-flow .danger__btn,
.danger-flow .btn-gold-solid { margin-top: 4px; }

/* Toast Notifications */
#toast-container {
  /* Anchored to the bottom, above the tab bar. At top:80px it sat directly over
     each screen's heading — on the OTP screen it covered "Enter the code"
     entirely, and two stacked toasts covered the number as well. */
  position: fixed;
  bottom: calc(var(--nav-height) + 14px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 500;
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
  width: 90%;
  max-width: 420px;
  pointer-events: none;
}

.toast {
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  background: rgba(28, 32, 38, 0.95);
  backdrop-filter: blur(12px);
  color: #FFFFFF;
  font-size: 0.85rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--border-gold);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  animation: toastRise 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes toastRise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-16px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Spinner */
.spinner {
  width: 36px;
  height: 36px;
  border: 3.5px solid rgba(224, 177, 66, 0.2);
  border-top-color: var(--gold-primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ==========================================================================
   Tabular figures --- tabular figures ---
   Balances, rates and transaction amounts are read down a column and change
   as rates move. Proportional digits made them jitter and misalign, so every
   surface carrying a figure uses lining tabular numerals. Prose elsewhere
   keeps the default proportional set.
   ========================================================================== */
.card-dark-gold,
.txn-card,
.status-pill,
.form-input,
.num {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ==========================================================================
   Boot splash
   The app previously showed a bare spinner while the shell booted, so the
   first paint was an unbranded grey screen. This is a real splash: the brand
   mark on the app's own ground, matching the manifest background_color so the
   OS-generated splash and this one are indistinguishable — no flash between them.
   It removes itself once the first route has rendered, and has a CSS-only
   fallback so a JavaScript failure cannot leave it stuck over the app.
   ========================================================================== */
#boot-splash {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: var(--bg-dark);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 22px;
    animation: splash-failsafe 0s linear 6s forwards;
}

#boot-splash.is-done {
    opacity: 0;
    pointer-events: none;
    transition: opacity .32s ease;
}

#boot-splash img {
    width: 84px;
    height: auto;
    animation: splash-mark .7s cubic-bezier(.2, .9, .25, 1) both;
}

#boot-splash .splash-word {
    font-family: var(--font-display);
    font-size: 1.45rem;
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--text-main);
    animation: splash-mark .7s cubic-bezier(.2, .9, .25, 1) .08s both;
}

#boot-splash .splash-bar {
    width: 92px;
    height: 3px;
    border-radius: 99px;
    background: rgba(255, 255, 255, .12);
    overflow: hidden;
}

#boot-splash .splash-bar::after {
    content: "";
    display: block;
    width: 40%;
    height: 100%;
    border-radius: 99px;
    background: var(--deal);
    animation: splash-sweep 1.1s ease-in-out infinite;
}

@keyframes splash-mark {
    from { opacity: 0; transform: translateY(8px) scale(.96); }
    to   { opacity: 1; transform: none; }
}

@keyframes splash-sweep {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(250%); }
}

/* Failsafe: if the app never signals ready, stop covering it. */
@keyframes splash-failsafe {
    to { opacity: 0; visibility: hidden; pointer-events: none; }
}

/* ==========================================================================
   Install prompt ("add to home screen")
   There was no way to install the app from inside it. This is a dismissible
   bar, shown only when the browser actually offers installation, or on iOS
   where the OS gives no event and the user has to be told the manual steps.
   ========================================================================== */
#install-bar {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: calc(var(--nav-height) + 12px + env(safe-area-inset-bottom, 0px));
    z-index: 60;
    display: none;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-radius: var(--radius-md);
    background: var(--bg-card-dark);
    border: 1px solid rgba(203, 243, 74, .28);
    box-shadow: 0 16px 40px -12px rgba(0, 0, 0, .7);
}

#install-bar.is-visible {
    display: flex;
    animation: install-rise .28s ease both;
}

#install-bar img { width: 34px; height: 34px; border-radius: 9px; flex: 0 0 auto; }
#install-bar .install-copy { flex: 1; min-width: 0; }
#install-bar .install-title { font-weight: 700; font-size: .88rem; color: var(--text-main); }
#install-bar .install-sub { font-size: .72rem; color: var(--text-muted); line-height: 1.35; }

#install-bar .install-go {
    flex: 0 0 auto;
    background: var(--deal);
    color: #10160A;
    border: 0;
    border-radius: 999px;
    padding: 9px 15px;
    font-family: var(--font-family);
    font-size: .8rem;
    font-weight: 800;
    cursor: pointer;
}

#install-bar .install-x {
    flex: 0 0 auto;
    background: none;
    border: 0;
    color: var(--text-muted);
    font-size: 1.1rem;
    line-height: 1;
    padding: 4px 2px;
    cursor: pointer;
}

@keyframes install-rise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

/* Headings pick up the display face now that it is loaded. */
h1, h2, h3, .display-type {
    font-family: var(--font-display);
    letter-spacing: -.02em;
}

/* With the header hidden on the sign-in screens, the view needs its own top
   inset or the content sits under the status bar. */
#app-shell.is-signed-out #view-container {
    padding-top: calc(28px + env(safe-area-inset-top, 0px));
}

/* ==========================================================================
   PHASE 2 — Onboarding (login / otp / signup)
   The sign-in screens used a white card floating on the dark ground, left over
   from the previous theme. These are built on the app's own surface instead,
   with the lime action colour shared with the website.
   ========================================================================== */
.auth {
    padding: 8px 2px 24px;
}

.auth__mark {
    width: 52px;
    height: 52px;
    border-radius: 15px;
    margin-bottom: 22px;
}

.auth h1 {
    font-family: var(--font-display);
    font-size: 2rem;
    font-weight: 800;
    letter-spacing: -.03em;
    line-height: 1.05;
    color: var(--text-main);
    margin-bottom: 8px;
}

.auth__sub {
    font-size: .94rem;
    color: var(--text-muted);
    line-height: 1.5;
    margin-bottom: 28px;
}

.auth__label {
    display: block;
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 9px;
}

/* Phone field: the country code is part of the control, not a separate box. */
.phone-field {
    display: flex;
    align-items: stretch;
    background: var(--bg-card-dark);
    border: 1.5px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    overflow: hidden;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.phone-field:focus-within {
    border-color: var(--deal);
    box-shadow: 0 0 0 3px var(--deal-glow);
}

.phone-field__cc {
    display: flex;
    align-items: center;
    padding: 0 14px;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--text-sub);
    background: rgba(255, 255, 255, .04);
    border-right: 1px solid rgba(255, 255, 255, .08);
}

.phone-field input {
    flex: 1;
    min-width: 0;
    background: none;
    border: 0;
    outline: none;
    color: var(--text-main);
    font-family: var(--font-family);
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: .04em;
    padding: 16px 14px;
    font-variant-numeric: tabular-nums;
}

.phone-field input::placeholder {
    color: var(--text-muted);
    font-weight: 500;
    letter-spacing: normal;
}

/* One box per digit beats a single field with letter-spacing: you can see how
   many digits are expected and how many you have entered. */
.otp-boxes {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
    margin-bottom: 6px;
}

.otp-box {
    width: 100%;
    aspect-ratio: 1 / 1.15;
    background: var(--bg-card-dark);
    border: 1.5px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    color: var(--text-main);
    font-family: var(--font-display);
    font-size: 1.7rem;
    font-weight: 700;
    text-align: center;
    outline: none;
    transition: border-color .15s ease, box-shadow .15s ease, transform .1s ease;
    font-variant-numeric: tabular-nums;
}

.otp-box:focus {
    border-color: var(--deal);
    box-shadow: 0 0 0 3px var(--deal-glow);
}

.otp-box.is-filled {
    border-color: rgba(203, 243, 74, .45);
}

.otp-box.is-error {
    border-color: var(--danger);
    animation: otp-shake .3s ease;
}

@keyframes otp-shake {
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
}

/* Lime primary action, matching the website's --deal button. */
.btn-deal-solid {
    width: 100%;
    background: var(--deal);
    color: #10160A;
    border: 0;
    border-radius: 999px;
    padding: 16px 20px;
    font-family: var(--font-family);
    font-size: 1rem;
    font-weight: 800;
    cursor: pointer;
    /* The tenth digit is the moment the screen has been waiting for, so the
       button arrives rather than just switching colour. */
    transition: transform .18s cubic-bezier(.2, .8, .3, 1), background .22s ease,
                opacity .22s ease, box-shadow .22s ease;
}

.btn-deal-solid:active { transform: translateY(1px); }
/* Enabled: it lifts slightly and takes on its own light. */
.btn-deal-solid:not(:disabled) { box-shadow: 0 8px 24px rgba(203, 243, 74, .16); }
@media (prefers-reduced-motion: reduce) {
    .btn-deal-solid { transition: background .15s ease, opacity .15s ease; }
}
/* Filled lime in both states, on request: the primary action is the lime one
   whether or not the field is complete yet.

   What separates the two states is the light. Waiting, the button is flat -
   no glow, no lift. Once the number is complete it takes on its own shadow
   (see :not(:disabled) above), so the change still registers without the
   colour going anywhere. */
.btn-deal-solid:disabled {
    background: var(--deal);
    color: #10160A;
    border: 0;
    cursor: default;
}

.btn-quiet {
    background: none;
    border: 0;
    color: var(--text-muted);
    font-family: var(--font-family);
    font-size: .86rem;
    font-weight: 600;
    padding: 8px 2px;
    cursor: pointer;
}

.btn-quiet--deal { color: var(--deal); font-weight: 700; }

.auth__legal {
    font-size: .74rem;
    color: var(--text-muted);
    line-height: 1.55;
    margin-top: 20px;
}

.auth__legal a {
    color: var(--text-sub);
    border-bottom: 1px solid rgba(255, 255, 255, .22);
}

.auth__back {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: .85rem;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 20px;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

/* ==========================================================================
   PHASE 3 — EZ Deals
   Cards were white tiles on the dark ground with the artwork squeezed into a
   75px strip. Here the artwork leads, the saving is the loudest text, and the
   whole tile is one button.
   ========================================================================== */
/* --------------------------------------------------------------------------
   Today's best - a rail, not a banner.

   The banner this replaces showed one deal, on artwork the vendor only ships
   at 200px, stretched to the full width of the phone. Eight cards at 132px
   each are inside what that artwork can render sharply, and eight deals is a
   reason to look at this screen rather than scroll past it.
   ----------------------------------------------------------------------- */
.ez-rail { margin: 0 0 18px; }

.ez-rail__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 11px;
}

.ez-rail__title {
    font-family: var(--font-display);
    font-size: 1.06rem;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--text-main);
    margin: 0;
}

.ez-rail__note {
    font-size: .74rem;
    font-weight: 700;
    color: var(--text-muted);
}
.ez-rail__note .num { color: var(--deal); }

/* Bleeds to the screen edge so the eighth card is visibly cut off, which is
   what tells you the row scrolls. */
.ez-rail__track {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 -16px;
    padding: 2px 16px 4px;
}
.ez-rail__track::-webkit-scrollbar { display: none; }

/* These rails hide their scrollbar, so on a pointer device the cursor is the
   only thing that says they move. See initRailDragging(). */
.ez-rail__track,
.ez-bar__scroll,
.filter-pills,
.signin__chips { cursor: grab; }

.ez-rail__track.is-dragging,
.ez-bar__scroll.is-dragging,
.filter-pills.is-dragging,
.signin__chips.is-dragging {
  cursor: grabbing;
  /* Snap points fight a drag in progress, tugging the row back as it moves. */
  scroll-snap-type: none;
}

/* The cards inside keep their own pointer, or every one of them looks like
   part of the background. */
.ez-rail__track .lead,
.ez-bar__scroll .ez-chip,
.filter-pills > *,
.signin__chips .chip { cursor: pointer; }
.ez-rail__track.is-dragging .lead,
.ez-bar__scroll.is-dragging .ez-chip,
.filter-pills.is-dragging > *,
.signin__chips.is-dragging .chip { cursor: grabbing; }

.lead {
    flex: 0 0 auto;
    width: 124px;
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 0;
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    font-family: var(--font-family);
}
.lead:active { transform: scale(.97); }

.lead__art {
    position: relative;
    display: block;
    aspect-ratio: 1 / 1;
    border-radius: 14px;
    overflow: hidden;
    background: #20242C;
    /* Half the catalogue's artwork is a logo on white. Without this ring the
       white ones dissolve into nothing on a near-black screen. */
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .1);
}
.lead__art img { width: 100%; height: 100%; object-fit: cover; display: block; }

.lead__fallback {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: 0 8px;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: .82rem;
    color: var(--text-sub);
    text-align: center;
}

/* The saving, set on the artwork, because the saving is the reason the rail
   exists. */
.lead__off {
    position: absolute;
    left: 7px;
    bottom: 7px;
    padding: 3px 8px;
    border-radius: 999px;
    background: var(--deal);
    color: #12160A;
    font-size: .78rem;
    font-weight: 800;
    letter-spacing: -.01em;
}

.lead__brand {
    font-size: .8rem;
    font-weight: 700;
    color: var(--text-sub);
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Search sits on the app surface rather than a white input. */
.ez-search {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--bg-card-dark);
    border: 1.5px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    padding: 0 12px;
    margin-bottom: 14px;
    color: var(--text-muted);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.ez-search:focus-within {
    border-color: var(--deal);
    box-shadow: 0 0 0 3px var(--deal-glow);
    color: var(--deal);
}

.ez-search input {
    flex: 1;
    min-width: 0;
    background: none;
    border: 0;
    outline: none;
    color: var(--text-main);
    font-family: var(--font-family);
    font-size: .95rem;
    font-weight: 600;
    padding: 14px 0;
}

.ez-search input::placeholder { color: var(--text-muted); font-weight: 500; }
.ez-search input::-webkit-search-cancel-button { display: none; }

.ez-search__clear {
    background: none;
    border: 0;
    color: var(--text-muted);
    font-size: 1.3rem;
    line-height: 1;
    padding: 4px;
    cursor: pointer;
}

/* Two columns, at every width. Breakpoints were tried here and were a
   mistake: the app shell is capped at 460px, so a `min-width: 820px` media
   query fires on the VIEWPORT while the grid it restyles is still 460px wide.
   On a wide screen that packed four tiles into 460px - about 106px each - and
   wrapped the brand names onto three lines. The container never gets wider,
   so the column count should never change. */
.ez-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}

.deal-tile {
    display: flex;
    flex-direction: column;
    background: var(--bg-card-dark);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: var(--radius-sm);
    overflow: hidden;
    padding: 0;
    text-align: left;
    cursor: pointer;
    transition: border-color .15s ease, transform .12s ease;
    font-family: var(--font-family);
}

.deal-tile:active { transform: scale(.98); }
.deal-tile:hover { border-color: rgba(203, 243, 74, .4); }

.deal-tile__art {
    display: block;
    aspect-ratio: 16 / 10;
    background: #20242C;
    overflow: hidden;
}

.deal-tile__art img { width: 100%; height: 100%; object-fit: cover; display: block; }

.deal-tile__fallback {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: .9rem;
    color: var(--text-sub);
    padding: 0 8px;
    text-align: center;
}

.deal-tile__foot {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 10px 11px 12px;
}

.deal-tile__brand {
    font-size: .82rem;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.deal-tile__off { font-size: .82rem; font-weight: 800; color: var(--deal); }
.deal-tile__off--none { color: var(--text-muted); font-weight: 600; }

/* --------------------------------------------------------------------------
   My vouchers. A bought voucher is a ticket, so it is shaped like one - the
   notched stub on the left is the only thing on this screen that says
   "you own this" rather than "you could buy this".
   ----------------------------------------------------------------------- */
.voucher {
    position: relative;
    display: flex;
    align-items: center;
    gap: 13px;
    background: var(--bg-card-dark);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: var(--radius-sm);
    padding: 14px 14px 14px 22px;
    margin-bottom: 10px;
    overflow: hidden;
}

.voucher__stub {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 5px;
    background: var(--deal);
}
.voucher.is-pending .voucher__stub { background: var(--warning); }

/* The perforation. */
.voucher::before,
.voucher::after {
    content: '';
    position: absolute;
    left: 2.5px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--bg-dark);
    transform: translateX(-50%);
}
.voucher::before { top: -5.5px; }
.voucher::after { bottom: -5.5px; }

.voucher__body { flex: 1; min-width: 0; }
.voucher__brand { font-weight: 700; font-size: .92rem; color: var(--text-main); }
.voucher__meta { font-size: .72rem; color: var(--text-muted); margin-top: 3px; }
.voucher__end { text-align: right; flex-shrink: 0; }
.voucher__amount {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--text-main);
    margin-bottom: 4px;
}

/* --------------------------------------------------------------------------
   The variant picker - what a brand with more than one product opens.
   ----------------------------------------------------------------------- */
.var-intro {
    font-size: .86rem;
    color: var(--text-muted);
    line-height: 1.55;
    margin: 0 0 14px;
}

.var-list { display: flex; flex-direction: column; gap: 8px; }

.var-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 9px 11px 9px 9px;
    background: var(--bg-card-dark);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 14px;
    cursor: pointer;
    text-align: left;
    font-family: var(--font-family);
    transition: border-color .15s ease, transform .12s ease;
}
.var-row:hover { border-color: rgba(203, 243, 74, .4); }
.var-row:active { transform: scale(.99); }

.var-row__art {
    flex-shrink: 0;
    width: 52px;
    height: 52px;
    border-radius: 11px;
    overflow: hidden;
    background: #20242C;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .1);
}
.var-row__art img { width: 100%; height: 100%; object-fit: cover; display: block; }

.var-row__body { flex: 1; min-width: 0; }
.var-row__name {
    display: block;
    font-size: .86rem;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.3;
}
.var-row__meta {
    display: block;
    font-size: .74rem;
    color: var(--text-muted);
    margin-top: 2px;
}

.var-row__end {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    color: var(--text-muted);
}
.var-row__off { font-size: .92rem; font-weight: 800; color: var(--deal); }
.var-row__off--none { font-size: .76rem; font-weight: 600; color: var(--text-muted); }
.var-row__lock {
    font-size: .64rem;
    font-weight: 800;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--gold-primary);
}
.var-row.is-locked .var-row__art img { filter: saturate(.35) brightness(.72); }


/* --------------------------------------------------------------------------
   Account: payout accounts and delivery addresses.

   A pair of boxes rather than two stacked sections. Both are usually empty,
   and full-width they spent a third of the screen saying "nothing saved yet"
   twice over. Side by side they read as what they are - where money leaves
   and where parcels arrive - and everything below them moves up.
   ----------------------------------------------------------------------- */
.acct-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 22px 0 4px;
}

.acct-box {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 13px 12px 14px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  min-width: 0;
}

.acct-box__head {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 7px;
}

.acct-box__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 25px;
  height: 25px;
  border-radius: 8px;
  background: rgba(255, 255, 255, .06);
  color: var(--text-sub);
  flex-shrink: 0;
}

.acct-box__head h2 {
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.25;
  min-width: 0;
}

/* A round + rather than the words "+ Add": at half the width there is no
   room for a label beside a title that already wraps to two lines. */
.acct-box__add {
  flex-shrink: 0;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .16);
  background: none;
  color: var(--text-sub);
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, transform .12s ease;
}
.acct-box__add:hover { background: rgba(255, 255, 255, .09); color: var(--text-main); }
.acct-box__add:active { transform: scale(.92); }

/* The circle is 25px because that is what fits beside a two-line title, but
   a finger needs more than that. The hit area is pushed out to 45px without
   moving anything on the page. The buttons this replaced were 44px tall. */
.acct-box__add { position: relative; }
.acct-box__add::before {
  content: '';
  position: absolute;
  inset: -10px;
}

.acct-item__remove { position: relative; }
.acct-item__remove::before {
  content: '';
  position: absolute;
  inset: -12px -10px;
}

.acct-box__empty {
  font-size: .76rem;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}

.acct-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 9px;
  border-radius: 9px;
  background: rgba(255, 255, 255, .04);
}

.acct-item__text { flex: 1; min-width: 0; }

.acct-item__name {
  font-size: .8rem;
  font-weight: 700;
  color: var(--text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.acct-item__meta {
  font-size: .7rem;
  color: var(--text-muted);
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.acct-item__remove {
  flex-shrink: 0;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, .07);
  color: var(--text-muted);
  font-size: .95rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.acct-item__remove:hover { background: var(--danger-bg); color: var(--danger); }

/* Below about 330px two columns stop being readable and the pair stacks. */
@media (max-width: 330px) {
  .acct-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   PHASE 4 — Gold core (vault + passbook)
   The vault was a decorative coin graphic over four stacked full-width
   buttons; the passbook had two tabs that did nothing and a hardcoded rate.
   ========================================================================== */








/* Four actions as a grid: each one says what it does, so the labels no longer
   have to carry the whole explanation. */







/* What is being held, and by whom. The claims are the company's own, as
   published on saveez.in - stated as a definition list because that is what
   they are, rather than as a row of trust badges. */
.gold-facts__head {
  margin: 20px 0 8px;
  font-family: var(--font-family);
  font-size: .68rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted);
}
.gold-facts { margin: 0 0 16px; }
.gold-fact-row {
  display: flex; align-items: baseline; gap: 14px;
  padding: 11px 2px;
  border-bottom: 1px solid var(--border-glass);
}
.gold-fact-row:last-child { border-bottom: 0; }
.gold-fact-row dt {
  flex: 0 0 38%;
  font-size: .78rem; color: var(--text-muted);
}
.gold-fact-row dd {
  flex: 1; margin: 0;
  font-size: .85rem; font-weight: 600; color: var(--text-sub);
}

.gold-track {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 14px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  font-family: inherit; text-align: left;
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color .18s ease;
}
.gold-track:active { border-color: var(--border-gold); }
/* Matches the tiles above it, so the row reads as one of the same family. */
.gold-track__icon {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: 11px;
  background: rgba(224, 177, 66, .12);
  color: var(--gold-primary);
  flex-shrink: 0;
}
.gold-track__main { flex: 1; min-width: 0; }
.gold-track__main strong {
  display: block;
  font-size: .92rem; font-weight: 700; color: var(--text-main);
}
.gold-track__main span {
  display: block; margin-top: 1px;
  font-size: .75rem; color: var(--text-muted);
}




/* A shared empty state. It was named .vault-empty, which was never true - the
   passbook and every other list use it too - and the name went stale the
   moment the Vault screen did. */
.empty-state {
    text-align: center;
    padding: 44px 18px;
}

.empty-state__title {
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text-main);
    margin-bottom: 8px;
}

.empty-state p {
    font-size: .86rem;
    color: var(--text-muted);
    line-height: 1.55;
    margin-bottom: 20px;
}

/* ---------------------------------------------------------------- passbook */
/* The position the passbook's rows add up to. Three figures across, because
   they are read together - invested against worth, and the difference - and a
   stacked list would make them three separate facts. Gold is right here: the
   passbook is a gold surface and these are figures about the metal. */
.ledger-pos {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    padding: 14px 16px;
    margin-bottom: 14px;
    border: 1px solid var(--border-glass);
    border-radius: var(--radius-md);
    background: rgba(224, 177, 66, .05);
}

.ledger-pos__cell { min-width: 0; }

.ledger-pos__label {
    display: block;
    font-size: .68rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 4px;
}

/* clamp: the third cell carries an amount and a percentage, and at 460px with
   a six-figure holding the row would otherwise push itself sideways. */
.ledger-pos__fig {
    display: block;
    font-size: clamp(.82rem, 3.4vw, .98rem);
    font-weight: 700;
    color: var(--text-sub);
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ledger-pos__fig.is-up { color: var(--success); }
.ledger-pos__fig.is-down { color: var(--danger); }

.ledger-pos__pct {
    display: block;
    font-size: .68rem;
    font-weight: 600;
    opacity: .8;
    margin-top: 1px;
}

.ledger-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.ledger-head__title {
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--text-main);
}

.ledger-head__sub { font-size: .78rem; color: var(--text-muted); margin-top: 2px; }

.ledger-head__rate {
    text-align: right;
    font-size: .7rem;
    color: var(--text-muted);
    line-height: 1.5;
}

.ledger-head__rate strong { display: block; font-size: .92rem; color: var(--gold-primary); }

.ledger-chips { display: flex; gap: 7px; margin-bottom: 14px; }

.ledger-chip {
  min-height: 44px;
    background: none;
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 999px;
    color: var(--text-muted);
    font-family: var(--font-family);
    font-size: .78rem;
    font-weight: 700;
    padding: 8px 15px;
    cursor: pointer;
}

.ledger-chip.is-on {
    background: var(--gold-primary);
    border-color: var(--gold-primary);
    color: var(--text-dark);
}

/* A day of the passbook. The date is a heading, so the rows below it do not
   each repeat it. */
.ledger-day { margin-bottom: 6px; }
.ledger-day__head {
  margin: 14px 2px 2px;
  font-family: var(--font-family);
  font-size: .66rem; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted);
}

/* Rows, not cards. Five transactions used to fill the screen. */
.ledger-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 2px;
  border-bottom: 1px solid var(--border-glass);
}
.ledger-row:last-child { border-bottom: 0; }

.ledger-row__dir {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; margin-top: 1px;
  border-radius: 10px;
  flex-shrink: 0;
}
/* Gold in, muted out - the direction is the thing worth reading at a glance. */
.ledger-row__dir.is-buy { background: rgba(224, 177, 66, .14); color: var(--gold-primary); }
.ledger-row__dir.is-sell { background: rgba(255, 255, 255, .07); color: var(--text-sub); }

.ledger-row__main { flex: 1; min-width: 0; }
.ledger-row__top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.ledger-row__top strong {
  font-size: .92rem; font-weight: 700; color: var(--text-main);
}
.ledger-row__amt { flex-shrink: 0; }

.ledger-row__meta {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin-top: 2px;
  font-size: .74rem; color: var(--text-muted);
}
/* The reference is what support asks for, so it is copyable rather than
   just printed. */
.ledger-row__ref {
  padding: 0;
  background: none; border: 0;
  border-bottom: 1px dashed rgba(255, 255, 255, .22);
  font-family: inherit; font-size: .72rem;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
}
.ledger-row__ref:active { color: var(--gold-primary); }

/* =========================================================================
   PHASE 5 - Gold actions (sell / send / delivery / FD) and their histories

   These seven screens used to be seven unrelated layouts. They now share one
   grammar: a flow header that always points back, a strip that states what
   you hold and what the metal is worth right now, then the form, then a
   single link to that action's own history.
   ========================================================================= */

.flow-head { margin-bottom: 18px; }

.flow-head__back {
  /* 44px tall tap area (WCAG 2.5.5) without pushing the title down: the extra
     height is padding above and below a 20px line. */
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; margin-left: -8px;
  background: none; border: none; padding: 0 8px 12px;
  color: var(--text-muted); font-size: .82rem; font-weight: 600;
  font-family: inherit; cursor: pointer;
}
.flow-head__back:hover { color: var(--text-sub); }

.flow-head__title {
  font-family: var(--font-display);
  font-size: 1.45rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--text-main); line-height: 1.15;
}

.flow-head__sub {
  font-size: .86rem; color: var(--text-muted);
  line-height: 1.5; margin-top: 6px;
}

/* What you hold / what it's worth. Never shows an invented number: if the
   rate fetch failed the value reads "unavailable" rather than a plausible lie. */
.flow-strip {
  display: grid; grid-template-columns: 1fr auto; gap: 12px;
  align-items: end;
  padding: 14px 16px; margin-bottom: 20px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 14px;
}
.flow-strip__label {
  display: block; font-size: .7rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-muted);
  font-weight: 700; margin-bottom: 4px;
}
.flow-strip__value { font-size: 1.1rem; font-weight: 800; color: var(--text-main); }
.flow-strip__rate { text-align: right; }
.flow-strip__rate .flow-strip__value { color: var(--gold-primary); font-size: 1rem; }
.flow-strip__value--off { color: var(--text-muted); font-size: .86rem; font-weight: 600; }

.flow-field { margin-bottom: 16px; }
.flow-field > label {
  display: block; font-size: .78rem; font-weight: 700;
  color: var(--text-sub); margin-bottom: 7px;
}
.flow-hint {
  font-size: .74rem; color: var(--text-muted);
  line-height: 1.5; margin-top: 7px;
}

/* Quantity row: input glued to its unit, chips underneath. */
.flow-amount {
  display: flex; align-items: stretch;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-gold);
  border-radius: 12px; overflow: hidden;
}
.flow-amount:focus-within { border-color: var(--gold-primary); }
.flow-amount .form-input {
  border: none; background: transparent; flex: 1; min-width: 0;
  font-size: 1.05rem; font-weight: 700;
}
.flow-amount__unit {
  display: flex; align-items: center; padding: 0 16px;
  background: rgba(224, 177, 66, .12);
  color: var(--gold-primary);
  font-weight: 800; font-size: .85rem;
  border-left: 1px solid var(--border-gold);
}

.flow-chips { display: flex; gap: 8px; margin-top: 10px; }
.flow-chip {
  flex: 1; min-height: 44px; padding: 8px 0;
  background: transparent;
  border: 1px solid var(--border-glass);
  border-radius: 999px;
  color: var(--text-sub); font-family: inherit;
  font-size: .78rem; font-weight: 700; cursor: pointer;
}
.flow-chip:hover { border-color: var(--border-gold); color: var(--gold-primary); }
.flow-chip:active { transform: scale(.97); }

/* Running total. It reads as a statement of fact, so it is typeset like one. */
.flow-quote {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 16px 0; margin: 4px 0 18px;
  border-top: 1px solid var(--border-glass);
  border-bottom: 1px solid var(--border-glass);
}
.flow-quote__label { font-size: .84rem; color: var(--text-muted); font-weight: 600; }
.flow-quote__value {
  font-family: var(--font-display);
  font-size: 1.6rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--gold-primary);
}

.flow-note {
  display: flex; gap: 9px; align-items: flex-start;
  font-size: .76rem; line-height: 1.55; color: var(--text-muted);
  margin-bottom: 18px;
}
.flow-note strong { color: var(--text-sub); font-weight: 700; }

.flow-dest {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 13px 15px; margin-bottom: 18px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 12px;
}
.flow-dest__label { font-size: .72rem; color: var(--text-muted); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.flow-dest__value { font-size: .88rem; color: var(--text-main); font-weight: 700; margin-top: 3px; }
.flow-dest a, .flow-dest button {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: .8rem; font-weight: 700;
  color: var(--gold-primary); white-space: nowrap;
}

/* One quiet link out to this action's history, instead of a second big button
   competing with the real call to action. */
.flow-foot {
  display: flex; justify-content: space-between; align-items: center;
  width: 100%; padding: 15px 0; margin-top: 8px;
  background: none; border: none; border-top: 1px solid var(--border-glass);
  font-family: inherit; font-size: .86rem; font-weight: 600;
  color: var(--text-sub); cursor: pointer;
}
.flow-foot:hover { color: var(--gold-primary); }
.flow-foot span:last-child { color: var(--text-muted); }

/* ------------------------------------------------- shared history row -----
   Sell history, transfer history and delivery orders were three different
   card treatments for the same shape of fact. They are one row now. */
.gold-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 15px 0;
  border-bottom: 1px solid var(--border-glass);
}
.gold-row__lead {
  flex-shrink: 0; width: 62px; padding-top: 2px;
  font-size: .66rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted);
}
.gold-row__lead.is-out { color: #F08A7A; }
.gold-row__lead.is-in { color: #7FD6A2; }
.gold-row__main { flex: 1; min-width: 0; }
.gold-row__top {
  display: flex; justify-content: space-between; gap: 10px;
  align-items: baseline; margin-bottom: 4px;
}
.gold-row__title { font-size: .95rem; font-weight: 700; color: var(--text-main); }
.gold-row__amt { font-size: 1rem; font-weight: 800; color: var(--gold-primary); }
.gold-row__meta {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: .76rem; color: var(--text-muted);
}
.gold-row__id {
  font-size: .7rem; color: var(--text-muted);
  margin-top: 6px; opacity: .75; word-break: break-all;
}

/* ------------------------------------------------------- redeem + FD ----- */
.redeem-card {
  display: flex; justify-content: space-between; align-items: center; gap: 14px;
  padding: 15px 16px; margin-bottom: 10px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 14px;
  width: 100%; text-align: left;
  font-family: inherit; cursor: pointer;
}
.redeem-card:hover { border-color: var(--border-gold); }
.redeem-card:active { transform: scale(.99); }
.redeem-card[disabled] { opacity: .5; cursor: not-allowed; }
.redeem-card__name { font-size: .95rem; font-weight: 700; color: var(--text-main); }
.redeem-card__meta { font-size: .76rem; color: var(--text-muted); margin-top: 4px; }
.redeem-card__weight {
  font-family: var(--font-display);
  font-size: 1.15rem; font-weight: 700; color: var(--gold-primary);
  white-space: nowrap;
}

.fd-scheme {
  padding: 16px; margin-bottom: 10px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: 14px;
}
.fd-scheme__top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.fd-scheme__name { font-size: .98rem; font-weight: 700; color: var(--text-main); }
.fd-scheme__rate {
  font-family: var(--font-display);
  font-size: 1.25rem; font-weight: 700; color: var(--gold-primary); white-space: nowrap;
}
.fd-scheme__meta { font-size: .78rem; color: var(--text-muted); margin-bottom: 14px; line-height: 1.5; }

/* Signing out is not a gold action, and Account is not a gold screen. The
   gold outline button stays where it belongs - the passbook and Gold FD. */
.btn-quiet-outline {
  width: 100%;
  padding: 15px;
  background: transparent;
  border: 1.5px solid rgba(255, 255, 255, .16);
  border-radius: var(--radius-sm);
  color: var(--text-sub);
  font-family: inherit;
  font-weight: 800;
  font-size: .95rem;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.btn-quiet-outline:hover {
  background: rgba(255, 255, 255, .05);
  border-color: rgba(255, 255, 255, .28);
  color: var(--text-main);
}
.btn-quiet-outline:active { transform: scale(.995); }

/* A membership is an EZ Deals thing, so a paid one reads in lime. Free is a
   state rather than a warning, so it is not amber either. */
.status-pill.deal {
  background: var(--deal-glow);
  color: var(--deal);
  border: 1px solid rgba(203, 243, 74, .3);
}
.status-pill.neutral {
  background: rgba(255, 255, 255, .06);
  color: var(--text-muted);
  border: 1px solid rgba(255, 255, 255, .12);
}

.section-label {
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; color: var(--text-muted);
  padding-bottom: 10px; margin: 26px 0 4px;
  border-bottom: 1px solid var(--border-glass);
}
.section-label:first-child { margin-top: 0; }

/* =========================================================================
   PHASE 6 — Account and support (account · kyc · sip · membership · faqs · support)

   Account becomes the hub these six screens hang off, rather than six unrelated
   destinations reachable only from the drawer. Everything here reuses the
   Phase 5 flow grammar (.flow-head, .flow-field, .section-label, .gold-row).
   ========================================================================= */

/* Identity card. The SaveEZ ID is here because Send Gold asks the sender for
   the recipient's ID, and until now there was nowhere to read your own. */
.acct-id {
  padding: 18px;
  margin-bottom: 22px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
}
.acct-id__name {
  font-family: var(--font-display);
  font-size: 1.25rem; font-weight: 700; letter-spacing: -.01em;
  color: var(--text-main);
}
.acct-id__contact {
  font-size: .82rem; color: var(--text-muted);
  line-height: 1.6; margin-top: 4px;
}
.acct-id__key {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 14px; padding-top: 14px;
  border-top: 1px solid var(--border-glass);
}
.acct-id__key div { min-width: 0; }
.acct-id__key .flow-strip__label { margin-bottom: 2px; }
.acct-id__value {
  font-size: .92rem; font-weight: 700; color: var(--gold-primary);
  word-break: break-all;
}
.acct-copy {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  flex-shrink: 0; min-height: 44px; padding: 7px 14px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px;
  color: var(--text-sub);
  font-family: inherit; font-size: .76rem; font-weight: 700;
  cursor: pointer;
}
.acct-copy:hover { background: rgba(255, 255, 255, .06); color: var(--text-main); }
.acct-copy:active { transform: scale(.96); }

/* A navigation row that also reports a status, so the hub answers "is my KYC
   done?" without making the user open the screen to find out. */
.acct-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 15px 0;
  background: none; border: none;
  border-bottom: 1px solid var(--border-glass);
  font-family: inherit; text-align: left; cursor: pointer;
}
.acct-row__icon { color: var(--text-sub); flex-shrink: 0; display: flex; }
.acct-row__body { flex: 1; min-width: 0; }
.acct-row__label { display: block; font-size: .92rem; font-weight: 700; color: var(--text-main); }
.acct-row__hint { display: block; font-size: .74rem; color: var(--text-muted); margin-top: 2px; line-height: 1.4; }
.acct-row__end { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.acct-row__chev { color: var(--text-muted); display: flex; }
.acct-row:hover .acct-row__label { color: var(--text-main); }
.acct-row:hover .acct-row__icon { color: var(--text-main); }

.empty-line {
  padding: 18px 0;
  font-size: .82rem; color: var(--text-muted); line-height: 1.55;
  border-bottom: 1px solid var(--border-glass);
}

/* ------------------------------------------------------- status banner ---- */
.status-band {
  display: flex; align-items: center; gap: 14px;
  padding: 16px; margin-bottom: 20px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
}
.status-band--ok { border-color: rgba(47, 158, 110, .45); }
.status-band--wait { border-color: rgba(217, 146, 43, .45); }
.status-band--stop { border-color: rgba(210, 69, 60, .45); }
.status-band__mark {
  width: 38px; height: 38px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
}
.status-band--ok .status-band__mark { background: var(--success-bg); color: var(--success); }
.status-band--wait .status-band__mark { background: var(--warning-bg); color: var(--warning); }
.status-band--stop .status-band__mark { background: var(--danger-bg); color: var(--danger); }
.status-band__title { font-size: .95rem; font-weight: 700; color: var(--text-main); }
.status-band__sub { font-size: .78rem; color: var(--text-muted); margin-top: 3px; line-height: 1.5; }

/* ------------------------------------------------------------ membership --
   Membership gates the gift-card catalogue and nothing on the gold side, so
   this screen speaks in the EZ Deals lime, not bullion gold. */
.plan-hero {
  padding: 24px 20px; margin-bottom: 4px;
  text-align: center;
  background: var(--bg-card-dark);
  border: 1px solid rgba(203, 243, 74, .32);
  border-radius: var(--radius-md);
}
.plan-hero__label {
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; color: var(--text-muted);
}
.plan-hero__name {
  font-family: var(--font-display);
  font-size: 2rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--deal); margin: 6px 0 4px; text-transform: capitalize;
}
.plan-hero__meta { font-size: .8rem; color: var(--text-muted); }

.plan-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 15px 0;
  border-bottom: 1px solid var(--border-glass);
}
.plan-row__name { font-size: .95rem; font-weight: 700; color: var(--text-main); }
.plan-row__gst { font-size: .72rem; color: var(--text-muted); margin-top: 3px; }
.plan-row__price {
  font-family: var(--font-display);
  font-size: 1.15rem; font-weight: 700; color: var(--text-main); white-space: nowrap;
}
.plan-row.is-current .plan-row__name,
.plan-row.is-current .plan-row__price { color: var(--deal); }

/* ------------------------------------------------------------------ faqs -- */
.faq-cat {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 16px 0;
  background: none; border: none;
  border-bottom: 1px solid var(--border-glass);
  font-family: inherit; text-align: left; cursor: pointer;
}
.faq-cat__title { font-size: .95rem; font-weight: 700; color: var(--text-main); }
.faq-cat.is-open .faq-cat__title { color: var(--gold-primary); }
.faq-cat__sign { color: var(--gold-primary); font-size: 1.1rem; font-weight: 700; line-height: 1; }

.faq-list { padding: 4px 0 8px 2px; border-bottom: 1px solid var(--border-glass); }
.faq-q {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  width: 100%; padding: 13px 0;
  background: none; border: none;
  font-family: inherit; text-align: left; cursor: pointer;
}
.faq-q__text { font-size: .86rem; font-weight: 600; color: var(--text-sub); line-height: 1.45; }
.faq-q__sign { color: var(--text-muted); font-size: 1rem; font-weight: 700; line-height: 1.2; flex-shrink: 0; }
.faq-a {
  font-size: .84rem; color: var(--text-muted);
  line-height: 1.65; padding: 0 0 14px 0;
}
.faq-loading { padding: 16px 0; font-size: .82rem; color: var(--text-muted); }

/* --------------------------------------------------------------- support -- */
.ticket-row {
  padding: 15px 0;
  border-bottom: 1px solid var(--border-glass);
}
.ticket-row__top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin-bottom: 5px;
}
.ticket-row__subject { font-size: .93rem; font-weight: 700; color: var(--text-main); }
.ticket-row__meta { font-size: .73rem; color: var(--text-muted); }
.ticket-row__body { font-size: .8rem; color: var(--text-muted); line-height: 1.55; margin-top: 7px; }

.contact-card {
  padding: 18px; margin-top: 24px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  text-align: center;
}
.contact-card__label {
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; color: var(--text-muted); margin-bottom: 10px;
}
.contact-card a {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px;
  font-size: .92rem; font-weight: 700;
  color: var(--gold-primary); text-decoration: none;
}
.contact-card a:last-child { margin-bottom: 0; }

textarea.form-input { resize: vertical; min-height: 96px; line-height: 1.5; }

/* =========================================================================
   PHASE 7 — polish

   Screen titles and section labels became real headings (h1/h2) so the page
   has a document outline for a screen reader. The elements are unchanged
   visually; only the browser's default heading margins need clearing.
   ========================================================================= */
h1.flow-head__title, h2.section-label, h1.acct-id__name { margin: 0; }
h2.section-label { margin: 26px 0 4px; }
h2.section-label:first-child { margin-top: 0; }

/* The route indicator. It only appears when a render is slow enough to notice,
   so ordinary navigation shows nothing at all. */
/* ------------------------------------------------- impersonation bar */
/* Shown only when a super admin has opened a customer's account from the admin
   panel. Deliberately the loudest thing on the screen: an impersonated session
   that looks like an ordinary one is how somebody acts on the wrong account.
   Red rather than gold or lime on purpose - it belongs to neither product and
   is not a feature of the app, it is a warning about who you are. */
.imp-bar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 14px;
    background: var(--danger);
    color: #fff;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .35);
}

.imp-bar__text {
    min-width: 0;
    font-size: .78rem;
    line-height: 1.3;
}

.imp-bar__text strong { display: block; font-weight: 700; }
.imp-bar__text span   { opacity: .9; }

.imp-bar__exit {
    flex: none;
    border: 1px solid rgba(255, 255, 255, .55);
    background: rgba(255, 255, 255, .12);
    color: #fff;
    font-size: .76rem;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 999px;
    /* 44px of hit area without a 44px-looking button. */
    position: relative;
}
.imp-bar__exit::before { content: ''; position: absolute; inset: -10px; }

/* Making room for it.

   Two rules, and the first version had only one - which broke the header. The
   shell already carries `padding-top: var(--header-height)` to clear the fixed
   .app-header, so setting padding-top to the bar height REPLACED that
   clearance instead of adding to it, and .app-header is itself `position:
   fixed; top: 0`, so it sat underneath the bar either way. Both have to move:
   the header down by the bar's height, and the shell's padding down by both. */
/* A fallback only. The real value is measured and set on <html> by
   AppComponents.paintImpersonationBar, because a long name wraps. */
:root { --imp-bar-height: 48px; }

body.has-imp-bar #app-shell  { padding-top: calc(var(--header-height) + var(--imp-bar-height)); }
body.has-imp-bar .app-header { top: var(--imp-bar-height); }

/* The drawer and any full-height overlay start below the bar too, so it stays
   readable while they are open - the bar is the one thing that should never be
   covered. */
body.has-imp-bar .side-drawer { top: var(--imp-bar-height); }

/* ------------------------------------------------------------- skeletons */
/* Painted by the router when a route takes longer than 150ms to answer, so a
   waiting screen shows the shape of what is coming rather than a blank. They
   are built from the same spacing and radii as the real components, because a
   skeleton whose blocks do not line up with the content that replaces them
   makes the page jump at the exact moment it finishes loading. */
.skel {
    background: linear-gradient(
        100deg,
        var(--bg-card-dark) 0%,
        rgba(255, 255, 255, .055) 45%,
        var(--bg-card-dark) 70%
    );
    background-size: 220% 100%;
    border-radius: 7px;
    animation: skel-sweep 1.35s ease-in-out infinite;
}

@keyframes skel-sweep {
    from { background-position: 140% 0; }
    to   { background-position: -40% 0; }
}

/* The animation is decoration. Someone who has asked for less motion still
   needs to see that something is loading, so the blocks stay and only the
   sweep stops. */
@media (prefers-reduced-motion: reduce) {
    .skel { animation: none; background: var(--bg-card-dark); }
}

.skel--bar   { display: block; }
.skel--dot   { width: 34px; height: 34px; border-radius: 50%; flex: none; }
.skel--chip  { width: 74px; height: 32px; border-radius: 999px; flex: none; }
.skel--card  { height: 168px; border-radius: var(--radius-lg); margin-bottom: 16px; }
.skel--strip { height: 76px; border-radius: var(--radius-md); margin-bottom: 14px; }
.skel--tile  { aspect-ratio: 16 / 10; width: 100%; border-radius: var(--radius-sm); }

.skel-head   { margin-bottom: 18px; }
.skel-chips  { display: flex; gap: 7px; margin-bottom: 16px; }
.skel-pair   { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 16px; }

.skel-grid   { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.skel-tile   { min-width: 0; }

.skel-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px 0;
    border-bottom: 1px solid var(--border-glass);
}
.skel-row:last-child   { border-bottom: 0; }
.skel-row__main        { flex: 1; min-width: 0; }

#route-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px;
  z-index: 60; pointer-events: none;
  background: transparent;
}
#route-progress.is-on::after {
  content: ''; display: block; height: 100%; width: 40%;
  background: var(--gold-metallic);
  animation: route-slide 1.1s ease-in-out infinite;
}
@keyframes route-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
@media (prefers-reduced-motion: reduce) {
  #route-progress.is-on::after { animation: none; width: 100%; opacity: .6; }
}

/* Visible to a screen reader, not to the eye: used where the screen's title is
   already obvious from the tab bar and printing it again would be noise. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* The offline notice sits under the header and pushes nothing around; it is
   hidden entirely while online so it costs no layout. */
#offline-bar {
  display: none;
  position: fixed; left: 0; right: 0; top: var(--header-height);
  z-index: 55;
  align-items: center; justify-content: center; gap: 8px;
  padding: 9px 16px;
  background: var(--danger-bg);
  border-bottom: 1px solid rgba(210, 69, 60, .45);
  color: #F3B4AE;
  font-size: .78rem; font-weight: 700;
  backdrop-filter: blur(8px);
}
#offline-bar.is-on { display: flex; }
/* The bar is fixed, so the screen underneath has to make room for it or the
   first band of content sits behind it. */
body.is-offline #view-container { padding-top: 42px; }
#offline-bar svg { flex-shrink: 0; }

/* =========================================================================
   Plan-gated gift cards

   Cards above the member's plan are shown rather than hidden, carrying the
   plan that unlocks them. They stay legible and appealing on purpose — the
   badge is an invitation to upgrade, not a "no entry" sign — so the artwork
   keeps its colour and only a small label is added.
   ========================================================================= */

.deal-tile__plan {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  padding: 3px 8px;
  border-radius: 6px;
  font-size: .58rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
  backdrop-filter: blur(6px);
  box-shadow: 0 2px 8px rgba(10, 11, 14, .35);
}

/* Plus reads as the ordinary next step; Elite as the premium one. */
.deal-tile__plan--plus {
  background: rgba(10, 11, 14, .82);
  color: #FFFFFF;
  border: 1px solid rgba(255, 255, 255, .18);
}

.deal-tile__plan--elite {
  background: var(--gold-metallic);
  color: #1A160F;
  border: 1px solid rgba(255, 255, 255, .25);
}

/* The art needs a positioning context for the badge. */
.deal-tile__art { position: relative; }

/* A locked tile is still inviting, just quieter than one you can buy today. */
.deal-tile.is-locked .deal-tile__art img { opacity: .92; }
.deal-tile.is-locked .deal-tile__off { color: var(--text-muted); }
.deal-tile.is-locked:hover .deal-tile__art img { opacity: 1; }

.ez-empty-box {
  text-align: center;
  padding: 36px 16px;
  background: var(--bg-card-dark, #12151B);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 16px;
  margin: 16px 0;
}

.ez-empty-icon {
  font-size: 2rem;
  margin-bottom: 8px;
}

.ez-empty-title {
  font-family: var(--font-display, inherit);
  font-size: 1rem;
  font-weight: 800;
  color: #FFFFFF;
  margin-bottom: 4px;
}

.ez-empty-sub {
  font-size: 0.8rem;
  color: var(--text-muted, #94A3B8);
  max-width: 280px;
  margin: 0 auto;
  line-height: 1.4;
}

/* =========================================================================
   Deal-toned modal

   The bottom sheet is shared by gold and EZ Deals. Gold keeps the gold
   accent; EZ Deals is a lime product and should never open a gold sheet,
   so the tone is switched on the sheet rather than duplicating the modal.
   ========================================================================= */

/* The title carried an inline colour, which no stylesheet rule could beat.
   It lives here now so the tone can actually switch it. */
#modal-title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--gold-primary);
  margin-bottom: 14px;
}

.modal-sheet.is-deal { border-top-color: rgba(203, 243, 74, .35); }
.modal-sheet.is-deal #modal-title { color: var(--deal); }
.modal-sheet.is-deal .form-input:focus {
  border-color: var(--deal);
  box-shadow: 0 0 0 3px var(--deal-glow);
}

/* Secondary action inside a deal sheet. */
.btn-deal-outline {
  display: block;
  width: 100%;
  padding: 14px;
  border-radius: 14px;
  background: transparent;
  border: 1px solid rgba(203, 243, 74, .35);
  color: var(--deal);
  font-family: inherit;
  font-size: .95rem;
  font-weight: 700;
  cursor: pointer;
}
.btn-deal-outline:hover { border-color: var(--deal); background: rgba(203, 243, 74, .06); }
.btn-deal-outline:active { transform: translateY(1px); }

/* =========================================================================
   EZ Deals control bar — one row

   Replaces three stacked rows (plan pills, category pills, count + sort).
   A fixed pair of buttons stays put while the category chips scroll past
   them, so the two controls that change what you are looking at are always
   reachable without scrolling the row back.
   ========================================================================= */

.ez-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 14px;
  min-width: 0;
}

.ez-bar__fixed {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
  padding-right: 8px;
  border-right: 1px solid var(--border-glass);
}

.ez-bar__scroll {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding: 2px 0;
  min-width: 0;
}
.ez-bar__scroll::-webkit-scrollbar { display: none; }

.ez-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  min-height: 36px;
  padding: 0 13px;
  border-radius: 999px;
  border: 1px solid var(--border-glass);
  background: transparent;
  color: var(--text-sub);
  font-family: inherit;
  font-size: .82rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.ez-chip::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

.ez-chip:hover { border-color: rgba(203, 243, 74, .4); color: var(--text-main); }
.ez-chip:active { transform: scale(.97); }

.ez-chip.is-on {
  background: var(--deal);
  border-color: var(--deal);
  color: #12160A;
  font-weight: 700;
}

/* The count rides inside the chip rather than taking its own line. */
.ez-chip__n {
  font-size: .74rem;
  font-weight: 700;
  opacity: .62;
}
.ez-chip.is-on .ez-chip__n { opacity: .75; }

/* "On my plan" — the one filter worth a permanent, distinct control. */
.ez-chip--mine { padding: 0 11px; color: var(--deal); border-color: rgba(203, 243, 74, .32); }
.ez-chip--mine.is-on { color: #12160A; }

.ez-chip--sheet { position: relative; padding: 0 11px; }

.ez-chip__dot {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--deal);
  color: #12160A;
  font-size: .62rem;
  font-weight: 800;
}

/* ------------------------------------------------------- the filter sheet */
.sheet-group { margin-bottom: 20px; }

.sheet-group__head {
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding-bottom: 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border-glass);
}

.sheet-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  padding: 10px 2px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border-glass);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.sheet-opt:last-of-type { border-bottom: 0; }

.sheet-opt__label { display: block; font-size: .95rem; font-weight: 600; color: var(--text-main); }
.sheet-opt__note { display: block; font-size: .76rem; color: var(--text-muted); margin-top: 2px; }
.sheet-opt__tick { color: var(--deal); opacity: 0; flex-shrink: 0; }

.sheet-opt.is-on .sheet-opt__label { color: var(--deal); }
.sheet-opt.is-on .sheet-opt__tick { opacity: 1; }

/* -------------------------------------------------------------------------
   Desktop affordances for the chip row

   The row is swipeable on a phone, but with a mouse there was no way to reach
   the far end: the scrollbar is hidden, the wheel scrolls the page, and there
   was no drag. The behaviour is in app.js; this is what it looks like.
   ------------------------------------------------------------------------- */

.ez-bar__scroll {
  cursor: grab;
  scroll-behavior: smooth;
  /* Fades are applied as a mask so they work over any background. */
  --edge-l: transparent 0, #000 0;
  --edge-r: #000 calc(100% - 0px), transparent 100%;
  -webkit-mask-image: linear-gradient(to right, var(--edge-l), var(--edge-r));
          mask-image: linear-gradient(to right, var(--edge-l), var(--edge-r));
}

.ez-bar__scroll.has-more-left {
  --edge-l: transparent 0, #000 22px;
}

.ez-bar__scroll.has-more-right {
  --edge-r: #000 calc(100% - 26px), transparent 100%;
}

/* While dragging, kill the smooth scroll so the row tracks the pointer, and
   stop the chips swallowing the gesture as text selection. */
.ez-bar__scroll.is-dragging {
  cursor: grabbing;
  scroll-behavior: auto;
  user-select: none;
}
.ez-bar__scroll.is-dragging .ez-chip { pointer-events: none; }



@media (prefers-reduced-motion: reduce) {
  .ez-bar__scroll { scroll-behavior: auto; }
}

/* =========================================================================
   Gift card purchase sheet

   The sheet used to carry a brand name and two number inputs. Everything the
   catalogue already knew — the artwork, the discount, what the value may be,
   how long it lasts, how to redeem it and the terms — was thrown away on the
   way in. This is what showing it looks like.
   ========================================================================= */

/* --------------------------------------------------------------------------
   The voucher, drawn rather than photographed.

   The vendor's artwork is 500px at its largest, so across the width of this
   sheet it was either small and lost or large and soft - which is exactly
   what it looked like. So the card is ours: the brand's own colour taken off
   its artwork, the amount set in type at whatever size suits, and the picture
   reduced to a small mark where 500px is far more than enough.

   --accent is set per card from assets/data/brand-colours.json, and every
   colour in that file is dark enough for white text to clear WCAG AA.
   ----------------------------------------------------------------------- */
.gc-card {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 14px;
  margin: -6px -4px 18px;
  padding: 14px;
  border-radius: 18px;
  overflow: hidden;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, .17) 0%, rgba(255, 255, 255, 0) 46%),
    var(--accent, #23262E);
  box-shadow: 0 16px 34px -14px rgba(0, 0, 0, .75);
  color: #FFF;
}

/* The sheen a plastic card catches, and the only thing here that is
   decoration rather than information. */
.gc-card::after {
  content: '';
  position: absolute;
  top: -60%;
  right: -22%;
  width: 62%;
  height: 210%;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .1) 50%, rgba(255, 255, 255, 0) 100%);
  transform: rotate(18deg);
  pointer-events: none;
}

/* The artwork, at a size where it can actually be read.
   It was a 56x34 chip, which turned BodyCraft's card - a photograph, a
   wordmark and a strapline - into a brown smudge. A composition does not
   shrink; it only becomes illegible, and illegible reads as blurred whatever
   the pixel count says.
   132px square against the 500px rendition is roughly a quarter scale, so it
   is sharp at any pixel density, and `contain` means nothing is cropped. */
.gc-card__mark {
  flex: 0 0 auto;
  display: block;
  position: relative;
  width: 132px;
  align-self: center;
  aspect-ratio: 1 / 1;
  border-radius: 13px;
  overflow: hidden;
  background: #FFF;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .32);
}

/* The artwork fills the tile rather than sitting in a white mat. The mat was
   invisible on the cards printed on cream and a heavy frame around the ones
   printed edge to edge, so the same rule looked like two different designs
   depending on the brand.
   The window is square and so is the rendition, so filling it crops nothing. */
.gc-card__mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Half the catalogue's artwork is printed on white. Without this the pale
   ones dissolve into a pale brand colour and lose their edge. */
.gc-card__mark::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 13px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .14);
  pointer-events: none;
}

.gc-card__body {
  position: relative;
  z-index: 1;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 10px;
  min-height: 132px;
}

.gc-card__brand {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -.015em;
  color: rgba(255, 255, 255, .96);
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.gc-card__count {
  display: block;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .72);
  margin-bottom: 1px;
}

/* Shrinks a step at a time rather than overflowing: this column is narrow and
   a jewellery voucher can run to six figures. */
.gc-card__value {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 8.5vw, 2.05rem);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.05;
  color: #FFF;
  white-space: nowrap;
}

.gc-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.gc-card__off {
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .17);
  font-size: .74rem;
  font-weight: 800;
  color: #FFF;
}

.gc-card__by {
  font-family: var(--font-display);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .02em;
  color: rgba(255, 255, 255, .58);
}

/* The locked card. Same object, but there is no amount to print on it - the
   reader cannot buy this yet - so the plan takes that line instead, set
   smaller because it is a word rather than a figure. */
.gc-card--locked .gc-card__value {
  font-size: 1.3rem;
  letter-spacing: -.01em;
}
.gc-card--locked .gc-card__off { font-weight: 700; }

/* No artwork at all: the type takes the whole width rather than leaving a
   gap where a picture would have been. */
.gc-card--noart .gc-card__body { min-height: 118px; }
.gc-card--noart .gc-card__value { font-size: 2.3rem; }


.gc-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}
.gc-head__brand {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -.015em;
  color: var(--text-main);
}
.gc-head__cat { font-size: .78rem; color: var(--text-muted); margin-top: 2px; }

.gc-off {
  flex-shrink: 0;
  padding: 5px 11px;
  border-radius: 999px;
  background: var(--deal);
  color: #12160A;
  font-size: .8rem;
  font-weight: 800;
}

/* Shown only when the value or quantity is outside what the brand allows, so
   the order is not refused after the fact. */
.gc-warn {
  margin: -8px 0 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--danger-bg);
  border: 1px solid rgba(210, 69, 60, .35);
  color: #F3B4AE;
  font-size: .82rem;
  line-height: 1.5;
}

/* Redemption steps and terms: long, and only wanted sometimes. */
.gc-sec {
  border-top: 1px solid var(--border-glass);
}
.gc-sec:last-of-type { border-bottom: 1px solid var(--border-glass); }

.gc-sec > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 48px;
  padding: 4px 2px;
  cursor: pointer;
  list-style: none;
  font-size: .9rem;
  font-weight: 600;
  color: var(--text-sub);
}
.gc-sec > summary::-webkit-details-marker { display: none; }
.gc-sec > summary svg { color: var(--text-muted); transition: transform .18s ease; flex-shrink: 0; }
.gc-sec[open] > summary { color: var(--deal); }
.gc-sec[open] > summary svg { transform: rotate(180deg); color: var(--deal); }

.gc-sec__body { padding: 2px 2px 14px; }
.gc-sec__body p {
  margin: 0 0 8px;
  font-size: .84rem;
  line-height: 1.6;
  color: var(--text-muted);
}
.gc-sec__body p:last-child { margin-bottom: 0; }

.gc-sec__link {
  display: inline-block;
  margin: 0 2px 14px;
  font-size: .82rem;
  font-weight: 700;
  color: var(--deal);
}

@media (prefers-reduced-motion: reduce) {
  .gc-sec > summary svg { transition: none; }
}

/* The value field and the running total are shared with the gold flows, so
   inside a deal sheet they have to take the lime accent too — otherwise the
   currency mark and the total read gold on an EZ Deals surface. */
.modal-sheet.is-deal .flow-amount { border-color: rgba(203, 243, 74, .35); }
.modal-sheet.is-deal .flow-amount:focus-within { border-color: var(--deal); }
.modal-sheet.is-deal .flow-amount__unit {
  background: rgba(203, 243, 74, .12);
  color: var(--deal);
  border-color: rgba(203, 243, 74, .35) !important;
}
.modal-sheet.is-deal .flow-quote__value { color: var(--deal); }

/* The category line carries the row on its own now the brand name is not
   repeated under the title. */
.gc-head__cat { font-size: .8rem; color: var(--text-muted); margin-top: 0; text-transform: capitalize; }
.gc-head { align-items: center; margin-bottom: 20px; }


/* =========================================================================
   No scrollbars anywhere

   This is a phone app that also runs in a desktop browser. A native app has
   no visible scrollbar, so neither does this: every scrollable surface keeps
   its scrolling and loses its bar. Where a row can scroll sideways, the edge
   fade is what says so (see .ez-bar__scroll).
   ========================================================================= */

html,
body,
#view-container,
.modal-sheet,
.side-drawer,
.card-body,
[class*="__scroll"],
.filter-pills {
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* old Edge */
}

html::-webkit-scrollbar,
body::-webkit-scrollbar,
#view-container::-webkit-scrollbar,
.modal-sheet::-webkit-scrollbar,
.side-drawer::-webkit-scrollbar,
.card-body::-webkit-scrollbar,
[class*="__scroll"]::-webkit-scrollbar,
.filter-pills::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

/* -------------------------------------------------------------------------
   Gift card bill

   Three lines rather than one total, because the discount comes off the price
   and not off the voucher: the card still arrives worth its full face value,
   which is the part worth being explicit about.
   ------------------------------------------------------------------------- */

.gc-bill {
  margin: 4px 0 18px;
  padding: 14px 16px;
  border-radius: 12px;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-glass);
}

.gc-bill__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  font-size: .88rem;
  color: var(--text-muted);
}

.gc-bill__row--off span:last-child { color: var(--deal); font-weight: 700; }

.gc-bill__row--pay {
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--border-glass);
  color: var(--text-sub);
  font-weight: 600;
}
.gc-bill__row--pay span:last-child {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--deal);
}

/* ------------------------------------------------------- back from PhonePe --
   What was bought and what it cost lead; the outcome band says what happened
   to the money. Gold purchases stay in bullion gold, vouchers in deal lime. */
.pay-result {
  padding: 22px 20px; margin-bottom: 14px;
  text-align: center;
  background: var(--bg-card-dark);
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-md);
}
.pay-result--deal { border-color: rgba(203, 243, 74, .32); }
.pay-result__what {
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; color: var(--text-muted);
}
.pay-result__amount {
  font-family: var(--font-display);
  font-size: 2rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--gold-primary); margin-top: 6px;
}
.pay-result--deal .pay-result__amount { color: var(--deal); }
.pay-ref { margin: 12px 2px 0; font-size: .74rem; color: var(--text-muted); }
.pay-ref .num { color: var(--text-main); letter-spacing: .02em; word-break: break-all; }
.pay-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }

/* ---------------------------------------------------------- Gold SIP setup -- */
.sip-chips { display: flex; gap: 8px; margin: 4px 0 14px; }
.sip-chip {
  flex: 1; min-height: 44px;
  background: var(--bg-card-dark); color: var(--text-main);
  border: 1px solid var(--border-glass); border-radius: 999px;
  font-family: var(--font-family); font-weight: 700; font-size: .92rem;
  cursor: pointer;
}
.sip-chip.is-on { border-color: var(--gold-primary); color: var(--gold-primary); box-shadow: 0 0 0 3px rgba(224, 177, 66, .14); }

.mandate-lead { font-size: .88rem; color: var(--text-muted); line-height: 1.5; margin-bottom: 14px; }
/* The code is scanned by a camera, so it sits on white whatever the theme. */
.mandate-qr {
  width: 220px; max-width: 70%; margin: 0 auto 16px;
  padding: 10px; background: #FFFFFF; border-radius: var(--radius-md);
}
.mandate-qr svg { display: block; width: 100%; height: auto; }
.mandate-open { display: block; text-align: center; text-decoration: none; margin-bottom: 14px; }
.mandate-status {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; margin-bottom: 12px;
  border: 1px solid var(--border-glass); border-radius: var(--radius-md);
  font-size: .85rem; font-weight: 600; color: var(--text-main);
}
.mandate-status__dot {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  background: var(--warning); animation: mandate-pulse 1.4s ease-in-out infinite;
}
.mandate-status[data-tone="ok"] .mandate-status__dot { background: var(--success); animation: none; }
.mandate-status[data-tone="stop"] .mandate-status__dot { background: var(--danger); animation: none; }
@keyframes mandate-pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .mandate-status__dot { animation: none; } }

/* The way out of a failed withdrawal, so it has to look like a button. */
.band-action {
  margin-top: 10px; padding: 9px 16px; min-height: 40px;
  background: transparent; color: var(--text-main);
  border: 1px solid var(--border-glass); border-radius: 999px;
  font-family: var(--font-family); font-size: .84rem; font-weight: 700;
  cursor: pointer;
}
.status-band--stop .band-action { border-color: rgba(210, 69, 60, .5); color: var(--danger); }
.band-action:hover { background: rgba(255, 255, 255, .04); }

/* ---------------------------------------------------------- boot splash -- */
#boot-splash .splash-aura {
  position: absolute; top: 50%; left: 50%;
  width: 360px; height: 360px; transform: translate(-50%, -62%);
  pointer-events: none;
  background: radial-gradient(circle at 50% 50%, rgba(224, 177, 66, .18) 0%, rgba(224, 177, 66, .05) 40%, transparent 70%);
}
#boot-splash .splash-tag {
  margin-top: 6px;
  font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold-primary);
  opacity: .85;
}

/* Signed out there is no menu to open - the button is hidden - but the
   drawer stayed in the layout, and its footer is taller than the viewport,
   which is what made the sign-in screen scroll past its own content. */
#app-shell.is-signed-out .side-drawer,
#app-shell.is-signed-out .side-drawer-overlay {
    display: none;
}

/* The shell reserves room for the header and the bottom nav. Both are hidden
   on the sign-in screens, so that reserved space was left as empty scroll
   below the fold. The view container keeps its own top inset (above). */
#app-shell.is-signed-out {
    padding-top: 0;
    padding-bottom: 0;
}

/* ========================================================================
   SIGN IN
   The gift cards are not a section on this screen - they are the screen. A
   wall of the real catalogue artwork drifts behind everything, the sign-in
   sheet rises over it the way a native app asks for something, and the gold
   rate rides the seam between the two.

   Every earlier version was a column of blocks that could not fill a tall
   phone, so the surplus height showed up as a hole above the deals. A wall
   grows to whatever height it is given, so there is no surplus left.

   Two colours, both already in the mark: gold is the asset, lime is the
   action, and neither borrows the other's.
   ======================================================================== */

/* The view container pads every screen and adds a top inset while the header
   is hidden. This one is full bleed, so it takes that padding back and
   manages its own. */
.signin {
  display: flex;
  flex-direction: column;
  margin: calc(-28px - env(safe-area-inset-top, 0px)) -16px -16px;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--bg-dark);
}

/* ------------------------------------------------------------ the wall -- */
.signin__canvas {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* Takes every pixel the sheet does not need, which is the whole point. */
  flex: 1 1 auto;
  min-height: 220px;
  overflow: hidden;
}

.cardwall {
  position: absolute;
  /* Oversized and turned, so the grid never shows an edge and never reads as
     a grid. */
  inset: -20% -30%;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  transform: rotate(-10deg);
  opacity: 0;
  transition: opacity 700ms ease;
}
.cardwall.is-on { opacity: .44; }
.cardwall__col { overflow: hidden; }
.cardwall__run {
  display: flex;
  flex-direction: column;
  gap: 8px;
  animation: wall-drift 54s linear infinite;
  will-change: transform;
}
/* Three speeds and one reversal, so the columns never line up into a pattern
   the eye can lock onto. */
.cardwall__col:nth-child(2) .cardwall__run { animation-duration: 71s; animation-direction: reverse; }
.cardwall__col:nth-child(3) .cardwall__run { animation-duration: 61s; }
@keyframes wall-drift {
  from { transform: translateY(0); }
  /* Each column is rendered twice, so one full set is half the run plus half
     a gap - without that correction the loop jumps by 5px every pass. */
  to   { transform: translateY(calc(-50% - 4px)); }
}
.cardwall img {
  display: block;
  width: 100%;
  aspect-ratio: 5 / 3;
  object-fit: cover;
  border-radius: 8px;
}

/* Dark at the top so the lockup reads, clear through the middle so the wall
   is actually visible, and dark again at the foot - but never quite solid, or
   the sheet's rounded corners would have nothing to sit against. */
.signin__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(10, 11, 14, .93) 0%,
    rgba(10, 11, 14, .56) 28%,
    rgba(10, 11, 14, .72) 70%,
    rgba(10, 11, 14, .78) 100%);
}
/* A vault's worth of light, behind the mark. */
.signin__aura {
  position: absolute;
  top: -18%; left: -12%;
  width: 420px; height: 420px; max-width: 130%;
  pointer-events: none;
  background: radial-gradient(circle at 50% 50%,
    rgba(224, 177, 66, .22) 0%, rgba(224, 177, 66, .06) 42%, transparent 70%);
}

.signin__brand,
.signin__over { position: relative; z-index: 2; }
.signin__brand {
  padding: max(24px, env(safe-area-inset-top, 0px)) 16px 0;
  animation: signin-rise 340ms cubic-bezier(.2, .7, .3, 1) backwards;
}
.signin__lockup { display: block; width: 150px; height: auto; }

.signin__over {
  padding: 0 16px 38px;   /* clears the 30px the rate strip rises */
  animation: signin-rise 340ms cubic-bezier(.2, .7, .3, 1) 60ms backwards;
}
.signin__head {
  margin: 0 0 9px;
  font-family: var(--font-display);
  /* Scales with the phone rather than stepping at a breakpoint. */
  font-size: clamp(1.7rem, 8.4vw, 2.15rem);
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.04;
  color: var(--text-main);
  text-shadow: 0 2px 28px rgba(10, 11, 14, .85);
}
.signin__pitch {
  margin: 0;
  font-size: .86rem;
  color: var(--text-sub);
  text-shadow: 0 1px 14px rgba(10, 11, 14, .9);
}
.signin__pitch strong { color: var(--deal); font-weight: 700; }

/* The strongest categories, as the saving each one reaches. One line that can
   be swiped rather than a grid that would cover the wall. */
.signin__chips {
  display: flex;
  flex-wrap: nowrap;
  gap: 7px;
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.chip {
  flex-shrink: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 6px 11px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-sub);
  background: rgba(10, 11, 14, .6);
  border: 1px solid rgba(255, 255, 255, .11);
  border-radius: 999px;
  -webkit-backdrop-filter: blur(9px);
  backdrop-filter: blur(9px);
}
.chip__off {
  font-family: var(--font-display);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--deal);
}

/* ----------------------------------------------------------- the sheet -- */
.signin__sheet {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  padding: 0 16px calc(16px + env(safe-area-inset-bottom, 0px));
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .03), transparent 140px),
    var(--bg-dark);
  border-top: 1px solid rgba(255, 255, 255, .13);
  border-radius: 26px 26px 0 0;
  box-shadow: 0 -26px 54px -30px rgba(0, 0, 0, 1);
  animation: signin-rise 400ms cubic-bezier(.2, .7, .3, 1) 90ms backwards;
}

/* The rate rides the seam: half on the wall, half on the sheet. It is the one
   element that belongs to both halves, which is what ties them together. */
.rate-strip {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin: -30px 0 0;
  padding: 11px 14px 26px;
  overflow: hidden;
  border-radius: 16px;
  background:
    linear-gradient(180deg, rgba(224, 177, 66, .11), rgba(224, 177, 66, .02)),
    var(--bg-card-dark);
  border: 1px solid var(--border-gold);
  box-shadow: 0 20px 38px -24px rgba(0, 0, 0, 1);
  transition: border-color 180ms ease, box-shadow 180ms ease;
}
.rate-strip:active { border-color: rgba(224, 177, 66, .55); }
@media (hover: hover) {
  .rate-strip:hover {
    border-color: rgba(224, 177, 66, .55);
    box-shadow: 0 20px 38px -24px rgba(0, 0, 0, 1),
                0 0 0 3px rgba(224, 177, 66, .07);
  }
}
/* The month of rates is the card's own floor, not a picture on it. */
.rate-strip__spark {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 26px;
  opacity: .5;
}
.rate-strip__spark svg { display: block; width: 100%; height: 100%; }
.rate-strip__main,
.rate-strip__side { position: relative; z-index: 1; min-width: 0; }
.rate-strip__side { text-align: right; }

.rate-strip__label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.rate-strip__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--gold-primary);
  box-shadow: 0 0 0 0 rgba(224, 177, 66, .55);
  animation: rate-pulse 2.4s ease-out infinite;
}
@keyframes rate-pulse {
  70%  { box-shadow: 0 0 0 7px rgba(224, 177, 66, 0); }
  100% { box-shadow: 0 0 0 0 rgba(224, 177, 66, 0); }
}
.rate-strip__value {
  display: block;
  margin-top: 4px;
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.05;
  color: var(--gold-primary);
}
.rate-strip__value span {
  font-size: .72rem; font-weight: 600;
  color: var(--text-muted); margin-left: 2px;
}
.rate-strip__delta {
  display: block;
  font-size: .72rem; font-weight: 700;
  color: var(--success);
  font-variant-numeric: tabular-nums;
}
/* Shown either way. A month where gold fell is still the truth, and for
   someone saving a little at a time it is not bad news. */
.rate-strip__delta.is-down { color: var(--text-muted); }
.rate-strip__delta span { font-weight: 600; color: var(--text-muted); margin-left: 3px; }
.rate-strip__note {
  display: block;
  margin-top: 4px;
  font-size: .67rem;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Everything the gold side does, on one line under the rate it applies to. */
.signin__ways {
  margin: 9px 2px 0;
  font-size: .72rem;
  font-weight: 600;
  color: var(--gold-primary);
  opacity: .8;
}

.signin .auth__label { padding-top: 17px; margin-bottom: 8px; }
.signin #get-otp-btn { margin-top: 14px; }
.signin .auth__legal { margin: 12px 0 0; }

@keyframes signin-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

#boot-splash .splash-lockup { width: 196px; height: auto; }

/* ----------------------------------------------------- the code, in lime --
   On the sign-in screen the gold rate rides the seam, because that screen is
   about the asset. Here the code rides it, because this screen is about the
   action: same position, same idea, the other colour.                      */
.code-strip {
  position: relative;
  margin: -34px 0 0;
  padding: 13px 12px 12px;
  border-radius: 16px;
  background:
    linear-gradient(180deg, rgba(203, 243, 74, .1), rgba(203, 243, 74, .02)),
    var(--bg-card-dark);
  border: 1px solid rgba(203, 243, 74, .34);
  box-shadow: 0 20px 38px -24px rgba(0, 0, 0, 1);
}
.code-strip__label {
  display: block;
  margin-bottom: 9px;
  text-align: center;
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Inside a card of their own, four more boxes would be a frame within a
   frame. They are cells with a rule under each instead, and the rule is what
   answers: grey while empty, lime once it has a digit. */
.signin--otp .otp-boxes { gap: 10px; margin-bottom: 0; }
.signin--otp .otp-box {
  aspect-ratio: auto;
  height: 52px;
  background: transparent;
  border: 0;
  border-bottom: 2px solid rgba(255, 255, 255, .16);
  border-radius: 0;
  caret-color: var(--deal);
  transition: border-color 180ms ease;
}
.signin--otp .otp-box:focus {
  border-bottom-color: var(--deal);
  box-shadow: none;
}
.signin--otp .otp-box.is-filled {
  background: transparent;
  border-bottom-color: rgba(203, 243, 74, .6);
  color: var(--text-main);
}
.signin--otp .otp-box.is-error { border-bottom-color: var(--danger); }

.signin--otp #verify-otp-btn { margin-top: 18px; }
.signin__resend {
  margin: 14px 0 0;
  text-align: center;
  font-size: .82rem;
  color: var(--text-muted);
}
.signin__resend strong {
  color: var(--text-sub);
  font-variant-numeric: tabular-nums;
}
/* The back control sits where the lockup does on the screen before it. */
.signin--otp .auth__back { margin: 0; }
/* The code strip rises further than the rate strip does. */
.signin--otp .signin__over { padding-bottom: 42px; }

/* Toasts are anchored above the tab bar, which is right everywhere the tab
   bar exists. On the sign-in screens there is none, so the toast dropped to
   the foot of the screen and landed square on the code boxes - covering the
   one thing the message is telling you to fill in. Over the wall instead,
   where it has nothing to obscure. */
#app-shell.is-signed-out #toast-container {
  top: calc(16px + env(safe-area-inset-top, 0px));
  bottom: auto;
  flex-direction: column;
}

/* On a short phone the wall gives way to the form, which matters more. */
@media (max-height: 700px) {
  .signin__canvas { min-height: 150px; }
  .signin__brand { padding-top: max(16px, env(safe-area-inset-top, 0px)); }
  .signin__lockup { width: 132px; }
  /* Not trimmed on a short phone: less than this and the strip rising from
     the sheet covers the line above it. */
  .signin__over { padding-bottom: 38px; }
  .signin__chips { display: none; }
  .signin__ways { display: none; }
  .rate-strip__value { font-size: 1.42rem; }
  .signin .auth__label { padding-top: 13px; }
}

/* Motion is decoration here, all of it. */
@media (prefers-reduced-motion: reduce) {
  .cardwall__run,
  .rate-strip__dot { animation: none; }
  .signin__brand,
  .signin__over,
  .signin__sheet { animation: none; }
  .rate-strip { transition: none; }
}
