:root {
  /* Rebranded to a blue anchored on the App Store icon's own gradient
     (sampled ~#25A3F3 top to ~#1D59EA bottom) rather than dmdent.com's
     teal -- the whole point being a system that reads as new at a glance,
     not just a recolored dental app. Neutrals (--bg/--ink/--muted/--border)
     got a matching cool-blue cast instead of the old teal-tinted grays, so
     nothing looks like a leftover from the fork. --accent (gold) and
     --danger are unchanged -- see the comments further down. */
  --bg: #F5F8FC;
  --surface: #FFFFFF;
  --surface-alt: #FBFDFE;
  --ink: #101827;
  --muted: #5B6B85;
  --primary: #1D78EA; /* buttons, highlights, active states, charts */
  --primary-text: #0B57C7; /* links & small text on white — kept darker than
                               --primary so it clears WCAG AA (4.5:1); the
                               lighter --primary would fail as text color */
  --primary-dark: #0A1E42;
  --primary-tint: #E9F2FE;
  --accent: #C08A3E;
  --accent-tint: #FBF1E0;
  --accent-text: #8A5F26; /* same idea as --primary-text above: --accent
                              itself only clears ~2.7:1 as small text on
                              --accent-tint (well under the 4.5:1 AA
                              minimum) -- this darkened variant is for use
                              as *text* on --accent-tint (e.g. pending-status
                              tags), never as a background/icon color. */
  --danger: #C4453A;
  --danger-tint: #FBEAE8;
  --danger-text: #B03E34; /* same idea, for --danger as text on
                              --danger-tint -- plain --danger only clears
                              ~4.2:1 there, just under the 4.5:1 AA minimum
                              for small text (it's fine as a background/icon
                              color, or as text on plain white, which is why
                              it's untouched elsewhere -- e.g. .btn-danger,
                              .flash-error). */
  --border: #E2E8F3;
  --radius: 14px;
  --radius-sm: 9px;
  --shadow-sm: 0 1px 2px rgba(10, 30, 66, 0.05);
  --shadow: 0 1px 2px rgba(10, 30, 66, 0.04), 0 10px 28px -14px rgba(10, 30, 66, 0.16);
  --shadow-lg: 0 24px 48px -16px rgba(10, 30, 66, 0.28);
  --font-heading: 'Plus Jakarta Sans', 'Inter', -apple-system, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 15px;
  line-height: 1.55;
  /* Safety net: nothing should legitimately need to push the page wider
     than the viewport (see the mobile nav overflow bug this was added
     alongside) -- if something does overflow anyway, clip it instead of
     letting the whole page shift/scroll sideways on mobile. */
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0;
  color: var(--primary-dark);
}

h1 { font-size: 1.7rem; }
h2 { font-size: 1.08rem; margin-bottom: 1rem; }
h3 { font-size: 1rem; margin: 0 0 0.35rem; }

.mono { font-family: 'IBM Plex Mono', monospace; letter-spacing: -0.01em; }

a { color: var(--primary-text); text-decoration: none; transition: color 0.15s ease; }
a:hover { text-decoration: underline; }

/* ---------- Shell layout ---------- */
.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: 248px;
  background: linear-gradient(180deg, #10294D 0%, #061225 100%);
  color: #E9F2FE;
  display: flex;
  flex-direction: column;
  padding: 1.7rem 1.15rem;
  flex-shrink: 0;
}

.sidebar-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 2.4rem; }
.brand { display: flex; align-items: center; gap: 0.65rem; padding: 0 0.3rem; text-decoration: none; min-width: 0; }
.brand:hover .brand-name { color: #fff; opacity: 0.85; }
.brand-mark-img { height: 32px; width: auto; display: block; flex-shrink: 0; }
.brand-name { font-family: var(--font-heading); font-weight: 700; font-size: 0.96rem; line-height: 1.2; letter-spacing: -0.005em; color: #fff; }

/* Hamburger toggle -- hidden entirely outside the mobile breakpoint below,
   where the sidebar is a top bar instead of a full-height column and needs
   a way to reveal the nav/footer without them taking up permanent space. */
.nav-toggle {
  display: none; flex-direction: column; justify-content: center; align-items: center;
  gap: 4px; width: 34px; height: 34px; flex-shrink: 0;
  background: none; border: none; border-radius: var(--radius-sm); cursor: pointer; padding: 0;
}
.nav-toggle:hover { background: rgba(233, 242, 254, 0.07); }
.nav-toggle-bar { display: block; width: 19px; height: 2px; border-radius: 2px; background: #E9F2FE; transition: transform 0.2s ease, opacity 0.2s ease; }
.sidebar.nav-open .nav-toggle-bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.sidebar.nav-open .nav-toggle-bar:nth-child(2) { opacity: 0; }
.sidebar.nav-open .nav-toggle-bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.nav { display: flex; flex-direction: column; gap: 0.2rem; flex: 1; }
.nav-link {
  color: #A9C4BD;
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  font-weight: 500;
  transition: background 0.15s ease, color 0.15s ease;
}
.nav-link:hover { background: rgba(233, 242, 254, 0.07); text-decoration: none; color: #fff; }
.nav-link.active { background: rgba(23, 133, 240, 0.35); color: #fff; font-weight: 600; box-shadow: inset 0 0 0 1px rgba(233, 242, 254, 0.08); }

.sidebar-footer { border-top: 1px solid rgba(233, 242, 254, 0.12); padding-top: 1.1rem; margin-top: 0.5rem; }
.user-chip { display: flex; align-items: center; gap: 0.65rem; margin-bottom: 0.9rem; }
.user-avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 0.85rem; flex-shrink: 0;
  box-shadow: 0 0 0 2px rgba(233, 242, 254, 0.15);
}
.user-name { font-size: 0.87rem; font-weight: 600; color: #fff; }
.user-role { font-size: 0.75rem; color: #8CAAA2; text-transform: capitalize; }
.signout { font-size: 0.82rem; color: #8CAAA2; display: block; margin-top: 0.45rem; transition: color 0.15s ease; }
.signout:first-of-type { margin-top: 0; }
.signout:hover { color: #fff; }

.main { flex: 1; padding: 2.6rem 3rem; max-width: 1140px; }

/* ---------- Flash messages ---------- */
.flash-stack { margin-bottom: 1.2rem; display: flex; flex-direction: column; gap: 0.5rem; }
.flash-stack-solo { max-width: 420px; margin: 1.5rem auto 0; }
.flash {
  padding: 0.65rem 0.9rem;
  border-radius: 8px;
  font-size: 0.88rem;
  font-weight: 500;
}
.flash-success { background: var(--primary-tint); color: var(--primary-dark); }
.flash-error { background: var(--danger-tint); color: var(--danger-text); }
.flash-warning { background: var(--accent-tint); color: var(--accent-text); }

/* ---------- Billing reminder banner ---------- */
.billing-alert {
  padding: 0.75rem 1rem; border-radius: 10px; font-size: 0.88rem;
  margin-bottom: 1.2rem; line-height: 1.5;
}
.billing-alert strong { display: inline; margin-right: 0.3rem; }
/* Same contrast issue as the status tags above (this banner's text is 0.88rem
   -- too small to qualify for WCAG's "large text" 3:1 exemption, so it needs
   the full 4.5:1) -- same --accent-text/--danger-text fix. */
.billing-alert-due_soon { background: var(--accent-tint); color: var(--accent-text); }
.billing-alert-overdue { background: var(--danger-tint); color: var(--danger-text); }
.billing-alert-link {
  font-weight: 700; text-decoration: underline; text-underline-offset: 2px;
  margin-left: 0.4rem; color: inherit;
}
.billing-alert-link:hover { opacity: 0.8; }

/* ---------- Page header ---------- */
.page-header {
  display: flex; align-items: flex-end; justify-content: space-between;
  margin-bottom: 1.6rem; gap: 1rem; flex-wrap: wrap;
}
.eyebrow {
  text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.09em;
  color: var(--muted); font-weight: 600; margin: 0 0 0.3rem;
}
.header-actions { display: flex; gap: 0.6rem; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.6rem 1.1rem;
  border-radius: var(--radius-sm);
  font-size: 0.87rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  border: 1px solid transparent;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn-block { width: 100%; }
.btn-primary { background: var(--primary); color: #fff; box-shadow: 0 1px 2px rgba(10,30,66,0.08), 0 6px 14px -6px rgba(23,133,240,0.45); }
.btn-primary:hover { background: var(--primary-dark); box-shadow: 0 2px 4px rgba(10,30,66,0.1), 0 10px 20px -8px rgba(23,133,240,0.5); }
.btn-secondary { background: var(--surface); color: var(--primary-dark); border-color: var(--border); }
.btn-secondary:hover { border-color: var(--primary); background: var(--primary-tint); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { color: var(--ink); background: var(--surface-alt); }
.btn-danger { background: var(--danger-tint); color: var(--danger-text); }
.btn-danger:hover { background: var(--danger); color: #fff; }
.link-danger {
  background: none; border: none; padding: 0; color: var(--danger);
  font-size: 0.83rem; cursor: pointer; font-family: inherit; text-decoration: underline;
}
.inline-form { display: inline; }

/* ---------- Stat cards ---------- */
.stat-row { display: flex; gap: 1rem; margin-bottom: 1.6rem; }
.stat-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.2rem 1.4rem; flex: 1; min-width: 0; box-shadow: var(--shadow);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.stat-value { display: block; font-family: var(--font-heading); font-weight: 700; font-size: 2rem; letter-spacing: -0.02em; color: var(--primary-dark); }
.stat-label { font-size: 0.85rem; color: var(--muted); }

/* ---------- Panels ---------- */
.panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.6rem 1.7rem; box-shadow: var(--shadow);
  /* Defensive: if anything inside a panel (a table, the weekly calendar grid,
     a long unbreakable string) is ever wider than the panel itself, scroll
     it inside its own box instead of forcing the whole page wider than the
     viewport -- the mobile nav overflow bug taught us that a single overflow
     anywhere can shift/clip everything else on the page. */
  overflow-x: auto;
}
/* h1/h2/h3 reset to margin:0 sitewide (see top of file) -- panels rely on
   their h2 title having *some* space before whatever follows (a chart,
   a card grid, a table), so give panel titles their gap back here instead
   of patching it per-page. Panels that truly want the title flush against
   the next element already override this inline (see admin_billing.html). */
.panel h2 { margin-bottom: 0.9rem; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; }
.grid-detail { display: grid; grid-template-columns: 300px 1fr; gap: 1.2rem; align-items: start; }

/* ---------- Table ---------- */
.table { width: 100%; border-collapse: collapse; }
.table th {
  text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600; padding: 0 0.6rem 0.6rem; border-bottom: 1px solid var(--border);
}
.table td { padding: 0.65rem 0.6rem; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.table tbody tr { cursor: pointer; }
.table tbody tr:hover { background: var(--primary-tint); }
.table tbody tr:last-child td { border-bottom: none; }

/* A plain reference table (e.g. the Data Diagram's table list, admin_guide.html
   Sec.10) isn't a clickable row list like the rest of .table's uses, so it
   opts out of the pointer cursor and hover tint above. */
.diagram-table-reference tbody tr { cursor: default; }
.diagram-table-reference tbody tr:hover { background: none; }
.diagram-table-reference tr.diagram-table-group td {
  padding-top: 1.1rem; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--primary-text); border-bottom: 1px solid var(--border);
}
.diagram-table-reference tr.diagram-table-group:first-child td { padding-top: 0.65rem; }
.diagram-table-reference td:first-child { white-space: nowrap; }
.diagram-table-reference code { background: var(--bg); padding: 0.1rem 0.35rem; border-radius: 4px; font-size: 0.85em; }

/* ---------- Invoice line items (billing) ---------- */
.invoice-items-table { width: 100%; border-collapse: collapse; }
.invoice-items-table th {
  text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600; padding: 0 0.4rem 0.5rem; border-bottom: 1px solid var(--border);
}
.invoice-items-table td { padding: 0.35rem 0.4rem; vertical-align: top; }
.invoice-items-table input {
  width: 100%; padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: 8px;
  font-size: 0.9rem; font-family: inherit; box-sizing: border-box;
}
.invoice-items-table .qty-input { width: 4.5rem; }
.invoice-items-table .price-input { width: 6.5rem; }
.invoice-items-table .remove-row {
  padding: 0.4rem 0.6rem; font-size: 1rem; line-height: 1;
}

/* ---------- Mini list (dashboard) ---------- */
.mini-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.mini-list li { border-bottom: none; }
/* Scoped to direct-child anchors only (li > a) — the Today's Appointments
   widget nests its link one level deeper (li > div > a) with its own layout,
   so this card treatment doesn't collide with that row's status tag/actions. */
.mini-list > li > a {
  display: block; position: relative; color: var(--ink);
  padding: 0.85rem 2.3rem 0.85rem 1rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-alt);
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.mini-list > li > a::after {
  content: '\2192'; color: var(--muted); font-size: 1rem;
  position: absolute; right: 1rem; top: 50%; transform: translateY(-50%);
  transition: transform 0.15s ease, color 0.15s ease;
}
.mini-list > li > a:hover {
  text-decoration: none; border-color: var(--primary); background: var(--primary-tint);
  box-shadow: var(--shadow-sm); transform: translateY(-1px);
}
.mini-list > li > a:hover::after { transform: translateY(-50%) translateX(3px); color: var(--primary-text); }
.mini-list-title { font-weight: 600; font-size: 0.9rem; display: block; }
.mini-list-sub { font-size: 0.85rem; color: var(--muted); display: block; margin-top: 0.2rem; }

/* ---------- Contact cards (Support page) ---------- */
/* There are three contact cards (Facebook, WhatsApp, Email) -- this was
   hard-set to two columns from when there were only two channels, which
   left the third (Email) sitting alone on its own row below a pair. */
.contact-card-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }
.contact-card {
  display: flex; align-items: flex-start; gap: 1rem; min-width: 0;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.4rem; color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.2s ease, transform 0.15s ease;
}
.contact-card:hover {
  text-decoration: none; border-color: transparent; box-shadow: var(--shadow-lg); transform: translateY(-2px);
}
.contact-icon {
  width: 44px; height: 44px; border-radius: 12px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.3rem; color: #fff;
}
.contact-icon-facebook { background: #1877F2; }
.contact-icon-whatsapp { background: #25D366; }
.contact-icon-email { background: var(--primary, #1D78EA); }
.contact-card-body { min-width: 0; }
.contact-card-title { font-weight: 700; font-size: 0.98rem; color: var(--ink); display: flex; align-items: center; gap: 0.4rem; }
.contact-card-sub {
  /* text-overflow only clips block-level boxes -- this is a <span> (inline
     by default), so overflow/ellipsis below were silently no-ops until
     this was added, and the un-wrapping URL just spilled out of the card
     instead of truncating. */
  display: block;
  font-size: 0.85rem; color: var(--muted); margin-top: 0.25rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.contact-card-arrow { margin-left: auto; color: var(--muted); font-size: 1.1rem; align-self: center; transition: transform 0.15s ease, color 0.15s ease; }
.contact-card:hover .contact-card-arrow { transform: translateX(3px); color: var(--primary-text); }

/* The Support page's logged-out view embeds these cards inside the narrow
   (~480px) auth panel, not the wide main content column — always stack them
   there regardless of viewport width, or a long URL squeezed into half of
   480px wraps letter-by-letter. */
.auth-panel .contact-card-grid { grid-template-columns: 1fr; }

/* Step down to 2 columns before the full single-column stack below, so
   three cards don't get squeezed uncomfortably narrow in the gap between
   "plenty of room for 3" and "only room for 1". */
@media (max-width: 1000px) {
  .contact-card-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 640px) {
  .contact-card-grid { grid-template-columns: 1fr; }
}

.empty { color: var(--muted); font-size: 0.9rem; padding: 0.5rem 0; }

/* ---------- Payment method selector (Pay now) ---------- */
.pay-method-grid { display: flex; gap: 0.6rem; flex-wrap: wrap; margin: 0.8rem 0 1.2rem; align-items: center; }
.pay-method-btn-logo { display: flex; align-items: center; justify-content: center; width: 108px; padding: 0.5rem 0.6rem; }
.pay-method-btn-logo svg { display: block; width: 100%; height: 26px; }
.pay-method-panel { margin-top: 0.8rem; }
.pay-qr-placeholder {
  width: 220px; height: 220px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.6rem; text-align: center; padding: 1rem; border: 2px dashed var(--border); border-radius: var(--radius);
  color: var(--muted); font-size: 0.85rem; background: var(--surface); margin-bottom: 0.8rem;
}
.pay-qr-placeholder[hidden] { display: none; }
.pay-qr-spinner {
  width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid var(--border); border-top-color: var(--primary, #0d6e6e);
  animation: pay-qr-spin 0.8s linear infinite;
}
@keyframes pay-qr-spin { to { transform: rotate(360deg); } }
.pay-qr-image {
  display: block; width: 220px; height: 220px; max-width: 100%; object-fit: contain;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  padding: 0.6rem; margin-bottom: 0.8rem;
}

/* ---------- Search bar ---------- */
.search-bar { display: flex; gap: 0.6rem; margin-bottom: 1.2rem; }
.search-bar input {
  flex: 1; min-width: 0; padding: 0.6rem 0.8rem; border: 1px solid var(--border); border-radius: 8px;
  font-family: inherit; font-size: 0.9rem; background: var(--surface);
}

/* ---------- Forms ---------- */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.2rem; }
.form-grid label {
  display: flex; flex-direction: column; gap: 0.35rem;
  font-size: 0.82rem; font-weight: 600; color: var(--muted);
}
.form-grid input, .form-grid select, .form-grid textarea {
  padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: 7px;
  font-family: inherit; font-size: 0.92rem; color: var(--ink); background: var(--surface);
  line-height: 1.4;
}
/* Reported as "list fields are smaller than the others" (book_intake.html's
   Yes/No dropdowns next to its text inputs): a <select> has its own
   browser/OS-native chrome (the dropdown arrow, its own internal vertical
   metrics), which doesn't reliably respect the shared padding/line-height
   rule above the way <input>/<textarea> do -- even with identical CSS, a
   native select can render visibly shorter. appearance: none hands full
   control of the box back to this stylesheet; the background-image below
   redraws the arrow that removes, sized/positioned/colored (--muted) to
   match the rest of the UI. Scoped to .form-grid select only, so
   input/textarea keep their native rendering. */
.form-grid select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%235F7369' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 13px;
}
.form-grid input:focus, .form-grid select:focus, .form-grid textarea:focus,
.search-bar input:focus, .auth-form input:focus {
  outline: 2px solid var(--primary); outline-offset: 1px; border-color: var(--primary);
}
.span-2 { grid-column: span 2; }
.form-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 0.4rem; }

/* ---------- Patient detail ---------- */
.detail-list { display: flex; flex-direction: column; gap: 0.7rem; margin: 0 0 1.3rem; }
.detail-list div { display: flex; justify-content: space-between; gap: 1rem; border-bottom: 1px dashed var(--border); padding-bottom: 0.5rem; }
.detail-list dt { color: var(--muted); font-size: 0.85rem; }
.detail-list dd { margin: 0; font-weight: 500; text-align: right; }
.detail-alert dd { color: var(--danger); }

/* ---------- Timeline (signature element) ---------- */
/* A patient's Dental history only grows over time, and used to render every
   visit fully expanded -- for a long-time patient that meant scrolling past
   a wall of old Treatment/Medications/Notes text just to reach the next
   entry, and the panel itself grew tall enough to push everything below it
   down the page indefinitely (same shape of problem as the calendar's
   hour grid earlier). Two changes address it together: each entry is a
   native <details>/<summary> (patient_detail.html) collapsed to one
   compact line by default -- date, visit type, diagnosis -- with the most
   recent entry pre-expanded; and the panel itself is capped and scrolls
   internally instead of growing the page, same pattern as
   .calendar-grid-wrap. <details> needs no JS, is keyboard-operable out of
   the box, and only the entries someone actually opens cost any real
   vertical space. */
.timeline-panel {
  max-height: calc(100vh - 280px); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.timeline-panel::-webkit-scrollbar { width: 8px; }
.timeline-panel::-webkit-scrollbar-track { background: transparent; }
.timeline-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; }
.timeline-panel::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.timeline { list-style: none; margin: 0; padding: 0; position: relative; }
.timeline::before {
  content: ''; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 2px; background: var(--border);
}
.timeline-item { position: relative; padding-left: 1.8rem; padding-bottom: 1.6rem; }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-marker {
  position: absolute; left: 0; top: 4px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--primary); border: 2px solid var(--surface); box-shadow: 0 0 0 2px var(--primary);
}
.timeline-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 0.25rem;
  cursor: pointer; list-style: none;
}
.timeline-head::-webkit-details-marker { display: none; }
.timeline-head::before {
  content: '\25B8'; color: var(--muted); font-size: 0.7rem; flex-shrink: 0;
  transition: transform 0.15s ease;
}
.timeline-body[open] > .timeline-head::before { transform: rotate(90deg); }
.timeline-summary-title {
  font-family: var(--font-heading); font-weight: 700; color: var(--primary-dark); font-size: 1rem;
}
.timeline-details { padding-top: 0.15rem; }
.timeline-date { font-family: 'IBM Plex Mono', monospace; font-size: 0.78rem; color: var(--muted); }
.tag {
  /* --accent-text, not --accent: plain --accent only clears ~2.7:1 as text
     here, well under WCAG AA's 4.5:1 minimum for small text -- see the
     --accent-text definition at the top of this file. */
  background: var(--accent-tint); color: var(--accent-text); font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; padding: 0.15rem 0.5rem; border-radius: 5px;
}
.tag.pay-status-paid { background: var(--primary-tint); color: var(--primary-dark); }
.tag.pay-status-pending { background: var(--accent-tint); color: var(--accent-text); }
.tag.pay-status-overdue { background: var(--danger-tint); color: var(--danger-text); }

/* ---------- Help guide ---------- */
.help-toc { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin: 0 0 1.6rem; padding: 0; list-style: none; }
.help-toc a { font-size: 0.85rem; font-weight: 600; }
.help-section { margin-top: 1.6rem; padding-top: 1.4rem; border-top: 1px solid var(--border); }
.help-section:first-of-type { margin-top: 0; padding-top: 0; border-top: none; }
.help-subsection { margin-top: 1.2rem; }
.help-subsection h3 { margin: 0 0 0.4rem; }
.help-callout {
  padding: 0.65rem 0.9rem; border-radius: 8px; font-size: 0.88rem; line-height: 1.5;
  margin: 0.6rem 0 1rem; border-left: 3px solid var(--primary); background: var(--primary-tint);
}
.help-callout.help-callout-tip { border-left-color: var(--accent); background: var(--accent-tint); }
.ui-label {
  display: inline-block; background: var(--surface-alt); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.05rem 0.4rem; font-size: 0.85em; font-weight: 600; color: var(--primary-dark);
}
.help-code {
  font-family: 'IBM Plex Mono', monospace; font-size: 0.82rem; line-height: 1.7;
  background: var(--surface-alt); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.7rem 0.9rem; margin: 0.5rem 0 1rem; overflow-x: auto; white-space: pre;
  color: var(--primary-dark);
}

/* ---------- Analytics ---------- */
.analytics-controls { display: flex; gap: 0.5rem; margin-bottom: 1.4rem; }
.analytics-group-heading {
  font-size: 1.3rem; font-weight: 700; color: var(--primary-dark);
  /* margin-top adds breathing room *above* the border-top rule itself --
     without it, the gap before this divider was only whatever bottom
     margin the preceding element happened to have (e.g. .analytics-note's
     1rem), which reads as the note text nearly touching the rule below it. */
  margin: 0.8rem 0 1rem; padding-top: 1.8rem; border-top: 1px solid var(--border);
}
.analytics-group-heading:first-of-type { padding-top: 0; border-top: none; margin-top: 0; }
.analytics-section { margin-top: 1.6rem; }
.analytics-section:first-of-type { margin-top: 0; }
.analytics-section h2 { margin-bottom: 0.9rem; }
.chart-wrap { position: relative; height: 260px; margin: 0.6rem 0 1.2rem; }
.chart-wrap.chart-wrap-sm { height: 200px; }
.analytics-note { font-size: 0.85rem; color: var(--muted); margin: -0.4rem 0 1rem; }
/* The -0.4rem top margin above is tuned for .analytics-note right after
   .analytics-controls (pulls it in closer to the range buttons) -- after a
   .grid-2 of chart panels it instead reads as the caption nearly touching
   the bottom edge of the cards, since .grid-2 itself has no bottom margin
   of its own to push against. Give that specific pairing real breathing
   room instead. */
.grid-2 + .analytics-note { margin-top: 0.9rem; }
.timeline-body p { margin: 0.25rem 0; font-size: 0.88rem; }
.timeline-notes { color: var(--muted); font-style: italic; }
.timeline-meta { display: flex; gap: 0.9rem; margin-top: 0.5rem; font-size: 0.85rem; color: var(--muted); align-items: center; }

/* ---------- Auth screen ---------- */
.auth-screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--bg); padding: 1rem; }
.auth-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 2.4rem 2.2rem; width: 340px; max-width: 100%; min-width: 0; box-shadow: var(--shadow); text-align: center;
  box-sizing: border-box;
}
.auth-mark {
  width: 44px; height: 44px; border-radius: 10px; background: var(--accent); color: var(--primary-dark);
  display: flex; align-items: center; justify-content: center; margin: 0 auto 1rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 1.4rem;
}
.auth-mark-img { height: 56px; width: auto; display: block; margin: 0 auto 1rem; }
.auth-mark-text { display: block; text-align: center; font-family: var(--font-heading); font-weight: 800; font-size: 1.3rem; letter-spacing: -0.01em; color: var(--primary-dark); margin: 0 auto 1rem; }
.auth-panel h1 { font-size: 1.5rem; }
.auth-sub { color: var(--muted); font-size: 0.85rem; margin: 0.2rem 0 1.6rem; }
.auth-form { display: flex; flex-direction: column; gap: 0.9rem; text-align: left; }
.auth-form label { font-size: 0.82rem; font-weight: 600; color: var(--muted); display: flex; flex-direction: column; gap: 0.35rem; }
.auth-form input { padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: 7px; font-family: inherit; font-size: 0.92rem; }
.auth-form .btn { margin-top: 0.4rem; }
.auth-hint { font-size: 0.75rem; color: var(--muted); margin-top: 1.2rem; line-height: 1.5; }
.auth-hint code { background: var(--bg); padding: 0.1rem 0.35rem; border-radius: 4px; }

/* ---------- Responsive ---------- */
/* This used to be 3 separate @media (max-width: 860px) blocks scattered
   through the file (here, plus two more further down, one for the
   odontogram tooth size and one for the intake checkbox grid) -- same
   breakpoint, same behavior, just harder to see at a glance that they're
   all the same condition. Consolidated into one block down where the third
   one used to live (see the "Responsive (consolidated)" comment below) so
   there's exactly one 860px query to update if this breakpoint ever
   changes. Moving each rule set later in the file rather than earlier is
   what keeps this safe -- CSS resolves same-specificity conflicts by
   source order, and every rule below already came *after* its own
   unconditional (non-media) counterpart at its old position, so moving it
   further down can only keep that override winning, never break it.
   Nothing here changes what actually renders at any screen width.
*/

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------- Odontogram (dental chart) ---------- */
.odontogram-header {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 0.8rem; margin-bottom: 0.8rem;
}
.odontogram-legend-groups { display: flex; flex-wrap: wrap; gap: 1.6rem; margin-bottom: 0.8rem; }
.odontogram-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 0.8rem; }
.legend-title {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 600; margin-right: 0.2rem;
}
.legend-item { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; color: var(--muted); }
.legend-item i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.odontogram-hint { font-size: 0.85rem; color: var(--muted); margin: 0 0 1.2rem; max-width: 640px; }

.tooth-row { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.odontogram-arch-label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--muted); font-weight: 600; margin: 0.6rem 0 0.4rem;
}
.odontogram-arch-label:first-child { margin-top: 0; }

.tooth {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  user-select: none; width: 40px;
}
.tooth-svg { width: 32px; height: 40px; transition: transform 0.1s ease; overflow: visible; }
.tooth:hover .tooth-svg { transform: translateY(-3px); }

.tooth-surface {
  fill: #FFFFFF; stroke: var(--border); stroke-width: 0.75;
  cursor: pointer; transition: fill 0.15s ease;
}
.tooth-surface:hover { stroke: var(--primary); stroke-width: 1.25; }

.tooth-root {
  fill: #FFFFFF; stroke: var(--border); stroke-width: 1.5;
  cursor: pointer; transition: stroke 0.15s ease;
}
.tooth-root:hover { stroke: var(--primary); }

.tooth-crown-outline {
  fill: none; stroke: var(--border); stroke-width: 1.5; pointer-events: none;
}
.tooth-whole-marked .tooth-crown-outline { stroke: var(--ink); stroke-width: 2; }

.tooth-whole-overlay {
  fill: none; opacity: 0; pointer-events: none;
  transition: fill 0.15s ease, opacity 0.15s ease;
}

.tooth-num {
  font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; color: var(--muted);
}

.odontogram-editor {
  display: none; margin-top: 1.2rem; padding-top: 1.2rem; border-top: 1px solid var(--border);
  max-width: 480px;
}
.odontogram-editor.open { display: block; }
.odontogram-editor-title { margin-bottom: 0.15rem; }
.odontogram-editor-surface { font-size: 0.85rem; color: var(--muted); margin: 0 0 0.8rem; font-weight: 600; }
.odontogram-conditions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.8rem; }
.odontogram-conditions button {
  padding: 0.4rem 0.7rem; border-radius: 7px; border: 1px solid var(--border);
  background: var(--surface); font-size: 0.8rem; font-weight: 600; color: var(--muted);
  cursor: pointer; font-family: inherit;
}
.odontogram-conditions button.active { border-color: var(--primary); color: var(--primary-dark); background: var(--primary-tint); }
.odontogram-editor textarea {
  width: 100%; padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: 7px;
  font-family: inherit; font-size: 0.88rem; resize: vertical; margin-bottom: 0.8rem;
}
.odontogram-editor-actions { display: flex; justify-content: flex-end; gap: 0.6rem; }

/* ---------- Appointment booking (public page) ---------- */
.booking-screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--bg); padding: 2.5rem 1rem; }
.booking-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 2.2rem 2rem; width: 100%; max-width: 560px; box-shadow: var(--shadow);
}
.booking-sub { color: var(--muted); font-size: 0.85rem; margin: 0.3rem 0 1.4rem; }

.booking-existing-banner {
  background: var(--primary-tint); color: var(--primary-dark); padding: 0.7rem 0.9rem;
  border-radius: 8px; font-size: 0.88rem; margin-bottom: 1.2rem;
}

/* ---------- Appointments (staff queue) ---------- */
.appointment-filters { display: flex; gap: 0.5rem; margin-bottom: 1.2rem; }
.appointment-actions { display: flex; gap: 0.7rem; }
.link-confirm {
  background: none; border: none; padding: 0; color: var(--primary-text);
  font-size: 0.83rem; cursor: pointer; font-family: inherit; text-decoration: underline;
}
.appt-status-pending { background: var(--accent-tint); color: var(--accent-text); }
.appt-status-confirmed { background: var(--primary-tint); color: var(--primary-dark); }
.appt-status-cancelled { background: var(--danger-tint); color: var(--danger-text); }

/* ---------- Appointments (weekly calendar) ---------- */
.view-toggle { display: flex; gap: 0.5rem; margin-left: auto; }

.calendar-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 1rem; flex-wrap: wrap; gap: 0.6rem;
}
.calendar-range { margin: 0; }
.calendar-nav { display: flex; gap: 0.4rem; }

/* Contained inside its own frame, with its own scrollbar, instead of the
   whole page growing tall enough to need the browser's page-level scrollbar
   -- the 37rem-tall hour grid below otherwise pushed total page height past
   most viewports, and the resulting full-height scrollbar down the right
   edge of the whole screen read as a stray UI element rather than part of
   the calendar. max-height is sized against the viewport, leaving room for
   the page header/toolbar above; the scrollbar itself is styled slim
   instead of the browser's default full-width track. */
.calendar-grid-wrap {
  overflow-x: auto; overflow-y: auto; max-height: calc(100vh - 280px);
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.calendar-grid-wrap::-webkit-scrollbar { width: 8px; height: 8px; }
.calendar-grid-wrap::-webkit-scrollbar-track { background: transparent; }
.calendar-grid-wrap::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; }
.calendar-grid-wrap::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.calendar-grid {
  display: grid;
  grid-template-columns: 52px repeat(7, minmax(104px, 1fr));
  min-width: 660px;
}

.calendar-corner { border-bottom: 1px solid var(--border); }
.calendar-day-head {
  text-align: center; padding: 0.4rem 0 0.6rem; border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 0.15rem;
}
.calendar-day-head.is-today .calendar-day-num { color: var(--primary-text); font-weight: 700; }
.calendar-day-label { font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }
.calendar-day-num { font-family: var(--font-heading); font-weight: 700; font-size: 1.1rem; color: var(--ink); }

.calendar-unscheduled-label {
  font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 600; display: flex; align-items: center;
  justify-content: flex-end; padding-right: 6px; border-bottom: 1px solid var(--border);
}
.calendar-unscheduled-cell {
  display: flex; flex-direction: column; gap: 0.25rem; padding: 0.35rem;
  border-bottom: 1px solid var(--border); border-left: 1px solid var(--border);
  min-height: 1.6rem;
}
.calendar-chip {
  display: block; width: 100%; font-size: 0.74rem; padding: 0.15rem 0.4rem; border-radius: 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  appearance: none; -webkit-appearance: none; border: none; text-align: left;
  font-family: inherit; cursor: pointer;
}
.calendar-chip:hover { filter: brightness(0.97); }

.calendar-time-axis, .calendar-day-col { position: relative; height: 37rem; }
.calendar-day-col { border-left: 1px solid var(--border); }
.calendar-hour-label {
  position: absolute; left: 0; right: 6px; transform: translateY(-50%);
  font-size: 0.72rem; color: var(--muted); text-align: right;
}
.calendar-hour-line { position: absolute; left: 0; right: 0; border-top: 1px solid var(--border); }

.appt-block {
  /* left/width are set inline per-block (see appointments.html) from the
     lane assignment _build_week_calendar() computes in app.py -- two
     appointments booked for the same or overlapping time (allowed on
     purpose, not a bug -- see the clash-warning in book_confirm()) get
     their own side-by-side lane instead of both being drawn at full column
     width in the same spot, where the second one used to completely and
     invisibly cover the first. 3px here is only a fallback for the
     unexpected case where lane data is somehow missing. */
  position: absolute; left: 3px; right: 3px; min-height: 32px; overflow: hidden;
  border-radius: 6px; border-left: 3px solid currentColor; padding: 2px 6px;
  font-size: 0.7rem; line-height: 1.15; display: flex; flex-direction: column;
  justify-content: center; color: inherit; box-shadow: var(--shadow);
  appearance: none; -webkit-appearance: none; border-top: none; border-right: none;
  border-bottom: none; text-align: left; font-family: inherit; cursor: pointer;
}
.appt-block:hover { filter: brightness(0.97); text-decoration: none; }
.appt-block-time { font-weight: 600; font-size: 0.68rem; }
.appt-block-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appt-block-clinic { font-size: 0.65rem; opacity: 0.8; }

.calendar-popover-backdrop { position: fixed; inset: 0; background: transparent; z-index: 40; }
.calendar-popover {
  position: absolute; z-index: 41; width: 260px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 0.9rem 1rem;
}
.calendar-popover-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.3rem; }
.calendar-popover-time { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
.calendar-popover-close {
  background: none; border: none; padding: 0; color: var(--muted); font-size: 1.2rem;
  line-height: 1; cursor: pointer;
}
.calendar-popover-close:hover { color: var(--ink); }
.calendar-popover h3 { margin: 0 0 0.15rem; }
.calendar-popover-meta { margin: 0 0 0.2rem; font-size: 0.85rem; color: var(--muted); }
.calendar-popover-reason { margin: 0.3rem 0; font-size: 0.85rem; }
.calendar-popover-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.7rem; }
.calendar-popover-actions .btn { padding: 0.4rem 0.75rem; font-size: 0.82rem; }

/* Generic centered modal (currently just the "Cancel appointment" dialog
   in appointments.html) -- distinct from .calendar-popover above, which is
   anchored to its trigger rather than centered, and doesn't dim the rest
   of the page. */
.modal-backdrop { position: fixed; inset: 0; background: rgba(10, 30, 66, 0.45); z-index: 60; }
.modal-panel {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 61;
  width: min(420px, calc(100vw - 2rem));
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 1.3rem 1.4rem;
}
.modal-panel h3 { margin: 0 0 0.3rem; }
.modal-panel .muted { margin: 0 0 0.8rem; font-size: 0.85rem; color: var(--muted); }
.modal-panel textarea {
  width: 100%; resize: vertical; font-family: inherit; font-size: 0.9rem; color: var(--ink);
  padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: 7px; background: var(--surface);
}
.modal-panel textarea:focus { outline: 2px solid var(--primary); outline-offset: 1px; border-color: var(--primary); }
.modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem; }

/* ---------- Dashboard: today's appointments ---------- */
.todays-appt-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 0.6rem 0.2rem;
}
.todays-appt-row .todays-appt-link {
  flex: 1; min-width: 0; padding: 0; display: flex !important; flex-direction: column;
}
.todays-appt-time { font-size: 0.78rem; color: var(--primary-text); font-weight: 700; }
.todays-appt-actions { display: flex; align-items: center; gap: 0.6rem; flex-shrink: 0; }

/* ---------- Freehand chart notes ---------- */
.freehand-toolbar { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.freehand-colors { display: flex; gap: 0.4rem; margin-right: 0.4rem; }
.color-swatch {
  width: 26px; height: 26px; border-radius: 50%; border: 2px solid transparent;
  cursor: pointer; padding: 0; box-shadow: 0 0 0 1px var(--border);
}
.color-swatch.active { border-color: var(--surface); box-shadow: 0 0 0 2px var(--primary); }
#freehand-erase.active { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.freehand-canvas-wrap { position: relative; touch-action: none; }
.freehand-canvas-wrap canvas {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  pointer-events: none;
  /* The canvas is a transparent layer sitting on top of the tooth-chart
     image, not part of the same bitmap -- by default a stroke drawn over a
     black outline paints fully opaque and blots the line out underneath it.
     multiply blends the stroke color with whatever's already there instead
     of covering it: black stays black no matter what color is drawn over
     it, and marks over white areas keep their full color, so linework stays
     readable under any freehand marking. */
  mix-blend-mode: multiply;
}
.freehand-canvas-wrap.editing canvas { pointer-events: auto; cursor: crosshair; }

/* Reuse the interactive tooth shapes as a plain, non-clickable outline background */
.freehand-bg-image {
  display: block; width: 100%; height: auto; user-select: none; pointer-events: none;
  border-radius: 8px;
}
.chart-attribution {
  font-size: 0.75rem; color: var(--muted); margin-top: 0.6rem;
}

/* ---------- Freehand chart accordion (adult / pediatric) ---------- */
.freehand-accordion-summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: space-between;
  font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem; color: var(--primary-dark);
}
.freehand-accordion-summary::-webkit-details-marker { display: none; }
.freehand-accordion-summary::marker { content: ""; }
.freehand-accordion-summary:hover { color: var(--primary-text); }
.accordion-chevron {
  color: var(--muted); font-size: 0.75rem; transition: transform 0.15s ease; flex-shrink: 0; margin-left: 0.8rem;
}
.freehand-accordion[open] .accordion-chevron { transform: rotate(180deg); }
.freehand-accordion-body { margin-top: 1rem; }
.chart-attribution a { color: var(--muted); text-decoration: underline; }

/* ---------- Generic collapsible panel (Billing, Documents, Platform
   Billing's sections, Site Guide, etc.) -- same <details>/<summary>
   open/close mechanics as the freehand chart accordion above, and
   reuses its .accordion-chevron, just generalized under a plain name so
   any .panel can become collapsible without a chart-specific class. */
.panel-accordion-summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: space-between;
}
.panel-accordion-summary::-webkit-details-marker { display: none; }
.panel-accordion-summary::marker { content: ""; }
.panel-accordion-summary h2 { margin-bottom: 0; }
.panel-accordion[open] .accordion-chevron { transform: rotate(180deg); }
.panel-accordion-body { margin-top: 0.9rem; }

/* ---------- Patient intake form ---------- */
.intake-panel { max-width: 720px; text-align: left; }
.intake-panel .booking-form { text-align: left; }
.intake-section-title {
  margin: 1.8rem 0 0.9rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
.intake-panel form > .intake-section-title:first-of-type,
.intake-section-title:first-child { margin-top: 0.6rem; padding-top: 0; border-top: none; }
.intake-subhead {
  font-weight: 700; font-size: 0.92rem; color: var(--primary-dark);
  margin: 1.2rem 0 0.6rem;
}
.intake-checkbox-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem 1rem; margin-bottom: 0.6rem;
}
.intake-checkbox-grid-3col { grid-template-columns: repeat(3, 1fr); }
.checkbox-item {
  display: flex; align-items: center; gap: 0.45rem; font-size: 0.85rem; color: var(--ink);
  font-weight: 400; cursor: pointer;
}
.checkbox-item input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px; flex-shrink: 0; margin: 0;
  border: 1px solid var(--border); border-radius: 5px; background: var(--surface);
  display: inline-grid; place-content: center; cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.checkbox-item input[type="checkbox"]::before {
  content: ""; width: 10px; height: 10px; background: var(--surface);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  transform: scale(0); transition: transform 0.1s ease-in-out;
}
.checkbox-item input[type="checkbox"]:checked { background: var(--primary); border-color: var(--primary); }
.checkbox-item input[type="checkbox"]:checked::before { transform: scale(1); }
.checkbox-item input[type="checkbox"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.inline-text-input {
  flex: 1; padding: 0.25rem 0.5rem; border: 1px solid var(--border); border-radius: 5px;
  font-family: inherit; font-size: 0.82rem; margin-left: 0.3rem;
}

.signature-pad-wrap {
  border: 1px dashed var(--border); border-radius: 8px; background: #FAFBFA;
  height: 160px; position: relative; touch-action: none; margin-bottom: 0.6rem;
}
.signature-pad-wrap canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: crosshair; }
.signature-actions { display: flex; justify-content: flex-end; margin-bottom: 0.4rem; }

/* ---------- Responsive (consolidated) ---------- */
/* See the comment near the top of the file (where this used to be the
   first of three separate 860px blocks) -- this is now the one place all
   of the app's max-width: 860px rules live. */
@media (max-width: 860px) {
  .shell { flex-direction: column; }
  /* The nav has up to 6 links (Dashboard, Patients, Appointments, Analytics,
     Settings, and Platform billing for super admins) -- that never fits on
     one unwrapping row on a phone-width screen. Previously .sidebar and
     .nav both stayed as a single flex row here, so the links silently
     overflowed past the sidebar's own background and pushed the whole page
     wider than the viewport, breaking the layout everywhere below it (see
     the "blank dashboard" mobile screenshot this fixed). Stacking the brand
     above a wrapping nav row keeps everything inside the sidebar's own
     width, no matter how many links are showing for a given user. */
  /* The sidebar becomes a slim top bar, not a full-height column -- with a
     phone screen this tall, showing the full nav (up to 6 links) and the
     full footer (account chip + 4 more links) at all times, as the earlier
     fix did, pushed real page content below the fold before you'd even
     scrolled. Collapsed by default, revealed on tap via .nav-toggle. */
  .sidebar { width: 100%; flex-direction: column; align-items: stretch; padding: 0.7rem 1rem; }
  .sidebar-top { margin-bottom: 0; }
  .nav-toggle { display: flex; }
  .nav {
    flex-direction: column; gap: 0.15rem; max-height: 0; overflow: hidden;
    margin-top: 0; transition: max-height 0.2s ease, margin-top 0.2s ease;
  }
  .sidebar-footer {
    max-height: 0; overflow: hidden; border-top: none; margin-top: 0; padding-top: 0;
    transition: max-height 0.2s ease;
  }
  .sidebar.nav-open .nav { max-height: 420px; margin-top: 0.9rem; }
  .sidebar.nav-open .sidebar-footer {
    max-height: 420px; margin-top: 0.9rem; padding-top: 0.9rem;
    border-top: 1px solid rgba(233, 242, 254, 0.12);
  }
  .main { padding: 1.4rem; }
  .grid-2, .grid-detail, .form-grid { grid-template-columns: 1fr; }
  .span-2 { grid-column: span 1; }
  /* Flex rows that assume desktop-width content and don't already wrap --
     without flex-wrap, any row like this that doesn't fit on one line
     pushes the whole page wider than the viewport (the same root cause as
     the earlier nav overflow bug), so these wrap on phone-width screens too. */
  .analytics-controls, .appointment-filters, .search-bar { flex-wrap: wrap; }
  .search-bar input { flex-basis: 100%; }
  /* Stat cards: at the 2rem desktop size, an amount like "PHP 12,850.00"
     is wider than a card can be once 3-4 of them share a phone-width row --
     the text doesn't clip, it just visually spills past the card's own
     right edge into whatever's next to it. Wrapping the row into a 2-column
     grid plus a smaller value size keeps every card self-contained. */
  .stat-row { flex-wrap: wrap; }
  .stat-card { flex: 1 1 calc(50% - 0.5rem); padding: 1rem 1.1rem; }
  .stat-value { font-size: 1.4rem; overflow-wrap: break-word; }

  /* Odontogram: smaller tooth targets so the full chart still fits a phone
     width without horizontal scrolling. */
  .tooth { width: 30px; }
  .tooth-svg { width: 24px; height: 32px; }
  .tooth-num { font-size: 0.6rem; }

  /* Patient intake form's condition checkboxes: single column instead of a
     multi-column grid, which gets too cramped at phone width. */
  .intake-checkbox-grid, .intake-checkbox-grid-3col { grid-template-columns: 1fr; }
}

/* ---------- Multi-tenant clinic context ---------- */
.clinic-context {
  font-size: 0.72rem; color: #9FBAB5; margin: -0.15rem 0 1.5rem 0.3rem;
  text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600;
}

.clinic-badge {
  display: inline-block; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--primary-dark); background: var(--primary-tint);
  padding: 0.15rem 0.55rem; border-radius: 5px;
}

.reset-form { display: flex; gap: 0.6rem; align-items: center; padding: 0.5rem 0; }
.reset-form input { flex: 1; max-width: 280px; padding: 0.5rem 0.7rem; border: 1px solid var(--border); border-radius: 7px; font-family: inherit; }

/* ---------- Document upload ---------- */
.upload-form {
  display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap;
  margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
.upload-form input[type="file"] {
  flex: 1; min-width: 200px; font-size: 0.85rem; color: var(--muted);
  padding: 0.4rem 0; font-family: inherit;
}

/* ---------- Consent modals ---------- */
.consent-checkbox-row { display: flex; align-items: baseline; gap: 0.6rem; margin-bottom: 0.7rem; font-size: 0.88rem; flex-wrap: wrap; }
.consent-checkbox-row .checkbox-item { margin: 0; }
.consent-link { color: var(--primary-text); font-weight: 600; }
.consent-modal-overlay {
  display: none; position: fixed; inset: 0; background: rgba(28, 38, 36, 0.55);
  z-index: 1000; align-items: center; justify-content: center; padding: 1.5rem;
}
.consent-modal-overlay.open { display: flex; }
.consent-modal {
  background: var(--surface); border-radius: 12px; max-width: 640px; width: 100%;
  max-height: 85vh; display: flex; flex-direction: column; box-shadow: var(--shadow);
}
.consent-modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1rem 1.3rem; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.consent-modal-header h3 { margin: 0; font-family: var(--font-heading); font-weight: 700; color: var(--primary-dark); }
.consent-modal-close {
  background: none; border: none; font-size: 1.6rem; line-height: 1; cursor: pointer; color: var(--muted);
  padding: 0 0.3rem;
}
.consent-modal-close:hover { color: var(--ink); }
.consent-modal-body { padding: 1.1rem 1.3rem; overflow-y: auto; font-size: 0.86rem; color: var(--ink); line-height: 1.5; }
.consent-modal-body h4 { font-size: 0.92rem; margin: 1rem 0 0.3rem; color: var(--primary-dark); }
.consent-modal-body h4:first-child { margin-top: 0; }
.consent-modal-body p { margin: 0.3rem 0; }
.consent-modal-body ul { margin: 0.3rem 0 0.6rem; padding-left: 1.2rem; }
.consent-modal-body li { margin-bottom: 0.3rem; }
