/* /opt/cert_tracker/static/site.css */

/* ── Theme tokens ────────────────────────────────────────────────────────
   Colors are driven by these CSS variables. :root holds the built-in
   "twilight" (dark) palette as a no-JS/no-DB fallback. The ACTIVE theme is
   data-driven: themes live in the public.themes table (name + a token→color
   map), the system/theme setting selects one, and the app injects that
   theme's tokens into :root at render (see _nav.html). Adding a theme = insert
   one themes row of colors — no CSS, no code.
   --page-bg = body backdrop; --surface/--surface2 = card/panel fills.
   NOTE: only colors that use these var() tokens re-theme — inline hardcoded
   hex in templates still needs converting; that's the incremental cleanup. */
:root {
    --page-bg: #020617;
    --bg: #0f172a;
    --panel: #111827;
    --panel-soft: #1f2937;
    --surface: #0f172a;
    --surface2: #1e2940;
    --text: #e5e7eb;
    --muted: #cbd5e1;
    --accent: #38bdf8;
    --accent2: #f59e0b;   /* secondary/attention accent — cert grouping highlight */
    --border: #334155;
    --header-height: 76px;
    --danger: #dc2626;
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: Arial, Helvetica, sans-serif;
    background: var(--page-bg, #020617);
    color: var(--text, #e5e7eb);
}

body.modal-open {
    overflow: hidden;
}

.container {
    width: min(1736px, calc(100% - 2rem));
    margin: 0 auto;
}

/* Wider container variant — used on data-heavy pages like the Monitor
   Inventory where the table's natural width can exceed the standard
   1736px container. On wide monitors this lets the table fill the
   available space without forcing a horizontal scrollbar. Narrower
   viewports still fall back to `calc(100% - 2rem)` so the page stays
   inside the window and the existing scrollbar mechanics kick in. */
.container.container-wide {
    width: min(1920px, calc(100% - 2rem));
}

.site-header {
    position: sticky;
    top: 0;
    z-index: 1000;
    /* Header follows the active theme (light header in daylight, etc.).
       --surface is a per-theme token; falls back to the old dark glass. */
    background: var(--surface, rgba(15, 23, 42, 0.95));
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border);
    /* iOS PWA standalone: apple-mobile-web-app-status-bar-style is
       set to black-translucent in _pwa_head.html so the status bar
       (clock / battery / Dynamic Island) overlays the page. Push the
       header's content down by the status-bar height so the
       hamburger isn't hidden underneath it. env() is 0 in regular
       browser tabs, ~44–59px in a home-screen-launched PWA. */
    padding-top: var(--safe-top);
}

/* 2-row nav: nav-stack column with secondary row.
   Header layout was a single flex row; restructured so the middle
   column is a flex-column "stack" that can hold one nav row (most
   users) or two (internal admins). Brand on the left and user/badge
   area on the right both vertically center against whatever the stack
   ends up at, so the header grows naturally when row 2 is present and
   stays compact when it isn't. */
.nav-shell {
    min-height: var(--header-height);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
}

.nav-left {
    display: flex;
    align-items: center;
    gap: 1rem;
}
/* Right block: a right-aligned 2-row stack (row 1 company+user, row 2
   tz+theme) with Logout standing alone, vertically centered, to its right. */
.nav-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.8rem;
}
.nav-id-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.35rem;
}
.nav-id-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 0.35rem 0.6rem;
}
.nav-right .nav-logout-form { margin: 0; }

/* Header theme picker — a custom dropdown (not a native <select>) so it's
   themed markup like everything else. Toggle button styled as a pill to match
   the other row-2 badges; the menu reuses the shared .nav-dropdown-menu. */
.theme-dropdown { position: relative; display: inline-flex; }
.theme-dropdown .theme-dd-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin: 0;
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.45rem 0.8rem;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 0.82rem;
    line-height: 1.2;
    cursor: pointer;
}
/* Right-align the menu so it doesn't overflow the header's right edge. */
.theme-dropdown-menu { left: auto; right: 0; transform: none; }
/* The menu items are submit buttons — make them fill the row like the
   anchor-based items do. */
button.nav-dropdown-item {
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    font: inherit;
    cursor: pointer;
}
.theme-switcher-caret {
    width: 0.7rem;
    height: 0.7rem;
    opacity: 0.6;
    pointer-events: none;
    flex: 0 0 auto;
}
/* Drawer theme options: full-width submit buttons styled like nav links. */
button.theme-drawer-item {
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    font: inherit;
    cursor: pointer;
}

.nav-stack {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    flex: 1;
    align-items: flex-end;
    justify-content: center;
}

.site-nav-secondary {
    /* Slightly smaller pills on row 2 so the internal-admin row reads
       as a secondary band rather than competing with the primary nav. */
    opacity: 0.92;
}
.site-nav-secondary .nav-link {
    padding: 0.45rem 0.75rem;
    font-size: 0.9rem;
}

.brand-link {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    color: var(--text, #e5e7eb);
    text-decoration: none;
}

.brand-logo-full {
    height: 55px;            /* +30% from previous 42px per owner request */
    width: auto;
    display: block;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    padding: 0.15rem;
}

.brand-wordmark {
    /* Bumped from 62px to 96px to fill the new two-row header height.
       The logo now sits comfortably alongside both rows of nav links
       for internal admins, and still looks proportionate for users
       who only see the single primary row. */
    height: 96px;
    width: auto;
    display: block;
}

.brand-text-group {
    display: flex;
    flex-direction: column;
    line-height: 1.1;
}

.brand-text {
    font-size: 1.25rem;
    font-weight: 700;
}

.brand-subtext {
    font-size: 0.8rem;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.site-nav {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.nav-link {
    text-decoration: none;
    color: var(--muted);
    padding: 0.65rem 0.9rem;
    border-radius: 10px;
}

.nav-link.active,
.nav-link:hover {
    background: var(--panel-soft);
    color: var(--text, #e5e7eb);
}

.user-badge,
.selected-company-badge {
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.45rem 0.8rem;
    color: var(--muted);
    white-space: nowrap;
}

/* Company-switcher pill button (internal admins only) */
button.company-switcher-trigger {
    background: transparent;
    font-size: 0.85rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    line-height: 1;
    font-family: inherit;
    transition: border-color 0.15s, color 0.15s;
}
button.company-switcher-trigger:hover {
    border-color: var(--text, #e5e7eb);
    color: var(--text, #e5e7eb);
}

/* Company-switcher modal overlay */
.company-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 9000;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 5rem;
}
.company-modal-overlay[hidden] { display: none; }

.company-modal {
    background: var(--panel, #111827);
    border: 1px solid var(--border);
    border-radius: 12px;
    width: 100%;
    max-width: 420px;
    max-height: 75vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.company-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.9rem 1rem 0.75rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.company-modal-title {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text, #e5e7eb);
}
.company-modal-close {
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.2rem;
    font-family: inherit;
}
.company-modal-close:hover { color: var(--text, #e5e7eb); }

.company-modal-search-wrap {
    padding: 0.6rem 1rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.company-modal-search {
    width: 100%;
    background: var(--bg, #0f172a);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.75rem;
    color: var(--text, #e5e7eb);
    font-size: 0.9rem;
    font-family: inherit;
    outline: none;
}
.company-modal-search:focus { border-color: #3b82f6; }
.company-modal-search::-webkit-search-cancel-button { display: none; }

.company-modal-list {
    list-style: none;
    margin: 0;
    padding: 0.35rem 0;
    overflow-y: auto;
    flex: 1 1 auto;
}

.company-modal-item {
    padding: 0.55rem 1rem;
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--text, #e5e7eb);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    border-radius: 0;
    transition: background 0.1s;
}
.company-modal-item:hover { background: var(--border); }
.company-modal-item-current { color: #4ade80; }
.company-modal-check {
    font-size: 0.85rem;
    flex-shrink: 0;
    color: #4ade80;
}

.company-modal-empty {
    margin: 0;
    padding: 1rem;
    text-align: center;
    color: var(--muted);
    font-size: 0.875rem;
}

.app-shell {
    padding: 1.5rem 0 4rem;
}

.hero {
    background: linear-gradient(180deg, #0f172a 0%, #111827 100%);
    padding: 2rem 0 4rem;
}

.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    margin-bottom: 4rem;
}

.nav-links {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.brand {
    font-size: 1.5rem;
    font-weight: 700;
}

.hero-content {
    max-width: 760px;
}

.hero-content h1 {
    font-size: clamp(2rem, 5vw, 4rem);
    line-height: 1.1;
    margin-bottom: 1rem;
}

.hero-content p {
    font-size: 1.125rem;
    color: var(--muted);
    line-height: 1.7;
}

.hero-actions {
    margin-top: 2rem;
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.section {
    padding: 1.5rem 0;
}

.grid-two {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.grid-three {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid-four {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.grid-five {
    display: grid;
    gap: 1.25rem;
    grid-template-columns: repeat(5, minmax(0, 1fr));
}

@media (max-width: 1300px) {
    .grid-five.pricing-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 1100px) {
    .grid-four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid-three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid-five.pricing-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .grid-four,
    .grid-three,
    .grid-two,
    .grid-five.pricing-grid { grid-template-columns: 1fr; }
}

.card,
.auth-card {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.5rem;
}

.card h2,
.auth-card h1 {
    margin-top: 0;
}

.button {
    display: inline-block;
    text-decoration: none;
    border: none;
    border-radius: 10px;
    background: var(--accent);
    color: #082f49;
    font-weight: 700;
    padding: 0.9rem 1.2rem;
    cursor: pointer;
}

.button-secondary {
    background: transparent;
    color: var(--text, #e5e7eb);
    border: 1px solid var(--border);
}

.button-small {
    padding: 0.55rem 0.85rem;
}

.button-danger {
    background: var(--danger);
    color: #fff;
}

.link-button {
    background: transparent;
    color: var(--accent);
    border: none;
    padding: 0;
    font: inherit;
    cursor: pointer;
    text-decoration: none;
}

.auth-shell {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 1rem;
}

.auth-card {
    width: min(460px, 100%);
}

.auth-card-wide {
    width: min(640px, 100%);
}

label {
    display: block;
    margin-top: 1rem;
    margin-bottom: 0.4rem;
}

input,
select {
    width: 100%;
    padding: 0.85rem 1rem;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    color: var(--text, #e5e7eb);
}

.checkbox-label {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1rem;
}

.checkbox-label input {
    width: auto;
    margin: 0;
}

.flash-error {
    background: #7f1d1d;
    border: 1px solid #b91c1c;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    margin-bottom: 1rem;
}

.flash-success {
    background: #14532d;
    border: 1px solid #166534;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    margin-bottom: 1rem;
}

.flash-info {
    background: #1e3a8a;
    border: 1px solid #1d4ed8;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    margin-bottom: 1rem;
}

/* Orange/amber — used for plan-upgrade prompts (Discovery on lower
   tiers, scanner download paywall, etc). Visually distinct from
   info (blue) so the user reads "I need to do something" instead of
   "this is informational." Same shape + padding as the other flash
   variants so layout doesn't shift between categories. */
.flash-warning {
    background: #78350f;
    border: 1px solid #d97706;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    margin-bottom: 1rem;
}

/* Flash backgrounds are fixed dark/saturated in every theme, so their text
   must stay light — don't let it inherit --text (dark on daylight = the
   unreadable black-on-blue we saw). */
.flash-error, .flash-success, .flash-info, .flash-warning,
.flash-error *, .flash-success *, .flash-info *, .flash-warning * {
    color: #f8fafc;
}

.stat-card .stat {
    font-size: 2rem;
    font-weight: 700;
    margin: 0.5rem 0 0;
}

.muted {
    color: var(--muted);
}

.stack-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.stack-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem;
    background: var(--panel-soft);
}

.stack-item-static {
    align-items: center;
}

.user-list-button,
.alert-recipient-button {
    width: 100%;
    text-align: left;
    color: var(--text, #e5e7eb);
    cursor: pointer;
}

/* Stacked-form layouts (login, signup, password_setup) put a button
   directly after the last input. Without this, the button sits flush
   against the input's bottom border. The flex-aligned .company-select-form
   and dashboard filter rows are intentionally NOT included here — those
   want everything baseline-aligned with no top margin. */
.auth-card form input + button,
.auth-card form input + .button,
.auth-card form select + button,
.auth-card form select + .button {
    margin-top: 1rem;
}

.user-list-copy {
    flex: 1;
}

.pill-group {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.pill {
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.3rem 0.7rem;
    font-size: 0.85rem;
    color: var(--muted);
    white-space: nowrap;
    display: inline-block;
}

.company-select-form {
    display: flex;
    gap: 1rem;
    align-items: end;
    flex-wrap: wrap;
}

.company-select-form label {
    margin-top: 0;
}

.table-wrap {
    overflow-x: auto;
    /* When a wide table forces horizontal scroll, hint the browser so
       trackpad/touchpad users get a clear affordance. */
    overscroll-behavior-x: contain;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 1rem;
}

/* Wide tables (Pollers, Monitors) keep their natural layout on narrow
   viewports rather than squishing cells into wrapped, multi-line content.
   The parent .table-wrap shows a horizontal scrollbar when needed. */
.table-wide {
    min-width: 1400px;
}

.table-wide-xl {
    /* Slightly wider than the .container's max content width so the
       monitor inventory always renders with horizontal scroll —
       avoids a layout shift when a Change State value (✓/⚑ buttons)
       widens a row's content past the container edge. */
    min-width: 1700px;
}

th,
td {
    text-align: left;
    padding: 0.85rem 0.75rem;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}

thead th {
    color: var(--muted);
    font-weight: 700;
}

.priority-col {
    /* Wide enough that the "Priority" header fits with breathing room. */
    width: 84px;
    min-width: 84px;
    max-width: 84px;
}

/* Just the drag handle — no header text. Width keeps the column itself
   narrow; the right padding gives the Endpoint column a visual gap so
   the ☰ doesn't crowd up against the hostname. */
.drag-col {
    width: 30px;
    min-width: 30px;
    max-width: 30px;
    padding-right: 1.25rem;
}

.priority-input {
    width: 52px;
    min-width: 52px;
    max-width: 52px;
    padding-left: 0.3rem;
    padding-right: 0.3rem;
    text-align: center;
    /* Hide the browser's up/down spinner controls — we save on change
       and the spinners take up width that pushes the column wider than
       the value itself needs. */
    -moz-appearance: textfield;
}
.priority-input::-webkit-outer-spin-button,
.priority-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Last-Checked staleness: applied by JS when (now - Audited) exceeds
   the poller interval + a 5-min grace window. Same red the rest of
   the table uses for alarming states. */
.lastchecked-stale {
    color: #f87171;
}

.date-col {
    width: 140px;
    min-width: 140px;
}

.date-two-line {
    width: 140px;
    min-width: 140px;
    max-width: 140px;
    white-space: normal;
    word-break: break-word;
    line-height: 1.35;
}

.drag-handle {
    cursor: grab;
    font-size: 1.25rem;
    user-select: none;
}

.draggable-row.dragging {
    opacity: 0.45;
}

.table-actions {
    margin-top: 1rem;
}

.section-header {
    display: flex;
    justify-content: space-between;
    align-items: start;
    gap: 1rem;
}

.section-divider {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 1.5rem 0;
}

.modal-backdrop[hidden] {
    display: none !important;
}

.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(2, 6, 23, 0.8);
    display: grid;
    place-items: center;
    z-index: 2000;
    padding: 1rem;
}

.modal-card {
    width: min(560px, 100%);
    max-height: calc(100vh - 2rem);
    overflow: auto;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.5rem;
    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
}

.modal-close {
    border: none;
    background: transparent;
    color: var(--text, #e5e7eb);
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
}

.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

.modal-actions-split {
    justify-content: space-between;
    align-items: center;
}

.modal-actions-right {
    display: flex;
    gap: 0.75rem;
}

/* Settings page — 4-column rows: label / range / input / helptext.
   Each row is a CSS grid; the column ratios privilege the helptext
   (the longest cell) and keep the input cell tight enough that the
   number sits next to its range hint rather than floating across the
   page. The header row above the inputs labels the columns; aria-hidden
   keeps screen readers focused on the actual labels in each row. */
.settings-form {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 1rem;
}

.settings-row {
    display: grid;
    grid-template-columns: minmax(180px, 1.4fr)
                           minmax(80px, 0.5fr)
                           minmax(140px, 1fr)
                           minmax(220px, 2.1fr);
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--panel-soft);
}

.settings-row-header {
    background: transparent;
    border: none;
    padding: 0 1rem;
    color: var(--muted);
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.settings-label {
    margin: 0;
    font-weight: 600;
}

.settings-range {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.settings-input {
    margin: 0;
}

.settings-help {
    font-size: 0.9rem;
    line-height: 1.4;
}

.settings-help-meta {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.8rem;
    opacity: 0.85;
}

.settings-actions {
    margin-top: 1rem;
}

@media (max-width: 1100px) {
    .grid-five {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 900px) {
    .grid-two,
    .grid-five {
        grid-template-columns: 1fr;
    }

    .nav-shell,
    .nav-left,
    .nav-right,
    .nav,
    .company-select-form,
    .section-header,
    .modal-actions,
    .modal-actions-split,
    .modal-actions-right {
        flex-direction: column;
        align-items: flex-start;
    }

    .site-nav {
        width: 100%;
    }

    .modal-actions .button,
    .modal-actions-right .button {
        width: 100%;
    }

    .date-col,
    .date-two-line {
        width: 100px;
        min-width: 100px;
        max-width: 100px;
    }

    /* Stack each settings row vertically; hide the column header (it
       only made sense when the cells were side-by-side). */
    .settings-row-header {
        display: none;
    }

    .settings-row {
        grid-template-columns: 1fr;
        gap: 0.4rem;
    }
}

/* =====================================================================
   Landing page (templates/index.html)
   Scoped under body.landing so these can't accidentally bleed into the
   logged-in app shell.
   ===================================================================== */

body.landing {
    background: radial-gradient(
        ellipse 1200px 700px at top right,
        rgba(56, 189, 248, 0.08) 0%,
        transparent 60%
    ), #020617;
}

.landing-eyebrow {
    display: inline-block;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.18em;
    color: var(--accent);
    font-weight: 700;
    margin-bottom: 0.85rem;
}

/* Hero eyebrow "The Lifecycle Sentinel" — 2x the base eyebrow, with the
   leading T / L / S enlarged and greened so they spell TLS (the inside joke). */
.eyebrow-sentinel {
    font-size: 1.125rem;      /* 50% bigger than the 0.75rem base */
    line-height: 1.15;
}
.eyebrow-sentinel .tls {
    color: #16a34a;           /* green */
    font-size: 1.5em;         /* +50% again (1.5em of 1.5rem = 2.25rem) */
    font-weight: 800;
}

.landing-hero {
    padding: 4.5rem 0 5rem;
}

.hero-split {
    display: flex;
    align-items: center;
    gap: 2rem;
}

.hero-split .landing-hero-content {
    flex: 0 0 520px;
    max-width: 520px;
}

.hero-logo-stage {
    flex: 1;
    position: relative;
    height: 420px;
    min-width: 300px;
    overflow: hidden;
}

/* ── Cert count banner ───────────────────────────────────────────────────── */
.cert-count-banner {
    text-align: center;
    padding: 0.75rem 1rem 1rem;
    font-size: 1rem;
    color: var(--muted);
    letter-spacing: 0.01em;
}

.cert-count-number {
    font-size: 2rem;
    font-weight: 700;
    color: var(--accent);
    margin-right: 0.4rem;
    letter-spacing: -0.02em;
}

.cert-count-label {
    font-size: 1rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 500;
}

.landing-hero-content {
    max-width: 820px;
}

.landing-hero-content h1 {
    font-size: clamp(2.25rem, 5vw, 3.75rem);
    line-height: 1.08;
    margin: 0 0 1.25rem 0;
    letter-spacing: -0.02em;
}

.landing-lede {
    font-size: 1.2rem;
    line-height: 1.7;
    color: var(--muted);
    margin: 0 0 1.5rem 0;
    max-width: 720px;
}

.landing-trust {
    margin: 1.25rem 0 0 0;
    font-size: 0.9rem;
    color: var(--muted);
}

.landing-section {
    padding: 4rem 0;
}

.landing-section-alt {
    background:
        linear-gradient(180deg, rgba(17, 24, 39, 0.4), rgba(17, 24, 39, 0));
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

.landing-section-head {
    max-width: 720px;
    margin: 0 auto 2.5rem auto;
    text-align: center;
}

.landing-section-head h2 {
    font-size: clamp(1.6rem, 3vw, 2.4rem);
    line-height: 1.15;
    letter-spacing: -0.01em;
    margin: 0 0 0.75rem 0;
}

.landing-section-sub {
    font-size: 1.05rem;
    color: var(--muted);
    margin: 0;
}

.landing-card,
.landing-step {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.landing-card h3,
.landing-step h3 {
    margin: 0;
    font-size: 1.2rem;
}

.landing-card p,
.landing-step p {
    margin: 0;
    color: var(--muted);
    line-height: 1.6;
}

.landing-icon {
    font-size: 1.75rem;
    line-height: 1;
    margin-bottom: 0.25rem;
}

.landing-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 999px;
    background: var(--accent);
    color: #082f49;
    font-weight: 800;
    font-size: 1.1rem;
    margin-bottom: 0.25rem;
}

/* ---------- Pricing ---------- */

.pricing-grid {
    align-items: stretch;
}

.pricing-card {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    position: relative;
}

.pricing-card .pricing-cta,
.pricing-card .pricing-checkout-form {
    margin: auto 0 0 0;  /* margin-top:auto shorthand-safe */
    text-align: center;
}

.pricing-popular {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent), 0 8px 30px rgba(56, 189, 248, 0.18);
}

.pricing-badge {
    position: absolute;
    top: -0.75rem;
    right: 1rem;
    background: var(--accent);
    color: #082f49;
    font-size: 0.7rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    padding: 0.3rem 0.65rem;
    border-radius: 999px;
}

.pricing-name {
    margin: 0;
    font-size: 1.1rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-weight: 700;
}

.pricing-price {
    margin: 0;
    line-height: 1;
}

.pricing-amount {
    font-size: 2.4rem;
    font-weight: 800;
    color: var(--text, #e5e7eb);
}

.pricing-period {
    font-size: 0.95rem;
    color: var(--muted);
    margin-left: 0.25rem;
}

.pricing-blurb {
    margin: 0;
    color: var(--muted);
    font-size: 0.95rem;
    min-height: 2.6rem;
}

.pricing-equivalent {
    margin: -0.4rem 0 0 0;
    color: var(--muted);
    font-size: 0.85rem;
    /* Reserve a single line of vertical space so the cards don't jump
       when toggling between Monthly (empty) and Annual (filled). */
    min-height: 1.2rem;
}

.pricing-features {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}

.pricing-features li {
    color: var(--text, #e5e7eb);
    font-size: 0.95rem;
    padding-left: 1.4rem;
    position: relative;
    line-height: 1.4;
}

.pricing-features li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--accent);
    font-weight: 800;
}

/* ---------- Final CTA strip ---------- */

.landing-cta {
    padding: 4rem 0 5rem;
}

.landing-cta-card {
    text-align: center;
    background: linear-gradient(135deg, rgba(56, 189, 248, 0.12), rgba(17, 24, 39, 0.6));
    border-color: rgba(56, 189, 248, 0.4);
    padding: 3rem 1.5rem;
}

.landing-cta-card h2 {
    margin: 0 0 0.75rem 0;
    font-size: clamp(1.5rem, 3vw, 2.2rem);
}

.landing-cta-card p {
    margin: 0 0 1.5rem 0;
    color: var(--muted);
    font-size: 1.05rem;
}

.landing-cta-card .hero-actions {
    justify-content: center;
}

/* ---------- Feature comparison table ---------- */

.compare-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.compare-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
    min-width: 640px;
}

.compare-table thead th {
    padding: 0.65rem 1rem;
    text-align: center;
    font-weight: 600;
    color: var(--text, #e5e7eb);
    border-bottom: 2px solid var(--border);
    white-space: nowrap;
}

.compare-table thead th.compare-feature-col {
    text-align: left;
    width: 22%;
}

.compare-table td {
    padding: 0.55rem 1rem;
    text-align: center;
    border-bottom: 1px solid var(--border);
    color: var(--muted);
    vertical-align: middle;
}

.compare-table td.compare-feature {
    text-align: left;
    color: var(--text, #e5e7eb);
    font-weight: 500;
}

.compare-table tr.compare-group-row td {
    background: var(--panel-soft);
    color: var(--accent);
    font-weight: 600;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.4rem 1rem;
    text-align: left;
    border-bottom: none;
}

/* Pro column highlight */
.compare-table th.compare-highlight,
.compare-table td.compare-highlight {
    background: rgba(56, 189, 248, 0.06);
    border-left: 1px solid rgba(56, 189, 248, 0.2);
    border-right: 1px solid rgba(56, 189, 248, 0.2);
}

.compare-table thead th.compare-highlight {
    color: var(--accent);
    border-top: 2px solid var(--accent);
    border-bottom: 2px solid var(--border);
}

.compare-yes  { color: #4ade80; font-weight: 600; }
.compare-no   { color: var(--border); }
.compare-note { font-size: 0.78rem; color: var(--muted); font-weight: 400; }

/* ---------- Footer ---------- */

.landing-footer {
    border-top: 1px solid var(--border);
    padding: 2rem 0;
    color: var(--muted);
    font-size: 0.9rem;
}

.landing-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 0.75rem;
}

.landing-footer-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.landing-footer-brand .brand-logo-full {
    height: 36px;
}

.landing-footer-links {
    display: flex;
    gap: 1.25rem;
    flex-wrap: wrap;
}

.landing-footer-links a {
    color: var(--muted);
    text-decoration: none;
}

.landing-footer-links a:hover {
    color: var(--text, #e5e7eb);
}

.landing-footer-copy {
    margin: 0;
    font-size: 0.85rem;
    opacity: 0.7;
}

@media (max-width: 700px) {
    .landing-hero { padding: 3rem 0 3.5rem; }
    .landing-section { padding: 3rem 0; }
    .landing-cta { padding: 3rem 0; }
    .pricing-blurb { min-height: 0; }
}

/* =====================================================================
   Billing page (templates/billing.html)
   Reuses .pricing-card/.pricing-* from the landing page; adds the status
   pills, current-plan highlighting, and the smaller "Current plan" badge
   used inside the in-app card grid.
   ===================================================================== */

.billing-status {
    margin-top: 1.25rem;
}

.billing-state-pill {
    display: inline-block;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    color: var(--text, #e5e7eb);
}

.billing-state-trial {
    border-color: var(--accent);
    background: rgba(56, 189, 248, 0.12);
    color: var(--accent);
}

.billing-state-active {
    border-color: #16a34a;
    background: rgba(22, 163, 74, 0.12);
    color: #4ade80;
}

.billing-state-grace {
    border-color: #d97706;
    background: rgba(217, 119, 6, 0.14);
    color: #fbbf24;
}

.billing-state-dormant {
    border-color: var(--danger);
    background: rgba(220, 38, 38, 0.14);
    color: #fca5a5;
}

.pricing-card.pricing-current {
    border-color: #16a34a;
    box-shadow: 0 0 0 1px #16a34a, 0 8px 30px rgba(22, 163, 74, 0.18);
}

.billing-current-badge {
    background: #16a34a;
    color: #052e1a;
}

.tier-status-banner {
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.tier-status-banner.tier-status-at-cap {
    border-color: #d97706;
    background: rgba(217, 119, 6, 0.10);
}

.tier-status-text strong {
    color: var(--text, #e5e7eb);
}

.add-monitor-blocked {
    padding: 0.85rem 1rem;
    border: 1px dashed #d97706;
    border-radius: 8px;
    background: rgba(217, 119, 6, 0.06);
    color: var(--muted);
}

.add-monitor-blocked a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 700;
}

.billing-cancel-banner {
    margin-top: 1.25rem;
    padding: 1.1rem 1.25rem;
    border-radius: 14px;
    border: 1px solid #d97706;
    background: rgba(217, 119, 6, 0.12);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.billing-cancel-banner-text strong {
    color: #fbbf24;
    display: block;
    margin-bottom: 0.25rem;
}

.billing-cancel-banner-text p {
    margin: 0;
    color: var(--muted);
    font-size: 0.95rem;
}

/* ---------- Monthly / Annual cadence toggle ---------- */

.pricing-cadence-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0;
    padding: 0.25rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    margin-top: 1.25rem;
}

.pricing-cadence-toggle .cadence-option {
    background: transparent;
    border: none;
    color: var(--muted);
    font-weight: 700;
    font-size: 0.9rem;
    padding: 0.55rem 1.1rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.pricing-cadence-toggle .cadence-option:hover {
    color: var(--text, #e5e7eb);
}

.pricing-cadence-toggle .cadence-option.active {
    background: var(--accent);
    color: #082f49;
}

.cadence-savings {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 0.15rem 0.45rem;
    border-radius: 999px;
    background: rgba(22, 163, 74, 0.18);
    color: #4ade80;
}

.pricing-cadence-toggle .cadence-option.active .cadence-savings {
    background: rgba(8, 47, 73, 0.2);
    color: #082f49;
}

/* =====================================================================
   Billing page (templates/billing.html)
   Reuses .pricing-card/.pricing-* from the landing page; adds the status
   pills, current-plan highlighting, and the smaller "Current plan" badge
   used inside the in-app card grid.
   ===================================================================== */

.billing-status {
    margin-top: 1.25rem;
}

.billing-state-pill {
    display: inline-block;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    color: var(--text, #e5e7eb);
}

.billing-state-trial {
    border-color: var(--accent);
    background: rgba(56, 189, 248, 0.12);
    color: var(--accent);
}

.billing-state-active {
    border-color: #16a34a;
    background: rgba(22, 163, 74, 0.12);
    color: #4ade80;
}

.billing-state-grace {
    border-color: #d97706;
    background: rgba(217, 119, 6, 0.14);
    color: #fbbf24;
}

.billing-state-dormant {
    border-color: var(--danger);
    background: rgba(220, 38, 38, 0.14);
    color: #fca5a5;
}

.pricing-card.pricing-current {
    border-color: #16a34a;
    box-shadow: 0 0 0 1px #16a34a, 0 8px 30px rgba(22, 163, 74, 0.18);
}

.billing-current-badge {
    background: #16a34a;
    color: #052e1a;
}

.tier-status-banner {
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.tier-status-banner.tier-status-at-cap {
    border-color: #d97706;
    background: rgba(217, 119, 6, 0.10);
}

.tier-status-text strong {
    color: var(--text, #e5e7eb);
}

.add-monitor-blocked {
    padding: 0.85rem 1rem;
    border: 1px dashed #d97706;
    border-radius: 8px;
    background: rgba(217, 119, 6, 0.06);
    color: var(--muted);
}

.add-monitor-blocked a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 700;
}

.billing-cancel-banner {
    margin-top: 1.25rem;
    padding: 1.1rem 1.25rem;
    border-radius: 14px;
    border: 1px solid #d97706;
    background: rgba(217, 119, 6, 0.12);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.billing-cancel-banner-text strong {
    color: #fbbf24;
    display: block;
    margin-bottom: 0.2rem;
}

/* =====================================================================
   Status / capacity pills  (pollers.html, monitors.html)
   Base .pill is already defined above; these colour variants layer on top.
   ===================================================================== */

.pill-online {
    border-color: #16a34a;
    background: rgba(22, 163, 74, 0.12);
    color: #4ade80;
}

.pill-offline {
    border-color: var(--danger);
    background: rgba(220, 38, 38, 0.12);
    color: #fca5a5;
}

.pill-ok {
    border-color: #16a34a;
    background: rgba(22, 163, 74, 0.10);
    color: #4ade80;
}

.pill-warn {
    border-color: #d97706;
    background: rgba(217, 119, 6, 0.12);
    color: #fbbf24;
}

.pill-danger {
    border-color: var(--danger);
    background: rgba(220, 38, 38, 0.14);
    color: #fca5a5;
}

/* =====================================================================
   Pollers page (templates/pollers.html)
   ===================================================================== */

/* One-time token reveal banner */
.poller-token-banner {
    margin-bottom: 1.5rem;
    padding: 1rem 1.25rem;
    border-radius: 12px;
    border: 1px solid #16a34a;
    background: rgba(22, 163, 74, 0.08);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.poller-token-value {
    display: inline-block;
    margin: 0 0.6rem;
    padding: 0.25rem 0.55rem;
    border-radius: 6px;
    background: var(--panel-soft);
    border: 1px solid var(--border);
    font-size: 0.85rem;
    letter-spacing: 0.03em;
    word-break: break-all;
    color: #4ade80;
}

/* Version / update download banner */
.poller-download-banner {
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--panel-soft);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    font-size: 0.9rem;
    color: var(--muted);
}

.poller-download-links {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
}

/* ── Settings page tab panels ─────────────────────────────────────────────── */
/* Panels are hidden by default; JS adds .tab-active to show the selected one. */
.settings-tab-panel            { display: none; }
.settings-tab-panel.tab-active { display: block; }

/* Tab buttons — pill-style matching the main nav */
.settings-tabs {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}
.settings-tab-btn {
    padding: 0.55rem 1.1rem;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--muted);
    background: none;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    font-family: inherit;
}
.settings-tab-btn:hover  { background: var(--panel-soft); color: var(--text, #e5e7eb); }
.settings-tab-btn.active { background: var(--panel-soft); color: var(--text, #e5e7eb); }

.compare-table tfoot td {
    padding: 0.85rem 1rem;
    text-align: center;
    border-top: 2px solid var(--border);
    border-bottom: none;
}

.compare-back {
    text-align: center;
    margin-top: 1.5rem;
    font-size: 0.9rem;
}

.compare-back a { color: var(--accent); text-decoration: none; }
.compare-back a:hover { text-decoration: underline; }

/* "Compare all plans" link below pricing grid */
.pricing-compare-link {
    text-align: center;
    margin-top: 1.5rem;
    font-size: 0.95rem;
}

.pricing-compare-link a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 500;
}

.pricing-compare-link a:hover { text-decoration: underline; }

/* Pill variants */
.pill-success { background: rgba(74, 222, 128, 0.15); color: #4ade80; border-color: rgba(74, 222, 128, 0.3); }
.pill-action  { cursor: pointer; background: var(--panel-soft); border: 1px solid var(--border); }
.pill-action:hover { border-color: var(--accent); color: var(--accent); }

/* ── UAT environment banner ─────────────────────────────────────────────── */
.uat-banner {
    position: sticky;
    top: 0;
    z-index: 2000;
    background: #ff1493;
    color: #fff;
    text-align: center;
    padding: 0.45rem 1rem;
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
}

.uat-banner-label {
    flex: 0 0 auto;
}

.uat-become-production-btn {
    flex: 0 0 auto;
    background: #fff;
    color: #b30070;
    border: 2px solid #fff;
    border-radius: 4px;
    padding: 0.2rem 0.75rem;
    font-weight: 700;
    font-size: 0.8rem;
    cursor: pointer;
    letter-spacing: 0.03em;
    transition: background 0.15s, color 0.15s;
}

.uat-become-production-btn:hover {
    background: #b30070;
    color: #fff;
}

/* Become Production modal */
.uat-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.65);
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
}

.uat-modal-overlay[hidden] {
    display: none;
}

.uat-modal {
    background: var(--panel, #111827);
    border: 2px solid #ff1493;
    border-radius: 8px;
    padding: 2rem;
    max-width: 420px;
    width: 90%;
}

.uat-modal-title {
    margin: 0 0 1rem;
    font-size: 1.1rem;
    color: #ff1493;
}

.uat-modal p, .uat-modal ul {
    font-size: 0.9rem;
    margin: 0.5rem 0;
}

.uat-modal ul {
    padding-left: 1.25rem;
}

.uat-modal-actions {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.5rem;
    justify-content: flex-end;
}

.uat-confirm-btn {
    background: #ff1493;
    border-color: #ff1493;
}

.uat-confirm-btn:hover {
    background: #b30070;
    border-color: #b30070;
}


/* =============================================================================
   Support nav dropdown
   ============================================================================= */
.nav-dropdown {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.nav-dropdown-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    font: inherit;
    /* Match the sibling .nav-link color instead of inheriting (which made
       "Support" drift to a different color than the other header links). */
    color: var(--muted);
    line-height: inherit;
}
.nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 0.75rem);
    left: 50%;
    transform: translateX(-50%);
    background: #1e2130;
    background: var(--panel-soft, #1f2937);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    box-shadow: 0 8px 32px rgba(0,0,0,0.45);
    min-width: 210px;
    padding: 0.35rem 0;
    z-index: 900;
    list-style: none;
    margin: 0;
}
.nav-dropdown-item {
    display: block;
    padding: 0.55rem 1.1rem;
    font-size: 0.9rem;
    color: var(--text, #e5e7eb);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.15s;
}
.nav-dropdown-item:hover,
.nav-dropdown-item.active {
    background: var(--panel-soft, #1f2937);
    color: var(--accent);
}
.nav-dropdown-disabled {
    opacity: 0.5;
    cursor: default;
    pointer-events: none;
}
.badge-coming-soon {
    font-size: 0.7rem;
    background: var(--accent);
    color: #fff;
    border-radius: 999px;
    padding: 0.1em 0.55em;
    margin-left: 0.4rem;
    vertical-align: middle;
    font-weight: 600;
    letter-spacing: 0.02em;
}
.badge-internal {
    font-size: 0.75rem;
    background: #7c3aed;
    color: #fff;
    border-radius: 999px;
    padding: 0.15em 0.65em;
    margin-left: 0.5rem;
    font-weight: 600;
    vertical-align: middle;
}

/* =============================================================================
   Roadmap page
   ============================================================================= */
.roadmap-hero {
    padding: 3.5rem 0 2rem;
}
.roadmap-hero-inner {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.roadmap-hero-text {
    text-align: left;
}
.roadmap-hero-text h1 {
    margin: 0.4rem 0 0.75rem;
    font-size: clamp(1.8rem, 3vw, 2.5rem);
}
.roadmap-hero-text .landing-lede {
    margin: 0;
}
.roadmap-hero-actions {
    display: flex;
    gap: 0.75rem;
    flex-shrink: 0;
    align-items: center;
    padding-bottom: 0.2rem;
}

.roadmap-board-section {
    padding: 0 0 3rem;
}
.roadmap-board {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1.25rem;
    align-items: start;
}
@media (max-width: 900px) {
    .roadmap-board { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
    .roadmap-board { grid-template-columns: 1fr; }
}

.roadmap-col {
    background: var(--panel, #111827);
    border: 1px solid var(--border);
    border-radius: 0.75rem;
    overflow: hidden;
}
.roadmap-col-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 0.9rem;
}
.roadmap-col-dot {
    width: 10px; height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}
.roadmap-col-header--inprogress .roadmap-col-dot  { background: #3b82f6; }
.roadmap-col-header--comingsoon .roadmap-col-dot  { background: #f59e0b; }
.roadmap-col-header--future     .roadmap-col-dot  { background: #8b5cf6; }
.roadmap-col-header--completed  .roadmap-col-dot  { background: #22c55e; }
.roadmap-col-header--inprogress { border-top: 3px solid #3b82f6; }
.roadmap-col-header--comingsoon { border-top: 3px solid #f59e0b; }
.roadmap-col-header--future     { border-top: 3px solid #8b5cf6; }
.roadmap-col-header--completed  { border-top: 3px solid #22c55e; }
.roadmap-col-count {
    margin-left: auto;
    background: var(--panel-soft, #1f2937);
    border-radius: 999px;
    padding: 0.1em 0.6em;
    font-size: 0.78rem;
    color: var(--muted, #cbd5e1);
}
.roadmap-col-title { flex: 1; }

.roadmap-tiles {
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
}
.roadmap-empty {
    color: var(--muted, #cbd5e1);
    font-size: 0.85rem;
    padding: 0.5rem 0.25rem;
    text-align: center;
}

/* Tile */
.roadmap-tile {
    background: var(--panel-soft, #1f2937);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
    padding: 0.85rem 0.9rem 0.7rem;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
    position: relative;
}
.roadmap-tile:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.roadmap-tile:focus {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.roadmap-tile-version {
    font-size: 0.7rem;
    font-weight: 600;
    color: #22c55e;
    letter-spacing: 0.04em;
    margin-bottom: 0.3rem;
    display: block;
}
.roadmap-tile-title {
    font-size: 0.9rem;
    font-weight: 600;
    margin: 0 0 0.35rem;
    line-height: 1.3;
}
.roadmap-tile-summary {
    font-size: 0.82rem;
    color: var(--muted, #cbd5e1);
    margin: 0 0 0.65rem;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.roadmap-tile-footer {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.roadmap-tile-comments {
    font-size: 0.78rem;
    color: var(--muted, #cbd5e1);
}
.roadmap-tile-votes-label {
    font-size: 0.78rem;
    color: var(--muted, #cbd5e1);
    margin-left: -0.3rem;  /* trim the gap left by .roadmap-tile-footer { gap: 0.6rem; } */
}

/* Vote button */
.vote-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: var(--panel, #111827);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.22em 0.75em;
    font-size: 0.82rem;
    cursor: pointer;
    color: var(--muted, #cbd5e1);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    line-height: 1;
}
.vote-btn:hover { border-color: var(--accent); color: var(--accent); }
.vote-btn--voted { background: var(--accent); border-color: var(--accent); color: #fff; }
.vote-btn--voted:hover { opacity: 0.85; }
.vote-icon { font-size: 0.7rem; line-height: 1; }
.vote-count { font-weight: 600; }

/* Admin tile controls */
.roadmap-tile-admin-actions {
    margin-left: auto;
}
.input-sm {
    font-size: 0.78rem;
    padding: 0.2em 0.5em;
    height: auto;
}

/* =============================================================================
   Roadmap modal
   ============================================================================= */
.roadmap-modal-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.55);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
.roadmap-modal-overlay[hidden] { display: none !important; }
.roadmap-modal {
    background: var(--panel, #111827);
    border: 1px solid var(--border);
    border-radius: 0.85rem;
    padding: 2rem;
    max-width: 600px;
    width: 100%;
    max-height: 85vh;
    overflow-y: auto;
    position: relative;
}
.roadmap-modal-close {
    position: absolute;
    top: 1rem; right: 1.1rem;
    background: none; border: none;
    font-size: 1.4rem;
    cursor: pointer;
    color: var(--muted, #cbd5e1);
    line-height: 1;
}
.roadmap-modal-close:hover { color: var(--text, #e5e7eb); }
.roadmap-modal-header {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    margin-bottom: 1rem;
}
.roadmap-modal-header h2 { margin: 0; font-size: 1.2rem; flex: 1; }
.roadmap-modal-detail {
    font-size: 0.92rem;
    color: var(--muted, #cbd5e1);
    line-height: 1.6;
    margin: 0 0 0.75rem;
    white-space: pre-wrap;
}
.roadmap-modal-meta {
    font-size: 0.8rem;
    color: var(--muted, #cbd5e1);
    margin: 0.2rem 0;
}
.roadmap-modal-admin {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding: 0.75rem;
    background: rgba(124,58,237,0.08);
    border: 1px solid rgba(124,58,237,0.2);
    border-radius: 0.5rem;
    margin: 0.75rem 0;
}
.roadmap-delete-btn { color: #ef4444 !important; border-color: #ef4444 !important; }
.roadmap-modal-vote {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1rem 0;
}
.roadmap-vote-label { font-size: 0.85rem; color: var(--muted, #cbd5e1); }

/* Status badge */
.roadmap-status-badge {
    font-size: 0.72rem;
    font-weight: 600;
    border-radius: 999px;
    padding: 0.2em 0.7em;
    white-space: nowrap;
    flex-shrink: 0;
}
.roadmap-status-badge--in_progress  { background: rgba(59,130,246,0.15); color: #3b82f6; }
.roadmap-status-badge--coming_soon  { background: rgba(245,158,11,0.15); color: #f59e0b; }
.roadmap-status-badge--future_ideas { background: rgba(139,92,246,0.15); color: #8b5cf6; }
.roadmap-status-badge--completed    { background: rgba(34,197,94,0.15);  color: #22c55e; }

/* Comments */
.roadmap-modal-comments { margin-top: 1.25rem; border-top: 1px solid var(--border); padding-top: 1rem; }
.roadmap-modal-comments h3 { font-size: 0.95rem; margin: 0 0 0.75rem; }
.roadmap-comment { margin-bottom: 0.85rem; }
.roadmap-comment-author { font-size: 0.82rem; font-weight: 600; margin-right: 0.5rem; }
.roadmap-comment-date { font-size: 0.78rem; color: var(--muted, #cbd5e1); }
.roadmap-comment-body { font-size: 0.87rem; margin: 0.2rem 0 0; color: var(--text, #e5e7eb); white-space: pre-wrap; }
.roadmap-no-comments { font-size: 0.85rem; color: var(--muted, #cbd5e1); }
.roadmap-comment-form { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.75rem; }
.roadmap-comment-form .input { resize: vertical; }
.roadmap-comment-form .button { align-self: flex-start; }
.roadmap-modal-loading, .roadmap-modal-err { text-align: center; padding: 2rem; color: var(--muted, #cbd5e1); }
.roadmap-modal-err { color: #ef4444; }

/* =============================================================================
   Suggest / Admin create form
   ============================================================================= */
.roadmap-suggest-section { padding: 0 0 3rem; }
.roadmap-suggest-card {
    background: var(--panel, #111827);
    border: 1px solid var(--border);
    border-radius: 0.85rem;
    padding: 2rem;
    max-width: 640px;
    margin: 0 auto;
}
.roadmap-suggest-card h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
.roadmap-suggest-card p  { color: var(--muted, #cbd5e1); font-size: 0.9rem; margin: 0 0 1rem; }
.roadmap-suggest-form { display: flex; flex-direction: column; gap: 0.65rem; }
.roadmap-suggest-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}
.roadmap-suggest-chars { font-size: 0.78rem; color: var(--muted, #cbd5e1); margin-left: auto; }
.roadmap-suggest-msg { font-size: 0.85rem; margin: 0; }
.roadmap-admin-card { border-color: rgba(124,58,237,0.3); }

/* ── Monitor Filter & Sort Bar ───────────────────────────────────────
   Sticky filter row above the Monitor Inventory table. Lets users
   constrain by domain / interval / poller status / online status /
   certificate status, and choose Priority vs. Days-until-expiration
   sort. URL-driven state — bookmarkable and shareable. */
.monitor-filter-bar {
    position: sticky;
    top: var(--header-height, 76px);
    z-index: 50;
    background: var(--panel-soft, #1f2937);
    border: 1px solid var(--border, #334155);
    border-radius: 0.5rem;
    padding: 0.65rem 0.85rem;
    margin: 0 0 0.85rem 0;
}

.monitor-filter-bar-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem;
}

.monitor-filter-field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    flex: 1 1 9rem;
    min-width: 9rem;
}

.monitor-filter-field label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted, #cbd5e1);
    margin: 0;
}

.monitor-filter-field input[type="text"],
.monitor-filter-field select {
    width: 100%;
    padding: 0.42rem 0.55rem;
    font-size: 0.9rem;
    background: var(--bg, #0f172a);
    color: var(--text, #e5e7eb);
    border: 1px solid var(--border, #334155);
    border-radius: 0.35rem;
}

.monitor-filter-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: 0 0 auto;
    padding-bottom: 0.05rem;
}

.monitor-filter-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.6rem;
    padding-top: 0.5rem;
    border-top: 1px dashed var(--border, #334155);
}

.monitor-filter-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    background: rgba(56, 189, 248, 0.12);
    border: 1px solid rgba(56, 189, 248, 0.4);
    color: var(--text, #e5e7eb);
    padding: 0.18rem 0.45rem 0.18rem 0.55rem;
    border-radius: 999px;
}

.monitor-filter-pill strong {
    font-weight: 600;
}

.monitor-filter-pill-sort {
    background: rgba(154, 205, 50, 0.12);
    border-color: rgba(154, 205, 50, 0.4);
}

.monitor-filter-pill-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.1);
    color: inherit;
    text-decoration: none;
    font-size: 0.9rem;
    line-height: 1;
}

.monitor-filter-pill-x:hover {
    background: rgba(255, 255, 255, 0.2);
}

/* ── Add Monitor form: collapse the single-row layout on small screens ────
   Desktop layout (set inline in monitors.html) packs Target, Port, Path,
   Protocol, Interval, Poller into one 6-column grid with Target ~1/3 wide.
   On phones that shape is uncomfortable — fields shrink below usability.
   These rules override grid-template-columns at the mobile breakpoint to
   give Target its own full-width row and collapse the other 5 fields
   into a 2-column grid (Port + Path on one row, Protocol on its own row
   since it's the widest, Interval + Poller paired). #ImportantOverride
   beats the inline style. */
@media (max-width: 720px) {
    .add-monitor-row {
        grid-template-columns: 1fr 1fr !important;
    }
    .add-monitor-row > div:first-child {
        grid-column: 1 / -1;   /* Target spans the whole row */
    }
}

@media (max-width: 720px) {
    .monitor-filter-field {
        flex: 1 1 100%;
    }
    .monitor-filter-bar {
        position: static;
    }
}

/* ── Monitor table: sticky headers + always-accessible scrollbars ───
   The wrap becomes a two-axis scrolling region so a long monitor
   list scrolls inside it, the column headers remain visible at the
   top, and the horizontal scrollbar lives at the bottom of the
   visible table region (right under the rows the user is looking at)
   instead of hidden at the bottom of a thousand-row list. */
.monitor-table-wrap {
    position: sticky;
    /* Stick just below the top scrollbar proxy (proxy is sticky one
       line up). Proxy height ≈ 14px + 0.35rem margin; rounded to 20px. */
    top: calc(var(--monitor-sticky-top, 130px) + 20px);
    max-height: calc(100vh - var(--monitor-sticky-top, 130px) - 120px);
    min-height: 240px;
    overflow: auto;
    z-index: 20;
}

#monitor-table thead th {
    position: sticky;
    top: 0;
    z-index: 4;
    background: var(--panel, #111827);
    /* Faux border-bottom: thead th borders don't sit above scrolling
       tbody rows correctly with position:sticky, so paint a 1px
       inset line via box-shadow instead. */
    box-shadow: inset 0 -1px 0 var(--border, #334155);
}

@media (max-width: 720px) {
    /* On narrow viewports let the page scroll naturally; an internal
       scrollbox inside a narrow card feels cramped, and there's no
       room to stack sticky-on-sticky-on-sticky chrome. */
    .monitor-table-wrap {
        position: static;
        max-height: none;
        min-height: 0;
    }
    #monitor-table thead th {
        position: static;
        box-shadow: none;
    }
}

.monitor-empty-row td {
    border-bottom: none;
}

/* ── Monitor table top scrollbar ───────────────────────────────────
   A 1px-tall proxy element whose width is matched (in JS) to the
   table's scrollWidth. The proxy's overflow-x:auto exposes a real
   horizontal scrollbar above the table headers; the scroll handler
   keeps proxy.scrollLeft and tableWrap.scrollLeft in sync so dragging
   either scrollbar scrolls the table. The proxy hides itself when
   the table has no horizontal overflow. */
.monitor-table-top-scroll {
    position: sticky;
    top: var(--monitor-sticky-top, 130px);
    z-index: 25;
    overflow-x: auto;
    overflow-y: hidden;
    height: 14px;
    margin-bottom: 0.35rem;
    background: var(--panel-soft, #1f2937);
    border: 1px solid var(--border, #334155);
    border-radius: 4px;
}

.monitor-table-top-scroll-inner {
    height: 1px;
    width: 1px; /* set dynamically by JS to table.scrollWidth */
}

@media (max-width: 720px) {
    /* Mobile shows the natural bottom scrollbar — no need for a proxy. */
    .monitor-table-top-scroll {
        display: none !important;
    }
}

/* ── Monitor bulk pause / resume controls ─────────────────────────
   Filter-scoped icon+count chips that live in the last column of
   the table header row (where the original pause-all button sat).
   Each is hidden when its count is 0. Manual pauses
   (PausedReason='manual') are sticky — bulk Resume never touches
   them. The chips show only the symbol and the count to fit in the
   narrow header column. The tooltip carries the explanatory text. */
.monitor-bulk-pause,
.monitor-bulk-resume {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    background: transparent;
    height: 28px;
    min-width: 28px;
    padding: 0 0.45rem;
    border-radius: 4px;
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    font-family: inherit;
    vertical-align: middle;
}

.monitor-bulk-pause + .monitor-bulk-resume,
.monitor-bulk-resume + .monitor-bulk-pause {
    margin-left: 0.25rem;
}

.monitor-bulk-pause {
    border: 1px solid #0e7490;
    color: #22d3ee;
}

.monitor-bulk-pause:hover {
    background: rgba(14, 116, 144, 0.18);
}

.monitor-bulk-resume {
    border: 1px solid #c2410c;
    color: #fb923c;
}

.monitor-bulk-resume:hover {
    background: rgba(194, 65, 12, 0.18);
}


/* =============================================================
 * Mobile foundation (M1)
 *
 * Standardized breakpoints, off-canvas nav drawer, hamburger
 * button, safe-area-inset handling. Customer-facing pages only.
 * The desktop nav remains unchanged above --bp-tablet.
 *
 * Breakpoints:
 *   --bp-mobile : 640px   phones
 *   --bp-tablet : 1024px  tablets + small laptops
 *
 * Pre-existing media queries (700/720/900/1100/1300px) are left
 * alone for now; M2-M4 will gradually consolidate them.
 * ============================================================= */

:root {
    --bp-mobile: 640px;
    --bp-tablet: 1024px;
    /* Safe-area insets default to 0 on browsers that don't expose them. */
    --safe-top:    env(safe-area-inset-top,    0px);
    --safe-right:  env(safe-area-inset-right,  0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left:   env(safe-area-inset-left,   0px);
}

/* Honor iOS notch / Android nav bar around the body's primary content. */
@supports (padding: max(0px)) {
    body {
        padding-left:  max(0px, var(--safe-left));
        padding-right: max(0px, var(--safe-right));
    }
}

/* ── Hamburger button ─────────────────────────────────────────
   Hidden above --bp-tablet; shown as a 44x44 tap target on
   mobile/tablet. Sits to the left of the brand in the header. */
.nav-hamburger {
    display: none;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    cursor: pointer;
    margin-right: 0.5rem;
    flex: 0 0 auto;
}
.nav-hamburger:hover { background: rgba(56, 189, 248, 0.08); }
.nav-hamburger:active { background: rgba(56, 189, 248, 0.15); }

@media (max-width: 1024px) {
    .nav-hamburger { display: inline-flex; }
}

/* ── Drawer scrim (backdrop) ──────────────────────────────────
   Full-viewport overlay shown when the drawer is open. Faded in
   via the .open class so the transition runs smoothly. Pointer
   events captured so taps on the scrim close the drawer. */
.nav-scrim {
    position: fixed;
    inset: 0;
    background: rgba(2, 6, 23, 0.6);
    backdrop-filter: blur(2px);
    z-index: 998;
    opacity: 0;
    transition: opacity 200ms ease;
}
.nav-scrim.open { opacity: 1; }

/* ── Drawer panel ─────────────────────────────────────────────
   Slides in from the left. Fixed width on phones (85vw, capped),
   slightly wider on tablets. Full-height with internal scroll so
   long nav (Internal admin section + Support sublinks) fits. */
.nav-drawer {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: min(85vw, 320px);
    background: var(--panel, #111827);
    border-right: 1px solid var(--border);
    z-index: 999;
    transform: translateX(-100%);
    transition: transform 240ms cubic-bezier(0.2, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding-top: var(--safe-top);
    padding-bottom: var(--safe-bottom);
    padding-left:  var(--safe-left);
}
.nav-drawer.open { transform: translateX(0); }

.nav-drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border);
    flex: 0 0 auto;
}
.nav-drawer-header .brand-wordmark {
    height: 28px;
    width: auto;
    display: block;
}

.nav-drawer-close {
    width: 44px;
    height: 44px;
    background: transparent;
    border: none;
    color: var(--text);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
}
.nav-drawer-close:hover { background: rgba(56, 189, 248, 0.08); }

.nav-drawer-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.5rem 0;
    /* Smooth momentum scrolling on iOS. */
    -webkit-overflow-scrolling: touch;
}

.nav-drawer-link {
    display: block;
    padding: 0.85rem 1.2rem;
    color: var(--text);
    text-decoration: none;
    font-size: 1rem;
    /* min tap target height (44px) is met by padding + line-height. */
    line-height: 1.2;
    border-left: 3px solid transparent;
}
.nav-drawer-link:hover {
    background: rgba(56, 189, 248, 0.06);
}
.nav-drawer-link.active {
    background: rgba(56, 189, 248, 0.10);
    color: var(--accent);
    border-left-color: var(--accent);
    font-weight: 600;
}

.nav-drawer-divider {
    height: 1px;
    background: var(--border);
    margin: 0.5rem 0;
}

.nav-drawer-section-label {
    padding: 0.5rem 1.2rem 0.25rem;
    color: var(--muted);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.nav-drawer-user {
    padding: 0.85rem 1.2rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}
.nav-drawer-username {
    color: var(--muted);
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}

/* Lock body scroll while the drawer is open so background content
   doesn't jiggle when the user pans on iOS. */
body.nav-drawer-open {
    overflow: hidden;
    /* Compensate for the missing scrollbar width to prevent layout
       shift on desktop-narrow viewports that happen to land below
       1024px (rare but possible). */
    padding-right: var(--safe-right);
}

/* ── Hide the inline desktop nav on mobile/tablet ─────────────
   The drawer replaces it. We keep the brand link visible in the
   header (so users can tap it to go home) and the user controls
   on the right collapse into the drawer's user section. */
@media (max-width: 1024px) {
    .site-nav,
    .site-nav-secondary,
    .nav-right .user-badge,
    .nav-right .selected-company-badge:not(.company-switcher-trigger),
    .nav-id-row-bottom,
    .nav-right > form {
        display: none;
    }

    /* Keep the company switcher in the header on mobile — it's the
       most-tapped right-side control. Tighten its size. */
    .nav-right .company-switcher-trigger {
        font-size: 0.85rem;
        max-width: 50vw;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Override the pre-existing 900px stack-vertical rule so the
       header stays horizontal: hamburger | brand | switcher. */
    .nav-shell {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: center;
        gap: 0.5rem;
    }
    .nav-left {
        flex-direction: row;
        align-items: center;
        flex: 0 1 auto;
        min-width: 0;
    }
    .nav-right {
        flex-direction: row;
        align-items: center;
        flex: 0 0 auto;
        margin-left: auto;
    }
    .brand-wordmark {
        /* Match the .nav-hamburger 44px tap-target height so the
           wordmark + hamburger present as a balanced pair. Earlier
           iterations at 28px / 42px were still hard to read on
           phone screens. */
        height: 44px;
    }
}

/* ── Tighten container padding on phones ─────────────────────── */
@media (max-width: 640px) {
    .container {
        width: calc(100% - 1rem);
    }
}

/* ── Phone-friendly form inputs ──────────────────────────────
   16px min font-size on inputs prevents iOS from zooming the
   viewport on focus. Apply broadly to text-like inputs. */
@media (max-width: 1024px) {
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="search"],
    input[type="url"],
    input[type="number"],
    input[type="tel"],
    select,
    textarea {
        font-size: 16px;
    }
}


/* =============================================================
 * Mobile foundation — landing additions (M1.5)
 *
 * Building on M1's drawer pattern: a CTA group inside the landing
 * drawer (login + signup stacked, full-width buttons), plus a
 * phone-friendly variant of the plan-compare table on features.html
 * (each plan column becomes a card, rows become labeled rows).
 * ============================================================= */

/* CTA buttons inside the landing drawer — stacked, full-width. */
.nav-drawer-cta {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.85rem 1.2rem;
}
.nav-drawer-cta .button {
    width: 100%;
    text-align: center;
    padding: 0.75rem 1rem;
    font-size: 0.95rem;
}

/* Phone: brand wordmark in the header gets slightly shorter so
   the company switcher / hamburger fit comfortably. M1 already did
   this for the auth nav at 1024px; we mirror at 640px for tighter
   phones. */
@media (max-width: 640px) {
    .brand-wordmark {
        /* Match the .nav-hamburger 44px tap-target height so the
           wordmark + hamburger present as a balanced pair. */
        height: 44px;
    }
}


/* M1.5 fix: landing nav-right contents need to be hidden on mobile too.
   M1's rule targets `.site-nav` etc. which the LANDING nav doesn't
   use — landing nav puts links + dropdown + CTAs directly inside
   .nav-right with no wrapper. Hide all of nav-right's children below
   --bp-tablet on landing pages only (auth pages need the company
   switcher to stay). */
@media (max-width: 1024px) {
    body.landing .nav-right > * {
        display: none;
    }
}

/* =============================================================
 * Compare-plans table — mobile transposed view (M1.5 v2)
 *
 * Below 640px the desktop .compare-table is hidden and replaced
 * by a JS-built .compare-mobile-cards container with one card per
 * plan. Each card lists every feature for that plan, with ✓/—
 * icons for boolean features and plain text for value features.
 * Pro card gets the "Most popular" highlight.
 *
 * The JS that builds this lives inline in features.html (CSP-
 * nonce'd). On desktop the cards container stays empty and the
 * table renders as normal.
 * ============================================================= */

.compare-mobile-cards {
    display: none;
}

@media (max-width: 640px) {
    .compare-mobile-cards {
        display: flex;
        flex-direction: column;
        gap: 1rem;
        margin: 1rem 0 0;
    }

    /* Hide the desktop table entirely on phone — the JS-built cards
       replace it. compare-wrap stays so .container layout doesn't
       collapse. */
    .compare-wrap > table.compare-table {
        display: none;
    }
}

.compare-mobile-card {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--panel-soft);
    overflow: hidden;
}
.compare-mobile-card-highlight {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent) inset, 0 4px 12px rgba(56,189,248,0.08);
}

.compare-mobile-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.85rem 1rem;
    background: rgba(56, 189, 248, 0.05);
    border-bottom: 1px solid var(--border);
}
.compare-mobile-card-header h3 {
    margin: 0;
    font-size: 1.15rem;
    color: var(--text);
}
.compare-mobile-card-badge {
    background: var(--accent);
    color: #0b1220;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.2rem 0.5rem;
    border-radius: 999px;
    white-space: nowrap;
}

.compare-mobile-card-rows {
    list-style: none;
    margin: 0;
    padding: 0;
}

.compare-mobile-card-group {
    background: rgba(15, 23, 42, 0.4);
    color: var(--muted);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.55rem 1rem;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
.compare-mobile-card-rows li:first-child.compare-mobile-card-group {
    border-top: none;
}

.compare-mobile-card-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 1rem;
    border-bottom: 1px solid rgba(255,255,255,0.04);
    font-size: 0.92rem;
}
.compare-mobile-card-rows li:last-child {
    border-bottom: none;
}
.compare-mobile-card-label {
    color: var(--text);
    flex: 1 1 auto;
    min-width: 0;
    /* Wrap long feature names instead of overflowing the card. */
    overflow-wrap: anywhere;
    word-break: break-word;
}
.compare-mobile-card-value {
    color: var(--text);
    text-align: right;
    flex: 0 0 auto;
    overflow-wrap: anywhere;
    max-width: 60%;
}
.compare-mobile-icon-yes {
    color: #4ade80;
    font-size: 1.1rem;
    font-weight: 700;
}
.compare-mobile-icon-no {
    color: var(--muted);
    font-size: 1.1rem;
}

/* M1.5 hotfix: tighten container too much was too aggressive.
   Bump back to half-rem each side for breathing room. */
@media (max-width: 640px) {
    .container {
        width: calc(100% - 1.5rem);
    }
}


/* =============================================================
 * Mobile fixes (M1.5 v3): hero reflow, plan-tier colors
 * ============================================================= */

/* Hero overflow on phones: the desktop flex layout locks
   .landing-hero-content at 520px and the logo stage at 300px min,
   summing to ~820px which overflows any phone viewport. Below
   --bp-tablet wrap the split, drop the 520px lock, and hide the
   logo stage (it's decorative and adds vertical scroll for no
   informational value on phone). */
@media (max-width: 1024px) {
    .hero-split {
        flex-direction: column;
        gap: 1.5rem;
    }
    .hero-split .landing-hero-content {
        flex: 1 1 100%;
        max-width: 100%;
        min-width: 0;
    }
    .hero-logo-stage {
        display: none;
    }
}

/* Tighter typography on phones for the hero so the H1 fits without
   side-scrolling on narrow viewports. */
@media (max-width: 640px) {
    .landing-hero {
        padding: 2rem 0 2.5rem;
    }
    .landing-hero-content h1 {
        font-size: 1.85rem;
        line-height: 1.2;
    }
    .landing-hero-content p {
        font-size: 1rem;
    }
    .hero-actions {
        flex-wrap: wrap;
        gap: 0.5rem;
    }
}

/* ── Plan-tier color coding on the mobile compare cards ──────────
   Without this every card looks identical except for Pro's blue
   border. Each plan gets a distinct header text color so the user
   can tell at a glance which tier they're scrolling through. The
   card border stays neutral; only the title text + a subtle bar
   on the left edge of the card header carry the color. */

.compare-mobile-card[data-plan="Personal"]   .compare-mobile-card-header h3 { color: #9ca3af; }
.compare-mobile-card[data-plan="Starter"]    .compare-mobile-card-header h3 { color: #0ea5e9; }
.compare-mobile-card[data-plan="Pro"]        .compare-mobile-card-header h3 { color: #38bdf8; }
.compare-mobile-card[data-plan="Business"]   .compare-mobile-card-header h3 { color: #10b981; }
.compare-mobile-card[data-plan="Enterprise"] .compare-mobile-card-header h3 { color: #f59e0b; }

.compare-mobile-card[data-plan="Personal"]   .compare-mobile-card-header { border-left: 4px solid #9ca3af; }
.compare-mobile-card[data-plan="Starter"]    .compare-mobile-card-header { border-left: 4px solid #0ea5e9; }
.compare-mobile-card[data-plan="Pro"]        .compare-mobile-card-header { border-left: 4px solid #38bdf8; }
.compare-mobile-card[data-plan="Business"]   .compare-mobile-card-header { border-left: 4px solid #10b981; }
.compare-mobile-card[data-plan="Enterprise"] .compare-mobile-card-header { border-left: 4px solid #f59e0b; }


/* =============================================================
 * Red X for "not included" (M1.5 v4)
 *
 * Both the desktop table (.compare-no cells) and the mobile per-plan
 * cards (.compare-mobile-icon-no) get a red × instead of a white
 * em-dash. The em-dash is the original HTML content; we hide it by
 * making the text transparent and overlay the × via ::before.
 *
 * Why ::before instead of editing the template: the template emits
 * &mdash; in many places and writing a sed pass risks touching
 * em-dashes outside compare-no cells (titles, copy, etc.). One CSS
 * rule covers every compare-no cell automatically.
 * ============================================================= */

/* Desktop table */
.compare-table td.compare-no {
    position: relative;
    color: transparent;
}
.compare-table td.compare-no::before {
    content: '\00d7';      /* × multiplication sign */
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #ef4444;
    font-size: 1.4rem;
    font-weight: 700;
}
/* compare-highlight + compare-no (Pro column NOT included row) keeps
   the column accent but still shows the X */
.compare-table td.compare-no.compare-highlight {
    background: rgba(56, 189, 248, 0.06);
}

/* Mobile per-plan card */
.compare-mobile-icon-no {
    color: #ef4444;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1;
}


/* Desktop compare-yes prominence: match the mobile card treatment
   (larger, bolder green checkmark) so the ✓ jumps out against the
   row text. Existing rule was just color:#4ade80 with font-weight:600;
   bump to 1.3rem / 700 for parity with the mobile icon. Pro column
   keeps its highlight background. */
.compare-table td.compare-yes {
    color: #4ade80;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1;
}


/* =============================================================
 * M2: Monitors page mobile UX
 *
 * Below 640px the desktop monitor-table renders as a stack of
 * tappable cards. Tapping a card opens the existing detail modal
 * (the click handler reads data-view-monitor="true" so we don't
 * need to touch JS).
 *
 * Card contents:
 *   - Hostname (full FQDN:port/path) as the card header
 *   - Tags chips (already inline in monitor-meta-row)
 *   - Alert recipients line (also inline in monitor-meta-row)
 *   - Status, Certificate, Expiration as compact label:value rows
 *
 * Hidden on phone cards (still visible on desktop):
 *   - Drag handle (touch reorder is fiddly; numeric priority input
 *     in the detail modal still works)
 *   - TLS version (visible in detail modal)
 *   - Availability (rolled into Status visually)
 *   - Change State action buttons (visible in detail modal)
 *   - Interval, Poller Status, Last Checked, Priority input
 *   - Action column (force poll / pause / delete — all available
 *     in the detail modal)
 *
 * Filter bar: collapses behind a "Filters" toggle button that
 * shows/hides the existing form. Sort dropdown stays inline above
 * the card list.
 *
 * Bulk action buttons (Bulk pencil, Pause N, Resume N): hidden on
 * mobile for v1. Bulk operations on phone aren't a common flow;
 * desktop or tablet covers them. M2 v2 can move them into a
 * floating action button if customer demand surfaces.
 * ============================================================= */

@media (max-width: 640px) {
    /* ── Filter bar collapse ─────────────────────────────────── */
    /* Hide the filter form by default. The toggle button below
       (added by JS) flips a data attribute on the body to reveal it. */
    body[data-mobile-filters="closed"] .monitor-filter-bar {
        display: none;
    }
    /* On mobile show the form as a card-stack of labeled fields
       when open, not the horizontal row. */
    .monitor-filter-bar-row {
        flex-direction: column;
        gap: 0.75rem;
    }
    .monitor-filter-field {
        width: 100%;
    }
    .monitor-filter-field label {
        display: block;
        margin-bottom: 0.3rem;
    }

    /* Hide the top scroll proxy — no horizontal scroll on cards */
    .monitor-table-top-scroll { display: none; }
    .monitor-table-wrap {
        overflow: visible;
        max-height: none;
    }

    /* ── Table → cards ──────────────────────────────────────── */
    #monitor-table,
    #monitor-table > thead,
    #monitor-table > tbody,
    #monitor-table > tbody > tr,
    #monitor-table > tbody > tr > td {
        display: block;
        width: 100%;
    }
    #monitor-table {
        border: none;
        background: transparent;
        min-width: 0;
    }
    /* Hide column headers entirely on mobile — the cards use
       inline labels instead. */
    #monitor-table > thead {
        display: none;
    }

    /* Each row -> tappable card */
    #monitor-table > tbody > tr {
        position: relative;
        border: 1px solid var(--border);
        border-radius: 12px;
        background: var(--panel-soft);
        padding: 0.85rem 1rem;
        margin-bottom: 0.6rem;
        cursor: pointer;
        /* Reset the sticky/positioned weirdness from desktop. */
        height: auto;
        line-height: 1.4;
    }
    #monitor-table > tbody > tr:hover {
        background: rgba(56, 189, 248, 0.04);
    }

    /* All TDs collapse to padding-less blocks by default */
    #monitor-table > tbody > tr > td {
        padding: 0;
        border: none;
        background: transparent;
        white-space: normal;
    }

    /* Hide non-essential columns on the card. Column numbering
       (1-indexed) per the actual monitors.html structure:
         1 drag-col  2 hostname  3 TLS  4 Status  5 Availability
         6 Certificate  7 ChangeState  8 Interval  9 PollerStatus
        10 LastChecked  11 Expiration  12 Priority  13 Actions
       We keep 2 (hostname), 4 (status), 6 (certificate), 11 (expires). */
    #monitor-table > tbody > tr > td.drag-col,
    #monitor-table > tbody > tr > td:nth-child(3),    /* TLS */
    #monitor-table > tbody > tr > td:nth-child(5),    /* Availability */
    #monitor-table > tbody > tr > td:nth-child(7),    /* Change State */
    #monitor-table > tbody > tr > td:nth-child(8),    /* Interval */
    #monitor-table > tbody > tr > td:nth-child(9),    /* Poller Status */
    #monitor-table > tbody > tr > td:nth-child(10),   /* Last Checked */
    #monitor-table > tbody > tr > td.priority-col,
    #monitor-table > tbody > tr > td:last-child {     /* Actions */
        display: none;
    }

    /* Hostname cell — teal bold title. Reserve room for a bell icon
       in the upper-right via padding-right. Tags below the title
       wrap naturally; alert-recipient line hidden on mobile (M3
       will surface it in the detail modal). */
    #monitor-table > tbody > tr > td.hostname-cell {
        padding: 0 2rem 0.7rem 0;   /* right padding leaves room for bell */
        font-size: 1.05rem;
    }
    #monitor-table > tbody > tr > td.hostname-cell .monitor-primary-name {
        font-weight: 700;
        font-size: 1.05rem;
        color: #2dd4bf;          /* teal-400 */
        display: block;
        overflow-wrap: anywhere;
        word-break: break-word;
    }
    /* Neutralize the desktop monitor-meta-row absolute positioning
       (top:2.8rem left:0.26in width:min(1000px,calc(100vw-380px))
       is meant for desktop sidebar layout). */
    #monitor-table > tbody > tr .monitor-meta-row {
        position: static;
        width: auto;
        top: auto;
        left: auto;
        display: block;
        margin-top: 0.4rem;
    }
    /* Hide alert-recipient line on the card — it's already
       available in the detail modal, and the bell icon below
       signals whether any are configured. */
    #monitor-table > tbody > tr .monitor-alert-line {
        display: none;
    }
    /* Tags wrap below the title in normal flow. */
    #monitor-table > tbody > tr .monitor-tags-inline {
        display: flex;
        flex-wrap: wrap;
        gap: 0.3rem;
        margin: 0;
        position: static;
    }

    /* Bell icon top-right when alert recipients are configured. The
       data-assigned-recipient-ids attribute is "" when none, comma-
       separated IDs when one or more — so the :not(="") check fires
       only for configured rows. */
    #monitor-table > tbody > tr[data-assigned-recipient-ids]:not([data-assigned-recipient-ids=""])::before {
        content: '\1F514';   /* 🔔 bell */
        position: absolute;
        top: 0.7rem;
        right: 0.9rem;
        font-size: 1.05rem;
        line-height: 1;
        z-index: 2;
        pointer-events: none;
    }

    /* Status (col 4), Certificate (col 6), Expiration (col 11) become
       compact labeled rows. */
    #monitor-table > tbody > tr > td:nth-child(4),
    #monitor-table > tbody > tr > td:nth-child(6),
    #monitor-table > tbody > tr > td:nth-child(11) {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 0.5rem;
        padding: 0.3rem 0;
        font-size: 0.9rem;
        border-top: 1px solid rgba(255,255,255,0.04);
    }
    #monitor-table > tbody > tr > td:nth-child(4)::before {
        content: 'Status';
    }
    #monitor-table > tbody > tr > td:nth-child(6)::before {
        content: 'Certificate';
    }
    #monitor-table > tbody > tr > td:nth-child(11)::before {
        content: 'Expires';
    }
    #monitor-table > tbody > tr > td:nth-child(4)::before,
    #monitor-table > tbody > tr > td:nth-child(6)::before,
    #monitor-table > tbody > tr > td:nth-child(11)::before {
        color: var(--muted);
        font-size: 0.75rem;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        font-weight: 600;
        flex: 0 0 auto;
    }
    /* Hide the "check details" sub-line in the certificate cell on
       phone — tap-anywhere already opens the detail modal, so the
       hint is redundant and was preventing the cert status word
       from right-aligning consistently with the Status row. */
    #monitor-table > tbody > tr > td:nth-child(6) > div {
        display: none;
    }

    /* Expiration row: label on its own line, date on its own line
       right-aligned, all in one line of text. The desktop
       .date-two-line class on the td sets word-break:break-word
       which caused character-by-character wrapping; override to
       normal and add white-space:nowrap so the full date string
       (YYYY-MM-DD HH:MM:SS TZ) stays on a single line. If the
       date is wider than the cell it'll overflow leftward
       (text-align:right) but stays visually intact. */
    #monitor-table > tbody > tr > td:nth-child(11) {
        /* Inherit display:flex from the shared rule above (cols 4/6/11
           all become labeled rows with label-left + value-right). The
           shared rule already sets justify-content:space-between and
           align-items:center, so we just need to undo the desktop
           .date-two-line fixed width and force the date to stay on one
           line at a smaller font. */
        width: 100%;
        min-width: 0;
        max-width: none;
        font-size: 0.85rem;
        word-break: normal;
        overflow-wrap: normal;
        white-space: nowrap;
    }

    /* Hide CSV Template / Import CSV buttons on mobile — desktop-
       only feature. The two buttons + the hidden file input share
       an unclassed flex container; target each by id. */
    #csv-download-headers,
    #csv-import-btn,
    #csv-file-input {
        display: none !important;
    }

    /* Empty-state row spans full width when no monitors */
    #monitor-table > tbody > tr.monitor-empty-row {
        background: transparent;
        border: 1px dashed var(--border);
        padding: 1.5rem 1rem;
        text-align: center;
    }
    #monitor-table > tbody > tr.monitor-empty-row > td {
        display: block;
        padding: 0;
    }

    /* Bulk action buttons (inside the hidden thead's last th) stay
       hidden along with the thead. M2 v2 can resurface via FAB if
       needed. */
}

/* ───────────────────────────────────────────────────────────────
 * M3: Monitor detail modal — full-screen mobile UX
 *
 * On phones, the detail modal becomes a full-screen page with a
 * compact sticky header (FQDN + days remaining + prev/next/close),
 * a horizontal tab strip (General / Advisories / Recipients /
 * Response / History), and a scrollable body. The existing
 * desktop sections are still in the DOM — JS at the end of
 * monitors.html tags each section with [data-mobile-tab="…"]
 * and wraps the middle children in a .detail-mobile-body scroller.
 *
 * Tab strip is hidden on desktop. data-mobile-tab and
 * data-active-tab attributes have no effect outside the breakpoint.
 * ─────────────────────────────────────────────────────────────── */

/* Hide tab strip everywhere by default — only the mobile @media
   below reveals it. Without this rule, the strip would briefly
   appear if a desktop user resizes down after the modal opens. */
.detail-mobile-tabs {
    display: none;
}

/* ── ST6: acquisition-method pill (monitor row + detail modal) ────
   Only renders when the monitor's MonitorType is not 'tls'. Inline
   so it sits next to the FQDN on desktop, drops to its own line on
   mobile cards because the card column is narrow.

   Color-coded by family: mail = amber, ldap = teal, db = purple. */
.monitor-type-pill {
    display: inline-block;
    /* No left margin: pill now lives inside .monitor-meta-row as its
       first child (flush with the alert-recipients line / tag chips
       that follow it on the same horizontal band). */
    margin: 0 0.5rem 0 0;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: 0.02em;
    white-space: nowrap;
    vertical-align: middle;
    border: 1px solid;
    /* Default — overridden below per family. */
    background: rgba(148,163,184,0.15);
    color: #cbd5e1;
    border-color: rgba(148,163,184,0.35);
}
.monitor-type-pill[data-monitor-type="smtp_starttls"],
.monitor-type-pill[data-monitor-type="imap_starttls"],
.monitor-type-pill[data-monitor-type="pop3_starttls"] {
    background: rgba(245,158,11,0.12);
    color: #fbbf24;
    border-color: rgba(245,158,11,0.45);
}
.monitor-type-pill[data-monitor-type="ldap_starttls"] {
    background: rgba(20,184,166,0.12);
    color: #5eead4;
    border-color: rgba(20,184,166,0.45);
}
.monitor-type-pill[data-monitor-type="postgresql"],
.monitor-type-pill[data-monitor-type="mysql"] {
    background: rgba(167,139,250,0.12);
    color: #c4b5fd;
    border-color: rgba(167,139,250,0.45);
}
/* Hide via [hidden] (used by the detail-modal pill which JS toggles).
   Without this the inline-block above would beat the UA hidden rule. */
.monitor-type-pill[hidden] { display: none !important; }

/* Mobile: the pill now lives inside the meta-row which sits below
   the big teal FQDN on card layout, so the pill flows naturally
   on the same line as alerts/tags. Slightly smaller text on phones. */
@media (max-width: 640px) {
    .monitor-type-pill {
        margin: 0 0.4rem 0 0;
        font-size: 0.65rem;
    }
}

@media (max-width: 640px) {
    /* Full-screen modal: kill the centered card padding, make the
       card fill the viewport, square corners, no margin. The
       inline width:min(1400px,96vw) needs !important to lose. */
    #monitor-detail-modal {
        padding: 0;
        align-items: stretch;
        justify-content: stretch;
    }
    #monitor-detail-modal .modal-card {
        width: 100vw !important;
        max-width: 100vw !important;
        height: 100vh;
        max-height: 100vh;
        border-radius: 0;
        margin: 0;
        padding: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    /* Compact sticky header: FQDN in 1rem, URL muted on its own
       line, days-remaining + prev/next/close inline at the right.
       Flex parent already exists from the template's inline
       display:flex; we tighten padding + font sizes. */
    #monitor-detail-modal .modal-header {
        padding: 0.65rem 0.85rem;
        flex: 0 0 auto;
        background: var(--panel);
        border-bottom: 1px solid var(--border);
        align-items: center;
    }
    #monitor-detail-modal .modal-header > div:first-child {
        flex: 1 1 auto;
        min-width: 0;
    }
    #monitor-detail-modal .modal-header h2 {
        font-size: 1rem;
        margin: 0;
        line-height: 1.2;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    #monitor-detail-modal #detail-monitor-url {
        font-size: 0.72rem !important;
        margin: 0.1rem 0 0 !important;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    #monitor-detail-modal #detail-days-remaining-line {
        font-size: 0.78rem !important;
        line-height: 1.1 !important;
    }
    #monitor-detail-modal .modal-header #monitor-detail-prev,
    #monitor-detail-modal .modal-header #monitor-detail-next {
        padding: 0.1rem 0.45rem !important;
        font-size: 1rem !important;
    }

    /* Tab strip below header, scrolls horizontally if needed */
    .detail-mobile-tabs {
        display: flex;
        overflow-x: auto;
        background: var(--panel-soft, var(--panel));
        border-bottom: 1px solid var(--border);
        flex: 0 0 auto;
        scrollbar-width: none;
    }
    .detail-mobile-tabs::-webkit-scrollbar {
        display: none;
    }
    .detail-mobile-tabs button {
        flex: 0 0 auto;
        background: transparent;
        border: none;
        color: var(--muted);
        padding: 0.75rem 1rem;
        font-size: 0.85rem;
        font-weight: 600;
        cursor: pointer;
        white-space: nowrap;
        border-bottom: 2px solid transparent;
        margin: 0;
        line-height: 1.2;
    }
    .detail-mobile-tabs button.active {
        color: var(--accent);
        border-bottom-color: var(--accent);
    }

    /* Scrollable body wrapper (JS-inserted). modal-actions stay
       pinned at the bottom outside this scroller. */
    .detail-mobile-body {
        flex: 1 1 auto;
        overflow-y: auto;
        overflow-x: hidden;
        padding: 0.85rem 0.9rem 1rem;
        -webkit-overflow-scrolling: touch;
    }
    .detail-mobile-body > * {
        /* Sections inside the body keep their own margin-top,
           but kill the leftover stack-item visual chrome. */
        margin-bottom: 0;
    }
    .detail-mobile-body .stack-item,
    .detail-mobile-body .stack-item-static {
        padding: 0;
        background: transparent;
        border: none;
        margin: 0;
    }

    /* Tab-driven visibility: hide every tagged element whose tab
       doesn't match the active tab. We hide the OUT-OF-TAB sections
       rather than force-showing in-tab ones — that way an in-tab
       section that's hidden for data reasons (empty advisories,
       no RT chart) keeps its hidden state. */
    .modal-card[data-active-tab="general"]    [data-mobile-tab]:not([data-mobile-tab="general"])    { display: none !important; }
    .modal-card[data-active-tab="advisories"] [data-mobile-tab]:not([data-mobile-tab="advisories"]) { display: none !important; }
    .modal-card[data-active-tab="recipients"] [data-mobile-tab]:not([data-mobile-tab="recipients"]) { display: none !important; }
    .modal-card[data-active-tab="response"]   [data-mobile-tab]:not([data-mobile-tab="response"])   { display: none !important; }
    .modal-card[data-active-tab="history"]    [data-mobile-tab]:not([data-mobile-tab="history"])    { display: none !important; }

    /* Tighten cert grid on mobile: collapse 2-col grid to 1-col so
       long values don't get squeezed. */
    #monitor-detail-modal .stack-item > div[style*="grid-template-columns"] {
        grid-template-columns: 1fr !important;
        gap: 0.75rem !important;
    }
    #monitor-detail-modal .stack-item > div[style*="grid-template-columns"] > div[style*="grid-column"] {
        grid-column: 1 / -1 !important;
    }

    /* Recipients block (Contact div moved out of cert grid) gets
       a panel look so it doesn't read as a floating field. */
    #monitor-detail-modal .modal-card > [data-mobile-tab="recipients"] {
        padding: 0.85rem 1rem;
        background: var(--surface2, var(--panel-soft));
        border-radius: 6px;
        margin-top: 0;
    }

    /* Compact modal-actions row */
    #monitor-detail-modal .modal-actions {
        flex: 0 0 auto;
        padding: 0.6rem 0.9rem;
        border-top: 1px solid var(--border);
        background: var(--panel);
        margin-top: 0;
    }

    /* Change History table on mobile: simplify the response-time
       chart and history table can horizontal-scroll inside their
       table-wrap; nothing more aggressive needed for v1. */
}

/* ───────────────────────────────────────────────────────────────
 * M4: Domains page mobile UX
 *
 * Mirrors the M2/M3 patterns for the Monitors page:
 *   - Phase 1: tr.domain-row → card layout (hide thead, hide
 *     Registrar/NS/Source/Actions cells, big FQDN on top, status
 *     + expires + last-polled as labeled rows).
 *   - Phase 2: full-screen detail modal with sticky compact
 *     header, horizontal tab strip (Details / Edit / History),
 *     scrollable body, and a footer with Edit (jumps to Edit
 *     tab) + Delete (red) buttons.
 *
 * The domains table uses class=monitor-table (not the #monitor-table
 * id that M2 keyed off), and the detail modal uses the bespoke
 * .domain-modal-backdrop / .domain-modal classes (not .modal-card).
 * So everything here is scoped to those selectors and won't bleed
 * into the monitor page rules.
 * ─────────────────────────────────────────────────────────────── */

/* Tab strip hidden everywhere by default (only the @media reveals it) */
.domain-mobile-tabs {
    display: none;
}

/* Baseline footer styling for the static .domain-modal-actions row
   that lives at the bottom of #domain-detail-backdrop. On desktop
   this sits naturally at the end of the modal body with a divider
   above. On mobile the @media block below pins it to the bottom. */
.domain-modal-actions {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border, #334155);
    display: flex;
    gap: 0.6rem;
    justify-content: flex-end;
    align-items: center;
}
.domain-modal-actions button {
    padding: 0.55rem 1rem;
    border-radius: 6px;
    border: 1px solid transparent;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
}
.domain-modal-actions .domain-action-edit {
    background: var(--accent, #38bdf8);
    color: #0b1220;
}
.domain-modal-actions .domain-action-delete {
    background: transparent;
    color: #f87171;
    border-color: #b91c1c;
}
.domain-modal-actions .domain-action-delete:hover {
    background: rgba(220, 38, 38, 0.08);
}

@media (max-width: 640px) {

    /* ── Phase 1: tr.domain-row card layout ─────────────────── */

    /* Kill the table chrome — hide the header row, collapse the
       table-wrap padding, let rows become cards. */
    table.monitor-table thead {
        display: none;
    }
    table.monitor-table,
    table.monitor-table tbody,
    table.monitor-table tr.domain-row,
    table.monitor-table tr.domain-row > td {
        display: block;
        width: 100%;
    }
    table.monitor-table tr.domain-row {
        background: var(--panel-soft, var(--panel));
        border: 1px solid var(--border);
        border-radius: 8px;
        margin: 0 0 0.75rem;
        padding: 0.8rem 0.9rem 0.85rem;
        position: relative;
        cursor: pointer;
    }
    table.monitor-table tr.domain-row > td {
        border: none;
        padding: 0;
        text-align: left !important;
    }

    /* Hide non-essential cells. The domains table column order is:
       1=Domain, 2=Status, 3=Registrar, 4=Expires, 5=NS, 6=Source,
       7=Last Checked, 8=Actions. Keep 1/2/4/7, hide 3/5/6/8. */
    table.monitor-table tr.domain-row > td:nth-child(3),
    table.monitor-table tr.domain-row > td:nth-child(5),
    table.monitor-table tr.domain-row > td:nth-child(6),
    table.monitor-table tr.domain-row > td:nth-child(8) {
        display: none;
    }

    /* Cell 1 (Domain) — big teal FQDN on top, optional "N new" pill
       wraps inline. Already contains <strong> + optional unack pill. */
    table.monitor-table tr.domain-row > td:nth-child(1) {
        font-size: 1.05rem;
        line-height: 1.25;
        margin-bottom: 0.35rem;
    }
    table.monitor-table tr.domain-row > td:nth-child(1) strong {
        color: var(--accent, #38bdf8);
        font-weight: 600;
        word-break: break-all;
    }

    /* Cell 2 (Status) — shows below the FQDN as a row. The cell
       contains a status span (Owned/Watching) + optional muted
       div ("available!" or "taken"). Show them inline. */
    table.monitor-table tr.domain-row > td:nth-child(2) {
        font-size: 0.85rem;
        margin-bottom: 0.45rem;
    }
    table.monitor-table tr.domain-row > td:nth-child(2) > div {
        display: inline-block;
        margin-left: 0.5rem;
    }

    /* Cells 4 + 7 (Expires, Last Checked) — labeled rows. Use
       a ::before pseudo for the label so we don't have to touch
       the template. */
    table.monitor-table tr.domain-row > td:nth-child(4),
    table.monitor-table tr.domain-row > td:nth-child(7) {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 0.5rem;
        font-size: 0.85rem;
        padding: 0.15rem 0;
        color: var(--text);
    }
    table.monitor-table tr.domain-row > td:nth-child(4)::before {
        content: "EXPIRES";
        color: var(--muted);
        font-size: 0.7rem;
        font-weight: 600;
        letter-spacing: 0.05em;
    }
    table.monitor-table tr.domain-row > td:nth-child(7)::before {
        content: "LAST CHECKED";
        color: var(--muted);
        font-size: 0.7rem;
        font-weight: 600;
        letter-spacing: 0.05em;
    }

    /* Empty-state row */
    table.monitor-table tr td.domain-empty {
        display: block;
        background: transparent;
        border: 1px dashed var(--border);
        padding: 1.25rem 1rem;
        text-align: center;
        border-radius: 8px;
        color: var(--muted);
    }

    /* ── Phase 2: full-screen detail modal ─────────────────── */

    /* On phones the modal covers everything — including the UAT
       banner (z-index ~9000) and the sticky site header (z-1000).
       Desktop deliberately sits below the header (z-999) so the nav
       stays usable while the modal is open, but on mobile the modal
       IS the page, so float it above everything. */
    .domain-modal-backdrop {
        padding: 0 !important;
        align-items: stretch !important;
        justify-content: stretch !important;
        z-index: 9999 !important;
    }
    .domain-modal-backdrop.open .domain-modal {
        width: 100vw !important;
        max-width: 100vw !important;
        height: 100vh;
        max-height: 100vh;
        border-radius: 0;
        margin: 0;
        padding: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        box-shadow: none;
    }

    /* Sticky compact header */
    .domain-modal .domain-modal-header {
        padding: 0.65rem 0.85rem;
        margin: 0;
        flex: 0 0 auto;
        background: var(--panel);
        border-bottom: 1px solid var(--border);
        align-items: center;
    }
    .domain-modal .domain-modal-header > div:first-child {
        flex: 1 1 auto;
        min-width: 0;
    }
    .domain-modal .domain-modal-header h3 {
        font-size: 1rem !important;
        margin: 0 !important;
        line-height: 1.2;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .domain-modal #domain-detail-subtitle {
        font-size: 0.72rem !important;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Compact close button on mobile — drop the "Close" label,
       keep the ✕ glyph at a tappable size. We use font-size:0 on
       the button to hide the original text node, then render the
       glyph via ::before at a normal size. */
    .domain-modal .domain-modal-close {
        padding: 0.35rem 0.6rem !important;
        line-height: 1;
        font-size: 0 !important;
    }
    .domain-modal .domain-modal-close::before {
        content: "\2715";
        font-size: 1.15rem;
        line-height: 1;
    }

    /* Tab strip (JS-injected) */
    .domain-mobile-tabs {
        display: flex;
        overflow-x: auto;
        background: var(--panel-soft, var(--panel));
        border-bottom: 1px solid var(--border);
        flex: 0 0 auto;
        scrollbar-width: none;
    }
    .domain-mobile-tabs::-webkit-scrollbar {
        display: none;
    }
    .domain-mobile-tabs button {
        flex: 0 0 auto;
        background: transparent;
        border: none;
        color: var(--muted);
        padding: 0.75rem 1rem;
        font-size: 0.85rem;
        font-weight: 600;
        cursor: pointer;
        white-space: nowrap;
        border-bottom: 2px solid transparent;
        margin: 0;
        line-height: 1.2;
    }
    .domain-mobile-tabs button.active {
        color: var(--accent);
        border-bottom-color: var(--accent);
    }

    /* Scrollable body wrapper (JS-injected) */
    .domain-mobile-body {
        flex: 1 1 auto;
        overflow-y: auto;
        overflow-x: hidden;
        padding: 0.85rem 0.9rem 1rem;
        -webkit-overflow-scrolling: touch;
    }

    /* Tab-driven visibility — hide OUT-OF-tab sections so a section
       that's empty (e.g. no history yet) keeps its own messaging. */
    .domain-modal[data-active-tab="details"] [data-mobile-tab]:not([data-mobile-tab="details"]) { display: none !important; }
    .domain-modal[data-active-tab="history"] [data-mobile-tab]:not([data-mobile-tab="history"]) { display: none !important; }

    /* Collapse the 2-col details grid to 1-col so values don't squish.
       The edit form's grid is in the separate #domain-edit-backdrop
       modal — it's targeted below in its own block. */
    .domain-modal .domain-modal-grid {
        grid-template-columns: 1fr !important;
        gap: 0.75rem !important;
    }
    .domain-modal .domain-modal-grid > div[style*="grid-column"] {
        grid-column: 1 / -1 !important;
    }

    /* Edit modal — collapse its form grid to 1-col on mobile too. */
    #domain-edit-backdrop #dd-edit-form {
        grid-template-columns: 1fr !important;
        gap: 0.85rem !important;
    }
    #domain-edit-backdrop #dd-edit-form > div[style*="grid-column"] {
        grid-column: 1 / -1 !important;
    }

    /* History section: kill the desktop top-border + extra padding
       since the tab already provides visual separation. */
    .domain-modal .domain-history-section {
        margin-top: 0;
        border-top: none;
        padding-top: 0;
    }

    /* Strip the History section's leading h4 since the tab label
       already says "History". */
    .domain-modal [data-mobile-tab="history"] > h4 {
        display: none;
    }

    /* History table — let it horizontal-scroll inside its own
       wrapper so the columns don't squish below readability. */
    .domain-modal .domain-history-table {
        font-size: 0.78rem;
    }

    /* Static modal footer: pin to bottom, full-width buttons.
       Baseline styles for the row + button colors live outside the
       @media block; here we override layout for mobile. */
    .domain-modal-actions {
        flex: 0 0 auto;
        padding: 0.6rem 0.9rem;
        border-top: 1px solid var(--border);
        background: var(--panel);
        display: flex;
        gap: 0.6rem;
        justify-content: space-between;
        align-items: center;
    }
    .domain-modal-actions button {
        flex: 1 1 0;
        padding: 0.55rem 0.85rem;
        border-radius: 6px;
        border: 1px solid transparent;
        font-size: 0.9rem;
        font-weight: 600;
        cursor: pointer;
    }
    .domain-modal-actions .domain-action-edit {
        background: var(--accent, #38bdf8);
        color: #0b1220;
    }
    .domain-modal-actions .domain-action-delete {
        background: transparent;
        color: #f87171;
        border-color: #b91c1c;
    }
    .domain-modal-actions .domain-action-delete:active {
        background: rgba(220, 38, 38, 0.12);
    }
}

/* ───────────────────────────────────────────────────────────────
 * M5: Administration page + cross-page polish
 *
 * Stack-list cards on /administration (Company Summary + others)
 * use 3-column inline-flex layouts with fixed/min widths that
 * overflow the 375px viewport, so the children pile on top of
 * each other. Force a column layout below the breakpoint so each
 * sub-section gets the full width.
 *
 * Scoped to .stack-list .stack-item so it applies to admin,
 * dashboard, and support_tickets stack lists, but NOT to the
 * monitor detail modal's bare .stack-item-static (which lives
 * directly under .modal-card, not inside a stack-list).
 * ─────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
    .stack-list .stack-item,
    .stack-list .stack-item-static {
        flex-direction: column !important;
        align-items: stretch !important;
        gap: 0.75rem !important;
    }
    /* Children with inline flex/width styles need !important to
       collapse to full width on mobile. */
    .stack-list .stack-item > *,
    .stack-list .stack-item-static > * {
        flex: 0 0 auto !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
    }

    /* Company Summary logo box — center the 180x95 logo + the
       Upload/Fetch chips beneath it within the now-full-width row. */
    .stack-list .stack-item-static > div:first-child {
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    .stack-list .stack-item-static > div:first-child > div:first-child {
        margin: 0 auto;
    }

    /* API Tokens table (8 columns: Name, Prefix, Created by, Created,
       Last used, Last used IP, Status, Actions) overflows its card on
       narrow viewports because each column has its own th + content.
       Wrap the table in a horizontal scroller so the card stays bounded
       and the user can swipe to see the right-side columns. */
    #api-tokens-table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
        max-width: 100%;
        -webkit-overflow-scrolling: touch;
    }
    #api-tokens-table thead,
    #api-tokens-table tbody {
        display: table;
        width: max-content;
        min-width: 100%;
    }
    #api-tokens-table th,
    #api-tokens-table td {
        white-space: nowrap;
    }

    /* Hard-cap every standard modal to fit the viewport. Some admin
       modals (Edit User, Holiday Calendars) contain children with
       intrinsic widths (e.g. wide <select> options, long inline
       styles) that can push the card past the viewport edge before
       the browser computes the width:min(560px,100%) cap. Forcing
       max-width:100vw on the backdrop's child wins reliably. */
    .modal-backdrop > .modal-card {
        width: 100% !important;
        max-width: calc(100vw - 1.5rem) !important;
        margin: 0 auto !important;
    }
    /* Same for the holiday picker / edit user inner content — kill any
       intrinsic minimums on the form so it can't push the card wider. */
    #holiday-picker-modal .modal-card,
    #user-modal .modal-card {
        padding: 1rem;
    }
    #holiday-picker-modal select,
    #holiday-picker-modal .input,
    #user-modal select,
    #user-modal .input,
    #user-modal input[type="text"],
    #user-modal input[type="email"] {
        max-width: 100% !important;
        min-width: 0;
    }
}

/* ───────────────────────────────────────────────────────────────
 * M5c: PWA install banner + iOS Add to Home Screen modal
 *
 * Bottom-sticky banner included site-wide via _pwa_install_prompt.
 * Hidden by default (the [hidden] attribute); the script removes
 * it after a grace period or when beforeinstallprompt fires. The
 * .open class drives a slide-up transition.
 *
 * Z-index 9500 so it sits above page chrome (header z-1000,
 * nav-drawer z-2000) but below the full-screen detail modal on
 * the monitors/domains pages (z-9999), which would visually win
 * over a bottom banner anyway when present.
 * ─────────────────────────────────────────────────────────────── */

.pwa-install-banner[hidden] {
    display: none;
}
.pwa-install-banner {
    position: fixed;
    left: 50%;
    bottom: 12px;
    transform: translate(-50%, calc(100% + 16px));
    width: calc(100vw - 24px);
    max-width: 480px;
    z-index: 9500;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.85rem 1rem;
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
    transition: transform 0.22s ease-out, opacity 0.18s ease-out;
    opacity: 0;
}
.pwa-install-banner.open {
    transform: translate(-50%, 0);
    opacity: 1;
}
.pwa-install-icon {
    flex: 0 0 auto;
}
.pwa-install-icon img {
    display: block;
    border-radius: 10px;
}
.pwa-install-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    font-size: 0.85rem;
}
.pwa-install-text strong {
    font-size: 0.95rem;
}
.pwa-install-text span {
    color: var(--muted);
    font-size: 0.8rem;
}
.pwa-install-actions {
    flex: 0 0 auto;
    display: flex;
    gap: 0.4rem;
}
.pwa-install-actions button {
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    padding: 0.45rem 0.75rem;
    border-radius: 8px;
    font-size: 0.85rem;
    cursor: pointer;
    line-height: 1;
}
.pwa-install-actions .pwa-install-cta {
    background: var(--accent, #38bdf8);
    border-color: var(--accent, #38bdf8);
    color: #0b1220;
    font-weight: 700;
}

/* iOS Add-to-Home-Screen instructions overlay */
.pwa-ios-instructions[hidden] {
    display: none;
}
.pwa-ios-instructions {
    position: fixed;
    inset: 0;
    background: rgba(2, 6, 23, 0.78);
    z-index: 9999;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 1rem;
}
.pwa-ios-instructions-card {
    position: relative;
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.25rem 1.4rem 1.4rem;
    max-width: 480px;
    width: 100%;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}
.pwa-ios-instructions-card h3 {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
}
.pwa-ios-instructions-card ol {
    margin: 0;
    padding-left: 1.25rem;
    line-height: 1.5;
}
.pwa-ios-instructions-card li + li {
    margin-top: 0.45rem;
}
.pwa-ios-share-glyph {
    display: inline-block;
    padding: 0 0.25rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin: 0 0.1rem;
    font-size: 0.85em;
}
.pwa-ios-close {
    position: absolute;
    top: 0.4rem;
    right: 0.5rem;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.2rem 0.5rem;
}

/* ───────────────────────────────────────────────────────────────
 * M5d: Swipe-to-acknowledge — affordance for domain history rows
 *
 * The JS in domains.html attaches touch handlers to unacked rows
 * (.domain-history-row-unack); on swipe-left past 90px we ack via
 * the row's hidden form. Here we add a green "Acknowledge" reveal
 * behind the row that becomes visible as the row translates left,
 * plus an "armed" highlight when the swipe crosses the threshold.
 *
 * The reveal is a ::after pseudo on the tr positioned at the right
 * edge; the tr's own transform pushes its content left, exposing
 * the pseudo. We can't easily do that on a <tr> alone (table layout
 * quirks), so we only style the affordance — the pseudo sits on
 * top, anchored to the right of the row.
 * ─────────────────────────────────────────────────────────────── */

@media (max-width: 640px) {
    .domain-history-table .swipe-target {
        position: relative;
        z-index: 0;
        cursor: grab;
        touch-action: pan-y;
    }
    .domain-history-table .swipe-target > td {
        background: var(--surface, #0f172a);
    }
    /* Green "Acknowledge" backdrop reveal sits on the row's right
       edge so leftward swipe exposes it. */
    .domain-history-table .swipe-target::after {
        content: "\2713  Acknowledge";
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        width: 160px;
        background: #16a34a;
        color: #0b1220;
        font-weight: 700;
        font-size: 0.9rem;
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: -1;
        border-radius: 4px;
    }
    .domain-history-table .swipe-target.swipe-armed::after {
        background: #15803d;
    }
    .domain-history-table .swipe-target.swipe-armed > td {
        background: rgba(22, 163, 74, 0.12);
    }

    /* First-time swipe hint — fade in a tiny chevron on the right
       side of every unacked row so users notice it's swipable. */
    .domain-history-table .swipe-target > td:last-child::before {
        content: "\2190";       /* leftward arrow */
        display: inline-block;
        margin-right: 0.4rem;
        color: var(--muted);
        font-size: 0.85rem;
        opacity: 0.7;
    }

    /* Generic .desktop-only / .mobile-only helpers. Used by surfaces
       that need to hide a single sentence on mobile (e.g. the
       monitors-page subhead's "Drag the ☰ to reorder" hint, which
       refers to a drag handle that's only visible on desktop). */
    .desktop-only {
        display: none !important;
    }
    .mobile-only {
        display: inline !important;
    }
}

/* ───────────────────────────────────────────────────────────────
 * M5e: Pull-to-refresh indicator
 *
 * Small floating circular indicator that the JS in
 * _pull_to_refresh.html drags down from above the viewport as the
 * user pulls. Once past the threshold it flips colors ("armed")
 * and on release becomes a steady "refreshing" state while we
 * reload the page.
 * ─────────────────────────────────────────────────────────────── */

.ptr-indicator {
    position: fixed;
    top: 0;
    left: 50%;
    transform: translate(-50%, -100%) rotate(0deg);
    z-index: 9700;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--panel);
    border: 1px solid var(--border);
    color: var(--accent, #38bdf8);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    pointer-events: none;
    transition: transform 0.22s ease-out, opacity 0.18s ease-out;
}
.ptr-indicator.armed {
    background: var(--accent, #38bdf8);
    color: #0b1220;
    border-color: var(--accent, #38bdf8);
}
.ptr-indicator.refreshing svg {
    animation: ptr-spin 0.9s linear infinite;
}
@keyframes ptr-spin {
    from { transform: rotate(0deg);   }
    to   { transform: rotate(360deg); }
}

/* ───────────────────────────────────────────────────────────────
 * M5f: Internal-admin pages mobile (logs, resellers, health)
 *
 * These pages all share the same shape: big card → wide table.
 * Card layouts don't make sense (admins need to scan many cols at
 * once), so the strategy is "scroll inside the card" rather than
 * "stack". Apply the horizontal-scroll-on-mobile pattern to every
 * admin table and let users swipe to see the right side.
 *
 * Below 1024px (tablet portrait):
 *   - .data-table (admin_resellers.html — 3 tables)
 *   - .logs-auth-table (logs_auth.html)
 *   - #public-poller-table, #private-poller-table,
 *     #services-table, #top-proc-table, #backup-history-table,
 *     .timer-table (health.html)
 *
 * Below 640px:
 *   - .logs-auth-filter collapses its 2-col grid to 1-col
 *   - health.html's inline 2-col grids collapse
 *   - .settings-tabs (the 8-tab strip on health) becomes a
 *     horizontal swipe strip instead of wrapping/overflowing
 * ─────────────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
    .data-table,
    .logs-auth-table,
    #public-poller-table,
    #private-poller-table,
    #services-table,
    #top-proc-table,
    #backup-history-table,
    .timer-table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
        max-width: 100%;
        -webkit-overflow-scrolling: touch;
    }
    .data-table thead,        .data-table tbody,
    .logs-auth-table thead,   .logs-auth-table tbody,
    #public-poller-table thead,  #public-poller-table tbody,
    #private-poller-table thead, #private-poller-table tbody,
    #services-table thead,    #services-table tbody,
    #top-proc-table thead,    #top-proc-table tbody,
    #backup-history-table thead, #backup-history-table tbody,
    .timer-table thead,       .timer-table tbody {
        display: table;
        width: max-content;
        min-width: 100%;
    }
    .data-table th, .data-table td,
    .logs-auth-table th, .logs-auth-table td,
    .timer-table th, .timer-table td {
        white-space: nowrap;
    }
}

@media (max-width: 640px) {
    /* Logs filter grid collapses to 1-col so the date pickers + text
       fields stop fighting each other on phones. */
    .logs-auth-filter {
        grid-template-columns: 1fr !important;
    }
    .logs-auth-filter .row-full {
        grid-column: 1 / -1 !important;
    }

    /* Health page: top header row of System Health + Auto-refresh
       controls stack vertically; refresh control row wraps cleanly. */
    .health-refresh {
        flex-wrap: wrap;
        gap: 0.4rem;
    }

    /* Settings tabs (8 tabs on the health page) horizontal-scroll
       instead of wrapping. Hide the scrollbar; users swipe. */
    .settings-tabs {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }
    .settings-tabs::-webkit-scrollbar {
        display: none;
    }
    .settings-tab-btn {
        flex: 0 0 auto;
        white-space: nowrap;
    }

    /* Inline 2-col grids inside health-panels (db, app/server
       sections both use grid-template-columns:1fr 1fr) collapse to
       1-col. The auto-fit/minmax grids in the same file already
       collapse on their own. */
    .health-panel > div[style*="grid-template-columns:1fr 1fr"],
    .health-panel > div[style*="grid-template-columns: 1fr 1fr"] {
        grid-template-columns: 1fr !important;
    }

    /* kv-grid (max-content 1fr) overflows when DB connection strings
       or process commands are long. Keep the 2-col, but let it scroll
       horizontally inside its parent so the page itself doesn't grow. */
    .kv-grid {
        overflow-x: auto;
        max-width: 100%;
    }
}

/* ───────────────────────────────────────────────────────────────
 * M5i: Safe-area-inset for full-screen mobile surfaces (iOS PWA)
 *
 * In iOS standalone (home-screen launched) mode the status bar
 * (clock / Dynamic Island) is set to black-translucent in
 * _pwa_head.html, so it overlays the page rather than pushing
 * content down. The site-header is already handled. This block
 * pads every other surface that goes flush to the viewport edge
 * so its first row of content (modal title, ack glyph, footer
 * buttons) clears the status bar / home indicator.
 *
 * env(safe-area-inset-top/bottom) is 0 in regular browser tabs,
 * ~44–59px in standalone on phones with notch/Dynamic Island.
 * Variables are defined in the M1 :root block as --safe-top etc.
 * ─────────────────────────────────────────────────────────────── */

@media (max-width: 640px) {
    /* Monitor detail modal — pad the sticky header so the FQDN /
       close X clear the status bar. modal-actions footer also
       gets the bottom inset for the home indicator. */
    #monitor-detail-modal .modal-header {
        padding-top: calc(0.65rem + var(--safe-top));
    }
    #monitor-detail-modal .modal-actions {
        padding-bottom: calc(0.6rem + var(--safe-bottom));
    }

    /* Domain detail modal — same treatment. */
    .domain-modal-backdrop.open .domain-modal .domain-modal-header {
        padding-top: calc(0.65rem + var(--safe-top));
    }
    .domain-modal-backdrop.open .domain-modal .domain-modal-actions {
        padding-bottom: calc(0.6rem + var(--safe-bottom));
    }
}

/* ── Pull-to-refresh indicator: drop its origin below the iOS
   status bar so the resting "refreshing" position sits in the
   visible viewport, not under the clock. The JS computes its
   transform from top:0; bumping the rule's `top` to safe-top
   moves the whole sliding range down without touching the JS. */
.ptr-indicator {
    top: var(--safe-top);
}

/* ── PWA install banner: clear the iOS home indicator at the
   bottom so it doesn't sit underneath the slim swipe gesture
   bar. Adds to the existing 12px breathing room. */
.pwa-install-banner {
    bottom: calc(12px + var(--safe-bottom));
}

/* ── PWA "Add to Home Screen" instructions: the card is
   anchored to the bottom of the overlay (align-items:flex-end).
   Pad its container so the card itself sits above the home
   indicator. */
.pwa-ios-instructions {
    padding-bottom: calc(1rem + var(--safe-bottom));
}

/* ── Mobile Filters toggle button ─────────────────────────────
   Hidden on desktop; on mobile it sits above the filter form and
   reveals/hides it. JS in monitors.html adds the button + wires
   the data-mobile-filters attribute. */
.mobile-filters-toggle {
    display: none;
}
@media (max-width: 640px) {
    .mobile-filters-toggle {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        padding: 0.5rem 0.85rem;
        background: var(--panel-soft);
        border: 1px solid var(--border);
        border-radius: 8px;
        color: var(--text);
        font-size: 0.9rem;
        margin: 0.5rem 0 0.75rem;
        cursor: pointer;
    }
    .mobile-filters-toggle[aria-expanded="true"] {
        background: rgba(56, 189, 248, 0.08);
        border-color: var(--accent);
    }
    /* When filters are active, show a small dot indicator */
    .mobile-filters-toggle .mobile-filters-active-dot {
        display: inline-block;
        width: 8px;
        height: 8px;
        background: var(--accent);
        border-radius: 50%;
    }
}
