:root {
    --vp-purple: #2563eb;
    --vp-blue: #f97316;
    --vp-gradient: linear-gradient(90deg, var(--vp-purple), var(--vp-blue));
    --vp-bg: #f6f9fc;
    --vp-card: #ffffff;
    --vp-text: #1f2430;
    --vp-muted: #6b7280;
    --vp-border: #e6eaf2;
    --vp-danger: #ef4444;
    --vp-success: #22c55e;
}

* { box-sizing: border-box; }

body {
    background: var(--vp-bg);
    color: var(--vp-text);
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    margin: 0;
    padding-bottom: env(safe-area-inset-bottom);
    -webkit-tap-highlight-color: transparent;
}

.vp-header {
    background: #fff;
    border-bottom: 1px solid var(--vp-border);
    padding: 10px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 50;
}
.vp-header img { height: 34px; }
.vp-header .vp-user { font-size: 13px; color: var(--vp-muted); text-align: right; display: flex; align-items: center; gap: 10px; }
.vp-user-role { color: var(--vp-muted); font-weight: 400; }

.vp-account-menu { position: relative; }
.vp-account-trigger {
    width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--vp-border); background: #fff;
    display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 16px; color: var(--vp-muted); padding: 0;
}
.vp-account-trigger:hover, .vp-account-menu.open .vp-account-trigger { color: var(--vp-purple); border-color: var(--vp-purple); }
.vp-account-dropdown {
    display: none; position: absolute; top: calc(100% + 8px); right: 0; background: #fff; border: 1px solid var(--vp-border);
    border-radius: 10px; box-shadow: 0 14px 32px rgba(16,24,40,.16); padding: 6px; min-width: 170px; z-index: 60;
}
.vp-account-menu.open .vp-account-dropdown { display: block; }
.vp-account-dropdown a { display: block; padding: 9px 12px; border-radius: 7px; color: var(--vp-text); text-decoration: none; font-size: 13.5px; font-weight: 500; text-align: left; }
.vp-account-dropdown a:hover { background: #f4f6fc; color: var(--vp-purple); }
.vp-account-dropdown a.danger:hover { background: #fce8e6; color: var(--vp-danger); }

.vp-container { max-width: 720px; margin: 0 auto; padding: 16px; }
.vp-container.wide { max-width: 1100px; }

.vp-card {
    background: var(--vp-card);
    border: 1px solid var(--vp-border);
    border-radius: 10px;
    padding: 18px;
    margin-bottom: 14px;
    box-shadow: none;
}

.vp-login-wrap {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px 16px;
}
.vp-login-wrap img.logo { max-width: 260px; margin-bottom: 24px; }
.vp-login-card { width: 100%; max-width: 380px; }

h1, h2, h3 { color: var(--vp-text); }
.vp-title { font-size: 16px; font-weight: 700; margin: 0 0 14px; }
.vp-page-title { font-size: 20px; font-weight: 800; margin: 0; letter-spacing: -.01em; }
.vp-card-title { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--vp-muted); margin: 0 0 12px; }

label { display: block; font-weight: 600; font-size: 13px; margin: 12px 0 4px; color: var(--vp-text); }
label.required::after { content: " *"; color: var(--vp-danger); font-weight: 700; }
input[type=text], input[type=password], input[type=tel], input[type=number], input[type=date], select, textarea {
    width: 100%;
    padding: 12px;
    font-size: 16px; /* prevents iOS zoom on focus */
    border: 1px solid var(--vp-border);
    border-radius: 8px;
    background: #fff;
    color: var(--vp-text);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--vp-purple); outline-offset: 1px; }

.vp-btn {
    display: inline-block;
    width: 100%;
    padding: 14px;
    font-size: 16px;
    font-weight: 700;
    border: none;
    border-radius: 10px;
    background: var(--vp-gradient);
    color: #fff;
    cursor: pointer;
    text-align: center;
    text-decoration: none;
    box-shadow: 0 1px 2px rgba(16,24,40,.06);
    transition: transform .12s ease, box-shadow .12s ease, opacity .12s ease, background .12s ease;
}
.vp-btn:hover { box-shadow: 0 4px 10px rgba(37,99,235,.18); transform: translateY(-1px); }
.vp-btn:active { opacity: 0.85; transform: translateY(0); }
.vp-btn.secondary { background: #fff; color: var(--vp-purple); border: 1.5px solid var(--vp-purple); box-shadow: none; }
.vp-btn.secondary:hover { background: #eef4ff; box-shadow: none; }
.vp-btn.danger { background: var(--vp-danger); }
.vp-btn.small { width: auto; padding: 9px 16px; font-size: 13.5px; border-radius: 8px; font-weight: 600; }
.vp-btn.ghost { width: auto; background: transparent; color: var(--vp-purple); border: 1.5px dashed var(--vp-border); box-shadow: none; padding: 9px 16px; font-size: 13.5px; font-weight: 600; }
.vp-btn.ghost:hover { border-color: var(--vp-purple); background: #f8faff; box-shadow: none; }

.vp-icon-btn { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; border-radius: 7px; cursor: pointer; font-size: 15px; padding: 0; }
.vp-icon-btn.danger { color: var(--vp-danger); }
.vp-icon-btn.danger:hover { background: #fce8e6; }

.vp-btn-row { display: flex; gap: 10px; margin-top: 18px; }
.vp-btn-row .vp-btn { flex: 1; }

/* Compact, left-aligned action row for desktop admin forms (Item Master / Set Target / etc.) —
   distinct from .vp-btn-row, which is the full-width stretched style used on mobile survey forms. */
.vp-actions-row { display: flex; align-items: center; gap: 10px; margin-top: 16px; flex-wrap: wrap; }

.vp-steps { display: flex; gap: 6px; margin-bottom: 18px; }
.vp-steps span { flex: 1; height: 5px; border-radius: 3px; background: var(--vp-border); }
.vp-steps span.active { background: var(--vp-gradient); }
.vp-steps span.done { background: var(--vp-success); }

.vp-radio-group { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 6px; }
.vp-radio-group label { display: flex; align-items: center; gap: 6px; font-weight: 500; font-size: 14px; margin: 0; }
.vp-radio-group input { width: auto; }

/* :not([hidden]) matters here — a plain `.vp-range-row { display:flex }` would have equal specificity to
   the browser's own `[hidden]{display:none}` rule, and author styles win ties over UA defaults, so the
   row would stay visible even with the hidden attribute set (exactly the "specific day mode still shows
   the date range fields" bug this was written to fix). Scoping the flex rule to :not([hidden]) means it
   simply doesn't apply while hidden, so the UA rule is free to hide the row with nothing to override it. */
.vp-range-row:not([hidden]) { display: flex; gap: 14px; }

.vp-checkbox-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px,1fr)); gap: 6px 14px; margin-top: 6px; }
.vp-checkbox-grid label { display: flex; align-items: center; gap: 6px; font-weight: 500; font-size: 13.5px; margin: 0; }
.vp-checkbox-grid input { width: auto; }

.vp-ga-summary { display: inline-flex; flex-wrap: wrap; gap: 4px; cursor: pointer; }
.vp-ga-summary .vp-chip { background: #eef2ff; color: var(--vp-purple); padding: 3px 9px; font-size: 11.5px; font-weight: 700; }
.vp-ga-summary .vp-chip.none { background: #f4f6fa; color: var(--vp-muted); font-weight: 500; }
details.vp-ga-edit { position: relative; }
details.vp-ga-edit summary { list-style: none; padding: 8px 12px; border: 1px solid var(--vp-border); border-radius: 8px; background: #fff; min-height: 20px; display: flex; align-items: center; }
details.vp-ga-edit summary::-webkit-details-marker { display: none; }
details.vp-ga-edit[open] .vp-ga-summary { display: none; }
details.vp-ga-edit[open] summary::after { content: "Select sites…"; color: var(--vp-muted); font-size: 13px; }
/* Floats as a real popover instead of pushing the rest of the filter row/page down when opened. Single
   column, not a multi-column grid — a 2-column layout let long names ("IGL - GGN - Vision") wrap onto
   several lines while the checkbox stayed vertically centered across the wrap, making it look like the
   checkbox was sitting in the middle of the word. One row per site avoids that entirely. Width is
   max-content (not a fixed px) so it always grows to fit the longest name rather than clipping it —
   a fixed width + nowrap had been silently cutting names off. */
details.vp-ga-edit .vp-checkbox-grid { position: absolute; top: calc(100% + 4px); left: 0; z-index: 30; display: block; width: max-content; min-width: 220px; max-width: min(340px, calc(100vw - 32px)); max-height: 280px; overflow-y: auto; background: #fff; border: 1px solid var(--vp-border); border-radius: 8px; padding: 6px; margin-top: 0; box-shadow: 0 10px 30px rgba(20,20,40,.14); }
details.vp-ga-edit .vp-checkbox-grid label { padding: 7px 8px; border-radius: 6px; white-space: nowrap; }
details.vp-ga-edit .vp-checkbox-grid label:hover { background: #f4f6fa; }
details.vp-ga-edit .vp-ga-select-all { display: flex; align-items: center; gap: 6px; padding: 7px 8px; margin: 0 0 4px; border-bottom: 1px solid var(--vp-border); font-weight: 700; font-size: 13.5px; cursor: pointer; }

/* Mobile: a fixed-position centered sheet instead of an anchored popover — an absolute popover anchored
   to a narrow "GA Sites" label can run off the right edge of a 360-390px phone screen. */
@media (max-width: 768px) {
    details.vp-ga-edit .vp-checkbox-grid { position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); width: calc(100vw - 40px); max-width: calc(100vw - 40px); max-height: 70vh; }
}

.vp-infra-row { border-bottom: 1px solid var(--vp-border); padding: 12px 0; }
.vp-infra-row:last-child { border-bottom: none; }
.vp-infra-row .cp-name { font-weight: 700; font-size: 14px; margin-bottom: 6px; }

.vp-alert { padding: 12px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 14px; }
.vp-alert.success { background: #e6f4ea; color: var(--vp-success); }
.vp-alert.error { background: #fce8e6; color: var(--vp-danger); }
.vp-alert.warn { background: #fff4e5; color: #b5651d; }

table.vp-table { width: 100%; border-collapse: collapse; font-size: 14px; line-height: 1.5; }
table.vp-table th, table.vp-table td { padding: 14px 16px; border-bottom: 1px solid var(--vp-border); text-align: left; vertical-align: middle; }
table.vp-table th { background: #f8f8fc; font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--vp-muted); font-weight: 700; }
table.vp-table tbody tr:hover, table.vp-table tr:hover { background: #fafbfd; }
table.vp-table td input[type=number], table.vp-table td input[type=text] { padding: 9px 10px; font-size: 14px; }
/* Compact variant — the Value Share period/total tables list every category/item and don't need the
   default table's generous 14px padding; this cuts row height noticeably without losing legibility. */
table.vp-table.compact { font-size: 12.5px; line-height: 1.3; }
table.vp-table.compact th, table.vp-table.compact td { padding: 6px 8px; }
table.vp-table.compact .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
/* GA Sites table: short fixed-format values (dates, ID, status, created-date) never wrap — nothing about
   them benefits from wrapping and it used to happen only when a wide sibling column squeezed them. FOA/LOA
   No. are the opposite case — a real (and sometimes very long, e.g. "BANDR/GAIL/71150/EOI-01/LMC/...")
   reference string that MUST be allowed to wrap, capped to a max-width so it wraps onto its own lines
   rather than forcing the whole table wider than the viewport (which is what caused the horizontal
   scrollbar) — Name gets the same treatment for the same reason. */
table.vp-table.nowrap-dates th, table.vp-table.nowrap-dates td { white-space: nowrap; }
table.vp-table.nowrap-dates td:nth-child(2) { white-space: normal; max-width: 200px; overflow-wrap: break-word; }
table.vp-table.nowrap-dates td:nth-child(3), table.vp-table.nowrap-dates td:nth-child(4) { white-space: normal; max-width: 220px; overflow-wrap: break-word; word-break: break-word; }

/* Native <dialog> used for confirmation prompts that need real content (e.g. admin/users.php's delete
   preview) rather than a plain JS confirm(). Backdrop + centering come free from the element itself. */
.vp-dialog { border: none; border-radius: 14px; padding: 20px 22px; width: min(480px, calc(100vw - 40px)); box-shadow: 0 20px 60px rgba(20,20,40,.25); }
.vp-dialog::backdrop { background: rgba(20,20,30,.45); }
.vp-dialog .vp-title { margin-bottom: 10px; }
table.vp-table.inner-detail, table.vp-table.inner { font-size: 13px; margin: 4px 0; }
table.vp-table.inner-detail th, table.vp-table.inner-detail td,
table.vp-table.inner th, table.vp-table.inner td { padding: 10px 14px; }
.vp-table-wrap { overflow-x: auto; }
.vp-table-wrap + .vp-table-wrap, .vp-table-wrap { margin-bottom: 6px; }

.vp-lock-badge { display: inline-flex; align-items: center; gap: 4px; margin-top: 6px; padding: 3px 9px; border-radius: 10px; background: #fff4e5; color: #b5651d; font-size: 11.5px; font-weight: 600; }

/* Card-based DPR entry/view layout — replaces a wide multi-column table (which forced horizontal
   scrolling) with cards that wrap naturally at any width, on both desktop and mobile. */
.vp-dpr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; margin-top: 14px; }
.vp-dpr-card { background: #fff; border: 1px solid var(--vp-border); border-radius: 12px; padding: 16px; display: flex; flex-direction: column; gap: 12px; transition: border-color .15s ease, box-shadow .15s ease; }
.vp-dpr-card:hover { border-color: #d7deee; box-shadow: 0 2px 10px rgba(16,24,40,.05); }
.vp-dpr-card.is-locked { background: #fbfbfd; }
.vp-dpr-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.vp-dpr-card-title { font-weight: 700; font-size: 14.5px; line-height: 1.35; }
.vp-dpr-card-uom { font-size: 10.5px; color: var(--vp-muted); background: #f4f6fc; padding: 3px 9px; border-radius: 6px; white-space: nowrap; font-weight: 600; }
.vp-dpr-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr)); gap: 10px; padding: 10px 0; border-top: 1px solid var(--vp-border); border-bottom: 1px solid var(--vp-border); }
.vp-dpr-stats .stat { text-align: center; }
.vp-dpr-stats .stat .lbl { display: block; font-size: 9.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--vp-muted); margin-bottom: 3px; font-weight: 700; }
.vp-dpr-stats .stat .val { font-weight: 700; font-size: 14px; }
.vp-dpr-stats .stat .val.negative { color: var(--vp-danger); }
.vp-dpr-input-row { display: flex; gap: 8px; align-items: center; }
.vp-dpr-input-row input[type=number] { flex: 1; margin: 0; }
.vp-dpr-input-row .vp-icon-btn { flex-shrink: 0; background: #fce8e6; }

/* Row variant of the same card language — one item per row instead of a card grid, for denser lists.
   The row itself wraps its own contents at narrow widths (never the page), so it never needs horizontal
   scroll either. */
.vp-dpr-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
/* .vp-dpr-row is a vertical stack: the name+qty-input "main" line always stays on one line (it never
   competes with the stat numbers for space), then the Target/Previous/Cumulative/Balance stats — when
   shown at all, see .vp-stats-extra below — get their own full-width line underneath. This is what
   stops the qty input from getting shoved onto a 3rd/4th line on a narrow phone: nothing upstream of it
   can force a wrap anymore. */
.vp-dpr-row { display: flex; flex-direction: column; gap: 8px; background: #fff; border: 1px solid var(--vp-border); border-radius: 10px; padding: 12px 16px; transition: border-color .15s ease, box-shadow .15s ease; }
.vp-dpr-row:hover { border-color: #d7deee; box-shadow: 0 2px 8px rgba(16,24,40,.05); }
.vp-dpr-row.is-locked { background: #fbfbfd; }
.vp-dpr-row-main { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.vp-dpr-row-info { flex: 1 1 auto; min-width: 0; }
.vp-dpr-row-name { font-weight: 700; font-size: 14px; overflow-wrap: break-word; }
.vp-dpr-row-uom { font-size: 10.5px; color: var(--vp-muted); background: #f4f6fc; padding: 2px 8px; border-radius: 6px; display: inline-block; margin-top: 3px; font-weight: 600; }
.vp-dpr-row-stats { display: flex; gap: 20px; flex-wrap: wrap; padding-top: 8px; border-top: 1px dashed var(--vp-border); }
.vp-dpr-row-stats .stat { text-align: center; min-width: 54px; }
.vp-dpr-row-stats .stat .lbl { display: block; font-size: 9.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--vp-muted); margin-bottom: 2px; font-weight: 700; }
.vp-dpr-row-stats .stat .val { font-weight: 700; font-size: 13.5px; }
.vp-dpr-row-stats .stat .val.negative { color: var(--vp-danger); }
.vp-dpr-row-input { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.vp-dpr-row-input input[type=number] { width: 90px; margin: 0; }
/* DPR Adjustment page: an item/field with a saved (non-zero) adjustment gets a colored left edge so it's
   obvious at a glance which rows have already been corrected, scrolling through a long list. */
.vp-dpr-row.has-adjustment { border-left: 3px solid var(--vp-purple); }
/* Target/Previous/Cumulative/Balance stat row (Daily Entry grids): distinct colors per stat so they read
   at a glance instead of all rendering the same default black — Target is the goal (neutral/muted-strong),
   Previous is history (blue, matches the app's primary accent), Cumulative is today's running total
   (purple), Balance is the actionable one (green when on track, red when behind — .negative already
   existed for this; the new default green here is what shows when it's NOT negative). */
.vp-dpr-row-stats .stat .val.target-val { color: var(--vp-muted); }
.vp-dpr-row-stats .stat .val.prev-val { color: var(--vp-blue); }
.vp-dpr-row-stats .stat .val.cum-val { color: var(--vp-purple); }
.vp-dpr-row-stats .stat .val.bal-val { color: var(--vp-success); }
.vp-dpr-row-stats .stat .val.bal-val.negative { color: var(--vp-danger); }
.vp-dpr-row-input input[type=text] { margin: 0; }

/* Target/Previous/Cumulative/Balance add real value when double-checking a number, but most days a
   site incharge is just punching in today's qty for item after item — showing 4 extra numbers on every
   single row, every time, isn't needed for that and was the main cause of tall, slow-scrolling entry
   pages. Collapsed by default; the checkbox above the grid (persisted per-device via localStorage)
   reveals them again on demand. */
.vp-stats-extra { display: none; }
.show-stats .vp-stats-extra { display: flex; }
.vp-stats-toggle-label { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13px; cursor: pointer; margin: 4px 0 12px; }

/* Message Formatter's block list — one row on desktop (handle + badge + label on the left, move/remove
   controls pinned to the right, exactly like before), but a true flex-wrap row rather than a fixed
   single line: the "main" group (handle+badge+label) and the "controls" group are each their own flex
   item, so on a screen too narrow to fit both, .vp-msg-block-controls wraps to its own line below
   instead of forcing horizontal overflow — desktop stays compact, mobile stays usable, same markup. */
.vp-msg-block { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 12px; border: 1px solid var(--vp-border); border-radius: 8px; margin-bottom: 6px; background: #fff; }
.vp-msg-block-main { display: flex; align-items: center; gap: 10px; flex: 1 1 220px; min-width: 0; }
.vp-msg-block-handle { color: var(--vp-muted); font-size: 16px; flex: 0 0 auto; }
.vp-msg-block-type { flex: 0 0 auto; }
.vp-msg-block-label { font-weight: 600; flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.vp-msg-block-controls { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; margin-left: auto; }
.vp-msg-block-moves { display: flex; gap: 4px; }
@media (max-width: 768px) {
    /* Once .vp-msg-block-controls wraps to its own line, margin-left:auto would strand it alone at
       the right edge — spread it across the full width instead so move buttons and Remove sit at
       opposite ends of a normal-looking row. */
    .vp-msg-block-controls { flex-basis: 100%; margin-left: 0; justify-content: space-between; }
}

/* Collapsible filter panel (Survey Map's 9-control filter bar was the worst offender for permanently
   eating vertical space on a phone before this — starts collapsed, expands on tap, shows how many
   filters are currently active right on the summary so you're not left guessing what's applied while
   it's closed). */
.vp-filter-disclosure summary { cursor: pointer; font-weight: 700; font-size: 14px; padding: 4px 0; list-style: none; }
.vp-filter-disclosure summary::-webkit-details-marker { display: none; }
.vp-filter-disclosure summary::before { content: '▸ '; display: inline-block; transition: transform .15s ease; }
.vp-filter-disclosure[open] summary::before { transform: rotate(90deg); }
.vp-filter-disclosure .vp-filters { margin-top: 12px; }

/* Day-summary cards — used by Entry History (admin) and My Progress Report (surveyor). */
.vp-day-card { background: #fff; border: 1px solid var(--vp-border); border-radius: 12px; padding: 16px 18px; margin-bottom: 12px; }
.vp-day-card-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
.vp-day-card-site { font-weight: 700; font-size: 15px; }
.vp-day-card-date { color: var(--vp-muted); font-size: 12.5px; margin-top: 2px; }
.vp-day-card-total { text-align: right; }
.vp-day-card-total .num { font-weight: 800; font-size: 18px; color: var(--vp-purple); }
.vp-day-card-total .lbl { font-size: 10.5px; color: var(--vp-muted); text-transform: uppercase; letter-spacing: .04em; }
.vp-day-card details { margin-top: 12px; }
.vp-day-card summary { cursor: pointer; color: var(--vp-purple); font-weight: 600; font-size: 12.5px; list-style: none; }
.vp-day-card summary::-webkit-details-marker { display: none; }
.vp-day-card summary::before { content: "▸ "; font-size: 10px; }
.vp-day-card details[open] summary::before { content: "▾ "; }

.vp-badge { display: inline-block; padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; }
.vp-badge.active { background: #e6f4ea; color: var(--vp-success); }
.vp-badge.deleted { background: #fce8e6; color: var(--vp-danger); }

.vp-img-thumb { width: 90px; height: 90px; object-fit: cover; border-radius: 8px; border: 1px solid var(--vp-border); }

.vp-capture-buttons { display: flex; gap: 10px; margin-bottom: 14px; }
.vp-capture-buttons label.vp-btn { display: flex; align-items: center; justify-content: center; }
.vp-capture-buttons input[type=file] { display: none; }

.vp-image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(90px,1fr)); gap: 10px; margin-top: 10px; }
.vp-image-grid .item { position: relative; }
.vp-image-grid .item img { width: 100%; height: 90px; object-fit: cover; border-radius: 8px; }
.vp-image-grid .item .remove { position: absolute; top: -6px; right: -6px; background: var(--vp-danger); color: #fff; border-radius: 50%; width: 22px; height: 22px; border: none; font-size: 14px; line-height: 1; }

nav.vp-tabs {
    display: flex; align-items: center; flex-wrap: wrap; gap: 2px;
    background: #fff; border-bottom: 1px solid var(--vp-border);
    box-shadow: 0 1px 4px rgba(16,24,40,.04);
    padding: 0 20px; margin: 0 0 22px;
}

/* Settings tab strip — sits inside the page's own container, one row of pill-tabs linking between the
   pages consolidated under Settings (App Settings, Target Analytics, GA Sites, Users, Recycle Bin, Login
   History, Export, Backup). Wraps onto a second row rather than scrolling horizontally — no axis of this
   page should ever scroll sideways, tabs included. */
.vp-settings-tabs {
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    background: #fff; border: 1px solid var(--vp-border); border-radius: 12px;
    padding: 6px; margin: 0 0 22px; box-shadow: 0 1px 4px rgba(16,24,40,.04);
}
.vp-settings-tabs a {
    display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
    padding: 8px 14px; border-radius: 8px; font-size: 13px; font-weight: 600;
    color: var(--vp-muted); text-decoration: none; white-space: nowrap; transition: background .15s ease, color .15s ease;
}
.vp-settings-tabs a .ico { font-size: 13px; }
.vp-settings-tabs a:hover { background: #f4f6fc; color: var(--vp-purple); }
.vp-settings-tabs a.active { background: var(--vp-gradient); color: #fff; }

/* App Settings — feature-toggle cards with a real switch control (not a bare checkbox) and a P&L rate
   grid with each field in its own labeled tile, replacing the old plain stacked-checkbox/inline-grid list. */
.vp-toggle-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(480px,1fr)); gap: 12px; }
.vp-toggle-card {
    display: flex; align-items: flex-start; gap: 14px; cursor: pointer;
    border: 1px solid var(--vp-border); border-radius: 12px; padding: 14px 16px;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.vp-toggle-card:hover { border-color: #d7deee; box-shadow: 0 2px 8px rgba(16,24,40,.05); }
.vp-toggle-card-text { flex: 1 1 auto; min-width: 0; }
.vp-toggle-card-title { font-weight: 700; font-size: 14px; display: flex; align-items: center; gap: 8px; }
.vp-toggle-card-title .ico { font-size: 15px; }
.vp-toggle-card-text p { color: var(--vp-muted); font-size: 12px; line-height: 1.5; margin: 6px 0 0; }
.vp-switch { position: relative; flex: 0 0 auto; width: 42px; height: 24px; }
.vp-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.vp-switch-track { position: absolute; inset: 0; background: #d5d9e3; border-radius: 999px; transition: background .15s ease; }
.vp-switch-track::before { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; background: #fff; border-radius: 50%; transition: transform .15s ease; box-shadow: 0 1px 3px rgba(16,24,40,.25); }
.vp-switch input:checked + .vp-switch-track { background: var(--vp-gradient); }
.vp-switch input:checked + .vp-switch-track::before { transform: translateX(18px); }
.vp-switch input:focus-visible + .vp-switch-track { outline: 2px solid var(--vp-purple); outline-offset: 2px; }

.vp-rate-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px,1fr)); gap: 12px; }
.vp-rate-field { border: 1px solid var(--vp-border); border-radius: 10px; padding: 10px 12px; }
.vp-rate-field label { font-size: 11.5px; font-weight: 700; color: var(--vp-muted); margin-bottom: 4px; }
.vp-rate-field input { margin: 0; }
nav.vp-tabs a {
    padding: 16px 13px; white-space: nowrap; text-decoration: none; color: #374151;
    font-weight: 700; font-size: 12px; letter-spacing: .05em; text-transform: uppercase;
    border-bottom: 2px solid transparent; transition: color .15s ease, border-color .15s ease;
}
nav.vp-tabs a:hover { color: var(--vp-purple); }
nav.vp-tabs a.active { color: var(--vp-purple); border-bottom-color: var(--vp-purple); }

.vp-nav-dropdown { position: relative; }
.vp-nav-dropdown > .vp-nav-trigger {
    display: flex; align-items: center; gap: 6px; padding: 16px 13px; white-space: nowrap;
    background: none; border: none; cursor: pointer; color: #374151; font-weight: 700;
    font-size: 12px; letter-spacing: .05em; text-transform: uppercase; font-family: inherit;
    border-bottom: 2px solid transparent; transition: color .15s ease, border-color .15s ease;
}
.vp-nav-dropdown > .vp-nav-trigger:hover { color: var(--vp-purple); }
.vp-nav-dropdown > .vp-nav-trigger .caret { font-size: 9px; transition: transform .15s ease; }
.vp-nav-dropdown.active > .vp-nav-trigger,
.vp-nav-dropdown.open > .vp-nav-trigger { color: var(--vp-purple); border-bottom-color: var(--vp-purple); }
.vp-nav-dropdown.open > .vp-nav-trigger .caret { transform: rotate(180deg); }
.vp-nav-dropdown .vp-nav-menu {
    display: none; position: absolute; top: 100%; left: 0; min-width: 210px;
    background: #fff; border: 1px solid var(--vp-border); border-radius: 12px;
    box-shadow: 0 14px 32px rgba(16,24,40,.16); padding: 8px; z-index: 45;
}
.vp-nav-dropdown.open .vp-nav-menu { display: block; }
.vp-nav-dropdown .vp-nav-menu a {
    display: block; padding: 10px 14px; border-radius: 8px; font-size: 13.5px; font-weight: 600;
    letter-spacing: normal; text-transform: none; color: var(--vp-text); text-decoration: none;
    border-bottom: none; white-space: nowrap;
}
.vp-nav-dropdown .vp-nav-menu a:hover { background: #f4f6fc; color: var(--vp-purple); }
.vp-nav-dropdown .vp-nav-menu a.active { color: var(--vp-purple); background: #eef4ff; }
/* Groups a run of related links inside a dropdown menu (e.g. "Target" over Set/View/History) without a
   full nested submenu — just a small divider + label, no extra click needed to reveal what's under it. */
.vp-nav-group-label {
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--vp-muted);
    padding: 10px 14px 4px; margin-top: 4px; border-top: 1px solid var(--vp-border);
}
.vp-nav-group-label:first-child { margin-top: 0; border-top: none; padding-top: 4px; }

.vp-filters { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
.vp-filters select, .vp-filters input { width: auto; min-width: 140px; }

.vp-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px,1fr)); gap: 10px; margin-bottom: 14px; }
.vp-stat-card { background: #fff; border: 1px solid var(--vp-border); border-radius: 10px; padding: 14px; text-align: center; }
.vp-stat-card .num { font-size: 24px; font-weight: 800; color: var(--vp-purple); line-height: 1.1; }
.vp-stat-card .lbl { font-size: 11.5px; color: var(--vp-muted); margin-top: 4px; }

/* Two-tier stat card (Target Analytics) — top tier is the date-window-prorated "expected by now"
   figure, bottom tier the plain current/total figure, each in its own color so they read as distinct
   at a glance rather than one number stacked on another. */
.vp-stat-card.tiered { padding: 0; overflow: hidden; text-align: center; }
.vp-stat-card.tiered .tier { padding: 10px 10px 9px; }
.vp-stat-card.tiered .tier-top { background: linear-gradient(180deg, rgba(37,99,235,.09), rgba(37,99,235,.02)); border-bottom: 1px dashed var(--vp-border); }
.vp-stat-card.tiered .tier-top .num { color: var(--vp-purple); }
.vp-stat-card.tiered .tier-bottom { background: #fff; }
.vp-stat-card.tiered .tier-bottom .num { color: var(--vp-text); }
.vp-stat-card.tiered .tier .num { font-size: 19px; font-weight: 800; line-height: 1.15; }
.vp-stat-card.tiered .tier .lbl { font-size: 10px; color: var(--vp-muted); margin-top: 2px; text-transform: uppercase; letter-spacing: .02em; }

/* Value Share's two breakdown columns (Achieved This Period / Total Achieved, each pie + list) — side by
   side on desktop, stacked on mobile. */
.vp-share-split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 4px; }
.vp-share-col .vp-gauge-title { text-align: left; }
@media (max-width: 640px) {
    .vp-share-split { grid-template-columns: 1fr; }
}

.vp-gauge-title { font-weight: 700; font-size: 13px; color: var(--vp-muted); text-transform: uppercase; letter-spacing: .02em; margin-bottom: 8px; }

/* Trend small multiples — one independently-scaled mini chart per category/item instead of one shared
   chart, since raw quantities/values here span very different magnitudes. */
.vp-trend-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 12px; }
.vp-trend-tile { border: 1px solid var(--vp-border); border-radius: 8px; padding: 10px; background: #fafbfe; }
.vp-trend-tile-title { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; font-size: 12.5px; font-weight: 700; margin-bottom: 6px; }
.vp-trend-tile-title span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vp-trend-tile-peak { font-size: 10.5px; font-weight: 500; color: var(--vp-muted); white-space: nowrap; }
.vp-trend-tile-chart { height: 90px; }

/* DPR Filing Calendar grid (admin/dpr_calendar.php) */
.vp-cal-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12px; color: var(--vp-muted); margin: 12px 0; align-items: center; }
.vp-cal-dot { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 4px; vertical-align: middle; }
.vp-cal-dot.filed { background: #d3f5df; border: 1px solid #22c55e; }
.vp-cal-dot.missing { background: #fce8e6; border: 1px solid var(--vp-danger); }
.vp-cal-dot.exempt { background: #fff7e0; border: 1px solid #ca8a04; }
.vp-cal-dot.future { background: #f4f6fa; border: 1px solid var(--vp-border); }
.vp-cal-dot.outside { background: #fafafa; border: 1px dashed #ddd; }
.vp-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.vp-cal-dow { text-align: center; font-size: 11px; font-weight: 700; color: var(--vp-muted); text-transform: uppercase; padding-bottom: 4px; }
.vp-cal-cell { position: relative; min-height: 62px; border-radius: 8px; padding: 6px; border: 1px solid var(--vp-border); background: #fafbfe; }
.vp-cal-cell.empty { background: transparent; border: none; }
.vp-cal-cell.outside { background: #fafafa; border-style: dashed; color: #ccc; }
.vp-cal-cell.filed { background: #d3f5df; border-color: #a8e6bb; }
.vp-cal-cell.missing { background: #fce8e6; border-color: #f3b4ae; cursor: pointer; }
.vp-cal-cell.exempt { background: #fff7e0; border-color: #f0dca0; }
.vp-cal-cell.future { background: #f4f6fa; }
.vp-cal-cell.is-today { outline: 2px solid var(--vp-purple); outline-offset: -2px; }
.vp-cal-cell .d { font-size: 11px; font-weight: 700; color: var(--vp-muted); }
.vp-cal-cell .mark { font-size: 15px; text-align: center; margin-top: 8px; }
.vp-cal-cell .mark.exempt-mark { font-weight: 800; color: #ca8a04; cursor: pointer; }
@media (max-width: 640px) {
    .vp-cal-grid { gap: 3px; }
    .vp-cal-cell { min-height: 44px; padding: 3px; }
    .vp-cal-dow { font-size: 9px; }
}

/* Collapsible BOQ table — hidden by default, a plain expandable summary rather than the popover-style
   `.vp-ga-edit` details used for the site picker. */
.vp-boq-details summary { cursor: pointer; font-weight: 700; font-size: 14px; padding: 8px 0; list-style: none; }
.vp-boq-details summary::-webkit-details-marker { display: none; }
.vp-boq-details summary::before { content: "▸ "; color: var(--vp-muted); }
.vp-boq-details[open] summary::before { content: "▾ "; }

/* Dashboard header: page title + range toolbar on one line */
.vp-dash-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }

.vp-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.vp-range-picker { display: flex; flex-wrap: wrap; gap: 6px; }
.vp-pill { display: inline-block; padding: 6px 12px; border-radius: 20px; font-size: 12.5px; font-weight: 600; text-decoration: none; color: var(--vp-muted); background: #fff; border: 1px solid var(--vp-border); }
.vp-pill:hover { color: var(--vp-purple); border-color: var(--vp-purple); }
.vp-pill.active { background: var(--vp-purple); color: #fff; border-color: var(--vp-purple); }
.vp-pill.custom-toggle { color: var(--vp-purple); border-style: dashed; }
.vp-range-label { font-size: 12.5px; color: var(--vp-muted); }

/* Target Analytics — "As Filed / Adjusted" switch (only shown when DPR Adjustment is enabled in Settings).
   A two-option segmented pill, matching the visual weight of the Print/Export buttons beside it. */
.vp-view-toggle { display: flex; border: 1px solid var(--vp-border); border-radius: 20px; overflow: hidden; }
.vp-view-toggle a { padding: 6px 14px; font-size: 12.5px; font-weight: 600; color: var(--vp-muted); text-decoration: none; }
.vp-view-toggle a:hover { color: var(--vp-purple); }
.vp-view-toggle a.active { background: var(--vp-gradient); color: #fff; }
.vp-custom-range { display: none; align-items: center; gap: 6px; margin-left: 4px; }
.vp-custom-range.open { display: inline-flex; }
.vp-custom-range input[type=date] { width: auto; min-width: 0; padding: 6px 8px; font-size: 13px; }

/* Chart cards: pair a chart with its compact numeric breakdown side-by-side */
.vp-chart-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px,1fr)); gap: 12px; margin-bottom: 12px; }
.vp-chart-grid .vp-card { margin-bottom: 0; }
.vp-split { display: flex; gap: 18px; align-items: center; }
.vp-split .vp-chart-wrap { flex: 0 0 150px; height: 150px; }
.vp-split .vp-mini-list { flex: 1; min-width: 0; }
.vp-chart-wrap { position: relative; height: 220px; overflow: hidden; }
.vp-chart-wrap.wide { height: 200px; }
.vp-chart-empty {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    text-align: center; padding: 0 16px; font-size: 12.5px; color: var(--vp-muted); background: #fff;
}

.vp-mini-list { display: flex; flex-direction: column; gap: 8px; }
.vp-mini-list .row { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.vp-mini-list .row .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.vp-mini-list .row .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--vp-text); }
.vp-mini-list .row .cnt { font-weight: 700; color: var(--vp-text); min-width: 22px; text-align: right; }

.vp-mini-stats { display: flex; gap: 20px; }
.vp-mini-stats .item .num { font-size: 20px; font-weight: 800; }
.vp-mini-stats .item .lbl { font-size: 12px; color: var(--vp-muted); }

.vp-infra-filters { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; align-items: flex-start; }
.vp-infra-filters select { width: auto; min-width: 150px; padding: 8px 10px; font-size: 13px; }
.vp-field { display: flex; flex-direction: column; gap: 3px; }
.vp-field .vp-field-label { margin: 0; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--vp-muted); }

.vp-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.vp-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 20px; font-size: 12.5px; font-weight: 600; background: #f4f6fa; color: var(--vp-text); }
.vp-chip b { font-weight: 800; }
.vp-chip .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.vp-chip .dot.good { background: var(--vp-success); }
.vp-chip .dot.bad { background: var(--vp-danger); }
.vp-chip .dot.neutral { background: #9ca3af; }

.vp-ready-card { background: linear-gradient(90deg, rgba(34,197,94,0.10), rgba(34,197,94,0.02)); border: 1px solid #bbf7d0; }
.vp-ready-card .num { color: var(--vp-success); }

.vp-table-wrap.scroll { max-height: 380px; overflow-y: auto; }

.vp-gps-status { font-size: 13px; padding: 10px; border-radius: 8px; margin-bottom: 10px; }
.vp-gps-status.ok { background: #e6f4ea; color: var(--vp-success); }
.vp-gps-status.bad { background: #fce8e6; color: var(--vp-danger); }
.vp-gps-status.checking { background: #fff4e5; color: #b5651d; }

@media (max-width: 480px) {
    .vp-btn-row { flex-direction: column; }
    .vp-header img { height: 28px; }
    .vp-header .vp-user { gap: 6px; font-size: 11.5px; max-width: 62vw; }
    .vp-user-info { line-height: 1.25; }
    .vp-user-role { display: block; font-size: 10.5px; }
}

/* ============ Mobile navigation: hamburger + slide-in sidebar ============
   Above 768px this is all inert (display:none / transform off-screen) — the
   existing horizontal nav.vp-tabs bar renders exactly as before. Below 768px,
   nav.vp-tabs itself becomes a fixed off-canvas drawer toggled by a small
   top bar + hamburger button that each nav partial adds just above it. */
.vp-mobile-bar { display: none; }
.vp-nav-toggle {
    display: none; align-items: center; gap: 8px;
    border: none; background: transparent; cursor: pointer;
    font-size: 14px; font-weight: 700; color: var(--vp-text);
    padding: 8px 10px; border-radius: 8px;
}
.vp-nav-toggle:hover { background: #f4f6fc; color: var(--vp-purple); }
.vp-nav-toggle .bars { font-size: 18px; line-height: 1; }

/* Leaflet's own internal panes/controls (tiles, markers, popups, zoom buttons) use z-index values up
   to 1000 — well above the nav drawer/dropdowns below. Leaflet sets position:relative on its container
   but no z-index, so on its own it never forms a real containing stacking context; its high internal
   z-index values were escaping to the page level and floating over the nav/dropdowns. Pinning the map
   container's own z-index (any positioned element + explicit z-index is enough) traps all of Leaflet's
   internal layers inside it, capped below everything that actually needs to sit above the map. */
#surveyMap, #dashboardMap { position: relative; z-index: 0; }

.vp-nav-overlay {
    display: none; position: fixed; inset: 0;
    background: rgba(16,24,40,.45);
    z-index: 90; opacity: 0; transition: opacity .2s ease;
}
.vp-nav-overlay.open { display: block; opacity: 1; }

.vp-nav-close { display: none; }

@media (max-width: 768px) {
    .vp-mobile-bar {
        display: flex; align-items: center;
        background: #fff; border-bottom: 1px solid var(--vp-border);
        padding: 4px 10px; margin: 0 0 14px;
    }
    .vp-nav-toggle { display: inline-flex; }

    nav.vp-tabs {
        position: fixed; top: 0; left: 0; bottom: 0;
        height: 100dvh;
        width: min(320px, 82vw); max-width: 82vw;
        flex-direction: column; flex-wrap: nowrap;
        align-items: stretch;
        overflow-y: auto;
        background: #fff;
        padding: 0 0 24px;
        margin: 0;
        gap: 0;
        border-bottom: none;
        border-right: 1px solid var(--vp-border);
        box-shadow: 6px 0 28px rgba(16,24,40,.18);
        transform: translateX(-105%);
        transition: transform .25s ease;
        z-index: 95;
    }
    nav.vp-tabs.open { transform: translateX(0); }

    .vp-nav-close {
        display: flex; align-items: center; justify-content: space-between;
        padding: 14px 18px; margin-bottom: 4px;
        border-bottom: 1px solid var(--vp-border);
        font-weight: 800; font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
        color: var(--vp-muted);
    }
    .vp-nav-close button {
        background: none; border: none; font-size: 20px; line-height: 1;
        color: var(--vp-text); cursor: pointer; padding: 4px 8px; border-radius: 6px;
    }
    .vp-nav-close button:hover { background: #f4f6fc; color: var(--vp-purple); }

    nav.vp-tabs a, .vp-nav-dropdown > .vp-nav-trigger {
        width: 100%; box-sizing: border-box;
        padding: 14px 18px; font-size: 13px;
        border-bottom: 1px solid var(--vp-border);
        border-left: 3px solid transparent;
        justify-content: flex-start;
    }
    nav.vp-tabs a.active { border-bottom-color: var(--vp-border); border-left-color: var(--vp-purple); background: #eef4ff; }

    .vp-nav-dropdown { width: 100%; }
    .vp-nav-dropdown > .vp-nav-trigger { width: 100%; }
    .vp-nav-dropdown .vp-nav-menu {
        position: static; display: none;
        min-width: 0; width: 100%;
        border: none; border-radius: 0; box-shadow: none; padding: 0;
        background: #fafbfe;
    }
    .vp-nav-dropdown.open .vp-nav-menu { display: block; }
    .vp-nav-dropdown .vp-nav-menu a {
        padding: 12px 18px 12px 34px; border-radius: 0;
        border-bottom: 1px solid var(--vp-border);
    }
}

/* Filter bars (GA/date/status dropdowns above tables, charts, maps) — on mobile, fixed min-width
   selects sitting in one flex-wrap row stack one-per-row and eat excessive vertical space. Pairing
   two controls per row (flex-basis ~45%) keeps them usable without the horizontal overflow a rigid
   min-width would cause on a 360-390px phone. Buttons/submit rows stay full-width for easy tapping. */
@media (max-width: 768px) {
    .vp-filters > div { flex: 1 1 45%; min-width: 0; }
    .vp-filters select, .vp-filters input:not([type="hidden"]) { width: 100% !important; min-width: 0 !important; box-sizing: border-box; }
    .vp-filters .vp-actions-row, .vp-filters > button { flex: 1 1 100%; }
}

/* Touch targets: delete/trash icon buttons throughout the DPR entry grids were sized for a mouse
   cursor (30px), below the ~40-44px a finger needs to hit reliably. Grown here rather than at the
   base rule so desktop's tighter row layout is untouched. */
@media (max-width: 768px) {
    .vp-icon-btn { width: 40px; height: 40px; font-size: 17px; }
}

/* Photo-remove button on the survey wizard's capture grid — the single most-used mobile control in
   the app (field surveyors deleting a bad shot mid-survey). 22px was both under the tap-target
   minimum and close enough to the next thumbnail's own button to risk mis-taps; both the button and
   the gap between thumbnails are grown together so a larger button can't overlap its neighbor. */
.vp-image-grid { gap: 14px; }
.vp-image-grid .item .remove { width: 30px; height: 30px; font-size: 16px; top: -8px; right: -8px; }
@media (max-width: 768px) {
    .vp-image-grid .item .remove { width: 36px; height: 36px; font-size: 18px; }
}

@media print {
    .no-print, .vp-account-menu, nav.vp-tabs, .vp-infra-filters, form.vp-custom-range { display: none !important; }
    body { background: #fff; }
    .vp-container.wide { max-width: 100%; }
    .vp-card { border: 1px solid #ccc; box-shadow: none; break-inside: avoid; }
    .vp-chart-grid { grid-template-columns: 1fr 1fr; }
    .vp-table-wrap.scroll { max-height: none; overflow: visible; }
    a.vp-pill { display: none; }

    /* Leaflet sizes its tile grid off the container's on-screen pixel box, which print's own layout
       pass and DPI scaling can leave stale — pin the container to a fixed height here (matched by a
       beforeprint invalidateSize() call in the map's own script) so the printed map isn't cropped to a
       sliver of the tiles it actually rendered. Zoom buttons and the attribution strip are UI chrome
       that only make sense on a page you can still click. */
    #dashboardMap, #surveyMap { height: 550px !important; }
    .leaflet-control-zoom, .leaflet-control-attribution { display: none !important; }
}
