/* ─────────────────────────────────────────────────────────────────────────────
   FieldFlow components — the shared building blocks every page should reach for
   instead of inventing its own.

   Rules this file keeps (and pages should too):
   • Colours come ONLY from --ff-* tokens (defined per theme in _Layout). A hard-coded
     #fff or #1e293b is how the light themes kept breaking.
   • ACTIONS and STATUSES never look alike. An action is a bordered button with a verb
     ("Mark paid"); a status is a soft, borderless pill with a state ("Paid"). The
     invoicing "✓ Paid" button that read as already-paid is the bug this prevents.
   • Hierarchy comes from size and weight, not from icons in pastel squares or
     tracked all-caps labels.
   • Money and counts use tabular numerals so columns of figures line up.

   First adopters: Home (Pages/Index) and Invoicing (Pages/Reports/Index). Move other
   pages onto these as they're touched for bugs — no big-bang rewrite.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
    --ff-radius: 12px;
    --ff-radius-sm: 8px;
}

/* ── Page header ───────────────────────────────────────────────────────────── */
.ff-page-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    flex-wrap: wrap; gap: 12px 16px;
    margin: 24px 0 18px;
}
.ff-page-head h1 {
    font-size: 1.55rem; font-weight: 700; letter-spacing: -0.015em;
    color: var(--ff-heading); margin: 0; line-height: 1.2;
}
.ff-eyebrow { font-size: 0.85rem; color: var(--ff-text-muted); margin-bottom: 2px; }
.ff-eyebrow a { color: inherit; text-decoration: none; }
.ff-eyebrow a:hover { color: var(--ff-accent); }
.ff-page-sub { font-size: 0.88rem; color: var(--ff-text-muted); margin-top: 3px; }
.ff-page-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* On phones the layout's menu button is fixed at the top-left (≤768px, site.css); the
   page head always sits at the top, so indent it beside that button instead of under it. */
@media (max-width: 768px) {
    .ff-page-head { margin-top: 12px; }
    .ff-page-head > div:first-child { padding-left: 50px; min-height: 40px; }
    .ff-page-head h1 { font-size: 1.3rem; }
}

/* _Layout's own <style> sets `.main-content h1/h2/h3` sizes. It loads AFTER this file at the
   same specificity, so without these scoped repeats every component heading rendered at the
   layout's generic 1.6rem / 1.35rem and the phone h1 size never applied. */
.main-content .ff-page-head h1 { font-size: 1.55rem; }
.main-content .ff-section-title h2 { font-size: 1rem; }
.main-content .ff-card-head h2, .main-content .ff-card-head h3 { font-size: 0.98rem; }
@media (max-width: 768px) { .main-content .ff-page-head h1 { font-size: 1.3rem; } }

/* ── Section title: sentence case, one weight step above body ──────────────── */
.ff-section-title {
    display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
    margin: 28px 0 10px;
}
.ff-section-title h2 {
    font-size: 1rem; font-weight: 650; color: var(--ff-heading); margin: 0;
}
.ff-section-title .ff-meta,
.ff-section-title a { font-size: 0.82rem; color: var(--ff-text-muted); text-decoration: none; }
.ff-section-title a:hover { color: var(--ff-accent); }

/* ── Card ──────────────────────────────────────────────────────────────────── */
.ff-card {
    background: var(--ff-surface);
    border: 1px solid var(--ff-border);
    border-radius: var(--ff-radius);
    padding: 18px 20px;
    min-width: 0;
}
.ff-card-head {
    display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 4px 10px;
    margin-bottom: 14px;
}
.ff-card-head h2, .ff-card-head h3 {
    font-size: 0.98rem; font-weight: 650; color: var(--ff-heading); margin: 0;
}
.ff-card code, .ff-page-sub code, .ff-callout code { color: var(--ff-info); font-size: 0.9em; }
.ff-card-head .ff-meta { font-size: 0.8rem; color: var(--ff-text-muted); }
.ff-card-head a.ff-meta { text-decoration: none; }
.ff-card-head a.ff-meta:hover { color: var(--ff-accent); }

.ff-grid-2 {
    display: grid; gap: 16px;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    align-items: start;
}
@media (max-width: 991px) { .ff-grid-2 { grid-template-columns: minmax(0, 1fr); } }

/* ── KPI: a number, what it is, and optional context ───────────────────────── */
.ff-kpis {
    display: grid; gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.ff-kpi {
    display: block; min-width: 0;
    background: var(--ff-surface);
    border: 1px solid var(--ff-border);
    border-radius: var(--ff-radius);
    padding: 14px 16px;
    color: inherit; text-decoration: none;
    transition: border-color 0.15s ease;
}
a.ff-kpi:hover { border-color: var(--ff-border-strong); color: inherit; }
.ff-kpi-label { font-size: 0.8rem; font-weight: 500; color: var(--ff-text-muted); }
.ff-kpi-value {
    font-size: 1.55rem; font-weight: 700; line-height: 1.15; letter-spacing: -0.015em;
    color: var(--ff-heading); margin-top: 4px;
    font-variant-numeric: tabular-nums;
}
.ff-kpi-sub { font-size: 0.78rem; color: var(--ff-text-dim); margin-top: 4px; }
/* The one number a page is really about: the same card, visibly louder. */
.ff-kpi--lead { border-color: var(--ff-border-strong); }
.ff-kpi--lead .ff-kpi-value { font-size: 2rem; }
/* A row of five or six totals (Crew Board). The default 170px floor wraps five tiles 4+1 at
   laptop width; this fits them on one line and still folds to two columns on a phone. */
.ff-kpis--compact { grid-template-columns: repeat(auto-fit, minmax(135px, 1fr)); gap: 10px; }
.ff-kpis--compact .ff-kpi { padding: 12px 14px; }
.ff-kpis--compact .ff-kpi-value { font-size: 1.35rem; }
/* Exactly two per row at every width (Time Clock's 2×2 beside the clock) — auto-fit would
   leave four tiles 3+1 in a half-width column. */
.ff-kpis--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.is-good { color: var(--ff-good) !important; }
.is-warn { color: var(--ff-warn) !important; }
.is-bad  { color: var(--ff-bad)  !important; }

.ff-trend { font-size: 0.78rem; font-weight: 600; white-space: nowrap; }
.ff-trend.up { color: var(--ff-good); }
.ff-trend.down { color: var(--ff-bad); }
.ff-trend.flat { color: var(--ff-text-dim); }

/* A big figure inside a card (not a card of its own). */
.ff-figure { font-size: 2.1rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1;
             color: var(--ff-heading); font-variant-numeric: tabular-nums; }
.ff-figure small { font-size: 0.95rem; font-weight: 500; color: var(--ff-text-muted); letter-spacing: 0; }

/* ── Ledger: label / value rows — many related numbers without a wall of tiles ── */
.ff-ledger { margin: 0; }
.ff-ledger > div {
    display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
    padding: 9px 0; border-bottom: 1px solid var(--ff-border);
}
.ff-ledger > div:last-child { border-bottom: 0; padding-bottom: 0; }
.ff-ledger dt { font-weight: 400; font-size: 0.88rem; color: var(--ff-text-muted); margin: 0; }
.ff-ledger dt a { color: inherit; text-decoration: none; }
.ff-ledger dt strong { color: var(--ff-heading); font-weight: 500; }
.ff-ledger dt a:hover { color: var(--ff-accent); }
.ff-ledger dd {
    margin: 0; font-size: 0.95rem; font-weight: 600; color: var(--ff-heading);
    font-variant-numeric: tabular-nums; text-align: right;
}
.ff-ledger > div.is-total { border-bottom: 0; }
.ff-ledger > div.is-total dt { color: var(--ff-text); font-weight: 600; }
.ff-ledger > div.is-total dd { font-size: 1.05rem; }

/* ── Progress bar ──────────────────────────────────────────────────────────── */
.ff-progress {
    height: 8px; border-radius: 999px; overflow: hidden;
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
}
.ff-progress > span { display: block; height: 100%; background: var(--ff-good); border-radius: inherit; }
/* The bar's colour says how it's going; the width is data, set inline (style="width:40%"). */
.ff-progress--info > span { background: var(--ff-info); }
.ff-progress--warn > span { background: var(--ff-warn); }
.ff-progress--bad > span  { background: var(--ff-bad); }
/* Segments side by side in one bar (Weekly List: done, then skipped) — each span's width is data. */
.ff-progress--stack { display: flex; }
.ff-progress--stack > span { flex-shrink: 0; border-radius: 0; }
.ff-progress > span.ff-seg--warn { background: var(--ff-warn); }

/* ── Tabs (put on a Bootstrap .nav so data-bs-toggle keeps working) ───────── */
.ff-tabs {
    display: flex; flex-wrap: nowrap; gap: 22px;
    border-bottom: 1px solid var(--ff-border);
    margin: 22px 0 16px; padding: 0;
    overflow-x: auto; scrollbar-width: none;
}
.ff-tabs::-webkit-scrollbar { display: none; }
.ff-tabs .nav-item { flex: 0 0 auto; }
.ff-tabs .nav-link {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 10px 0; margin-bottom: -1px;
    background: none !important; border: 0; border-bottom: 2px solid transparent; border-radius: 0;
    color: var(--ff-text-muted); font-size: 0.9rem; font-weight: 500; white-space: nowrap;
}
.ff-tabs .nav-link:hover { color: var(--ff-text); }
.ff-tabs .nav-link.active { color: var(--ff-heading); border-bottom-color: var(--ff-accent); font-weight: 600; }
.ff-count {
    font-size: 0.72rem; font-weight: 600; line-height: 1;
    padding: 3px 7px; border-radius: 999px;
    background: var(--ff-surface-2); color: var(--ff-text-muted); border: 1px solid var(--ff-border);
    font-variant-numeric: tabular-nums;
}
.ff-tabs .nav-link.active .ff-count { color: var(--ff-heading); }

/* ── Chips: filters and quick ranges ───────────────────────────────────────── */
.ff-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ff-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 12px; border-radius: 999px;
    border: 1px solid var(--ff-border); background: transparent;
    color: var(--ff-text-muted); font-size: 0.82rem; font-weight: 500; line-height: 1.3;
    text-decoration: none; cursor: pointer; white-space: nowrap;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.ff-chip:hover { color: var(--ff-text); border-color: var(--ff-border-strong); }
.ff-chip.active, .ff-chip[aria-pressed="true"] {
    color: var(--ff-heading); background: var(--ff-accent-soft); border-color: var(--ff-accent);
}

/* ── Date range: two date inputs, Apply, and preset chips in one wrapping row ─────────
   <form class="ff-range"> <input type=date class="form-control form-control-sm ff-input">
   <span class="sep">–</span> <input …> <button class="ff-btn ff-btn--sm">Apply</button>
   <span class="ff-chips">…</span> </form> */
.ff-range { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-bottom: 18px; }
.ff-range input[type=date] { width: 150px; }
.ff-range .sep { color: var(--ff-text-dim); }
@media (max-width: 768px) { .ff-range input[type=date] { width: calc(50% - 20px); } }

/* A table's total row. */
.ff-table > tfoot > tr > td {
    font-weight: 700; color: var(--ff-heading); font-variant-numeric: tabular-nums;
    border-top: 2px solid var(--ff-border-strong); border-bottom: 0;
}

/* ── Buttons (actions) ─────────────────────────────────────────────────────── */
.ff-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    padding: 6px 12px; border-radius: var(--ff-radius-sm);
    border: 1px solid var(--ff-border-strong); background: var(--ff-surface);
    color: var(--ff-text); font-size: 0.84rem; font-weight: 600; line-height: 1.3;
    text-decoration: none; cursor: pointer; white-space: nowrap;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.ff-btn:hover { color: var(--ff-heading); border-color: var(--ff-accent); }
/* .disabled covers <a class="ff-btn"> that JS disables while a request is in flight. */
.ff-btn:disabled, .ff-btn.disabled { opacity: 0.55; cursor: default; pointer-events: none; }
.ff-btn i { font-size: 0.85em; }
.ff-btn--sm { padding: 3px 9px; font-size: 0.76rem; border-radius: 7px; }
/* Money-in actions: neutral at rest, green on intent — never a filled green pill. */
.ff-btn--pay:hover { color: var(--ff-good); border-color: var(--ff-good); }
.ff-btn--danger:hover { color: var(--ff-bad); border-color: var(--ff-bad); }
.ff-btn--quiet { border-color: transparent; background: transparent; color: var(--ff-text-muted); }
.ff-btn--quiet:hover { background: var(--ff-surface-2); border-color: var(--ff-border); }
/* The one committing action of a modal or form ("Save", "Add job"). Tinted, not a solid
   fill: there is no on-accent text token, and --ff-heading reads on the tint in every theme. */
.ff-btn--primary { background: var(--ff-accent-soft); border-color: var(--ff-accent); color: var(--ff-heading); }
.ff-btn--primary:hover { background: color-mix(in srgb, var(--ff-accent) 45%, transparent); }
.ff-btn--block { display: flex; width: 100%; padding-top: 9px; padding-bottom: 9px; }
/* A field action tapped with a thumb (Time Clock's Clock in / Clock out): 48px tall. */
.ff-btn--lg { min-height: 48px; min-width: 130px; padding: 10px 18px; font-size: 1rem; }
/* Field actions the crew find by colour (Weekly List: Start is amber, Done is green). Tinted AND
   bordered, so still plainly a button — never the soft borderless pill of a .ff-status. The tint
   mixes into the surface rather than transparent, so it stays opaque on a floating control. */
.ff-btn--good { background: color-mix(in srgb, var(--ff-good) 14%, var(--ff-surface)); border-color: color-mix(in srgb, var(--ff-good) 60%, var(--ff-border)); color: var(--ff-heading); }
.ff-btn--good > i { color: var(--ff-good); }
.ff-btn--good:hover { background: color-mix(in srgb, var(--ff-good) 24%, var(--ff-surface)); border-color: var(--ff-good); }
.ff-btn--warn { background: color-mix(in srgb, var(--ff-warn) 14%, var(--ff-surface)); border-color: color-mix(in srgb, var(--ff-warn) 60%, var(--ff-border)); color: var(--ff-heading); }
.ff-btn--warn > i { color: var(--ff-warn); }
.ff-btn--warn:hover { background: color-mix(in srgb, var(--ff-warn) 24%, var(--ff-surface)); border-color: var(--ff-warn); }
/* A big either/or choice: the verb, and under it a line saying what it does (Complete job's
   "Mark complete & paid" / "& bill"). <button class="ff-btn ff-btn--block ff-btn--stack">
   <span>…verb…</span><span class="ff-btn-sub">…</span></button> */
.ff-btn--stack { flex-direction: column; gap: 2px; padding: 12px 14px; white-space: normal; text-align: center; }
.ff-btn-sub { font-size: 0.74rem; font-weight: 400; color: var(--ff-text-muted); }

/* ── Status pills (states, not buttons) ────────────────────────────────────── */
.ff-status {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 2px 9px; border-radius: 999px;
    font-size: 0.74rem; font-weight: 600; line-height: 1.5; white-space: nowrap;
    background: var(--ff-surface-2); color: var(--ff-text-muted);
}
.ff-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.ff-status--good { color: var(--ff-good); background: color-mix(in srgb, var(--ff-good) 14%, transparent); }
.ff-status--warn { color: var(--ff-warn); background: color-mix(in srgb, var(--ff-warn) 15%, transparent); }
.ff-status--bad  { color: var(--ff-bad);  background: color-mix(in srgb, var(--ff-bad) 14%, transparent); }
.ff-status--info { color: var(--ff-info); background: color-mix(in srgb, var(--ff-info) 14%, transparent); }
.ff-status--plain::before { display: none; }
/* Something running right now ("On the clock"): the dot pulses. */
.ff-status--live::before { animation: ff-pulse 1.5s ease-in-out infinite; }
@keyframes ff-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .ff-status--live::before { animation: none; } }

/* A labelled category (payment method, job type): a coloured dot + plain text. */
.ff-tag { display: inline-flex; align-items: center; gap: 7px; font-size: 0.85rem; color: var(--ff-text); white-space: nowrap; }
.ff-tag::before { content: ""; width: 8px; height: 8px; border-radius: 2px; background: var(--ff-tag, var(--ff-text-dim)); }

/* A data colour on its own (a crew, a category) — the colour is data, so it's set inline:
   <span class="ff-dot" style="--ff-dot:@crew.Color"></span>. Anything else stays on tokens. */
.ff-dot {
    display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
    background: var(--ff-dot, var(--ff-text-dim)); vertical-align: middle;
}

/* ── Callout: something needs a decision ───────────────────────────────────── */
.ff-callout {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 12px 16px; border-radius: var(--ff-radius);
    border: 1px solid color-mix(in srgb, var(--ff-warn) 45%, var(--ff-border));
    background: color-mix(in srgb, var(--ff-warn) 8%, var(--ff-surface));
    color: var(--ff-text); font-size: 0.9rem;
}
.ff-callout > i { color: var(--ff-warn); }
/* The message grows and shrinks beside the icon; without this a long message wraps onto
   its own line on a phone and leaves the icon stranded above it. */
.ff-callout > :not(i):not(.ff-callout-actions):not(.btn-close) { flex: 1 1 220px; min-width: 0; }
/* Dismissible (a TempData banner): add Bootstrap's `alert fade show` to the callout — its
   close script looks for .alert — and end it with
   <button class="btn-close btn-close-white" data-bs-dismiss="alert" aria-label="Close">. */
.ff-callout.alert { margin-bottom: 16px; }
.ff-callout > .btn-close { margin-left: auto; flex-shrink: 0; font-size: 0.7rem; }
.ff-callout .ff-callout-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
/* Confirmation after a save ("Rates updated") — same shape, nothing to decide. */
.ff-callout--good {
    border-color: color-mix(in srgb, var(--ff-good) 40%, var(--ff-border));
    background: color-mix(in srgb, var(--ff-good) 8%, var(--ff-surface));
}
.ff-callout--good > i { color: var(--ff-good); }
/* A tip or explanation — nothing wrong, nothing to decide. */
.ff-callout--info {
    border-color: color-mix(in srgb, var(--ff-info) 40%, var(--ff-border));
    background: color-mix(in srgb, var(--ff-info) 8%, var(--ff-surface));
}
.ff-callout--info > i { color: var(--ff-info); }
.ff-callout ul { margin: 0; padding-left: 1.1rem; }
/* Something failed ("Could not save") — same shape again. */
.ff-callout--bad {
    border-color: color-mix(in srgb, var(--ff-bad) 40%, var(--ff-border));
    background: color-mix(in srgb, var(--ff-bad) 8%, var(--ff-surface));
}
.ff-callout--bad > i { color: var(--ff-bad); }

.ff-callout a:not(.ff-btn) { color: var(--ff-info); }

/* ── Theme picker (Settings → Appearance). Each .ff-theme-card carries data-theme="<key>",
   and _Layout's theme token blocks are unscoped [data-theme] selectors — so INSIDE a card the
   --ff-* tokens are that card's own theme. The meta strip uses tokens and always pairs its
   text with its background from the same theme; the preview swatches are inline because they
   are data (a picture of each theme), like a crew colour. _Layout's script toggles .active. ── */
.ff-theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 14px; }
.ff-theme-card {
    display: flex; flex-direction: column; padding: 0; overflow: hidden; text-align: left; cursor: pointer;
    background: var(--ff-surface); border: 2px solid var(--ff-border); border-radius: var(--ff-radius);
    transition: transform 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.ff-theme-card:hover { transform: translateY(-3px); border-color: var(--ff-border-strong); }
.ff-theme-card.active { border-color: var(--ff-accent); box-shadow: 0 0 0 3px var(--ff-accent-soft); }
.ff-tc-preview { display: flex; height: 64px; }
.ff-tc-sidebar { width: 26px; flex-shrink: 0; }
.ff-tc-body { flex: 1; padding: 8px; display: flex; align-items: flex-start; }
.ff-tc-surface { width: 100%; height: 100%; border-radius: 5px; border: 1px solid; padding: 6px; display: flex; align-items: center; gap: 5px; }
.ff-tc-accent { width: 14px; height: 14px; border-radius: 4px; flex-shrink: 0; }
.ff-tc-lines { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.ff-tc-line { height: 3px; border-radius: 2px; opacity: 0.55; }
.ff-tc-line.short { width: 60%; opacity: 0.3; }
.ff-tc-meta {
    display: flex; align-items: center; justify-content: space-between; gap: 6px;
    padding: 7px 10px; background: var(--ff-surface-2); border-top: 1px solid var(--ff-border);
}
.ff-tc-name { font-size: 0.8rem; font-weight: 600; color: var(--ff-heading); }

/* ── Overlay dialog: a lightweight modal a page opens by setting style.display = 'flex'
   (the Weekly List and Crew Board reschedule dialogs). Bootstrap .modal is still fine
   elsewhere. The scrim stays dark in every theme, like Bootstrap's backdrop. ─────────── */
:root { --ff-scrim: rgba(0, 0, 0, 0.6); }
.ff-overlay {
    position: fixed; inset: 0; z-index: 1050;
    display: none; align-items: center; justify-content: center;
    padding: 16px; background: var(--ff-scrim);
}
.ff-overlay-dialog {
    width: 360px; max-width: 100%; max-height: 100%; overflow-y: auto;
    background: var(--ff-surface); color: var(--ff-text);
    border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 22px;
}
/* A page whose script toggles a class instead (the Weekly List's route-loading overlay). */
.ff-overlay.active { display: flex; }
.ff-overlay-dialog h2 { font-size: 1.05rem; font-weight: 650; color: var(--ff-heading); margin: 0 0 4px; }
.main-content .ff-overlay-dialog h2 { font-size: 1.05rem; }

/* ── Tables ────────────────────────────────────────────────────────────────── */
.ff-table { width: 100%; }
.ff-table > thead > tr > th {
    font-size: 0.78rem; font-weight: 600; color: var(--ff-text-muted);
    text-transform: none; letter-spacing: 0;
    border-bottom: 1px solid var(--ff-border-strong); white-space: nowrap;
}
.ff-table > tbody > tr > td { vertical-align: middle; border-color: var(--ff-border); }
.ff-table .ff-num, .ff-num { text-align: right; font-variant-numeric: tabular-nums; }
/* A column that must not squash (a status message, a "maps to" select). */
.ff-table .ff-wide, .ff-table td > .form-select { min-width: 160px; }
/* Links inside a table read as links on the theme's info colour, not Bootstrap's fixed blue. */
.ff-table a:not(.ff-btn) { color: var(--ff-info); text-decoration: none; }
.ff-table a:not(.ff-btn):hover { text-decoration: underline; }
/* DataTables right-aligns any column it detects as a date; dates read left-to-right. */
.ff-table th.dt-type-date, .ff-table td.dt-type-date { text-align: left; }
.ff-muted { color: var(--ff-text-muted) !important; }
.ff-dim { color: var(--ff-text-dim) !important; }

/* ── Form controls that follow the theme (replaces bg-dark text-white border-secondary) ── */
.ff-input {
    background-color: var(--ff-surface-2) !important; color: var(--ff-text) !important;
    border: 1px solid var(--ff-border) !important; border-radius: var(--ff-radius-sm) !important;
    font-size: 0.85rem;
}
/* Bootstrap 5.1's select arrow is a fixed dark grey, invisible on a dark surface. An SVG data
   URI can't read --ff-* tokens, so there is one arrow per theme family — the only literal
   colours in this file. (background-color above, not the shorthand, so the image survives.) */
.form-select.ff-input {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
}
[data-theme="paper"] .form-select.ff-input, [data-theme="daylight"] .form-select.ff-input {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23586259' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
}
.ff-input:focus { border-color: var(--ff-accent) !important; box-shadow: 0 0 0 3px var(--ff-accent-soft) !important; }

/* A narrow, right-aligned number box (price, quantity, percent) — add to .ff-input. */
.ff-input--num { width: 92px; text-align: right; font-variant-numeric: tabular-nums; }

textarea.ff-input { resize: vertical; }

/* A labelled form field.Labels are sentence case at body weight+1 — not tracked caps. */
.ff-field { margin-bottom: 14px; }
.ff-label { display: block; margin-bottom: 5px; font-size: 0.84rem; font-weight: 600; color: var(--ff-text-strong); }
.ff-label .ff-muted { font-weight: 400; }
.ff-hint { font-size: 0.76rem; color: var(--ff-text-dim); margin-top: 4px; }
.ff-hint a { color: var(--ff-info); }

/* A checkbox with a title and a line of explanation; the whole row is the click target.
   <label class="ff-checkrow" for="x"><input type="checkbox" class="form-check-input" id="x">
   <span><span class="ff-checkrow-title">…</span><span class="ff-hint">…</span></span></label>
   --warn tints it for a choice with consequences (Complete job's "Last visit"). */
.ff-checkrow {
    display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
    padding: 11px 12px; margin-bottom: 14px; border-radius: var(--ff-radius-sm);
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
}
.ff-checkrow > .form-check-input { flex-shrink: 0; margin: 2px 0 0; }
.ff-checkrow-title { display: block; font-size: 0.9rem; font-weight: 600; color: var(--ff-heading); }
.ff-checkrow .ff-hint { display: block; margin-top: 2px; }
.ff-checkrow--warn {
    border-color: color-mix(in srgb, var(--ff-warn) 40%, var(--ff-border));
    background: color-mix(in srgb, var(--ff-warn) 9%, var(--ff-surface));
}

/* ── Switch: an on/off setting. Markup: <label class="ff-switch"><input type="checkbox"><span></span></label>
   The input stays a real checkbox, so pages keep reading .checked. ───────── */
.ff-switch { position: relative; display: inline-block; width: 36px; height: 20px; flex-shrink: 0; margin: 0; }
.ff-switch > input { position: absolute; opacity: 0; width: 0; height: 0; }
.ff-switch > span {
    position: absolute; inset: 0; cursor: pointer; border-radius: 999px;
    background: var(--ff-border); transition: background 0.2s;
}
.ff-switch > span::before {
    content: ""; position: absolute; width: 14px; height: 14px; left: 3px; top: 3px;
    border-radius: 50%; background: var(--ff-text-dim); transition: transform 0.2s, background 0.2s;
}
.ff-switch > input:checked + span { background: var(--ff-accent); }
.ff-switch > input:checked + span::before { transform: translateX(16px); background: var(--ff-surface); }
.ff-switch > input:focus-visible + span { box-shadow: 0 0 0 3px var(--ff-accent-soft); }

/* A labelled on/off setting in a list: text left, .ff-switch right (reminder intervals). */
.ff-setting {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 10px 0; border-bottom: 1px solid var(--ff-border);
    font-size: 0.88rem; color: var(--ff-text-strong);
}
.ff-setting:last-child { border-bottom: 0; }

/* A pickable row in a list: checkbox, name, secondary text, optional status (bulk reminders'
   account picker). The secondary text drops under the name on a phone. */
.ff-pick {
    display: flex; align-items: center; gap: 12px;
    padding: 8px 12px; border-bottom: 1px solid var(--ff-border); font-size: 0.88rem;
}
.ff-pick:last-child { border-bottom: 0; }
.ff-pick:hover { background: var(--ff-surface-2); }
.ff-pick > .form-check-input { flex-shrink: 0; margin: 0; }
.ff-pick-name { flex: 1; min-width: 0; color: var(--ff-heading); }
.ff-pick-sub { flex: 1; min-width: 0; color: var(--ff-text-dim); font-size: 0.78rem; word-break: break-word; }
label.ff-pick { cursor: pointer; margin: 0; }
/* A row that can't be picked (a rained-out day with no jobs left): faded, not clickable-looking. */
.ff-pick.is-off { opacity: 0.55; cursor: default; }
.ff-pick.is-off:hover { background: transparent; }
@media (max-width: 575px) {
    .ff-pick { flex-wrap: wrap; gap: 4px 12px; }
    .ff-pick-sub { flex-basis: 100%; order: 3; padding-left: 28px; }
}

/* ── Chart box: Chart.js with maintainAspectRatio:false sizes itself to its parent, so the
   parent needs a definite height. Put the <canvas> inside. (Canvas colours can't use var() —
   resolve --ff-* tokens with getComputedStyle; series colours are --ff-cat-1..8.) ──────── */
.ff-chart { position: relative; height: 260px; }

/* ── Empty state inside a card or section ──────────────────────────────────── */
.ff-empty { text-align: center; padding: 22px 16px; color: var(--ff-text-muted); font-size: 0.86rem; }
.ff-empty > i { display: block; font-size: 1.5rem; margin-bottom: 8px; color: var(--ff-text-dim); }
.ff-empty a:not(.ff-btn) { color: var(--ff-info); }

/* ── Narrow page: a lone form or wizard, centred instead of stretched across the screen.
   .ff-narrow (640px) · .ff-narrow--sm (520px) · .ff-narrow--lg (900px) ──────────────── */
.ff-narrow { max-width: 640px; margin-left: auto; margin-right: auto; }
.ff-narrow--sm { max-width: 520px; }
.ff-narrow--lg { max-width: 900px; }
/* Roomier card for a form that is the whole page. */
.ff-card--form { padding: 24px 26px; }
@media (max-width: 575px) { .ff-card--form { padding: 18px 16px; } }

/* Field rows: two (or 2:1:1) fields side by side, stacking on a phone. */
.ff-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.ff-form-row--3 { grid-template-columns: 2fr 1fr 1fr; }
@media (max-width: 520px) { .ff-form-row, .ff-form-row--3 { grid-template-columns: 1fr; } }
.ff-req { color: var(--ff-bad); font-weight: 700; }
/* Bootstrap's .invalid-feedback (shown beside an .is-invalid input) on the theme's own red. */
.ff-field .invalid-feedback { color: var(--ff-bad); font-size: 0.78rem; }

/* A wizard step's panel: only the .active one shows. */
.ff-wizard-panel { display: none; }
.ff-wizard-panel.active { display: block; }

/* A chosen file: name, size, and a remove button. */
.ff-file {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 6px 4px 10px; border-radius: var(--ff-radius-sm);
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
    color: var(--ff-text-muted); font-size: 0.78rem;
}
.ff-file > i { color: var(--ff-text-dim); }
.ff-file .ff-dim { font-variant-numeric: tabular-nums; }
/* A validation message under a field (put on the asp-validation-for span). */
.ff-error { display: block; margin-top: 4px; font-size: 0.78rem; color: var(--ff-bad); }
/* Bootstrap's is-invalid ring, on the theme's own red. */
.ff-input.is-invalid { border-color: var(--ff-bad) !important; }
/* A $ (or other unit) inside the left edge of a text box. */
.ff-input-affix { position: relative; }
.ff-input-affix > span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ff-text-dim); font-size: 0.85rem; pointer-events: none; }
.ff-input-affix > .ff-input { padding-left: 26px; }

/* The button row that ends a form: back on the left, the committing action on the right. */
.ff-form-actions {
    display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;
    margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--ff-border);
}

/* ── Steps: a 1-2-3 wizard header. Markup:
   <div class="ff-steps"><div class="ff-step active|done"><span class="ff-step-dot">1</span>
   <span class="ff-step-label">Customer</span></div>…</div>
   Active is tinted with the accent, done with green; neither needs an on-colour text token. ── */
.ff-steps { display: flex; justify-content: center; position: relative; margin-bottom: 24px; }
.ff-steps::before {
    content: ""; position: absolute; top: 17px; left: 17%; right: 17%;
    height: 2px; background: var(--ff-border);
}
.ff-step { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.ff-step.clickable, .ff-step[onclick] { cursor: pointer; }
.ff-step-dot {
    width: 34px; height: 34px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.85rem; font-weight: 700; font-variant-numeric: tabular-nums;
    border: 2px solid var(--ff-border); background: var(--ff-surface); color: var(--ff-text-dim);
    transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.ff-step-label { font-size: 0.8rem; font-weight: 500; color: var(--ff-text-dim); text-align: center; }
.ff-step.active .ff-step-dot { border-color: var(--ff-accent); background: var(--ff-accent-soft); color: var(--ff-heading); }
.ff-step.active .ff-step-label { color: var(--ff-heading); font-weight: 600; }
.ff-step.done .ff-step-dot { border-color: var(--ff-good); background: color-mix(in srgb, var(--ff-good) 16%, var(--ff-surface)); color: var(--ff-good); }
.ff-step.done .ff-step-label { color: var(--ff-text-muted); }

/* ── Drop zone: click or drag a file onto it. .drag-over is set by the page's script. ──── */
.ff-dropzone {
    border: 2px dashed var(--ff-border-strong); border-radius: var(--ff-radius);
    padding: 26px 20px; text-align: center; cursor: pointer;
    background: var(--ff-surface-2); color: var(--ff-text-muted);
    transition: border-color 0.15s, background 0.15s;
}
.ff-dropzone:hover, .ff-dropzone.drag-over { border-color: var(--ff-accent); background: var(--ff-accent-soft); }
.ff-dropzone > i { display: block; font-size: 1.6rem; color: var(--ff-text-dim); margin-bottom: 8px; }
.ff-dropzone-title { font-size: 0.95rem; font-weight: 600; color: var(--ff-text); }
.ff-dropzone-sub { font-size: 0.8rem; color: var(--ff-text-dim); margin-top: 3px; }
.ff-dropzone-file { display: none; font-size: 0.85rem; font-weight: 600; color: var(--ff-good); margin-top: 8px; }
.ff-dropzone--sm { padding: 14px; }
.ff-dropzone--sm > i { display: inline; font-size: 1.1rem; margin: 0; }

/* A scrolling table well (long preview lists): the header stays put while rows scroll. */
.ff-scroll { max-height: 420px; overflow: auto; border: 1px solid var(--ff-border); border-radius: var(--ff-radius-sm); }
/* A wide table inside the well keeps each cell on one line and scrolls sideways; a column that
   should wrap anyway (an address, a status message) takes .ff-wrap. */
.ff-scroll > .ff-table { margin-bottom: 0; font-size: 0.82rem; }
.ff-scroll > .ff-table td { white-space: nowrap; }
.ff-scroll > .ff-table td.ff-wrap, .ff-scroll > .ff-table td.ff-wide { white-space: normal; min-width: 170px; }
.ff-scroll > .ff-table > thead > tr > th { position: sticky; top: 0; z-index: 1; background: var(--ff-surface-2); }
/* Row tints for a preview that flags problems. */
.ff-table > tbody > tr.is-bad-row > td  { background: color-mix(in srgb, var(--ff-bad) 9%, var(--ff-surface)); }
.ff-table > tbody > tr.is-warn-row > td { background: color-mix(in srgb, var(--ff-warn) 8%, var(--ff-surface)); }

/* ── Bar: a neutral strip of controls (the "3 selected" bulk-action bar). Hidden until the
   page's script adds .show — put both .ff-bar and .ff-bar--toggle on it. ─────────────── */
.ff-bar {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    background: var(--ff-surface); border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 10px 14px; margin-bottom: 12px;
}
.ff-bar--toggle { display: none; }
.ff-bar--toggle.show { display: flex; }
.ff-bar strong { color: var(--ff-heading); font-variant-numeric: tabular-nums; }

/* ── Search box: magnifier at the left, a clear (×) button at the right that appears once
   there is text (needs a placeholder on the input). ───────────────────────────────────── */
.ff-search { position: relative; flex: 1 1 220px; max-width: 460px; min-width: 0; }
.ff-search > input { width: 100%; padding: 8px 32px 8px 34px; }
.ff-search > i { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ff-text-dim); pointer-events: none; font-size: 0.85rem; }
.ff-search-clear {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    display: none; padding: 2px 8px; background: transparent; border: 0;
    color: var(--ff-text-dim); font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.ff-search-clear:hover { color: var(--ff-text); }
.ff-search > input:not(:placeholder-shown) + .ff-search-clear { display: block; }
.ff-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
@media (max-width: 575px) {
    .ff-toolbar { flex-direction: column; align-items: stretch; }
    .ff-toolbar .form-select { width: 100% !important; }
    .ff-search { max-width: none; flex-basis: auto; }
}

/* ── Tiles: a grid of record cards (customers). A tile can be selected (.selected, set by the
   page's script); its footer buttons sit at the bottom however tall the tile is. ─────────── */
.ff-tiles { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
@media (max-width: 575px) { .ff-tiles { grid-template-columns: minmax(0, 1fr); gap: 10px; } }
.ff-tile {
    display: flex; flex-direction: column; min-width: 0;
    background: var(--ff-surface); border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 14px 16px;
    transition: border-color 0.15s, background 0.15s;
}
.ff-tile:hover { border-color: var(--ff-border-strong); }
.ff-tile.selected {
    border-color: var(--ff-accent);
    background: color-mix(in srgb, var(--ff-accent) 9%, var(--ff-surface));
}
.ff-tile-top { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 6px; }
.ff-tile-top .form-check-input { margin-top: 3px; flex-shrink: 0; width: 18px; height: 18px; cursor: pointer; }
.ff-tile-name {
    flex: 1; min-width: 0; font-size: 1.02rem; font-weight: 650; line-height: 1.25;
    color: var(--ff-heading); text-decoration: none; word-break: break-word;
}
a.ff-tile-name:hover { color: var(--ff-accent); }
.ff-tile-actions { display: flex; gap: 6px; margin-top: auto; padding-top: 12px; }
.ff-tile-actions > .ff-btn, .ff-tile-actions > form { flex: 1; }
.ff-tile-actions > form { display: flex; margin: 0; }
.ff-tile-actions > form > .ff-btn { flex: 1; }
.ff-tile-actions > .ff-tile-icon { flex: 0 0 auto; }
.ff-tile-actions > form.ff-tile-icon > .ff-btn { min-width: 36px; }

.ff-tile-top > div { min-width: 0; }
/* A tile's secondary line (type · tag) and its photo. */
.ff-tile-sub { font-size: 0.8rem; color: var(--ff-text-muted); margin-top: 2px; }
.ff-tile-photo {
    display: block; width: 100%; height: 120px; object-fit: cover; margin: 8px 0;
    border-radius: var(--ff-radius-sm); background: var(--ff-surface-2); border: 1px solid var(--ff-border);
}
/* A photo preview on an edit form. */
.ff-thumb { display: block; max-width: 200px; max-height: 160px; border-radius: var(--ff-radius-sm); border: 1px solid var(--ff-border); }
/* Facts: small label-over-value pairs, two per row (an asset's make, serial, hours). */
.ff-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; margin: 8px 0; font-size: 0.82rem; color: var(--ff-text-strong); }
.ff-facts > div { min-width: 0; word-break: break-word; }
.ff-facts dt, .ff-facts .ff-fact-label { font-size: 0.74rem; font-weight: 500; color: var(--ff-text-dim); }
.ff-facts dd { margin: 0; font-variant-numeric: tabular-nums; }
/* A tile's footer controls wrap: small selects and buttons sharing one row. */
.ff-tile-actions--wrap { flex-wrap: wrap; border-top: 1px solid var(--ff-border); margin-top: auto; padding-top: 10px; }
.ff-tile-actions--wrap > .form-select { flex: 1 1 120px; width: auto; }
.ff-tile-actions--wrap > .ff-btn, .ff-tile-actions--wrap > form { flex: 0 0 auto; }

/* Contact lines inside a tile: a dim icon, then the value (or a faint "no email"). */
.ff-lines { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; color: var(--ff-text-strong); margin-top: 4px; }
.ff-line { display: flex; gap: 8px; align-items: flex-start; word-break: break-word; }
.ff-line > i { color: var(--ff-text-dim); width: 14px; text-align: center; flex-shrink: 0; margin-top: 3px; font-size: 0.78rem; }
.ff-line a { color: inherit; text-decoration: none; }
.ff-line a:hover { color: var(--ff-accent); text-decoration: underline; }
.ff-line .ff-none { color: var(--ff-text-dim); font-style: italic; }
/* A free-text note on a record (three lines, then clipped). */
.ff-note {
    margin-top: 8px; padding: 6px 9px; font-size: 0.8rem; color: var(--ff-text);
    border-left: 3px solid var(--ff-warn); border-radius: 4px;
    background: color-mix(in srgb, var(--ff-warn) 8%, var(--ff-surface));
    white-space: pre-wrap; word-break: break-word;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* A note shown in full (a lead's notes can hold a gate code — never clip those). */
.ff-note--full { display: block; -webkit-line-clamp: unset; overflow: visible; }
/* A soft inset box inside a tile (a claimed lead's contact details). */
.ff-well { margin-top: 10px; padding: 10px 12px; border-radius: var(--ff-radius-sm); background: var(--ff-surface-2); font-size: 0.86rem; }
.ff-well .ff-lines { margin-top: 4px; }
/* Photo thumbnails that open full size. */
.ff-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ff-thumbs img {
    display: block; width: 56px; height: 56px; object-fit: cover;
    border-radius: 6px; border: 1px solid var(--ff-border); background: var(--ff-surface-2);
}
.ff-thumbs a:hover img { border-color: var(--ff-accent); }
/* A ledger row that doesn't apply (a service they don't offer): faded. */
.ff-ledger > div.is-off { opacity: 0.55; }

/* An editable row that turns tinted once its value differs from the original — the page's
   script adds .changed (bulk notes editor). */
.ff-editrow { padding: 10px 8px; border-bottom: 1px solid var(--ff-border); border-radius: var(--ff-radius-sm); }
.ff-editrow:last-child { border-bottom: 0; }
.ff-editrow-name { font-size: 0.92rem; font-weight: 600; color: var(--ff-heading); margin-bottom: 4px; }
.ff-editrow-flag { display: none; margin-left: 6px; font-size: 0.75rem; font-weight: 500; color: var(--ff-warn); }
.ff-editrow.changed { background: var(--ff-accent-soft); }
.ff-editrow.changed .ff-editrow-flag { display: inline; }

/* ── Inline fields: words and controls that read as one sentence ("Every [2] [weeks]").
   Put .ff-input--num on a count; a select in the row takes the remaining width. ────────── */
.ff-inline-fields { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.ff-inline-fields > .form-select { flex: 1 1 150px; min-width: 150px; width: auto; }
.ff-inline-word { font-size: 0.88rem; color: var(--ff-text-muted); }

/* A computed result under a form ("5,000 sqft lot: you $52.00 / visit") — the Pricing page's
   live example. Soft strip, label left, figures right, wraps on a phone. */
.ff-strip {
    display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 4px 10px;
    margin-top: 12px; padding: 9px 12px; border-radius: var(--ff-radius-sm);
    background: var(--ff-surface-2); color: var(--ff-text-strong); font-size: 0.84rem;
}
.ff-strip strong { color: var(--ff-heading); font-variant-numeric: tabular-nums; }

/* A long form's save button that stays in reach at the bottom of the screen. */
.ff-sticky-save {
    position: sticky; bottom: 0; z-index: 5; padding: 12px 0;
    background: linear-gradient(transparent, var(--ff-app-bg) 30%);
}

/* ── Plans: side-by-side subscription tiers (Billing). .is-featured is the suggested one,
   .is-current the one they're on; the flag above the card is a real element
   (<span class="ff-plan-flag ff-status …">Current plan</span>), not ::before caps text. ── */
.ff-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; max-width: 700px; }
.ff-plan {
    position: relative; display: flex; flex-direction: column; text-align: center;
    background: var(--ff-surface); border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 28px 24px 24px;
}
.ff-plan.is-featured { border-color: var(--ff-accent); }
.ff-plan.is-current { border-color: var(--ff-good); }
.ff-plan-flag { position: absolute; top: -11px; left: 50%; transform: translateX(-50%); background: var(--ff-surface) !important; border: 1px solid currentColor; }
.ff-plan-name { font-size: 1rem; font-weight: 650; color: var(--ff-heading); margin-bottom: 6px; }
.ff-plan-price { font-size: 2.6rem; font-weight: 800; line-height: 1; letter-spacing: -0.02em; color: var(--ff-heading); font-variant-numeric: tabular-nums; }
.ff-plan-price small { font-size: 0.9rem; font-weight: 400; letter-spacing: 0; color: var(--ff-text-dim); }
.ff-plan-desc { font-size: 0.85rem; color: var(--ff-text-muted); margin: 10px 0 18px; }
.ff-plan-features { list-style: none; padding: 0; margin: 0 0 22px; text-align: left; }
.ff-plan-features li { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 0.85rem; color: var(--ff-text-strong); }
.ff-plan-features li > i { color: var(--ff-good); width: 16px; text-align: center; flex-shrink: 0; }
.ff-plan > form, .ff-plan > .ff-plan-foot { margin-top: auto; }

/* ── Pills: records attached to a record (a crew's members), each with an optional remove ×.
   <div class="ff-pills"><span class="ff-pill"><i …></i> Name <form><button class="ff-pill-x">
   ×</button></form></span></div>. .ff-pill--lead marks the one that leads (star on amber). */
.ff-pills { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ff-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 6px 3px 10px; border-radius: 999px;
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
    color: var(--ff-text); font-size: 0.8rem; line-height: 1.4; white-space: nowrap;
}
.ff-pill > i { color: var(--ff-text-dim); font-size: 0.85em; }
.ff-pill--lead { border-color: color-mix(in srgb, var(--ff-amber) 45%, var(--ff-border)); }
.ff-pill--lead > i { color: var(--ff-amber); }
.ff-pill > form { display: inline; margin: 0; }
.ff-pill-x {
    background: none; border: 0; padding: 0 4px; line-height: 1; cursor: pointer;
    color: var(--ff-text-dim);
}
.ff-pill-x:hover { color: var(--ff-bad); }

/* A record that is switched off (an inactive sensor, a retired asset): still readable, visibly set aside. */
.ff-card.is-off, .ff-tile.is-off { opacity: 0.6; }

/* ── Copy box: a value the user copies elsewhere (a link, a key). Monospace, wraps anywhere. */
.ff-copybox {
    padding: 10px 12px; border-radius: var(--ff-radius-sm);
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
    color: var(--ff-text-strong); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem; word-break: break-all;
}

/* ── Events: an activity history, newest first. Markup:
   <div class="ff-events"><div class="ff-event"><span class="ff-event-icon"><i …></i></span>
   <div><div class="ff-event-title">Sent</div><div class="ff-event-note">…</div>
   <div class="ff-event-time">Sep 3, 2026 9:14 AM</div></div></div>…</div> ─────────────── */
.ff-event { display: flex; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--ff-border); font-size: 0.85rem; }
.ff-event:first-child { padding-top: 0; }
.ff-event:last-child { border-bottom: 0; padding-bottom: 0; }
.ff-event > div { flex: 1; min-width: 0; }
.ff-event-icon {
    width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--ff-surface-2); border: 1px solid var(--ff-border);
    color: var(--ff-text-muted); font-size: 0.72rem;
}
.ff-event-title { font-weight: 600; color: var(--ff-heading); }
.ff-event-note { color: var(--ff-text-muted); font-size: 0.8rem; word-break: break-word; }
.ff-event-time { color: var(--ff-text-dim); font-size: 0.74rem; font-variant-numeric: tabular-nums; }

/* ── Edit grid: a table of inputs (bid line items). No row borders, tight cells, and a floor
   width so a phone scrolls it sideways (wrap it in .table-responsive) instead of crushing it. */
/* table-layout: fixed so the <th> widths hold — otherwise wide selects squeeze the qty box to nothing. */
.ff-table--edit { min-width: 760px; table-layout: fixed; border-collapse: separate; border-spacing: 0; }
.ff-table--edit > thead > tr > th { padding: 0 4px 6px; border-bottom: 0; }
.ff-table--edit > tbody > tr > td { padding: 3px 4px; vertical-align: top; border: 0; }
.ff-table--edit .ff-input { font-size: 0.82rem; }
/* A spreadsheet-wide grid (Add assets: 16 columns) and its column widths — put .ff-w-* on the <th>s. */
.ff-table--wide { min-width: 1300px; }
.ff-w-icon { width: 40px; } .ff-w-xs { width: 70px; } .ff-w-sm { width: 90px; }
.ff-w-md { width: 120px; } .ff-w-lg { width: 160px; }
/* A row the page's script has marked as filled in (.row-changed): accent edge on the left. */
.ff-table--edit > tbody > tr.row-changed > td:first-child { box-shadow: inset 3px 0 0 var(--ff-accent); }

/* ── Tasks: a setup checklist. Each row is numbered until it's done, then ticked green; its
   actions drop under the text on a phone. .ff-task.done marks a finished step. ────────── */
.ff-tasks { display: flex; flex-direction: column; gap: 10px; }
.ff-task {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    background: var(--ff-surface); border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 16px 18px;
}
.ff-task-dot {
    width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.9rem; font-weight: 700; font-variant-numeric: tabular-nums;
    border: 2px solid var(--ff-accent); background: var(--ff-accent-soft); color: var(--ff-heading);
}
.ff-task.done .ff-task-dot {
    border-color: var(--ff-good); color: var(--ff-good);
    background: color-mix(in srgb, var(--ff-good) 16%, var(--ff-surface));
}
.ff-task-body { flex: 1 1 200px; min-width: 0; }
.ff-task-title { font-size: 0.98rem; font-weight: 650; color: var(--ff-heading); }
.ff-task-sub { font-size: 0.84rem; color: var(--ff-text-muted); margin-top: 2px; }
.ff-task-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
@media (max-width: 575px) { .ff-task-actions { margin-left: 50px; } }

/* ── Side nav + prose: a long help/reference page. The nav is a vertical list beside the text;
   under 1024px it becomes wrapping chips above it. .active marks the open section. ─────── */
.ff-sidenav-layout { display: flex; gap: 28px; align-items: flex-start; }
.ff-sidenav {
    flex: 0 0 230px; position: sticky; top: 24px;
    display: flex; flex-direction: column;
    background: var(--ff-surface); border: 1px solid var(--ff-border); border-radius: var(--ff-radius);
    padding: 6px;
}
.ff-sidenav a {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px; border-radius: var(--ff-radius-sm);
    color: var(--ff-text-muted); font-size: 0.88rem; font-weight: 500; text-decoration: none;
}
.ff-sidenav a > i { width: 16px; text-align: center; color: var(--ff-text-dim); font-size: 0.85em; }
.ff-sidenav a:hover { color: var(--ff-text); background: var(--ff-surface-2); }
.ff-sidenav a.active { color: var(--ff-heading); background: var(--ff-accent-soft); font-weight: 600; }
.ff-sidenav a.active > i { color: var(--ff-accent); }
.ff-sidenav-body { flex: 1; min-width: 0; }
/* One panel per nav item; the page's script moves .active between them. */
.ff-sidenav-panel { display: none; }
.ff-sidenav-panel.active { display: block; }
@media (max-width: 1024px) {
    .ff-sidenav-layout { flex-direction: column; gap: 16px; }
    .ff-sidenav {
        position: static; flex: none; width: 100%;
        flex-direction: row; flex-wrap: wrap; gap: 6px;
        background: none; border: 0; padding: 0;
    }
    .ff-sidenav a { padding: 5px 12px; border: 1px solid var(--ff-border); border-radius: 999px; font-size: 0.82rem; }
    .ff-sidenav a.active { border-color: var(--ff-accent); }
    .ff-sidenav a > i { width: auto; }
}

/* Long-form text (help articles). Readable measure and rhythm; headings in sentence case.
   (.main-content repeats: _Layout's generic h2/h3 sizes load later at the same specificity.) */
.ff-prose { max-width: 760px; color: var(--ff-text-strong); font-size: 0.93rem; line-height: 1.7; }
.ff-prose h2, .main-content .ff-prose h2 {
    font-size: 1.35rem; font-weight: 700; letter-spacing: -0.01em; color: var(--ff-heading);
    margin: 0 0 14px; padding-bottom: 10px; border-bottom: 1px solid var(--ff-border);
}
.ff-prose h3, .main-content .ff-prose h3 { font-size: 1.02rem; font-weight: 650; color: var(--ff-heading); margin: 26px 0 8px; }
.ff-prose p, .ff-prose ul, .ff-prose ol { margin-bottom: 14px; }
.ff-prose ul, .ff-prose ol { padding-left: 1.4rem; }
.ff-prose li { margin-bottom: 6px; }
.ff-prose strong { color: var(--ff-text); font-weight: 600; }
.ff-prose code { color: var(--ff-info); font-size: 0.9em; }
.ff-prose .ff-card { margin-bottom: 12px; }
.ff-prose .ff-card h4 { font-size: 0.95rem; font-weight: 650; color: var(--ff-heading); margin: 0 0 4px; }
.ff-prose .ff-card h4 > i { color: var(--ff-text-dim); width: 18px; font-size: 0.9em; }
.ff-prose .ff-card p { margin: 0; font-size: 0.88rem; line-height: 1.6; }
.ff-prose .ff-callout { margin-bottom: 14px; line-height: 1.6; }

/* ── DataTables 2 chrome, app-wide ─────────────────────────────────────────── */
.dt-container { font-size: 0.85rem; color: var(--ff-text-muted); }
.dt-container .dt-search input,
.dt-container .dt-length select {
    background: var(--ff-surface-2); color: var(--ff-text);
    border: 1px solid var(--ff-border); border-radius: var(--ff-radius-sm);
    padding: 5px 10px; font-size: 0.84rem;
}
.dt-container .dt-search input { min-width: 220px; }
.dt-container .dt-search input:focus,
.dt-container .dt-length select:focus { outline: none; border-color: var(--ff-accent); box-shadow: 0 0 0 3px var(--ff-accent-soft); }
.dt-container .dt-search label, .dt-container .dt-length label { color: var(--ff-text-muted); font-size: 0.82rem; }
.dt-container .dt-info { color: var(--ff-text-dim); font-size: 0.8rem; }
.dt-container .dt-paging .pagination { gap: 4px; }
.dt-container .dt-paging .page-link {
    background: transparent; border: 1px solid var(--ff-border); border-radius: 7px !important;
    color: var(--ff-text-muted); font-size: 0.8rem; padding: 4px 10px;
}
.dt-container .dt-paging .page-item.active .page-link {
    background: var(--ff-accent-soft); border-color: var(--ff-accent); color: var(--ff-heading);
}
.dt-container .dt-paging .page-item.disabled .page-link { opacity: 0.4; }
@media (max-width: 575px) { .dt-container .dt-search input { min-width: 0; width: 100%; } }

/* ── App shell: sidebar + nav (Shared/_Layout, Shared/_LoginPartial) ──────────────────────
   The sidebar keeps its own dark gradient (--ff-sidebar, set per theme in _Layout) in EVERY
   theme, the light ones included, so its ink can't come from --ff-text/--ff-heading — those
   flip to dark on paper/daylight. These fixed light-on-dark tokens are its palette instead
   (same idea as --ff-scrim). Links used to be `.text-light`, which the light themes flip to
   dark ink and _Layout then had to flip back.

   Everything is scoped to #MainHeader: the old unscoped .nav-link/.nav-item rules leaked into
   every Bootstrap .nav in the app (the .ff-tabs padding never applied, and tabs picked up the
   sidebar's hover stripe).

   JS hooks (the collapse script at the bottom of _Layout): #MainHeader .collapsed/.mobile-open,
   #MainContent .expanded, #SidebarOpen .show, #SidebarBackdrop .show, #ToggleMenu, a.nav-link,
   .nav-collapse-toggle .collapsed, and the #growCollapse/#reportsCollapse/… ids. */
:root {
    --ff-nav-ink:        #ffffff;                 /* brand, hovered link */
    --ff-nav-text:       rgba(255,255,255,0.88);  /* links */
    --ff-nav-text-muted: rgba(255,255,255,0.62);  /* group headers, close button, signed-in name */
    --ff-nav-label:      rgba(255,255,255,0.40);  /* the tiny CUSTOMERS / MONEY labels */
    --ff-nav-line:       rgba(255,255,255,0.07);  /* dividers, the sidebar's right edge */
    --ff-nav-hover:      rgba(255,255,255,0.10);  /* close-button hover */
    --ff-nav-danger:     #f87171;                 /* Logout */
}

.bodyFlex { display: flex; min-height: 100vh; }

header#MainHeader {
    position: fixed; top: 0; left: 0;
    height: 100vh; width: 230px;
    z-index: 100;
    transition: transform 0.25s ease;
    transform: translateX(0);
    flex-shrink: 0;
    overflow-y: auto; overflow-x: hidden;
    /* Safety net behind the nav so scrolling never reveals a bare strip. */
    background: var(--ff-app-bg);
}
header#MainHeader.collapsed { transform: translateX(-100%); }
/* Thin strip beside the collapsed sidebar so it can still be hovered */
header#MainHeader.collapsed::after {
    content: ""; position: absolute; top: 0; right: -24px;
    width: 24px; height: 100%; z-index: 101;
}

#MainHeader .navbar {
    display: flex; flex-direction: column;
    /* min-height (not height) so the gradient grows to cover every item when the menu is
       taller than the screen — with height:100% the scrolled-past items had no background. */
    min-height: 100%;
    z-index: 0;
    margin-bottom: 0;
    padding-top: 0.5rem;
    background: var(--ff-sidebar);
    border-right: 1px solid var(--ff-nav-line);
}
#MainHeader .navbar-brand { margin-left: .45rem; color: var(--ff-nav-ink); }
#MainHeader .navbar-nav { display: block; width: 100%; }

#MainHeader .nav-item {
    border-left: 3px solid transparent;
    transition: border-color 0.15s, background-color 0.15s;
}
#MainHeader .nav-item:hover {
    cursor: pointer;
    background-color: var(--ff-accent-soft);
    border-left-color: var(--ff-accent);
}
#MainHeader .nav-link {
    padding: 5px 16px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: 14px;
    color: var(--ff-nav-text);
}
#MainHeader .nav-link:hover, #MainHeader .nav-link:focus { color: var(--ff-nav-ink); }
#MainHeader .nav-link i,
#MainHeader .nav-collapse-toggle > span > i {
    width: 28px; text-align: center; margin-right: 6px;
}
/* External-link arrow after a label (FlowOps) */
#MainHeader .nav-link i.nav-ext { width: auto; margin: 0 0 0 4px; font-size: 0.65em; opacity: 0.6; }

/* User menu (_LoginPartial): the signed-in name is a label, not a link */
#MainHeader .nav-item.nav-user { pointer-events: none; }
#MainHeader .nav-user .nav-link { font-size: 12px; color: var(--ff-nav-text-muted); }
#MainHeader .nav-link.nav-logout { font-size: 13px; color: var(--ff-nav-danger); cursor: pointer; }

#ToggleMenu {
    cursor: pointer;
    padding: 4px 8px; border-radius: 4px;
    color: var(--ff-nav-text-muted);
    transition: background-color 0.15s, color 0.15s;
}
#ToggleMenu:hover { background-color: var(--ff-nav-hover); color: var(--ff-nav-ink); }

/* Floating open button while the sidebar is hidden — a piece of the sidebar itself */
#SidebarOpen {
    position: fixed; top: 12px; left: 12px; z-index: 99;
    background: var(--ff-sidebar);
    color: var(--ff-nav-ink);
    border: 1px solid var(--ff-nav-line);
    border-radius: 6px;
    padding: 6px 10px;
    cursor: pointer;
    font-size: 16px;
    transition: opacity 0.2s;
    opacity: 0; pointer-events: none;
}
#SidebarOpen.show { opacity: 1; pointer-events: auto; }

.main-content {
    margin-left: 230px;
    flex: 1;
    padding: 0 20px;
    transition: margin-left 0.25s ease;
    width: calc(100% - 230px);
}
.main-content.expanded { margin-left: 0; width: 100%; }

#MainHeader .nav-divider { border-top: 1px solid var(--ff-nav-line); margin: 6px 14px; }
#MainHeader .nav-section {
    font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.8px;
    color: var(--ff-nav-label);
    padding: 6px 16px 1px;
}

/* Collapsible groups (Grow / Reports / Team / Setup) */
#MainHeader .nav-collapse-toggle {
    cursor: pointer; display: flex; align-items: center; justify-content: space-between;
    padding: 5px 16px; font-size: 13px;
    color: var(--ff-nav-text-muted);
    transition: color 0.15s;
}
#MainHeader .nav-collapse-toggle:hover { color: var(--ff-nav-text); }
#MainHeader .nav-collapse-toggle i.chevron { font-size: 10px; transition: transform 0.2s; }
#MainHeader .nav-collapse-toggle.collapsed i.chevron { transform: rotate(-90deg); }

/* Phones (≤768px): the sidebar becomes an overlay over a scrim, and the menu button always
   shows. The !importants beat the desktop collapsed/expanded state the script may have set. */
@media (max-width: 768px) {
    header#MainHeader { width: 260px !important; transform: translateX(-100%) !important; }
    header#MainHeader.mobile-open { transform: translateX(0) !important; }
    /* never use the desktop collapsed logic on a phone */
    header#MainHeader.collapsed { transform: translateX(-100%) !important; }

    .main-content { margin-left: 0 !important; width: 100% !important; padding: 0 12px !important; }

    #ToggleMenu { display: inline-block !important; }
    #SidebarOpen { opacity: 1 !important; pointer-events: auto !important; }

    .sidebar-backdrop {
        position: fixed; inset: 0; z-index: 99;
        background: var(--ff-scrim);
        display: none;
    }
    .sidebar-backdrop.show { display: block; }
}
