/* Winston's management portal -- modern financial/operational theme.
   Global stylesheet, always loaded (see base.html). Page-family modules
   (appointments.css, schedule.css, dev_menu.css) build on these tokens
   and shared primitives; they do not redefine :root. */

:root {
    /* Primary/operational */
    --wb-primary: #1D4ED8;
    --wb-primary-hover: #1E40AF;
    --wb-primary-tint: #EFF6FF;
    --wb-secondary: #475569;
    --wb-secondary-hover: #334155;

    /* Semantic status -- used by meaning, not decoratively */
    --wb-success: #15803D;
    --wb-success-tint: #F0FDF4;
    --wb-warning: #B45309;
    --wb-warning-tint: #FFFBEB;
    --wb-danger: #B91C1C;
    --wb-danger-tint: #FEF2F2;
    --wb-purple: #6D28D9;
    --wb-purple-tint: #F5F3FF;
    --wb-teal: #0F766E;
    --wb-teal-tint: #F0FDFA;
    /* Customers module identity -- a new, dedicated hue (rose/pink),
       confirmed with the user rather than reusing --wb-teal (already
       POS/Sales' identity, see .wb-table--commerce) or --wb-purple
       (Employees). Same "generic hue-named token" pattern as --wb-purple/
       --wb-teal above, not a --wb-module-customers-* pair, since there's
       no navy card-header page for this module (yet) to need the
       separate border/icon shades those pairs exist for. */
    --wb-rose: #BE185D;
    --wb-rose-tint: #FDF2F8;

    /* Navigation shell */
    --wb-nav-bg: #0F172A;
    --wb-nav-bg-active: #1E293B;
    --wb-nav-text: #CBD5E1;
    --wb-nav-text-active: #FFFFFF;
    --wb-nav-accent: var(--wb-primary);

    /* Subtle brand reference only -- sidebar brand lockup, nowhere else.
       Admin-overridable via BusinessSettings.primary_colour/secondary_colour
       (see base.html's inline style on <body>). */
    --wb-brand-accent: #6b1220;
    --wb-brand-accent-secondary: #b8925a;

    /* Card-header dark-navy background -- a gradient, deliberately lighter/
       more blue than --wb-nav-bg (the sidebar's near-black #0F172A) so the
       card header itself reads as "deep navy," not "near black." Its own
       dedicated tokens rather than reusing --wb-nav-bg, so this can keep
       evolving independently of the sidebar. */
    --wb-card-header-bg-start: #1E3A66;
    --wb-card-header-bg-end: #16294A;

    /* Workspace navy -- a softer, single-tone alternative to the
       gradient above, used only where a page keeps full coloured card
       headers as an active-workspace convention rather than the
       lighter section-light treatment now used on detail/reporting
       pages (currently just the POS workspace -- see .wb-pos-cart/
       .wb-pos-workspace/.wb-pos-quick__section .wb-card-header in
       appointments.css). Its own token, not a change to
       --wb-card-header-bg-start/-end, so Dashboard/Appointment Detail/
       Employee Detail/Schedule Management (which still use the
       original navy) are unaffected. */
    --wb-workspace-navy: #233B63;

    /* Card-header module accents -- see .wb-card-header--* below. Fixed,
       NOT admin-overridable (unlike --wb-brand-accent above): a module's
       colour-coding needs to stay stable regardless of what a given shop
       sets as its own brand colour, so these are separate tokens even
       where the default happens to match (employees/finance). Each module
       now has two shades -- the *-border one keeps the originally-named
       colour (used for the thin left border only) and the *-icon one is a
       lighter tint of the same hue, specifically for keeping the icon
       glyph clearly visible against the dark-navy header background;
       dedicated, standalone values throughout (no longer aliased to
       --wb-teal/--wb-secondary), so a future change to those tokens'
       other uses (dashboard stat cards, secondary buttons) never
       accidentally shifts a module's card-header colour too. */
    /* Muted blue for the old navy card-header's thin border/icon only --
       "Royal Blue" Appointments tickets (e.g. the Appointments landing
       page's light-card accent and table header) mean --wb-primary
       instead, same as Employees/purple and Customers/rose resolved
       their own "-border token isn't the colour they actually mean". */
    --wb-module-appointments-border: #5B7FA6;
    --wb-module-appointments-icon: #9CC2EE;
    --wb-module-pos-border: #0F766E;
    --wb-module-pos-icon: #4DD0C4;
    --wb-module-inventory-border: #1E5631;
    --wb-module-inventory-icon: #5FA876;
    --wb-module-employees-border: #6b1220;
    --wb-module-employees-icon: #C15C74;
    --wb-module-finance-border: #b8925a;
    --wb-module-finance-icon: #D9B77E;
    --wb-module-reports-border: #475569;
    --wb-module-reports-icon: #A8B5C4;
    --wb-module-settings-border: #334155;
    --wb-module-settings-icon: #8C9BAF;

    /* BAT-016: standard helper/instructional text colour -- Winston slate
       blue-grey, reused everywhere via .wb-appt-form__help/.wb-help-text
       (see appointments.css). Deliberately darker than the ticket's first
       suggestion (#66788A, ~4.5:1 against white -- right at the WCAG AA
       floor for normal-size text) since this text also renders italic and
       small; #596B7D gives a safer contrast margin. */
    --wb-help-text: #596B7D;

    /* Neutral surfaces */
    --wb-ink: #0F172A;
    --wb-ink-muted: #475569;
    --wb-ink-faint: #94A3B8;
    --wb-surface: #FFFFFF;
    --wb-surface-alt: #F8FAFC;
    --wb-border: #E2E8F0;
    --wb-border-strong: #CBD5E1;

    --wb-sidebar-width: 260px;
    --wb-topbar-height: 60px;
    --wb-radius: 8px;
    --wb-shadow: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06);
    --wb-shadow-raised: 0 4px 12px rgba(15, 23, 42, 0.12);
}

* {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    background-color: var(--wb-surface-alt);
    color: var(--wb-ink);
    font-family: "Inter", "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    -webkit-font-smoothing: antialiased;
}

.wb-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1000;
    background-color: var(--wb-primary);
    color: #fff;
    padding: 0.6rem 1rem;
    border-radius: 0 0 4px 0;
}

.wb-skip-link:focus {
    left: 0;
}

/* ---------- Sidebar ---------- */

.wb-sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--wb-sidebar-width);
    background-color: var(--wb-nav-bg);
    color: var(--wb-nav-text);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    z-index: 40;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
}

.wb-sidebar.wb-nav--open {
    transform: translateX(0);
    box-shadow: var(--wb-shadow-raised);
}

.wb-sidebar__brand {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 1.1rem 1.25rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.wb-sidebar__brand-mark {
    width: 10px;
    height: 28px;
    border-radius: 2px;
    background: linear-gradient(180deg, var(--wb-brand-accent), var(--wb-brand-accent-secondary));
    flex-shrink: 0;
}

.wb-sidebar__brand-name {
    display: block;
    color: #fff;
    font-weight: 700;
    font-size: 1rem;
    line-height: 1.2;
}

.wb-sidebar__brand-tagline {
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--wb-nav-text);
    opacity: 0.7;
}

.wb-sidebar__nav {
    padding: 0.75rem 0.75rem 1.5rem;
}

.wb-sidebar__section {
    margin-bottom: 1.1rem;
}

.wb-sidebar__section-label {
    display: block;
    padding: 0 0.6rem;
    margin-bottom: 0.35rem;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--wb-ink-faint);
}

.wb-sidebar__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.wb-nav__link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.5rem 0.6rem;
    border-radius: 6px;
    color: var(--wb-nav-text);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    border-left: 3px solid transparent;
    transition: background-color 0.12s ease, color 0.12s ease;
}

.wb-nav__link:hover {
    background-color: var(--wb-nav-bg-active);
    color: var(--wb-nav-text-active);
}

.wb-nav__link--active {
    background-color: var(--wb-nav-bg-active);
    color: var(--wb-nav-text-active);
    border-left-color: var(--wb-nav-accent);
    font-weight: 600;
}

.wb-nav__link--soon {
    opacity: 0.55;
    cursor: default;
    pointer-events: none;
}

.wb-nav__link-icon {
    font-size: 1rem;
    width: 1.1rem;
    text-align: center;
    flex-shrink: 0;
}

.wb-nav__link-text {
    flex: 1;
}

.wb-nav__soon-pill {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background-color: rgba(255, 255, 255, 0.12);
    color: var(--wb-nav-text);
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
}

.wb-nav__link:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
}

/* ---------- Topbar & shell ---------- */

.wb-shell__main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

body > .wb-main {
    /* Unauthenticated pages (login) render <main> directly under <body>,
       with no sidebar/topbar -- still needs to fill the flex row. */
    flex: 1;
}

.wb-topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 1rem;
    height: var(--wb-topbar-height);
    padding: 0 1.5rem;
    background-color: var(--wb-surface);
    border-bottom: 1px solid var(--wb-border);
}

.wb-topbar__title {
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--wb-ink);
}

.wb-topbar__spacer {
    flex: 1;
}

.wb-topbar__user {
    font-size: 0.85rem;
    color: var(--wb-ink-muted);
}

.wb-nav__toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    background: none;
    border: 1px solid var(--wb-border-strong);
    border-radius: 6px;
    padding: 0.45rem 0.6rem;
    cursor: pointer;
}

.wb-nav__toggle-bar {
    width: 18px;
    height: 2px;
    background-color: var(--wb-ink);
}

.wb-nav__toggle-label {
    display: none;
}

.wb-nav__toggle:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-nav__logout {
    margin: 0;
}

.wb-nav__logout-btn {
    background: none;
    border: 1px solid var(--wb-border-strong);
    color: var(--wb-ink);
    padding: 0.4rem 0.85rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.82rem;
    font-family: inherit;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.wb-nav__logout-btn:hover {
    background-color: var(--wb-surface-alt);
    border-color: var(--wb-ink-muted);
}

.wb-nav__logout-btn:focus-visible,
.wb-nav__toggle:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-main {
    max-width: 1280px;
    width: 100%;
    margin: 0 auto;
    padding: 1.75rem 2rem 3rem;
}

@media (min-width: 992px) {
    .wb-sidebar {
        transform: none;
        box-shadow: none;
    }

    .wb-shell__main {
        margin-left: var(--wb-sidebar-width);
    }

    .wb-nav__toggle {
        display: none;
    }
}

/* ---------- Messages / alerts ---------- */

.wb-messages {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.wb-messages__item {
    background-color: var(--wb-surface);
    border: 1px solid var(--wb-border);
    border-left: 4px solid var(--wb-primary);
    border-radius: var(--wb-radius);
    padding: 0.65rem 1rem;
    font-size: 0.9rem;
}

.wb-messages__item--success {
    border-left-color: var(--wb-success);
}

.wb-messages__item--warning {
    border-left-color: var(--wb-warning);
}

.wb-messages__item--error {
    border-left-color: var(--wb-danger);
}

/* ---------- Page heading ---------- */

.wb-dashboard__heading {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
    border-bottom: 1px solid var(--wb-border);
    padding-bottom: 0.9rem;
    margin-bottom: 1.5rem;
}

.wb-dashboard__heading h2 {
    margin: 0;
    color: var(--wb-ink);
    font-weight: 700;
}

.wb-dashboard__date {
    color: var(--wb-ink-muted);
    font-size: 0.9rem;
}

/* Manager command-centre header enhancement (BAT-000A "subtle" --
   date stays exactly as-is, just gains a second, quieter line of
   at-a-glance stats computed from data the dashboard already fetches,
   e.g. "10 appointments today · 3 employees working"). Only rendered
   when a role's dashboard builder supplies dashboard_header_stats;
   every other role's plain .wb-dashboard__date is unaffected. */
.wb-dashboard__heading-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.15rem;
}

.wb-dashboard__header-stats {
    font-size: 0.78rem;
    color: var(--wb-ink-faint);
}

/* ---------- Detail-page section jump-nav ---------- */

.wb-detail-jumpnav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    position: sticky;
    top: calc(var(--wb-topbar-height) + 0.75rem);
    z-index: 10;
    background-color: var(--wb-surface-alt);
    padding: 0.5rem 0;
    margin-bottom: 1.25rem;
}

.wb-detail-jumpnav a {
    padding: 0.35rem 0.85rem;
    border-radius: 999px;
    border: 1px solid var(--wb-border-strong);
    background-color: var(--wb-surface);
    color: var(--wb-ink-muted);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.wb-detail-jumpnav a:hover {
    border-color: var(--wb-primary);
    color: var(--wb-primary);
}

.wb-detail-jumpnav a:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

/* Employee Detail's pills use the Employees purple accent instead of
   the sitewide --wb-primary -- scoped to this page's own modifier
   class rather than touching .wb-detail-jumpnav itself, since that
   base rule is shared with Customer/Product/Supplier Detail. */
.wb-detail-jumpnav--employees a:hover,
.wb-detail-jumpnav--employees a:active {
    border-color: var(--wb-purple);
    color: var(--wb-purple);
}

.wb-detail-jumpnav--employees a:focus-visible {
    outline-color: var(--wb-purple);
}

/* Keeps each card's heading clear of the sticky jump-nav bar above it
   after an anchor jump. */
.wb-employee-detail__section {
    scroll-margin-top: 1.5rem;
}

/* Scoped to Employee Detail's own layout wrapper rather than a
   sitewide `html { scroll-behavior: smooth }`, so anchor jumps on
   other pages are unaffected. */
html:has(.wb-employee-detail__layout) {
    scroll-behavior: smooth;
}

/* ---------- Workspace tab strip (e.g. Schedules) ---------- */
/* Distinct from .wb-detail-jumpnav (same-page anchor jump) -- these are
   real links between separate routed pages, so the active tab needs a
   stronger, unambiguous treatment: a filled background rather than just
   a border/color change, matching the sidebar's own active-link style. */

.wb-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    border-bottom: 1px solid var(--wb-border);
    padding-bottom: 0.75rem;
    margin-bottom: 1.5rem;
}

.wb-tabs__link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 1rem;
    border-radius: 999px;
    color: var(--wb-ink-muted);
    font-size: 0.88rem;
    font-weight: 600;
    text-decoration: none;
    transition: background-color 0.12s ease, color 0.12s ease;
}

.wb-tabs__link:hover {
    background-color: var(--wb-primary-tint);
    color: var(--wb-primary);
}

.wb-tabs__link--active {
    background-color: var(--wb-primary);
    color: #fff;
}

.wb-tabs__link--active:hover {
    background-color: var(--wb-primary);
    color: #fff;
}

.wb-tabs__link:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-tabs__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.3rem;
    height: 1.3rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background-color: var(--wb-danger);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
}

.wb-tabs__link--active .wb-tabs__badge {
    background-color: #fff;
    color: var(--wb-danger);
}

/* ---------- KPI stat cards ---------- */

.wb-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin-bottom: 2rem;
}

/* wb-dashboard-kpi is the BAT-017 canonical alias for this component --
   both classes are rendered together (see _dashboard_card.html) and
   share every rule below, same alias precedent as BAT-016's
   .wb-appt-form__help/.wb-help-text. */
.wb-stat-card,
.wb-dashboard-kpi {
    background-color: var(--wb-surface);
    border: 1px solid var(--wb-border);
    border-radius: var(--wb-radius);
    box-shadow: var(--wb-shadow);
    padding: 1.1rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    position: relative;
    cursor: pointer;
    border-top: 3px solid var(--wb-primary);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    /* BAT-017: a floor so every KPI card reads the same height regardless
       of how much (or how little) of its own content it has -- combined
       with .wb-stat-card__sub's own reserved-space rule below, no card's
       height depends on its specific label/value/sub text length. Grid's
       own default row-stretch (see .wb-stats) already equalizes cards
       within the same row; this floor also covers a lone card in its own
       row (e.g. the last row on a narrow layout). */
    min-height: 150px;
}

.wb-stat-card:hover,
.wb-stat-card:focus-within {
    transform: translateY(-2px);
    box-shadow: var(--wb-shadow-raised);
}

.wb-stat-card--success { border-top-color: var(--wb-success); }
.wb-stat-card--warning { border-top-color: var(--wb-warning); }
.wb-stat-card--danger { border-top-color: var(--wb-danger); }
.wb-stat-card--purple { border-top-color: var(--wb-purple); }
.wb-stat-card--teal { border-top-color: var(--wb-teal); }
.wb-stat-card--rose { border-top-color: var(--wb-rose); }
.wb-stat-card--inventory { border-top-color: var(--wb-module-inventory-border); }

.wb-stat-card--alert {
    border-top-color: var(--wb-danger);
}

.wb-stat-card--static {
    cursor: default;
}

.wb-stat-card--static:hover,
.wb-stat-card--static:focus-within {
    transform: none;
    box-shadow: var(--wb-shadow);
}

.wb-stretched-link {
    color: inherit;
    text-decoration: none;
}

.wb-stretched-link::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--wb-radius);
}

.wb-stretched-link:hover .wb-stat-card__value {
    text-decoration: underline;
    text-decoration-color: var(--wb-border-strong);
}

.wb-stretched-link:focus-visible::after {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-stat-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.wb-stat-card__label {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--wb-ink-muted);
}

.wb-stat-card__icon {
    /* BAT-017: bumped from 1.1rem for stronger visual alignment/weight
       against the label. */
    font-size: 1.2rem;
    color: var(--wb-ink-faint);
}

.wb-stat-card__value {
    font-size: 1.85rem;
    font-weight: 700;
    color: var(--wb-ink);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}

.wb-stat-card__sub {
    font-size: 0.8rem;
    color: var(--wb-ink-muted);
    /* BAT-017: reserves space for up to ~2 lines always -- _dashboard_
       card.html now renders this element unconditionally (even empty),
       so every card, whether its own sub text is absent, one line, or
       wraps to two, occupies identical vertical space. */
    min-height: 2.1em;
}

.wb-stat-card__trend {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.78rem;
    font-weight: 600;
    width: fit-content;
}

.wb-stat-card__trend--up {
    color: var(--wb-success);
}

.wb-stat-card__trend--down {
    color: var(--wb-danger);
}

.wb-stat-card__trend--flat {
    color: var(--wb-ink-muted);
}

/* Manager command-centre KPI grouping -- Informational KPIs render via
   the plain .wb-stats section above (unchanged); Action KPIs (Low
   Stock, Outstanding Tips, etc.) get this small label ahead of their
   own .wb-stats row so the two groups read as visually distinct
   without a second heavy heading style. */
.wb-dashboard-kpi-group__label {
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--wb-ink-muted);
}

/* ---------- Dashboard quick actions (BAT-017/BAT-017A) ---------- */
/* A dashboard-specific action row -- adapted from, not shared with,
   .wb-appt-detail__actions (that class keeps its own separate rule and
   its other page -- Appointment Detail -- is unaffected). Reuses the
   sitewide .wb-button--secondary look; only adds icon alignment, scoped
   to this container so .wb-button--secondary's other uses elsewhere are
   untouched.

   BAT-017A: widened the gap (0.75rem -> 1rem) and the top/bottom margin
   (1.5rem -> 2rem) so the row reads as intentionally spaced rather than
   a continuous segmented button group -- flexbox's single `gap` value
   already applies to both axes, so wrapped rows get the same vertical
   spacing between rows as the horizontal spacing between buttons within
   a row, with no separate rule needed for the wrapped case. */
.wb-dashboard-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 2rem 0;
}

.wb-dashboard-actions .wb-button--secondary {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
}

/* "Today's Business" empty-state contextual actions -- same row shape
   as the sitewide dashboard actions above, just tighter margins since
   it sits inside a panel's own padding rather than directly on the
   page. */
.wb-dashboard-actions--compact {
    margin: 0.75rem 0 0;
}

/* ---------- Dashboard empty state (BAT-017) ---------- */
/* Reusable "no data yet" presentation for dashboard panels/tables (see
   _dashboard_empty_state.html) -- an icon + message instead of a plain
   <p class="wb-empty">, used consistently across every dashboard panel
   and the whole-page "no dashboard assigned" state. */
.wb-dashboard-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 2rem 1rem;
    text-align: center;
}

.wb-dashboard-empty__icon {
    font-size: 1.8rem;
    color: var(--wb-ink-faint);
}

.wb-dashboard-empty__text {
    margin: 0;
    font-size: 0.88rem;
    font-style: italic;
    color: var(--wb-ink-muted);
    max-width: 32rem;
}

/* ---------- Panels ---------- */

.wb-panels {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
    gap: 1.25rem;
    /* .wb-appt-detail__actions (often the element directly above this
       grid -- see dashboard.html/inventory_overview.html) only carries
       margin-top, not margin-bottom, so without this the action-button
       row and the first panel row would sit flush against each other.
       Ordinary margin collapsing means this has no effect where
       .wb-panels instead follows .wb-stats (margin-bottom: 2rem)
       directly -- the larger of the two collapsed margins still wins. */
    margin-top: 1.5rem;
}

.wb-panel {
    background-color: var(--wb-surface);
    border: 1px solid var(--wb-border);
    border-radius: var(--wb-radius);
    box-shadow: var(--wb-shadow);
    padding: 1.25rem 1.4rem;
}

.wb-panel__title {
    color: var(--wb-ink);
    font-weight: 700;
    font-size: 1rem;
    margin: 0 0 1rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--wb-border);
}

/* BAT-017: .wb-dashboard-panel is the reusable marker class every
   dashboard table/list partial co-applies alongside .wb-panel
   .wb-panel--headed (see _dashboard_table_*.html) -- gives dashboard-
   only refinements a dedicated hook without touching the shared
   .wb-panel/.wb-panel--headed rules every other headed card in the app
   also uses.
   BAT-017A: min-width: 0 overrides a grid item's default min-width:auto
   -- without it, a wide table inside the panel would force this grid
   track wider than its 1fr share, breaking the 3/2/1-column layout
   below and pushing the whole page into horizontal scroll. The panel's
   own .wb-table-scroll wrapper (inside each _dashboard_table_*.html)
   remains the actual overflow boundary -- this just lets the grid
   track itself shrink to fit alongside its siblings first. */
.wb-dashboard-panel {
    min-width: 0;
}

/* "Today's Business" -- deliberately full-width, above the 2-column
   .wb-dashboard-panels grid below it, since it's the day's single most
   important widget and a 2-column-shared width would cramp its table. */
.wb-dashboard-panel--full {
    margin-top: 1.5rem;
}

/* ---------- Manager command-centre: Employee Activity ---------- */

.wb-employee-activity {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.wb-employee-activity__row {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--wb-border);
}

.wb-employee-activity__row:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.wb-employee-activity__name {
    font-weight: 700;
    font-size: 0.95rem;
}

.wb-employee-activity__meta {
    font-size: 0.85rem;
    color: var(--wb-ink-muted);
}

.wb-employee-activity__next {
    font-size: 0.8rem;
    color: var(--wb-ink-faint);
}

/* ---------- Manager command-centre: Requires Attention ---------- */
/* Icon colour + a thin left accent border communicate each alert's
   module -- no coloured backgrounds/fills, per this ticket's own "cards
   remain white" rule. */

.wb-dashboard-alerts {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.wb-dashboard-alerts__item {
    border-left: 3px solid var(--wb-border-strong);
    border-radius: 4px;
}

.wb-dashboard-alerts__link {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.5rem 0.7rem;
    text-decoration: none;
    color: var(--wb-ink);
    border-radius: 4px;
    transition: background-color 0.12s ease;
}

.wb-dashboard-alerts__link:hover,
.wb-dashboard-alerts__link:focus-visible {
    background-color: var(--wb-surface-alt);
}

.wb-dashboard-alerts__link:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-dashboard-alerts__icon {
    font-size: 1rem;
    margin-top: 0.15rem;
    flex-shrink: 0;
    color: var(--wb-ink-faint);
}

.wb-dashboard-alerts__text {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.wb-dashboard-alerts__label {
    font-weight: 600;
    font-size: 0.88rem;
}

.wb-dashboard-alerts__detail {
    font-size: 0.8rem;
    color: var(--wb-ink-muted);
}

.wb-dashboard-alerts__item--finance { border-left-color: var(--wb-module-finance-border); }
.wb-dashboard-alerts__item--finance .wb-dashboard-alerts__icon { color: var(--wb-module-finance-border); }

.wb-dashboard-alerts__item--inventory { border-left-color: var(--wb-module-inventory-border); }
.wb-dashboard-alerts__item--inventory .wb-dashboard-alerts__icon { color: var(--wb-module-inventory-border); }

.wb-dashboard-alerts__item--appointments { border-left-color: var(--wb-primary); }
.wb-dashboard-alerts__item--appointments .wb-dashboard-alerts__icon { color: var(--wb-primary); }

/* ---------- Manager command-centre: Recent Activity ---------- */
/* Same "icon/left-border colour only, never a filled background" rule
   as the Alerts widget above -- this feed spans six different modules,
   so the colour is what lets a manager recognize an entry's source at
   a glance while scanning down the list. */

.wb-recent-activity {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    max-height: 420px;
    overflow-y: auto;
}

.wb-recent-activity__item {
    border-left: 3px solid var(--wb-border-strong);
    border-radius: 4px;
}

.wb-recent-activity__link {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.45rem 0.65rem;
    text-decoration: none;
    color: var(--wb-ink);
    border-radius: 4px;
    transition: background-color 0.12s ease;
}

.wb-recent-activity__link:hover,
.wb-recent-activity__link:focus-visible {
    background-color: var(--wb-surface-alt);
}

.wb-recent-activity__link:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-recent-activity__icon {
    font-size: 0.95rem;
    margin-top: 0.2rem;
    flex-shrink: 0;
    color: var(--wb-ink-faint);
}

.wb-recent-activity__body {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    font-size: 0.85rem;
}

.wb-recent-activity__time {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--wb-ink-faint);
    font-variant-numeric: tabular-nums;
    flex-basis: 100%;
}

.wb-recent-activity__title {
    font-weight: 600;
}

.wb-recent-activity__detail {
    color: var(--wb-ink-muted);
}

.wb-recent-activity__item--pos { border-left-color: var(--wb-teal); }
.wb-recent-activity__item--pos .wb-recent-activity__icon { color: var(--wb-teal); }

.wb-recent-activity__item--appointments { border-left-color: var(--wb-primary); }
.wb-recent-activity__item--appointments .wb-recent-activity__icon { color: var(--wb-primary); }

.wb-recent-activity__item--inventory { border-left-color: var(--wb-module-inventory-border); }
.wb-recent-activity__item--inventory .wb-recent-activity__icon { color: var(--wb-module-inventory-border); }

.wb-recent-activity__item--finance { border-left-color: var(--wb-module-finance-border); }
.wb-recent-activity__item--finance .wb-recent-activity__icon { color: var(--wb-module-finance-border); }

.wb-recent-activity__item--customers { border-left-color: var(--wb-rose); }
.wb-recent-activity__item--customers .wb-recent-activity__icon { color: var(--wb-rose); }

/* BAT-017A: the dashboard's own panel grid -- deliberately NOT .wb-panels
   (used sitewide by many non-dashboard pages with a simpler auto-fit
   layout); this dashboard-only class gets its own responsive rules so
   other pages are completely unaffected.

   .wb-dashboard (the outer wrapper in dashboard.html) establishes a
   size container so this grid responds to the REAL rendered width of
   the dashboard content area -- which already differs from the raw
   viewport width once the sidebar's margin and .wb-main's max-width are
   factored in -- rather than a viewport-based @media breakpoint that
   would need to duplicate that arithmetic and go stale if either
   changes. Mobile-first: stacks by default, widening to 2 then 3
   columns only once the container genuinely has room, so panels never
   go narrower than comfortable before collapsing to fewer columns. */
.wb-dashboard {
    container-type: inline-size;
    container-name: wb-dashboard;
}

.wb-dashboard-panels {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    margin-top: 2rem;
}

/* BAT-017B revision: capped at 2 columns max (was 3 at >=1100px) -- two
   rounds of compacting the appointment tables (padding, then an uneven
   1.15fr/1.15fr/0.7fr column split) still left some horizontal scroll
   at the 3-column width per in-browser DevTools measurement (panel
   388px vs. table's natural ~441px, even after both fixes). Rather
   than keep chasing an ever-smaller remaining gap, 2 columns gives
   each panel roughly double the previous width, comfortably fitting
   the appointment tables' real content with room to spare. */
@container wb-dashboard (min-width: 700px) {
    .wb-dashboard-panels {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Every dashboard role has exactly 3 panels (see DashboardView's
       five _xxx_dashboard() builders) -- without this, the 3rd would
       sit alone in row 2's first column with an empty gap beside it.
       :last-child:nth-child(odd) matches only when the total count is
       odd, so this stays correct if a role's panel count ever changes
       later, not just for today's fixed count of 3. */
    .wb-dashboard-panels > :last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }
}

/* ---------- Card headers (Winston's card-header design system) ----------
   Opt-in only -- .wb-panel--headed zeroes out .wb-panel's own padding and
   lets .wb-card-header bleed edge-to-edge as a dark-navy band, with
   .wb-panel__body carrying the padding .wb-panel used to provide directly.
   Every other .wb-panel usage (dashboard, list pages, etc.) is completely
   unaffected since it doesn't add this modifier. See
   barbershop/templates/barbershop/_card_header.html for the reusable
   template partial that builds the header markup below, and docs/
   design-system/card-headers.md for the full module colour-map reference. */
.wb-panel--headed {
    padding: 0;
    overflow: hidden;
}

.wb-panel__body {
    padding: 1.25rem 1.4rem;
}

.wb-card-header {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    background: linear-gradient(180deg, var(--wb-card-header-bg-start), var(--wb-card-header-bg-end));
    padding: 1rem 1.35rem;
}

.wb-card-header__icon {
    font-size: 1.35rem;
    line-height: 1;
    color: rgba(255, 255, 255, 0.9);
    flex-shrink: 0;
}

.wb-card-header__title {
    margin: 0;
    color: #fff;
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.2;
}

/* A header nested inside another already-headed card (e.g. Selected
   Appointment inside the Availability aside) -- same colours, smaller
   scale, so two full-size navy bars don't stack directly on top of
   each other. */
.wb-card-header--compact {
    padding: 0.55rem 0.8rem;
}

.wb-card-header--compact .wb-card-header__icon {
    font-size: 0.9rem;
}

.wb-card-header--compact .wb-card-header__title {
    font-size: 0.85rem;
}

/* Module accents -- the *only* accent mechanism used, consistently, for
   every module: a thin left border (the originally-named colour) plus a
   lighter tint of that same hue on the icon, for contrast against the
   dark-navy header. Never a bright/saturated header background -- the
   dark navy above stays dominant everywhere. */
.wb-card-header--appointments { border-left: 3px solid var(--wb-module-appointments-border); }
.wb-card-header--appointments .wb-card-header__icon { color: var(--wb-module-appointments-icon); }

.wb-card-header--pos { border-left: 3px solid var(--wb-module-pos-border); }
.wb-card-header--pos .wb-card-header__icon { color: var(--wb-module-pos-icon); }

.wb-card-header--inventory { border-left: 3px solid var(--wb-module-inventory-border); }
.wb-card-header--inventory .wb-card-header__icon { color: var(--wb-module-inventory-icon); }

.wb-card-header--employees { border-left: 3px solid var(--wb-module-employees-border); }
.wb-card-header--employees .wb-card-header__icon { color: var(--wb-module-employees-icon); }

.wb-card-header--finance { border-left: 3px solid var(--wb-module-finance-border); }
.wb-card-header--finance .wb-card-header__icon { color: var(--wb-module-finance-icon); }

.wb-card-header--reports { border-left: 3px solid var(--wb-module-reports-border); }
.wb-card-header--reports .wb-card-header__icon { color: var(--wb-module-reports-icon); }

.wb-card-header--settings { border-left: 3px solid var(--wb-module-settings-border); }
.wb-card-header--settings .wb-card-header__icon { color: var(--wb-module-settings-icon); }

/* ---------- Lighter section-header variant (see _section_header.html) ----------
   For pages that pair one primary .wb-panel--headed card with several
   more sections underneath -- rather than repeating the full dark-navy
   band on every one of them, "wb-panel--section-light" gives the panel
   a thin top accent line (still the module colour) and this partial
   renders a plain icon+bold-title row as ordinary content inside the
   panel's own normal padding -- no padding-zeroing/.wb-panel__body
   split needed, unlike the full .wb-panel--headed treatment. */
.wb-panel--section-light {
    border-top: 4px solid var(--wb-border);
}

.wb-section-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0 0 1.1rem;
}

.wb-section-header__icon {
    font-size: 1.15rem;
    line-height: 1;
    flex-shrink: 0;
    color: var(--wb-ink-muted);
}

.wb-section-header__title {
    margin: 0;
    color: var(--wb-ink);
    font-size: 1rem;
    font-weight: 700;
}

.wb-panel--section-light:has(.wb-section-header--pos) {
    border-top-color: var(--wb-module-pos-border);
}

.wb-section-header--pos .wb-section-header__icon {
    color: var(--wb-module-pos-border);
}

.wb-panel--section-light:has(.wb-section-header--finance) {
    border-top-color: var(--wb-module-finance-border);
}

.wb-section-header--finance .wb-section-header__icon {
    color: var(--wb-module-finance-border);
}

/* var(--wb-purple), not --wb-module-employees-border (that token is
   actually burgundy) -- confirmed with the user that the Employees
   module should use the genuinely-purple token already established
   elsewhere (Dashboard's Active Employees KPI card, .wb-badge--purple).
   Used by both the Employees landing page and Employee Detail's four
   light section cards; .wb-card-header--employees (the burgundy navy
   variant) is kept as-is for any future navy-styled Employees page. */
.wb-panel--section-light:has(.wb-section-header--employees) {
    border-top-color: var(--wb-purple);
}

.wb-section-header--employees .wb-section-header__icon {
    color: var(--wb-purple);
}

/* Customers landing page -- its own module identity (rose), not shared
   with any other module. See --wb-rose's definition above for why this
   isn't --wb-teal/--wb-purple. */
.wb-panel--section-light:has(.wb-section-header--customers) {
    border-top-color: var(--wb-rose);
}

.wb-section-header--customers .wb-section-header__icon {
    color: var(--wb-rose);
}

/* Appointments module -- "Royal Blue" tickets mean the sitewide
   var(--wb-primary) (#1D4ED8, already every primary button/link/focus
   ring), not the muted --wb-module-appointments-border (#5B7FA6, used
   only by the old navy .wb-card-header--appointments variant) -- same
   "generic hue token, not the -border/-icon pair" resolution as
   Employees' purple and Customers' rose above. First use of
   module="appointments" with this light-card mechanism (Employee
   Detail's Weekly Schedule/Services Offered cards already pass
   module="appointments" but had no matching variant yet, so this also
   gives those their intended blue accent instead of the default grey
   fallback). */
.wb-panel--section-light:has(.wb-section-header--appointments) {
    border-top-color: var(--wb-primary);
}

.wb-section-header--appointments .wb-section-header__icon {
    color: var(--wb-primary);
}

/* Inventory/Purchasing -- reuses the same green already established
   sitewide (Inventory Overview, Products, Purchase Orders' navy
   card-header), not a new colour, so the Manager Dashboard's Inventory-
   themed widgets stay consistent with every other Inventory page. */
.wb-panel--section-light:has(.wb-section-header--inventory) {
    border-top-color: var(--wb-module-inventory-border);
}

.wb-section-header--inventory .wb-section-header__icon {
    color: var(--wb-module-inventory-border);
}

/* Alerts -- semantic danger red, same token every other "this needs
   attention" element in the app already uses (.wb-badge--danger, etc.),
   not a dashboard-only colour. */
.wb-panel--section-light:has(.wb-section-header--alerts) {
    border-top-color: var(--wb-danger);
}

.wb-section-header--alerts .wb-section-header__icon {
    color: var(--wb-danger);
}

/* Shared/system -- neutral, for dashboard widgets that aren't really
   "owned" by any one business module (e.g. a mixed-source activity
   feed). Reuses --wb-secondary (already the sitewide neutral/secondary
   token), not a new navy value. */
.wb-panel--section-light:has(.wb-section-header--system) {
    border-top-color: var(--wb-secondary);
}

.wb-section-header--system .wb-section-header__icon {
    color: var(--wb-secondary);
}

/* Business Settings/Configuration -- muted burgundy. Reuses
   --wb-module-employees-border (#6b1220, genuinely burgundy), not a new
   colour: that token has been unused for its literal colour since
   Employees was confirmed to use the purple token instead (see that
   confirmation note above) -- module="business-settings" is what
   finally spends it on a module that actually wants a burgundy accent.
   --wb-module-settings-border (slate) is a different, still-unused
   token reserved separately and deliberately not reused here. */
.wb-panel--section-light:has(.wb-section-header--business-settings) {
    border-top-color: var(--wb-module-employees-border);
}

.wb-section-header--business-settings .wb-section-header__icon {
    color: var(--wb-module-employees-border);
}

/* ---------- Tables ---------- */

/* BAT-017: .wb-dashboard-table is a reusable marker co-applied on every
   dashboard table (see _dashboard_table_*.html) -- shares every rule
   below with the sitewide .wb-table (this is genuinely shared
   infrastructure, same call as BAT-016's .wb-help-text: a table-spacing/
   typography refinement here is meant to read consistently everywhere
   tables appear, not just on the dashboard). .wb-table--compact (further
   below) still overrides both with its own tighter padding, unaffected
   by this change. */
.wb-table,
.wb-dashboard-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
    /* BAT-017A: already the browser default for a <table> with no other
       layout override anywhere in this stylesheet -- stated explicitly
       so column widths stay content-aware (letting Customer/Barber
       absorb whatever space the compact columns below don't need)
       rather than an equal/fixed split. */
    table-layout: auto;
}

/* BAT-017A: dashboard tables never wrap a cell's content onto a second
   line (times splitting into "9:00"/"AM", dates wrapping, status
   badges going vertically lopsided) -- scoped to .wb-dashboard-table
   only, NOT the sitewide .wb-table, so every other table in the app
   keeps wrapping normally where that's actually wanted (e.g. long
   customer notes in a detail-page table). */
.wb-dashboard-table th,
.wb-dashboard-table td {
    white-space: nowrap;
}

/* BAT-017A: compact columns (Date/Time/Status) shrink to their content
   via width: 1% -- a well-known table trick where, combined with
   table-layout: auto, a tiny percentage width tells the browser "give
   this column only what it needs," leaving the remaining space for the
   unmarked text columns (Customer/Barber) to absorb naturally. Applied
   in _dashboard_table_appointments.html; any other dashboard table
   partial with an analogous compact column can reuse the same classes. */
.wb-dashboard-table__date,
.wb-dashboard-table__time,
.wb-dashboard-table__status {
    width: 1%;
    white-space: nowrap;
}

.wb-table th {
    text-align: left;
    text-transform: uppercase;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--wb-ink-muted);
    /* BAT-017: slightly taller rows/wider cell padding for readability
       (was 0.55rem 0.6rem). */
    padding: 0.7rem 0.85rem;
    border-bottom: 2px solid var(--wb-border-strong);
    background-color: var(--wb-surface-alt);
}

.wb-table td {
    padding: 0.7rem 0.85rem;
    border-bottom: 1px solid var(--wb-border);
    vertical-align: middle;
}

/* BAT-017B: the appointment tables (Today's/Upcoming/Recently Checked
   In -- see _dashboard_table_appointments.html) were still forcing
   horizontal scroll inside their dashboard card at normal desktop
   widths even after BAT-017A's content-aware column sizing, because
   .wb-dashboard-table's own padding (0.7rem 0.85rem, same as the
   sitewide .wb-table) was still generous enough to push 4-5 columns
   past the card's share of the three-column grid. This modifier only
   tightens padding -- font-size, vertical-align, and every other
   .wb-table/.wb-dashboard-table rule are untouched -- and is scoped to
   THIS class alone, so no other dashboard table (Low Stock, Purchase
   Orders, etc.) or any non-dashboard table anywhere else in the app is
   affected. Declared AFTER .wb-table th/.wb-table td above (equal
   specificity -- one class + one element each -- so source order is
   what lets this win the cascade; moving it earlier would silently
   have no effect). */
.wb-dashboard-table--appointments th,
.wb-dashboard-table--appointments td {
    padding: 0.5rem 0.5rem;
}

/* Insurance per BAT-017B section 5 -- only reached if the status
   badge's own horizontal padding is still part of what's causing
   overflow. Reuses .wb-badge as-is (colours/text/logic all untouched);
   scoped to these tables only, never the sitewide .wb-badge rule other
   pages' badges use. */
.wb-dashboard-table--appointments .wb-badge {
    padding: 0.2rem 0.5rem;
}

.wb-table tbody tr:nth-child(even) {
    background-color: var(--wb-surface-alt);
}

.wb-table tbody tr:last-child td {
    border-bottom: none;
}

.wb-table tbody tr:hover {
    background-color: var(--wb-primary-tint);
}

.wb-table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.wb-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Qualified with the `th` element to match/exceed .wb-table th's
   specificity -- that broader rule (text-align: left, for every other
   header) also matches a <th class="wb-num"> and was winning on
   specificity alone, which is what left numeric column headers
   left-aligned above right-aligned data instead of matching it. */
.wb-table th.wb-num {
    text-align: right;
}

.wb-amount {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.wb-amount--positive {
    color: var(--wb-success);
}

.wb-amount--negative {
    color: var(--wb-danger);
}

.wb-amount--muted {
    color: var(--wb-ink-muted);
    font-weight: 400;
}

.wb-amount--total {
    font-weight: 700;
    font-size: 1.05em;
}

.wb-row-link {
    color: var(--wb-primary);
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px dashed transparent;
}

.wb-row-link:hover {
    border-bottom-color: var(--wb-primary);
}

.wb-row-link:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ---------- Badges ---------- */

.wb-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background-color: var(--wb-ink-faint);
    color: #fff;
    /* BAT-016: several Purchase Order pages nest a badge directly inside
       .wb-appt-form__help/.wb-help-text ("Current status: <span
       class="wb-badge ...">") -- that class is now italic, and without
       this the badge's status text would inherit it. Explicit normal
       keeps status pills reading the same regardless of context. */
    font-style: normal;
    /* BAT-017: standardized sizing -- slightly larger/more padded for
       legibility against the also-larger table row height above (was
       0.72rem font / 0.2rem 0.55rem padding). Colours/status-mapping
       unchanged -- .wb-badge--* modifiers below are untouched. */
    font-size: 0.74rem;
    font-weight: 600;
    padding: 0.25rem 0.65rem;
    border-radius: 999px;
    line-height: 1.4;
}

a.wb-badge:hover {
    opacity: 0.85;
}

a.wb-badge:focus-visible {
    outline: 2px solid var(--wb-ink);
    outline-offset: 2px;
}

.wb-badge--success { background-color: var(--wb-success); }
.wb-badge--warning { background-color: var(--wb-warning); }
.wb-badge--danger { background-color: var(--wb-danger); }
.wb-badge--primary { background-color: var(--wb-primary); }
.wb-badge--purple { background-color: var(--wb-purple); }
.wb-badge--teal { background-color: var(--wb-teal); }
.wb-badge--neutral {
    background-color: var(--wb-surface-alt);
    color: var(--wb-ink-muted);
    border: 1px solid var(--wb-border-strong);
}

/* Purchase Order status badges -- one class per PurchaseOrderStatus.code,
   mapped centrally in barbershop.purchasing.purchase_order_status_badge_class
   (Python) / the po_status_badge_class template filter (templates). Reuses
   the same semantic colour tokens as the generic .wb-badge--* modifiers
   above rather than introducing new hex values, so a status badge always
   reads consistently with the rest of the app's colour language. Every
   variant keeps solid-colour-plus-text (never colour alone) for contrast. */
.wb-badge--draft {
    background-color: var(--wb-surface-alt);
    color: var(--wb-ink-muted);
    border: 1px solid var(--wb-border-strong);
}
.wb-badge--submitted { background-color: var(--wb-primary); }
.wb-badge--partially-received { background-color: var(--wb-warning); }
.wb-badge--received { background-color: var(--wb-success); }
.wb-badge--cancelled { background-color: var(--wb-danger); }
/* Fallback for a status code with no specific mapping yet -- treated as
   still "open"/non-terminal rather than an error/unknown state. */
.wb-badge--open { background-color: var(--wb-primary); }

/* Administrative warning banner -- e.g. a missing Business Settings row
   (see barbershop.purchasing._get_configured_tax_rate's 0%-fallback
   warning on the Purchase Order detail page). Amber, not red: a
   safe-fallback notice, not a blocking error. */
.wb-admin-warning {
    background-color: var(--wb-warning-tint);
    border: 1px solid var(--wb-warning);
    border-radius: var(--wb-radius);
    padding: 0.75rem 1rem;
    margin-bottom: 1.25rem;
    color: var(--wb-ink);
}

/* Neutral informational notice (not a warning/error) -- e.g. Refund
   Detail's "this is an amount-only refund, no items linked" message.
   Reuses the existing primary-blue tokens (var(--wb-primary)/
   --wb-primary-tint), not a new colour. */
.wb-info-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    background-color: var(--wb-primary-tint);
    border: 1px solid var(--wb-primary);
    border-radius: var(--wb-radius);
    padding: 0.85rem 1rem;
    color: var(--wb-ink);
}

.wb-info-alert__icon {
    color: var(--wb-primary);
    font-size: 1.1rem;
    line-height: 1.4;
    flex-shrink: 0;
}

.wb-info-alert p {
    margin: 0;
}

/* ---------- Lists ---------- */

.wb-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.wb-list li {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--wb-border);
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
}

.wb-list li:last-child {
    border-bottom: none;
}

.wb-list__meta {
    color: var(--wb-ink-muted);
    font-size: 0.85rem;
}

.wb-empty {
    color: var(--wb-ink-muted);
    font-style: italic;
}

/* ---------- Pagination / search ---------- */

.wb-pagination {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 1.25rem;
}

.wb-pagination__status {
    font-size: 0.85rem;
    color: var(--wb-ink-muted);
}

.wb-search-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1.5rem;
}

.wb-search-bar input[type="text"],
.wb-search-bar input[type="date"],
.wb-search-bar select {
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--wb-border-strong);
    border-radius: 6px;
    font-size: 0.88rem;
    background-color: #fff;
    color: var(--wb-ink);
    font-family: inherit;
}

.wb-search-bar input[type="text"]:focus-visible,
.wb-search-bar input[type="date"]:focus-visible,
.wb-search-bar select:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 1px;
    border-color: var(--wb-primary);
}

.wb-search-bar input[type="text"] {
    flex: 1 1 280px;
    min-width: 220px;
}

/* ---------- Buttons ---------- */

.wb-button {
    margin-top: 1rem;
    padding: 0.6rem 1.1rem;
    background-color: var(--wb-primary);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 0.92rem;
    font-weight: 600;
    font-family: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.wb-button:hover {
    background-color: var(--wb-primary-hover);
    text-decoration: none;
}

.wb-button:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-button:disabled {
    background-color: var(--wb-ink-faint);
    color: var(--wb-surface-alt);
    cursor: not-allowed;
}

/* A genuinely-disabled <button> paired with an inline explanation --
   e.g. Submit on a Purchase Order with no line items yet (see
   purchase_order_detail.html). Not a styled-to-look-disabled <a>; the
   button really can't be activated. */
.wb-appt-detail__disabled-action {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.wb-appt-detail__disabled-action .wb-appt-form__help {
    margin-top: 0;
}

/* ---------- Auth form (login) ---------- */

.wb-auth {
    display: flex;
    justify-content: center;
    padding: 3rem 0;
}

.wb-auth__panel {
    width: 100%;
    max-width: 380px;
    background-color: var(--wb-surface);
    border: 1px solid var(--wb-border);
    border-radius: var(--wb-radius);
    box-shadow: var(--wb-shadow);
    padding: 2rem;
}

.wb-auth__error {
    color: var(--wb-danger);
    font-weight: 600;
    font-size: 0.9rem;
}

.wb-auth__form {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.wb-auth__form label {
    font-size: 0.82rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--wb-ink-muted);
    margin-top: 0.6rem;
}

.wb-auth__form input[type="text"],
.wb-auth__form input[type="password"] {
    padding: 0.55rem 0.65rem;
    border: 1px solid var(--wb-border-strong);
    border-radius: 6px;
    font-size: 1rem;
    background-color: #fff;
    color: var(--wb-ink);
    font-family: inherit;
}

.wb-auth__form input[type="text"]:focus-visible,
.wb-auth__form input[type="password"]:focus-visible {
    outline: 2px solid var(--wb-primary);
    outline-offset: 1px;
    border-color: var(--wb-primary);
}

/* ---------- Point of Sale workspace ----------
   Adaptive cashier layout modelled after a retail POS terminal (Square/
   Lightspeed/Clover-style), not a general CRUD/management page.

   Responsive priority (search always wins the most space): search field
   > search results > product/service selection > Current Sale summary
   > Current Sale detail > secondary (quick) actions.

   The DEFAULT (mobile-first-ish, but really "compact desktop and
   below") layout is a single column in exactly that priority order: a
   slim, sticky, always-visible Current Sale SUMMARY (name, status,
   item count, total -- "minimize customer information to only what is
   necessary during checkout"), an expandable Current Sale DETAIL (full
   item list/customer info/totals/pay button, a native <details>
   disclosure -- collapsed by default so it never competes with the
   search workspace for room), the search workspace itself, and finally
   the lowest-priority quick-actions column, now a full-width row.

   Only at a genuinely wide desktop breakpoint (see the min-width query
   below) does the layout switch to a fixed three-column grid with the
   cart permanently occupying the left column and force-expanded --
   because only there is there enough room for that without shrinking
   the search workspace or introducing horizontal scrolling. Reuses the
   app's existing tokens/badges/buttons throughout -- no new colours or
   typography are introduced here. */

/* The POS page needs more breathing room than the app's normal 1280px
   content cap (see .wb-main) to fit three real working columns on a
   1920x1080 monitor -- scoped to only the page that actually contains
   .wb-pos, so every other management page's width is unaffected. */
.wb-main:has(> .wb-pos) {
    max-width: 1600px;
}

.wb-pos-layout {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

/* Current Sale -- sticky so the compact summary (name/status/item
   count/total) never scrolls out of view while searching, at every
   width. Only the full detail below it collapses/expands. */
.wb-pos-cart {
    position: sticky;
    top: calc(var(--wb-topbar-height) + 0.5rem);
    z-index: 5;
}

.wb-pos-cart__summary {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.wb-pos-cart__summary-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.wb-pos-cart__summary-name {
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--wb-ink);
}

/* JS-free disclosure for everything beyond the always-visible summary,
   built as a "checkbox hack" (a visually hidden, still-focusable
   checkbox + a <label> as the toggle + plain sibling-combinator CSS)
   rather than a native <details>/<summary>. A native <details> was
   tried first, force-opened via a CSS override at the wide-desktop
   breakpoint -- but browsers hide a closed <details>'s non-summary
   children through an internal mechanism that isn't reliably
   overridable by an ordinary author stylesheet rule, and in practice it
   wasn't overridden: the cart appeared to do nothing once an item was
   added (the empty-cart case defaults to open, which is why it looked
   fine until then). The checkbox hack has no such internal mechanism to
   fight -- show/hide is decided entirely by ordinary CSS specificity
   under our own control, so the wide-tier override is guaranteed to
   apply. Collapsed by default (unchecked) so the compact/adaptive
   layout doesn't cost the search workspace any space until the cashier
   actually wants the full cart; force-shown at the wide-desktop
   breakpoint below, where there's room for it to just read as a
   normal, always-open panel. */
.wb-pos-cart-toggle-checkbox {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Styled as an unmistakable button (not just coloured text) -- this is
   the ONLY clickable control for showing/hiding the cart, and needs to
   read as clearly different from the adjacent .wb-badge status pill
   (e.g. "Open," the sale's status, not an action) so the two are never
   confused for each other. */
.wb-pos-cart-detail__toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    cursor: pointer;
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--wb-primary);
    margin-top: 0.5rem;
    padding: 0.5rem;
    border: 1px solid var(--wb-border-strong);
    border-radius: 6px;
    background-color: var(--wb-surface-alt);
}

.wb-pos-cart-detail__toggle:hover {
    background-color: var(--wb-primary-tint);
    border-color: var(--wb-primary);
}

.wb-pos-cart-toggle-checkbox:focus-visible ~ .wb-pos-cart-detail__toggle {
    outline: 2px solid var(--wb-primary);
    outline-offset: 2px;
}

.wb-pos-cart-detail__toggle-label-open {
    display: none;
}

.wb-pos-cart-toggle-checkbox:checked ~ .wb-pos-cart-detail__toggle .wb-pos-cart-detail__toggle-label-closed {
    display: none;
}

.wb-pos-cart-toggle-checkbox:checked ~ .wb-pos-cart-detail__toggle .wb-pos-cart-detail__toggle-label-open {
    display: inline;
}

.wb-pos-cart-detail__toggle-chevron {
    transition: transform 0.15s ease;
}

.wb-pos-cart-toggle-checkbox:checked ~ .wb-pos-cart-detail__toggle .wb-pos-cart-detail__toggle-chevron {
    transform: rotate(180deg);
}

.wb-pos-cart-detail__body {
    display: none;
    margin-top: 0.6rem;
}

.wb-pos-cart-toggle-checkbox:checked ~ .wb-pos-cart-detail__body {
    display: block;
}

.wb-pos-customer {
    margin-bottom: 1rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--wb-border);
}

.wb-pos-customer p {
    margin: 0.2rem 0;
}

.wb-pos-customer__meta {
    font-size: 0.82rem;
    color: var(--wb-ink-muted);
}

.wb-pos-customer__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.5rem;
    flex-wrap: wrap;
}

/* <details>/<summary> -- a native, JS-free disclosure widget for
   "Change Customer": keyboard-accessible (Enter/Space toggles) with no
   custom script needed. */
.wb-pos-customer-change {
    width: 100%;
}

.wb-pos-customer-change summary {
    cursor: pointer;
    list-style: none;
}

.wb-pos-customer-change summary::-webkit-details-marker {
    display: none;
}

.wb-pos-customer-change[open] {
    margin-top: 0.5rem;
}

.wb-pos-customer-search-bar {
    margin: 0.5rem 0;
}

/* "A fixed-height scrollable list for sale items" -- capped regardless
   of breakpoint, so a long cart never pushes the totals/pay button off
   screen or grows the sticky panel past a sane size. */
.wb-pos-cart__items {
    max-height: 320px;
    overflow-y: auto;
    min-height: 2rem;
    margin-bottom: 0.25rem;
}

.wb-pos-cart-item {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--wb-border);
}

.wb-pos-cart-item:last-child {
    border-bottom: none;
}

.wb-pos-cart-item__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.wb-pos-cart-item__desc {
    font-weight: 600;
    color: var(--wb-ink);
}

.wb-pos-cart-item__remove {
    color: var(--wb-danger);
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.1rem 0.3rem;
}

.wb-pos-cart-item__meta {
    font-size: 0.78rem;
    color: var(--wb-ink-muted);
    margin-top: 0.2rem;
}

.wb-pos-totals {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--wb-border);
    flex: 0 0 auto;
}

.wb-pos-totals p {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    margin: 0.25rem 0;
}

/* Visual hierarchy priority #1 (see the Phase spec) -- the total is the
   single most important number on this screen. */
.wb-pos-totals__grand {
    font-size: 1.2rem;
    font-weight: 700;
    padding-top: 0.4rem;
    border-top: 1px dashed var(--wb-border-strong);
}

/* Visual hierarchy priority #2 -- large, full-width, unmissable. Still
   just .wb-button under the hood (same colour/hover/disabled system),
   just bigger. */
.wb-pos-pay-button {
    display: block;
    width: 100%;
    margin-top: 0.85rem;
    padding: 0.85rem 1.1rem;
    font-size: 1.05rem;
    text-align: center;
}

.wb-pos-cart__note {
    margin-top: 0.5rem;
    font-size: 0.75rem;
    color: var(--wb-ink-faint);
    font-style: italic;
    text-align: center;
}

/* Center: search + tabs + results. */
.wb-pos-workspace {
    min-width: 0;
}

.wb-pos-universal-search {
    margin-bottom: 0.75rem;
}

.wb-pos-universal-search input[type="text"] {
    font-size: 1rem;
    padding: 0.65rem 0.85rem;
}

/* Tighter row padding than the standard .wb-table -- "compact,
   optimized for scanning" per the spec, for the two search-result
   tables specifically (every other .wb-table in the app is unaffected). */
.wb-table--compact th,
.wb-table--compact td {
    padding: 0.4rem 0.55rem;
    font-size: 0.85rem;
}

/* BAT-022 follow-up: keep the Stock badge text on one line -- narrower
   compact-table columns were letting "Out of Stock" wrap onto two
   lines. Scoped to compact tables only (the two POS search-result
   tables, their only call site) rather than the shared .wb-badge base
   rule, which every other status pill in the app also uses. */
.wb-table--compact .wb-badge {
    white-space: nowrap;
}

.wb-pos-employee-select {
    max-width: 9rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--wb-border-strong);
    border-radius: 6px;
    font-size: 0.82rem;
    font-family: inherit;
}

/* Quick actions -- lowest responsive priority (#6, "secondary
   actions"). In the default/compact layout this is a full-width row of
   sections that wraps as needed; only the wide-desktop tier below turns
   it into a single narrow right-hand column.

   align-items: stretch is explicit (it's the default, but spelling it
   out documents why it matters here): in the wide-desktop column
   layout, stretch is what makes every card fill the same width as its
   siblings -- cross axis in flex-direction: column is width, so this
   never affects card height. */
.wb-pos-quick {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 1rem;
    min-width: 0;
}

/* BAT-022: padding now comes from .wb-panel__body (this became a
   headed card) -- no own padding here, matching every other headed
   card sitewide; this class has no other call site to keep compatible.

   flex-basis is auto (was a hardcoded 260px), and shrink is 0 (was the
   default 1): a hardcoded 260px basis is fine as a WIDTH in the
   default row-wrap layout below, but the wide-desktop tier switches
   .wb-pos-quick to flex-direction: column, where that same 260px
   became a HEIGHT basis instead -- and since overflow: hidden
   (.wb-panel--headed) removes a flex item's normal "never smaller than
   its own content" floor, a card whose content needed MORE than 260px
   (e.g. Sale Actions' four stacked buttons) rendered at exactly 260px
   and clipped the rest, regardless of the shrink setting. auto sizes
   each card to its own content on whichever axis is the main one for
   the current direction, in both modes -- min-width below replaces
   the old 260px's other job (a sane width floor before row-wrap
   sections get too narrow). */
.wb-pos-quick__section {
    flex: 1 0 auto;
    min-width: 260px;
}

.wb-pos-quick-services {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.wb-pos-sale-actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    align-items: stretch;
}

.wb-pos-sale-actions form {
    width: 100%;
}

.wb-pos-sale-actions button {
    width: 100%;
}

/* Wide desktop ONLY: enough real width (aligned with .wb-main's own
   1600px cap for this page) that a permanent three-column grid doesn't
   meaningfully reduce the search workspace, so this is the one tier
   where the Current Sale panel is allowed to occupy a fixed column
   instead of collapsing. Below this, the flex column layout declared
   above (search-first, cart summary + collapsible detail, quick
   actions last) is what applies -- "preserve the full search area as
   long as possible" as width decreases from here. */
@media (min-width: 1600px) {
    .wb-pos-layout {
        display: grid;
        grid-template-columns: 300px minmax(0, 1fr) 280px;
        gap: 1.25rem;
        align-items: start;
    }

    .wb-pos-cart {
        top: calc(var(--wb-topbar-height) + 1rem);
        max-height: calc(100vh - var(--wb-topbar-height) - 2rem);
        overflow-y: auto;
        display: flex;
        flex-direction: column;
    }

    /* Force the detail shown regardless of the checkbox's checked
       state -- at this width there's room for it to just read as a
       normal, always-visible panel (no click-to-expand needed), which
       is why the toggle (and its now-pointless checkbox) are hidden
       too. Ordinary author-rule specificity/source-order, so this
       reliably wins over the unchecked-by-default base rule above --
       no browser-internal visibility mechanism to fight, unlike the
       native <details> approach this replaced. */
    .wb-pos-cart-toggle-checkbox,
    .wb-pos-cart-detail__toggle {
        display: none;
    }

    .wb-pos-cart-detail__body {
        display: block;
    }

    .wb-pos-quick {
        flex-direction: column;
        flex-wrap: nowrap;
    }
}

@media (max-width: 560px) {
    .wb-pos-quick {
        flex-direction: column;
    }
}

/* ---------- Sale Detail (Phase 7) ---------- */

/* The actions row (Download PDF/Print Receipt/View Customer/Issue
   Refund) has no bottom margin of its own -- on every other page that
   uses .wb-appt-detail__actions it's the last thing on the page, so
   nothing was needed. Here it's immediately followed by the Sale
   Summary card, which likewise has no top margin (its own spacing
   convention puts the gap on whatever precedes it), so without this
   the two sit flush against each other. Scoped to this exact adjacency
   (not a blanket change to .wb-appt-detail__actions, which would
   affect every other detail page using it) via the sibling combinator. */
.wb-appt-detail__actions + .wb-sale-summary {
    margin-top: 1.5rem;
}

/* Same adjacency fix as above, for Refund Detail's action row (Download
   Refund PDF) immediately followed by the Refund Summary card -- also
   sits flush without this, same reasoning: neither element carries its
   own margin for this specific pairing. */
.wb-appt-detail__actions + .wb-refund-summary {
    margin-top: 1.5rem;
}

/* Six panels (Items/Financial Summary/Payments/Refund History/
   Timeline/Inventory) -- single full-width column, not a multi-column
   grid: real sale records can have many items/payments/refunds, and a
   narrower grid track was squeezing wide tables (Payments has 10
   columns) into horizontal scrolling. A full-width column lets each
   table lay out naturally instead, prioritizing readability over
   minimizing page height -- the page is meant to read top-to-bottom
   like a financial report, not as side-by-side widgets. Still a grid
   (not a plain block stack) so .wb-panels' own `gap` keeps spacing
   between cards consistent with the rest of the app; minmax(0, 1fr)
   keeps a wide table from forcing the column past the available
   content width regardless. Scoped to this page only -- every other
   .wb-panels usage keeps its normal auto-fit sizing. */
.wb-sale-panels {
    grid-template-columns: minmax(0, 1fr);
}

/* Full-width cards give the Items/Payments/Refund History tables real
   room now, so their columns get a bit more breathing space than the
   sitewide .wb-table default -- horizontal only (row height/font-size
   untouched), scoped to this page's tables so no other .wb-table
   usage elsewhere is affected. */
.wb-sale-panels .wb-table th,
.wb-sale-panels .wb-table td {
    padding-left: 1rem;
    padding-right: 1rem;
}

/* Financial Summary is now a full-width card -- a bit more side
   padding than the shared .wb-panel default keeps its numbers from
   reading too close to the (now much wider) card edges. */
.wb-sale-financial-summary {
    padding-left: 2rem;
    padding-right: 2rem;
}

/* Sale Summary's two-column fields (replaces the old 6-card KPI row +
   "Sale Details" panel) -- left (descriptive: Customer/Sold By/
   Appointment/Sale Date) + right (Status/Payment Status/Services/
   Products/Total Items/Total Amount) columns, side by side on desktop/
   tablet-wide, one column on narrower screens. Same tight label/value
   grouping technique as .wb-refund-summary-fields/.wb-payment-detail-
   fields (label sized to its own widest text via max-content, value
   directly after) -- a fresh page-scoped name here, not a reuse of
   either of those, since this is yet another page/context. */
.wb-sale-summary-fields {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.5rem 2.5rem;
    margin: 1rem 0 0;
}

@media (max-width: 768px) {
    .wb-sale-summary-fields {
        grid-template-columns: 1fr;
        gap: 1.25rem;
    }
}

.wb-sale-summary-fields__group {
    margin: 0;
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.55rem 0.85rem;
    min-width: 0;
}

.wb-sale-summary-fields__row {
    display: contents;
}

.wb-sale-summary-fields__row dt {
    margin: 0;
    align-self: baseline;
    font-weight: 600;
    color: var(--wb-ink-muted);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.wb-sale-summary-fields__row dd {
    margin: 0;
    align-self: baseline;
    min-width: 0;
}

/* Bold + slightly larger than a normal field value, but well short of
   the KPI-card 1.85rem scale -- "give emphasis without oversized KPI
   typography," per the ticket. */
.wb-sale-summary-fields__amount {
    font-weight: 700;
    font-size: 1.05em;
}

/* Financial Summary panel -- deliberately just .wb-pos-totals (the
   same subtotal/tax/total block already used on the POS workspace and
   here previously inside the Items panel), now its own panel so it's
   visually separate from the line-item table per the Phase 7 spec.
   in a wb-panels grid it can end up narrower than its content wants;
   this keeps the numbers from starting flush against the panel edge. */
.wb-sale-financial-summary .wb-pos-totals {
    margin-top: 0;
}

/* Greater emphasis on "Total (excl. tips)"/"Total Collected" -- purely
   typographic (larger size, more room above/below, right-aligned
   amount already handled by .wb-pos-totals p's own flex layout), never
   a coloured background. Scoped to this one panel's totals, not the
   shared .wb-pos-totals__grand rule the POS workspace also uses. */
.wb-sale-financial-summary .wb-pos-totals__grand {
    margin-top: 0.5rem;
    padding-top: 0.75rem;
    font-size: 1.15rem;
}

.wb-sale-financial-summary .wb-pos-totals__grand .wb-amount {
    font-size: 1.2em;
    letter-spacing: 0.01em;
}

/* Three consistently aligned columns (Event/Date-Time/Description) --
   was display:flex with no fixed widths, so the date/time span's
   horizontal position drifted per row depending on how long that row's
   Event label happened to be, reading as inconsistently/"centered"
   rather than a real column. Grid gives every row the same column
   boundaries regardless of content length. Row padding/border-bottom
   separators are inherited as-is from the shared .wb-list li rule --
   only the layout axis changes here. */
.wb-sale-timeline li {
    display: grid;
    grid-template-columns: 220px 220px 1fr;
    align-items: baseline;
    gap: 0.3rem 1rem;
}

.wb-sale-timeline__label {
    font-weight: 600;
    color: var(--wb-ink);
}

/* Left-align the date/time column -- .wb-list__meta is shared sitewide
   (phone numbers, appointment times, etc.), so scoped to this list only. */
.wb-sale-timeline li .wb-list__meta {
    text-align: left;
}

.wb-sale-timeline__detail {
    color: var(--wb-ink-muted);
    font-size: 0.88rem;
}

@media (max-width: 640px) {
    .wb-sale-timeline li {
        grid-template-columns: 1fr;
        gap: 0.15rem;
    }
}

/* A <button> that should read as an inline text link (e.g. "Select" /
   "Link" / "Unlink" next to a search result) rather than a boxed
   button -- reset the native button chrome, keep .wb-row-link's colour/
   weight/underline-on-hover behaviour. */
.wb-link-button {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    cursor: pointer;
}

/* A <form> that should sit inline with surrounding text/buttons rather
   than starting on its own line -- forms are block-level by default. */
.wb-inline-form {
    display: inline;
}

/* Same idea as .wb-inline-form, but for a form with more than one
   control (e.g. a quantity input plus its submit button) that still
   needs to read as one compact row, not stack vertically -- nowrap so
   the input and button always stay horizontally aligned, even in a
   compact table column; the containing .wb-table-scroll already
   handles any resulting overflow with its own contained scrollbar
   rather than wrapping onto a second line. */
.wb-inline-flex-form {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: nowrap;
}

/* .wb-button's own base rule always carries margin-top: 1rem (for the
   common case of a submit button sitting below a field) -- inside this
   flex row that extra margin pushed the button visibly lower than the
   input it's supposed to sit level with, even once wrapping was fixed
   above. Scoped to buttons inside this one form class only, not a
   change to .wb-button generally. */
.wb-inline-flex-form .wb-button {
    margin-top: 0;
}

/* A short numeric input (cart/product quantity) -- doesn't need a
   product form's full input width. */
.wb-input--qty {
    width: 4.5rem;
}

@media (max-width: 768px) {
    .wb-main {
        padding: 1.25rem 1rem 2rem;
    }

    .wb-topbar {
        padding: 0 1rem;
    }

    .wb-panels {
        grid-template-columns: 1fr;
    }
}
