:root {
    --brand-primary: #2563eb;        /* clean clinical blue — primary actions, links, accents */
    --brand-primary-dark: #1d4ed8;   /* hover / pressed state, dark-on-light text use */
    --brand-primary-darker: #1e3a8a; /* darkest stop for gradients (navbar/auth header) */
    --brand-primary-pale: #eff6ff;   /* pale tint for icon badges, hover backgrounds */
    --brand-accent: #818cf8;         /* secondary indigo stop — brand mark + button glow only,
                                         never body text/links, kept subtle and CRM-appropriate */
    --surface: #f4f6fa;
    --surface-card: #ffffff;
    --surface-sunken: #eef1f6;
    --border-subtle: #e2e6ee;
    --text-ink: #1a2233;
    --text-muted: #64748b;
    --text-faint: #94a3b8;
    --text-on-brand: #ffffff;        /* white text on a solid/gradient brand-blue fill */
    --font-sans: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
    --radius-lg: 16px;
    --radius-md: 12px;
    --radius-sm: 8px;
    /* Shared elevation scale (2026 refresh) — a soft ambient shadow paired
       with a tighter contact shadow reads as "premium" rather than the
       previous single flat drop-shadow; used consistently across cards,
       stat tiles, the nav-on-scroll state, and dropdowns so the whole app
       shares one depth language instead of each component inventing its
       own shadow values. */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05);
    --shadow-md: 0 1px 2px rgba(15, 23, 42, 0.04), 0 10px 28px -8px rgba(15, 23, 42, 0.10);
    --shadow-lg: 0 20px 45px -20px rgba(15, 23, 42, 0.22);
}

/* ---------------------------------------------------------------------
   Typography — one family (Inter), a real weight/size hierarchy instead
   of relying on Bootstrap's default h1-h6, and tighter letter-spacing on
   headings so the UI reads as a designed product rather than a form dump.
   --------------------------------------------------------------------- */
html {
    -webkit-text-size-adjust: 100%;
}

body {
    background: var(--surface);
    font-family: var(--font-sans);
    color: var(--text-ink);
    font-size: 0.9375rem;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans);
    font-weight: 700;
    letter-spacing: -0.015em;
    color: var(--text-ink);
}

h3 { font-size: 1.4rem; }
h3.mb-0 { line-height: 1.3; }

.text-muted {
    color: var(--text-muted) !important;
}

/* Page-level intro paragraph under an h3 title — a touch larger than body
   copy and always given breathing room before the content below it. */
p.text-muted {
    font-size: 0.9rem;
    max-width: 68ch;
    margin-bottom: 1.25rem !important;
}

label, .form-label {
    font-weight: 600;
    font-size: 0.83rem;
    color: #374151;
    letter-spacing: 0.005em;
}

small, .small {
    font-size: 0.8125rem;
}

.fw-semibold { font-weight: 600 !important; }

.card-header.fw-semibold {
    font-size: 0.95rem;
    letter-spacing: -0.005em;
}

/* ---------------------------------------------------------------------
   Layout — cap the content width on large screens so cards don't float
   in a sea of empty whitespace, while staying fully fluid (and padded
   sensibly) down to phone widths.
   --------------------------------------------------------------------- */
.app-navbar .container-fluid,
.app-main > .container-fluid {
    max-width: 1360px;
    margin-inline: auto;
}

.app-main {
    min-height: calc(100vh - 130px);
    /* Split into two animations deliberately — do not merge back into a
       single "app-main-fade-in" animation with transform: translateY(...).
       A fill-mode of "both" makes the LAST keyframe's computed styles
       persist forever after the animation ends, and any non-"none"
       transform on an ancestor creates a new containing block for
       position:fixed descendants (CSS spec). Any Bootstrap modal whose
       markup lives inside .app-main (the normal case for inline
       view-level modals, e.g. #holidayConfirmModal in doctor_schedule.php)
       would then be positioned relative to THIS element's box instead of
       the real viewport and render far off-screen — display:block, class
       "modal fade show", everything JS-wise correct, but invisible. Only
       the opacity animation keeps fill-mode "both" (harmless, opacity
       never affects containing blocks); the transform animation uses the
       default fill-mode so it reverts to no-transform once the 0.35s
       fade-in completes, well before any user interaction could open a
       modal. */
    animation: app-main-fade-in-opacity 0.35s ease both,
               app-main-fade-in-transform 0.35s ease;
}
@keyframes app-main-fade-in-opacity {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes app-main-fade-in-transform {
    from { transform: translateY(6px); }
    to { transform: translateY(0); }
}
/* Pure CSS, no JS involved, so this is safe to disable outright for
   reduced-motion visitors rather than needing a script-side check. */
@media (prefers-reduced-motion: reduce) {
    .app-main { animation: none; }
}

.app-main > .container-fluid {
    padding-left: 1.5rem;
    padding-right: 1.5rem;
}

@media (max-width: 575.98px) {
    .app-main > .container-fluid,
    .app-navbar .container-fluid {
        padding-left: 0.9rem;
        padding-right: 0.9rem;
    }
    h3 { font-size: 1.2rem; }
    .stat-tile { padding: 1rem; }
    .stat-tile .stat-value { font-size: 1.4rem; }
}

.app-footer {
    background: transparent;
    color: var(--text-faint);
}

/* Clean white nav bar with a crisp bottom border and a soft shadow —
   replaces the previous dark neon-glow bar, which read as loud/gamey
   rather than clinical. Brand name and icon carry the accent color;
   everything else stays neutral ink/gray for a calm, professional top
   bar, matching the light "SaaS dashboard" look used across the app.
   Sticky + a scroll-triggered shadow (".is-scrolled", toggled by a tiny
   scroll listener in footer.php) is the one piece of "premium" motion
   borrowed from the landing-page refresh that still reads as a CRM —
   no floating pill, no blur, no shrink, just staying in place and
   gaining depth once the page is no longer at the very top. */
.app-navbar {
    background: var(--surface-card);
    border-bottom: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 0;
    z-index: 1030;
    transition: box-shadow 0.2s ease;
}
.app-navbar.is-scrolled {
    box-shadow: var(--shadow-md);
}

.navbar-brand {
    color: var(--brand-primary-dark) !important;
    font-weight: 700;
    letter-spacing: -0.01em;
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
}

/* Icon badge wrapper around the brand mark (bi-heart-pulse-fill), same
   gradient-chip idea as the landing page's .landing-brand-mark but sized
   and toned down for a dense nav bar rather than a hero section. */
/* Clinic name is admin-editable free text (settings.clinic_name) — the
   exact same overflow risk fixed on the landing page's floating nav
   applies here (toggler button + long name in a narrow flex row on a
   phone). Ellipsis-truncate first; below 400px hide the text and keep
   just the icon mark, since the name is still shown on every dashboard
   page's own heading. */
.navbar-brand-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 46vw;
}
/* Previously hidden outright below 400px (reasoning: the icon mark alone
   is enough, and the clinic name is shown again on every dashboard page's
   own heading) — but that leaves the top bar on a phone with no brand
   name at all, which reads as broken rather than intentional on pages
   that DON'T repeat the name in a heading (most of them). Shrinking the
   text and giving it less room instead keeps the name visible everywhere,
   at the cost of possibly truncating a very long clinic name harder than
   on desktop — an acceptable trade, and still ellipsis-truncated, never
   overflowing/wrapping the nav row. */
@media (max-width: 399.98px) {
    .navbar-brand-text {
        max-width: 34vw;
        font-size: 0.92rem;
    }
    .navbar-brand-mark {
        width: 28px;
        height: 28px;
        font-size: 0.9rem;
    }
}

.navbar-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-accent) 100%);
    color: #fff;
    font-size: 1.05rem;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(37, 99, 235, 0.28);
}

.role-badge {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.68rem;
    font-weight: 700;
    padding: 0.4em 0.65em;
}
.role-patient  { background: #dbeafe; color: #1e40af; }
.role-counter  { background: #fef3c7; color: #92400e; }
.role-doctor   { background: #dcfce7; color: #166534; }
.role-pharmacy { background: #ede9fe; color: #5b21b6; }
.role-admin    { background: #fee2e2; color: #991b1b; }

/* ---------------------------------------------------------------------
   Avatar — circular profile photo (or a blank placeholder person icon
   when no photo has been uploaded yet, the common case for every account
   until someone sets one). See user_avatar_html() in app/Helpers/
   functions.php; used first in the nav header's account chip below.
   --------------------------------------------------------------------- */
.avatar-circle {
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}
.avatar-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--surface-sunken, #eef1f6);
    color: var(--text-faint, #94a3b8);
    border: 1px solid var(--border-subtle);
}

/* Account chip — the logged-in user's identity in the top nav (both the
   desktop row and the collapsed mobile panel share this exact markup, see
   header.php's nav-account-row). Previously just a role badge followed by
   a plain gray line of text with no visual hierarchy and no photo, which
   read as leftover debug text rather than "this is you" — this groups
   avatar + name + role into one recognizable identity block. */
.nav-account-row {
    display: flex;
}
.nav-account-chip {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.3rem 0.6rem 0.3rem 0.3rem;
    border-radius: 999px;
    background: var(--surface-sunken, #f1f4f9);
    max-width: 100%;
}
.nav-account-info {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
    line-height: 1.15;
}
.nav-account-name {
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--text-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 42vw;
}
.nav-account-info .role-badge {
    align-self: flex-start;
}
@media (min-width: 992px) {
    .nav-account-name { max-width: 220px; }
}

.auth-shell {
    min-height: calc(100vh - 130px);
    display: flex;
    align-items: center;
    justify-content: center;
}

.auth-card {
    max-width: 440px;
    width: 100%;
    border: none;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

.auth-card .card-header {
    background: linear-gradient(135deg, var(--brand-primary-darker), var(--brand-primary-dark));
    color: #fff;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0 !important;
    padding: 1.5rem;
    position: relative;
}
.auth-card .card-header i,
.auth-card .card-header h1,
.auth-card .card-header h2,
.auth-card .card-header h3,
.auth-card .card-header h4,
.auth-card .card-header h5,
.auth-card .card-header h6 {
    color: #fff;
}

.otp-input {
    letter-spacing: 0.6em;
    font-weight: 600;
}

/* Live password strength meter + rule chips — see
   public/assets/js/password-strength.js, which builds this markup and
   owns all pass/fail state; everything here is purely presentation. */
.password-checklist {
    margin-top: 0.65rem;
}

/* The 4-segment strength bar sits above the rule chips — a single glance
   answer ("how good is this password") before reading the individual
   rules below it, the same combo most modern sign-up forms use rather
   than a checklist alone. Empty input = no color, nothing claimed yet. */
.pw-strength-meter {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.55rem;
}
.pw-strength-track {
    display: flex;
    gap: 0.28rem;
    flex: 1 1 auto;
}
.pw-strength-seg {
    height: 5px;
    flex: 1 1 0;
    border-radius: 999px;
    background: var(--border-subtle);
    transition: background 0.2s ease;
}
.pw-strength-track.is-weak .pw-strength-seg.is-filled { background: #dc2626; }
.pw-strength-track.is-fair .pw-strength-seg.is-filled { background: #d97706; }
.pw-strength-track.is-good .pw-strength-seg.is-filled { background: #2563eb; }
.pw-strength-track.is-strong .pw-strength-seg.is-filled { background: #0f9d63; }
.pw-strength-label {
    font-size: 0.75rem;
    font-weight: 700;
    white-space: nowrap;
    min-width: 4.2em;
    text-align: right;
}
.pw-strength-label.is-weak { color: #dc2626; }
.pw-strength-label.is-fair { color: #d97706; }
.pw-strength-label.is-good { color: #2563eb; }
.pw-strength-label.is-strong { color: #0f9d63; }

/* Each rule as a small pill/chip — matches the visual language of the
   username-suggestion chips right above this same form (register.php) —
   rather than bare inline text, so a scan of 5 short phrases reads as 5
   distinct at-a-glance states instead of one run-on sentence. */
.pw-rule-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.4rem;
}
@media (max-width: 420px) {
    .pw-rule-grid {
        grid-template-columns: 1fr;
    }
}
.pw-rule {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.6rem;
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    font-size: 0.76rem;
    font-weight: 500;
    color: var(--text-faint);
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.pw-rule-icon {
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.65rem;
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    color: var(--text-faint);
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.pw-rule.is-valid {
    background: #ecfdf5;
    border-color: #b7e4cd;
    color: #0f9d63;
}
.pw-rule.is-valid .pw-rule-icon {
    background: #0f9d63;
    border-color: #0f9d63;
    color: #fff;
}
.pw-rule.is-invalid {
    background: #fef2f2;
    border-color: #f7c5c5;
    color: #b42318;
}
.pw-rule.is-invalid .pw-rule-icon {
    background: #fff;
    border-color: #f3a9a9;
    color: #dc2626;
}

/* Registration's "suggested for you" username chips (register.php) — two
   candidates generated from the name/email already typed, so picking a
   username can be a tap instead of a think. Reuses the app's existing
   badge/pill visual language rather than inventing a new component. */
.username-suggestions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
}
.username-suggestion-chip {
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    padding: 0.3rem 0.85rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-ink);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.username-suggestion-chip:hover {
    background: var(--brand-primary-pale);
    border-color: var(--brand-primary);
    color: var(--brand-primary-dark);
}
/* Live "is this taken?" readout under the username field, updated as the
   person types or after tapping a suggestion chip above. */
.username-availability {
    min-height: 1.1rem;
    font-size: 0.8rem;
    margin-top: 0.35rem;
}

.btn {
    font-weight: 600;
    font-size: 0.875rem;
    letter-spacing: 0.002em;
}

.btn-brand {
    position: relative;
    overflow: hidden;
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: var(--text-on-brand);
    font-weight: 600;
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.btn-brand:hover, .btn-brand:focus {
    background: var(--brand-primary-dark);
    border-color: var(--brand-primary-dark);
    color: var(--text-on-brand);
    transform: translateY(-1px);
}
.btn-brand:disabled {
    transform: none;
}
/* Pointer-tracked glow, same technique as the landing page's CTA buttons
   (--mx/--my updated on mousemove by a shared script in footer.php) —
   with no JS or on touch devices the glow simply never appears, so this
   is a pure hover-only enhancement, never load-bearing. Kept subtle
   (low opacity, brand-accent tint) so it reads as a refined button, not
   a marketing effect, on a form full of them. */
.btn-brand::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(180px circle at var(--mx, 50%) var(--my, 50%), rgba(255, 255, 255, 0.28), transparent 70%);
    opacity: 0;
    transition: opacity 0.2s ease;
    pointer-events: none;
}
.btn-brand:hover::before {
    opacity: 1;
}
@media (hover: none) {
    .btn-brand::before { display: none; }
}

.btn-outline-brand {
    background: transparent;
    border-color: var(--brand-primary);
    color: var(--brand-primary-dark);
    font-weight: 600;
}
.btn-outline-brand:hover, .btn-outline-brand:focus {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: var(--text-on-brand);
}

.form-control, .form-select {
    font-size: 0.9rem;
}

.form-control:focus, .form-select:focus {
    border-color: var(--brand-primary);
    box-shadow: 0 0 0 0.2rem var(--brand-primary-pale);
}

.form-text {
    font-size: 0.8rem;
}

.dashboard-card {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    background: var(--surface-card);
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}
/* A slight lift on hover for pointer users only — media(hover) keeps this
   from sticking "on" after a tap on touch devices, where :hover has no
   real off-state. */
@media (hover: hover) {
    .dashboard-card:hover {
        box-shadow: var(--shadow-lg);
        transform: translateY(-2px);
    }
}

.dashboard-card .card-header {
    border-bottom: 1px solid var(--border-subtle);
    padding: 0.9rem 1.25rem;
}

.dashboard-card .card-body {
    padding: 1.25rem;
}

@media (max-width: 575.98px) {
    .dashboard-card .card-body,
    .dashboard-card .card-header {
        padding: 0.9rem;
    }
}

.stat-tile {
    border-radius: var(--radius-md);
    padding: 1.25rem;
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
    border-top: 3px solid var(--brand-primary);
    height: 100%;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}
@media (hover: hover) {
    .stat-tile:hover {
        box-shadow: var(--shadow-md);
        transform: translateY(-2px);
    }
}

.stat-tile .stat-value {
    font-size: 1.75rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--brand-primary-dark);
    line-height: 1.15;
}

.stat-tile .stat-label {
    color: var(--text-muted);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 0.2rem;
}

/* .stat-tile already had a :hover lift (above) with no way to trigger it —
   the dashboard's 4 top tiles were plain <div>s, not links, despite
   looking exactly like the chart's already-clickable bars right below
   them. a.stat-tile makes the whole tile a real link (to that count's
   filtered list) while keeping every existing visual rule, which target
   .stat-tile regardless of tag. */
a.stat-tile {
    display: block;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}
a.stat-tile:hover .stat-value {
    color: var(--brand-primary);
}
a.stat-tile .stat-label {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}
a.stat-tile .stat-label::after {
    content: "\f285"; /* bootstrap-icons "chevron-right" */
    font-family: "bootstrap-icons" !important;
    font-weight: 400;
    font-size: 0.7rem;
    opacity: 0.45;
    margin-left: auto;
}

/* ---------------------------------------------------------------------
   Overview stat tiles with a top-corner icon badge (Reception dashboard's
   "at a glance" cards) — purely additive markup on top of .stat-tile, so
   every other page using the plain tile is unaffected.
   --------------------------------------------------------------------- */
.stat-tile-flex {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
}
/* Flex items default to min-width:auto (their content's min-content size),
   which for a single-word label like "Emergency" is that whole word's
   width — on a narrow col-6 tile (two tiles per row on a phone) that is
   wider than the space left after the fixed 40px icon badge, so the text
   refused to shrink and pushed the badge past the tile's own right edge.
   min-width:0 lets the flex item shrink and wrap normally; overflow-wrap
   is the last-resort backstop for a single word still too wide to fit
   even on its own line. Same overflow bug class as the landing page's
   .row.g-5 fix, different mechanism (flexbox min-content, not a grid
   gutter) — found by the same real-device-width sweep. */
.stat-tile-flex > div:first-child {
    min-width: 0;
    overflow-wrap: break-word;
}

/* Below 576px the tile itself shrinks to ~half the phone's width (two
   .col-6 tiles per row), which only leaves ~36px beside a 40px badge for
   text — even after the min-width:0 fix above, a single word like
   "Emergency" or "Partially" still can't wrap into that, so it fell back
   to character-by-character breaking ("PART / IALL / Y"), which is
   readable but looks broken, not premium. Floating the badge in the
   tile's top-right corner instead frees the tile's near-full width for
   the label, so it wraps at word boundaries like normal text again. */
@media (max-width: 575.98px) {
    /* Stack instead of side-by-side: the badge becomes its own row,
       pinned to the tile's right edge (order:-1 + align-self:flex-end
       moves it above the text purely via flexbox, no markup change
       needed even though the badge is the *second* element in the
       HTML), then the label/value row below gets the tile's full
       content width to wrap normally at word boundaries. A same-row
       layout only had ~36px left beside a 40px badge on a 320px phone's
       two-per-row tiles — not enough for any real word, hence the ugly
       "PARTI / ALLY" character-level breaks a same-row fix couldn't
       avoid. */
    .stat-tile-flex {
        display: flex;
        flex-direction: column;
    }
    .stat-tile-flex .stat-icon-badge {
        order: -1;
        align-self: flex-end;
        width: 30px;
        height: 30px;
        font-size: 0.95rem;
        margin-bottom: 0.5rem;
    }
}
.stat-icon-badge {
    width: 40px;
    height: 40px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    flex-shrink: 0;
    background: var(--brand-primary-pale);
    color: var(--brand-primary-dark);
}

/* ---------------------------------------------------------------------
   Semantic tile colors — a stat tile's meaning (a queue count, a revenue
   figure, a warning, a completed total) is not "blue" by default; giving
   each family its own icon-badge tint + matching top accent is what reads
   as a considered, professional dashboard (the fintech/portal references
   the client sent) instead of one repeated brand-blue block wherever a
   number appears. Purely additive modifier classes on top of the existing
   .stat-tile / .stat-icon-badge rules — nothing here changes their layout,
   only color, so every current tile keeps working unmodified.
   --------------------------------------------------------------------- */
.stat-icon-badge.badge-green { background: #ecfdf5; color: #0f9d63; }
.stat-icon-badge.badge-amber { background: #fffbeb; color: #b7791f; }
.stat-icon-badge.badge-red { background: #fef2f2; color: #dc2626; }
.stat-icon-badge.badge-indigo { background: #eef2ff; color: #4f46e5; }
.stat-icon-badge.badge-teal { background: #f0fdfa; color: #0d9488; }

.stat-tile.accent-green { border-top-color: #0f9d63; }
.stat-tile.accent-amber { border-top-color: #b7791f; }
.stat-tile.accent-red { border-top-color: #dc2626; }
.stat-tile.accent-indigo { border-top-color: #4f46e5; }
.stat-tile.accent-teal { border-top-color: #0d9488; }
.stat-tile-hero {
    background: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-primary-darker) 100%);
    border-top-color: transparent;
    box-shadow: 0 4px 16px rgba(37, 99, 235, 0.22);
}
@media (hover: hover) {
    .stat-tile-hero:hover {
        box-shadow: 0 8px 28px rgba(37, 99, 235, 0.3);
    }
}
.stat-tile-hero .stat-value,
.stat-tile-hero .stat-label {
    color: var(--text-on-brand);
}
.stat-tile-hero .stat-label { opacity: 0.85; }
.stat-tile-hero .stat-icon-badge {
    background: rgba(255, 255, 255, 0.18);
    color: var(--text-on-brand);
}

/* Single 100%-stacked horizontal bar showing today's patient journey
   (not checked in -> waiting for doctor -> seen) as proportions of one
   whole, rather than three separate bars — the point is "where is
   everyone right now", read as one shape. */
.journey-bar {
    display: flex;
    height: 28px;
    border-radius: 999px;
    overflow: hidden;
    background: var(--surface-sunken);
}
.journey-bar-segment {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    transition: width 0.3s ease;
}

.empty-state {
    text-align: center;
    padding: 3rem 1rem;
    color: var(--text-muted);
}
.empty-state i {
    font-size: 2.5rem;
    color: #cbd5e1;
}

/* ---------------------------------------------------------------------
   Charts — shared chrome around the hand-built SVG/HTML charts so every
   one gets the same card treatment: a headline number next to the title,
   consistent axis/gridline color, and generous internal padding instead
   of a few thin bars lost in a large blank card.
   --------------------------------------------------------------------- */
.chart-card .card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.chart-headline {
    font-size: 1.15rem;
    font-weight: 800;
    color: var(--text-ink);
    letter-spacing: -0.02em;
}
.chart-gridline { stroke: #e2e6ee; stroke-width: 1; }
.chart-axis-label { fill: var(--text-faint); font-family: var(--font-sans); }
.chart-value-label { fill: var(--text-ink); font-family: var(--font-sans); font-weight: 700; }
.chart-cat-label { fill: #475569; font-family: var(--font-sans); font-weight: 600; }
/* An <a> wrapping <text> inside SVG gets the browser's default link
   underline just like an HTML anchor — strip it so bar/category labels
   read as labels, not links, while the bar itself stays clickable. */
.chart-card svg a, .chart-card svg a text { text-decoration: none; }

/* -----------------------------------------------------------------
   Line/area charts (see charts.js) — crosshair + floating tooltip on
   hover/tap. The crosshair and hover-dot live IN the SVG (so they scale
   and position exactly like the data they're pointing at); the tooltip
   is a plain HTML div layered on top via absolute positioning, because
   readable multi-line text with its own background/shadow is much
   simpler as HTML than as SVG <text>/<rect>. */
.chart-line-wrap { touch-action: pan-y; }
.chart-hit-area { cursor: crosshair; }
.chart-crosshair { transition: opacity 0.1s ease; }
.chart-hover-dot { transition: opacity 0.1s ease; }
.chart-tooltip {
    position: absolute;
    pointer-events: none;
    background: var(--text-ink);
    color: #fff;
    padding: 0.4rem 0.65rem;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    font-size: 0.78rem;
    line-height: 1.35;
    white-space: nowrap;
    transition: opacity 0.1s ease;
    z-index: 3;
}
.chart-tooltip-date { color: #cbd5e1; font-size: 0.72rem; }
.chart-tooltip-value { font-weight: 700; font-size: 0.88rem; }

/* Shared by the charts' own hint AND any wide data table (see
   .table-scroll-hint below) — same idea in both places: a bare
   .table-responsive on a phone has no visual cue that there's more to see
   sideways, so a column like Role/Email/Status on a wide table (or a bar
   past the edge of a chart) can look like it's simply been cut off/broken
   rather than one swipe away. */
.chart-scroll-hint,
.table-scroll-hint {
    display: none;
    align-items: center;
    gap: 0.35rem;
    color: var(--text-faint);
    margin-bottom: 0.5rem;
}
@media (max-width: 767.98px) {
    .chart-scroll-hint { display: flex; }
}
@media (max-width: 991.98px) {
    .table-scroll-hint { display: flex; }
}

.role-pick-card {
    border-color: var(--border-subtle) !important;
    border-radius: var(--radius-md);
    transition: border-color 0.15s ease, background 0.15s ease;
}
.role-pick-card:hover { border-color: var(--brand-primary) !important; background: var(--brand-primary-pale); }
.role-pick-card:has(input:checked) { border-color: var(--brand-primary) !important; background: var(--brand-primary-pale); }

/* Account-summary sidebar on the staff create/edit form. */
.account-info-card .info-row {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--border-subtle);
    font-size: 0.85rem;
}
.account-info-card .info-row:last-child { border-bottom: none; }
.account-info-card .info-label { color: var(--text-muted); font-weight: 600; }
.account-info-card .info-value { font-weight: 600; text-align: right; color: var(--text-ink); }

a {
    color: var(--brand-primary-dark);
}
a:hover {
    color: var(--brand-primary);
}

/* ---------------------------------------------------------------------
   Notification bell — nav-item dropdown shared by every role (header.php).
   --------------------------------------------------------------------- */
.notif-bell-btn {
    position: relative;
    border: none;
    background: transparent;
    color: var(--text-ink);
    font-size: 1.25rem;
    padding: 0.35rem 0.55rem;
    line-height: 1;
}
.notif-bell-btn:hover { color: var(--brand-primary); }
.notif-bell-dot {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 16px;
    height: 16px;
    padding: 0 3px;
    border-radius: 999px;
    background: #dc3545;
    color: #fff;
    font-size: 0.62rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    border: 2px solid var(--surface-card);
}
.notif-dropdown-menu {
    width: 340px;
    max-width: 90vw;
    padding: 0;
    max-height: 420px;
    overflow-y: auto;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    border-color: var(--border-subtle);
}
.notif-dropdown-menu .dropdown-item-text {
    white-space: normal;
}
.notif-item-unread { background: var(--brand-primary-pale); }

/* Below the lg breakpoint the navbar collapses into a plain vertical list
   (Bootstrap's own default — no gap, no dividers), which used to leave the
   notification bell, the role badge, the user's name and Logout stacked
   flush against each other with nothing to separate them. This gives the
   whole collapsed list breathing room plus a light divider under the
   account row so it reads as one organized section instead of an unspaced
   pile. Scoped to <lg only — the existing desktop row is untouched. */
@media (max-width: 991.98px) {
    .navbar-collapse .navbar-nav {
        padding-top: 0.65rem;
        padding-bottom: 0.35rem;
        border-top: 1px solid var(--border-subtle);
        margin-top: 0.5rem;
        row-gap: 0.6rem;
    }
    .nav-account-row {
        padding-bottom: 0.6rem;
        border-bottom: 1px solid var(--border-subtle);
    }
}

/* Mobile Menu panel (<lg only — see header.php's d-lg-none block). A
   floating dropdown-menu is the wrong pattern once its content is a long,
   per-role module list on a narrow screen: it overlaps whatever's below it
   instead of pushing it down, and reads as a stray overlay rather than a
   real piece of navigation. This is a plain Bootstrap `collapse` instead —
   same in-flow expand/collapse mechanic the navbar-toggler itself already
   uses — styled as one clearly-labeled, full-width toggle button that
   opens into a bordered card of full-width tappable rows (icon + name),
   each a real 44px-tall touch target, not a cramped icon-only list. */
.mobile-menu-toggle {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: var(--surface-sunken);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 0.65rem 0.9rem;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text-ink);
}
.mobile-menu-toggle:hover,
.mobile-menu-toggle:focus {
    color: var(--brand-primary-dark);
}
.mobile-menu-chevron {
    font-size: 0.75rem;
    color: var(--text-faint);
    transition: transform 0.2s ease;
}
.mobile-menu-toggle[aria-expanded="true"] .mobile-menu-chevron {
    transform: rotate(180deg);
}
.mobile-menu-panel {
    margin-top: 0.5rem;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--surface-card);
    max-height: 60vh;
    overflow-y: auto;
}
.mobile-menu-link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 0.9rem;
    min-height: 44px;
    color: var(--text-ink);
    text-decoration: none;
    font-size: 0.88rem;
    font-weight: 500;
    border-bottom: 1px solid var(--border-subtle);
}
.mobile-menu-link:last-child { border-bottom: none; }
.mobile-menu-link i {
    color: var(--brand-primary);
    font-size: 1rem;
    width: 1.25rem;
    text-align: center;
    flex-shrink: 0;
}
.mobile-menu-link:hover,
.mobile-menu-link:active {
    background: var(--brand-primary-pale);
    color: var(--brand-primary-dark);
}

/* Roles & Permissions matrix (Admin → Roles): below md, the mobile role
   picker (roles.php) toggles this class on non-selected role columns via a
   small inline script, filtering the same 5-role table down to one role at
   a time so managing permissions from a phone doesn't mean scrolling back
   and forth per checkbox. Scoped to the mobile breakpoint so a stale class
   left over after resizing back to a desktop width never hides a column
   there — the desktop table always shows every role regardless. */
@media (max-width: 767.98px) {
    .roles-matrix .col-hidden-mobile {
        display: none;
    }
}

/* ---------------------------------------------------------------------
   Global page-loading overlay ("stethoscope loader")
   Shown by public/assets/js/loader.js on real page navigations (link
   clicks, non-AJAX form submits) and explicitly around slow AJAX calls
   (booking/reschedule confirm, online payment) so the user always has a
   visible "please wait" instead of a frozen-looking page. Markup lives
   once in header.php; toggled purely via the .is-visible class so it
   never needs to be re-inserted per page.
   --------------------------------------------------------------------- */
.clinic-loader-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(244, 246, 250, 0.86);
    backdrop-filter: blur(2px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s ease;
}
.clinic-loader-overlay.is-visible {
    opacity: 1;
    visibility: visible;
}
.clinic-loader-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.9rem;
    padding: 1.75rem 2.25rem;
    background: var(--surface-card);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
.clinic-loader-scope {
    width: 64px;
    height: 64px;
    color: var(--brand-primary);
}
/* Earpieces + tube stay static; only the chest-piece disc "listens" —
   a soft pulse ring plus a gentle bob, echoing a heartbeat rather than a
   generic spinner. */
.clinic-loader-scope .cl-pulse-ring {
    transform-origin: 32px 46px;
    animation: clLoaderPulse 1.15s ease-out infinite;
}
.clinic-loader-scope .cl-chestpiece {
    transform-origin: 32px 46px;
    animation: clLoaderBob 1.15s ease-in-out infinite;
}
@keyframes clLoaderPulse {
    0%   { transform: scale(0.55); opacity: 0.9; }
    70%  { transform: scale(1.35); opacity: 0; }
    100% { transform: scale(1.35); opacity: 0; }
}
@keyframes clLoaderBob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-2px); }
}
.clinic-loader-text {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-muted);
    letter-spacing: 0.01em;
}
@media (prefers-reduced-motion: reduce) {
    .clinic-loader-scope .cl-pulse-ring,
    .clinic-loader-scope .cl-chestpiece {
        animation: none;
    }
}

/* ---------------------------------------------------------------------
   Booking page — "how will you pay" picker. Replaces a plain Bootstrap
   btn-group (two toggle buttons that only differed by Bootstrap's default
   checked-outline-button fill — a dull gray with nothing that read as
   "this one is selected" at a glance, and both icons at the same visual
   weight). Two full selectable cards instead: icon, title, one-line
   description, and an explicit checkmark badge that only appears on the
   selected card, with a brand-colored border + tinted background as the
   second, redundant "this is the one" signal — never color alone.
   --------------------------------------------------------------------- */
.payment-method-picker {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
}
@media (max-width: 575.98px) {
    .payment-method-picker { grid-template-columns: 1fr; }
}
.payment-method-card {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.9rem 1rem;
    border: 2px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-card);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.payment-method-card:hover { border-color: #c7d2e5; }
.payment-method-card input[type="radio"] {
    /* Visually hidden but still a real, keyboard/focus-visible, screen
       reader-reachable input — the card's own :focus-within ring stands
       in for its native outline. */
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}
.payment-method-card:focus-within {
    box-shadow: 0 0 0 3px var(--brand-primary-pale);
}
.payment-method-card.selected {
    border-color: var(--brand-primary);
    background: var(--brand-primary-pale);
}
.payment-method-icon {
    width: 38px;
    height: 38px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.05rem;
    flex-shrink: 0;
    background: var(--surface-sunken);
    color: var(--text-muted);
}
.payment-method-card.selected .payment-method-icon {
    background: var(--brand-primary);
    color: #fff;
}
.payment-method-body { min-width: 0; flex: 1; }
.payment-method-title {
    display: block;
    font-weight: 700;
    color: var(--text-ink);
    font-size: 0.95rem;
}
.payment-method-desc {
    display: block;
    color: var(--text-muted);
    font-size: 0.78rem;
    margin-top: 0.1rem;
    line-height: 1.4;
}
.payment-method-check {
    position: absolute;
    top: 0.7rem;
    right: 0.7rem;
    color: var(--brand-primary);
    font-size: 1.05rem;
    opacity: 0;
    transform: scale(0.7);
    transition: opacity 0.15s ease, transform 0.15s ease;
}
.payment-method-card.selected .payment-method-check { opacity: 1; transform: scale(1); }

/* ---------------------------------------------------------------------
   Booking confirmation "ticket" — the on-page result of a successful
   booking (paid online, pay-at-counter reservation, or a free follow-up),
   styled like an actual event/travel ticket: a brand-gradient stub with
   the essential at-a-glance facts (doctor, date, time), a dashed tear-line
   with cut-out notches, and the token number as the big "boarding pass"
   figure below it. Deliberately shown in place on the booking page itself
   rather than only ever redirecting away, so a successful payment ends on
   something that visually confirms "you're done" instead of just moving on.
   --------------------------------------------------------------------- */
.booking-ticket {
    max-width: 480px;
    margin: 0 auto;
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-lg);
    background: var(--surface-card);
}
/* The brief in-between state while we wait for the payment webhook to
   confirm a Razorpay success client-side reported — see book.php's
   renderConfirmingPayment(). Same card footprint as the ticket it's about
   to be replaced by, so nothing jumps around when the real ticket lands. */
.booking-ticket-confirming {
    max-width: 480px;
    margin: 0 auto;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
}
/* Reminds the patient which date/time they're booking. Sticky (pinned just
   below the app's own sticky navbar, 61px tall) rather than just sitting
   at the top of the confirm form — a plain non-sticky placement would
   scroll out of view the moment Symptoms/Vitals/Payment are reached,
   which is exactly the "what time is this" confusion reported: the
   payment-method cards are far enough down the form that the date/time
   picked above had already scrolled off screen by then. See book.php's
   selectedSlotSummary. Empty (and so invisible, no empty box) until a
   slot is actually held. */
.selected-slot-summary:not(:empty) {
    background: var(--brand-primary-pale);
    color: var(--brand-primary-dark);
    border: 1px solid var(--brand-primary-pale);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
    font-size: 0.9rem;
    position: sticky;
    top: 4.25rem;
    z-index: 5;
}
.booking-ticket-stub {
    background: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-primary-darker) 100%);
    color: #fff;
    padding: 1.5rem 1.5rem 1.75rem;
    text-align: center;
}
.booking-ticket-check {
    width: 52px;
    height: 52px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.18);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.6rem;
    margin: 0 auto 0.75rem;
}
.booking-ticket-status {
    font-weight: 800;
    font-size: 1.15rem;
    letter-spacing: -0.01em;
}
.booking-ticket-sub {
    opacity: 0.9;
    font-size: 0.85rem;
    margin-top: 0.15rem;
}
.booking-ticket-tear {
    position: relative;
    height: 0;
    border-top: 2px dashed rgba(148, 163, 184, 0.5);
    margin: 0 1.25rem;
}
.booking-ticket-tear::before,
.booking-ticket-tear::after {
    content: "";
    position: absolute;
    top: -11px;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    background: var(--surface);
}
.booking-ticket-tear::before { left: -1.5625rem; }
.booking-ticket-tear::after { right: -1.5625rem; }
.booking-ticket-body { padding: 1.5rem; }
.booking-ticket-token {
    text-align: center;
    margin-bottom: 1.25rem;
}
.booking-ticket-token .label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-faint);
}
.booking-ticket-token .value {
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: 0.03em;
    color: var(--text-ink);
    font-family: "SFMono-Regular", Consolas, monospace;
}
.booking-ticket-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem 1.25rem;
}
.booking-ticket-grid .item .label {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    margin-bottom: 0.15rem;
}
.booking-ticket-grid .item .value {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text-ink);
}
.booking-ticket-actions {
    padding: 0 1.5rem 1.5rem;
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.booking-ticket-actions .btn { flex: 1; }

/* Payment-failed fallback card — same rounded/shadow language as the
   ticket above but amber-toned (a decline/timeout is not an error state,
   it's just "didn't finish", so this stays calm rather than alarming red)
   with its two recovery actions front and center. */
.booking-payment-issue {
    max-width: 480px;
    margin: 0 auto;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    background: var(--surface-card);
    border: 1px solid #fde68a;
    overflow: hidden;
}
.booking-payment-issue-head {
    background: #fffbeb;
    padding: 1.25rem 1.5rem;
    text-align: center;
    border-bottom: 1px solid #fde68a;
}
.booking-payment-issue-icon {
    width: 46px;
    height: 46px;
    border-radius: 999px;
    background: #fef3c7;
    color: #b7791f;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    margin: 0 auto 0.6rem;
}
.booking-payment-issue-body { padding: 1.25rem 1.5rem; }
.booking-payment-issue-actions {
    padding: 0 1.5rem 1.5rem;
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.booking-payment-issue-actions .btn { flex: 1; }

/* =========================================================================
   Date picker (flatpickr) — restyles the vendored flatpickr calendar
   (public/assets/vendor/flatpickr) so it matches this app's own design
   system instead of shipping the library's stock purple theme. Enhanced
   automatically on every <input type="date"> by assets/js/datepicker.js —
   see that file for the "why global" reasoning. Selectors here target
   flatpickr's own class names (its stylesheet is loaded before this file,
   see header.php, so these rules win on cascade order without !important).
   ------------------------------------------------------------------------- */

/* The visible text field flatpickr shows the user (altInput mode — the
   real <input type="date"> becomes a hidden field holding the raw Y-m-d
   value; this one shows "2 Sep 2026" and is what gets focus/clicks). Reuses
   the same visual language as every other text input in the app. */
.cl-datepicker-alt {
    background: var(--surface-card);
    cursor: pointer;
    padding-right: 2.5rem !important; /* room for the calendar glyph drawn by .cl-datepicker-wrap::after so typed text never runs under it */
}
.cl-datepicker-alt:disabled {
    background: var(--surface-sunken);
    cursor: not-allowed;
}

/* A small calendar glyph inside the field makes it obvious this opens a
   picker rather than accepting free-typed text, without relying on the
   native input's (now-removed) spinner affordance. */
.cl-datepicker-wrap {
    position: relative;
}
.cl-datepicker-wrap::after {
    content: "\f1e8";
    font-family: "bootstrap-icons";
    position: absolute;
    right: 0.9rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-faint);
    pointer-events: none;
    font-size: 0.95rem;
}

/* The floating calendar popup itself. */
.flatpickr-calendar {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    font-family: var(--font-sans);
    padding: 0.75rem;
    width: 300px !important;
}
/* flatpickr's own stylesheet hardcodes the day grid (.flatpickr-days,
   .dayContainer) to a fixed 307.875px, independent of whatever width we
   give .flatpickr-calendar above. Nothing here originally overrode that,
   so at any calendar width narrower than ~308px + our own padding (which
   the mobile rule below was) the grid simply spilled out past the card's
   right edge with no clipping — days in the last one-two columns rendered
   fully outside the visible card, sitting on top of whatever page content
   was underneath, and a real tap on their visible position hit that page
   content instead of the date (confirmed directly: elementFromPoint() at
   a "5" cell's on-screen center returned a div from the page behind it,
   not the day). Forcing every inner layer to 100% of the calendar's own
   (padded) content box makes the day grid always genuinely match
   whatever width .flatpickr-calendar is given, at every breakpoint. */
.flatpickr-innerContainer,
.flatpickr-rContainer,
.flatpickr-days,
.dayContainer {
    width: 100% !important;
    max-width: 100%;
    min-width: 0;
}
.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after {
    display: none; /* the little pointer triangle looks noisy against the card shadow */
}

/* Month header — current month/year label + prev/next arrows. */
.flatpickr-months {
    padding-bottom: 0.5rem;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid var(--border-subtle);
}
.flatpickr-months .flatpickr-month {
    color: var(--text-ink);
    height: 2.25rem;
}
.flatpickr-current-month {
    font-size: 0.95rem;
    font-weight: 600;
    padding-top: 0.35rem;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
    font-weight: 600;
    color: var(--text-ink);
    appearance: none;
    border-radius: var(--radius-sm);
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
    background: var(--surface-sunken);
}
.flatpickr-current-month input.cur-year {
    font-weight: 600;
    color: var(--text-ink);
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
    color: var(--text-muted);
    border-radius: var(--radius-sm);
    top: 0.4rem;
    padding: 0.3rem;
}
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
    background: var(--surface-sunken);
}
.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg {
    fill: currentColor;
}

/* Weekday row. */
.flatpickr-weekdays {
    margin-bottom: 0.15rem;
}
span.flatpickr-weekday {
    color: var(--text-faint);
    font-weight: 600;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: transparent;
}

/* Day cells. */
.flatpickr-day {
    color: var(--text-ink);
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
    font-weight: 500;
}
.flatpickr-day:hover,
.flatpickr-day:focus {
    background: var(--brand-primary-pale);
    border-color: var(--brand-primary-pale);
    color: var(--brand-primary-dark);
}
.flatpickr-day.today {
    border-color: var(--brand-primary);
    font-weight: 700;
}
.flatpickr-day.today:hover {
    background: var(--brand-primary-pale);
    color: var(--brand-primary-dark);
}
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.selected:focus {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: var(--text-on-brand);
    font-weight: 700;
    box-shadow: none;
}
.flatpickr-day.inRange,
.flatpickr-day.inRange:hover {
    background: var(--brand-primary-pale);
    border-color: var(--brand-primary-pale);
    box-shadow: -5px 0 0 var(--brand-primary-pale), 5px 0 0 var(--brand-primary-pale);
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
    color: var(--text-faint);
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
    color: var(--text-faint);
    opacity: 0.45;
    cursor: not-allowed;
    background: transparent;
}
.flatpickr-day.notAllowed,
.flatpickr-day.notAllowed.prevMonthDay,
.flatpickr-day.notAllowed.nextMonthDay {
    color: var(--text-faint);
    opacity: 0.35;
}

/* Mobile — flatpickr already forces disableMobile:true (see datepicker.js),
   so every phone gets this same calendar UI, never a different OS picker.
   300px (not narrower) so each day cell still lands close to flatpickr's
   own ~39px default — comfortably tappable — now that the rule above
   makes the day grid actually track this width instead of silently
   overflowing it. Even on a 320px-wide phone this leaves real margin on
   both sides; flatpickr positions the popup itself and keeps it inside
   the viewport, so this doesn't cause page-level horizontal scroll. */
@media (max-width: 575.98px) {
    .flatpickr-calendar {
        width: 300px !important;
    }
}
