/*
 * site.css - shared chrome for the Admin, Operator, and Customer portals.
 *
 * Hand-written, no build step (CLAUDE.md). Brand colours are CSS custom
 * properties so a tenant's palette can be emitted as one server-rendered
 * :root block rather than a stylesheet per Company (spec 9).
 *
 * The Cleaner mobile UI does not load this file - see cleaner.css. Both
 * import the shared self-hosted typefaces from fonts.css.
 *
 * --- Brand system ---
 * Two typefaces: Space Grotesk (headings, labels, buttons, body) and IBM
 * Plex Mono (the operational layer only - taglines, eyebrow labels, job
 * codes, timestamps, IDs; never body-copy length).
 *
 * Colour is authored in oklch on a warm-neutral base (hue 95, low chroma -
 * never pure #fff/#000, never a cool gray). Two accents share lightness 0.62
 * and chroma 0.16, differing only in hue: Turn blue (250) is the
 * interactive/primary colour - links, primary buttons, active nav. Fresh
 * green (165) is confirmation/completion only - done states, verified,
 * "guest-ready". Never use green for a primary CTA. `--error`/`--warn` below
 * are conventional status utility colours (a product needs them regardless
 * of brand palette) authored to match the same warm, muted register rather
 * than a fourth "accent" - the two-accent rule is about brand identity
 * colours, not system feedback states.
 */

@import url("fonts.css");

:root {
    /* Warm neutrals */
    --ink: oklch(0.22 0.01 95);
    --ink-secondary: oklch(0.45 0.01 95);
    --ink-muted: oklch(0.55 0.01 95);
    --rule: oklch(0.90 0.006 95);
    --surface: oklch(0.995 0.003 95);
    --paper: oklch(0.965 0.008 95);

    /* The two brand accents - same L/C, hue only */
    --blue: oklch(0.62 0.16 250);
    --green: oklch(0.62 0.16 165);
    --blue-on-dark: oklch(0.72 0.16 250);
    --green-on-dark: oklch(0.75 0.16 165);

    /* Status utility colours (not brand accents - see file header) */
    --error: oklch(0.55 0.16 25);
    --error-bg: oklch(0.93 0.03 25);
    --warn-ink: oklch(0.45 0.1 70);
    --warn-bg: oklch(0.92 0.05 85);

    /* Aliases: most of this file's rules are written against these names
       rather than the raw palette above, so a future retheme (or a
       per-tenant override, spec 9) only has to touch this block. */
    --brand-primary: var(--blue);
    --brand-accent: var(--blue);
    --surface-sunken: var(--paper);

    --font-sans: "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    --radius: 6px;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.5;
    text-wrap: pretty;
    color: var(--ink);
    background: var(--surface-sunken);
}

button, input, select, textarea { font-family: inherit; }

/* iOS Safari zooms the viewport on focus for any text input/select/textarea
   under 16px - body's own 15px base is just under that threshold. This
   doesn't touch button text (buttons don't trigger the zoom). */
input, select, textarea { font-size: 16px; }

/* A visited link isn't a meaningfully different state in operational
   software the way it is in a document browser - same color either way
   (brand style guide). Context-specific rules below (nav on a dark header,
   footer links, etc.) already override this for their own elements; this is
   the baseline for any plain link that isn't otherwise styled. */
a { color: var(--blue); text-decoration: none; }
a:visited { color: var(--blue); }
a:hover { text-decoration: underline; }

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans);
    font-weight: 700;
    line-height: 1.1;
    margin: 0 0 0.5em;
}
h1 { letter-spacing: -0.04em; }
h2 { letter-spacing: -0.035em; }
h3 { letter-spacing: -0.03em; }
h4, h5, h6 { letter-spacing: -0.02em; }

/* Eyebrow/label treatment (brand system): mono, uppercase, wide tracking,
   muted - for taglines, section labels, job codes, never prose. */
.eyebrow {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--ink-muted);
}
.eyebrow--on-dark { color: oklch(0.995 0.003 95 / 55%); }

/* --- brand mark + wordmark lockup (_BrandMark.cshtml, _BrandMarkAuth.cshtml) --- */
.brandmark {
    display: inline-flex;
    align-items: center;
    gap: 11px;
}
.brandmark__word {
    font-family: var(--font-sans);
    font-weight: 700;
    letter-spacing: -0.035em;
    font-size: 1.15rem;
    color: inherit;
}
.brandmark__and { color: var(--blue-on-dark); }

.brandmark--auth {
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}
.brandmark--auth .brandmark__word { font-size: 1.5rem; color: var(--ink); }
.brandmark--auth .brandmark__and { color: var(--blue); }
.brandmark--auth .brandmark__tagline {
    font-family: var(--font-mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--ink-muted);
}

/* --- portal chrome --- */

.portal__header {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    padding: 0.75rem 1.25rem;
    background: var(--ink);
    color: var(--surface);
}

.portal__brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -0.03em;
    color: var(--surface);
    text-decoration: none;
}

.portal__logo {
    height: 28px;
    width: auto;
    border-radius: 3px;
}

.portal__nav { display: flex; gap: 1rem; flex: 1; }
.portal__nav a, .portal__signin { color: oklch(0.995 0.003 95 / 80%); text-decoration: none; }
.portal__nav a:hover, .portal__signin:hover { color: var(--surface); text-decoration: underline; }

.portal__signout button {
    background: transparent;
    border: 1px solid oklch(0.995 0.003 95 / 35%);
    color: var(--surface);
    border-radius: var(--radius);
    padding: 0.35rem 0.75rem;
    cursor: pointer;
    font-weight: 600;
}

/* The Operator console is visually distinct from the Company portal on
   purpose - moving between "my console" and "somebody's business" should never
   be ambiguous. Same warm-neutral family, just darker - never a cool gray. */
.portal--operator .portal__header { background: oklch(0.16 0.005 95); }

.portal__main {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1.5rem 1.25rem 3rem;
    background: var(--surface);
    min-height: 70vh;
}

.portal__footer {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1rem 1.25rem;
    color: var(--ink-muted);
    font-size: 0.8rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    align-items: center;
}

.portal__footer-links { display: flex; gap: 1rem; margin-left: auto; }
.portal__footer-links a { color: var(--ink-muted); text-decoration: none; }
.portal__footer-links a:hover { text-decoration: underline; }

.portal__credit { color: var(--ink-muted); text-decoration: none; }
.portal__credit:hover { text-decoration: underline; }

/* --- marketing site (spec 7.1) --- */

.marketing__hero {
    text-align: center;
    padding: 2rem 0 2.5rem;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 2rem;
}
/* The pilot landing page's hero has no feature sections beneath it to
   separate from - the rule just reads as a stray line above empty space. */
.marketing__hero--standalone { border-bottom: none; margin-bottom: 0; }

.marketing__hero h1 {
    font-size: 2.25rem;
    margin: 0 0 0.75rem;
    color: var(--ink);
}

.marketing__subhead {
    max-width: 40rem;
    margin: 0 auto 1.5rem;
    color: var(--ink-muted);
    font-size: 1.05rem;
}

.marketing__cta { margin: 0; }
.marketing__cta a { margin: 0 0.5rem; }

.marketing__button {
    display: inline-block;
    padding: 0.65rem 1.5rem;
    background: var(--blue);
    color: var(--surface) !important;
    text-decoration: none;
    border-radius: var(--radius);
    font-weight: 600;
}

.marketing__features {
    list-style: none;
    padding: 0;
    display: grid;
    gap: 1rem;
}

.marketing__features li {
    padding: 0.9rem 1.1rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
}

.marketing__plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
}

.marketing__plan {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 1.1rem;
}

.marketing__plan h3 { margin: 0 0 0.4rem; }

.marketing__plan-price {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--ink);
    margin: 0 0 0.6rem;
}

.marketing__plan-period {
    font-size: 0.85rem;
    font-weight: 400;
    color: var(--ink-muted);
}

.marketing__plan ul {
    margin: 0.5rem 0 0;
    padding-left: 1.1rem;
    color: var(--ink-muted);
    font-size: 0.9rem;
}

/* A wider, denser grid than .marketing__features - built for a dozen
   short capability blurbs rather than five substantial ones. */
.marketing__caps {
    list-style: none;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
}
.marketing__caps li {
    padding: 0.9rem 1.1rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
}
.marketing__caps h3 { margin: 0 0 0.35rem; font-size: 1rem; }
.marketing__caps p { margin: 0; color: var(--ink-muted); font-size: 0.92rem; }

/* Impersonation has to be impossible to miss (spec 6.1). */
.impersonation-banner {
    background: var(--warn-bg);
    color: var(--warn-ink);
    padding: 0.6rem 1.25rem;
    font-weight: 600;
    border-bottom: 1px solid oklch(0.7 0.09 80);
}

/* --- auth --- */

.auth { display: grid; place-items: center; min-height: 100vh; background: var(--surface-sunken); }

.auth__card {
    background: var(--surface);
    padding: 2rem;
    border-radius: var(--radius);
    width: min(400px, 92vw);
    box-shadow: 0 1px 3px oklch(0.22 0.01 95 / 12%);
    text-align: center;
}

.auth__card h1 { text-align: left; font-size: 1.4rem; }
.auth__card form, .auth__card .auth__links, .auth__card .auth__steps { text-align: left; }

.auth__card label { display: block; margin-top: 0.9rem; font-size: 0.85rem; font-weight: 600; }
.auth__card input { width: 100%; padding: 0.55rem; border: 1px solid var(--rule); border-radius: var(--radius); }
.auth__card button {
    margin-top: 1.25rem;
    width: 100%;
    padding: 0.6rem;
    background: var(--blue);
    color: var(--surface);
    border: 0;
    border-radius: var(--radius);
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
}

.auth__card--wide { width: min(480px, 92vw); }
.auth__card--legal { width: min(720px, 92vw); }

.auth__terms-body {
    max-height: 50vh;
    overflow-y: auto;
    margin-top: 1rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--surface-sunken);
    font-size: 0.85rem;
    text-align: left;
}
.auth__terms-body h1 { font-size: 1.1rem; }
.auth__terms-body h2 { font-size: 1rem; }

.auth__hint { color: var(--ink-muted); font-size: 0.9rem; margin-top: -0.25rem; }
.auth__hint--tight { margin-top: 0.3rem; font-size: 0.8rem; }

.auth__subdomain-row { display: flex; align-items: center; gap: 0.4rem; }
.auth__subdomain-row input { flex: 1; }
.auth__subdomain-suffix { color: var(--ink-muted); font-size: 0.9rem; white-space: nowrap; }

.auth__divider { border: 0; border-top: 1px solid var(--rule); margin: 1.25rem 0; }

.auth__error {
    background: var(--error-bg);
    color: var(--error);
    padding: 0.6rem 0.85rem;
    border-radius: var(--radius);
    font-size: 0.88rem;
}

.auth__field-error {
    display: block;
    color: var(--error);
    font-size: 0.82rem;
    font-weight: 600;
    margin: 0.5rem 0 0.85rem;
}
.auth__field-error:empty { margin: 0; }

.auth__links {
    margin-top: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    font-size: 0.85rem;
}
.auth__links a { color: var(--blue); text-decoration: none; }
.auth__links a:hover { text-decoration: underline; }

.auth__steps { padding-left: 1.2rem; font-size: 0.9rem; }
.auth__steps li { margin-bottom: 0.6rem; }

.auth__secret {
    margin-top: 0.4rem;
    padding: 0.6rem 0.8rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
    font-family: var(--font-mono);
    letter-spacing: 0.05em;
    word-break: break-all;
}

.auth__recovery-codes {
    list-style: none;
    padding: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.4rem;
    margin: 1rem 0;
}
.auth__recovery-codes code {
    display: block;
    padding: 0.4rem 0.6rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
    text-align: center;
    font-family: var(--font-mono);
}

.auth__context-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.auth__context-option {
    width: 100%;
    text-align: left;
    padding: 0.75rem 1rem;
    background: var(--surface-sunken);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    font-size: 0.95rem;
    cursor: pointer;
}
.auth__context-option:hover { border-color: var(--blue); }

/* --- dev-only pages --- */
.dev-badge {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    background: var(--warn-bg);
    color: var(--warn-ink);
    padding: 0.15rem 0.5rem;
    border-radius: 3px;
    vertical-align: middle;
}

/* Generic list/table styling - used by the Team roster as well as the dev
   outbox viewer, hence the name is not dev-prefixed even though it lives in
   this section. */
.data-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin-top: 1rem; }
.data-table th, .data-table td {
    text-align: left;
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid var(--rule);
    /* middle, not top - a cell holding a button (its own padding/border make
       it taller than a line of plain text) needs to sit centered against a
       plain-text cell in the same row, or the row reads as misaligned even
       though every cell's top edge technically lines up. */
    vertical-align: middle;
}
.data-table td form { margin: 0; }
.data-table__data { word-break: break-all; font-size: 0.8rem; font-family: var(--font-mono); }

/* --- small icon-only row actions (e.g. Jobs/Index's Edit/Delete) ---
   currentColor so one rule covers every icon; color itself only changes on
   hover/focus, so a resting row never reads as more alarming than a plain
   list. type="submit" buttons and <a> links share this so a delete
   confirmation and an edit navigation look identical at rest. */
.icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    padding: 0;
    margin: 0;
    border: 0;
    border-radius: var(--radius);
    background: transparent;
    color: var(--ink-secondary);
    cursor: pointer;
    text-decoration: none;
}
.icon-button svg { width: 1.05rem; height: 1.05rem; }
.icon-button:hover, .icon-button:focus-visible { background: var(--surface-sunken); color: var(--ink); }
.icon-button--danger:hover, .icon-button--danger:focus-visible { color: var(--error); }
.icon-button-group { display: flex; gap: 0.25rem; }

/* --- Jobs/Index staffing flag (spec 4.3) ---
   Unstaffed, Understaffed, or Overstaffed - the replacement for the old
   inline "Assign" dropdown; the fix is opening the Job in Jobs/Edit. */
.staffing-flag {
    display: inline-block;
    background: var(--warn-bg);
    color: var(--warn-ink);
    padding: 0.15rem 0.5rem;
    border-radius: var(--radius);
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}

/* --- drag-to-schedule cleaner grid (Jobs/New, Jobs/Edit) ---
   Time (6:00a-8:00p, 15-minute columns) on the X axis, one row per cleaner
   on the Y axis (spec 4.3). Each cleaner's row is background-shaded with
   their own recurring weekly pattern (--available, editable only on their
   own Cleaner/Availability page) so a good day/cleaner pairing is visible
   before dragging one, not only as a conflict warning after. --scheduled
   is the block itself; --preview is the live drag before mouseup commits it. */
/* Wider than .record-form's own 480px cap (nothing clips a child that
   overflows its parent's box without overflow:hidden) - a 6a-8p/15-minute
   grid reads far better with room to breathe than squeezed into a
   single-column form's reading width. An explicit width, not max-width -
   a block element's auto width already fills (and is capped by) the 480px
   parent, so a mere max-width above that has nothing to expand into. */
/* min-width:0 on both: a <fieldset>'s intrinsic width otherwise grows to
   fit an unwrapped child's min-content (a long-standing fieldset sizing
   quirk, the same one flex/grid items hit) - without it, an explicit width
   here is a floor, not a cap, and the wide table drags the fieldset (and
   the page) open instead of scrolling inside .schedule-grid-wrap. */
.schedule-fieldset { width: min(1000px, calc(100vw - 4rem)); max-width: none; min-width: 0; }
.schedule-grid-wrap { overflow-x: auto; min-width: 0; border: 1px solid var(--rule); border-radius: var(--radius); }
/* table-layout:fixed so the per-cell widths below are load-bearing instead
   of mere hints - auto layout shrinks a 57-column table to fit its
   container rather than letting .schedule-grid-wrap's own scrollbar do
   the work. */
.schedule-grid { border-collapse: collapse; font-size: 0.7rem; table-layout: fixed; }
.schedule-grid__name-cell {
    position: sticky;
    left: 0;
    z-index: 1;
    width: 9rem;
    background: var(--surface);
    text-align: left;
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0.3rem 0.75rem 0.3rem 0.4rem;
    white-space: nowrap;
    border-right: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}
.schedule-grid__col-header {
    height: 1.6rem;
    font-weight: 400;
    color: var(--ink-secondary);
    text-align: left;
    padding-left: 0.2rem;
    vertical-align: bottom;
    border-left: 1px solid var(--ink-muted);
    border-bottom: 1px solid var(--rule);
}
.schedule-grid__cell {
    width: 1.7rem;
    height: 1.8rem;
    border-left: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    cursor: pointer;
    user-select: none;
}
.schedule-grid__cell--hour { border-left: 1px solid var(--ink-muted); }
.schedule-grid__cell--available { background: oklch(0.62 0.16 165 / 12%); }
.schedule-grid__cell--busy { background: oklch(0.55 0.16 25 / 16%); }
.schedule-grid__cell--scheduled { background: var(--green); }
.schedule-grid__cell--preview { background: var(--blue); }
.schedule-grid__tally { margin: 0.6rem 0 0; font-weight: 600; }

/* --- dashboard onboarding checklist --- */
.onboarding-panel {
    background: oklch(0.62 0.16 250 / 7%);
    border: 1px solid oklch(0.62 0.16 250 / 25%);
    border-radius: 12px;
    padding: 1.25rem 1.5rem 1.5rem;
    margin-top: 0;
    margin-bottom: 2rem;
}
.onboarding-checklist { list-style: none; margin: 1rem 0 0; padding: 0; }
.onboarding-checklist__item {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid oklch(0.62 0.16 250 / 16%);
}
.onboarding-checklist__item:last-child { border-bottom: none; }
.onboarding-checklist__item--done { color: var(--ink-muted); }
.onboarding-checklist__mark {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 1.3rem;
    height: 1.3rem;
    border: 1px solid oklch(0.62 0.16 250 / 45%);
    border-radius: 50%;
    font-size: 0.75rem;
    background: var(--surface);
}
/* Done is a completion state, not a navigation/CTA one - green, not blue
   (brand system: green means confirmation/completion, never primary). */
.onboarding-checklist__item--done .onboarding-checklist__mark {
    background: var(--green);
    border-color: var(--green);
    color: var(--surface);
}

/* --- company dashboard ---
   Visual weight follows how often an Admin actually uses each surface:
   Today/attention items lead (checked constantly), the four heaviest daily
   destinations get large tiles, occasional setup screens get smaller ones,
   and Settings/Billing (touched once in a while) shrink to a plain text
   row - nothing is hidden, everything just takes up space proportional to
   how often it's reached for. */
.dash-section-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 1.1rem;
    margin: 0 0 0.85rem;
}
.dash-section-title--sub {
    font-size: 0.82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
    margin-top: 2rem;
}
.dash-section-title svg { width: 1.2rem; height: 1.2rem; flex-shrink: 0; color: var(--blue); }

.dash-card {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: 12px;
    padding: 1.25rem 1.5rem;
    margin-bottom: 2rem;
}
.dash-card .data-table { margin-top: 0.6rem; }

/* attention alerts - glanceable entry points, not a full report in themselves */
.dash-alerts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.85rem;
}
.dash-alerts + .data-table { margin-top: 1.25rem; }
.dash-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.9rem 1rem;
    border-radius: 10px;
    text-decoration: none;
    color: var(--ink);
    border: 1px solid var(--rule);
    background: var(--surface);
}
.dash-alert:hover { text-decoration: none; border-color: var(--ink-secondary); }
.dash-alert__icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    border-radius: 50%;
}
.dash-alert__icon svg { width: 1.1rem; height: 1.1rem; }
.dash-alert__body { font-size: 0.88rem; line-height: 1.4; }
.dash-alert__body strong { font-size: 1.05rem; }
.dash-alert--info .dash-alert__icon { background: oklch(0.62 0.16 250 / 12%); color: var(--blue); }
.dash-alert--danger { border-color: oklch(0.55 0.16 25 / 30%); }
.dash-alert--danger .dash-alert__icon { background: var(--error-bg); color: var(--error); }
.dash-alert--action .dash-alert__icon { background: oklch(0.62 0.16 165 / 14%); color: var(--green); }

/* quick-access tiles, sized by tier (--lg for daily work, --md for
   occasional setup/maintenance screens) */
.dash-tiles { display: grid; gap: 1rem; }
.dash-tiles--lg { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.dash-tiles--md { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }

.dash-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1.25rem;
    border-radius: 12px;
    border: 1px solid var(--rule);
    background: var(--surface);
    text-decoration: none;
    color: var(--ink);
}
.dash-tile:hover { border-color: var(--blue); text-decoration: none; }

/* iOS-style notification badge - a count of open items in that section
   (an understaffed Job, a report awaiting review, ...), not a decoration. */
.dash-tile__badge {
    position: absolute;
    top: -0.4rem;
    right: -0.4rem;
    min-width: 1.3rem;
    height: 1.3rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--error);
    color: var(--surface);
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.3rem;
    text-align: center;
    box-shadow: 0 0 0 2px var(--surface);
}
.dash-tile__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 10px;
    background: oklch(0.62 0.16 250 / 10%);
    color: var(--blue);
}
.dash-tile__icon svg { width: 1.4rem; height: 1.4rem; }
.dash-tile__title { font-weight: 700; font-size: 1rem; }
.dash-tile__desc { font-size: 0.82rem; color: var(--ink-muted); line-height: 1.35; }

/* the "set up & maintain" tier reads deliberately quieter - smaller icon
   badge, neutral ink instead of brand blue, no description copy */
.dash-tiles--md .dash-tile { padding: 0.9rem 1rem; gap: 0.5rem; }
.dash-tiles--md .dash-tile__icon {
    width: 2.15rem;
    height: 2.15rem;
    background: var(--surface-sunken);
    color: var(--ink-secondary);
}
.dash-tiles--md .dash-tile__icon svg { width: 1.15rem; height: 1.15rem; }
.dash-tiles--md .dash-tile__title { font-size: 0.9rem; }

/* Settings/Billing: touched rarely, so no card at all - a plain, muted
   utility row is the whole point (spec: importance tracks frequency). */
.dash-utility-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
}
.dash-utility-link { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--ink-muted); font-size: 0.85rem; }
.dash-utility-link svg { width: 1rem; height: 1rem; }
.dash-utility-link:hover { color: var(--ink); text-decoration: underline; }

/* Today's job-status pill (distinct from .staffing-flag, which only ever
   appears on Jobs/Index for an open Requested/Scheduled job). */
.status-badge { display: inline-block; padding: 0.15rem 0.55rem; border-radius: 999px; font-size: 0.76rem; font-weight: 600; white-space: nowrap; }
.status-badge--neutral { background: var(--surface-sunken); color: var(--ink-secondary); }
.status-badge--scheduled { background: oklch(0.62 0.16 250 / 12%); color: var(--blue); }
.status-badge--progress { background: var(--warn-bg); color: var(--warn-ink); }
.status-badge--done { background: oklch(0.62 0.16 165 / 14%); color: var(--green); }
.status-badge--canceled { background: var(--surface-sunken); color: var(--ink-muted); }

/* --- modal (native <dialog>) - Team's Invite/Edit forms, and anywhere
   else a form is better as a popup than an always-visible page section. --- */
dialog.modal {
    border: none;
    border-radius: 12px;
    padding: 0;
    width: min(480px, calc(100vw - 2rem));
    box-shadow: 0 24px 60px -20px oklch(0.22 0.01 95 / 35%);
}
dialog.modal::backdrop { background: oklch(0.22 0.01 95 / 45%); }
.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--rule);
}
.modal__header h2 { margin: 0; font-size: 1.05rem; }
.modal__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    background: transparent;
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
    color: var(--ink-muted);
    padding: 0;
}
.modal__close svg { width: 1.1rem; height: 1.1rem; }
.modal__close:hover { background: var(--surface-sunken); color: var(--ink); }
.modal__body { padding: 1.25rem; }
.modal__body .record-form { max-width: none; }
.modal__actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1.25rem; }
.modal__actions button[data-close-modal] {
    background: transparent;
    color: var(--ink-secondary);
    border: 1px solid var(--rule);
}

/* --- daily traffic chart (Operator Analytics) --- */
.traffic-chart { display: block; width: 100%; height: auto; margin: 0.75rem 0 1.5rem; }
.traffic-chart-legend { display: flex; gap: 1.25rem; font-size: 0.85rem; color: var(--ink-secondary); }
.traffic-chart-legend__item { display: inline-flex; align-items: center; gap: 0.4rem; }
.traffic-chart-legend__swatch { display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 2px; }

/* --- team --- */
.team__roster-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}
.team__roster-header h1 { margin: 0; }
.team__invite-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.55rem 1rem;
    background: var(--blue);
    color: var(--surface);
    border: 0;
    border-radius: var(--radius);
    font-weight: 600;
    cursor: pointer;
}
.team__invite-trigger svg { width: 1.1rem; height: 1.1rem; }
.team__success {
    background: oklch(0.62 0.16 165 / 12%);
    color: var(--green);
    padding: 0.6rem 0.85rem;
    border-radius: var(--radius);
    font-size: 0.88rem;
}

.team__invite {
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
}

/* Shared labeled-field form styling - the Team invite form, and the
   Customer/Property create-and-edit forms (.record-form). */
.team__invite form, .record-form { max-width: 480px; }

/* A primary submit button that isn't inside .record-form/.team__invite -
   e.g. a form whose body also holds something wide (a table of line
   items) that a 480px-capped .record-form would break. Gives it the same
   spacing and brand styling those get, without capping the form itself. */
.button-primary {
    display: inline-block;
    margin-top: 1rem;
    padding: 0.55rem 1rem;
    background: var(--blue);
    color: var(--surface);
    border: 0;
    border-radius: var(--radius);
    font-weight: 600;
    cursor: pointer;
}
/* A label always sits above its control, everywhere - not just inside
   .record-form/.team__invite/fieldset. A label without this ends up
   inline with (and vertically bottom-aligned against) whatever it wraps,
   which reads especially badly for a textarea. */
label {
    display: block;
    margin-top: 0.9rem;
    font-size: 0.85rem;
    font-weight: 600;
}

/* Likewise, a bare <textarea> anywhere on the site gets a real editing
   area rather than the 2-row browser default - the "make the textarea
   larger" half of the label fix above. .record-form/.team__invite reuse
   this rather than redeclaring it. */
textarea {
    display: block;
    width: 100%;
    min-height: 6rem;
    padding: 0.5rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    font: inherit;
    resize: vertical;
}

.team__invite input, .team__invite select,
.record-form input, .record-form select {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    font: inherit;
}
.record-form input + input { margin-top: 0.4rem; } /* AddressLine1/2 stacked pair */
.record-form fieldset {
    margin-top: 1.25rem;
    padding: 0.75rem 1rem 1rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}
.record-form legend { font-weight: 600; font-size: 0.85rem; padding: 0 0.4rem; }
.checkbox-label { display: flex !important; align-items: center; gap: 0.5rem; font-weight: 400 !important; }
.checkbox-label input[type="checkbox"] { width: auto !important; }

.team__invite button, .record-form button {
    padding: 0.55rem 1rem;
    background: var(--blue);
    color: var(--surface);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
    font-weight: 600;
}

/* Only the section's own "Add/Invite" button gets the top margin that
   separates it from the fields above it - a button nested inside a
   .data-table row has no such fields to separate itself from, and the
   margin there just pushed the button out of line with its own row. */
.team__invite button:not(.data-table button), .record-form button {
    margin-top: 1rem;
}

/* --- roster filter bars --- */
.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 1rem;
    margin: 1rem 0;
    padding: 0.85rem 1rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
}
.filters label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; font-weight: 600; }
.filters input, .filters select { padding: 0.4rem; border: 1px solid var(--rule); border-radius: var(--radius); }
.filters button {
    padding: 0.45rem 0.9rem;
    background: var(--blue);
    color: var(--surface);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
    font-weight: 600;
}

/* --- scaffold markers ---
   Everything wearing this class is a placeholder. Deliberately conspicuous, so
   an unfinished screen cannot be mistaken for a finished one during review. */
.scaffold-note {
    background: var(--surface-sunken);
    border-left: 3px solid var(--blue);
    padding: 0.75rem 1rem;
    color: var(--ink-muted);
    font-size: 0.9rem;
    border-radius: 0 var(--radius) var(--radius) 0;
}

.scaffold-note code {
    background: oklch(0.9 0.006 95);
    padding: 0.1rem 0.3rem;
    border-radius: 3px;
    font-size: 0.85em;
    font-family: var(--font-mono);
}

/* --- toast (form-feedback.js) ---
   A transient "Saved"/error confirmation for an AJAX-converted action -
   the Admin side's equivalent of the Cleaner area's persistent .cleaner__sync
   pill, but momentary rather than sticky, since there's no offline queue
   here to keep reflecting. */
.toast-stack {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: min(320px, calc(100vw - 2.5rem));
}

.toast {
    padding: 0.6rem 1rem;
    border-radius: var(--radius);
    font-weight: 600;
    font-size: 0.9rem;
    box-shadow: 0 2px 8px oklch(0.22 0.01 95 / 20%);
    animation: toast-in 0.15s ease-out;
}

.toast--success { background: var(--green); color: var(--surface); }
.toast--error { background: var(--error); color: var(--surface); }

@keyframes toast-in {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
}

/* --- in-flight spinner (form-feedback.js) ---
   The label stays in the layout (visibility:hidden, not display:none) so
   it keeps reserving its own space, while the spinner is taken out of flow
   with position:absolute and centered on top of it - so a button never
   changes size when this turns on or off, regardless of label length.
   currentColor keeps the spinner matching each button's own text color
   without per-context overrides. */
button.is-saving {
    position: relative;
}

button.is-saving .btn-label {
    visibility: hidden;
}

.btn-spinner {
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    border: 0.15em solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: btn-spin 0.6s linear infinite;
}

button.is-saving .btn-spinner {
    position: absolute;
    left: 50%;
    top: 50%;
    margin: 0;
}

@keyframes btn-spin {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to { transform: translate(-50%, -50%) rotate(360deg); }
}

/* --- small inline status labels, e.g. "Comped" on the Operator roster --- */
.badge {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.1rem 0.5rem;
    background: var(--warn-bg);
    color: var(--warn-ink);
    font-size: 0.78rem;
    font-weight: 600;
    border-radius: 999px;
}
