/* ═══════════════ RemindClient — ShiftedTech ═══════════════ */

:root {
  /* Four brand colours and nothing else: white #FFFFFF, black #000000,
     red #CE1126, green #007A3D. Every grey below is neutral, mixed from black
     rather than tinted, so no fifth hue creeps in.

     Three conventions to know before editing.

     1. Green is the ACTION colour and red is the DANGER colour. Both are brand
        colours, so the split is by role, not by preference: Save, Confirm and
        Mark as Paid are green; Delete, Reject and Overdue are red. Making the
        primary button red would put it in the same visual language as Delete
        Student, which sits four inches below it.

     2. A status colour comes as a quartet — the solid tone drives fills, dots
        and bars, while -text / -soft / -line are the tinted-chip trio. Both
        brand colours clear 4.5:1 on white as text (red 5.6, green 5.5) and
        carry white text as a fill, which is unusually convenient; the tints
        exist for chips, not to rescue contrast.

     3. The chrome (.navbar, .sidebar) re-declares these same tokens instead of
        overriding each child rule, so everything nested inside inverts itself.
        Neither brand colour is legible on black (red 3.7, green 3.9), so the
        chrome scopes lighten them rather than using them raw. */

  --bg: #FFFFFF;              /* page ground */
  --surface: #FFFFFF;         /* cards, dialogs, calendar cells */
  --surface-subtle: #F6F6F6;  /* insets sitting on a white surface */
  --field-bg: #FFFFFF;        /* input ground */
  --sidebar: #000000;         /* the top bar */
  --sidebar-fill: #242424;    /* the client-list rail — a step lighter than the
                                  top bar so the two read as separate surfaces */
  --hover: #F0F0F0;

  --text-primary: #000000;
  --text-secondary: #5C5C5C;
  --placeholder: #8C8C8C;

  /* Cards are the same white as the page, so the border is the only thing
     separating them — it carries more weight here than it did on an off-white
     ground and is a step darker to match. */
  --border: #E0E0E0;
  --border-strong: #BFBFBF;

  --accent: #007A3D;
  --accent-hover: #005E2F;
  --accent-text: #007A3D;
  --accent-soft: #E6F3EC;
  --accent-line: #A9D4BE;
  --accent-ring: rgba(0, 122, 61, 0.22);

  /* Paid shares the action green: both mean "this went well". */
  --success: #007A3D; --success-text: #006934; --success-soft: #E6F3EC; --success-line: #A9D4BE;

  /* No amber in the palette, so "warning" is neutral rather than invented.
     Due-but-not-late is genuinely not an alarm, and the states that must shout
     — overdue, and the navbar count — have red to themselves. */
  --warning: #4A4A4A; --warning-text: #333333; --warning-soft: #F0F0F0; --warning-line: #C7C7C7;

  --danger:  #CE1126; --danger-text:  #A80E1F; --danger-soft:  #FBE8EA; --danger-line:  #EFB4BC;

  /* The wordmark's second half. Red is the louder brand colour and the
     wordmark is where it earns its place; on black it needs lightening. */
  --mark: #CE1126;

  --track: #E0E0E0;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.05), 0 6px 20px rgba(0, 0, 0, 0.07);
  --shadow-dialog: 0 24px 48px rgba(0, 0, 0, 0.22);

  --radius: 12px;
  --radius-sm: 8px;
  --radius-pill: 99px;
  --ease: 0.2s ease;

  --font-head: 'Bricolage Grotesque', system-ui, sans-serif;
  --font-body: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;

  --nav-h: 60px;
  --tabbar-h: 62px;
}

* { box-sizing: border-box; }

/* The user-agent [hidden] rule is user-agent origin, so any author `display`
   declaration outranks it. !important also beats equal-specificity class
   selectors below regardless of source order. */
[hidden] { display: none !important; }

html, body { height: 100%; }
/* An app, not a document: no rubber-band bounce dragging the whole shell
   around, and no iOS text inflation on rotate. */
html { scroll-behavior: smooth; overscroll-behavior: none; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
::selection { background: rgba(0, 122, 61, 0.22); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-head); margin: 0; letter-spacing: -0.01em; }

/* ── scrollbars ── */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track { background: transparent; border-radius: var(--radius-pill); }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
*::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Kills the 300ms double-tap-zoom delay on touch without disabling scroll. */
button, a, .btn, .chip-action, .tabbar-btn, .type-btn, .suggestion, .student, .cal-pill {
  touch-action: manipulation;
}

/* ── type scale ──
   page 24/700 · section 18/600 · card 15/700 · body 15/400 · label 12/500 */
.page-title { font-family: var(--font-head); font-size: 24px; font-weight: 700; letter-spacing: -0.01em; }
.section-heading { font-family: var(--font-head); font-size: 18px; font-weight: 600; }
.label-caps {
  font-family: var(--font-body); font-size: 12px; font-weight: 500;
  color: var(--text-secondary); letter-spacing: 0.04em; text-transform: uppercase;
}
/* Figures read as data, so they take the body face rather than the display one. */
.numeric { font-family: var(--font-body); font-size: 20px; font-weight: 600; color: var(--accent-text); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ═══════════════ wordmark ═══════════════ */
.wordmark {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 17px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  white-space: nowrap;
}
.wordmark span { color: var(--mark); }
.wordmark-lg { font-size: 28px; letter-spacing: -0.01em; }

/* ═══════════════ buttons ═══════════════ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--ease), border-color var(--ease), color var(--ease), transform var(--ease), opacity var(--ease);
}
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn-sm { padding: 7px 12px; font-size: 13px; }
.btn-block { width: 100%; }

.btn-primary { background: var(--accent); color: #FFFFFF; }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }

.btn-ghost { background: transparent; border-color: var(--border); color: var(--text-secondary); }
.btn-ghost:hover { background: var(--hover); color: var(--text-primary); border-color: var(--border-strong); }

.btn-danger { background: transparent; border-color: var(--danger-line); color: var(--danger-text); }
.btn-danger:hover { background: var(--danger-soft); }

.btn-google { width: 100%; background: #FFFFFF; color: #1F1F1F; font-weight: 600; border-color: var(--border-strong); }
.btn-google:hover { background: #F1F3F4; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  background: transparent; border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text-secondary);
  cursor: pointer; transition: all var(--ease);
}
.icon-btn:hover { color: var(--text-primary); background: var(--hover); }
.icon-btn:active:not(:disabled) { transform: scale(0.97); }
.icon-btn.is-active { color: var(--accent-text); background: var(--accent-soft); border-color: var(--accent-line); }

.linkish {
  background: none; border: 0; padding: 0;
  color: var(--accent-text); font: inherit; font-weight: 600; cursor: pointer;
  transition: color var(--ease);
}
.linkish:hover { color: var(--accent-hover); text-decoration: underline; }

/* ═══════════════ form fields ═══════════════ */
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label {
  font-size: 12px; font-weight: 500; color: var(--text-secondary);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.field-label em { font-style: normal; opacity: 0.65; }

input, select, textarea {
  width: 100%;
  padding: 10px 12px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 14px;
  transition: border-color var(--ease), box-shadow var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--placeholder); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
select { appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235C5C5C' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px;
}
textarea { resize: vertical; line-height: 1.5; }

.error {
  margin: 0;
  padding: 10px 12px;
  background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  border-radius: var(--radius-sm);
  color: var(--danger-text);
  font-size: 13px;
}

/* ═══════════════ LOGIN ═══════════════ */
.auth {
  position: relative;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  overflow: hidden;
}
.glow { position: absolute; width: 520px; height: 520px; border-radius: 50%; pointer-events: none; filter: blur(90px); }
.glow-tr { top: -240px; right: -200px; background: radial-gradient(circle, rgba(0,122,61,0.13), transparent 70%); }
.glow-bl { bottom: -260px; left: -220px; background: radial-gradient(circle, rgba(206,17,38,0.09), transparent 70%); }

.auth-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  padding: 32px 28px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}
.tagline { margin: 6px 0 24px; color: var(--text-secondary); font-size: 14px; }
.stack { display: flex; flex-direction: column; gap: 14px; }

.divider { display: flex; align-items: center; gap: 12px; margin: 18px 0; color: var(--text-secondary); font-size: 12px; }
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

.switch-mode { margin: 16px 0 0; font-size: 13px; color: var(--text-secondary); text-align: center; }
.byline { margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--border);
  color: var(--text-secondary); font-size: 12px; text-align: center; opacity: 0.75; }
#auth-error { margin-top: 16px; }

.legal-links { margin: 8px 0 0; font-size: 12px; text-align: center; }
.legal-links a, .settings-legal a { color: var(--text-secondary); }
.legal-links a:hover, .settings-legal a:hover { color: var(--text-primary); }
.consent-title { margin: 22px 0 0; font-size: 20px; }

/* Terms checkbox — a tick beside a sentence with links in it. The 44px touch
   floor set on bare inputs for phones must not stretch the box itself. */
.check { display: flex; align-items: flex-start; gap: 10px; color: var(--text-secondary); font-size: 13px; line-height: 1.5; cursor: pointer; }
.check input { flex: 0 0 auto; width: 18px; height: 18px; min-height: 0; margin: 2px 0 0; padding: 0; accent-color: var(--accent); cursor: pointer; }
.check a { color: var(--accent-text); font-weight: 600; }

/* ═══════════════ APP SHELL ═══════════════ */
/* Pinned to the viewport. Each pane scrolls inside itself, so the page never
   does — nothing can drag the shell sideways or bounce it on a phone. */
.app { position: fixed; inset: 0; display: flex; flex-direction: column; overflow: hidden; }

.navbar {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  /* Installed on an iPhone the page runs under the status bar
     (black-translucent), so the bar grows by the notch and keeps its content below it. */
  height: calc(var(--nav-h) + env(safe-area-inset-top)); flex: 0 0 auto;
  padding: env(safe-area-inset-top) 16px 0;

  /* Chrome, not content: the bar re-declares the surface tokens so the
     wordmark, ghost buttons and admin tag inside it invert on their own. */
  --text-primary: #FFFFFF;
  --text-secondary: #A6A6A6;
  --placeholder: #8C8C8C;
  --border: rgba(255, 255, 255, 0.16);
  --border-strong: rgba(255, 255, 255, 0.32);
  --hover: rgba(255, 255, 255, 0.10);
  /* Brand green is 3.9:1 on black and brand red 3.7:1 — both fail as text, so
     the rail carries lightened versions rather than the raw values. */
  --accent-text: #45D188;
  --accent-soft: rgba(0, 122, 61, 0.30);
  --accent-line: rgba(0, 122, 61, 0.62);
  --mark: #FF6B7C;
  background: var(--sidebar);
  /* Inheritance carries the *computed* colour, so re-declaring --text-primary
     above does nothing for children that never name a colour of their own.
     This line makes the element resolve the local token and pass it down. */
  color: var(--text-primary);
  border-bottom: 1px solid var(--border);
}
/* UI polish 1: the wider gap sits *between* the tool group and the identity
   group; buttons inside a group keep the original 10px. */
.nav-right { display: flex; align-items: center; gap: 22px; }
.nav-group { display: flex; align-items: center; gap: 10px; }
.admin-icon { display: none; }
.avatar {
  display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--accent-text); font-weight: 700; font-size: 12px; letter-spacing: 0.02em;
}
.nav-name { color: var(--text-secondary); font-size: 13px; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.layout { flex: 1; display: flex; min-height: 0; }

/* ── sidebar ── */
/* Two bands: the head is pinned, only the list below it scrolls. The
   column itself must not scroll, hence overflow:hidden here. */
.sidebar {
  display: flex; flex-direction: column;
  width: 100%; min-height: 0;

  /* Same trick as the navbar: one token block, and every student row, chip,
     input and ghost button nested below follows it. The phone breakpoint
     hands these back to the light values, because there the rail *is* the
     page rather than a rail beside one. */
  --surface: rgba(255, 255, 255, 0.10);
  --surface-subtle: rgba(255, 255, 255, 0.05);
  --field-bg: rgba(255, 255, 255, 0.08);
  --hover: rgba(255, 255, 255, 0.10);
  --text-primary: #FFFFFF;
  --text-secondary: #A6A6A6;
  --placeholder: #8C8C8C;
  --border: rgba(255, 255, 255, 0.16);
  --border-strong: rgba(255, 255, 255, 0.32);
  --track: rgba(255, 255, 255, 0.18);
  --accent-text: #45D188;
  --accent-soft: rgba(0, 122, 61, 0.30);
  --accent-line: rgba(0, 122, 61, 0.62);
  --mark: #FF6B7C;
  background: var(--sidebar-fill);
  color: var(--text-primary);
  border-right: 1px solid var(--border);
  overflow: hidden;
}
.side-top { flex: 0 0 auto; display: flex; flex-direction: column; gap: 12px; padding: 16px 16px 12px; }
.side-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 16px 12px; }
.side-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* kept under the 17px wordmark so the brand stays the largest type on the page */
.side-title { font-size: 15px; font-weight: 600; }

.search-wrap { position: relative; }
.search-icon { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); pointer-events: none; }
.search { padding-left: 34px; }
.sidebar .search::-webkit-search-cancel-button { filter: invert(0.7); }

/* One line per client, so twenty fit without scrolling on a laptop screen. */
.student-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.student {
  position: relative; overflow: hidden;
  display: flex; align-items: center;
  padding: 5px 12px 5px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.student:hover { background: var(--hover); }
.student.is-selected { background: var(--surface); border-color: var(--accent); }

/* A scaled pseudo-element, not a border colour: this way the edge wipes in
   from the left instead of simply appearing. */
.student::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.2s ease;
}
.student:hover::before,
.student.is-today::before,
.student.is-selected::before { transform: scaleX(1); }

/* Names are user data and can be any length — every one of them truncates. */
.student-name { font-weight: 500; font-size: 13.5px; line-height: 1.35; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── chips ── */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px;
  border: 1px solid transparent; border-radius: var(--radius-pill);
  font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
  white-space: nowrap;
}
.chip::before { content: '●'; font-size: 8px; line-height: 1; }


.main { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.placeholder { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; padding: 32px; }
.placeholder-art { opacity: 0.9; }
.placeholder-text { display: flex; align-items: center; gap: 10px; margin: 0; color: var(--text-secondary); font-size: 14px; }
.placeholder-arrow { color: var(--accent-text); animation: nudge 1.8s ease-in-out infinite; }
@keyframes nudge { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-5px); } }

#student-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }

/* ── tabs ── */
.tabs { display: flex; gap: 8px; padding: 0 24px; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.tab {
  padding: 14px 4px; margin-right: 20px;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--text-secondary);
  font-family: var(--font-body); font-size: 12px; font-weight: 600; letter-spacing: 0.08em;
  cursor: pointer; transition: color var(--ease), border-color var(--ease);
}
.tab:hover { color: var(--text-primary); }
.tab.is-active { color: var(--accent-text); border-bottom-color: var(--accent); }

.tab-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }
#tab-overview { overflow-y: auto; gap: 24px; padding: 24px; }

/* ── overview ── */
.student-hero {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 16px;
  padding: 24px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}
.hero-name { font-size: 24px; font-weight: 700; overflow-wrap: anywhere; }
.hero-payer { margin: 4px 0 0; color: var(--text-secondary); font-size: 13px; }
.hero-side { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.hero-fee { font-family: var(--font-body); font-size: 20px; font-weight: 600; color: var(--accent-text); white-space: nowrap; }

.hero-actions { display: flex; gap: 10px; }
.hero-actions .btn { flex: 1; }

.info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
  background: var(--border); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.info-cell { display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--bg); }
.info-label { color: var(--text-secondary); font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; }
.info-value { font-size: 15px; font-weight: 500; }

/* ── connect telegram ── */
.connect { padding: 24px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-card); }
.connect-title { font-size: 15px; font-weight: 700; margin-bottom: 12px; }
.connect-actions { display: flex; gap: 10px; }
.connect-actions .btn { flex: 1; }
.connect-help { margin: 10px 0 0; color: var(--text-secondary); font-size: 12px; line-height: 1.5; }
.connect-linked { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

@media (max-width: 767px) {
  /* Side by side, the buttons left the name column so little width that
     "Connected to Telegram" broke one word per line. Stacked, the name gets
     the full row and the buttons wrap their own if they need to. */
  .connect-linked { flex-direction: column; align-items: flex-start; }
}
.connect-ok { color: var(--success-text); font-size: 13px; font-weight: 600; }

.btn-primary-outline { background: transparent; border-color: var(--accent-line); color: var(--accent-text); }
.btn-primary-outline:hover:not(:disabled) { background: var(--accent-soft); border-color: var(--accent); }
.btn-primary-outline.is-copied { background: var(--accent-soft); border-color: var(--accent); }

/* a deep link with no token yet must not look clickable */
.btn[aria-disabled="true"] { opacity: 0.5; pointer-events: none; }

.notes { padding: 24px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-card); }
.notes-title { font-size: 15px; font-weight: 700; margin-bottom: 8px; }
.notes-body { margin: 0; color: var(--text-secondary); font-size: 13px; white-space: pre-wrap; }
.overview-foot { display: flex; gap: 10px; align-items: center; padding-top: 16px; border-top: 1px solid var(--border); }

/* ── chat ── */
.chat-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  padding: 16px 24px 12px; flex: 0 0 auto; }
.chat-title { font-size: 18px; font-weight: 600; }
.chat-sub { margin: 0; color: var(--text-secondary); font-size: 12px; }
/* Only once there is a client to name — an empty subtitle must not leave a
   separator hanging off the title. */
.chat-sub:not(:empty)::before { content: '·'; margin-right: 10px; }

.quota { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.quota-text { color: var(--text-secondary); font-size: 12px; white-space: nowrap; }
/* Short and inline now that it rides in the header: enough to read the day's
   budget at a glance, not enough to cost a row of its own. */
.quota-track {
  display: block; width: 56px; height: 4px; flex: 0 0 auto;
  background: var(--track); border-radius: 999px; overflow: hidden;
}
.quota-fill { display: block; height: 100%; width: 0%; background: var(--accent); border-radius: 999px; transition: width 0.3s ease, background var(--ease); }
.quota-fill.is-low { background: var(--warning); }
.quota-fill.is-full { background: var(--danger); }

.messages { flex: 1; min-height: 0; overflow-y: auto; padding: 8px 24px;
  display: flex; flex-direction: column; gap: 16px; }

.msg { display: flex; flex-direction: column; max-width: min(78%, 560px); }
.msg-user { align-self: flex-end; align-items: flex-end; }
.msg-model { align-self: flex-start; align-items: flex-start; }

.bubble { padding: 11px 14px; border-radius: var(--radius-sm); font-size: 14px; white-space: pre-wrap; word-break: break-word; }
.msg-user .bubble { background: var(--accent); color: #FFFFFF; border-bottom-right-radius: 3px; }
.msg-model .bubble { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); border-bottom-left-radius: 3px; box-shadow: var(--shadow-card); }

.msg-time { margin-top: 5px; color: var(--text-secondary); font-size: 11px; opacity: 0.65; }
/* Must wrap: with Copy + Telegram + WhatsApp the row exceeds the bubble's
   max-width, and because .messages sets overflow-y the x-axis computes to auto
   — so a non-wrapping row pushes the later buttons out of sight rather than
   onto a second line. */
.msg-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }

.chip-action {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px;
  background: transparent; border: 1px solid var(--border); border-radius: 999px;
  color: var(--text-secondary); font: 600 12px var(--font-body); cursor: pointer; text-decoration: none;
  transition: all var(--ease);
}
.chip-action:hover { color: var(--text-primary); background: var(--hover); border-color: var(--border-strong); }
/* Both brand hues are pale enough to fail on white, so the label takes a
   darkened tone of each and the brand colour survives in the border. */
.chip-wa { color: var(--accent-text); border-color: var(--accent-line); }
.chip-wa:hover { background: var(--accent-soft); color: var(--accent-text); }

.chip-tg { color: var(--text-primary); border-color: var(--border-strong); }
.chip-tg:hover:not(:disabled) { background: var(--hover); color: var(--text-primary); }
.chip-tg:disabled { opacity: 0.65; cursor: default; }
.chip-tg.is-sent { color: var(--success-text); border-color: var(--success-line); background: var(--success-soft); }

.suggestions { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 24px 16px; flex: 0 0 auto; }
.suggestion {
  padding: 7px 12px; background: var(--accent-soft);
  border: 1px solid var(--accent-line); border-radius: 999px;
  color: var(--accent-text); font: 500 12px var(--font-body); cursor: pointer; transition: background var(--ease);
}
.suggestion:hover { background: #D3E9DD; }


.composer {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 16px 24px; flex: 0 0 auto;
  background: var(--surface); border-top: 1px solid var(--border);
}
.composer textarea { flex: 1; max-height: 160px; min-height: 42px; padding: 11px 12px; }
.btn-send { width: 42px; height: 42px; padding: 0; flex: 0 0 auto; }

/* loading dots */
.typing { display: flex; gap: 4px; align-items: center; padding: 14px; }
.typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: blink 1.3s infinite both; }
.typing span:nth-child(2) { animation-delay: 0.18s; }
.typing span:nth-child(3) { animation-delay: 0.36s; }
@keyframes blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }

.spinner {
  width: 14px; height: 14px; border-radius: 50%;
  /* Rides on both a blue button and a white card, so the track is derived
     from whatever colour the spinner inherits rather than fixed. */
  border: 2px solid rgba(0, 0, 0, 0.28);
  border: 2px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color: currentColor;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── mobile tab bar ── */
.tabbar {
  display: none;
  flex: 0 0 var(--tabbar-h);
  background: var(--surface); border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar-btn {
  position: relative;
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px;
  min-height: 44px;
  /* Sits on the safe-area padding rather than centred in the bar above it —
     centred left a band of dead space between the label and the home
     indicator that read as the whole tab bar sitting too high. */
  padding-bottom: 8px;
  background: none; border: 0; color: var(--text-secondary);
  font: 600 11px var(--font-body); cursor: pointer; transition: color var(--ease);
}
.tabbar-btn.is-active { color: var(--accent-text); }
.tabbar-btn.is-active::before {
  content: ''; position: absolute; top: 0; left: 24%; right: 24%; height: 2px;
  background: var(--accent); border-radius: 0 0 2px 2px;
}

/* ═══════════════ MODAL ═══════════════ */
.dialog {
  width: min(480px, calc(100vw - 32px));
  padding: 0; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text-primary);
  box-shadow: var(--shadow-dialog);
}
.dialog::backdrop { background: rgba(0, 0, 0, 0.55); backdrop-filter: blur(3px); }
.dialog form { display: flex; flex-direction: column; gap: 16px; padding: 24px; }
.dialog-title { font-size: 20px; font-weight: 700; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field-wide { grid-column: 1 / -1; }
.dialog-foot { display: flex; align-items: center; gap: 10px; padding-top: 4px; border-top: 1px solid var(--border); margin-top: 2px; padding-top: 18px; }
.spacer { flex: 1; }

/* ═══════════════ toast ═══════════════ */
.toast {
  position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom));
  z-index: 100; max-width: calc(100vw - 32px);
  padding: 12px 20px;
  background: var(--sidebar); border: 0; border-radius: var(--radius-pill);
  color: #FFFFFF; font-size: 14px; font-weight: 600;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
  animation: toast-up 0.22s ease;
}
.toast.is-error { background: var(--danger-text); }
@keyframes toast-up {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ═══════════════ CALENDAR VIEW ═══════════════ */

#calendar-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }

/* ═══════════════ SETTINGS VIEW ═══════════════ */

#settings-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.settings-body { flex: 1; min-height: 0; overflow-y: auto; padding: 24px; }
.settings-legal { margin: 8px 0 0; padding-top: 16px; border-top: 1px solid var(--border); color: var(--text-secondary); font-size: 12px; text-align: center; }

.cal-head {
  display: flex; align-items: center; gap: 16px;
  padding: 16px 24px; flex: 0 0 auto;
  border-bottom: 1px solid var(--border);
}
.cal-title { font-size: 18px; font-weight: 600; min-width: 168px; text-align: center; }

.cal-legend { display: flex; gap: 14px; margin: 0; padding: 0; list-style: none;
  color: var(--text-secondary); font-size: 11.5px; }
.cal-legend li { display: flex; align-items: center; gap: 6px; }

.cal-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 24px 24px; }

/* minmax(0,...) not 1fr: the pills are nowrap, and a plain 1fr column takes the
   text's min-content width as its floor — one long name would widen the grid. */
.cal-weekdays, .cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.cal-weekdays { margin-bottom: 6px; }
.cal-weekdays span { text-align: center; color: var(--text-secondary);
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }

.cal-cell {
  position: relative;
  display: flex; flex-direction: column; align-items: stretch; gap: 4px;
  min-width: 0; min-height: 80px; padding: 6px 5px;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-primary); font: inherit; text-align: left; cursor: pointer;
  overflow: hidden;
  transition: background var(--ease), border-color var(--ease);
}
.cal-cell:hover { background: var(--hover); }
.cal-cell.is-selected { background: var(--accent-soft); border-color: var(--accent); }
/* days from the neighbouring months keep the grid rectangular and do nothing else */
.cal-cell.is-outside { background: transparent; border-color: transparent; cursor: default; }

/* The day's own hit area, stretched behind everything else. Lets the pills be
   real buttons without nesting one button inside another. */
.cal-open {
  position: absolute; inset: 0; z-index: 0;
  margin: 0; padding: 0;
  background: transparent; border: 0; border-radius: inherit;
  font: inherit; color: inherit; cursor: pointer;
}
.cal-open:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Content sits above the hit layer but lets clicks fall through to it —
   except the pills, which claim their own. */
.cal-date, .cal-more, .cal-events { position: relative; z-index: 1; pointer-events: none; }
.cal-pill { pointer-events: auto; }

.cal-date { align-self: center; flex: 0 0 auto;
  display: grid; place-items: center; width: 24px; height: 24px;
  border-radius: 50%; font-size: 12.5px; font-weight: 600; }
.cal-cell.is-today .cal-date { background: var(--accent-text); color: #FFFFFF; font-weight: 700; }

/* ── event pills ── */
.cal-events { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cal-pill {
  display: block; width: 100%; padding: 2px 6px;
  border: 0; border-radius: 4px;
  font-family: var(--font-body); font-size: 11px; font-weight: 600; line-height: 1.5;
  color: inherit; text-align: left; cursor: pointer;
  /* a month cell is never wide enough for a full name — clip, don't wrap */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: filter var(--ease);
}
/* Darkening, not brightening: these are pale fills now, and brightening a
   pale fill just bleaches it. */
.cal-pill:hover { filter: brightness(0.96); }
/* An 11px label on a saturated fill cannot clear 4.5:1 at this size, so the
   pills carry the status as a tint and keep the colour in the text. */
.pill-paid    { background: var(--success-soft); color: var(--success-text); }
.pill-unpaid  { background: var(--warning-soft); color: var(--warning-text); }
.pill-overdue { background: var(--danger-soft);  color: var(--danger-text); }
.pill-note    { background: #EDEDED;             color: #4A4A4A; }
.pill-pending_verification { background: #000000; color: #FFFFFF; }

/* A saved one-off keeps its student's status colour and is set apart by a
   darker rim. An inset shadow, not a border — a border would make these pills
   two pixels taller than the recurring ones sitting beside them. */
.cal-pill.is-event { box-shadow: inset 0 0 0 1.5px rgba(0, 0, 0, 0.24); }

.cal-more { padding-left: 3px; color: var(--text-secondary); font-size: 10.5px; line-height: 1.4; }

/* legend swatches echo the pill shape rather than the older dots */
.cal-dot { width: 9px; height: 9px; border-radius: 3px; flex: 0 0 auto; }
.dot-paid    { background: var(--success); }
.dot-unpaid  { background: var(--warning); }
.dot-overdue { background: var(--danger); }

/* a student with no phone on record — explains itself instead of offering a dead link */
.chip-action.is-inert { opacity: 0.55; cursor: default; }

/* ═══════════════ DAY DETAIL MODAL ═══════════════ */
.dialog-day { width: min(560px, calc(100vw - 32px)); }

.day-wrap { display: flex; flex-direction: column; max-height: min(80dvh, 640px); }

.day-head {
  display: flex; align-items: center; gap: 16px;
  flex: 0 0 auto; padding: 16px 24px;
  border-bottom: 1px solid var(--border);
}
.day-title { flex: 1; min-width: 0; font-size: 18px; font-weight: 600; }
.day-head-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.day-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 24px 24px; }

.day-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.day-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 9px 14px; align-items: center;
  padding: 12px 14px;
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.day-row:hover { background: var(--accent-soft); border-color: var(--accent-line); }

.chip-view { color: var(--accent-text); border-color: var(--accent-line); }
.chip-view:hover { background: var(--accent-soft); color: var(--accent-text); }
.day-time { font-family: var(--font-body); font-size: 15px; font-weight: 600; white-space: nowrap; }
.day-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.day-name { font-size: 15px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-payer { color: var(--text-secondary); font-size: 13px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-side { display: flex; align-items: center; gap: 10px; }
.day-fee { font-family: var(--font-body); font-size: 20px; font-weight: 600; color: var(--accent-text); white-space: nowrap; }
.day-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px; }


/* ═══════════════ ADD EVENT MODAL ═══════════════ */
.event-wrap { display: flex; flex-direction: column; gap: 16px; padding: 24px; }

.type-toggle { display: flex; gap: 10px; }
.type-btn {
  flex: 1; padding: 14px 10px;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font: 600 14px var(--font-body); cursor: pointer;
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.type-btn:hover { background: var(--hover); color: var(--text-primary); }
.type-btn.is-active { background: var(--accent); border-color: var(--accent); color: #FFFFFF; }

.event-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
/* the date is decided by the cell that was clicked — shown, never edited */
.event-fields input[readonly] { color: var(--text-secondary); cursor: default; background: var(--surface-subtle); }
.event-fields input[readonly]:focus { border-color: var(--border); box-shadow: none; }

/* ═══════════════ SUBSCRIBE MODAL ═══════════════ */
.dialog-sm { width: min(400px, calc(100vw - 32px)); }
.sub-wrap { padding: 24px; }
.dialog-sub { margin: 6px 0 20px; color: var(--text-secondary); font-size: 13px; line-height: 1.5; }
.sub-actions { display: flex; flex-direction: column; }
.sub-help { margin: 7px 0 16px; color: var(--text-secondary); font-size: 11.5px; text-align: center; }
.sub-foot { margin: 2px 0 0; color: var(--text-secondary); font-size: 12px; text-align: center; line-height: 1.5; }
/* UI polish 3: the dialog's own heading, ruled off from the sections below so
   it reads as the title rather than a heading stacked on "Notifications". */
.settings-title { padding-bottom: 16px; margin-bottom: 20px; border-bottom: 1px solid var(--border); }
/* Settings: one block per concern, ruled apart the same way as the dialog foot. */
.settings-section { padding-bottom: 20px; }
.settings-section + .settings-section { padding-top: 20px; border-top: 1px solid var(--border); }
.settings-section .dialog-sub { margin-bottom: 14px; }
.settings-section .sub-help:last-child { margin-bottom: 0; }
.btn-ghost.is-copied { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-text); }

/* Account: who's signed in, then whether the backend is actually reachable —
   both read at a glance, above the Sign Out button they sit alongside. */
.settings-email { margin: 0 0 10px; font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.health-status { display: flex; align-items: center; gap: 8px; margin: 0 0 16px; color: var(--text-secondary); font-size: 13px; }
.health-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--border-strong); }
.health-dot.is-ok { background: var(--success); }
.health-dot.is-down { background: var(--danger); }

/* Subscription: status line, a two-tier comparison with the coach's own tier
   picked out, then the one action — matches Notifications/App/Calendar's
   short-description-then-button shape. */
.plan-status { margin: 0 0 14px; font-size: 14px; font-weight: 600; }
.plan-compare { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
.plan-tier { padding: 14px; background: var(--surface-subtle); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.plan-tier.is-current { background: var(--accent-soft); border-color: var(--accent-line); }
.plan-tier-name { font-size: 13px; font-weight: 700; }
.plan-tier.is-current .plan-tier-name { color: var(--accent-text); }
.plan-tier-badge {
  display: none; margin-left: 6px;
  color: var(--accent-text); font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em;
}
.plan-tier.is-current .plan-tier-badge { display: inline; }
.plan-tier-features {
  display: flex; flex-direction: column; gap: 6px;
  margin: 10px 0 0; padding: 0; list-style: none;
  color: var(--text-secondary); font-size: 12.5px;
}
.plan-tier-features li::before { content: '✓ '; color: var(--accent-text); }

/* ═══════════════ LESSON DAY PICKER ═══════════════ */
.day-picker { margin: 0; padding: 0; border: 0; min-width: 0; }
.day-picker legend { padding: 0; margin-bottom: 8px; }
.day-pills { display: flex; flex-wrap: wrap; gap: 8px; }

.day-pill { position: relative; }
/* The native box is hidden but still focusable, so the pill keeps keyboard
   access and the checked state drives the styling. */
.day-pill input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  min-height: 0; margin: 0; padding: 0; opacity: 0; cursor: pointer;
}
.day-pill span {
  display: block; min-width: 48px; padding: 8px 12px;
  background: transparent;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-size: 13px; font-weight: 600; text-align: center;
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.day-pill:hover span { border-color: var(--border-strong); color: var(--text-primary); }
.day-pill input:checked + span { background: var(--accent); border-color: var(--accent); color: #FFFFFF; }
.day-pill input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ═══════════════ EVENT DETAIL SHEET ═══════════════ */
.detail-wrap { display: flex; flex-direction: column; }
.detail-body {
  display: grid; grid-template-columns: auto 1fr; gap: 12px 16px;
  margin: 0; padding: 24px;
}
.detail-body dt {
  color: var(--text-secondary); font-size: 12px; font-weight: 500;
  letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap;
}
.detail-body dd { margin: 0; font-size: 15px; overflow-wrap: anywhere; white-space: pre-wrap; }
.detail-wrap .dialog-foot { padding: 0 24px 24px; border-top: 0; }

/* the entry the coach tapped, pinned to the top of the day list */
.day-row.is-highlighted { border-color: var(--accent); background: var(--accent-soft); }

/* pencil and other icon-only row actions */
.chip-action.chip-icon { padding: 6px 9px; }

/* ═══════════════ OCCURRENCE OVERRIDES ═══════════════ */
/* A cancelled date stays listed but reads as switched off, so the coach can
   see it was deliberate — and put it back. */
.day-row.is-cancelled { opacity: 0.55; cursor: default; }
.day-row.is-cancelled:hover { background: var(--bg); border-color: var(--border); }
.day-row.is-cancelled .day-time,
.day-row.is-cancelled .day-name { text-decoration: line-through; }

.chip-cancelled {
  background: #F0F0F0; color: #4A4A4A;
  border-color: var(--border-strong);
}
.edited-badge { color: var(--text-secondary); font-size: 12px; white-space: nowrap; }

/* ── apply-to scope ── */
.scope-choice { margin: 0; padding: 0; border: 0; min-width: 0; }
.scope-choice legend { padding: 0; margin-bottom: 8px; }
.scope-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; margin-bottom: 8px;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 14px; cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.scope-opt:last-child { margin-bottom: 0; }
.scope-opt:hover { background: var(--hover); }
.scope-opt input { width: 16px; height: 16px; min-height: 0; flex: 0 0 auto; accent-color: var(--accent); cursor: pointer; }
.scope-opt:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }

/* ── delete student, kept away from everything else ── */

/* ═══════════════ LOADING SKELETONS ═══════════════ */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--track);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent);
  animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

.skeleton-list { display: flex; flex-direction: column; gap: 8px; }
.skel-student { height: 30px; }
.skel-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.skel-cell { height: 80px; }

/* ═══════════════ EMPTY STATES ═══════════════ */
.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 40px 24px; text-align: center;
}
.empty-icon {
  display: grid; place-items: center;
  width: 64px; height: 64px; border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  font-size: 28px; line-height: 1;
}
.empty-text { margin: 0; max-width: 32ch; color: var(--text-secondary); font-size: 15px; line-height: 1.6; }
/* .messages is a flex column, so the empty state needs auto margins to centre */
.messages > .empty { margin: auto; }

/* ═══════════════ MICRO-INTERACTIONS ═══════════════ */
.chip-action:active:not(:disabled),
.suggestion:active,
.type-btn:active,
.tabbar-btn:active { transform: scale(0.97); }

/* The pills are tinted rather than solid now, so they no longer need
   dimming to sit quietly in a cell. */
.cal-pill:hover { opacity: 1; }

/* Panels fade rather than cut when a view or tab changes. */
.tab-panel:not([hidden]),
#calendar-pane:not([hidden]),
#student-pane:not([hidden]),
#no-selection:not([hidden]) { animation: fade-panel 0.15s ease; }
@keyframes fade-panel { from { opacity: 0; } to { opacity: 1; } }

/* Dialogs scale up on open and back down on close. `allow-discrete` plus
   @starting-style is what lets a display:none element animate both ways;
   browsers without it simply show and hide instantly. */
.dialog {
  opacity: 0; transform: scale(0.95);
  transition: opacity 0.18s ease, transform 0.18s ease,
              overlay 0.18s ease allow-discrete, display 0.18s ease allow-discrete;
}
.dialog[open] { opacity: 1; transform: scale(1); }
@starting-style { .dialog[open] { opacity: 0; transform: scale(0.95); } }

.dialog::backdrop { opacity: 0; transition: opacity 0.18s ease, overlay 0.18s ease allow-discrete, display 0.18s ease allow-discrete; }
.dialog[open]::backdrop { opacity: 1; }
@starting-style { .dialog[open]::backdrop { opacity: 0; } }

/* ═══════════════ ERROR / RETRY ═══════════════ */
.retry-state { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 24px; text-align: center; }
.retry-text { margin: 0; color: var(--danger-text); font-size: 14px; line-height: 1.6; }

/* ═══════════════ APP FOOTER ═══════════════ */
.app-footer {
  padding: 24px 16px calc(24px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  color: var(--text-secondary); font-size: 12px; text-align: center;
}

/* stat figures rise into place when they land */
.stat-value.is-in { animation: stat-in 0.32s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes stat-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ═══════════════ responsive ═══════════════ */
@media (max-width: 767px) {
  .layout { position: relative; }
  .sidebar, .main { display: none; }
  .layout[data-pane="students"] .sidebar { display: flex; }
  .layout[data-pane="detail"] .main,
  .layout[data-pane="calendar"] .main,
  .layout[data-pane="settings"] .main { display: flex; }
  /* Full-bleed on a phone, so the rail stops being chrome and hands its
     tokens back to the light page it has become. */
  .sidebar {
    --surface: #FFFFFF;
    --surface-subtle: #F6F6F6;
    --field-bg: #FFFFFF;
    --hover: #F0F0F0;
    --text-primary: #000000;
    --text-secondary: #5C5C5C;
    --placeholder: #8C8C8C;
    --border: #E0E0E0;
    --border-strong: #BFBFBF;
    --track: #E0E0E0;
    --accent-text: #007A3D;
    --accent-soft: #E6F3EC;
    --accent-line: #A9D4BE;
    --mark: #CE1126;
    background: var(--bg);
    color: var(--text-primary);
    border-right: 0;
  }
  .sidebar .search::-webkit-search-cancel-button { filter: none; }
  .tabbar { display: flex; }
  .nav-name { display: none; }
  .form-grid { grid-template-columns: 1fr; }
  .student-hero { flex-direction: column; }
  .hero-side { flex-direction: row; align-items: center; justify-content: space-between; width: 100%; }
  .info-grid { grid-template-columns: 1fr; }
  .hero-actions { flex-direction: column; }
  .msg { max-width: 88%; }
  /* Four items will not sit on one line at phone width. Dropping the spacer
     lets them flow and wrap by their own size instead of the quota being
     shoved to the far edge and taking a row of its own. */
  .chat-head { padding: 14px 16px 10px; gap: 4px 10px; }
  .chat-head .spacer { display: none; }

  /* Anything tappable clears 44px, the smallest target a thumb reliably hits. */
  .btn, .icon-btn, .chip-action, .suggestion, .type-btn,
  input, select, textarea { min-height: 44px; }
  .icon-btn { width: 44px; }

  /* The top bar gets one phone's width. The calendar button repeats the tab
     bar below and the avatar is decoration, so both step aside, Admin shrinks
     to an icon, and the buttons trade 4px of width (never height) to fit. */
  .navbar { padding-left: 12px; padding-right: 12px; }
  .navbar .wordmark { font-size: 15px; }
  /* UI polish 1: same two groups, scaled to a phone's width */
  .nav-right { gap: 10px; }
  .nav-group { gap: 4px; }
  #nav-calendar-btn, .avatar { display: none; }
  .navbar .icon-btn, #admin-link { width: 40px; padding: 0; }
  .admin-icon { display: block; }
  .admin-label { display: none; }

  /* iOS zooms into any field under 16px on focus and leaves the page zoomed. */
  input, select, textarea { font-size: 16px; }

  /* except the pills packed inside a 60px calendar cell */
  .cal-pill { min-height: 0; }
  /* the day picker's target is the visible pill, not the checkbox over it */
  .day-pill span { min-height: 44px; display: flex; align-items: center; justify-content: center; }

  /* the client rail is the whole page here, so its rows get the same thumb target */
  .student { min-height: 44px; }

  /* clear of the home indicator on a notched iPhone */
  .side-scroll { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  .toast { bottom: calc(16px + var(--tabbar-h) + env(safe-area-inset-bottom)); }
  .day-wrap { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }

  /* calendar: same 7-column grid, smaller cells; the legend drops to its own row */
  .cal-head { flex-wrap: wrap; padding: 12px 14px; }
  /* The title takes the free space, so the arrows sit at the two edges. With
     the spacer also sharing it, the title squeezed onto two lines. */
  .cal-head .spacer { display: none; }
  .cal-title { flex: 1; min-width: 0; font-size: 15px; }
  .cal-legend { flex-basis: 100%; justify-content: center; gap: 12px; }
  .cal-body { padding: 12px 14px 20px; }
  .cal-weekdays, .cal-grid { gap: 4px; }
  .cal-weekdays span { font-size: 10px; letter-spacing: 0.03em; }
  .cal-cell { min-height: 60px; gap: 3px; padding: 4px 3px; }
  .cal-date { width: 20px; height: 20px; font-size: 11.5px; }
  /* a seventh of a phone screen is ~48px, so the pill trades size for characters */
  .cal-pill { font-size: 10px; padding: 1px 4px; border-radius: 3px; }
  .cal-events { gap: 2px; }
  .cal-more { font-size: 9.5px; padding-left: 2px; }
  .cal-dot { width: 8px; height: 8px; }

  /* day detail goes full-bleed rather than floating in a phone-width gutter */
  .dialog-day { width: 100vw; max-width: 100vw; border: 0; border-radius: 0; }
  .day-wrap { max-height: 100dvh; }
  .day-head { padding: 14px 16px; }
  .day-title { font-size: 15px; }
  .day-body { padding: 14px 16px 18px; }
  .event-fields { grid-template-columns: 1fr; }
  .day-row { grid-template-columns: auto 1fr; }
  .day-side { grid-column: 1 / -1; justify-content: space-between; }
  /* .ov-grid, .rem-fields and .lesson-fields each get their own mobile
     arrangement further down, next to where they're defined — a
     same-specificity rule here would lose to their later base rule. */
  .ov { gap: 16px; }
  .ov-box-h { padding: 16px 18px; font-size: 18px; }
  .ov-box-body { padding: 16px 18px 18px; }
  /* The trash button would otherwise sit alone in a tall second column beside
     three stacked fields. */
  .lesson-row { grid-template-columns: minmax(0, 1fr); }
  .lesson-remove { justify-self: end; }
  .verify-panel { left: 8px; right: 8px; width: auto; }
  .tabbar-btn { font-size: 10.5px; }

  /* At phone width the placeholder wraps to two lines but the box only had
     room for one, so the second line was clipped at the tab bar's edge. */
  .composer textarea { min-height: 64px; }

  /* Two tiers side by side leave each column too narrow for its feature list. */
  .plan-compare { grid-template-columns: 1fr; }
}

@media (min-width: 768px) {
  .sidebar { width: 280px; flex: 0 0 280px; }
  .tabbar { display: none; }
}

/* ═══════════════ overview editor ═══════════════ */
/* Six independent cards, one per concern, in place of one long card with
   hairline dividers — each can be read from its heading alone and opened
   only when there is something in it to change. */
.ov { display: flex; flex-direction: column; gap: 20px; }

/* <details>/<summary> carries the collapse for free: no JS, no state to
   track, and a screen reader announces the expanded/collapsed state on its
   own. All six start open, since that was the only state before this. */
.ov-box {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-card);
}
.ov-box-h {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 20px 22px;
  font-family: var(--font-head); font-size: 20px; font-weight: 700;
  cursor: pointer; list-style: none;
  transition: background var(--ease);
}
.ov-box-h::-webkit-details-marker { display: none; }
.ov-box-h:hover { background: var(--hover); }
.ov-box-chevron { flex: 0 0 auto; color: var(--text-secondary); transition: transform 0.18s ease; }
.ov-box[open] .ov-box-chevron { transform: rotate(180deg); }

.ov-box-body {
  display: flex; flex-direction: column; gap: 14px;
  padding: 18px 22px 22px;
  border-top: 1px solid var(--border);
}
/* A right-aligned action or note that used to sit beside the heading now
   sits at the top of the content it belongs to, so a closed box shows
   nothing but its name. */
.ov-box-actions { display: flex; justify-content: flex-end; }
.ov-box-hint { margin: -4px 0 0; text-align: right; }

.ov-note { color: var(--text-secondary); font-size: 12px; }
.ov-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.ov-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.ov-danger { display: flex; }

@media (max-width: 767px) {
  /* Same bug as .rem-fields/.lesson-fields: two columns side by side left
     Name/Phone and Fee/Currency squeezed to unreadable widths on a phone,
     because the fix has to land after this base rule to actually win it. */
  .ov-grid { grid-template-columns: 1fr; }
}

/* Save sits at the end of the form it saves, outside every box, with the one
   irreversible action pushed to the far side so the two are never adjacent
   under a thumb. */
.ov-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

/* ── lesson schedule ── */
.lessons { display: flex; flex-direction: column; gap: 12px; }
.lesson-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: end; gap: 10px 12px;
  padding: 14px;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.lesson-name { grid-column: 1 / -1; font-size: 14px; font-weight: 600; }
.lesson-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.lesson-remove { color: var(--danger-text); }
.lesson-remove:hover { background: var(--danger-soft); border-color: var(--danger-line); }

@media (max-width: 767px) {
  /* Three narrow columns left Location truncated mid-word. It becomes its
     own full-width row below Day/Time — via `order`, so the DOM (and the
     desktop column order) is untouched. */
  .lesson-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lesson-fields .field:has(.lesson-location) { grid-column: 1 / -1; order: 1; }
}

/* ── auto reminders ── */
.rems { display: flex; flex-direction: column; gap: 12px; }
.rem {
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.rem-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.rem-name { font-size: 14px; font-weight: 600; }
.rem-fields { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.rem-meta { margin: 0; color: var(--text-secondary); font-size: 12px; line-height: 1.45; }
/* A lesson reminder with nothing selected cannot be saved — say so at the
   control rather than in an error line after the attempt. */
.rem-lesson.is-missing { border-color: var(--danger-line); background: var(--danger-soft); }

@media (max-width: 767px) {
  /* Four narrow columns left every value showing one letter. Type sits
     beside "For lesson", Day beside Time, in the same document order. */
  .rem-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── switch ──
   The native checkbox stays in the DOM, stretched invisibly over the whole
   label, so the control keeps keyboard focus and the checked state drives the
   painting. */
.switch { position: relative; display: inline-flex; align-items: center; gap: 9px; cursor: pointer; }
.switch input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  min-height: 0; margin: 0; padding: 0; border: 0;
  opacity: 0; cursor: pointer;
}
.switch-track {
  flex: none; position: relative;
  width: 38px; height: 22px; border-radius: 999px;
  background: var(--border-strong);
  transition: background var(--ease);
}
.switch-track::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #FFFFFF; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
  transition: transform var(--ease);
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch input:disabled + .switch-track { opacity: 0.55; }
.rem-state {
  min-width: 24px; font-size: 12px; font-weight: 600;
  color: var(--text-secondary); letter-spacing: 0.02em;
}

/* ═══════════════ payment verification ═══════════════ */

/* Amber card, not red: a declaration is good news awaiting a signature, and
   dressing it as an error would train the coach to dread the badge. */
.verify-card {
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px;
  background: var(--warning-soft);
  border: 1px solid var(--warning-line);
  border-radius: var(--radius-sm);
}
.verify-title { font-size: 14px; font-weight: 700; color: var(--warning-text); }
.verify-claim { margin: 0; font-size: 14px; }
.verify-meta { margin: 0; color: var(--text-secondary); font-size: 12px; }
.verify-card .ov-actions { margin-top: 4px; }

/* ── navbar badge ── */
.bell { position: relative; overflow: visible; }
.badge {
  position: absolute; top: -5px; right: -5px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: grid; place-items: center;
  background: var(--danger); color: #FFFFFF;
  border-radius: 999px; border: 2px solid var(--sidebar);
  font: 700 10px/1 var(--font-body); letter-spacing: 0;
}

/* ── dropdown panel ── */
.verify-panel {
  position: absolute; top: calc(var(--nav-h) + env(safe-area-inset-top) - 4px); right: 12px; z-index: 30;
  width: min(420px, calc(100vw - 24px));
  max-height: min(70dvh, 560px); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-dialog);
}
.verify-panel-head {
  position: sticky; top: 0;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px;
  background: var(--surface); border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.verify-list { list-style: none; margin: 0; padding: 8px; display: flex; flex-direction: column; gap: 8px; }
.verify-row {
  display: flex; flex-direction: column; gap: 7px;
  padding: 12px;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.verify-row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.verify-who { font-size: 14px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.verify-fee { font-size: 14px; font-weight: 600; color: var(--accent-text); white-space: nowrap; }
.verify-row-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.verify-open { align-self: flex-start; font-size: 12px; }

/* ── receipt lightbox ── */
.dialog-receipt { width: min(560px, calc(100vw - 32px)); }
.receipt-wrap { display: flex; flex-direction: column; }
.receipt-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
/* A phone screenshot is tall; cap it so the dialog never runs off the screen. */
.receipt-img {
  display: block; width: 100%; max-height: 62dvh;
  object-fit: contain;
  background: var(--surface-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.receipt-img:not([src]) { display: none; }

/* ═══════════════ trial bar ═══════════════ */

/* Sits between the top bar and the work area, full width, so it is read once
   and then ignored — a floating toast would demand attention every render. */
.trial-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 16px;
  font-size: 13px; font-weight: 600;
  border-bottom: 1px solid var(--border);
}
.trial-text { flex: 1; min-width: 0; }
.trial-cta {
  color: inherit; text-decoration: underline; text-underline-offset: 2px;
  white-space: nowrap; font-weight: 700;
}
.trial-x {
  display: inline-flex; padding: 4px; flex: none;
  background: none; border: 0; border-radius: 6px;
  color: inherit; opacity: 0.75; cursor: pointer;
}
.trial-x:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); }

/* Three bands. Green while there is room, neutral once it is close, red when
   it has run out or nearly has — the palette has no amber, and inventing one
   for a single bar would put a fifth colour in the product. */
.trial-bar.is-fine { background: var(--accent); color: #FFFFFF; border-bottom-color: var(--accent); }
.trial-bar.is-soon { background: var(--sidebar); color: #FFFFFF; border-bottom-color: var(--sidebar); }
.trial-bar.is-over { background: var(--danger); color: #FFFFFF; border-bottom-color: var(--danger); }

@media (max-width: 767px) {
  .trial-bar { padding: 8px 12px; font-size: 12px; gap: 8px; }
}

/* ═══════════════ guided tour ═══════════════ */

/* Three layers: a blocker that eats clicks meant for the page, a ring that
   paints the dimming as its own outward shadow so no mask is needed, and the
   card above both. The ring takes no pointer events — the blocker beneath it
   is what actually stops a stray click reaching a Delete button. */
.tour { position: fixed; inset: 0; z-index: 200; }
.tour-block { position: absolute; inset: 0; }
.tour-ring {
  position: fixed;
  border-radius: 10px;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.62);
  pointer-events: none;
  transition: top 0.18s ease, left 0.18s ease, width 0.18s ease, height 0.18s ease;
}
/* With nothing to spotlight the dimming has no anchor, so the blocker carries
   it instead and the card floats in the middle. */
.tour:has(.tour-ring[hidden]) .tour-block { background: rgba(0, 0, 0, 0.62); }

.tour-card {
  position: fixed;
  width: min(340px, calc(100vw - 24px));
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-dialog);
}
.tour-count {
  margin: 0; font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-secondary);
}
.tour-title { font-family: var(--font-head); font-size: 17px; font-weight: 700; }
.tour-body { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-primary); }
/* A step describing something not currently on screen is telling, not showing —
   the left rule says so without a second paragraph explaining itself. */
.tour-card.is-note .tour-body {
  padding-left: 11px; border-left: 3px solid var(--border-strong);
  color: var(--text-secondary);
}
.tour-foot { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.tour-foot .spacer { flex: 1; }
.tour-skip { color: var(--text-secondary); }

@media (max-width: 767px) {
  /* Pinned to the bottom: a card that chases the target around a small screen
     spends half the tour covering the thing it is pointing at. */
  .tour-card {
    top: auto !important;
    left: 12px !important;
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom));
    width: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tour-ring { transition: none; }
}

/* ═══════════════ admin dashboard ═══════════════ */
.admin-shell { display: flex; flex-direction: column; min-height: 100dvh; }
.admin-tag {
  font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: 0.12em;
  padding: 3px 7px; margin-left: 6px; vertical-align: middle;
  background: var(--accent-soft); border: 1px solid var(--accent-line);
  border-radius: 999px; color: var(--accent-text);
}
.admin-main { flex: 1; width: 100%; max-width: 1040px; margin: 0 auto; padding: 24px 20px 48px;
  display: flex; flex-direction: column; gap: 20px; }

.admin-state { display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 72px 20px; text-align: center; color: var(--text-secondary); }
.admin-state-title { font-size: 20px; font-weight: 700; color: var(--text-primary); }
.spinner-lg { width: 26px; height: 26px; border-width: 3px; color: var(--accent-text); }

.stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.stat { display: flex; flex-direction: column; gap: 6px; padding: 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-card); }
.stat-label { color: var(--text-secondary); font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; }
.stat-value { font-family: var(--font-body); font-size: 20px; font-weight: 600; color: var(--accent-text); }
.stat-value.is-accent { color: var(--accent-text); font-size: 20px; }
.stat-value.is-warn { color: var(--warning-text); font-size: 20px; }

.admin-card { padding: 18px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-card); }
.admin-h2 { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
.admin-note { margin: 0 0 14px; color: var(--text-secondary); font-size: 12.5px; line-height: 1.5; }

.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table th { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border);
  color: var(--text-secondary); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap; }
.table td { padding: 11px 12px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr { transition: background var(--ease); }
.table tbody tr:hover { background: var(--hover); }
.muted-cell { color: var(--text-secondary); font-style: italic; white-space: normal; }

.allow-form { display: grid; grid-template-columns: 2fr 1fr 1fr auto; gap: 10px;
  align-items: end; margin-bottom: 14px; }
#allowlist-error { margin-bottom: 14px; }

@media (max-width: 767px) {
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  .allow-form { grid-template-columns: 1fr; }
}

/* ── admin: plan badges, banner, pager ── */
.plan {
  display: inline-flex; align-items: center;
  padding: 3px 10px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.03em; white-space: nowrap;
}
.plan-none,
.plan-legacy,
.plan-admin   { background: #F0F0F0;            color: #4A4A4A; }
.plan-trial   { background: var(--warning-soft); color: var(--warning-text); }
.plan-monthly { background: var(--accent-soft);  color: var(--accent-text); }
.plan-annual  { background: #D3E9DD;             color: var(--accent-text); }

.banner {
  margin: 0 0 16px; padding: 11px 14px;
  background: var(--warning-soft);
  border: 1px solid var(--warning-line);
  border-radius: var(--radius-sm);
  color: var(--warning-text); font-size: 12.5px; line-height: 1.5;
}
.banner-on { background: var(--success-soft); border-color: var(--success-line); color: var(--success-text); }

.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.card-head .admin-h2 { margin-bottom: 0; }
.admin-note-inline { color: var(--text-secondary); font-size: 12px; }

.pager { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
.mono-cell { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; color: var(--text-secondary); }
.stat-value.is-accent-num { color: var(--accent-text); }
.admin-state-actions { display: flex; gap: 10px; }
.admin-state-hint { margin: 0; color: var(--text-secondary); font-size: 12.5px; max-width: 460px; }
.connect-who { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.connect-warn { color: var(--warning-text); font-size: 11.5px; line-height: 1.45; }
.connect-btns { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: none; }

.paid-wrap { display: flex; flex-direction: column; gap: 8px; padding: 22px; }
.paid-sub { margin: 0; color: var(--text-secondary); font-size: 13px; line-height: 1.5; }
.paid-wrap .dialog-foot { margin-top: 8px; }
