/* ============================================================
   YellowPad Design System — "Ink on a Legal Pad"
   Source of truth: DESIGN.md (root). Tokens here mirror it.
   Charcoal ink reads; yellow highlights the one thing that
   matters; slate is structure; white-smoke is the margin.
   ============================================================ */

:root {
    /* Native form controls, scrollbars, and the UA color follow the theme. */
    color-scheme: light;

    /* ---- Color: brand ---- */
    --yp-yellow: #F5E35D;          /* primary action + active marker; ALWAYS under charcoal text */
    --yp-yellow-deep: #E7CE3A;     /* hover / pressed yellow */
    --yp-yellow-soft: #FCF7D2;     /* brand-tinted wells, selected rows */

    /* ---- Color: ink / neutral ---- */
    --yp-ink: #333333;             /* body + headings */
    --yp-ink-strong: #1E1E1E;      /* max-emphasis text on the page surface */
    --yp-on-accent: #1E1E1E;       /* text on a BRIGHT yellow fill — dark in both themes */
    --yp-ink-soft: #5C6066;        /* secondary text, placeholders */
    --yp-canvas: #F5F5F5;          /* app shell / page background */
    --yp-surface: #FFFFFF;         /* cards, panels, inputs, table bodies */
    --yp-border: #DFE2E6;          /* 1px hairline — does the separating */

    /* ---- Color: structure ---- */
    --yp-slate: #708090;           /* icons, metadata, ghost borders (never text-fill) */
    --yp-slate-deep: #565D66;      /* filled neutral w/ white text */
    --yp-slate-soft: #ECEEF0;      /* row / ghost hover */

    /* ---- Color: status (functional only) ---- */
    --yp-success: #2F7D4F;
    --yp-success-soft: #E7F2EC;
    --yp-warning: #B45309;
    --yp-warning-soft: #FBEBD7;
    --yp-danger: #C62A2A;
    --yp-danger-deep: #A12424;
    --yp-danger-soft: #F7E3E3;
    /* Danger reads two ways and one color can't serve both once the theme
       flips: as text it needs to lift off the background, as a fill it needs
       to stay dark enough under white. Split here so dark mode can move them
       in opposite directions. On light they're the same red. */
    --yp-danger-fill: #C62A2A;
    --yp-danger-fill-hover: #A12424;

    /* ---- Typography ---- */
    --yp-font-sans: 'Hanken Grotesk', 'Segoe UI', system-ui, sans-serif;
    --yp-font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', monospace;

    /* ---- Radius ---- */
    --yp-radius-sm: 4px;
    --yp-radius-md: 8px;
    --yp-radius-lg: 12px;
    --yp-radius-full: 999px;

    /* ---- Spacing ---- */
    --yp-space-xs: 4px;
    --yp-space-sm: 8px;
    --yp-space-md: 12px;
    --yp-space-lg: 16px;
    --yp-space-xl: 24px;
    --yp-space-2xl: 32px;
    --yp-space-3xl: 48px;

    /* Scroll space reserved under a form for the fixed edit commit bar. Sized
       so the tallest case — the bar with a save error showing, ~122px — still
       clears the last section by roughly a --yp-space-xl gap, on top of the
       48px .content already ends with. Retuned on mobile, where .content
       reserves the tab bar's height instead. */
    --yp-editbar-space: 96px;

    /* ---- Elevation (shadows only float) ---- */
    --yp-shadow-hover: 0 1px 2px rgba(51,51,51,0.06), 0 2px 6px rgba(51,51,51,0.08);
    --yp-shadow-overlay: 0 4px 12px rgba(51,51,51,0.10), 0 1px 3px rgba(51,51,51,0.08);
    --yp-shadow-modal: 0 16px 48px rgba(51,51,51,0.18);

    /* ---- Focus (charcoal — yellow would vanish on white) ---- */
    --yp-focus-ring: 0 0 0 3px rgba(51,51,51,0.30);

    /* ---- Scrim + skeleton shimmer (tokenized so dark can re-tint) ---- */
    --yp-scrim: rgba(51, 51, 51, 0.45);
    --yp-shimmer: rgba(255, 255, 255, 0.55);

    /* ---- Motion ---- */
    --yp-ease: cubic-bezier(0.25, 1, 0.5, 1);
    --yp-dur-fast: 120ms;
    --yp-dur-base: 200ms;

    /* ---- z-index scale ---- */
    --yp-z-dropdown: 1000;
    --yp-z-sticky: 1020;
    --yp-z-modal-backdrop: 1040;
    --yp-z-modal: 1050;
    --yp-z-toast: 1080;
    --yp-z-tooltip: 1090;
}

/* ============================================================
   Bootstrap variable overrides — keep classed components on-brand
   ============================================================ */
:root {
    --bs-body-font-family: var(--yp-font-sans);
    --bs-body-color: var(--yp-ink);
    --bs-body-bg: var(--yp-canvas);
    --bs-border-color: var(--yp-border);
    --bs-border-radius: var(--yp-radius-md);
    --bs-primary: var(--yp-yellow);
    --bs-primary-rgb: 245, 227, 93;
    --bs-link-color: var(--yp-ink);
    --bs-link-hover-color: var(--yp-ink-strong);
    --bs-emphasis-color: var(--yp-ink-strong);
}

/* ============================================================
   Dark theme — "Ink on a Legal Pad" after hours
   The same system inverted at the token layer: the page is the
   charcoal now and near-white ink does the reading, while the
   highlighter yellow stays bright with dark text under it (the one
   rule that never flips). Set on <html> by the index.html boot
   script before paint; toggled live by ypTheme.
   ============================================================ */
html[data-theme="dark"] {
    color-scheme: dark;

    /* Brand — yellow is still the highlighter; eased a touch so it doesn't
       glare on the dark canvas. Soft becomes a deep olive wash so selected
       rows / the running clock read with light ink, not dark. */
    --yp-yellow: #F1DC55;
    --yp-yellow-deep: #E7CE3A;
    --yp-yellow-soft: #38330F;

    /* Ink / neutral — inverted. on-accent stays dark (text on bright yellow). */
    --yp-ink: #E7E6E0;
    --yp-ink-strong: #FAF9F4;
    --yp-on-accent: #1E1E1E;
    --yp-ink-soft: #A4A59F;
    --yp-canvas: #1A1B1D;
    --yp-surface: #242629;
    --yp-border: #3A3D41;

    /* Structure — slate lifts so icons/metadata stay visible on dark; the
       filled neutral darkens but keeps white text legible. */
    --yp-slate: #8C929B;
    --yp-slate-deep: #4A4E55;
    --yp-slate-soft: #303236;

    /* Status — lighter faces so the color reads as text on dark, with deep
       tinted soft fills behind them. */
    --yp-success: #5FBE83;
    --yp-success-soft: #17301E;
    --yp-warning: #E0A23C;
    --yp-warning-soft: #33270F;
    /* Danger as text: #E2615B measured 4.40:1 on the dark surface — under the
       4.5 floor for the menu rows and alerts that use it. Lifted to 5.2:1. */
    --yp-danger: #EC7169;
    --yp-danger-deep: #C84A45;
    --yp-danger-soft: #371D1D;
    /* Danger as fill: the text face is far too light to hold white, so the
       button goes the other way — 6.0:1 under white, 4.9:1 on hover. */
    --yp-danger-fill: #B03A35;
    --yp-danger-fill-hover: #C24741;

    /* Elevation — charcoal shadows vanish on dark; go black and deeper. */
    --yp-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.40), 0 2px 6px rgba(0, 0, 0, 0.50);
    --yp-shadow-overlay: 0 4px 12px rgba(0, 0, 0, 0.55), 0 1px 3px rgba(0, 0, 0, 0.45);
    --yp-shadow-modal: 0 16px 48px rgba(0, 0, 0, 0.65);

    /* A charcoal focus ring disappears on dark — switch to a light ring. */
    --yp-focus-ring: 0 0 0 3px rgba(245, 245, 245, 0.32);

    --yp-scrim: rgba(0, 0, 0, 0.62);
    --yp-shimmer: rgba(255, 255, 255, 0.06);
}

/* Bootstrap's select chevron is a baked-in dark SVG; swap it for a light one
   so the arrow stays visible on the dark input. */
html[data-theme="dark"] .form-select {
    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='%23A4A59F' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* ============================================================
   Base
   ============================================================ */
html, body {
    font-family: var(--yp-font-sans);
    color: var(--yp-ink);
    background-color: var(--yp-canvas);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

body {
    font-size: 1rem;
    line-height: 1.5;
}

h1, h2, h3, h4, h5, h6 {
    color: var(--yp-ink);
    font-weight: 600;
    line-height: 1.2;
    text-wrap: balance;
}

h1 {
    font-size: clamp(1.625rem, 1.2rem + 1.8vw, 2.25rem);
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.01em;
}

h2 { font-size: 1.375rem; letter-spacing: -0.005em; }
h3 { font-size: 1.0625rem; }

p { text-wrap: pretty; max-width: 75ch; }

a {
    color: var(--yp-ink);
    text-underline-offset: 2px;
    transition: color var(--yp-dur-fast) var(--yp-ease);
}
a:hover { color: var(--yp-ink-strong); }

:focus-visible {
    outline: none;
    box-shadow: var(--yp-focus-ring);
    border-radius: var(--yp-radius-sm);
}

/* FocusOnNavigate parks focus on the page <h1> so screen readers land on the
   heading after a route change. It isn't an interactive target, so it must not
   wear the focus ring — and app.css's `outline: none` doesn't cover ours,
   which is a box-shadow. Most visible on mobile, where the first paint has no
   prior pointer interaction and :focus-visible therefore matches. */
h1:focus,
h1:focus-visible {
    outline: none;
    box-shadow: none;
}

hr {
    border: none;
    border-top: 1px solid var(--yp-border);
    opacity: 1;
}

code { color: var(--yp-danger); font-family: var(--yp-font-mono); }

/* ============================================================
   Buttons — reusable classes (.yp-btn family)
   Bare <button> gets sensible defaults; explicit classes for actions.
   ============================================================ */
button { font-family: inherit; }

.yp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--yp-space-sm);
    font-family: var(--yp-font-sans);
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
    min-height: 40px;
    padding: 10px 18px;
    border: 1px solid transparent;
    border-radius: var(--yp-radius-md);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--yp-dur-fast) var(--yp-ease),
                border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease),
                transform var(--yp-dur-fast) var(--yp-ease);
}
.yp-btn:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
.yp-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Primary — the highlighter. One per screen. */
.yp-btn-primary {
    background: var(--yp-yellow);
    color: var(--yp-on-accent);
}
.yp-btn-primary:hover {
    background: var(--yp-yellow-deep);
    box-shadow: var(--yp-shadow-hover);
    transform: translateY(-1px);
}
.yp-btn-primary:active { transform: translateY(0); }

/* Secondary / ghost — the everyday button */
.yp-btn-secondary {
    background: var(--yp-surface);
    color: var(--yp-ink);
    border-color: var(--yp-border);
}
.yp-btn-secondary:hover { background: var(--yp-slate-soft); }

/* Danger — destructive only, never confusable with the brand */
.yp-btn-danger {
    background: var(--yp-danger-fill);
    color: #fff;
}
.yp-btn-danger:hover { background: var(--yp-danger-fill-hover); }
.yp-btn-danger:focus-visible { box-shadow: 0 0 0 3px rgba(198,42,42,0.35); }

.yp-btn-sm { min-height: 32px; padding: 6px 12px; }
/* On touch devices the compact button still needs a thumb-sized target. */
@media (pointer: coarse) {
    .yp-btn-sm { min-height: 44px; }
}

/* ---- Bootstrap .btn mapping so classed buttons match ---- */
.btn { border-radius: var(--yp-radius-md); font-weight: 600; }
.btn-primary {
    --bs-btn-bg: var(--yp-yellow);
    --bs-btn-color: var(--yp-on-accent);
    --bs-btn-border-color: var(--yp-yellow);
    --bs-btn-hover-bg: var(--yp-yellow-deep);
    --bs-btn-hover-color: var(--yp-on-accent);
    --bs-btn-hover-border-color: var(--yp-yellow-deep);
    --bs-btn-active-bg: var(--yp-yellow-deep);
    --bs-btn-active-color: var(--yp-on-accent);
    --bs-btn-focus-shadow-rgb: 51, 51, 51;
}
.btn-danger {
    --bs-btn-bg: var(--yp-danger-fill);
    --bs-btn-border-color: var(--yp-danger-fill);
    --bs-btn-hover-bg: var(--yp-danger-fill-hover);
    --bs-btn-hover-border-color: var(--yp-danger-fill-hover);
}

/* ============================================================
   Forms
   ============================================================ */
.yp-field { display: flex; flex-direction: column; gap: 6px; }
.yp-field-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--yp-space-md); }
/* A grouped set of inputs (e.g. an address). <fieldset>/<legend> give the
   group a real accessible name; this resets the browser chrome so it reads
   like a normal stacked field. min-width:0 keeps it from blowing out flex. */
.yp-fieldset { border: none; margin: 0; padding: 0; min-width: 0; }
.yp-fieldset > legend { padding: 0; }
.yp-label {
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
    color: var(--yp-ink);
}

.yp-input,
.form-control,
.form-select {
    font-family: var(--yp-font-sans);
    font-size: 1rem;
    color: var(--yp-ink);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    padding: 10px 12px;
    min-height: 40px;
    box-sizing: border-box;
    transition: border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease);
}
.yp-input::placeholder,
.form-control::placeholder { color: var(--yp-ink-soft); }

/* Drop the native number steppers — they aren't themed and crowd
   right-aligned (yp-num) or $-prefixed values, clipping the text. */
.yp-input[type="number"]::-webkit-outer-spin-button,
.yp-input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.yp-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

.yp-input:focus,
.form-control:focus,
.form-select:focus {
    outline: none;
    border-color: var(--yp-ink);
    box-shadow: var(--yp-focus-ring);
}

/* Checkbox + label pairing — charcoal accent, generous hit target */
.yp-check {
    display: inline-flex;
    align-items: center;
    gap: var(--yp-space-sm);
    cursor: pointer;
    font-size: 1rem;
    color: var(--yp-ink);
    min-height: 40px;
}
.yp-check input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--yp-ink);
    cursor: pointer;
    flex: 0 0 auto;
}
.yp-check input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: var(--yp-focus-ring);
    border-radius: var(--yp-radius-sm);
}

/* "Optional" tag next to a section title */
.yp-optional {
    display: inline-block;
    margin-left: var(--yp-space-sm);
    padding: 1px 8px;
    border-radius: var(--yp-radius-full);
    background: var(--yp-slate-soft);
    color: var(--yp-slate-deep);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: middle;
}

/* Blazor validation states */
.valid.modified:not([type=checkbox]) { outline: none; border-color: var(--yp-success); }
.invalid { outline: none; border-color: var(--yp-danger); }
.validation-message { color: var(--yp-danger); font-size: 0.8125rem; }

/* ============================================================
   Cards / surfaces
   ============================================================ */
.yp-card {
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    padding: var(--yp-space-xl);
}
.yp-card-link { display: block; text-decoration: none; color: inherit; }
.yp-card-link:hover {
    box-shadow: var(--yp-shadow-hover);
    transform: translateY(-1px);
    transition: box-shadow var(--yp-dur-fast) var(--yp-ease), transform var(--yp-dur-fast) var(--yp-ease);
}
.yp-well {
    background: var(--yp-canvas);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    padding: var(--yp-space-lg);
}

.card {
    border-color: var(--yp-border);
    border-radius: var(--yp-radius-lg);
    --bs-card-border-color: var(--yp-border);
}

/* ============================================================
   Tables
   ============================================================ */
.yp-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--yp-surface);
}
.yp-table thead th {
    text-align: left;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
    padding: 10px 14px;
    border-bottom: 1px solid var(--yp-border);
}
.yp-table tbody td {
    padding: 12px 14px;
    border-bottom: 1px solid var(--yp-border);
    min-height: 48px;
}
.yp-table tbody tr { transition: background var(--yp-dur-fast) var(--yp-ease); }
.yp-table tbody tr:hover { background: var(--yp-slate-soft); }
.yp-table tbody tr.is-selected { background: var(--yp-yellow-soft); }
/* Numeric columns right-align both the cell and its header so values sit under the label. */
.yp-num { text-align: right; }
.yp-table thead th.yp-num { text-align: right; }
/* Centered columns (e.g. small counts) — header override beats the default left-align. */
.yp-center { text-align: center; }
.yp-table thead th.yp-center { text-align: center; }

/* Sortable headers: the label is a button; the caret shows only on the active
   column, idle sortable headers darken on hover. Reusable across data tables. */
.yp-th-sort-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    transition: color var(--yp-dur-fast) var(--yp-ease);
}
.yp-th-sort-btn:hover { color: var(--yp-ink); }
.yp-th-sort.is-sorted .yp-th-sort-btn { color: var(--yp-ink); }
.yp-th-caret { font-size: 0.65em; line-height: 1; }

/* ============================================================
   Numbers — money & time always tabular mono
   ============================================================ */
.yp-mono, .yp-amount {
    font-family: var(--yp-font-mono);
    font-weight: 500;
    font-feature-settings: 'tnum' 1;
}
.yp-amount { font-size: 1.375rem; color: var(--yp-ink); }

/* ============================================================
   Badges / status pills
   ============================================================ */
.yp-badge {
    display: inline-flex;
    align-items: center;
    font-weight: 600;
    font-size: 0.75rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    border-radius: var(--yp-radius-full);
    padding: 2px 10px;
    background: var(--yp-slate-soft);
    /* Ink, not slate-deep: slate-deep is a *fill* color and flips dark-on-dark in
       dark mode (1.5:1). Ink flips the right way, and reading stronger than
       yp-badge-muted is what separates Active from Upcoming in both themes. */
    color: var(--yp-ink);
}
.yp-badge-success { background: var(--yp-success-soft); color: var(--yp-success); }
.yp-badge-warning { background: var(--yp-warning-soft); color: var(--yp-warning); }
.yp-badge-danger  { background: var(--yp-danger-soft);  color: var(--yp-danger); }
.yp-badge-brand   { background: var(--yp-yellow);       color: var(--yp-on-accent); }
.yp-badge-muted   { background: var(--yp-slate-soft);   color: var(--yp-ink-soft); }
/* The one filled status pill. Slate Deep is the only neutral that legally carries
   text (The Quiet Slate Rule), and being the sole solid chip in the row is what
   makes "billed, money still out" read at a glance without spending a new hue. */
/* #fff, not --yp-surface: surface flips to near-black in dark mode, which would
   put dark text on a dark fill. Matches .yp-tab.is-active, the other slate-deep
   fill in this system. */
.yp-badge-strong  { background: var(--yp-slate-deep);   color: #fff; }

/* "private" cue on price columns the customer never sees — functional, muted charcoal. */
.yp-private-cue {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    color: var(--yp-ink-soft);
    white-space: nowrap;
}
.yp-private-cue .bi { font-size: 0.75rem; }

/* ============================================================
   Overlays / modals
   ============================================================ */
.modal-content { border: 1px solid var(--yp-border); border-radius: var(--yp-radius-lg); box-shadow: var(--yp-shadow-modal); }
.dropdown-menu { border: 1px solid var(--yp-border); border-radius: var(--yp-radius-md); box-shadow: var(--yp-shadow-overlay); }

/* Hand-rolled dialog (edit/add flows that aren't Bootstrap modals).
   Charcoal scrim, modal shadow, semantic z-index from the scale. */
.yp-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: var(--yp-z-modal-backdrop);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--yp-space-lg);
    background: var(--yp-scrim);
}
.yp-modal {
    width: 460px;
    max-width: 100%;
    max-height: calc(100svh - 2 * var(--yp-space-lg));
    overflow-y: auto;
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    box-shadow: var(--yp-shadow-modal);
    padding: var(--yp-space-xl);
}
.yp-modal-title { font-size: 1.0625rem; font-weight: 600; margin: 0 0 var(--yp-space-lg); }
.yp-modal-body { display: grid; gap: var(--yp-space-lg); }
.yp-modal-actions {
    display: flex;
    gap: var(--yp-space-sm);
    justify-content: flex-end;
    margin-top: var(--yp-space-xl);
}

/* ============================================================
   Loading splash (index.html) — recolor off stock blue
   ============================================================ */
.loading-progress circle:last-child { stroke: var(--yp-yellow-deep) !important; }
.loading-progress-text { color: var(--yp-ink); }

/* ============================================================
   Auth pages — split-panel "legal pad"
   (login / forgot / reset / accept-invite share this chrome)
   Desktop: legal-pad brand panel left, form right.
   Mobile:  legal pad becomes the full background; the form
            floats centered in a white card — no scroll to log in.
   ============================================================ */
.yp-auth {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    min-height: 100vh;
    min-height: 100svh;
}

/* ---- Left: legal-pad brand panel ---- */
.yp-auth-brand {
    /* The red rule is the pad's margin line; content begins just to its
       right, the way you'd actually write on a legal pad. Both the line
       (::after) and this gutter read from one value so they can't drift. */
    --yp-pad-margin: 88px;
    position: relative;
    overflow: hidden;
    background: var(--yp-yellow-soft);
    border-right: 1px solid var(--yp-border);
    padding: var(--yp-space-3xl) 56px var(--yp-space-3xl)
             calc(var(--yp-pad-margin) + var(--yp-space-lg));
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}
/* faint horizontal pad rules */
.yp-auth-brand::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: repeating-linear-gradient(to bottom, transparent 0 38px, rgba(112, 128, 144, 0.16) 38px 39px);
    background-position: 0 96px;
    pointer-events: none;
}
/* the pad's margin line */
.yp-auth-brand::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--yp-pad-margin);
    width: 1px;
    background: rgba(198, 42, 42, 0.30);
    pointer-events: none;
}
.yp-auth-wordmark,
.yp-auth-cardmark {
    position: relative;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--yp-ink-strong);
}
.yp-auth-pad {
    background: var(--yp-yellow);
    color: var(--yp-on-accent);
    padding: 1px 9px;
    border-radius: 6px;
    margin-left: 2px;
}
.yp-auth-pitch { position: relative; max-width: 24ch; }
.yp-auth-pitch-lead {
    font-size: clamp(1.9rem, 1.4rem + 1.6vw, 2.6rem);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.02em;
    color: var(--yp-ink-strong);
    text-wrap: balance;
    margin: 0 0 var(--yp-space-lg);
}
.yp-auth-pitch-sub {
    font-size: 1.0625rem;
    line-height: 1.5;
    color: var(--yp-ink);
    margin: 0;
    max-width: 32ch;
}
.yp-auth-foot {
    position: relative;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--yp-ink-soft);
}

/* ---- Right: form column ---- */
.yp-auth-main {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--yp-space-3xl) var(--yp-space-2xl);
    background: var(--yp-surface);
}
.yp-auth-card { width: 100%; max-width: 360px; }
.yp-auth-cardmark { display: none; margin-bottom: var(--yp-space-xl); }
.yp-auth-title {
    font-size: 1.625rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0 0 6px;
}
.yp-auth-sub {
    font-size: 0.9375rem;
    color: var(--yp-ink-soft);
    margin: 0 0 var(--yp-space-2xl);
    max-width: none;
}
.yp-auth-card .yp-field { margin-bottom: var(--yp-space-lg); }
.yp-auth-card .yp-input { min-height: 44px; }
/* Auth is the one screen everyone hits on a phone; match the input's
   thumb-sized target rather than the 40px desktop default. */
.yp-auth-card .yp-btn { min-height: 44px; }

/* An alternate way in (passkey), not a second action on the password
   form. The labelled hairline separates the two paths so the buttons
   stop reading as one stacked control group. */
.yp-auth-alt { margin-top: var(--yp-space-xl); }
.yp-auth-alt-label {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    margin-bottom: var(--yp-space-lg);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--yp-ink-soft);
}
.yp-auth-alt-label::before,
.yp-auth-alt-label::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--yp-border);
}

.yp-auth-forgot {
    display: block;
    text-align: right;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--yp-ink);
    text-decoration: none;
    margin: -2px 0 var(--yp-space-xl);
}
.yp-auth-forgot:hover { color: var(--yp-ink-strong); text-decoration: underline; }

.yp-auth-alert {
    display: flex;
    gap: var(--yp-space-sm);
    align-items: flex-start;
    background: var(--yp-danger-soft);
    border: 1px solid rgba(198, 42, 42, 0.28);
    border-radius: var(--yp-radius-md);
    padding: 10px 12px;
    margin-bottom: var(--yp-space-lg);
    color: var(--yp-danger);
    font-size: 0.875rem;
}
.yp-auth-notice {
    background: var(--yp-success-soft);
    border: 1px solid rgba(47, 125, 79, 0.28);
    color: var(--yp-success);
    border-radius: var(--yp-radius-md);
    padding: 12px 14px;
    font-size: 0.9375rem;
}
.yp-auth-back {
    display: inline-block;
    margin-top: var(--yp-space-lg);
    font-weight: 600;
    font-size: 0.875rem;
}

.yp-btn-block { width: 100%; }

@media (max-width: 820px) {
    .yp-auth { display: block; min-height: 100svh; }
    .yp-auth-brand { display: none; }
    .yp-auth-main {
        position: relative;
        min-height: 100svh;
        padding: var(--yp-space-xl);
        background: var(--yp-yellow-soft);
    }
    .yp-auth-main::before {
        content: "";
        position: absolute;
        inset: 0;
        background-image: repeating-linear-gradient(to bottom, transparent 0 38px, rgba(112, 128, 144, 0.16) 38px 39px);
        background-position: 0 24px;
        pointer-events: none;
    }
    .yp-auth-main::after {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        left: 40px;
        width: 1px;
        background: rgba(198, 42, 42, 0.30);
        pointer-events: none;
    }
    .yp-auth-card {
        position: relative;
        z-index: 1;
        background: var(--yp-surface);
        border: 1px solid var(--yp-border);
        border-radius: var(--yp-radius-lg);
        box-shadow: var(--yp-shadow-modal);
        padding: var(--yp-space-2xl) var(--yp-space-xl);
        max-width: 400px;
    }
    .yp-auth-cardmark { display: block; }
}

/* ============================================================
   Job create — focused single-column flow (/jobs/new)
   Lean by design: title + client, then the job detail page is
   where the rest accretes. Targets sized for touch.
   ============================================================ */
.yp-back {
    display: inline-block;
    margin-bottom: var(--yp-space-md);
    font-weight: 600;
    font-size: 0.875rem;
    color: var(--yp-ink-soft);
    text-decoration: none;
}
.yp-back:hover { color: var(--yp-ink-strong); }

.yp-create { max-width: 480px; }
.yp-create-hint {
    color: var(--yp-ink-soft);
    font-size: 0.9375rem;
    margin: 6px 0 var(--yp-space-xl);
    max-width: 52ch;
}
.yp-create .yp-field + .yp-field { margin-top: var(--yp-space-lg); }
.yp-create .yp-input { min-height: 44px; }
.yp-create-actions {
    display: flex;
    gap: var(--yp-space-sm);
    margin-top: var(--yp-space-xl);
}
.yp-create-actions .yp-btn { min-height: 44px; }

/* ============================================================
   Combobox — searchable picker with inline create
   (SearchableClientPicker). Dropdown escapes its card; no
   overflow:hidden ancestor clips it. Charcoal focus, slate hover,
   yellow-soft only as a fill under the create row's charcoal text.
   ============================================================ */
.yp-combobox { position: relative; width: 100%; }
/* The field fills its container so long placeholders don't clip. */
.yp-combobox > .yp-input { width: 100%; box-sizing: border-box; }

.yp-combobox-chip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--yp-space-sm);
    background: var(--yp-yellow-soft);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    padding: 8px 8px 8px 14px;
    min-height: 44px;
}
.yp-combobox-chip-name { font-weight: 600; color: var(--yp-ink); }
/* Holds the chip's exact height while the job name resolves, so the picker never
   flashes a search input (and its keyboard) at a selection that already exists. */
.yp-combobox-chip-skel { height: 1rem; width: min(60%, 220px); }
.yp-combobox-change {
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-sm);
    color: var(--yp-ink);
    font-family: inherit;
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
    padding: 6px 12px;
    min-height: 32px;
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease);
}
.yp-combobox-change:hover { background: var(--yp-slate-soft); }

.yp-combobox-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    box-shadow: var(--yp-shadow-overlay);
    max-height: 288px;
    overflow-y: auto;
    padding: 4px;
    z-index: var(--yp-z-dropdown);
}
.yp-combobox-option {
    display: flex;
    align-items: center;
    gap: var(--yp-space-sm);
    width: 100%;
    text-align: left;
    border: none;
    background: none;
    cursor: pointer;
    font-family: inherit;
    font-size: 1rem;
    color: var(--yp-ink);
    padding: 10px 12px;
    min-height: 44px;
    border-radius: var(--yp-radius-sm);
    transition: background var(--yp-dur-fast) var(--yp-ease);
}
.yp-combobox-option:hover,
.yp-combobox-option.is-active { background: var(--yp-slate-soft); }
.yp-combobox-option-meta {
    margin-left: auto;
    color: var(--yp-ink-soft);
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}
.yp-combobox-create { font-weight: 600; }
.yp-combobox-create:hover,
.yp-combobox-create.is-active { background: var(--yp-yellow-soft); }
.yp-combobox-plus {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 22px;
    height: 22px;
    border-radius: var(--yp-radius-sm);
    background: var(--yp-yellow);
    color: var(--yp-on-accent);
    font-weight: 700;
    line-height: 1;
}
.yp-combobox-empty {
    padding: 10px 12px;
    color: var(--yp-ink-soft);
    font-size: 0.875rem;
}
/* Section header inside the menu (e.g. "Recent" / "Active jobs" / "Upcoming jobs").
   Sticky because the group is load-bearing: an upcoming job starts when you log
   time against it, so the heading has to still be readable once the list has
   scrolled past it. Opaque so rows pass underneath rather than through. */
.yp-combobox-group {
    position: sticky;
    /* -4px, not 0: the menu's own 4px padding would otherwise leave a gap above
       the pinned heading where the row scrolling past shows through as a sliver. */
    top: -4px;
    z-index: 1;
    background: var(--yp-surface);
    padding: 8px 12px 4px;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-combobox-group:not(:first-child) {
    margin-top: 2px;
    border-top: 1px solid var(--yp-border);
    padding-top: 8px;
}
/* The job number reads as tabular mono so #s line up down the list. */
.yp-combobox-jobnum {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    color: var(--yp-ink);
}

/* Inline (in-sheet) variant: search box stays put, the list renders in flow and
   scrolls in place instead of floating as a dropdown a short sheet would clip. */
.yp-combobox-inline {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    gap: var(--yp-space-sm);
}
.yp-combobox-inline > .yp-input { flex: none; }
.yp-combobox-menu-inline {
    position: static;
    /* Size to the list, capped at ~5 rows (matching the floating menu) and
       scrolling past that. Letting it fill instead grew the sheet to the full
       viewport on a long job list, pushing the search box behind the keyboard. */
    flex: 0 1 auto;
    min-height: 0;
    max-height: 288px;
    box-shadow: none;
}
/* Selected row (charcoal check on a highlighter wash); wins over hover/active. */
.yp-combobox-option.is-selected,
.yp-combobox-option.is-selected:hover,
.yp-combobox-option.is-selected.is-active { background: var(--yp-yellow-soft); }
.yp-combobox-check { flex: none; margin-left: auto; color: var(--yp-ink); }

/* ============================================================
   App page chrome — heads, toolbars, filter tabs, alerts,
   empty states. Shared across list/detail pages so Jobs,
   Clients, etc. read as one tool.
   ============================================================ */
.yp-page-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--yp-space-md);
    flex-wrap: wrap;
    margin-bottom: var(--yp-space-xl);
}
.yp-toolbar {
    display: flex;
    gap: var(--yp-space-md);
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: var(--yp-space-lg);
}
/* Filter toolbars align their controls to the input baseline (labels sit above). */
.yp-toolbar-end { align-items: flex-end; }

/* Small layout utilities — keep one-off positioning out of inline styles. */
.yp-push-end { margin-left: auto; }
.yp-nowrap { white-space: nowrap; }
.yp-cell-strong { font-weight: 600; }
.yp-cell-muted { color: var(--yp-ink-soft); }
/* Supporting second line inside a table cell — the job under the client, the
   type under the invoice number. Pairing columns this way keeps a wide list
   from pushing its rightmost columns off a laptop screen. */
.yp-cell-sub {
    display: block;
    margin-top: 2px;
    font-size: 0.8125rem;
    line-height: 1.35;
    color: var(--yp-ink-soft);
}
/* Declared last so it also recolors a `.yp-cell-sub` it sits on (the
   "9 days late" line under an overdue due date). */
.yp-text-danger { color: var(--yp-danger); }
.yp-label-optional { font-weight: 400; color: var(--yp-ink-soft); }

/* Filter tabs — selected uses the filled-neutral token (slate-deep),
   so brand yellow stays reserved for the one primary action. */
.yp-tabs { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.yp-tab {
    font-family: inherit;
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
    padding: 7px 14px;
    min-height: 36px;
    border-radius: var(--yp-radius-md);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    color: var(--yp-ink);
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease),
                border-color var(--yp-dur-fast) var(--yp-ease);
}
.yp-tab:hover { background: var(--yp-slate-soft); }
.yp-tab.is-active {
    background: var(--yp-slate-deep);
    border-color: var(--yp-slate-deep);
    color: #fff;
}
/* Field use is thumb-first: give the filter tabs a full target on touch. */
@media (pointer: coarse) {
    .yp-tab { min-height: 44px; }
}

.yp-search { width: 260px; max-width: 100%; }

/* Generic inline alerts (page-level; auth pages keep their own .yp-auth-alert) */
.yp-alert {
    display: flex;
    gap: var(--yp-space-sm);
    align-items: flex-start;
    border: 1px solid transparent;
    border-radius: var(--yp-radius-md);
    padding: 10px 14px;
    font-size: 0.9375rem;
    margin-bottom: var(--yp-space-lg);
}
.yp-alert-danger  { background: var(--yp-danger-soft);  border-color: rgba(198, 42, 42, 0.28); color: var(--yp-danger); }
.yp-alert-warning { background: var(--yp-warning-soft); border-color: rgba(180, 83, 9, 0.28);  color: var(--yp-warning); }
.yp-alert-success { background: var(--yp-success-soft); border-color: rgba(47, 125, 79, 0.28); color: var(--yp-success); }

.yp-empty { color: var(--yp-ink-soft); font-size: 0.9375rem; padding: var(--yp-space-md) 0; }
/* Alert sitting in a gap-spaced flex column shouldn't add its own margin. */
.yp-alert-flush { margin: 0; }

/* First-run empty state: teaches what the surface is for, then offers the
   one action that fills it. Reused by any "no records yet" landing. */
.yp-emptystate {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--yp-space-sm);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    padding: var(--yp-space-2xl) var(--yp-space-xl);
    max-width: 480px;
}
.yp-emptystate-title { margin: 0; font-weight: 600; font-size: 1.0625rem; color: var(--yp-ink); }
.yp-emptystate-body { margin: 0 0 var(--yp-space-sm); color: var(--yp-ink-soft); max-width: 46ch; }

/* Skeleton loading — mirrors the page's shape instead of a mid-content
   spinner. The shimmer is decorative; the global reduced-motion rule
   below stops it, leaving calm static blocks. */
.yp-skel-page { display: block; }
.yp-skel {
    background: var(--yp-slate-soft);
    border-radius: var(--yp-radius-sm);
    position: relative;
    overflow: hidden;
}
.yp-skel::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, var(--yp-shimmer), transparent);
    animation: yp-shimmer 1.4s var(--yp-ease) infinite;
}
@keyframes yp-shimmer { 100% { transform: translateX(100%); } }

/* /time loading skeleton — mirrors the focal clock, hours strip, and entries. */
.yp-skel-time { max-width: 560px; }
.yp-skel-clock { height: 188px; border-radius: var(--yp-radius-lg); margin-bottom: var(--yp-space-2xl); }
.yp-skel-sum { height: 74px; border-radius: var(--yp-radius-lg); margin-bottom: var(--yp-space-2xl); }
.yp-skel-heading { height: 18px; width: 140px; margin-bottom: var(--yp-space-lg); }
.yp-skel-rows { display: flex; flex-direction: column; gap: var(--yp-space-sm); }
.yp-skel-rows .yp-skel { height: 44px; }

/* Inline link under a form field (e.g. "View client →") */
.yp-field-link {
    align-self: flex-start;
    margin-top: 2px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--yp-ink-soft);
    text-decoration: none;
}
.yp-field-link:hover { color: var(--yp-ink-strong); text-decoration: underline; }

/* Detail page building blocks */
.yp-form-grid { display: grid; gap: var(--yp-space-lg); max-width: 540px; }
.yp-section-title { font-size: 1.0625rem; font-weight: 600; margin: 0 0 var(--yp-space-md); }
.yp-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--yp-space-md);
    margin: 0 0 var(--yp-space-md);
}
.yp-section-head .yp-section-title { margin: 0; }
.yp-section + .yp-section { margin-top: var(--yp-space-2xl); }
.yp-input-sm { min-height: 34px; padding: 6px 8px; font-size: 0.9375rem; }

/* Icon-only buttons (line-item save/remove, etc.) */
.yp-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    cursor: pointer;
    padding: 6px;
    min-width: 32px;
    min-height: 32px;
    border-radius: var(--yp-radius-sm);
    color: var(--yp-ink-soft);
    transition: background var(--yp-dur-fast) var(--yp-ease), color var(--yp-dur-fast) var(--yp-ease);
}
.yp-icon-btn:hover { background: var(--yp-slate-soft); color: var(--yp-ink); }
.yp-icon-btn-confirm:hover { background: var(--yp-success-soft); color: var(--yp-success); }
.yp-icon-btn-danger:hover { background: var(--yp-danger-soft); color: var(--yp-danger); }

/* Text link button (inline edit/delete in tables) */
.yp-linkbtn {
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--yp-ink);
    padding: 4px 6px;
    border-radius: var(--yp-radius-sm);
}
.yp-linkbtn:hover { background: var(--yp-slate-soft); }
.yp-linkbtn-danger { color: var(--yp-danger); }
.yp-linkbtn-danger:hover { background: var(--yp-danger-soft); }

/* Inline two-step confirm for a destructive row action (no modal needed). */
.yp-confirm-inline { display: inline-flex; align-items: center; gap: var(--yp-space-xs); white-space: nowrap; }
.yp-confirm-q { font-size: 0.8125rem; font-weight: 600; color: var(--yp-ink); }

/* Field use is thumb-first: give the dense icon/link controls a full
   target on touch, matching .yp-tab and .yp-btn-sm. */
@media (pointer: coarse) {
    .yp-icon-btn { min-width: 44px; min-height: 44px; }
    .yp-linkbtn { min-height: 44px; padding: 10px 12px; }
}

/* On small screens, wide data tables scroll rather than crush (anti-ERP). */
.yp-table-wrap { overflow-x: auto; }

/* Responsive data table: the flat hairline table on desktop, stacked
   labeled cards below 641px (anti-ERP, thumb-first). Each cell carries a
   data-label that becomes its field name when stacked; the value sits
   right, editable inputs included. Read-only and editable tables share it. */
@media (max-width: 640px) {
    .yp-table-stack thead { display: none; }
    .yp-table-stack tbody,
    .yp-table-stack tbody tr,
    .yp-table-stack tbody td { display: block; }
    .yp-table-stack tbody tr {
        border: 1px solid var(--yp-border);
        border-radius: var(--yp-radius-md);
        background: var(--yp-surface);
        padding: var(--yp-space-sm) var(--yp-space-md);
        margin-bottom: var(--yp-space-sm);
    }
    .yp-table-stack tbody td {
        border: none;
        padding: 6px 0;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--yp-space-md);
        text-align: right;
    }
    .yp-table-stack tbody td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        text-align: left;
        font-size: 0.75rem;
        font-weight: 600;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: var(--yp-ink-soft);
    }
    .yp-table-stack tbody td:empty { display: none; }
    .yp-table-stack tbody td .yp-input { flex: 1 1 auto; width: auto; max-width: 62%; }
    .yp-table-stack tfoot { display: block; }
    .yp-table-stack tfoot tr {
        display: flex;
        justify-content: space-between;
        gap: var(--yp-space-md);
        padding-top: var(--yp-space-sm);
        border-top: 1px solid var(--yp-border);
    }
    /* `tr td` raises specificity above the desktop `.yp-table tfoot td`
       border/padding rule so the stacked footer keeps one clean separator
       (the tr's border-top) instead of a 2px border per cell. */
    .yp-table-stack tfoot tr td { display: block; border-top: none; padding: 0; }
    .yp-table-stack tfoot td:empty { display: none; }
}

/* Row-as-link list that collapses a navigational table into stacked,
   tappable cards below 641px. Generalizes the clients-list pattern. */
.yp-rows-stack { display: flex; flex-direction: column; gap: var(--yp-space-sm); }
.yp-rows-table { display: none; }
@media (min-width: 641px) {
    .yp-rows-stack { display: none; }
    .yp-rows-table { display: block; }
}
.yp-stackrow {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: var(--yp-space-md);
    min-height: 44px;
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    background: var(--yp-surface);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease);
}
.yp-stackrow:hover { border-color: var(--yp-slate); box-shadow: var(--yp-shadow-hover); }
.yp-stackrow-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--yp-space-sm);
}
.yp-stackrow-title { font-weight: 600; color: var(--yp-ink); }
.yp-stackrow-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 0.875rem;
    color: var(--yp-ink-soft);
}
.yp-stackrow-sep { color: var(--yp-border); }
/* Keeps a segment glued to the separator that introduces it, so wrapping the
   meta line never leaves a "·" stranded at the end of a row. */
.yp-stackrow-part { display: inline-flex; align-items: baseline; gap: 6px; }
/* Labeled money line at the foot of a stacked row. On a phone there are no
   Total/Balance columns to read across to, so the figures get their own rule
   and their own labels rather than hiding in the meta run-on. */
/* Balance sits hard right, the way it does in the desktop Balance column —
   the figure to read lands on the same edge in both layouts. */
.yp-stackrow-money {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--yp-space-lg);
    padding-top: 8px;
    border-top: 1px solid var(--yp-border);
}
.yp-stackrow-money-item { display: flex; align-items: baseline; gap: 6px; }
.yp-stackrow-money-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}

/* Row-as-link: the title cell is a real anchor (keyboard focus, semantics,
   open-in-new-tab); the whole row stays mouse-clickable as an enhancement.
   The anchor stops propagation so the row handler doesn't double-fire. */
.yp-row-link { cursor: pointer; }
.yp-row-link-title {
    font-weight: 600;
    color: var(--yp-ink);
    text-decoration: none;
}
.yp-row-link-title:hover { color: var(--yp-ink-strong); text-decoration: underline; }

/* ------------------------------------------------------------
   Clients list. Desktop is the flat hairline table; below 641px
   it collapses to stacked, tappable rows (anti-ERP, thumb-first).
   ------------------------------------------------------------ */
.yp-client-list { display: flex; flex-direction: column; gap: var(--yp-space-sm); }
.yp-client-table { display: none; }
@media (min-width: 641px) {
    .yp-client-list { display: none; }
    .yp-client-table { display: block; }
}
.yp-client-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: var(--yp-space-md);
    min-height: 44px;
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    background: var(--yp-surface);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease);
}
.yp-client-row:hover { border-color: var(--yp-slate); box-shadow: var(--yp-shadow-hover); }
.yp-client-row-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--yp-space-sm);
}
.yp-client-name { font-weight: 600; color: var(--yp-ink); }
.yp-client-row-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 0.875rem;
    color: var(--yp-ink-soft);
}
.yp-client-row-sep { color: var(--yp-border); }

/* ============================================================
   Time clock — signature widget (/time)
   The highest-frequency field action gets a defined "legal pad"
   panel sized for a one-thumb tap.

   Stopped: a neutral white panel framed as a question — "What are
   you working on?". The job picker is the hero. Attaching a job is
   the rewarded path: only then does the yellow "Clock in"
   highlighter appear. Clocking in with no job is still allowed but
   demoted to a quiet ghost button — compelled, never blocked.

   Running: the panel washes yellow-soft (the legal pad you're
   writing on), the mono timer is the center of gravity, and the
   primary action becomes Clock out (danger) — so the brand
   highlighter and the action color never collide.
   ============================================================ */
.yp-clock {
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    padding: var(--yp-space-2xl) var(--yp-space-xl);
    margin-bottom: var(--yp-space-2xl);
}
.yp-clock.is-running {
    background: var(--yp-yellow-soft);
    border-color: var(--yp-yellow-deep);
}

/* ---- Stopped: the "what are you on?" form ---- */
.yp-clock-stopped { display: flex; flex-direction: column; gap: var(--yp-space-md); }
.yp-clock-prompt {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--yp-ink);
}
.yp-clock-prompt-sub {
    display: block;
    margin-top: 2px;
    font-size: 0.875rem;
    font-weight: 400;
    color: var(--yp-ink-soft);
}

/* The nudge that makes a job feel expected without blocking it.
   It names the reason (this time bills against the job). */
.yp-clock-nudge {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin-top: calc(var(--yp-space-xs) * -1);
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--yp-ink-soft);
}
.yp-clock-nudge svg { flex: none; margin-top: 1px; color: var(--yp-slate); }
/* Same nudge, but below a field instead of tucked under the picker. */
.yp-clock-nudge-spaced { margin-top: var(--yp-space-sm); }

/* ---- Running: the live timer ---- */
.yp-clock-running {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--yp-space-sm);
    margin-inline: auto;
    max-width: 380px;
}

.yp-clock-pulse {
    display: inline-block;
    width: 7px;
    height: 7px;
    margin-right: 6px;
    border-radius: 50%;
    background: var(--yp-success);
    animation: yp-pulse 2s var(--yp-ease) infinite;
}
@keyframes yp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

.yp-clock-job {
    /* inline-block, not block: as a link the hit area should be the title,
       not the full width of the panel. */
    display: inline-block;
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--yp-ink);
    margin-top: var(--yp-space-xs);
    text-decoration: none;
}
a.yp-clock-job:hover { color: var(--yp-ink-strong); text-decoration: underline; }
a.yp-clock-job:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); border-radius: var(--yp-radius-sm); }
.yp-clock-job.is-none { font-weight: 400; color: var(--yp-ink-soft); }

.yp-clock-timer {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: clamp(2.75rem, 2rem + 4vw, 3.75rem);
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--yp-ink-strong);
    margin: var(--yp-space-xs) 0 0;
}
.yp-clock-since { font-size: 0.875rem; color: var(--yp-ink-soft); }

/* Running-with-no-job: a sunken white well over the yellow-soft panel
   (a well, not a nested card) holding the late job assignment. */
.yp-clock-assign-well {
    width: 100%;
    max-width: 360px;
    margin-top: var(--yp-space-sm);
    padding: var(--yp-space-md);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    text-align: left;
}
.yp-clock-assign-well .yp-label { display: block; margin-bottom: 6px; }
.yp-clock-assign { width: 100%; margin-top: var(--yp-space-sm); }

/* Clock in / out — taller than a normal button for gloved thumbs. */
.yp-clock-action { width: 100%; min-height: 52px; font-size: 0.9375rem; margin-top: var(--yp-space-xs); }
/* The quiet escape hatch under the primary CTA: clock in without a job. */
.yp-clock-skip {
    align-self: center;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
    padding: 8px 10px;
    min-height: 36px;
    border-radius: var(--yp-radius-sm);
    text-decoration: underline;
    text-underline-offset: 2px;
    transition: color var(--yp-dur-fast) var(--yp-ease);
}
.yp-clock-skip:hover { color: var(--yp-ink); }
@media (pointer: coarse) { .yp-clock-skip { min-height: 44px; } }

/* ============================================================
   Time summary strip (/time, My time) — hours at a glance.
   Functional data (this week / today), not a hero-metric block:
   modest mono numerals, sturdy frame, two items max.
   ============================================================ */
.yp-timesum {
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: var(--yp-space-xl);
    padding: var(--yp-space-lg) var(--yp-space-xl);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    margin-bottom: var(--yp-space-2xl);
}

/* ---- Desktop composition (My time) ----
   The clock and hours stats are focal controls: capped and centered so
   they don't stretch across a wide monitor. The entries below break out
   to the full content width as a timesheet (see .yp-timesheet). On a
   phone the focal column is simply the full width. */
.yp-time-focal { max-width: 560px; }
@media (min-width: 641px) {
    .yp-time-focal { margin-inline: auto; }
}
.yp-timesum-item { display: flex; flex-direction: column; gap: 6px; }
.yp-timesum-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-timesum-value {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--yp-ink-strong);
}
.yp-timesum-divider { width: 1px; background: var(--yp-border); }

/* Three figures (Today / This week / This pay) get tight on a phone —
   trim the gap and value size so they stay on one row. */
@media (max-width: 640px) {
    .yp-timesum { gap: var(--yp-space-lg); padding: var(--yp-space-md) var(--yp-space-lg); }
    .yp-timesum-value { font-size: 1.25rem; }
}

/* ============================================================
   My-time recent entries — grouped by day, field-first.
   Each day is a header (label + total hours in mono) over a light
   row list that reflows on mobile instead of scrolling a grid. The
   admin "All entries" view keeps the full .yp-table.
   ============================================================ */
.yp-daygroup { max-width: 560px; }
.yp-daygroup + .yp-daygroup { margin-top: var(--yp-space-xl); }
.yp-daygroup-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--yp-space-md);
    padding-bottom: 6px;
    margin-bottom: var(--yp-space-xs);
    border-bottom: 1px solid var(--yp-border);
}
.yp-daygroup-day { font-weight: 600; font-size: 0.9375rem; color: var(--yp-ink); }
.yp-daygroup-total {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 0.875rem;
    color: var(--yp-ink-soft);
}

.yp-entry {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    padding: 10px var(--yp-space-sm);
    margin-inline: calc(var(--yp-space-sm) * -1);
    border-radius: var(--yp-radius-md);
    transition: background var(--yp-dur-fast) var(--yp-ease);
}
.yp-entry:hover { background: var(--yp-slate-soft); }
.yp-entry-main { flex: 1 1 auto; min-width: 0; }
.yp-entry-job { font-weight: 600; color: var(--yp-ink); }
.yp-entry-job.is-none { font-weight: 400; color: var(--yp-ink-soft); }
.yp-entry-time {
    margin-top: 2px;
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
}
.yp-entry-time .yp-mono { font-size: 0.8125rem; color: var(--yp-ink-soft); }
.yp-entry-dur {
    flex: none;
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--yp-ink);
    min-width: 60px;
    text-align: right;
}
.yp-entry .yp-linkbtn { flex: none; }

/* Admin "All entries" — totals footer so the office can trust the sum. */
.yp-table tfoot td {
    padding: 12px 14px;
    border-top: 2px solid var(--yp-border);
    font-weight: 600;
    color: var(--yp-ink);
}

/* ============================================================
   My-time recent entries — desktop timesheet.
   Below 641px the stacked .yp-timelog-mobile list shows (field,
   one thumb). At 641px+ (the layout's sidebar breakpoint) the
   record breaks out to a full-width timesheet: real Clock in /
   Clock out / Hours columns that justify the width, with each
   day's subtotal as a banded header row. One reads top-to-bottom
   like a paper time card.
   ============================================================ */
.yp-timesheet { display: none; }
.yp-timelog-mobile { display: block; }

@media (min-width: 641px) {
    .yp-timelog-mobile { display: none; }
    .yp-timesheet {
        display: table;
        width: 100%;
        border-collapse: collapse;
        background: var(--yp-surface);
    }
}

.yp-timesheet thead th {
    text-align: left;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
    padding: 10px 14px;
    border-bottom: 1px solid var(--yp-border);
    white-space: nowrap;
}
.yp-timesheet thead th.yp-num { text-align: right; }

/* Day subtotal header row — a banded rule across the table. */
.yp-timesheet-dayrow > * {
    background: var(--yp-canvas);
    border-top: 1px solid var(--yp-border);
    border-bottom: 1px solid var(--yp-border);
    padding: 8px 14px;
}
.yp-timesheet tbody:first-of-type .yp-timesheet-dayrow > * { border-top: none; }
.yp-timesheet-day {
    text-align: left;
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--yp-ink);
}
.yp-timesheet-daytotal {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    color: var(--yp-ink-soft);
    white-space: nowrap;
}

/* Entry rows */
.yp-timesheet-entry td {
    padding: 12px 14px;
    border-bottom: 1px solid var(--yp-border);
    vertical-align: baseline;
}
.yp-timesheet-entry { transition: background var(--yp-dur-fast) var(--yp-ease); }
.yp-timesheet-entry:hover { background: var(--yp-slate-soft); }
.yp-timesheet-entry .yp-job { font-weight: 600; color: var(--yp-ink); }
.yp-timesheet-entry .yp-job.is-none { font-weight: 400; color: var(--yp-ink-soft); }
.yp-timesheet-entry .yp-mono { color: var(--yp-ink-soft); white-space: nowrap; }
.yp-timesheet-entry .yp-num.yp-mono { color: var(--yp-ink); }
/* The Job column flexes; time/hours columns size to content. */
.yp-timesheet col.yp-col-job { width: 100%; }
.yp-timesheet .yp-col-actions { white-space: nowrap; text-align: right; }

/* ---- Progressive history: "Load older entries" ----
   The record opens on the last week; this reveals two more weeks per
   press. Secondary (ghost) button — yellow stays reserved for Clock in.
   Newly revealed day groups fade up once on mount; already-visible days
   keep their DOM node (keyed by date) and don't re-animate. */
.yp-loadmore {
    display: flex;
    justify-content: center;
    margin-top: var(--yp-space-xl);
    margin-bottom: var(--yp-space-2xl);
}
.yp-loadmore .yp-btn { min-width: 200px; }
.yp-loadmore-end {
    margin-top: var(--yp-space-xl);
    margin-bottom: var(--yp-space-2xl);
    max-width: none;
    text-align: center;
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
}

.yp-daygroup-reveal,
.yp-exp-reveal {
    animation: yp-day-reveal var(--yp-dur-base) var(--yp-ease) both;
}
@keyframes yp-day-reveal {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ---- Clock chip — persistent top-bar timer (ClockWidget) ----
   Compact sibling of the panel above: a yellow-soft running pill
   with a success pulse and mono timer, a popover for the job, and
   a danger Clock-out. Stopped state is a quiet ghost button. */
.yp-clockchip { display: flex; align-items: center; gap: var(--yp-space-sm); }

/* A link to Time, not a disclosure: the Time page carries the current job,
   the assign-a-job control, and Clock out, so there is nothing left worth
   duplicating in a popover. */
.yp-clockchip-time {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 6px 12px;
    background: var(--yp-yellow-soft);
    border: 1px solid var(--yp-yellow-deep);
    border-radius: var(--yp-radius-md);
    cursor: pointer;
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--yp-ink-strong);
    text-decoration: none;
    white-space: nowrap;
    transition: box-shadow var(--yp-dur-fast) var(--yp-ease);
}
.yp-clockchip-time:hover { color: var(--yp-ink-strong); }
.yp-clockchip-time:hover { box-shadow: var(--yp-shadow-hover); }
.yp-clockchip-time:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }

/* On a small phone the running chip plus Clock out needs the whole bar. Drop
   the wordmark rather than let it truncate to "Yello…" — the clock is what a
   crew is looking at, and a clipped brand mark reads as a bug. Only while the
   clock runs: the stopped state's button leaves room at any width. Lives here
   rather than in MainLayout's scoped sheet because :has() can't reach through
   ::deep into the ClockWidget child component. */
@media (max-width: 379.98px) {
    .top-row:has(.yp-clockchip) .app-brand { display: none; }
}

/* ============================================================
   Expenses — field-first log, office-grade record.
   Mobile (<641px): a stacked .yp-exp list, one thumb per row.
   Desktop (641px+): the full .yp-table breaks out. A summary
   strip carries the running total in mono so the office trusts
   the number. Type is a neutral chip (Business slate / Personal
   yellow-soft wash); reconciled is the only status color here.
   ============================================================ */
.yp-exp-summary {
    display: flex;
    align-items: baseline;
    gap: var(--yp-space-sm);
    margin-bottom: var(--yp-space-lg);
    color: var(--yp-ink-soft);
    font-size: 0.875rem;
}
.yp-exp-summary-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-exp-summary-total {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 1.0625rem;
    color: var(--yp-ink);
}
.yp-exp-summary-dot { color: var(--yp-border); }

/* Type chips — neutral, never a status color (The Highlighter Rule
   keeps yellow for the one action; status color is reconciled only). */
.yp-exp-type {
    display: inline-block;
    border-radius: var(--yp-radius-full);
    padding: 2px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}
.yp-exp-type-business { background: var(--yp-slate-soft); color: var(--yp-slate-deep); }
.yp-exp-type-personal { background: var(--yp-yellow-soft); color: var(--yp-ink-strong); }

/* Receipt indicator */
.yp-exp-clip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--yp-ink-soft);
    font-size: 0.8125rem;
}
.yp-exp-clip.is-missing { color: var(--yp-warning); }
.yp-exp-clip svg { flex: none; }

/* Clickable receipt indicator — opens the quick-view lightbox.
   Sits inside a row that's also clickable, so it stops propagation in markup. */
.yp-exp-clip-btn {
    appearance: none;
    border: 1px solid transparent;
    background: transparent;
    padding: 4px 8px;
    border-radius: var(--yp-radius-full);
    font: inherit;
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease),
                border-color var(--yp-dur-fast) var(--yp-ease),
                color var(--yp-dur-fast) var(--yp-ease);
}
.yp-exp-clip-btn:hover {
    background: var(--yp-slate-soft);
    border-color: var(--yp-border);
    color: var(--yp-ink);
}
.yp-exp-clip-btn:focus-visible {
    outline: none;
    box-shadow: var(--yp-focus-ring);
    color: var(--yp-ink);
}

/* ---- Receipt quick-view (lightbox) ---- */
.yp-rv {
    width: min(560px, 100%);
    padding: var(--yp-space-lg);
    display: flex;
    flex-direction: column;
    gap: var(--yp-space-md);
    animation: yp-rv-in var(--yp-dur-base) var(--yp-ease);
}
.yp-rv-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--yp-space-md);
}
.yp-rv-sub {
    margin: 4px 0 0;
    color: var(--yp-ink-soft);
    font-size: 0.875rem;
}
.yp-rv-dot { color: var(--yp-border); margin: 0 2px; }
.yp-rv-stage {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--yp-canvas);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    padding: var(--yp-space-sm);
    min-height: 240px;
    overflow: hidden;
}
.yp-rv-img {
    display: block;
    max-width: 100%;
    max-height: 68vh;
    width: auto;
    height: auto;
    border-radius: var(--yp-radius-sm);
    object-fit: contain;
}
.yp-rv-pdf {
    width: 100%;
    height: 68vh;
    border: 0;
    border-radius: var(--yp-radius-sm);
    background: var(--yp-surface);
}
.yp-rv-skel { width: 100%; height: 360px; border-radius: var(--yp-radius-sm); }
.yp-rv-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-full);
    background: rgba(255, 255, 255, 0.92);
    color: var(--yp-ink);
    cursor: pointer;
    box-shadow: var(--yp-shadow-overlay);
    transition: background var(--yp-dur-fast) var(--yp-ease), transform var(--yp-dur-fast) var(--yp-ease);
}
.yp-rv-arrow:hover { background: var(--yp-surface); }
.yp-rv-arrow:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
.yp-rv-arrow-prev { left: var(--yp-space-md); }
.yp-rv-arrow-next { right: var(--yp-space-md); }
.yp-rv-foot {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
}
.yp-rv-name {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--yp-ink-soft);
    font-size: 0.8125rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.yp-rv-count {
    flex: none;
    color: var(--yp-ink-soft);
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}
.yp-rv-open { flex: none; }
@keyframes yp-rv-in {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .yp-rv { animation: none; }
}

/* Mobile stacked list (mirrors the time log pattern) */
.yp-explist { display: block; }
.yp-exptable { display: none; }
@media (min-width: 641px) {
    .yp-explist { display: none; }
    .yp-exptable { display: block; }
}
.yp-exp {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-bottom: 1px solid var(--yp-border);
    padding: 12px var(--yp-space-sm);
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease);
}
.yp-exp:hover { background: var(--yp-slate-soft); }
.yp-exp:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); border-radius: var(--yp-radius-sm); }
.yp-exp-main { flex: 1 1 auto; min-width: 0; }
.yp-exp-vendor {
    font-weight: 600;
    color: var(--yp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.yp-exp-meta {
    margin-top: 3px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
}
.yp-exp-meta-sep { color: var(--yp-border); }
.yp-exp-end { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.yp-exp-amount {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--yp-ink);
    white-space: nowrap;
}

/* Reconcile toggle — a clickable status pill (admin All view) */
.yp-reconcile {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: 1px solid var(--yp-border);
    background: var(--yp-surface);
    border-radius: var(--yp-radius-full);
    padding: 3px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--yp-ink-soft);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--yp-dur-fast) var(--yp-ease), border-color var(--yp-dur-fast) var(--yp-ease);
}
.yp-reconcile:hover { background: var(--yp-slate-soft); }
.yp-reconcile:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
.yp-reconcile.is-done {
    background: var(--yp-success-soft);
    border-color: rgba(47, 125, 79, 0.28);
    color: var(--yp-success);
}

/* Filter bar (admin All view) — on-brand controls that wrap */
.yp-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--yp-space-sm);
    align-items: flex-end;
    margin-bottom: var(--yp-space-lg);
}
.yp-filters .yp-input,
.yp-filters select.yp-input { width: auto; min-width: 0; }
.yp-filters-spacer { flex: 1 1 auto; }

/* ============================================================
   Add / edit expense — responsive sheet.
   Desktop: a centered .yp-modal. Phone (<641px): a full-height
   bottom sheet that slides up, with a sticky title and a sticky
   action bar so Save/Cancel stay reachable above the keyboard.
   The least-friction shape for logging an expense in the field.
   ============================================================ */
.yp-sheet { width: 480px; }
.yp-sheet-actions {
    display: flex;
    gap: var(--yp-space-sm);
    align-items: center;
    margin-top: var(--yp-space-xl);
}
.yp-sheet-actions-end { margin-left: auto; display: flex; gap: var(--yp-space-sm); }

/* ============================================================
   Clock-in sheet — a compact command palette: pinned title and
   search, a job list that scrolls in place, pinned actions. Same
   shape on desktop (centered) and the mobile bottom sheet, so the
   list never collapses into a clipped scroll strip on short windows.
   ============================================================ */
.yp-modal.yp-clockin-sheet {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
}
.yp-clockin-sheet .yp-modal-title {
    margin: 0;
    padding: var(--yp-space-xl) var(--yp-space-xl) var(--yp-space-md);
    border-bottom: 1px solid var(--yp-border);
}
.yp-clockin-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    gap: var(--yp-space-md);
    padding: var(--yp-space-lg) var(--yp-space-xl);
    overflow: hidden;
}
.yp-clockin-sheet .yp-sheet-actions {
    margin: 0;
    padding: var(--yp-space-md) var(--yp-space-xl);
    border-top: 1px solid var(--yp-border);
}

/* Segmented Personal/Business control — neutral, not the yellow action */
.yp-segment { display: flex; gap: 0; }
.yp-segment-btn {
    flex: 1;
    min-height: 42px;
    border: 1px solid var(--yp-border);
    background: var(--yp-surface);
    color: var(--yp-ink-soft);
    font-weight: 600;
    font-size: 0.9375rem;
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease), color var(--yp-dur-fast) var(--yp-ease);
}
.yp-segment-btn:first-child { border-radius: var(--yp-radius-md) 0 0 var(--yp-radius-md); }
.yp-segment-btn:last-child { border-radius: 0 var(--yp-radius-md) var(--yp-radius-md) 0; border-left: none; }
.yp-segment-btn:hover { background: var(--yp-slate-soft); }
.yp-segment-btn:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); position: relative; z-index: 1; }
.yp-segment-btn.is-active {
    background: var(--yp-slate-soft);
    color: var(--yp-ink-strong);
    border-color: var(--yp-slate);
}
.yp-segment-btn.is-active:last-child { border-left: 1px solid var(--yp-slate); }

/* Amount field with a leading $ */
.yp-amount-field { position: relative; }
.yp-amount-field::before {
    content: "$";
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--yp-ink-soft);
    font-family: var(--yp-font-mono);
    pointer-events: none;
}
.yp-amount-field .yp-input {
    padding-left: 26px;
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
}

/* Existing receipts — chips with a remove control */
.yp-receipts { display: flex; flex-direction: column; gap: 6px; }
.yp-receipt {
    display: flex;
    align-items: center;
    gap: var(--yp-space-sm);
    padding: 7px 10px;
    background: var(--yp-canvas);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
}
.yp-receipt-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--yp-ink);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.yp-receipt-name:hover { text-decoration: underline; }
.yp-receipt-pending { color: var(--yp-ink-soft); font-weight: 400; }
.yp-receipt-drop {
    display: block;
    margin-top: 6px;
    padding: 12px;
    border: 1px dashed var(--yp-slate);
    border-radius: var(--yp-radius-md);
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
    background: var(--yp-surface);
}

@media (max-width: 640px) {
    .yp-modal-backdrop:has(.yp-sheet) { align-items: flex-end; padding: 0; }
    .yp-sheet {
        width: 100%;
        max-width: 100%;
        max-height: 92svh;
        border-radius: var(--yp-radius-lg) var(--yp-radius-lg) 0 0;
        padding: 0;
        display: flex;
        flex-direction: column;
        animation: yp-sheet-up var(--yp-dur-base) var(--yp-ease);
    }
    .yp-sheet .yp-modal-title {
        position: sticky;
        top: 0;
        margin: 0;
        padding: var(--yp-space-lg) var(--yp-space-lg) var(--yp-space-md);
        background: var(--yp-surface);
        border-bottom: 1px solid var(--yp-border);
        z-index: 1;
    }
    .yp-sheet .yp-modal-body {
        overflow-y: auto;
        padding: var(--yp-space-lg);
        flex: 1 1 auto;
    }
    .yp-sheet .yp-sheet-actions {
        position: sticky;
        bottom: 0;
        margin-top: 0;
        padding: var(--yp-space-md) var(--yp-space-lg) calc(var(--yp-space-md) + env(safe-area-inset-bottom));
        background: var(--yp-surface);
        border-top: 1px solid var(--yp-border);
    }
    .yp-sheet .yp-btn { min-height: 46px; }
}
@keyframes yp-sheet-up {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
}

/* ============================================================
   "More" sheet — the mobile tab bar's fifth slot. Holds what the
   four tabs don't carry: the office screens (admin only), theme,
   Settings, and Log out. Rendered by MobileNav through YpModal,
   so these live here rather than in that component's scoped
   sheet — the panel element belongs to YpModal.
   Rows are the account popover's vocabulary at thumb size: 52px
   tall, slate glyph, yellow-soft wash on the page you're on.
   ============================================================ */
.yp-more-sheet { padding: 0; }
.yp-more-sheet .yp-modal-body { gap: var(--yp-space-lg); padding: var(--yp-space-lg); }

/* Title row. A menu sheet has no Save/Cancel to dismiss it, so the close
   control lives here rather than leaving "tap the dark part" as the only
   discoverable way out. */
.yp-more-head {
    display: flex;
    align-items: center;
    gap: var(--yp-space-sm);
    padding: var(--yp-space-md) var(--yp-space-sm) var(--yp-space-md) var(--yp-space-lg);
    border-bottom: 1px solid var(--yp-border);
}
.yp-more-title {
    flex: 1 1 auto;
    margin: 0;
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--yp-ink);
}
.yp-more-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    background: transparent;
    border: none;
    border-radius: var(--yp-radius-md);
    color: var(--yp-ink-soft);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--yp-dur-fast) var(--yp-ease),
                color var(--yp-dur-fast) var(--yp-ease);
}
.yp-more-close .bi { width: auto; margin: 0; font-size: 1rem; color: inherit; }
.yp-more-close:active { background: var(--yp-slate-soft); color: var(--yp-ink); }
.yp-more-close:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
@media (hover: hover) {
    .yp-more-close:hover { background: var(--yp-slate-soft); color: var(--yp-ink); }
}

.yp-more-id {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    min-width: 0;
}
.yp-more-avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--yp-radius-full);
    background: var(--yp-slate-deep);
    color: #fff;
    font-weight: 700;
    font-size: 0.875rem;
    letter-spacing: 0.01em;
}
.yp-more-name {
    min-width: 0;
    font-weight: 600;
    font-size: 1.0625rem;
    color: var(--yp-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Pulled out to the sheet's edge so glyphs line up with the avatar and the
   title above them, while the row's wash still bleeds past the text. */
.yp-more-group {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-inline: calc(var(--yp-space-sm) * -1);
}

.yp-more-item {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    width: 100%;
    min-height: 52px;
    padding: 0 var(--yp-space-sm);
    background: transparent;
    border: none;
    border-radius: var(--yp-radius-md);
    text-align: left;
    text-decoration: none;
    font-family: inherit;
    font-weight: 600;
    font-size: 1rem;
    color: var(--yp-ink);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--yp-dur-fast) var(--yp-ease);
}
.yp-more-item .bi {
    flex: 0 0 auto;
    width: 1.25rem;
    text-align: center;
    font-size: 1.0625rem;
    color: var(--yp-slate);
}
.yp-more-item:active { background: var(--yp-slate-soft); }
.yp-more-item:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
@media (hover: hover) {
    .yp-more-item:hover { background: var(--yp-slate-soft); }
}

/* Where you are, marked with the highlighter's soft wash. */
.yp-more-item.is-current,
.yp-more-item.is-current:active {
    background: var(--yp-yellow-soft);
    color: var(--yp-ink-strong);
}
.yp-more-item.is-current .bi { color: var(--yp-ink-strong); }

.yp-more-item-danger,
.yp-more-item-danger .bi { color: var(--yp-danger); }
.yp-more-item-danger:active { background: var(--yp-danger-soft); }
@media (hover: hover) {
    .yp-more-item-danger:hover { background: var(--yp-danger-soft); }
}

/* Theme sits in the row rhythm, its segmented control sized for a thumb. */
.yp-more-theme {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--yp-space-md);
    min-height: 52px;
    padding: 0 var(--yp-space-sm);
}
.yp-more-theme-label { font-weight: 600; font-size: 1rem; color: var(--yp-ink); }
.yp-more-theme-seg {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    background: var(--yp-slate-soft);
    border-radius: var(--yp-radius-md);
}
.yp-more-theme-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 40px;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: var(--yp-ink-soft);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--yp-dur-fast) var(--yp-ease),
                color var(--yp-dur-fast) var(--yp-ease);
}
.yp-more-theme-btn .bi { width: auto; margin: 0; font-size: 1.0625rem; color: inherit; }
.yp-more-theme-btn:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
.yp-more-theme-btn.is-active {
    background: var(--yp-surface);
    color: var(--yp-ink);
    box-shadow: var(--yp-shadow-hover);
}

@media (max-width: 640px) {
    .yp-more-head {
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--yp-surface);
    }

    /* No pinned action row here, so the last item clears the gesture bar. */
    .yp-more-sheet .yp-modal-body {
        padding-bottom: calc(var(--yp-space-lg) + env(safe-area-inset-bottom));
    }
}

/* ============================================================
   Action menu (YpActionMenu) — the "…" in a detail page's header.
   Holds what the header shouldn't spend a slot on: the rare, and
   the destructive. Two presentations, one DOM: a dropdown at
   641px+, a bottom sheet below it, where a header only has room
   for the two or three actions people actually reach for.
   Rows borrow the "More" sheet's vocabulary above, so the app has
   one idea of what a menu row is.
   ============================================================ */
.yp-actions { position: relative; display: inline-flex; }

/* Squarer than a labelled button so the glyph sits centered, and the
   row buys back the width a spelled-out "Delete" was costing it. */
.yp-actions-trigger { padding: 10px 12px; }
.yp-actions-trigger .bi { font-size: 1rem; line-height: 1; }
.yp-actions-trigger[aria-expanded="true"] { background: var(--yp-slate-soft); }

/* Dismissal surface. Invisible on desktop (it only needs to catch the
   click); below 641px it becomes the sheet's scrim. */
.yp-actions-scrim {
    position: fixed;
    inset: 0;
    z-index: calc(var(--yp-z-dropdown) - 1);
}

.yp-actions-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: var(--yp-z-dropdown);
    min-width: 208px;
    padding: var(--yp-space-xs);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    box-shadow: var(--yp-shadow-overlay);
    animation: yp-actions-in var(--yp-dur-fast) var(--yp-ease);
}
.yp-actions-items { display: flex; flex-direction: column; gap: 2px; }

/* Puts a hairline between the ordinary rows and the ones that undo something,
   so a destructive row is never the next thing under your thumb by accident. */
.yp-more-item-sep {
    margin-top: var(--yp-space-xs);
    border-top: 1px solid var(--yp-border);
    border-radius: 0 0 var(--yp-radius-md) var(--yp-radius-md);
}

/* The title row belongs to the sheet; a dropdown is titled by the
   trigger it hangs from. */
.yp-actions-head { display: none; }

/* Dropdown rows sit at desk density — the 52px sheet row would read as
   a stack of buttons rather than a menu on a wide screen. */
.yp-actions-menu .yp-more-item {
    min-height: 40px;
    padding: 0 var(--yp-space-md);
    font-size: 0.9375rem;
    white-space: nowrap;
}

@keyframes yp-actions-in {
    from { opacity: 0; transform: translateY(-2px); }
    to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 640px) {
    .yp-actions-scrim {
        z-index: var(--yp-z-modal-backdrop);
        background: var(--yp-scrim);
    }
    .yp-actions-menu {
        position: fixed;
        inset: auto 0 0 0;
        z-index: var(--yp-z-modal);
        min-width: 0;
        padding: 0 0 env(safe-area-inset-bottom);
        border-width: 1px 0 0;
        border-radius: var(--yp-radius-lg) var(--yp-radius-lg) 0 0;
        box-shadow: var(--yp-shadow-modal);
        animation: yp-sheet-up var(--yp-dur-base) var(--yp-ease);
    }
    .yp-actions-head { display: flex; }
    .yp-actions-items { padding: var(--yp-space-sm); gap: 2px; }
}

/* The entrance is feedback, not choreography: without motion the surface
   still arrives, it just doesn't travel. */
@media (prefers-reduced-motion: reduce) {
    .yp-actions-menu { animation: none; }
}

/* ============================================================
   Scope line items (new-job wizard). A read-only list of the
   labor & materials to bill: each row is tappable to edit and
   carries a remove control. Entry happens in the shared sheet,
   so the list stays clean instead of a grid of raw inputs that
   stacked badly on phones.
   ============================================================ */
.yp-linelist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--yp-space-sm); }
.yp-lineitem {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    padding: var(--yp-space-sm) var(--yp-space-md);
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
}
.yp-lineitem-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--yp-space-xs) 0;
    text-align: left;
    background: none;
    border: none;
    cursor: pointer;
    color: inherit;
}
.yp-lineitem-main:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); border-radius: var(--yp-radius-sm); }
.yp-lineitem-desc {
    font-weight: 600;
    color: var(--yp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.yp-lineitem-meta { font-size: 0.8125rem; color: var(--yp-ink-soft); font-family: var(--yp-font-mono); }
.yp-lineitem-total { font-weight: 600; color: var(--yp-ink); white-space: nowrap; }
.yp-linelist-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-top: var(--yp-space-md);
    padding-top: var(--yp-space-md);
    border-top: 1px solid var(--yp-border);
    font-weight: 700;
}
.yp-linelist-total .yp-mono { font-size: 1.0625rem; }
/* Live line-total inside the entry sheet */
.yp-lineitem-preview {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: var(--yp-space-md);
    background: var(--yp-canvas);
    border-radius: var(--yp-radius-md);
    font-weight: 600;
    color: var(--yp-ink);
}
.yp-lineitem-preview .yp-mono { font-size: 1.0625rem; }

/* ============================================================
   Settings — admin home for app configuration.
   A single readable column of sections; each section pairs a
   title + one-line description with its controls. Built to take
   more sections without rework.
   ============================================================ */
.yp-settings { max-width: 640px; }
.yp-settings-section + .yp-settings-section { margin-top: var(--yp-space-2xl); }
.yp-settings-head { margin-bottom: var(--yp-space-md); }
.yp-settings-title { font-size: 1.375rem; font-weight: 600; line-height: 1.2; margin: 0; color: var(--yp-ink); }
.yp-settings-desc { margin: 4px 0 0; font-size: 0.9375rem; color: var(--yp-ink-soft); max-width: 60ch; }

.yp-cat-add { display: flex; gap: var(--yp-space-sm); margin-bottom: var(--yp-space-md); }
.yp-cat-add .yp-input { flex: 1 1 auto; }
.yp-cat-list { display: flex; flex-direction: column; }
.yp-cat {
    display: flex;
    align-items: center;
    gap: var(--yp-space-sm);
    padding: 10px var(--yp-space-md);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
}
.yp-cat + .yp-cat { margin-top: 6px; }
.yp-cat-name { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--yp-ink); }
.yp-cat-edit { flex: 1 1 auto; }
.yp-cat-actions { display: flex; gap: 2px; flex: none; }

/* ------------------------------------------------------------
   Settings shell — left area rail + the active panel.
   Once Settings holds more than one area (Team, Expense
   categories, ...) the single column gives way to a sub-nav
   rail. The rail collapses above the panel on narrow screens.
   ------------------------------------------------------------ */
.yp-settings-shell {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    gap: var(--yp-space-2xl);
    align-items: start;
}
.yp-settings-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: sticky;
    top: var(--yp-space-lg);
}
.yp-settings-navlink {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: var(--yp-radius-md);
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--yp-ink-soft);
    text-decoration: none;
    transition: background var(--yp-dur-fast) var(--yp-ease),
                color var(--yp-dur-fast) var(--yp-ease);
}
.yp-settings-navlink .bi { color: var(--yp-slate); font-size: 1rem; }
.yp-settings-navlink:hover { background: var(--yp-slate-soft); color: var(--yp-ink); }
/* Active area: the legal-pad wash marks where you are without a second
   full-yellow highlight competing with the sidebar's active item. */
.yp-settings-navlink.is-active { background: var(--yp-yellow-soft); color: var(--yp-ink-strong); }
.yp-settings-navlink.is-active .bi { color: var(--yp-ink-strong); }
.yp-settings-panel { min-width: 0; }
.yp-settings-narrow { max-width: 560px; }

.yp-settings-panelhead {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--yp-space-md);
    flex-wrap: wrap;
    margin-bottom: var(--yp-space-lg);
}
.yp-settings-panelhead .yp-settings-desc { max-width: 52ch; }

/* ------------------------------------------------------------
   Choice list — a short set of selectable rows over a real
   radiogroup, each pairing a name with a line saying what picking
   it means. For choices where the consequence needs spelling out
   and a bare select would hide it: the theme picker (Settings ›
   Appearance, which also previews each scheme in a swatch), and
   how a job's prices appear to the customer.

   List rows, not a card grid. The 64px row is a glove-friendly tap
   target, and selection reads from the filled row rather than from
   a radio dot alone.
   ------------------------------------------------------------ */
.yp-choice-list {
    display: flex;
    flex-direction: column;
    gap: var(--yp-space-sm);
    border: none;
    margin: 0;
    padding: 0;
    min-width: 0;
}
/* Width is the caller's business: in a form the rows line up with the inputs
   above them, while a settings panel wants them held short. */
.yp-choice-list-narrow { max-width: 420px; }
.yp-choice {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
    padding: var(--yp-space-md) var(--yp-space-lg);
    min-height: 64px;
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    cursor: pointer;
    transition: background var(--yp-dur-fast) var(--yp-ease),
                border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease);
}
.yp-choice:hover { border-color: var(--yp-slate); }
.yp-choice.is-selected {
    background: var(--yp-yellow-soft);
    border-color: var(--yp-yellow-deep);
}
/* The real radio is visually hidden but keeps keyboard + a11y; its focus
   lights the whole row via :focus-within (charcoal/light ring per theme). */
.yp-choice-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
.yp-choice:focus-within {
    outline: none;
    border-color: var(--yp-ink);
    box-shadow: var(--yp-focus-ring);
}
.yp-theme-swatch {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--yp-radius-md);
    border: 1px solid var(--yp-border);
    font-size: 1.15rem;
}
/* Literal previews — fixed colors so each tile shows its own scheme
   regardless of the active theme. */
.yp-theme-swatch-light { background: #F5F5F5; color: #333333; }
.yp-theme-swatch-dark { background: #1A1B1D; color: #E7E6E0; border-color: #3A3D41; }
.yp-theme-swatch-system {
    background: linear-gradient(120deg, #F5F5F5 0 50%, #1A1B1D 50% 100%);
    color: #8C929B;
}
.yp-choice-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    min-width: 0;
}
.yp-choice-name { font-weight: 600; color: var(--yp-ink); }
.yp-choice-desc { font-size: 0.8125rem; color: var(--yp-ink-soft); }
.yp-choice-check {
    flex: 0 0 auto;
    color: var(--yp-ink);
    font-size: 1.2rem;
    opacity: 0;
    transform: scale(0.7);
    transition: opacity var(--yp-dur-fast) var(--yp-ease),
                transform var(--yp-dur-fast) var(--yp-ease);
}
.yp-choice.is-selected .yp-choice-check { opacity: 1; transform: scale(1); }
.yp-theme-status {
    margin-top: var(--yp-space-md);
    min-height: 1.25em;
    font-size: 0.875rem;
    color: var(--yp-ink-soft);
}
@media (prefers-reduced-motion: reduce) {
    .yp-choice-check { transition: none; }
}

/* ------------------------------------------------------------
   Team area (Settings › Team). Desktop is a flat hairline table;
   below 641px it collapses to a stacked card list (anti-ERP).
   ------------------------------------------------------------ */
.yp-team-role { color: var(--yp-ink-soft); font-weight: 600; font-size: 0.8125rem; }
.yp-team-rowactions { display: flex; gap: 4px; justify-content: flex-end; white-space: nowrap; }
.yp-linkbtn:disabled { opacity: 0.5; cursor: not-allowed; }
.yp-linkbtn:disabled:hover { background: none; }
.yp-field-hint { margin: 0; font-size: 0.8125rem; color: var(--yp-ink-soft); }

.yp-team-list { display: flex; flex-direction: column; gap: var(--yp-space-sm); }
.yp-team-table { display: none; }
@media (min-width: 641px) {
    .yp-team-list { display: none; }
    .yp-team-table { display: block; }
}
.yp-team-row {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: var(--yp-space-md);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    background: var(--yp-surface);
}
.yp-team-row-top { display: flex; justify-content: space-between; gap: var(--yp-space-sm); align-items: flex-start; }
.yp-team-name { font-weight: 600; color: var(--yp-ink); }
.yp-team-email { font-size: 0.875rem; color: var(--yp-ink-soft); word-break: break-word; }
.yp-team-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; justify-content: flex-end; }
.yp-team-actions { display: flex; gap: var(--yp-space-sm); }

@media (max-width: 720px) {
    .yp-settings-shell { grid-template-columns: 1fr; gap: var(--yp-space-lg); }
    .yp-settings-nav {
        flex-direction: row;
        position: static;
        overflow-x: auto;
        gap: 4px;
        padding-bottom: var(--yp-space-sm);
        border-bottom: 1px solid var(--yp-border);
    }
    .yp-settings-navlink { white-space: nowrap; }
}

/* ============================================================
   Reports — office-grade output. A launcher directory, then
   two report surfaces (payroll, expenses) that share one
   vocabulary: a filter bar, a KPI stat strip in tabular mono,
   the full .yp-table on desktop, a stacked list on mobile.
   ============================================================ */

/* --- Launcher directory (/reports) --- */
.yp-report-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--yp-space-lg);
    max-width: 760px;
}
.yp-report-card {
    display: flex;
    gap: var(--yp-space-lg);
    align-items: flex-start;
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    padding: var(--yp-space-xl);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--yp-dur-fast) var(--yp-ease),
                box-shadow var(--yp-dur-fast) var(--yp-ease),
                transform var(--yp-dur-fast) var(--yp-ease);
}
.yp-report-card:hover {
    border-color: var(--yp-slate);
    box-shadow: var(--yp-shadow-hover);
    transform: translateY(-1px);
}
.yp-report-card:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); }
.yp-report-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--yp-radius-md);
    background: var(--yp-slate-soft);
    color: var(--yp-slate-deep);
}
.yp-report-body { flex: 1 1 auto; min-width: 0; }
.yp-report-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--yp-space-sm);
    margin: 0;
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--yp-ink);
}
.yp-report-arrow {
    flex: none;
    color: var(--yp-slate);
    transition: transform var(--yp-dur-fast) var(--yp-ease), color var(--yp-dur-fast) var(--yp-ease);
}
.yp-report-card:hover .yp-report-arrow { transform: translateX(3px); color: var(--yp-ink); }
.yp-report-desc {
    margin: 6px 0 0;
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--yp-ink-soft);
}

/* --- KPI stat strip (payroll + expense totals) --- */
.yp-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--yp-space-md);
    margin-bottom: var(--yp-space-xl);
}
.yp-stat {
    background: var(--yp-surface);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    padding: var(--yp-space-lg);
}
.yp-stat-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-stat-value {
    margin-top: 6px;
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 1.5rem;
    line-height: 1.1;
    color: var(--yp-ink);
}
/* Overtime flags amber when present — the one number the office double-checks. */
.yp-stat.is-flag { background: var(--yp-warning-soft); border-color: rgba(180, 83, 9, 0.28); }
.yp-stat.is-flag .yp-stat-label,
.yp-stat.is-flag .yp-stat-value { color: var(--yp-warning); }

/* --- By-category breakdown (expense report) --- */
.yp-breakdown {
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    background: var(--yp-surface);
    padding: var(--yp-space-lg) var(--yp-space-xl);
    margin-bottom: var(--yp-space-xl);
}
.yp-breakdown-title {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
    margin-bottom: var(--yp-space-sm);
}
.yp-breakdown-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 2px var(--yp-space-xl);
}
.yp-breakdown-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--yp-space-sm);
    padding: 6px 0;
    border-bottom: 1px solid var(--yp-border);
}
.yp-breakdown-cat { color: var(--yp-ink); font-size: 0.9375rem; }
.yp-breakdown-amt {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 500;
    color: var(--yp-ink);
    white-space: nowrap;
}

/* --- Payroll mobile stacked list (desktop uses .yp-table) --- */
.yp-rstack { display: block; }
.yp-rtable { display: none; }
@media (min-width: 641px) {
    .yp-rstack { display: none; }
    .yp-rtable { display: block; }
}
.yp-prrow {
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-bottom: 1px solid var(--yp-border);
    padding: 12px var(--yp-space-sm);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: background var(--yp-dur-fast) var(--yp-ease);
}
.yp-prrow:hover { background: var(--yp-slate-soft); }
.yp-prrow:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); border-radius: var(--yp-radius-sm); }
.yp-prrow-top { display: flex; align-items: center; justify-content: space-between; gap: var(--yp-space-sm); }
.yp-prrow-name { font-weight: 600; color: var(--yp-ink); }
.yp-prrow-total {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    color: var(--yp-ink);
    white-space: nowrap;
}
.yp-prrow-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 0.8125rem; color: var(--yp-ink-soft); }
.yp-prrow-meta .yp-mono { color: var(--yp-ink-soft); }
.yp-prrow-meta .is-ot { color: var(--yp-warning); }

/* --- Expandable per-employee entry detail (shared desktop + mobile) --- */
.yp-subwell {
    background: var(--yp-canvas);
    border-radius: var(--yp-radius-md);
    padding: var(--yp-space-sm) var(--yp-space-md);
}
.yp-subtable { width: 100%; border-collapse: collapse; }
.yp-subtable th {
    text-align: left;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
    padding: 6px 8px;
}
.yp-subtable th.yp-num { text-align: right; }
.yp-subtable td { padding: 6px 8px; font-size: 0.8125rem; border-top: 1px solid var(--yp-border); }
.yp-subtable tr:first-child td { border-top: none; }

.yp-report-foot { margin-top: var(--yp-space-md); font-size: 0.8125rem; color: var(--yp-ink-soft); }

/* ============================================================
   Job detail — glance-first hub (/jobs/{id}).
   The title leads; a ledger strip reads the job's money and labor
   at a glance; below it, details / jobsite / line items read as
   plain values and flip to a form on Edit. Phase 2's P&L matrix
   expands the ledger in place.
   ============================================================ */

/* Identity line under the job title: client · #number · created. */
.yp-detail-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px var(--yp-space-sm);
    margin-top: 6px;
    font-size: 0.9375rem;
    color: var(--yp-ink-soft);
}
.yp-detail-sub a { color: var(--yp-ink); font-weight: 600; text-decoration: none; }
.yp-detail-sub a:hover { color: var(--yp-ink-strong); text-decoration: underline; }
.yp-detail-sub .yp-mono { color: var(--yp-ink-soft); font-size: 0.875rem; }
.yp-detail-sub-sep { color: var(--yp-border); }

/* Header action cluster — wraps under the title on narrow screens. */
.yp-detail-actions { display: flex; gap: var(--yp-space-sm); flex-wrap: wrap; }

/* A phone header carries three or four actions plus the "…". At the desktop
   18px inset that overruns 360-390px and wraps the last one onto a line of
   its own; trimming the horizontal padding buys ~36px and keeps the row
   intact. Height and tap target are untouched — this is width, not density. */
@media (max-width: 640px) {
    .yp-detail-actions .yp-btn { padding: 10px 12px; }
}

/* ============================================================
   Edit commit bar (YpEditBar) — the counterpart to the header
   cluster above. While a detail page is in edit mode the header's
   actions empty out and Save/Cancel dock to the bottom of the
   viewport, so they stay reachable through a form that scrolls
   (Job detail runs four sections plus a line-item list).

   This is the mobile sheet's sticky action row promoted to page
   level: surface fill, a 1px top border doing the separating, and
   no shadow — every --yp-shadow-* casts downward, which on a
   bottom-docked bar throws it off-screen rather than onto the
   content it covers.
   ============================================================ */
.yp-editbar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: var(--yp-z-sticky);
    background: var(--yp-surface);
    border-top: 1px solid var(--yp-border);
    animation: yp-sheet-up var(--yp-dur-base) var(--yp-ease);
}

/* Reserves the scroll space the fixed bar covers, so a form's last field
   doesn't sit under it. Rendered by the component at the end of the page. */
.yp-editbar-space { height: var(--yp-editbar-space); }

.yp-editbar-inner {
    display: flex;
    flex-direction: column;
    gap: var(--yp-space-sm);
    padding: var(--yp-space-md) var(--yp-space-lg) calc(var(--yp-space-md) + env(safe-area-inset-bottom));
}

.yp-editbar-row {
    display: flex;
    align-items: center;
    gap: var(--yp-space-md);
}

/* Truncates rather than wrapping: at 360px the label plus both buttons is
   only just wide enough, and the buttons must not drop to a second line. */
.yp-editbar-state {
    min-width: 0;
    margin: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 0.9375rem;
    color: var(--yp-ink-soft);
}

.yp-editbar-actions { display: flex; gap: var(--yp-space-sm); margin-left: auto; }
.yp-editbar .yp-btn { min-height: 44px; }

@media (max-width: 640px) {
    /* .content already reserves the tab bar's 58px + inset, and the tab bar is
       hidden for the session, so most of the bar is cleared before this. What's
       left is the difference once a save error pushes it to ~122px. */
    .yp-editbar-space { height: var(--yp-space-3xl); }
    .yp-editbar .yp-btn { min-height: 46px; }
}

/* Desktop: clear the sidebar rather than run under it, and track its collapse
   on the same curve so the bar's edge doesn't lag behind the rail. Inner
   padding matches article's, lining Save up with the content column. */
@media (min-width: 641px) {
    .yp-editbar {
        left: 250px;
        transition: left var(--yp-dur-base) var(--yp-ease);
    }
    .page.sidebar-collapsed .yp-editbar { left: 68px; }
    .yp-editbar-inner { padding-left: 2rem; padding-right: 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
    .yp-editbar { animation: none; transition: none; }
}

/* Ledger strip — at-a-glance money / labor. Hairline-separated cells in
   one bordered panel (the 1px gap reveals the border-colored backing),
   wrapping to a grid as it narrows. Numbers are tabular mono. */
.yp-jobledger {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
    background: var(--yp-border);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    overflow: hidden;
    margin-bottom: var(--yp-space-2xl);
}
.yp-jobledger-item {
    flex: 1 1 150px;
    background: var(--yp-surface);
    display: flex;
    flex-direction: column;
    gap: var(--yp-space-sm);
    padding: var(--yp-space-lg) var(--yp-space-xl);
}
.yp-jobledger-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-jobledger-value {
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    font-size: 1.375rem;
    line-height: 1;
    color: var(--yp-ink-strong);
}
/* The status cell carries a badge where the others carry a number;
   align it to the baseline the mono values sit on. */
.yp-jobledger-item .yp-badge { align-self: flex-start; }

/* Read-only field display (view mode): label over value. A blank value
   reads as a muted em dash rather than empty space. */
.yp-readgrid { display: grid; gap: var(--yp-space-xl); max-width: 540px; margin: 0; }
.yp-readfield { display: flex; flex-direction: column; gap: 4px; margin: 0; }
.yp-readfield-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--yp-ink-soft);
}
.yp-readfield-value { margin: 0; font-size: 1rem; line-height: 1.5; color: var(--yp-ink); }
.yp-readfield-value.is-empty { color: var(--yp-ink-soft); }
.yp-readfield-value a { color: var(--yp-ink); font-weight: 600; text-decoration: none; }
.yp-readfield-value a:hover { color: var(--yp-ink-strong); text-decoration: underline; }
/* "Call Jane Doe →" / "Email Jane Doe →" beneath a phone or email value: the
   value dials on its own, but nothing said so. Same trailing-arrow affordance
   the jobsite uses for directions; the two-class selector outranks the
   `.yp-readfield-value a` rule above so it stays quiet rather than ink-bold. */
.yp-readfield-value .yp-section-link {
    display: block;
    color: var(--yp-ink-soft);
}
.yp-readfield-value .yp-section-link:hover { color: var(--yp-ink-strong); }

/* Jobsite address block + a directions link out to maps. The address carries no
   bottom margin (Bootstrap's reboot adds 1rem) so the link below reads as part
   of the same group rather than a separate line. */
.yp-address { font-style: normal; line-height: 1.55; color: var(--yp-ink); margin: 0; }
/* Trailing "→" affordance under a detail section's value: Get directions, View client. */
.yp-section-link,
.yp-address-dir {
    display: inline-block;
    margin-top: var(--yp-space-xs);
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--yp-ink-soft);
    text-decoration: none;
}
.yp-section-link:hover,
.yp-address-dir:hover { color: var(--yp-ink-strong); text-decoration: underline; }

/* A plain value standing alone in a detail section (no dt/dd label needed when
   the section title already names it). */
.yp-detail-value { margin: 0; font-size: 1rem; line-height: 1.55; color: var(--yp-ink); }
.yp-detail-value.is-empty { color: var(--yp-ink-soft); }
.yp-detail-notes { white-space: pre-wrap; max-width: 68ch; text-wrap: pretty; }

/* View ⇄ edit crossfade. The global reduced-motion rule below neutralizes it. */
.yp-swap { animation: yp-fade var(--yp-dur-base) var(--yp-ease); }
@keyframes yp-fade { from { opacity: 0; } to { opacity: 1; } }

/* Compact mobile roll-up rows — shared by the read-only line items, time
   entries, and expenses on the job page. Desktop keeps the hairline table; on
   mobile each record is a two-line item (title + figure on top, supporting meta
   beneath) instead of stacking every column as its own labeled row. */
.yp-rollup-mobile { display: block; }
.yp-rollup-desktop { display: none; }
@media (min-width: 641px) {
    .yp-rollup-mobile { display: none; }
    .yp-rollup-desktop { display: block; }
}
.yp-rollup-item {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: var(--yp-space-md) 0;
    border-bottom: 1px solid var(--yp-border);
    color: inherit;
    text-decoration: none;
}
.yp-rollup-item:first-child { padding-top: var(--yp-space-xs); }
.yp-rollup-item:last-of-type { border-bottom: none; }
/* Clickable rows (e.g. a time entry that deep-links to /time). */
a.yp-rollup-item:hover .yp-rollup-title { color: var(--yp-ink-strong); text-decoration: underline; }
a.yp-rollup-item:focus-visible { outline: none; box-shadow: var(--yp-focus-ring); border-radius: var(--yp-radius-sm); }
.yp-rollup-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--yp-space-md);
}
.yp-rollup-title { min-width: 0; font-weight: 600; color: var(--yp-ink); }
.yp-rollup-amt {
    flex: none;
    font-family: var(--yp-font-mono);
    font-feature-settings: 'tnum' 1;
    font-weight: 600;
    color: var(--yp-ink);
    white-space: nowrap;
}
.yp-rollup-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    font-size: 0.8125rem;
    color: var(--yp-ink-soft);
}
.yp-rollup-sub .yp-mono { font-size: inherit; color: inherit; }
.yp-rollup-sub .yp-badge { font-size: 0.6875rem; }
.yp-rollup-dot { color: var(--yp-border); }
.yp-rollup-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--yp-space-md);
    padding-top: var(--yp-space-md);
    border-top: 2px solid var(--yp-border);
    font-weight: 700;
    color: var(--yp-ink);
}
.yp-rollup-total .yp-mono { font-weight: 700; }

/* ============================================================
   Invoice wizard — step indicator + footer
   ============================================================ */
.yp-stepper {
    list-style: none;
    margin: 0 0 var(--yp-space-2xl);
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--yp-space-sm);
    counter-reset: yp-step;
}
.yp-stepper-item {
    display: flex;
    align-items: center;
    gap: var(--yp-space-sm);
    flex: 1 1 0;
    min-width: 140px;
    padding: var(--yp-space-sm) var(--yp-space-md);
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-md);
    background: var(--yp-surface);
    color: var(--yp-ink-soft);
    font-weight: 600;
    font-size: 0.9375rem;
}
.yp-stepper-item.is-active {
    border-color: var(--yp-ink);
    color: var(--yp-ink);
}
.yp-stepper-item.is-done {
    color: var(--yp-ink);
}
.yp-stepper-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex: 0 0 auto;
    border-radius: var(--yp-radius-full);
    background: var(--yp-slate-soft);
    color: var(--yp-ink);
    font-family: var(--yp-font-mono);
    font-size: 0.8125rem;
    font-weight: 600;
}
.yp-stepper-item.is-active .yp-stepper-num {
    background: var(--yp-yellow);
    color: var(--yp-on-accent);
}
.yp-stepper-item.is-done .yp-stepper-num {
    background: var(--yp-success-soft);
    color: var(--yp-success);
}

.yp-wizard-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--yp-space-md);
    margin-top: var(--yp-space-2xl);
    padding-top: var(--yp-space-lg);
    border-top: 1px solid var(--yp-border);
}
.yp-wizard-foot-end {
    display: flex;
    gap: var(--yp-space-sm);
    margin-left: auto;
}

/* Customer-facing review surface — a sheet of "paper" the bill is read from */
.yp-review-sheet {
    border: 1px solid var(--yp-border);
    border-radius: var(--yp-radius-lg);
    background: var(--yp-surface);
    padding: var(--yp-space-xl);
}
.yp-review-to {
    display: flex;
    align-items: baseline;
    gap: var(--yp-space-sm);
    flex-wrap: wrap;
    margin-bottom: var(--yp-space-lg);
    color: var(--yp-ink-soft);
}
.yp-review-to strong { color: var(--yp-ink); }

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
    .yp-btn-primary:hover, .yp-card-link:hover, .yp-report-card:hover { transform: none; }
    .yp-report-card:hover .yp-report-arrow { transform: none; }
}
