:root {
    --primary: #4a90d9;
    --primary-dark: #2c5282;
    --bg: #f0f4f8;
    --card-bg: #ffffff;
    --text: #2d3748;
    --text-muted: #718096;
    --border: #e2e8f0;
    --green: #48bb78;
    --green-bg: #f0fff4;
    --yellow: #d69e2e;
    --yellow-bg: #fffff0;
    --red: #e53e3e;
    --red-bg: #fff5f5;
    --gray: #a0aec0;
    --gray-bg: #f7fafc;
    --radius: 10px;
    /* Tint applied to tiled background JPGs in dark mode (multiply blend
       so the patterns dim into the dark surface). 0 = no tint. */
    --bg-image-overlay: rgba(0,0,0,0);
    --card-image-overlay: rgba(0,0,0,0);
}

/* Dark theme — applied via :root[data-theme="dark"] when the user has
   explicitly chosen dark, or via the `prefers-color-scheme: dark` media
   query when they're on 'system' (data-theme attribute absent). The
   :root[data-theme="light"] explicit override is the escape hatch when
   a user wants light even though their device is dark. */
:root[data-theme="dark"],
:root:not([data-theme="light"]) {
    /* Default-light values stay above; the media query below promotes
       these to dark when prefers-color-scheme matches. */
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #1a202c;
        --card-bg: #2d3748;
        /* Dark-mode text is light gray (#e2e8f0), not pure white —
           pure white reads as harsh on dark surfaces. Same call on
           --text-muted: a softer gray (#a0aec0) that keeps contrast
           against --text without screaming. */
        --text: #e2e8f0;
        --text-muted: #a0aec0;
        --border: #4a5568;
        --green-bg: #1f3a2c;
        --yellow-bg: #3a2e1c;
        --red-bg: #3a1f1f;
        --gray-bg: #2a3140;
        --bg-image-overlay: rgba(0,0,0,0.85);
        --card-image-overlay: rgba(0,0,0,0.78);
    }
}

/* Explicit dark override — wins regardless of device pref. */
:root[data-theme="dark"] {
    --bg: #1a202c;
    --card-bg: #2d3748;
    --text: #e2e8f0;
    --text-muted: #a0aec0;
    --border: #4a5568;
    --green-bg: #1f3a2c;
    --yellow-bg: #3a2e1c;
    --red-bg: #3a1f1f;
    --gray-bg: #2a3140;
    --bg-image-overlay: rgba(0,0,0,0.85);
    --card-image-overlay: rgba(0,0,0,0.78);
}

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Bundled brand font — Nunito, variable weight axis (200-1000). Single
   .ttf covers every weight Compose / SwiftUI / CSS asks for. font-display:
   swap renders the system stack first, then re-flows when the .ttf lands
   so first-paint isn't blocked. The system stack stays as the fallback so
   anything outside the Nunito glyph set (rare CJK, etc.) stays legible. */
@font-face {
    font-family: 'Nunito';
    /* `format('truetype')` is the cross-browser hint for variable TTFs.
       The spec also defines `format('truetype-variations')` but Chrome
       silently refuses to load the font when that's used — the
       font-weight: 200 1000 range tells the browser this is a variable
       font without needing the more-specific format hint. */
    src: url('/static/fonts/Nunito-VariableFont_wght.ttf') format('truetype');
    font-weight: 200 1000;
    font-style: normal;
    font-display: swap;
}

body {
    font-family: 'Nunito', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    /* Layered background: a flat-color overlay (transparent in light, dark
       wash in dark) sits on top of the tiled JPG, then the solid bg color
       sits below as a fallback. The overlay is what dims the bright
       light-mode background pattern down to something legible in dark
       mode without needing a separate dark-mode tile asset. */
    background-color: var(--bg);
    background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_all.jpg');
    background-repeat: repeat;
    color: var(--text);
    line-height: 1.5;
    overflow-y: scroll;
}
body.filter-medication { background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_med.jpg'); }
body.filter-household { background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_household.jpg'); }
body.filter-all { background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_all.jpg'); }
body.filter-trial { background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_trials.jpg'); }
body.filter-nudge { background-image: linear-gradient(var(--bg-image-overlay), var(--bg-image-overlay)), url('/static/background_nudge.jpg'); }

/* Boring themes — flat color fills, no tiled background images on body
   or cards. Set by base.html's bootstrap script when the user picks
   "Boring light" or "Boring dark" in Settings. The data-theme attribute
   still gets the underlying light/dark value so all the existing color
   variables apply unchanged; data-flat just kills the imagery layer. */
:root[data-flat="true"] body,
:root[data-flat="true"] body.filter-medication,
:root[data-flat="true"] body.filter-household,
:root[data-flat="true"] body.filter-all,
:root[data-flat="true"] body.filter-trial,
:root[data-flat="true"] body.filter-nudge,
:root[data-flat="true"] .card,
:root[data-flat="true"] .card.cat-medication,
:root[data-flat="true"] .card.cat-household,
:root[data-flat="true"] .card.cat-trial,
:root[data-flat="true"] .card.cat-nudge {
    background-image: none;
}

/* Nav */
.nav {
    background: var(--primary-dark);
    padding: 0 20px;
    display: flex;
    align-items: center;
    height: 56px;
    gap: 24px;
}
.nav-brand {
    display: flex;
    align-items: center;
    text-decoration: none;
}
.nav-logo {
    height: 40px;
    width: auto;
}
.nav a {
    color: rgba(255,255,255,0.85);
    text-decoration: none;
    font-size: 0.9rem;
}
.nav a:hover { color: #fff; }
.nav-right {
    margin-left: auto;
    display: flex;
    gap: 16px;
}
.nav-icon {
    font-size: 1.4rem;
    line-height: 1;
    opacity: 0.85;
}
.nav-icon:hover { opacity: 1; }
.nav-icon-img {
    height: 22px;
    width: auto;
    vertical-align: middle;
}

/* Container */
.container {
    max-width: 900px;
    margin: 0 auto;
    padding: 24px 16px;
}

/* Flash messages */
.flash {
    padding: 12px 16px;
    border-radius: var(--radius);
    margin-bottom: 16px;
    font-size: 0.9rem;
}
.flash-success { background: var(--green-bg); color: #276749; border: 1px solid #c6f6d5; }
.flash-error { background: var(--red-bg); color: #9b2c2c; border: 1px solid #fed7d7; }

/* Cards */
.card {
    /* Same layered-background trick as body — the tile is dimmed in dark
       mode by the linear-gradient overlay so cards stay readable on top
       of dark surfaces. */
    background-color: var(--card-bg);
    background-image: linear-gradient(var(--card-image-overlay), var(--card-image-overlay)), url('/static/background_all_s.jpg');
    background-repeat: repeat;
    border-radius: var(--radius);
    box-shadow: 0 1px 3px rgba(0,0,0,0.08);
    padding: 20px;
    margin-bottom: 16px;
    border-left: 15px solid var(--gray);
}
.card.cat-medication { background-image: linear-gradient(var(--card-image-overlay), var(--card-image-overlay)), url('/static/background_med_s.jpg'); }
.card.cat-household { background-image: linear-gradient(var(--card-image-overlay), var(--card-image-overlay)), url('/static/background_household_s.jpg'); }
.card.cat-trial { background-image: linear-gradient(var(--card-image-overlay), var(--card-image-overlay)), url('/static/background_trials_s.jpg'); }
.card.cat-nudge { background-image: linear-gradient(var(--card-image-overlay), var(--card-image-overlay)), url('/static/background_nudge_s.jpg'); }
/* Plain-white card variant — used on Settings sub-sections that don't
   correspond to an item category (Notifications, Account & Security,
   Backup & Restore) so only the Medication Refill Rules card carries the
   tinted category background. */
.card.card-plain { background-image: none; }
/* Light-gray card variant — used for Settings cards that aren't part of
   the main settings form (Account & Security, Backup & Restore) so they
   read as visually de-emphasized "utility" sections. Light enough that
   the existing dark-gray hint and section-header text stays legible. */
.card.card-gray { background-image: none; background-color: #d0d0d0; }
.card.status-green { border-left-color: var(--green); }
.card.status-yellow { border-left-color: var(--yellow); }
.card.status-red { border-left-color: var(--red); }
.card.status-gray { border-left-color: var(--gray); }

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}
.card-title {
    font-size: 1.15rem;
    font-weight: 600;
}
/* Title is a link (opens edit). Strip the default browser blue + underline
   so it visually matches the prior plain title; keep `cursor: pointer`
   from the anchor so the hover state still hints at clickability. */
.title-link {
    color: var(--text);
    text-decoration: none;
}
.title-link:hover {
    text-decoration: underline;
}
.card-pharmacy {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* Filter tabs */
.filter-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 10px;
}
.filter-tab {
    padding: 8px 18px;
    border-radius: 6px;
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
    color: var(--text-muted);
    background: var(--card-bg);
    border: 1px solid var(--border);
    transition: all 0.15s;
}
.filter-tab:hover { color: var(--text); border-color: var(--primary); }
.filter-tab.active {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
}

/* Category tags */
.card-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
}
.category-tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 4px;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.tag-med { background: #ebf4ff; color: #2b6cb0; }
.tag-household { background: #faf5ff; color: #6b46c1; }
.tag-trial { background: #fff8f1; color: #c05621; }
.tag-nudge { background: #f0fff4; color: #276749; }

/* Status badge */
.badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.badge-green { background: var(--green-bg); color: #276749; }
.badge-yellow { background: var(--yellow-bg); color: #975a16; }
.badge-red { background: var(--red-bg); color: #9b2c2c; }
.badge-gray { background: var(--gray-bg); color: #4a5568; }

/* Card details */
.card-details {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px 16px;
    font-size: 0.88rem;
    color: var(--text-muted);
    margin-bottom: 12px;
}
.card-details strong { color: var(--text); }

.card-notes {
    font-size: 0.82rem;
    color: var(--text-muted);
    font-style: italic;
    margin-bottom: 12px;
}

/* Action row */
.card-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

/* Icon-only action buttons (Edit / Trash / Archive). Sized to feel
   inline with the primary "Log Pickup" / "Keep It" buttons. The "/"
   between trash and archive is a non-clickable visual separator. */
.card-action-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 4px;
    cursor: pointer;
    text-decoration: none;
    line-height: 0;
    border-radius: 6px;
    transition: background 0.15s;
}
.card-action-icon:hover {
    background: rgba(0, 0, 0, 0.06);
}
:root[data-theme="dark"] .card-action-icon:hover {
    background: rgba(255, 255, 255, 0.08);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .card-action-icon:hover {
        background: rgba(255, 255, 255, 0.08);
    }
}
.card-action-icon img {
    width: 22px;
    height: 22px;
    display: block;
}
/* Source PNGs are black-silhouettes-on-transparent. The filter chain
   collapses any visible pixels to pure black, then `invert(N)` maps
   black to RGB(N*255, ...) which lets us pick an exact gray:
     - light theme: invert(0.235) → RGB(60,60,60) = #3C3C3C
     - dark  theme: invert(0.56)  → RGB(142,142,142) = #8E8E8E
   Both values are mirrored in the nav-icon rule below + iOS
   `AppColors.iconGray` + Android `IconGrayLight` / `IconGrayDark`,
   so the whole icon family is one color per theme everywhere. */
.card-action-icon img {
    filter: brightness(0) invert(0.235);
    opacity: 1;
}
:root[data-theme="dark"] .card-action-icon img {
    filter: brightness(0) invert(0.56);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .card-action-icon img {
        filter: brightness(0) invert(0.56);
    }
}

/* Buttons */
.btn {
    display: inline-block;
    padding: 8px 16px;
    border: none;
    border-radius: 6px;
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: opacity 0.15s;
}
.btn:hover { opacity: 0.85; }
.btn-primary { background: var(--primary); color: #fff; }
.btn-secondary { background: var(--border); color: var(--text); }
.btn-danger { background: var(--red); color: #fff; }
.btn-sm { padding: 6px 12px; font-size: 0.8rem; }

/* Forms */
.form-group {
    margin-bottom: 16px;
}
.form-group label {
    display: block;
    font-size: 0.88rem;
    font-weight: 500;
    margin-bottom: 4px;
}
.form-group input,
.form-group textarea,
.form-group select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: inherit;
    /* Explicit light-mode fill: white background, dark text. The
       browser's color-scheme: light dark meta otherwise lets it pick
       dark form controls automatically — which gave a dark fill /
       white text combo in light mode (heavy and inconsistent with the
       Android/iOS native fields). The dark-theme override block at
       the bottom of this file flips these to the dark variant. */
    background-color: var(--card-bg);
    color: var(--text);
}
.form-group input:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(74,144,217,0.15);
}
.form-group input[type="checkbox"] {
    width: auto;
    padding: 0;
    margin-right: 6px;
    vertical-align: middle;
}
.form-group input[type="time"] {
    width: auto;
}
.checkbox-label {
    display: flex;
    align-items: center;
    font-weight: 500;
    font-size: 0.88rem;
    cursor: pointer;
}
.form-group .hint {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-top: 2px;
}

.form-actions {
    display: flex;
    gap: 8px;
    margin-top: 20px;
}

/* Section headers */
.section-header {
    font-size: 0.85rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    margin-top: 24px;
    margin-bottom: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
}

/* Empty state */
.empty-state {
    text-align: center;
    padding: 60px 20px;
    color: var(--text-muted);
    font-size: 1.05rem;
}
.empty-state a { color: var(--primary); }

/* Page header */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.page-header h1 {
    font-size: 1.5rem;
    font-weight: 700;
}
.page-header .date {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* Dashboard header */
.dashboard-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0;
    margin-bottom: 0;
}
.add-button img {
    height: 80px;
    width: auto;
}
.header-right-group {
    display: flex;
    align-items: center;
    gap: 16px;
}
.header-right-group .date {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text);
}
.header-icon {
    height: 26px;
    width: auto;
    opacity: 0.7;
    filter: brightness(0) saturate(100%);
}
.header-icon:hover {
    opacity: 1;
}
.header-icon-text {
    font-size: 22px;
    opacity: 0.7;
    text-decoration: none;
}
.header-icon-text:hover {
    opacity: 1;
}

/* Table */
table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
}
th, td {
    text-align: left;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
}
th {
    font-weight: 600;
    color: var(--text-muted);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Login page */
.login-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: var(--bg);
}
.login-card {
    background: var(--card-bg);
    border-radius: var(--radius);
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    padding: 40px;
    width: 100%;
    max-width: 380px;
    text-align: center;
}
.login-card h1 {
    font-size: 1.5rem;
    margin-bottom: 24px;
}

/* Confirm modal */
.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.45);
    z-index: 1000;
    align-items: center;
    justify-content: center;
}
.modal-overlay.active { display: flex; }
.modal {
    background: var(--card-bg);
    border-radius: var(--radius);
    box-shadow: 0 8px 32px rgba(0,0,0,0.18);
    padding: 28px 28px 24px;
    max-width: 360px;
    width: 90%;
}
.modal h3 {
    font-size: 1.05rem;
    font-weight: 600;
    margin-bottom: 8px;
    color: var(--text);
}
.modal p {
    font-size: 0.9rem;
    color: var(--text-muted);
    margin-bottom: 20px;
}
.modal-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

/* Compact view */
.compact-due {
    display: none;
    font-size: 0.8rem;
    color: var(--text-muted);
    font-weight: 400;
    margin-left: 8px;
}
.compact-toggle {
    display: flex;
    align-items: center;
    margin-left: 6px;
}
/* Anchor for the absolute-positioned #filter-panel — the panel needs
   to drop just under the funnel icon, so its containing block has to
   be a positioned ancestor that's the right size + position. The
   wrapper holds both the icon and the panel as siblings. */
.filter-toggle-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.compact-toggle .header-icon {
    filter: none;
    opacity: 1;
}
/* Filter icon is authored in dark gray; recolor to the brand primary
   blue so it visually matches the (already-blue) compact-view icon
   sitting next to it. Same filter chain `.nav-icon.active` uses to
   recolor a black source to #4A90D9. When a quick filter is active
   we just bump opacity (full vs slightly dim) so the user can see
   the icon is "doing something" without breaking the same-blue
   relationship with the compact icon. */
#filter-toggle .header-icon {
    filter: brightness(0) saturate(100%) invert(51%) sepia(83%) saturate(423%) hue-rotate(183deg) brightness(95%) contrast(88%);
    opacity: 0.7;
}
#filter-toggle.filter-active .header-icon {
    opacity: 1;
}

/* Quick-filter popover. Drops below the filter icon, positioned
   relative to the .compact-toggle anchor. Theme-aware via the same
   var(--card-bg) / var(--text) / var(--border) trio used by the
   notification toast. Hidden by `[hidden]` until the icon is tapped. */
.filter-panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 50;
    min-width: 280px;
    background: var(--card-bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 6px 18px rgba(0,0,0,0.18);
    padding: 12px 14px;
    font-size: 0.92rem;
    line-height: 1.4;
    /* The .filter-tabs parent is text-aligned; reset so the rows
       below can use their natural flex / inline layout. */
    text-align: left;
    /* Keep above the dashboard cards but below modal dialogs. */
}
.filter-panel[hidden] { display: none; }
.filter-panel .filter-heading {
    font-weight: 600;
    font-size: 0.92rem;
    color: var(--text);
    padding-bottom: 4px;
}
.filter-panel .filter-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 0;
    cursor: pointer;
}
.filter-panel .filter-row input[type="checkbox"] {
    width: auto;
    margin: 0;
    cursor: pointer;
}
.filter-panel .filter-days-input {
    width: 56px;
    padding: 2px 6px;
    font-size: 0.92rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--card-bg);
    color: var(--text);
    text-align: center;
}
.filter-panel .filter-hint {
    font-size: 0.78rem;
    color: var(--text-muted);
    margin-top: 6px;
    line-height: 1.3;
}
/* Active-page nav icon: force Primary blue (#4A90D9) regardless of source PNG
   color. The filter chain first collapses any color/alpha to black, then maps
   black → #4A90D9 via invert/hue-rotate/saturate. Tweaked visually against
   the brand blue; small deltas are acceptable for a tint. */
.nav-icon.active .header-icon {
    filter: brightness(0) saturate(100%) invert(51%) sepia(83%) saturate(423%) hue-rotate(183deg) brightness(95%) contrast(88%);
    opacity: 1;
}
/* Tint for non-active nav icons: matches the card-action icons.
   Light = #3C3C3C, dark = #8E8E8E. The :not(.active) guard preserves
   the blue active-page indicator. .compact-toggle is NOT a .nav-icon
   so the rule doesn't catch it — its source PNG color stays
   untouched. */
.nav-icon:not(.active) .header-icon {
    filter: brightness(0) invert(0.235);
    opacity: 1;
}
:root[data-theme="dark"] .nav-icon:not(.active) .header-icon {
    filter: brightness(0) invert(0.56);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .nav-icon:not(.active) .header-icon {
        filter: brightness(0) invert(0.56);
    }
}
.compact-view .full-only { display: none !important; }
.compact-view .compact-due { display: inline; }
.compact-view .card { padding: 12px 16px 10px; margin-bottom: 8px; }
.compact-view .card-header { margin-bottom: 4px; }
.compact-view .card-actions { margin-top: 0; }
.compact-view .card-actions .btn { padding: 4px 10px; font-size: 0.78rem; }

/* Browser-notification toast (in-page fallback or explicit "inpage" mode). */
#nudgerator-toast-container {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 10000;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: min(360px, calc(100vw - 32px));
    pointer-events: none; /* let clicks pass except on toasts */
}
.nudgerator-toast {
    pointer-events: auto;
    background: var(--card-bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-left: 4px solid #4299e1;
    border-radius: 8px;
    padding: 12px 14px 12px 14px;
    padding-right: 36px;
    position: relative;
    cursor: pointer;
    box-shadow: 0 6px 18px rgba(0,0,0,0.28);
    animation: nudgerator-toast-in 220ms ease-out;
    font-size: 0.92rem;
    line-height: 1.35;
}
.nudgerator-toast strong { display: block; margin-bottom: 3px; font-size: 0.98rem; }
.nudgerator-toast-body { opacity: 0.92; }
.nudgerator-toast.priority-high { border-left-color: #e53e3e; }
.nudgerator-toast.priority-low  { border-left-color: #718096; }
.nudgerator-toast-close {
    position: absolute;
    top: 4px;
    right: 6px;
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 20px;
    line-height: 1;
    padding: 4px 8px;
    cursor: pointer;
    opacity: 0.7;
}
.nudgerator-toast-close:hover { opacity: 1; }
/* Action buttons inside the toast (Snooze, Snooze…, snooze-picker
   options). Theme-aware so they're readable in both light and dark
   modes — the previous inline JS styles hard-coded background:#fff
   with no explicit color, which inherited the toast's white text and
   produced unreadable white-on-white in light mode. */
.nudgerator-toast-actions {
    display: flex;
    gap: 6px;
    margin-top: 8px;
}
.nudgerator-toast-btn,
.nudgerator-snooze-option {
    padding: 5px 12px;
    font-size: 0.85rem;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    border-radius: 4px;
    cursor: pointer;
    font-weight: 500;
    line-height: 1.2;
}
.nudgerator-snooze-option {
    padding: 4px 10px;
    font-size: 0.82rem;
    font-weight: 400;
}
.nudgerator-toast-btn:hover,
.nudgerator-snooze-option:hover {
    background: var(--border);
}
.nudgerator-snooze-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
}
@keyframes nudgerator-toast-in {
    from { transform: translateX(20px); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

/* Compact view: truncate long titles to one line with ellipsis. */
.compact-view .card-title {
    display: flex;
    align-items: baseline;
    min-width: 0;
}
.compact-view .card-title .title-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1 1 auto;
}
.compact-view .card-title .compact-due {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Calendar view */
.cal-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin: 8px 0 10px;
}
.cal-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    font-size: 1.5rem;
    line-height: 1;
    text-decoration: none;
    color: var(--primary);
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    transition: all 0.15s;
}
.cal-nav-btn:hover {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
}
.cal-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text);
    min-width: 180px;
    text-align: center;
}
.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-template-rows: auto;       /* weekday header row */
    grid-auto-rows: 1fr;            /* data rows share remaining height equally — works for 5- or 6-week months without leaving a blank row */
    gap: 2px;
    background: var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: 0 1px 3px rgba(0,0,0,0.08);
    height: calc(100vh - 230px);
    min-height: 420px;
}
.cal-weekday {
    background: var(--card-bg);
    padding: 6px 4px;
    text-align: center;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.cal-day {
    background: var(--card-bg);
    padding: 4px 6px;
    display: flex;
    flex-direction: column;
    font-size: 0.85rem;
    min-height: 0;
    overflow: hidden;
}
.cal-day-num {
    font-weight: 500;
    color: var(--text);
    font-size: 0.85rem;
    line-height: 1.1;
}
.cal-day-off .cal-day-num {
    color: var(--gray);
    opacity: 0.55;
}
.cal-day-today {
    background: rgba(74,144,217,0.12);
}
.cal-day-today .cal-day-num {
    color: var(--primary-dark);
    font-weight: 700;
}
/* Calendar task bars (Google Calendar-style) — full-width coloured pills
   stacked vertically in each day cell, each showing the task name. */
.cal-tasks {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 4px;
    min-height: 0;
    overflow: hidden;
}
.cal-task {
    display: block;
    width: 100%;
    font-size: 0.72rem;
    line-height: 1.15;
    padding: 1px 5px;
    border-radius: 3px;
    color: rgba(0,0,0,0.78);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
    transition: filter 0.1s;
    user-select: none;
}
.cal-task:hover { filter: brightness(0.93); }
.cal-task.cat-trial { background: #A6FFA2; }
.cal-task.cat-nudge { background: #ACFFFF; }
.cal-task.cat-medication { background: #FF9D9B; }
.cal-task.cat-household { background: #3AB1FF; }

/* Calendar popup card (centered modal with list-view card content) */
.cal-popup-card {
    max-width: 480px;
    width: 90%;
    max-height: 85vh;
    overflow-y: auto;
    margin: 0;
    cursor: default;
}

/* Responsive */
@media (max-width: 640px) {
    .container { padding: 16px 12px; }
    .card-details { grid-template-columns: 1fr; }
    .page-header { flex-direction: column; align-items: flex-start; gap: 4px; }
    .card-actions { flex-direction: column; }
    .card-actions .btn { width: 100%; }
    .cal-grid { height: calc(100vh - 210px); min-height: 360px; }
    .cal-day { padding: 2px 3px; }
    .cal-weekday { font-size: 0.7rem; padding: 4px 2px; }
    .cal-day-num { font-size: 0.78rem; }
    .cal-task { font-size: 0.65rem; padding: 0 3px; }
    .cal-title { min-width: 130px; font-size: 0.95rem; }
}

/* ─── Dark theme overrides ─────────────────────────────────────────────
   The :root vars above swap surface/text/border colors automatically for
   anything that uses them. The block below patches the spots where
   styles still hardcode dark text on a light tinted background — those
   would become invisible without this. Mirrors the same swap on Android
   and iOS.

   Each rule duplicates between the explicit `[data-theme="dark"]`
   selector (user opted in) and the `prefers-color-scheme: dark` media
   query (user is on 'system' and their OS is dark). The
   `:not([data-theme="light"])` guard in the media query is the escape
   hatch when a user wants light despite a dark device.
*/
:root[data-theme="dark"] .flash-success,
:root[data-theme="dark"] .badge-green {
    color: #9ae6b4;
}
:root[data-theme="dark"] .flash-error,
:root[data-theme="dark"] .badge-red {
    color: #feb2b2;
}
:root[data-theme="dark"] .badge-yellow {
    color: #f6e05e;
}
:root[data-theme="dark"] .badge-gray {
    color: #cbd5e0;
}
:root[data-theme="dark"] .tag-med {
    background: #1e3a5f;
    color: #90cdf4;
}
:root[data-theme="dark"] .tag-household {
    background: #3b1f5f;
    color: #d6bcfa;
}
:root[data-theme="dark"] .tag-trial {
    background: #5c2b00;
    color: #fbd38d;
}
:root[data-theme="dark"] .tag-nudge {
    background: #1f3a2c;
    color: #9ae6b4;
}
:root[data-theme="dark"] .card.card-gray {
    background-color: #4a5568;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .flash-success,
    :root:not([data-theme="light"]) .badge-green {
        color: #9ae6b4;
    }
    :root:not([data-theme="light"]) .flash-error,
    :root:not([data-theme="light"]) .badge-red {
        color: #feb2b2;
    }
    :root:not([data-theme="light"]) .badge-yellow {
        color: #f6e05e;
    }
    :root:not([data-theme="light"]) .badge-gray {
        color: #cbd5e0;
    }
    :root:not([data-theme="light"]) .tag-med {
        background: #1e3a5f;
        color: #90cdf4;
    }
    :root:not([data-theme="light"]) .tag-household {
        background: #3b1f5f;
        color: #d6bcfa;
    }
    :root:not([data-theme="light"]) .tag-trial {
        background: #5c2b00;
        color: #fbd38d;
    }
    :root:not([data-theme="light"]) .tag-nudge {
        background: #1f3a2c;
        color: #9ae6b4;
    }
    :root:not([data-theme="light"]) .card.card-gray {
        background-color: #4a5568;
    }
}
