/* ── Theme Tokens (Extending Bootstrap) ──
   Palette: the Haggerty Construction brand system ("the contractor look,
   codified") — light theme. Denim = the one interactive accent; Leather =
   warm material accent; job-site status colors (work-green / hi-vis amber /
   danger-tape red). Hex values live ONLY in this :root block — everything
   else must reference tokens. */
:root {
    --bs-font-sans-serif: 'Inter', sans-serif;

    /* Display face: Archivo at 125% width (brand "extra-wide" headline look).
       Page titles & section headlines only — body/UI text stays Inter. */
    --font-display: 'Archivo', 'Inter', sans-serif;
    --font-display-stretch: 125%;

    /* Theme Colors */
    --bs-primary: #45688E; /* Workwear Denim — the interactive accent */
    --bs-primary-rgb: 69, 104, 142;

    --bs-secondary: #6B7682; /* Steel Grey (ink-500) */
    --bs-secondary-rgb: 107, 118, 130;

    --bs-success: #6B9150; /* Work Green — go */
    --bs-success-rgb: 107, 145, 80;

    --bs-warning: #F5A524; /* Hi-Vis Amber — caution, the one signature yellow */
    --bs-warning-rgb: 245, 165, 36;

    --bs-info: #45688E; /* Info is denim, by design (job-site status set) */
    --bs-info-rgb: 69, 104, 142;

    --bs-danger: #B23B2F; /* Danger-Tape Red (oxide/primer) — stop */
    --bs-danger-rgb: 178, 59, 47;

    /* Body text: warm near-black ink */
    --bs-body-color: #1A1A1A;
    --bs-body-color-rgb: 26, 26, 26;

    /* Dark surfaces: warm charcoal ramp (work shirt, not screen-grey).
       bg-dark / navbars / project sidebar pull from these. */
    --bs-dark: #1C1B19;
    --bs-dark-rgb: 28, 27, 25;
    --hq-charcoal-950: #141312;
    --hq-charcoal-900: #1C1B19;
    --hq-charcoal-800: #262421;
    --hq-charcoal-700: #2F2D29;

    --hq-body-bg: #F6F8FA;

    /* Semantic surfaces & borders */
    --hq-surface: #FFFFFF;
    --hq-ink-soft: #3F4A56;  /* ink-700 — strong secondary text */
    --hq-surface-sunken: #EEF1F4;
    --hq-border-color: rgba(26, 26, 26, 0.10);
    --hq-border-strong: rgba(26, 26, 26, 0.16);

    /* Leather — warm material accent (eyebrows, accent bars, stat figures).
       Sparingly; it's seasoning, not a second interactive color. */
    --hq-accent-warm: #A87B4E;
    --hq-accent-warm-strong: #8A6646; /* darker leather for text on light */
    --hq-accent-warm-muted: rgba(168, 123, 78, 0.20);

    /* Interaction shades of primary (hover/pressed). Together with the
       ramps above, the only other place hex values may live. */
    --hq-primary-hover: #38567A;
    --hq-primary-active: #2E4763;

    /* Test-environment chrome: deep leather-brown (leather darkened toward
       charcoal) — clearly not prod's charcoal, still in the brand family. */
    --hq-env-test-surface: #4A3721;

    /* Point Bootstrap's link + focus styles at the brand color. Bootstrap
       compiles its default blue into these, so they must be overridden
       explicitly — overriding --bs-primary alone is not enough. */
    --bs-link-color: var(--bs-primary);
    --bs-link-color-rgb: var(--bs-primary-rgb);
    --bs-link-hover-color: var(--hq-primary-hover);
    --bs-link-hover-color-rgb: 56, 86, 122;
    --bs-focus-ring-color: rgba(var(--bs-primary-rgb), 0.25);
            

    /* ── Turnover task categories ──
       The checklist colour-codes each task by trade so a room reads at a
       glance. Thirteen tinted pairs (surface + AA text) that sit alongside the
       brand palette rather than inside it — they're wayfinding, not status. */
    --hq-cat-painting-bg: #E8F0FE;      --hq-cat-painting-fg: #1746A2;
    --hq-cat-plumbing-bg: #D4F1F9;      --hq-cat-plumbing-fg: #0B6E8A;
    --hq-cat-bulbs-bg: #FFF4D6;         --hq-cat-bulbs-fg: #8A6D00;
    --hq-cat-general-bg: #EEEEEE;       --hq-cat-general-fg: #444444;
    --hq-cat-toilet-bg: #E6F4EA;        --hq-cat-toilet-fg: #1E6B34;
    --hq-cat-hardware-bg: #F3E8FD;      --hq-cat-hardware-fg: #6B21A8;
    --hq-cat-blinds-bg: #E0F7F5;        --hq-cat-blinds-fg: #0F6E66;
    --hq-cat-drywall-bg: #FDE8E8;       --hq-cat-drywall-fg: #9B1C1C;
    --hq-cat-carpentry-bg: #EFE7DD;     --hq-cat-carpentry-fg: #7A4A1E;
    --hq-cat-cleanup-bg: #E7EEF7;       --hq-cat-cleanup-fg: #2A4D73;
    --hq-cat-compliance-bg: #FBE2EF;    --hq-cat-compliance-fg: #9D174D;
    --hq-cat-filter-bg: #E9F5E9;        --hq-cat-filter-fg: #2F6F2F;

    /* ── Half-done (first pass complete) ──
       Amber-600 rather than --bs-warning: the light hi-vis yellow can't carry
       white glyphs, and this mark sits under a white dash. */
    --hq-half-solid: #D97706;
    --hq-half-bg: #FEF3C7;
    --hq-half-fg: #92400E;
    --hq-half-tint: #FFFBF2;
    --hq-half-border: #FCD9A5;

    /* ── Checklist surfaces ──
       Near-white tints for done/partial task rows, and the greys the task card
       chrome is built from. */
    --hq-task-done-bg: #FBFDFB;
    --hq-task-partial-bg: #FFFDF6;
    --hq-hairline: #EEF0F2;
    --hq-control-border: #C4CAD3;
    --hq-ink-faint: #9AA0A6;
    --hq-ink-muted: #6B7280;
    --hq-panel-bg: #F7F8FA;
    --hq-panel-border: #D7DBE0;
    --hq-panel-ink: #3C4147;
    --hq-note-bg: #E7F6F8;
    --hq-note-fg: #0C5460;
    --hq-danger-ink: #B02A37;
    --hq-danger-border: #F0C2C7;
    --hq-danger-tint: #FDECEE;
    --hq-thumb-border: #E0E3E7;

    /* Typography Scale */
    --font-xs: 0.65rem;
    --font-sm: 0.75rem;
    --font-md: 0.85rem;
    --font-lg: 1rem;

    /* Spacing scale */
    --space-xs: 0.25rem;   
    --space-sm: 0.5rem;
    --space-md: 1rem;      
    --space-lg: 1.5rem;
    --space-xl: 2rem;

    /* Border radius scale — brand handoff values (squarer than Bootstrap):
       4px inputs/tags · 8px buttons/cards/images · 12px panels/sheets */
    --radius-sm: 0.25rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;
    --radius-pill: 50rem;

    /* Shadows */
    --shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.1);
    --shadow-lg: 0 8px 16px rgba(26, 26, 26, 0.08);

    /* Layout */
    --bottom-nav-height: 64px;
    /* Mobile header height below the safe-area inset (button row 44px + 12px bottom padding) */
    --mobile-header-base: 56px;
    --sidebar-width: 260px;
    --sidebar-min-width: 200px;
    --sidebar-max-width: 400px;
    --sidebar-collapsed-width: 56px;
    --sidebar-top-offset: 56px; /* desktop navbar height */

    /* Text-grade semantic colors — WCAG AA (≥4.5:1) on white surfaces.
       Use these for colored TEXT (stats, labels, link-like text); keep the
       brighter --bs-* base hues for fills, badges, borders and accents.
       (Denim and oxide red pass AA as-is; green and amber need darkening.) */
    --hq-text-success: #55743F;
    --hq-text-info: #45688E;
    --hq-text-warning: #8F5600;
    --hq-text-danger: #B23B2F;

    /* Chart series colors — brand-adjacent denim + leather, saturation
       raised past the chroma floor so marks don't read gray, and validated
       as a categorical pair (lightness band, CVD separation, 3:1 contrast
       on white). Charts read these via getComputedStyle; keep hexes here. */
    --hq-chart-1: #3D6CA6;
    --hq-chart-2: #BC7830;
    --hq-chart-ink: #6B7682;
    --hq-chart-grid: rgba(26, 26, 26, 0.08);
}

/* ── Display typography ──
   Page titles and big headlines wear the brand's extra-wide face.
   Card titles, modal titles and UI labels (h5/h6, .card-title) stay Inter.
   font-stretch engages Archivo's variable width axis (125%). */
h1, h2, h3, h4, .hq-display {
    font-family: var(--font-display);
    font-stretch: var(--font-display-stretch);
    letter-spacing: -0.01em;
}

/* ── Text-grade color utilities (see token comment above) ── */
.hq-text-success { color: var(--hq-text-success) !important; }
.hq-text-info    { color: var(--hq-text-info) !important; }
.hq-text-warning { color: var(--hq-text-warning) !important; }
.hq-text-danger  { color: var(--hq-text-danger) !important; }

/* Stat-tile values: a figure must never wrap mid-digit — abbreviate the
   number upstream (|compact_currency) instead of letting it break. */
.hq-stat-value {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Micro-label floor: 12px minimum so uppercase labels stay legible outdoors. */
.hq-text-xs { font-size: var(--font-sm, 0.75rem); }

/* ── Job workspace tabs (project detail) ──
   Segmented, hash-deep-linkable section tabs. Sticky on mobile (below the
   fixed header) where the desktop project sidebar is unavailable. */
.hq-workspace-tabs {
    display: flex;
    gap: 0.35rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 0.5rem 0.25rem;
    margin: 0 -0.25rem 1rem;
}
.hq-workspace-tabs::-webkit-scrollbar { display: none; }

@media (max-width: 991.98px) {
    .hq-workspace-tabs {
        position: sticky;
        top: calc(var(--mobile-header-base, 56px) + env(safe-area-inset-top, 0px));
        z-index: 1020;
        background: linear-gradient(var(--hq-body-bg, #F6F8FA) 88%, rgba(246,248,250,0));
    }
}

.hq-workspace-tab {
    flex: 0 0 auto;
    padding: 0.45rem 0.9rem;
    border-radius: var(--radius-pill, 50rem);
    border: 1px solid var(--hq-border-color, rgba(var(--bs-primary-rgb), 0.1));
    background: var(--hq-surface, #fff);
    color: var(--bs-primary, #45688E);
    font-size: var(--font-md, 0.85rem);
    font-weight: 600;
    white-space: nowrap;
    box-shadow: var(--shadow-sm);
    transition: background 0.15s ease, color 0.15s ease;
    -webkit-tap-highlight-color: transparent;
    min-height: 2.5rem;              /* comfortable touch target */
}
.hq-workspace-tab i { opacity: 0.75; }
.hq-workspace-tab:hover { background: rgba(var(--bs-primary-rgb), 0.06); }
.hq-workspace-tab:active { transform: scale(0.97); }

.hq-workspace-tab.active {
    background: var(--bs-primary, #45688E);
    border-color: var(--bs-primary, #45688E);
    color: #fff;
}
.hq-workspace-tab.active i { opacity: 1; }

/* ── Keyboard focus (WCAG 2.4.7) ──
   A clearly visible ring for keyboard/switch users; invisible for touch and
   mouse thanks to :focus-visible. */
:focus-visible {
    outline: 3px solid rgba(var(--bs-primary-rgb), 0.6);
    outline-offset: 2px;
}
.navbar-dark :focus-visible,
.project-sidebar :focus-visible,
/* Header controls: one shape family — same rounded square for every action. */
.mobile-header__btn {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-md, 0.5rem);
    background: rgba(255, 255, 255, 0.08);
    color: var(--bs-white);
    font-size: 1.15rem;
    line-height: 1;
    padding: 0;
    text-decoration: none;
    flex: 0 0 auto;
}
.mobile-header__btn:active { background: rgba(255, 255, 255, 0.16); }

/* Project pages swap the brand for back + job title (base_project.html). */
.mobile-header__context {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
    color: var(--bs-white);
    text-decoration: none;
}
.mobile-header__context .bi { color: rgba(255, 255, 255, 0.65); flex: 0 0 auto; }
.mobile-header__title {
    font-weight: 700;
    font-size: var(--font-lg, 1rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.mobile-header__page { color: rgba(255, 255, 255, 0.55); font-weight: 500; }

.mobile-header :focus-visible {
    outline-color: rgba(255, 255, 255, 0.75);
}

/* Smooth anchor scrolling, but never for users who asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* ── Bootstrap Component Bridge ──
   Bootstrap 5.3 bakes its default blue (var(--bs-primary)) into per-component CSS
   variables (e.g. .btn-primary { --bs-btn-bg: var(--bs-primary) }), so overriding
   --bs-primary in :root does NOT re-theme buttons, tabs, pagination, or
   form controls. This bridge re-points those component variables at the
   brand tokens. Without it the app shows two different "primary" colors. */
/* .form-range is the one control Bootstrap does NOT expose a variable for:
   its thumb colour is compiled in as a literal Bootstrap-blue value rather
   than a variable, so no amount of re-pointing --bs-primary reaches it.
   (Spelling that value out here would trip the linter's own legacy-palette
   rule, which is the rule working.) The vendor selectors cannot be
   combined — a browser drops the whole rule when it meets a prefix it does
   not know. */
.form-range::-webkit-slider-thumb { background-color: var(--bs-primary); }
.form-range::-moz-range-thumb { background-color: var(--bs-primary); }
.form-range:active::-webkit-slider-thumb { background-color: var(--hq-primary-active); }
.form-range:active::-moz-range-thumb { background-color: var(--hq-primary-active); }

.btn-primary {
    --bs-btn-bg: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--hq-primary-hover);
    --bs-btn-hover-border-color: var(--hq-primary-hover);
    --bs-btn-active-bg: var(--hq-primary-active);
    --bs-btn-active-border-color: var(--hq-primary-active);
    --bs-btn-disabled-bg: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
.btn-outline-primary {
    --bs-btn-color: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--bs-primary);
    --bs-btn-hover-border-color: var(--bs-primary);
    --bs-btn-active-bg: var(--bs-primary);
    --bs-btn-active-border-color: var(--bs-primary);
    --bs-btn-disabled-color: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
/* .btn-info is the same bug as .text-bg-info further down, in button form:
   :root re-tones --bs-info to Workwear Denim, but Bootstrap compiled
   `.btn-info` against its own light cyan — background from a literal, and a
   black foreground chosen to sit on it. The token move never reaches either,
   so every "info" button in the app rendered Bootstrap cyan with black text.

   White on denim is 5.8:1 (the same measurement as .text-bg-info). This makes
   info buttons denim, which is the intent: FRONTEND_GUIDELINES §2.2 has one
   interactive color, and --bs-info is deliberately the denim hue — an info
   button that looked different was the anomaly, not the fix. */
.btn-info {
    --bs-btn-bg: var(--bs-info);
    --bs-btn-border-color: var(--bs-info);
    --bs-btn-color: var(--bs-white);
    --bs-btn-hover-bg: var(--hq-primary-hover);
    --bs-btn-hover-border-color: var(--hq-primary-hover);
    --bs-btn-hover-color: var(--bs-white);
    --bs-btn-active-bg: var(--hq-primary-active);
    --bs-btn-active-border-color: var(--hq-primary-active);
    --bs-btn-active-color: var(--bs-white);
    --bs-btn-disabled-bg: var(--bs-info);
    --bs-btn-disabled-border-color: var(--bs-info);
    --bs-btn-disabled-color: var(--bs-white);
    --bs-btn-focus-shadow-rgb: var(--bs-info-rgb);
}
.btn-outline-info {
    --bs-btn-color: var(--bs-info);
    --bs-btn-border-color: var(--bs-info);
    --bs-btn-hover-bg: var(--bs-info);
    --bs-btn-hover-border-color: var(--bs-info);
    --bs-btn-hover-color: var(--bs-white);
    --bs-btn-active-bg: var(--bs-info);
    --bs-btn-active-border-color: var(--bs-info);
    --bs-btn-active-color: var(--bs-white);
    --bs-btn-disabled-color: var(--bs-info);
    --bs-btn-disabled-border-color: var(--bs-info);
    --bs-btn-focus-shadow-rgb: var(--bs-info-rgb);
}
.btn-link {
    --bs-btn-color: var(--bs-primary);
    --bs-btn-hover-color: var(--hq-primary-hover);
    --bs-btn-active-color: var(--hq-primary-active);
}
.nav-pills {
    --bs-nav-pills-link-active-bg: var(--bs-primary);
}
/* .nav-link colors need no bridge: .nav derives them from --bs-link-color
   (overridden above), and .navbar-nav keeps its own navbar palette. */
.pagination {
    --bs-pagination-color: var(--bs-primary);
    --bs-pagination-hover-color: var(--hq-primary-hover);
    --bs-pagination-active-bg: var(--bs-primary);
    --bs-pagination-active-border-color: var(--bs-primary);
    --bs-pagination-focus-color: var(--hq-primary-hover);
    --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}
.list-group {
    --bs-list-group-active-bg: var(--bs-primary);
    --bs-list-group-active-border-color: var(--bs-primary);
}
.dropdown-menu {
    --bs-dropdown-link-active-bg: var(--bs-primary);
}
.form-check-input:checked {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
}
.form-control:focus,
.form-select:focus {
    border-color: rgba(var(--bs-primary-rgb), 0.5);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}

/* ── Project Sidebar (Dark Theme) ── */
.project-sidebar {
    position: fixed;
    top: var(--sidebar-top-offset);
    left: 0;
    bottom: 0;
    width: var(--sidebar-width);
    background: var(--hq-charcoal-900);
    border-right: 1px solid rgba(255, 255, 255, 0.08);
    z-index: 1019;
    display: flex;
    flex-direction: column;
    transition: width 0.2s ease, box-shadow 0.2s ease;
}

/* Make desktop navbar sticky so sidebar top offset stays correct */
.navbar-top {
    position: sticky;
    top: 0;
    z-index: 1030;
}

.project-sidebar__inner {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: var(--space-lg) var(--space-md);
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.2) transparent;
}

/* Resize handle on right edge */
.project-sidebar__resize {
    position: absolute;
    top: 0;
    right: 0;
    width: 4px;
    height: 100%;
    cursor: col-resize;
    z-index: 2;
    transition: background-color 0.15s ease;
}
.project-sidebar__resize:hover,
.project-sidebar__resize.dragging {
    background-color: rgba(255, 255, 255, 0.3);
}

/* Collapse toggle button — top-right inline */
.project-sidebar__toggle {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    background: transparent;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.15s ease;
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.4);
    margin-top: 2px;
}
.project-sidebar__toggle:hover {
    background: rgba(255, 255, 255, 0.1);
    color: rgba(255, 255, 255, 0.9);
}

/* Collapsed state */
.project-sidebar.collapsed {
    width: var(--sidebar-collapsed-width);
}
.project-sidebar.collapsed .project-sidebar__toggle {
    background: rgba(255, 255, 255, 0.08);
    width: 100%;
    height: 36px;
    border-radius: 0;
    margin: 0;
}
.project-sidebar.collapsed .project-sidebar__toggle i::before {
    content: "\F285"; /* bi-chevron-right */
}
.project-sidebar.collapsed .sidebar-label,
.project-sidebar.collapsed .sidebar-section-title,
.project-sidebar.collapsed .sidebar-project-meta,
.project-sidebar.collapsed .sidebar-quick-actions,
.project-sidebar.collapsed .sidebar-project-identity,
.project-sidebar.collapsed .sidebar-back-link {
    display: none;
}
.project-sidebar.collapsed .sidebar-nav-link {
    justify-content: center;
    padding: 0.5rem;
}
.project-sidebar.collapsed .sidebar-nav-link i {
    margin-right: 0;
    font-size: 1.2rem;
}



/* Sidebar internal components — Dark theme */
.sidebar-back-link {
    display: flex;
    align-items: center;
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: rgba(255, 255, 255, 0.5);
    text-decoration: none;
    margin-bottom: var(--space-lg);
    transition: color 0.15s ease;
}
.sidebar-back-link:hover {
    color: #fff;
}

.sidebar-project-identity {
    margin-bottom: var(--space-lg);
}
.sidebar-project-identity h5 {
    color: #fff !important;
}
.sidebar-project-identity .badge {
    background-color: rgba(255, 255, 255, 0.15) !important;
    color: #fff !important;
    border-color: rgba(255, 255, 255, 0.25) !important;
}
.sidebar-project-identity .text-muted {
    color: rgba(255, 255, 255, 0.6) !important;
}
.sidebar-project-identity a {
    color: rgba(255, 255, 255, 0.6) !important;
}
.sidebar-project-identity a:hover {
    color: #fff !important;
}

.sidebar-nav-link {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-size: var(--font-md);
    color: rgba(255, 255, 255, 0.75);
    text-decoration: none;
    transition: all 0.15s ease;
    white-space: nowrap;
}
.sidebar-nav-link:hover {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
}
.sidebar-nav-link.active {
    background-color: rgba(255, 255, 255, 0.15);
    color: #fff;
}
.sidebar-nav-link.active i {
    color: #fff;
}
.sidebar-nav-link i {
    margin-right: 0.75rem;
    font-size: 1rem;
    color: rgba(255, 255, 255, 0.5);
    width: 1.25rem;
    text-align: center;
}

/* Section toggles (Buildertrend-style) */
.sidebar-nav-sections {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.sidebar-section-toggle {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-size: var(--font-md);
    color: rgba(255, 255, 255, 0.75);
    background: transparent;
    border: none;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
    width: 100%;
    text-align: left;
}
.sidebar-section-toggle:hover {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
}
.sidebar-section-toggle i:first-child {
    margin-right: 0.75rem;
    font-size: 1rem;
    color: rgba(255, 255, 255, 0.5);
    width: 1.25rem;
    text-align: center;
}
.sidebar-chevron {
    margin-left: auto;
    font-size: 0.65rem;
    transition: transform 0.2s ease;
    color: rgba(255, 255, 255, 0.3);
}
.sidebar-section-toggle.collapsed .sidebar-chevron {
    transform: rotate(-90deg);
}

/* Sub-nav container */
.sidebar-sub-nav {
    display: none;
    flex-direction: column;
    padding-left: 2.25rem;
    margin-bottom: 0.25rem;
    border-left: 2px solid rgba(255, 255, 255, 0.08);
    margin-left: 1.35rem;
}
.sidebar-sub-nav.show {
    display: flex;
}

/* Sub-links */
.sidebar-sub-link {
    display: block;
    padding: 0.3rem 0.75rem;
    font-size: 0.8rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.55);
    text-decoration: none;
    border-radius: var(--radius-sm);
    transition: all 0.15s ease;
}
.sidebar-sub-link:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.05);
}
.sidebar-sub-link.active {
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
}

/* Separator */
.sidebar-separator {
    height: 1px;
    background: rgba(255, 255, 255, 0.08);
    margin: var(--space-md) var(--space-sm);
}

/* Collapsed state — hide sub-navs and chevrons */
.project-sidebar.collapsed .sidebar-sub-nav,
.project-sidebar.collapsed .sidebar-chevron,
.project-sidebar.collapsed .sidebar-separator {
    display: none !important;
}
.project-sidebar.collapsed .sidebar-section-toggle {
    justify-content: center;
    padding: 0.5rem;
}
.project-sidebar.collapsed .sidebar-section-toggle i:first-child {
    margin-right: 0;
    font-size: 1.2rem;
}
.project-sidebar.collapsed .sidebar-section-toggle .sidebar-label {
    display: none;
}

/* Content area offset when sidebar is present */
.has-project-sidebar .main-content {
    margin-left: var(--sidebar-width);
    width: calc(100% - var(--sidebar-width));
    transition: margin-left 0.2s ease, width 0.2s ease;
}
.has-project-sidebar.sidebar-collapsed .main-content {
    margin-left: var(--sidebar-collapsed-width);
    width: calc(100% - var(--sidebar-collapsed-width));
}

/* Hide sidebar on mobile */
@media (max-width: 991.98px) {
    .project-sidebar {
        display: none !important;
    }
    .has-project-sidebar .main-content {
        margin-left: 0 !important;
        width: 100% !important;
    }
}

/* ── Base ── */
body {
    background-color: var(--hq-body-bg);
    font-family: var(--bs-font-sans-serif);
}

/* ── Test Environment Visual Indicator ──
   "Caution tape" treatment: the header shifts from prod's charcoal to a
   deep leather-brown (same warm family, unmistakably different), and the
   TEST pill + accent lines wear hi-vis amber — the system's caution color.
   Must stay obvious at a glance; never ship these on production chrome. */
.env-test .navbar-top,
.env-test .mobile-header,
.env-test #mobileSearchDrawer {
    background-color: var(--hq-env-test-surface) !important;
    box-shadow: inset 0 -3px 0 var(--bs-warning) !important;
}
/* Caution accent on the mobile bottom nav */
.env-test .mobile-bottom-nav {
    border-top: 2px solid var(--bs-warning) !important;
}
/* "TEST" indicator pill in the desktop navbar brand area */
.env-test .navbar-brand::after {
    content: "TEST";
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.1em 0.5em;
    font-size: 0.55rem;
    font-weight: 800;
    letter-spacing: 1px;
    text-transform: uppercase;
    background: var(--bs-warning);
    color: var(--hq-charcoal-900);
    border-radius: var(--radius-pill);
    vertical-align: middle;
}

/* ── Components: Card ── */
.hq-card {
    border: 1px solid var(--hq-border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    background-color: var(--hq-surface);
    overflow: hidden;
}

.hq-card-header {
    padding: var(--space-md);
    border-bottom: 1px solid var(--hq-border-color);
    background-color: transparent;
}

.hq-card-body {
    padding: var(--space-md);
}

/* ── Components: Typography ── */
.hq-section-header {
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--bs-secondary);
    margin-bottom: var(--space-sm);
}

/* ── Components: Buttons ── */
.hq-btn-primary {
    background-color: var(--bs-primary);
    color: #fff;
    border: none;
    border-radius: var(--radius-md);
    padding: 0.5rem 1rem;
    font-weight: 600;
    transition: background-color 0.2s ease, transform 0.1s ease;
}
.hq-btn-primary:hover {
    background-color: var(--hq-primary-hover);
    color: #fff;
    transform: translateY(-1px);
}
.hq-btn-primary:active {
    transform: translateY(0);
}

.hq-btn-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    padding: 0.4rem 1rem;
    font-size: var(--font-sm);
    font-weight: 600;
    border: 1px solid var(--hq-border-color);
    background-color: var(--hq-surface);
    color: var(--bs-primary);
    transition: all 0.2s ease;
    text-decoration: none;
}
.hq-btn-action:hover {
    background-color: rgba(var(--bs-primary-rgb), 0.05);
    color: var(--bs-primary);
}

/* ── Components: Badges ── */
.hq-badge {
    border-radius: var(--radius-pill);
    padding: 0.25em 0.6em;
    font-size: var(--font-xs);
    font-weight: 600;
}
.hq-badge-primary { background-color: rgba(var(--bs-primary-rgb), 0.1); color: var(--bs-primary); }
.hq-badge-success { background-color: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }
.hq-badge-warning { background-color: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }
.hq-badge-danger  { background-color: rgba(var(--bs-danger-rgb), 0.1); color: var(--bs-danger); }
.hq-badge-info    { background-color: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }
/* The quiet one: a fact about the row that carries no judgement ("owner only",
   "picked by hand"). Steel grey as text measures 4.4:1 on white — just under
   the AA floor — so the tint carries the identity and body ink carries the
   text, the same trade .hq-badge-vip makes. Templates were already asking for
   this class before it existed, and rendering an untinted pill. */
.hq-badge-secondary {
    background-color: rgba(var(--bs-secondary-rgb), 0.12);
    color: var(--bs-body-color);
}

/* VIP is an attribute of a property, not an alarm about it. It was
   danger-red on every row it appeared on, which spent the loudest colour in
   the system on something nobody has to act on — and left the rows that DO
   need action with no way to outrank it. Leather tints it warm without
   joining the status set, and the star carries the signal. Body ink for the
   text: leather-on-leather measures 4.1:1, under the AA floor. */
.hq-badge-vip {
    background-color: var(--hq-accent-warm-muted);
    color: var(--bs-body-color);
}

/* Bootstrap bakes `.text-bg-*` foreground colors at compile time, computed
   against ITS palette — not ours. The :root block above re-tones --bs-info to
   Workwear Denim, a dark hue, but Bootstrap still ships `.text-bg-info` with a
   black foreground because its own info is light cyan. Black on denim measures
   3.6:1 — under the 4.5:1 WCAG AA floor badge-sized text needs; white on the
   same denim is 5.8:1.

   Overriding a token without overriding the utility that hardcoded its
   contrast is the bug, so any future re-tone of --bs-info revisits this line.
   Checked and left alone: --bs-warning keeps black (10.3:1). See the note on
   --bs-success in §2.2 of FRONTEND_GUIDELINES.md. */
.text-bg-info { color: var(--bs-white) !important; }


/* ── Mobile & Safe Area Fixes ── */
@media (max-width: 991.98px) {
    body {
        /* Prevent content from hiding behind fixed bottom nav */
        padding-bottom: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom));
    }

    .navbar-top {
        display: none !important;
    }

    .mobile-header {
        display: flex !important;
        /* Control vertical padding here (not via Bootstrap's py-3) so the
           iOS status-bar / notch inset is respected. !important is required
           to win against Bootstrap utility classes. */
        padding-top: max(0.75rem, env(safe-area-inset-top)) !important;
        padding-bottom: 0.75rem !important;
    }

    /* Offsets the fixed mobile header. Tracks the real header height
       (base + whichever is larger: default padding or the safe-area inset)
       so page content always clears the header on every device. */
    .mobile-header-spacer {
        height: calc(var(--mobile-header-base) + max(0.75rem, env(safe-area-inset-top)));
    }

    /* Search drawer slides in directly beneath the header */
    #mobileSearchDrawer {
        top: calc(var(--mobile-header-base) + max(0.75rem, env(safe-area-inset-top)));
    }

    .mobile-bottom-nav {
        height: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom));
        padding-bottom: env(safe-area-inset-bottom);
        z-index: 1030;
        box-shadow: 0 -4px 16px rgba(var(--bs-primary-rgb), 0.08);
    }

    .bottom-nav-item {
        flex: 1;
        padding: 0.5rem 0;
        border-radius: 14px;
        /* Suppress the default grey tap flash — we provide our own feedback */
        -webkit-tap-highlight-color: transparent;
        transition: color 0.15s ease-in-out, transform 0.12s ease-in-out, background-color 0.12s ease-in-out;
    }

    .bottom-nav-item.active {
        color: var(--bs-primary) !important;
        transform: translateY(-2px);
    }
    .bottom-nav-item.active i {
        color: var(--bs-primary);
    }

    /* Immediate press feedback so a tap registers instantly, before the
       next page has loaded. Added by JS on pointerdown; :active is a
       no-JS fallback (unreliable for <a> on iOS, hence the class). */
    a.bottom-nav-item.is-pressing,
    a.bottom-nav-item:active {
        transform: scale(0.90);
        background-color: rgba(var(--bs-primary-rgb), 0.12);
        color: var(--bs-primary) !important;
    }
    a.bottom-nav-item.is-pressing i,
    a.bottom-nav-item:active i {
        color: var(--bs-primary);
    }

    /* Expand minimum tap target sizes on mobile */
    .btn-sm, .btn-group-sm > .btn {
        padding: 0.5rem 0.75rem;
        font-size: 0.875rem;
        min-height: 44px;
        min-width: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    .offcanvas-action-item {
        padding: 1rem;
    }
}

@media (min-width: 992px) {
    .mobile-header {
        display: none !important;
    }
}

/* ── Standalone Floating Action Button ── */
.global-fab {
    position: fixed;
    bottom: 1.5rem;
    right: 1.5rem;
    z-index: 1040;
}

.fab-button {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: var(--bs-primary);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 6px 16px rgba(var(--bs-primary-rgb), 0.35);
    border: 4px solid #fff;
    transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.2s ease;
    text-decoration: none;
}

.fab-button:active {
    transform: scale(0.92);
    box-shadow: 0 2px 8px rgba(var(--bs-primary-rgb), 0.3);
}

.fab-button:hover {
    transform: scale(1.05);
    box-shadow: 0 8px 20px rgba(var(--bs-primary-rgb), 0.45);
    color: white;
}

/* ── Central Quick Add FAB ── */
.central-fab {
    width: 58px !important;
    height: 58px !important;
    margin-top: -26px !important;
    border: 4px solid var(--hq-surface) !important;
    box-shadow: 0 6px 16px rgba(var(--bs-primary-rgb), 0.4) !important;
    transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.2s ease;
}

.central-fab:active {
    transform: scale(0.92);
    box-shadow: 0 2px 8px rgba(var(--bs-primary-rgb), 0.3) !important;
}

.central-fab:hover {
    transform: scale(1.05);
    box-shadow: 0 8px 20px rgba(var(--bs-primary-rgb), 0.45) !important;
    color: white;
}

/* ── Offcanvas action items ── */
.offcanvas-action-item {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    border-radius: 12px;
    transition: background-color 0.2s ease;
    text-decoration: none;
    color: var(--bs-primary);
}
.offcanvas-action-item:hover, .offcanvas-action-item:active {
    background-color: rgba(var(--bs-primary-rgb), 0.05);
}
.offcanvas-icon-box {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 16px;
}

/* ── Global Utility Classes (Extracted) ── */
.progress-xs {
    height: 4px;
}

.section-label {
    letter-spacing: 1px;
}

.icon-circle {
    width: 36px;
    height: 36px;
    font-size: var(--font-lg, 0.9rem);
}

.avatar-circle {
    width: 32px;
    height: 32px;
    font-size: var(--font-md, 0.8rem);
}

.text-xs {
    font-size: var(--font-sm, 0.75rem);
}

/* Hide scrollbar but keep scroll functionality */
.hide-scrollbar::-webkit-scrollbar {
    display: none;
}
.hide-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

/* Bootstrap accordion overrides for design system */
.accordion-button:not(.collapsed) {
    color: inherit;
}
.accordion-button:focus {
    box-shadow: none;
}

.task-checkbox {
    width: 1.4em;
    height: 1.4em;
    cursor: pointer;
}

.task-completing {
    opacity: 0.5;
    text-decoration: line-through;
    transition: opacity 0.3s ease;
}

.badge-sm {
    font-size: var(--font-xs, 0.65rem);
    padding: 0.2em 0.5em;
}

/* Fix EasyMDE button conflicts with Bootstrap */
.editor-toolbar button.table {
    width: 30px !important;
    margin: 0 !important;
    padding: 0 !important;
    color: var(--bs-primary) !important;
}

/* ── Note Composer (Global — for offcanvas usage) ── */
.note-composer-card {
    border: 2px solid var(--bs-primary);
    border-radius: var(--radius-md, 0.75rem);
    overflow: hidden;
    box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,0.08));
    background: #fff;
    transition: box-shadow 0.2s ease;
}
.note-composer-card:focus-within {
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), 0.12), 0 1px 3px rgba(0,0,0,0.08);
}
.nc-cam-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 16px;
    height: 16px;
    border-radius: 50rem;
    background: var(--bs-primary);
    color: #fff;
    font-size: 0.6rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
}
.note-composer-textarea {
    width: 100%;
    border: none;
    outline: none;
    resize: none;
    padding: 12px 16px;
    font-size: 0.9rem;
    line-height: 1.5;
    min-height: 80px;
    max-height: 300px;
    overflow-y: auto;
    background: transparent;
    transition: height 0.15s ease;
}
.note-composer-textarea::placeholder { color: var(--bs-secondary); }
.note-composer-textarea:focus { box-shadow: none; }
.note-composer-tags {
    display: flex;
    gap: 6px;
    padding: 0 12px 8px;
    overflow-x: auto;
    scrollbar-width: none;
}
.note-composer-tags::-webkit-scrollbar { display: none; }
.nc-tag-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 50rem;
    border: 1px solid var(--hq-border-strong);
    background: #fff;
    color: var(--bs-secondary);
    font-size: 0.72rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: all 0.15s ease;
}
.nc-tag-pill:hover {
    border-color: var(--bs-primary);
    color: var(--bs-primary);
    background: rgba(var(--bs-primary-rgb), 0.04);
}
.nc-tag-pill.active {
    border-color: var(--bs-primary);
    background: var(--bs-primary);
    color: #fff;
}
.nc-tag-pill i { font-size: 0.7rem; }

/* Follow-up panel — "how did it go" chips and the next-follow-up date.
   Shares the tag-pill shape; a quieter fill so the type row stays primary. */
.nc-followup { border-top: 1px solid var(--hq-surface-sunken); }
.nc-outcome-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.nc-outcome-chip {
    padding: 4px 10px;
    border-radius: 50rem;
    border: 1px solid var(--hq-border-strong);
    background: var(--hq-surface);
    color: var(--bs-secondary);
    font-size: 0.72rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: all 0.15s ease;
}
.nc-outcome-chip:hover {
    border-color: var(--bs-primary);
    color: var(--bs-primary);
}
.nc-outcome-chip.active {
    border-color: var(--bs-primary);
    background: rgba(var(--bs-primary-rgb), 0.12);
    color: var(--bs-primary);
}
.note-composer-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 8px;
    border-top: 1px solid var(--hq-surface-sunken);
    background: #fafafa;
}
.nc-toolbar-btn {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    border: none;
    background: transparent;
    color: var(--bs-secondary);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.15s ease;
    font-size: 1rem;
}
.nc-toolbar-btn:hover { background: rgba(var(--bs-primary-rgb), 0.08); color: var(--bs-primary); }
.nc-toolbar-btn.active { background: rgba(var(--bs-primary-rgb), 0.1); color: var(--bs-primary); }
.nc-send-btn {
    display: inline-flex;
    align-items: center;
    padding: 6px 16px;
    border-radius: 8px;
    border: none;
    background: var(--bs-primary);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s ease;
}
.nc-send-btn:hover { background: var(--hq-primary-hover); transform: translateY(-1px); }
.nc-send-btn:active { transform: translateY(0); }

/* ── Photo Preview Strip (in composer) ── */
.nc-photo-preview {
    display: flex;
    gap: 8px;
    padding: 8px 12px;
    overflow-x: auto;
    scrollbar-width: none;
}
.nc-photo-preview::-webkit-scrollbar { display: none; }
.nc-thumb {
    position: relative;
    width: 64px;
    height: 64px;
    border-radius: 8px;
    overflow: hidden;
    flex-shrink: 0;
    background: var(--hq-surface-sunken);
}
.nc-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.nc-thumb-video {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--hq-primary-hover);
    color: #fff;
    font-size: 1.5rem;
}
.nc-thumb-remove {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: none;
    background: rgba(0,0,0,0.6);
    color: #fff;
    font-size: 0.65rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
    line-height: 1;
}
.nc-thumb-remove:hover { background: rgba(var(--bs-danger-rgb),0.9); }
/* Upload state overlay on composer thumbnails (queued/uploading/done/error).
   pointer-events:none so the remove button stays clickable and an error tap
   falls through to the thumb (tap-to-retry). */
.nc-thumb-status {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 1rem;
    background: rgba(0,0,0,0.35);
    pointer-events: none;
}
.nc-thumb-status.is-done {
    background: transparent;
    align-items: flex-end;
    justify-content: flex-start;
    padding: 2px;
    font-size: 0.85rem;
    color: #4ade80;
    text-shadow: 0 0 3px rgba(0,0,0,0.7);
}
.nc-thumb-status.is-error {
    background: rgba(var(--bs-danger-rgb), 0.55);
    font-size: 1.25rem;
    cursor: pointer;
}

/* ── Activity Feed Attachments ── */
.activity-attachments {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    flex-wrap: wrap;
}
.activity-attachment-thumb {
    width: 80px;
    height: 80px;
    border-radius: 8px;
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    background: var(--hq-surface-sunken);
}
.activity-attachment-thumb:hover {
    transform: scale(1.05);
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.activity-attachment-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.activity-attachment-thumb .video-badge {
    position: absolute;
    bottom: 4px;
    right: 4px;
    background: rgba(0,0,0,0.6);
    color: #fff;
    border-radius: 4px;
    padding: 1px 4px;
    font-size: 0.55rem;
    font-weight: 700;
}
.activity-more-badge {
    width: 80px;
    height: 80px;
    border-radius: 8px;
    background: var(--hq-surface-sunken);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--bs-secondary);
    cursor: pointer;
}


/* ── Follow-up modal (global: included from dashboard, pipeline, lead detail, jobs list) ── */
.fu-type-btn {
    border: 1.5px solid var(--hq-border-strong);
    border-radius: 0.5rem;
    padding: 0.4rem 0.8rem;
    font-size: 0.8rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
    background: var(--hq-surface);
    color: var(--hq-ink-soft);
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.fu-type-btn:hover {
    background: var(--hq-surface-sunken);
    border-color: var(--hq-border-strong);
}
.fu-type-btn.active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: var(--hq-surface);
}

/* Follow-up modal — section headings.

   The sheet records the past and commits to the future, and those are two
   different acts. Without a visual break they read as one undifferentiated
   column of five fields, which is what made the form feel arbitrary. */
.fu-section-label {
    font-size: var(--font-sm, 0.75rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--hq-ink-soft);
    padding-bottom: 0.4rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid var(--hq-border-color);
}

/* "What's next" sits on its own ground — subordinate to the record above it,
   but unmistakably a separate commitment. */
.fu-next {
    margin-top: 1.25rem;
    padding: 0.9rem;
    background: var(--hq-surface-sunken);
    border-radius: 0.75rem;
}

.fu-next--cleared {
    opacity: 0.45;
}

.fu-no-followup {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.75rem;
    padding: 0.3rem 0.1rem;
    border: 0;
    background: none;
    color: var(--hq-ink-soft);
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.fu-no-followup.active {
    color: var(--bs-primary);
}

/* Reschedule sheet — the date being moved, for orientation before changing it. */
.rs-current {
    font-size: var(--font-sm, 0.75rem);
    color: var(--hq-ink-soft);
    margin-bottom: 0.6rem;
}

/* Relative-day chip — "Today", "Friday", "3 days".
   Carries an active state, because the whole point is that the control says
   which day is selected instead of making you decode MM/DD/YYYY. */
.fu-day-chip {
    border: 1.5px solid var(--hq-border-color);
    background: var(--hq-surface);
    color: var(--hq-ink-soft);
    border-radius: var(--radius-pill, 50rem);
    padding: 0.3rem 0.75rem;
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.fu-day-chip:hover {
    border-color: var(--hq-border-strong);
}
.fu-day-chip.active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: var(--hq-surface);
}

/* The chosen day, in words. The date input can only show MM/DD/YYYY; this
   says "Monday, Aug 31" so nobody has to work out what that means. */
.fu-resolved {
    margin-top: 0.4rem;
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    color: var(--hq-ink-soft);
}

/* Hibernate — deliberately not a chip. Parking a deal for a quarter is a
   different kind of decision from "chase this on Thursday", and putting it in
   the same row is how the shortcut list grew a tail out to "1 year". */
.rs-hibernate {
    display: inline-flex;
    align-items: center;
    margin-top: 0.6rem;
    padding: 0.3rem 0.1rem;
    border: 0;
    background: none;
    color: var(--hq-ink-soft);
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.rs-hibernate:hover { color: var(--bs-primary); }

/* Follow-up modal — quick-date shortcut row */
.fu-quick-dates {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.5rem;
}

.fu-outcome-chip {
    border: 1.5px solid var(--hq-border-color);
    background: var(--hq-surface);
    color: var(--hq-ink-soft);
    border-radius: var(--radius-pill, 50rem);
    padding: 0.35rem 0.8rem;
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    -webkit-tap-highlight-color: transparent;
}
.fu-outcome-chip.active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: var(--hq-surface);
}

/* ── Navbar search typeahead ── */
.hq-search-results {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    width: 320px;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--hq-surface, #fff);
    border: 1px solid var(--hq-border-color);
    border-radius: var(--radius-md, 0.75rem);
    z-index: 1040;
    padding: 0.25rem;
}
.hq-search-result {
    display: block;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-sm, 0.5rem);
    text-decoration: none;
}
/* Omnibox section headers (Jobs / Clients / Tasks / …) — leather eyebrow
   with a hairline rule so the groups parse at a glance. */
.hq-search-group {
    padding: 0.5rem 0.75rem 0.25rem;
    font-size: var(--font-xs, 0.65rem);
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--hq-accent-warm-strong);
    border-bottom: 1px solid var(--hq-border-color);
    margin: 0 0.25rem 0.25rem;
}
.hq-search-group + .hq-search-group,
.hq-search-result + .hq-search-group { margin-top: 0.5rem; }

/* Activity feed burst roll-ups ("6 tasks added" → expandable) */
.hq-feed-group summary { cursor: pointer; list-style-position: outside; }
.hq-feed-group summary::marker { color: var(--bs-secondary); }
.hq-feed-group summary > span { display: flex; }
.hq-feed-group-list { padding-left: 1.1rem; }
.hq-feed-group-list li { margin-bottom: 0.15rem; }
.hq-search-result:hover,
.hq-search-result.highlighted {
    background: rgba(var(--bs-primary-rgb), 0.08);
}
.hq-search-result__title {
    display: block;
    font-weight: 600;
    font-size: var(--font-md, 0.85rem);
    color: var(--bs-body-color);
}
.hq-search-result__meta {
    display: block;
    font-size: var(--font-sm, 0.75rem);
    color: var(--bs-secondary);
}

/* ── In-card filter chips (e.g. Selections status filter) ── */
.hq-filter-chip {
    border: 1px solid var(--hq-border-color);
    background: var(--hq-surface);
    color: var(--hq-ink-soft);
    border-radius: var(--radius-pill, 50rem);
    padding: 0.25rem 0.7rem;
    font-size: var(--font-sm, 0.75rem);
    font-weight: 600;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
}
.hq-filter-chip:hover {
    border-color: var(--hq-border-strong);
    color: var(--bs-primary);
}
.hq-filter-chip.active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: var(--hq-surface);
}
.hq-filter-chip__count {
    opacity: 0.65;
    font-weight: 500;
    margin-left: 0.15rem;
}

/* Flex chip row WITHOUT !important — Bootstrap's d-flex overrides Alpine's
   x-show display toggle; this class cooperates with it. */
.hq-chip-row { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* List rows that Alpine shows/hides: flex WITHOUT !important (see .hq-chip-row). */
.hq-list-row { display: flex; align-items: center; }

/* Flex children default to min-width:auto, so a long word inside can push the
   whole row (and page) wider than the viewport. Templates already lean on this
   class next to .flex-grow-1 — without it, text-truncate never engages. */
.min-width-0 { min-width: 0; }

/* Mobile search drawer: results panel spans the drawer width */
.hq-search-results--mobile {
    left: 0;
    right: 0;
    width: auto;
    top: calc(100% + 4px);
}

/* ── Quick Add launcher grid ── */
.hq-launch-group-label {
    font-size: var(--font-sm, 0.75rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--bs-secondary);
    margin-bottom: 0.5rem;
}
.hq-launch-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
}
.hq-launch-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.15rem;
    padding: 0.75rem 0.5rem;
    border-radius: var(--radius-md, 0.75rem);
    border: 1px solid var(--hq-border-color);
    background: var(--hq-surface, #fff);
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    -webkit-tap-highlight-color: transparent;
}
.hq-launch-tile:active { transform: scale(0.97); }
.hq-launch-tile__icon {
    width: 42px;
    height: 42px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    margin-bottom: 0.15rem;
}
.hq-launch-tile__label {
    font-weight: 700;
    font-size: var(--font-md, 0.85rem);
    color: var(--bs-body-color);
}
.hq-launch-tile__hint {
    font-size: var(--font-sm, 0.75rem);
    color: var(--bs-secondary);
    line-height: 1.2;
}

/* ── Mobile search drawer: results live INSIDE the drawer surface ── */
#mobileSearchDrawer { padding: 12px !important; }
.hq-search-results--mobile {
    position: static;
    width: auto;
    margin-top: 8px;
    max-height: 55vh;
    border: none;
}
.hq-search-results--mobile .hq-search-result {
    padding: 0.7rem 0.85rem;      /* comfortable thumb rows */
}
.hq-search-results--mobile .hq-search-result + .hq-search-result {
    border-top: 1px solid var(--hq-border-color);
    border-radius: 0;
}

/* ── Activity feed: day headers + quiet system entries ── */
/* Day headers match the omnibox group treatment: leather eyebrow +
   hairline rule, so chronology is the feed's one strong structural cue. */
.hq-feed-day {
    font-size: var(--font-xs, 0.65rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--hq-accent-warm-strong);
    margin: 1rem 0 0.6rem;
    padding-bottom: 0.3rem;
    border-bottom: 1px solid var(--hq-border-color);
}
.hq-feed-day:first-child { margin-top: 0; }

/* Timestamps are reference material, not content: smallest size, quiet
   ink, tabular digits so the right rail aligns. */
.feed-date {
    font-size: var(--font-xs, 0.65rem) !important;
    color: var(--bs-secondary);
    opacity: 0.75;
    font-variant-numeric: tabular-nums;
}

/* System events read as ambient context; human entries pop. */
.hq-activity--system { opacity: 0.66; }
.hq-activity--system h6 { font-weight: 500 !important; font-size: var(--font-md, 0.85rem); }
.hq-activity--system .feed-icon { transform: scale(0.85); }
.hq-activity--system:hover { opacity: 1; }

/* ── Quick-action tiles (project detail + lead detail) ── */
/* ── Quick Actions ── */
.quick-action-btn {
    height: 66px;
}

.quick-action-label {
    font-size: var(--font-sm, 0.75rem);  /* 12px legibility floor */
    font-weight: 500;
}

/* Quick-action tiles share one language: soft tinted fill per hue with
   AA text tones (a lone filled tile reads as the odd one out, and bare
   outlines wash out in sunlight). */
.quick-action-btn.btn-outline-primary { background: rgba(var(--bs-primary-rgb), 0.07); }
.quick-action-btn.btn-outline-success { color: var(--hq-text-success, #1E8449); background: rgba(var(--bs-success-rgb), 0.08); }
.quick-action-btn.btn-outline-success:hover, .quick-action-btn.btn-outline-success:active { color: #fff; }
.quick-action-btn.btn-outline-dark { background: rgba(33, 37, 41, 0.06); }
.quick-action-btn.btn-outline-warning {
    color: var(--hq-text-warning, #8F5600);
    background: rgba(var(--bs-warning-rgb), 0.12);
    border-color: rgba(var(--bs-warning-rgb), 0.55);
}
.quick-action-btn.btn-outline-warning:hover,
.quick-action-btn.btn-outline-warning:active {
    color: #fff;
    background: var(--hq-text-warning, #8F5600);
    border-color: var(--hq-text-warning, #8F5600);
}


/* ── Components: Area Shell ──
   Tier-2 navigation for a feature area. The global nav (base.html) picks the
   AREA; this picks the SECTION within it; object pages keep their own
   back-links inside the content. Turnovers is the first tenant — see
   docs/TURNOVERS_DOMAIN_ARCHITECTURE.md §4.1 before adding a second.

   Deliberately chrome, not content: a compact identity band plus underline
   tabs, so a page's own <h4> still reads as the page title. */
.hq-area-head {
    margin-bottom: var(--space-lg);
    border-bottom: 1px solid var(--hq-border-color);
}

.hq-area-identity {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin-bottom: var(--space-sm);
}

.hq-area-title {
    font-family: var(--font-display);
    font-stretch: var(--font-display-stretch);
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.1;
    margin: 0;
    color: var(--bs-body-color);
    text-decoration: none;
}
a.hq-area-title:hover { color: var(--bs-primary); }

/* Underline tabs. Scroll horizontally on narrow screens rather than wrapping
   into a second row — the band must stay one line tall on a phone. */
.hq-area-nav {
    display: flex;
    align-items: stretch;
    gap: var(--space-md);
    overflow-x: auto;
    scrollbar-width: none;
    margin-bottom: -1px;   /* tabs sit ON the head's bottom border */
}
.hq-area-nav::-webkit-scrollbar { display: none; }

.hq-area-nav__link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-sm) 0;
    white-space: nowrap;
    font-size: var(--font-md);
    font-weight: 600;
    color: var(--bs-secondary);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.hq-area-nav__link:hover {
    color: var(--bs-primary);
    border-bottom-color: var(--hq-border-strong);
}
.hq-area-nav__link.is-active {
    color: var(--bs-primary);
    border-bottom-color: var(--bs-primary);
}

/* Admin-only sections read as settings, not as peer workflow tabs. */
.hq-area-nav__link--admin { margin-left: auto; padding-left: var(--space-md); }
@media (max-width: 575.98px) {
    .hq-area-nav__link--admin { margin-left: 0; }
}

/* ── Object nav ────────────────────────────────────────────────────────
   Tier-3 of the navigation: facets of one record (a turnover's Overview /
   Time & Labor / Billing). Same underline language as .hq-area-nav one size
   down, so "these are views of the same thing" reads instantly — navigation
   must never dress as action buttons. */
.hq-object-nav {
    display: flex;
    align-items: stretch;
    gap: var(--space-lg);
    overflow-x: auto;
    scrollbar-width: none;
    border-bottom: 1px solid var(--hq-border-color);
}
.hq-object-nav::-webkit-scrollbar { display: none; }

.hq-object-nav__link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-sm) 0;
    white-space: nowrap;
    font-size: var(--font-md);
    font-weight: 600;
    color: var(--bs-secondary);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;   /* underline sits ON the nav's bottom border */
    transition: color 0.15s ease, border-color 0.15s ease;
}
.hq-object-nav__link:hover {
    color: var(--bs-primary);
    border-bottom-color: var(--hq-border-strong);
}
.hq-object-nav__link.is-active {
    color: var(--bs-primary);
    border-bottom-color: var(--bs-primary);
}

/* Trade-category badges. Colour is the fastest way to read a mixed list —
   on a task card it says what kind of work this is, on the shopping list
   grouped by category it labels the aisle you are standing in. Shared, so it
   lives here rather than in either page's sheet. */
.cat { font-weight: 600; }
.cat-painting        { background:var(--hq-cat-painting-bg); color:var(--hq-cat-painting-fg); }
.cat-plumbing        { background:var(--hq-cat-plumbing-bg); color:var(--hq-cat-plumbing-fg); }
.cat-light_bulbs     { background:var(--hq-cat-bulbs-bg); color:var(--hq-cat-bulbs-fg); }
.cat-general_repair  { background:var(--hq-cat-general-bg); color:var(--hq-cat-general-fg); }
.cat-toilet          { background:var(--hq-cat-toilet-bg); color:var(--hq-cat-toilet-fg); }
.cat-hardware_fixtures{ background:var(--hq-cat-hardware-bg); color:var(--hq-cat-hardware-fg); }
.cat-blinds          { background:var(--hq-cat-blinds-bg); color:var(--hq-cat-blinds-fg); }
.cat-drywall         { background:var(--hq-cat-drywall-bg); color:var(--hq-cat-drywall-fg); }
.cat-carpentry       { background:var(--hq-cat-carpentry-bg); color:var(--hq-cat-carpentry-fg); }
.cat-cleanup         { background:var(--hq-cat-cleanup-bg); color:var(--hq-cat-cleanup-fg); }
.cat-compliance      { background:var(--hq-cat-compliance-bg); color:var(--hq-cat-compliance-fg); }
.cat-air_filter      { background:var(--hq-cat-filter-bg); color:var(--hq-cat-filter-fg); }
.cat-trip_charge     { background:var(--hq-cat-general-bg); color:var(--hq-cat-general-fg); }

/* ── Inline editor ─────────────────────────────────────────────────────
   The surface every inline form in a card or a list row sits on.

   These forms used to be loose inputs stacked at the same level as the
   section headers around them, so opening one didn't read as "you are now
   editing" — it read as "three more things appeared." A panel, a title
   saying which job you are doing, and a footer that always looks the same
   are what turn a pile of fields back into a form.

   The fields themselves carry real labels. A placeholder is an example, not
   a name: it disappears exactly when a field is filled, which is the moment
   an *edit* form needs it most — the old one was three unlabelled boxes
   reading [2] [ea] [$5.50], and nobody who hadn't just typed them could say
   what the 2 was. */
.hq-editor {
    background: var(--hq-surface-sunken);
    border: 1px solid var(--hq-border-color);
    border-left: 3px solid var(--hq-panel-border);
    border-radius: var(--radius-sm);
    padding: var(--space-md);
    margin-top: var(--space-sm);
}
.hq-editor__title {
    font-size: var(--font-sm); font-weight: 700;
    color: var(--hq-ink-soft); margin-bottom: var(--space-sm);
}
.hq-field + .hq-field, .hq-field-row + .hq-field, .hq-field + .hq-field-row { margin-top: var(--space-sm); }
.hq-field__label {
    display: block; font-size: var(--font-xs); text-transform: uppercase;
    letter-spacing: .05em; font-weight: 700; color: var(--hq-ink-faint);
    margin-bottom: 2px;
}
.hq-field-row { display: flex; gap: var(--space-sm); align-items: flex-end; flex-wrap: wrap; }
.hq-field-row > .hq-field { margin-top: 0; }

/* Progressive disclosure. The essential shopping line is *what* and *how
   many*; the spec and the estimate are refinements you may not have yet, and
   asking for all five at once put the throwaway guess in the same row as the
   number that matters. Opens by default whenever the fields behind it are
   already filled — never hide data that exists. */
.hq-editor__more {
    background: none; border: 0; padding: 0; margin-top: var(--space-sm);
    font-size: var(--font-sm); color: var(--bs-primary); font-weight: 600;
}
.hq-editor__more:hover { color: var(--hq-primary-hover); }
.hq-editor__panel { margin-top: var(--space-sm); }

/* One footer shape everywhere: confirming action right, where the thumb
   already is; dismissing action to its left. Both are real buttons at the
   same size — Cancel used to be an underlined link, which is a navigation
   affordance doing an action's job. */
.hq-editor__foot {
    display: flex; justify-content: flex-end; align-items: center;
    gap: var(--space-sm); margin-top: var(--space-md);
}
/* Destructive gets the far side and a rule, never a neighbouring seat to the
   button you meant to press. */
.hq-editor__destroy { margin-right: auto; }

/* ── Checklist vocabulary (task cards + shopping list) ─────────────────
   These started life page-scoped to turnover_detail.html. The shopping list
   reuses the same row shapes — a big check target, a quoted work-order block,
   pill-shaped actions — and only ever loaded design_system.css, so every one
   of them rendered raw there: a browser checkbox with a loose tick beside it,
   an expanded panel with no panel. Shared markup needs a shared stylesheet.

   The names keep their original spelling rather than gaining an `hq-` prefix:
   renaming would touch every task-card template and its tests to change
   nothing a user sees. New reusable classes still take the prefix. */

/* Big, tappable checkbox (44px target, 26px box). The native input stays in
   the DOM, invisible, so it still reports state to screen readers and to
   anyone tabbing through; .tcheck-box is what you actually see. */
.tcheck { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; flex-shrink: 0; cursor: pointer; margin: 0; }
.tcheck-input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.tcheck-box {
    width: 26px; height: 26px; border: 2px solid var(--hq-control-border);
    border-radius: var(--radius-md); display: flex; align-items: center;
    justify-content: center; color: var(--hq-surface); transition: all .15s ease;
}
.tcheck-box .bi { opacity: 0; font-size: var(--font-lg); line-height: 1; }
.tcheck-input:checked + .tcheck-box { background: var(--bs-success); border-color: var(--bs-success); }
.tcheck-input:checked + .tcheck-box .bi { opacity: 1; }
.tcheck-static { width: 42px; display: flex; justify-content: center; flex-shrink: 0; }

/* Disclosure chevron on an expandable row. */
.chev .bi { transition: transform .15s ease; }
.chev.rot .bi { transform: rotate(180deg); }

/* Quoted source text (the PM's work-order wording) and its micro-label. */
.pm-block {
    background: var(--hq-panel-bg); border-left: 3px solid var(--hq-panel-border);
    border-radius: var(--radius-sm); padding: var(--space-sm) 10px;
    font-size: var(--font-md); color: var(--hq-panel-ink);
}
.pm-label {
    text-transform: uppercase; font-size: var(--font-xs); letter-spacing: .05em;
    color: var(--hq-ink-faint); font-weight: 700; margin-bottom: 2px;
}
.our-note {
    font-size: var(--font-md); color: var(--hq-note-fg); background: var(--hq-note-bg);
    border-radius: var(--radius-sm); padding: 6px 9px; cursor: pointer;
}

/* Pill-shaped action. Quiet by default — these sit in rows of three or four
   and none of them is the page's primary action. */
.chip {
    font-size: var(--font-sm); border: 1px solid var(--hq-panel-border);
    background: var(--hq-surface); color: var(--hq-cat-general-fg);
    border-radius: 999px; padding: 3px 10px; line-height: 1.4;
}
.chip:hover { background: var(--hq-surface-sunken); }
.chip-danger { color: var(--hq-danger-ink); border-color: var(--hq-danger-border); }
.chip-danger:hover { background: var(--hq-danger-tint); }

/* ── Shopping list ─────────────────────────────────────────────────────
   Built for a phone held one-handed in a store aisle: big check targets,
   the item description as the loudest thing on the row, everything else
   behind a tap. Shares the task card's visual language without depending
   on turnover_detail.html's page-scoped block. */

.shop-row { border-bottom: 1px solid var(--hq-border-color); }
.shop-row:last-child { border-bottom: 0; }
.shop-row.htmx-swapping { opacity: 0; transform: translateX(10px); transition: opacity .3s ease, transform .3s ease; }

.shop-group-body > .shop-row:last-child { border-bottom: 0; }

.shop-row-head { display: flex; align-items: flex-start; gap: var(--space-sm); padding: var(--space-sm); }
.shop-row-end { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }

.shop-desc { font-weight: 600; line-height: 1.25; word-break: break-word; }
.shop-desc.done { text-decoration: line-through; color: var(--hq-ink-soft); }
.shop-spec { font-size: var(--font-sm); color: var(--hq-ink-soft); margin-top: 1px; }
.shop-meta { font-size: var(--font-xs); color: var(--hq-ink-soft); white-space: nowrap; }
.shop-detail { font-size: var(--font-sm); }

/* One quiet meta line under the row instead of a stack of pills. It wraps,
   so a long room name doesn't push the price off the screen. */
.shop-meta-line {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
    font-size: var(--font-xs); color: var(--hq-ink-soft); margin-top: 2px;
}
.shop-note {
    font-size: var(--font-sm); color: var(--hq-note-fg); background: var(--hq-note-bg);
    border-radius: var(--radius-sm); padding: 3px 7px; margin-top: 4px;
    display: inline-block;
}
/* A figure with no judgment attached is ink; the paid figure is green because
   it means "this is settled", which is the good outcome. */
.shop-money { font-size: var(--font-xs); color: var(--hq-ink-soft); white-space: nowrap; }
.shop-money.is-paid { color: var(--hq-text-success); font-weight: 600; }

.shop-group-head {
    display: flex; justify-content: space-between; align-items: center;
    flex-wrap: wrap; gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    border-bottom: 1px solid var(--hq-border-color);
    background: var(--hq-surface-sunken);
}

/* Toolbar: search, then group + sort. Stacks on a phone, sits on one line
   from md up. */
.shop-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }
.shop-search { flex: 1 1 220px; min-width: 0; }
.shop-toolbar-selects { display: flex; align-items: center; gap: 6px; }
.shop-toolbar-selects .form-select { width: auto; }
/* Magnifier and spinner occupy the same slot; htmx adds .htmx-request to the
   form while a search is in flight and swaps which one is showing. */
#shop-searching { position: relative; }
.shop-search-busy { position: absolute; width: .8rem; height: .8rem; }
.shop-search-busy, .htmx-request .shop-search-idle { opacity: 0; }
.htmx-request .shop-search-busy { opacity: 1; }

/* Secondary destinations. These were three full-width outline buttons eating
   the first screen of a page used one-handed in a shop. */
.shop-links { display: flex; flex-wrap: wrap; gap: var(--space-md); font-size: var(--font-sm); }
.shop-links a { text-decoration: none; color: var(--hq-ink-soft); }
.shop-links a:hover { color: var(--bs-primary); }

/* The strip a row settles into once it leaves the filter you're looking at. */
.shop-settled {
    border-bottom: 1px solid var(--hq-border-color);
    padding: var(--space-sm);
    background: rgba(var(--bs-success-rgb), 0.06);
}
.shop-settled-head { display: flex; align-items: center; gap: var(--space-sm); }
.shop-settled-desc { font-weight: 600; }
/* Empty state inside a card section — a sentence, not a panel, with its
   action pushed to the right so it lines up under the section header's own
   control rather than opening a second left edge. */
.shop-empty { font-size: var(--font-sm); color: var(--hq-ink-soft); }
.shop-empty-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-sm); flex-wrap: wrap;
}
.shop-in-paid { width: 110px; flex-shrink: 0; }
.shop-summary { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }

.shop-thumb {
    width: 72px; height: 72px; object-fit: cover;
    border-radius: var(--radius-md); border: 1px solid var(--hq-border-color); display: block;
}
.shop-receipt-thumb {
    max-width: 220px; max-height: 220px; object-fit: contain;
    border-radius: var(--radius-md); border: 1px solid var(--hq-border-color);
}

/* Break-even room. Advisory, never a gate — amber is a nudge, red means the
   task is already under water. */
.shop-headroom {
    font-size: var(--font-sm);
    color: var(--hq-ink-soft);
    background: var(--hq-surface-sunken);
    border-radius: var(--radius-sm);
    padding: var(--space-xs) var(--space-sm);
}
.shop-headroom.is-warn {
    color: var(--hq-text-warning);
    background: rgba(var(--bs-warning-rgb), 0.12);
}
.shop-headroom.is-over {
    color: var(--bs-danger);
    background: rgba(var(--bs-danger-rgb), 0.10);
}

/* "Buy these together" — the same item wanted at several properties. */
.hq-dupe-card { border-left: 3px solid var(--hq-accent-warm); }

/* Allocation total stays in reach while you work down a long receipt. */
.hq-alloc-footer { position: sticky; bottom: var(--space-sm); z-index: 5; }

/* Material cost that came from a receipt rather than being typed. */
.task-derived-cost {
    font-size: var(--font-sm);
    color: var(--hq-ink-soft);
    background: var(--hq-surface-sunken);
    border-radius: var(--radius-sm);
    padding: var(--space-xs) var(--space-sm);
    display: inline-block;
}
.task-shop-items { font-size: var(--font-sm); }

/* Spend roll-up: what we spent, what bills back, what the job absorbs. */
.hq-spend-tile { display: flex; flex-wrap: wrap; gap: var(--space-lg); }
.hq-spend-tile > div { min-width: 90px; }
.hq-spend-label {
    font-size: var(--font-xs); text-transform: uppercase; letter-spacing: .05em;
    color: var(--hq-ink-soft); font-weight: 700;
}
.hq-spend-value { font-size: var(--font-lg); font-weight: 600; }
.hq-spend-value.is-absorbed { color: var(--bs-danger); }
.hq-spend-value.is-billable { color: var(--bs-success); }

/* ── Components: Record Card (mobile table replacement) ──
   A dense data table can't shrink to a phone: horizontal scroll hides the
   action column and makes the sort headers unreachable. Below the md
   breakpoint the same rows render as cards instead — one record per card,
   the scan-first field (status) top-right, the action a full-width target.
   Used by the Work Orders list; reusable for any record list. */
.hq-record-card {
    display: block;
    padding: var(--space-md);
    border-bottom: 1px solid var(--hq-border-color);
    text-decoration: none;
    color: inherit;
}
.hq-record-card:last-child { border-bottom: 0; }
.hq-record-card:active { background-color: var(--hq-surface-sunken); }

.hq-record-card__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-sm);
}

.hq-record-card__title {
    font-weight: 700;
    font-size: var(--font-lg);
    line-height: 1.2;
    color: var(--bs-body-color);
}

.hq-record-card__sub {
    font-size: var(--font-md);
    color: var(--hq-ink-soft);
    margin-top: 2px;
}

/* Label/value pairs. Two columns on a phone, so a card stays short enough
   that the action button is reachable without scrolling past it. */
.hq-record-card__meta {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-xs) var(--space-md);
    margin-top: var(--space-sm);
}
.hq-record-card__meta dt {
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bs-secondary);
    margin: 0;
}
.hq-record-card__meta dd {
    font-size: var(--font-md);
    color: var(--bs-body-color);
    margin: 0 0 var(--space-xs);
}

.hq-record-card__actions {
    display: flex;
    gap: var(--space-sm);
    margin-top: var(--space-sm);
}
.hq-record-card__actions > * { flex: 1; }
.hq-record-card__actions .btn {
    min-height: 44px;           /* thumb-sized, per the touch-target rule */
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ── Deal row (Sales → Follow-ups) ──
   The queue as a list item, not a card. Three lines on a phone: which deal,
   what to do and when, who and how much. One line on desktop, where the
   width buys a stage tag and a value column. Rows share a hairline inside one
   surface rather than each wearing a border, radius and shadow — the list is
   the object, the row is a line in it. One colour per row: the leading edge
   (red overdue, amber today), echoed by the due phrase and nothing else.
   See docs/sales_followups_list_spec.md. */
.hq-deal-list {
    display: flex;
    flex-direction: column;
    background: var(--hq-surface);
    border: 1px solid var(--hq-border-strong);
    border-radius: 0.75rem;
    overflow: hidden;
}
.hq-deal-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "main acts"
        "next acts"
        "meta acts";
    align-items: center;
    column-gap: var(--space-sm);
    padding: var(--space-sm) var(--space-sm) var(--space-sm) calc(var(--space-md) - 3px);
    border-left: 3px solid transparent;
    border-bottom: 1px solid var(--hq-border-color);
    min-height: 72px;
    cursor: pointer;
}
.hq-deal-row:last-child { border-bottom: 0; }
.hq-deal-row:active { background-color: var(--hq-surface-sunken); }
.hq-deal-row[hidden] { display: none; }
.hq-deal-row--overdue { border-left-color: var(--bs-danger); }
.hq-deal-row--today   { border-left-color: var(--bs-warning); }

.hq-deal-row__main { grid-area: main; min-width: 0; }
.hq-deal-row__title {
    display: block;
    font-weight: 700;
    font-size: var(--font-md);
    color: var(--bs-body-color);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.hq-deal-row__title:hover { color: var(--bs-primary); }
.hq-deal-row__client { display: none; }              /* on a phone the client is on line three */

.hq-deal-row__next {
    grid-area: next;
    min-width: 0;
    font-size: var(--font-md);
    font-weight: 500;
    color: var(--bs-body-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.hq-deal-row__none { color: var(--bs-secondary); font-style: italic; font-weight: 400; }
.hq-deal-row__due  { font-weight: 700; color: var(--bs-secondary); }

.hq-deal-row__meta {
    grid-area: meta;
    min-width: 0;
    font-size: var(--font-sm);
    color: var(--bs-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hq-deal-row__stage, .hq-deal-row__value, .hq-deal-row__when { display: none; }

.hq-deal-row__acts { grid-area: acts; display: flex; align-items: center; gap: var(--space-xs); }
.hq-deal-row__acts .btn {
    min-height: 44px;           /* thumb-sized, per the touch-target rule */
    display: inline-flex;
    align-items: center;
}
.hq-deal-row__more {
    border: 0;
    background: transparent;
    color: var(--bs-secondary);
    font-size: 1.1rem;
    width: 32px;
    height: 44px;
    display: grid;
    place-items: center;
    border-radius: 0.375rem;
    line-height: 1;
}
.hq-deal-row__more:hover { background: var(--hq-surface-sunken); color: var(--bs-body-color); }

@media (min-width: 992px) {
    .hq-deal-row {
        grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto 6.5rem 6.5rem auto;
        grid-template-areas: "main next stage value when acts";
        column-gap: var(--space-md);
        min-height: 52px;
    }
    .hq-deal-row__client { display: block; font-size: var(--font-sm); color: var(--bs-secondary); }
    .hq-deal-row__meta { display: none; }
    .hq-deal-row__next .hq-deal-row__due { display: none; }   /* the due phrase has its own column */
    .hq-deal-row__stage { display: inline-block; grid-area: stage; justify-self: start; }
    .hq-deal-row__value {
        display: block;
        grid-area: value;
        text-align: right;
        font-size: var(--font-md);
        font-weight: 600;
        color: var(--hq-ink-soft);
        font-variant-numeric: tabular-nums;
    }
    .hq-deal-row__when { display: block; grid-area: when; font-size: var(--font-md); font-weight: 600; color: var(--bs-secondary); }
    .hq-deal-row__acts .btn { min-height: 0; }
}

/* Filter pills scroll rather than wrapping into a three-row stack. */
.hq-pill-scroller {
    display: flex;
    gap: var(--space-sm);
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
}
.hq-pill-scroller::-webkit-scrollbar { display: none; }
.hq-pill-scroller > * { flex: 0 0 auto; }

/* Flex/grid children won't shrink below their content without this, so a long
   unbroken word pushes the row wider than the screen. */
.hq-min-w-0 { min-width: 0; }

/* The billing page's pinned totals bar. */
.hq-bill-totals {
    background: var(--bs-body-bg);
    z-index: 1020;                    /* above card shadows, below modals */
}

/* ── Components: Avatar ───────────────────────────────────────────────
   One shape for every "who" in the app: the navbar, the team list, and the
   profile page. A photo and an initials fallback must be the same circle at
   the same size, or the page reflows depending on who has uploaded one.

   Sizing is driven by --hq-avatar-size so a caller sets one custom property
   instead of needing a modifier class per surface. object-fit: cover is what
   makes the crop — nothing is cropped server-side, so a portrait photo and a
   landscape one both fill the circle. */
.hq-avatar {
    --hq-avatar-size: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 var(--hq-avatar-size);
    width: var(--hq-avatar-size);
    height: var(--hq-avatar-size);
    border-radius: var(--radius-pill);
    background: var(--hq-surface-sunken);
    color: var(--bs-body-color);
    /* Derived, so a caller sets one custom property and the initials scale
       with the circle instead of needing a second value kept in sync. */
    font-size: calc(var(--hq-avatar-size) * 0.4);
    font-weight: 700;
    line-height: 1;
    text-transform: uppercase;
    overflow: hidden;
    user-select: none;
}

.hq-avatar > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* For light surfaces that want the person to read as brand rather than as
   another quiet chip — the mobile menu header, where it is the only avatar. */
.hq-avatar--brand {
    background: var(--bs-primary);
    color: rgba(255, 255, 255, 0.95);
}

/* On the dark navbar the sunken surface disappears; give the initials their
   own contrast rather than inheriting a body color meant for white pages. */
.hq-avatar--on-dark {
    background: rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.95);
}

/* ── Calendar heatmap (Product Usage rhythm card) ─────────────────────
   One shaded square per day, weeks as columns. Cell color is an inline
   rgba(var(--bs-primary-rgb), alpha) computed per-cell in the view; the
   base square here is the zero-events state. 2px gaps do the separating
   (surface gap, not strokes). */
.hq-cal { display: flex; flex-direction: column; gap: 2px; overflow-x: auto; padding-bottom: 4px; }
.hq-cal-row { display: flex; gap: 2px; align-items: center; }
.hq-cal-daylabel { width: 2.1rem; font-size: .7rem; color: var(--bs-secondary); flex: none; }
.hq-cal-cell {
    width: 13px; height: 13px; border-radius: 3px; flex: none;
    background: var(--hq-surface-sunken, var(--hq-border-color));
}
.hq-cal-cell--pad { visibility: hidden; }

/* ── Components: Photo picker (profile) ───────────────────────────────
   The avatar doubles as the control that changes it — direct manipulation,
   the same gesture every app in this category uses. The button beside it is
   the discoverable spelling of the same action; both point at one hidden
   <input type="file">, so there is a single control to keep accessible.

   The input is visually-hidden rather than display:none so it stays in the
   tab order and keeps its accessible name. Because it is invisible, its focus
   ring would be too — :focus-within puts the ring on the avatar instead, so
   keyboard and switch users can see where they are (WCAG 2.4.7, same as the
   :focus-visible block above). */
.hq-photo-target {
    position: relative;
    display: inline-flex;
    cursor: pointer;
    border-radius: var(--radius-pill);
    flex: 0 0 auto;
}

/* The badge scales with the photo rather than being a fixed 1.75rem dot that
   looks like a speck on the large profile avatar and a wart on a small one. */
.hq-photo-target__hint {
    position: absolute;
    right: 4%;
    bottom: 4%;
    width: 2.25rem;
    height: 2.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    background: var(--bs-primary);
    color: rgba(255, 255, 255, 0.95);
    border: 3px solid var(--hq-surface);
    font-size: var(--font-md);
    line-height: 1;
}

.hq-photo-picker:focus-within .hq-photo-target {
    outline: 3px solid rgba(var(--bs-primary-rgb), 0.6);
    outline-offset: 2px;
}

/* The mobile tap-target rule above only expands .btn-sm, so a plain .btn is
   about 38px on a phone — under the 44px floor FRONTEND_GUIDELINES.md sets.
   These two are thumb-operated on a job site, so they get the floor at every
   width rather than only below the breakpoint; 44px on a desktop is merely
   generous. (The general .btn gap is real and app-wide, and is not this
   branch's to widen.) */
/* The cropper's window is round, because the avatar is. Cropper.js draws a
   square view box; these two rules are the only thing that makes what you are
   framing match what you will get. */
.hq-cropper {
    max-height: 60vh;
    background: var(--hq-surface-sunken);
    border-radius: var(--radius-md);
    overflow: hidden;
}
.hq-cropper > img { max-width: 100%; display: block; }
.hq-cropper .cropper-view-box,
.hq-cropper .cropper-face { border-radius: 50%; }
.hq-cropper .cropper-view-box { outline-color: rgba(255, 255, 255, 0.85); }

.hq-photo-picker .btn {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* The photo about to replace the current one, so Save is a confirmation
   rather than a leap. Dashed ring says "not committed yet" without needing a
   caption to explain itself. */
.hq-photo-target--pending .hq-avatar {
    box-shadow: 0 0 0 3px var(--hq-surface), 0 0 0 5px rgba(var(--bs-primary-rgb), 0.55);
}

/* Queued for removal: show what you are about to be left with (the initials),
   dimmed, so Remove previews its own outcome instead of describing it. */
.hq-photo-target--removing .hq-avatar {
    opacity: 0.45;
}

/* ── Components: Price book ──────────────────────────────────────────
   The catalog is a price LIST that happens to be editable, not a
   spreadsheet. Every field being a live input is what flattened it: forty
   rows of identical boxes, with the service name — the thing you scan for —
   rendered in exactly the same weight as the internal note beside it.

   So the row shows the service and the two numbers you sweep (price, each
   additional); everything that describes the service rather than prices it
   moves into a labelled panel that opens under the row. Same <form>, same
   single Save — a hidden row still submits — so bulk price edits survive the
   change. */

/* A <button>, so the whole header is the collapse target and the keyboard
   gets it for free — hence the explicit reset of the button chrome. */
.hq-pb-group-header {
    background-color: var(--hq-surface-sunken);
    padding: 0.6rem var(--space-md);
    border: 0;
    border-bottom: 1px solid var(--hq-border-color);
    color: inherit;
}
.hq-pb-group-header:hover { filter: brightness(0.985); }

/* Points down when open, right when shut — the one convention nobody has to
   be taught. */
.hq-pb-chevron {
    transition: transform 0.15s ease;
    color: var(--bs-secondary);
}
.hq-pb-group--collapsed .hq-pb-chevron { transform: rotate(-90deg); }

@media (prefers-reduced-motion: reduce) {
    .hq-pb-chevron { transition: none; }
}

.hq-pb-group-name {
    font-weight: 600;
    letter-spacing: 0.2px;
}

/* The service name, finally reading as a title rather than as the first of
   four stacked inputs. */
.hq-pb-service {
    font-weight: 600;
    color: var(--bs-body-color);
    line-height: 1.3;
}

/* The engine's code for this service. Present because task rows store it and
   invoices trace to it, quiet because nobody types it. */
.hq-pb-code {
    font-family: var(--bs-font-monospace, monospace);
    font-size: var(--font-xs);
    letter-spacing: 0.4px;
    color: var(--bs-secondary);
    background-color: var(--hq-surface-sunken);
    border-radius: var(--radius-sm);
    padding: 0.1rem 0.35rem;
}

.hq-pb-row > td { vertical-align: middle; }
.hq-pb-row:hover > td { background-color: rgba(var(--bs-primary-rgb), 0.03); }

/* Inactive services stay listed — they are history an invoice may still point
   at — but must not compete with the live catalog for attention. */
.hq-pb-row--off .hq-pb-service,
.hq-pb-row--off .hq-pb-meta { opacity: 0.55; }

.hq-pb-meta {
    font-size: var(--font-sm);
    color: var(--bs-secondary);
}

/* The row, swapped into its own form. It replaces the row rather than opening
   under it, so the accent edge and tint are the whole signal that this one
   line of the catalog is the thing being changed. */
.hq-pb-editing > td {
    background-color: rgba(var(--bs-primary-rgb), 0.06);
    border-left: 3px solid var(--bs-primary);
}

.hq-pb-editor { padding: var(--space-md); }

.hq-pb-fieldset-label {
    font-size: var(--font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--bs-secondary);
}

/* Tabular figures keep the decimal points in a line down the page instead of
   drifting with digit width. */
.hq-pb-money { font-variant-numeric: tabular-nums; }

/* Row actions stay out of the way until the row is under the pointer, but
   never vanish for keyboard users. */
.hq-pb-actions .btn { opacity: 0.7; transition: opacity 0.15s ease; }
.hq-pb-row:hover .hq-pb-actions .btn,
.hq-pb-row--editing .hq-pb-actions .btn,
.hq-pb-actions .btn:focus-visible,
.hq-pb-actions .btn[aria-expanded="true"] { opacity: 1; }

@media (max-width: 767.98px) {
    .hq-pb-actions .btn { opacity: 1; }
}

/* A group with nothing left in it after filtering, and the "no matches at
   all" line, are both driven from JS by toggling [hidden]. */
.hq-pb-group[hidden] { display: none; }

/* ══════════════════════════════════════════════════════════════
   PHOTO LIGHTBOX (templates/partials/_photo_lightbox.html)
   Dark, fullscreen, and the same control shapes as the document
   viewer (static/documents/css/viewer.css) so the two read as one.
   ══════════════════════════════════════════════════════════════ */
.photo-lightbox {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    flex-direction: column;
    -webkit-user-select: none;
    user-select: none;
}
.photo-lightbox.active { display: flex; }
.photo-lightbox__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(var(--bs-black-rgb), 0.92);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
}
.photo-lightbox__topbar {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0.75rem;
    padding-top: max(0.6rem, env(safe-area-inset-top));
    color: var(--bs-white);
}
.photo-lightbox__heading { min-width: 0; flex: 1 1 auto; }
.photo-lightbox__title {
    font-weight: 700;
    font-size: var(--font-md);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.photo-lightbox__counter {
    font-size: var(--font-sm);
    color: rgba(var(--bs-white-rgb), 0.65);
}
.photo-lightbox__actions { display: flex; gap: 0.25rem; flex: 0 0 auto; }
.photo-lightbox__btn {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid rgba(var(--bs-white-rgb), 0.12);
    border-radius: var(--radius-md);
    background: rgba(var(--bs-white-rgb), 0.1);
    color: var(--bs-white);
    font-size: var(--font-lg);
    text-decoration: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.photo-lightbox__btn:hover { background: rgba(var(--bs-white-rgb), 0.2); color: var(--bs-white); }
.photo-lightbox__btn:active { background: rgba(var(--bs-white-rgb), 0.28); }
.photo-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    width: 48px;
    height: 48px;
    border: none;
    border-radius: var(--radius-md);
    background: rgba(var(--bs-white-rgb), 0.1);
    color: var(--bs-white);
    font-size: 1.3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0.6;
    transition: opacity 0.15s ease, background 0.15s ease;
}
.photo-lightbox__nav:hover { background: rgba(var(--bs-white-rgb), 0.2); opacity: 1; }
.photo-lightbox__nav--prev { left: 16px; }
.photo-lightbox__nav--next { right: 16px; }
.photo-lightbox__nav[disabled] { opacity: 0.15; cursor: default; pointer-events: none; }
@media (max-width: 767px) {
    .photo-lightbox__nav { display: none; }   /* swipe instead */
}
.photo-lightbox__stage {
    position: relative;
    z-index: 1;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    touch-action: none;   /* pinch, pan and swipe are handled in JS */
}
.photo-lightbox__stage.is-zoomed { cursor: grab; }
.photo-lightbox__img {
    max-width: 92%;
    max-height: 82vh;
    object-fit: contain;
    border-radius: var(--radius-md);
    transition: opacity 0.2s ease;
    will-change: transform;
    transform-origin: center center;
}
.photo-lightbox__img.loading { opacity: 0.3; }
.photo-lightbox__caption {
    position: relative;
    z-index: 2;
    padding: 0.6rem 1rem;
    padding-bottom: max(0.6rem, env(safe-area-inset-bottom));
    color: rgba(var(--bs-white-rgb), 0.7);
    font-size: var(--font-sm);
    text-align: center;
}
.photo-lightbox__caption[hidden] { display: none; }

/* ── The invoice editor (turnover billing) ───────────────────────────
   One row per task: the work on top (what they said, what the crew did,
   tiny photo squares), the editable line underneath. Evidence is clamped,
   not folded — a tap un-clamps it — so nothing a price depends on is ever
   behind a disclosure. See docs/turnover_invoice_editor_design.md. */
.hq-inv-row + .hq-inv-row { border-top: 1px solid var(--hq-border-color); }
.hq-inv-row.is-unbilled { background: rgba(var(--bs-warning-rgb), .06); }
.hq-inv-row.is-cancelled { color: var(--bs-secondary-color); }
.hq-inv-row.is-cancelled .hq-ev-text { text-decoration: line-through; }
.hq-inv-line { margin-top: .25rem; }
.hq-inv-line.is-deleted { opacity: .45; }
.hq-inv-line.is-deleted input, .hq-inv-line.is-deleted select { text-decoration: line-through; }
.hq-inv-line.has-errors { outline: 1px solid rgba(var(--bs-danger-rgb), .5); border-radius: var(--bs-border-radius); padding: .25rem; }
.hq-inv-line-ro { padding-left: 1.6rem; }          /* aligns under the evidence text */
.hq-inv-qty { width: 4.25rem; flex: 0 0 auto; }
.hq-inv-rate { width: 7rem; flex: 0 0 auto; }
.hq-inv-amount { min-width: 4.5rem; text-align: right; }

/* Evidence text: two lines for the instruction, one for the crew note. */
.hq-ev-text { cursor: pointer; }
.hq-ev-text > span:first-child { font-size: var(--font-xs); text-transform: uppercase; letter-spacing: .02em; margin-right: .25rem; }
.hq-clamp-2, .hq-clamp-1 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.hq-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.hq-clamp-1 { -webkit-line-clamp: 1; line-clamp: 1; }
.hq-clamp-2.is-open, .hq-clamp-1.is-open { -webkit-line-clamp: unset; line-clamp: unset; display: block; }

/* Tiny photo squares: enough to see there is a photo and roughly what of;
   the tap opens it. A green edge marks an "after". */
.hq-ev-thumb { display: block; width: 40px; height: 40px; }
.hq-ev-thumb img {
    width: 100%; height: 100%; object-fit: cover;
    border-radius: var(--bs-border-radius-sm);
    border: 2px solid var(--hq-border-color);
}
.hq-ev-thumb.is-after img { border-color: var(--bs-success); }
.hq-ev-more { font-size: var(--font-xs); align-self: center; }
