:root {
    --bg: #f4f3f1;
    --surface: #ffffff;
    --surface-sunken: #faf9f6;
    --border: #e5e3dd;
    --text: #1c1a17;
    --muted: #6b6660;
    --primary: #c2660a;
    --primary-dark: #9c5108;
    --primary-soft: #f7e6d3;
    --sidebar: #1c1917;
    --sidebar-text: #d6d3ce;
    --sidebar-muted: #8a847a;
    --sidebar-active: #29241f;
    --success: #16a34a;
    --success-soft: #dcfce7;
    --danger: #dc2626;
    --danger-dark: #b91c1c;
    --danger-soft: #fee2e2;
    --warning: #d97706;
    --warning-soft: #fef3c7;

    --radius: 12px;
    --shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family:
        Inter,
        -apple-system,
        BlinkMacSystemFont,
        "Segoe UI",
        sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.45;
}

a {
    color: inherit;
    text-decoration: none;
}

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

svg {
    flex-shrink: 0;
}

/* Keyboard focus is always visible — never remove this without replacing it */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Numbers that should line up in a column: prices, quantities, counts */
.num {
    font-variant-numeric: tabular-nums;
}

/* Application shell */

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

/* Sidebar */

.sidebar {
    width: 264px;
    min-height: 100vh;
    background: var(--sidebar);
    color: var(--sidebar-text);
    padding: 22px 16px;
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    scrollbar-width: none;
    transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    z-index: 100;
}

.sidebar::-webkit-scrollbar {
    display: none;
}

.sidebar nav {
    flex: 1;
}

.sidebar-footer {
    padding: 13px 13px 0;
    font-size: 12px;
    color: #6b665e;
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.15s ease;
}

.brand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 13px;
    height: 44px;
    min-height: 44px;
    max-height: 44px;
    margin-bottom: 20px;
    box-sizing: border-box;
}

.brand-logo,
.brand-logo-fallback {
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    object-fit: contain;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.brand-logo-fallback {
    background: var(--sidebar-active);
    color: var(--primary);
}

/* min-width:0 lets the name actually wrap/ellipsize inside a flex
   row instead of forcing the row wider than the sidebar. */
.brand-text {
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.15s ease;
}

.brand-name {
    color: #ffffff;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.3px;
    line-height: 1.2;
}

.brand-subtitle {
    margin-top: 2px;
    font-size: 12px;
    line-height: 1.2;
    color: #a39d94;
}

.nav-section {
    margin-top: 14px;
}

.nav-section:first-child,
.nav-section:first-of-type {
    margin-top: 0;
}

.nav-label {
    display: flex;
    align-items: center;
    padding: 0 13px;
    height: 22px;
    min-height: 22px;
    max-height: 22px;
    margin-bottom: 4px;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--sidebar-muted);
    box-sizing: border-box;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.15s ease;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 0 13px;
    height: 40px;
    min-height: 40px;
    max-height: 40px;
    margin-bottom: 2px;
    border-radius: 9px;
    font-size: 15px;
    font-weight: 500;
    color: var(--sidebar-text);
    border-left: 3px solid transparent;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
    box-sizing: border-box;
    white-space: nowrap;
}

.nav-item .nav-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    min-width: 20px;
    min-height: 20px;
    flex-shrink: 0;
    color: var(--sidebar-muted);
    transition: color 0.15s ease;
}

.nav-icon svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.nav-item span:last-child {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav-item:hover {
    background: var(--sidebar-active);
    color: #ffffff;
}

.nav-item:hover .nav-icon {
    color: var(--sidebar-text);
}

.nav-item.active {
    background: var(--sidebar-active);
    color: #ffffff;
    border-left-color: var(--primary);
}

.nav-item.active .nav-icon {
    color: var(--primary);
}

/* Finance's expandable sub-nav — the only nested nav group in the
   sidebar. The parent row reuses .nav-item itself (it's a real link to
   /finance.php) alongside a separate toggle button, so it needs no
   visual styling of its own beyond the flex layout that pairs the two
   and the indentation for children. */
.nav-group-header {
    display: flex;
    align-items: center;
    height: 40px;
    min-height: 40px;
    max-height: 40px;
    margin-bottom: 2px;
    box-sizing: border-box;
}

.nav-group-header .nav-item {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
}

.nav-group-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 31px;
    height: 31px;
    margin-left: 2px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--sidebar-muted);
    cursor: pointer;
}

.nav-group-toggle:hover {
    background: var(--sidebar-active);
    color: #ffffff;
}

.nav-group-toggle svg {
    transition: transform 0.15s ease;
}

.nav-group-toggle.expanded svg {
    transform: rotate(90deg);
}

.nav-subitems {
    display: flex;
    flex-direction: column;
    padding-left: 22px;
}

.nav-subitems[hidden] {
    display: none;
}

.nav-subitems .nav-item {
    font-size: 14.5px;
}

/* Manual collapse toggle — hidden on mobile (the drawer doesn't need
   it); shown from 768px up by the media query below. */
.sidebar-collapse-toggle {
    display: none;
    align-items: center;
    gap: 13px;
    width: 100%;
    margin: 0 0 14px;
    padding: 0 13px;
    height: 40px;
    min-height: 40px;
    max-height: 40px;
    border: 1px solid var(--sidebar-active);
    border-left: 3px solid transparent;
    border-radius: 9px;
    background: transparent;
    color: var(--sidebar-muted);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    box-sizing: border-box;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.sidebar-collapse-toggle .nav-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    min-width: 20px;
    min-height: 20px;
    flex-shrink: 0;
    transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.sidebar-collapse-toggle:hover {
    background: var(--sidebar-active);
    color: #ffffff;
}

/* Mobile-only off-canvas drawer scrim — hidden everywhere else; shown
   by the mobile media query below. */
.nav-backdrop {
    display: none;
}

/* Main area */

.main {
    margin-left: 264px;
    width: calc(100% - 264px);
    min-height: 100vh;
    /* .app is a flex container — without this, wide content (a table,
       a grid track) can force .main itself wider than the viewport,
       causing the whole page to scroll horizontally instead of just
       the overflowing element. */
    min-width: 0;
    transition: margin-left 0.2s cubic-bezier(0.4, 0, 0.2, 1), width 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    /* A faint dot grid under the cards — reads as a workspace canvas
       (Apple/Figma-style) rather than a flat fill. Wide spacing and a
       low-opacity dot (rgba of --muted at low alpha — a CSS variable
       can't carry its own opacity) so it stays in the background at a
       glance; only shows through in the gaps between cards/tables. */
    background-image: radial-gradient(rgba(107, 102, 96, 0.16) 1.8px, transparent 1.8px);
    background-size: 28px 28px;
    background-position: -6px -6px;
    background-color: var(--bg);
    /* Anchored to the viewport rather than scrolling with the page's
       content, like the sidebar (position: fixed) already is — the
       canvas stays put underneath whatever scrolls over it. */
    background-attachment: fixed;
}

/* Top bar */

.topbar {
    height: 58px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 32px;
    gap: 12px;
    position: sticky;
    top: 0;
    z-index: 50;
}

/* Mobile-only nav toggle — hidden everywhere else; shown by the
   mobile media query below. */
.hamburger-button {
    display: none;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    flex-shrink: 0;
}

.hamburger-button:hover {
    background: var(--surface-sunken);
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    flex: 1;
}

.user-menu {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-shrink: 0;
}

.topbar-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    height: 38px;
    width: 100%;
    max-width: 280px;
    padding: 0 12px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface-sunken);
}

.topbar-search-icon {
    display: flex;
    align-items: center;
    color: var(--muted);
    flex-shrink: 0;
}

.topbar-search-input {
    flex: 1;
    min-width: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    height: 28px;
    color: var(--text);
    font-size: 14px;
    outline: none;
}

.topbar-search-input::placeholder {
    color: var(--muted);
}

.topbar-search-results {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    min-width: 320px;
    max-width: 360px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 14px 30px rgba(0, 0, 0, 0.16);
    padding: 6px;
    z-index: 60;
    max-height: 360px;
    overflow-y: auto;
}

.topbar-search-results[hidden] {
    display: none;
}

/* A white disc with a soft ring, rather than a flat grey circle —
   same "ring around a portrait" language as most modern account
   avatars. The little figure inside isn't an icon at all: ::before is
   a head, ::after is the shoulders, each its own radial-gradient
   sphere (bright highlight, gold midtone, brown-shadow edge) so they
   read as lit, glossy 3D shapes — like the rounded-plastic avatar
   glyphs this was modeled on — rather than a flat silhouette. overflow:
   hidden clips the shoulders piece to the circle for free, so it
   doesn't need its own precise curve. */
.avatar {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: #ffffff;
    position: relative;
    overflow: hidden;
    border: none;
    cursor: pointer;
    box-shadow:
        0 0 0 3px var(--primary-soft),
        0 3px 8px rgba(156, 81, 8, 0.2);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.avatar::before,
.avatar::after {
    content: '';
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    background: radial-gradient(circle at 33% 28%, #fef08a, #eab308 55%, #a16207 100%);
    box-shadow: inset 0 -2px 3px rgba(161, 98, 7, 0.45);
}

.avatar::before {
    top: 6px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
}

.avatar::after {
    bottom: -4px;
    width: 30px;
    height: 20px;
    border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}

.avatar:hover {
    box-shadow:
        0 0 0 3px var(--primary-soft),
        0 5px 14px rgba(156, 81, 8, 0.3);
    transform: translateY(-1px);
}

/* Account dropdown (topbar avatar) — anchored floating menu, distinct
   from the centered .modal-backdrop dialogs and the sidebar's
   persistent nav-group expand; see public/js/user-menu.js. */
.user-menu-trigger {
    position: relative;
}

.user-menu-dropdown {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    min-width: 190px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 14px 30px rgba(0, 0, 0, 0.16);
    padding: 6px;
    z-index: 60;
}

.user-menu-dropdown[hidden] {
    display: none;
}

.user-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: 7px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--text);
    transition: background 0.15s ease;
}

.user-menu-item:hover {
    background: var(--surface-sunken);
}

.user-menu-item-danger {
    color: var(--muted);
}

.user-menu-divider {
    height: 1px;
    background: var(--border);
    margin: 6px 4px;
}

/* Page */

.page {
    padding: 32px;
}

.page-header {
    margin-bottom: 28px;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

/* For a header that's just a title + action, with no description line
   underneath — vertically centers the two against each other instead of
   flex-start (which only mattered for lining a button up against a
   taller title+description block) and reclaims some of the margin that
   description line's own line-height used to need. */
.page-header-tight {
    margin-bottom: 10px;
    align-items: center;
}

.page-title {
    margin: 0;
    font-size: 28px;
    letter-spacing: -0.7px;
}

.page-description {
    margin: 7px 0 0;
    color: var(--muted);
    font-size: 14px;
}

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 42px;
    padding: 0 18px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--primary);
    color: #ffffff;
    font-weight: 600;
    font-size: 14px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.button svg {
    width: 16px;
    height: 16px;
}

.button:hover {
    background: var(--primary-dark);
}

.button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.button.secondary {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border);
}

.button.secondary:hover {
    background: var(--surface-sunken);
    border-color: #d6d3cb;
}

/* Destructive actions get their own unmistakable style, never the same
   look as a neutral "view" or "secondary" action. */
.button.danger {
    background: var(--surface);
    color: var(--danger-dark);
    border-color: #f3c7c7;
}

.button.danger:hover {
    background: var(--danger-soft);
    border-color: var(--danger);
}

/* Compact variant for inline row actions inside tables/lists */
.button.sm {
    height: 32px;
    padding: 0 12px;
    font-size: 12.5px;
    gap: 6px;
    border-radius: 7px;
}

.button.sm svg {
    width: 14px;
    height: 14px;
}

/* Cards */

.stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 18px;
}

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.stat-card {
    padding: 20px;
}

.stat-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.stat-icon {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: var(--primary-soft);
    color: var(--primary-dark);
    flex-shrink: 0;
}

.stat-icon.success { background: var(--success-soft); color: #166534; }
.stat-icon.warning { background: var(--warning-soft); color: #92400e; }

.stat-label {
    color: var(--muted);
    font-size: 13px;
}

.stat-value {
    margin-top: 8px;
    font-size: 25px;
    font-weight: 700;
    letter-spacing: -0.5px;
    font-variant-numeric: tabular-nums;
}

.stat-meta {
    margin-top: 8px;
    font-size: 12px;
    color: var(--muted);
}

.stat-meta.warning {
    color: var(--warning);
    font-weight: 600;
}

/* Content grid */

.content-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 20px;
    margin-top: 20px;
}

/* Paging through Recent job cards jumps back to #recent-job-cards
   (see render_numbered_pagination()'s $fragment) instead of the page
   top — scroll-margin-top keeps that landing spot clear of the sticky
   topbar (58px) instead of tucking the card header behind it. */
#recent-job-cards {
    scroll-margin-top: 74px;
}

/* Settings: each card sizes to its own content instead of stretching
   to match its row's tallest neighbor, and wraps to fewer columns —
   down to one on narrow screens — instead of a fixed column count. */
.settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 20px;
    align-items: start;
    margin-top: 20px;
}

/* The Organization/Tax/Service sub-sections inside the single combined
   "Workshop settings" card — same idea as .settings-grid's columns,
   but without each section getting its own card border/shadow, since
   they're one form with one Save button, not separate settings. */
.settings-sections {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 28px;
}

/* A .card that's also a plain navigation link (e.g. an employee card in
   .settings-grid on employees.php) — same box as .card, just clickable
   as a whole instead of needing a separate "View" link inside it. */
.card-link {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.card-link:hover {
    box-shadow: var(--shadow), 0 8px 20px rgba(0, 0, 0, 0.08);
    transform: translateY(-1px);
}

/* Same idea as .card-link, but for a <button class="card"> that opens
   a modal instead of navigating (the payroll cards) — resets the
   native button chrome so it reads as a plain card, not a button. */
button.card {
    display: block;
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    padding: 0;
    cursor: pointer;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

button.card:hover {
    box-shadow: var(--shadow), 0 8px 20px rgba(0, 0, 0, 0.08);
    transform: translateY(-1px);
}

.employee-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.employee-card-name {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 15px;
}

.employee-card-top .icon-badge {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border-radius: 7px;
    background: var(--surface-sunken);
    color: var(--muted);
    flex-shrink: 0;
}

.employee-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 12px;
    font-size: 12.5px;
    color: var(--muted);
}

.employee-card-meta span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.settings-section-title {
    margin: 0 0 14px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
}

/* Grid items default to min-width:auto (content-sized) — without this,
   a wide table inside a card can force the whole grid track wider than
   the viewport, bypassing the table's own .table-wrap scroll entirely. */
.content-grid > *,
.stats > * {
    min-width: 0;
}

/* Invoice and job-card pages: a narrower, fixed-width right column
   (balance due / status / running total) rather than the usual 2fr:1fr
   split. */
.content-grid-aside {
    grid-template-columns: 1fr 340px;
}

/* Reports page: a slightly wider left column than the 2fr:1fr default. */
.content-grid-reports {
    grid-template-columns: 1.4fr 1fr;
}

.card-header {
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
    font-size: 14.5px;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.card-header-title {
    display: flex;
    align-items: center;
    gap: 9px;
}

.card-header-title .icon-badge {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border-radius: 7px;
    background: var(--surface-sunken);
    color: var(--muted);
    flex-shrink: 0;
}

.card-header-link {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--muted);
    white-space: nowrap;
}

.card-header-link:hover {
    color: var(--primary-dark);
}

.muted {
    color: var(--muted);
}

/* Only the Job Cards page has a .kanban block — scoped rather than
   making every .page a flex column, so this doesn't reflow anything
   else. Flex-column here just gives .page-footer-links' margin-top:
   auto below something to push against, landing it at the page's own
   bottom edge instead of sitting right under the board. */
.page:has(.kanban) {
    display: flex;
    flex-direction: column;
    /* Trimmed from the global 32px, same reasoning as the top-padding
       override further down — every px reclaimed here goes straight
       into .kanban-column's height instead of sitting empty below the
       Deleted Job Cards link. */
    padding-bottom: 20px;
}

/* A quiet utility link below the kanban board — kept out of the page
   header/nav since it's a rarely-used escape hatch, not primary action. */
.page-footer-links {
    display: flex;
    justify-content: center;
    margin-top: 18px;
}

.page:has(.kanban) .page-footer-links {
    margin-top: auto;
    padding-top: 10px;
}

.page-footer-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--muted);
    font-size: 13px;
    text-decoration: none;
}

.page-footer-link:hover {
    color: var(--text);
    text-decoration: underline;
}

/* The live-search box shown in a card-header, next to its title */
.header-search {
    max-width: 260px;
    width: 100%;
    height: 40px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-sunken);
    color: var(--text);
    font-size: 14px;
    outline: none;
    transition: border-color 0.15s ease;
}

.header-search::placeholder {
    color: var(--muted);
}

.header-search:focus {
    border-color: var(--primary);
    background: var(--surface);
}

.link-action {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary-dark);
    white-space: nowrap;
}

.link-action:hover {
    color: var(--primary);
}

.link-action svg {
    width: 14px;
    height: 14px;
}

.card-body {
    padding: 20px;
}

.empty-state {
    min-height: 120px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: var(--muted);
    font-size: 14px;
    text-align: center;
    padding: 24px 20px;
    line-height: 1.6;
}

.empty-state svg {
    color: #c9c5bb;
}

/* Tables */

.table-wrap {
    overflow-x: auto;
}

table.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.data-table th {
    text-align: left;
    padding: 12px 20px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
}

.data-table td {
    padding: 14px 20px;
    border-bottom: 1px solid var(--border);
}

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

.data-table tr.clickable {
    cursor: pointer;
}

.part-labour-row td {
    padding: 6px 20px 14px 44px;
    border-bottom: 1px solid var(--border);
    color: var(--muted);
    font-size: 12.5px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.part-labour-label {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.part-labour-label svg {
    flex-shrink: 0;
}

.part-labour-row .num {
    flex-shrink: 0;
    font-weight: 600;
    color: var(--text);
}

.data-table tr.clickable:hover {
    background: #faf9f6;
}

.data-table select,
.data-table input[type="number"] {
    width: 100%;
    height: 38px;
    padding: 0 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    outline: none;
}

.data-table select:focus,
.data-table input[type="number"]:focus {
    border-color: var(--primary);
}

/* Pagination */

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 20px;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}

.pagination-summary {
    font-size: 12.5px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 10px;
}

.pagination-page {
    font-size: 12.5px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.button[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

/* The compact numbered variant (render_numbered_pagination()) — a
   dashboard-widget footer, not a full list page, so it's centered and
   built from small pill buttons instead of the Prev/Next + "Page X of
   Y" summary above. */
.pagination-compact {
    justify-content: center;
    gap: 4px;
    padding: 12px 20px;
}

.pagination-number,
.pagination-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 28px;
    padding: 0 6px;
    border-radius: 7px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    font-variant-numeric: tabular-nums;
}

.pagination-number:hover,
.pagination-arrow:hover {
    background: var(--surface-sunken);
    color: var(--text);
}

.pagination-number.active {
    background: var(--primary-soft);
    color: var(--primary-dark);
}

.pagination-arrow[aria-disabled="true"] {
    opacity: 0.35;
    pointer-events: none;
}

.pagination-ellipsis {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 28px;
    color: var(--muted);
    font-size: 12.5px;
}

/* Status badges */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 99px;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: capitalize;
    white-space: nowrap;
}

.badge svg {
    width: 12px;
    height: 12px;
}

.badge-received { background: #e0e7ff; color: #3730a3; }
.badge-in_progress { background: #fef3c7; color: #92400e; }
.badge-quality_check { background: #fef9c3; color: #713f12; }
.badge-ready { background: #dcfce7; color: #166534; }
.badge-delivered { background: #e5e3dd; color: #57534e; }
.badge-on_hold { background: #fee2e2; color: #991b1b; }
.badge-cancelled { background: #e5e3dd; color: #a8a29e; }

.badge-unpaid { background: #fee2e2; color: #991b1b; }
.badge-partial { background: #fef3c7; color: #92400e; }
.badge-paid { background: #dcfce7; color: #166534; }
.badge-void { background: #e5e3dd; color: #78716c; }

.badge-success { background: #dcfce7; color: #166534; }
.badge-warning { background: #fef3c7; color: #92400e; }
.badge-danger { background: #fee2e2; color: #991b1b; }
.badge-info { background: #e0e7ff; color: #3730a3; }
.badge-neutral,
.badge-subtle { background: #e5e3dd; color: #57534e; }

.badge-present { background: #dcfce7; color: #166534; }
.badge-half_day { background: #fef3c7; color: #92400e; }
.badge-absent { background: #fee2e2; color: #991b1b; }
.badge-not_marked { background: #e5e3dd; color: #78716c; }

.badge-create { background: #dcfce7; color: #166534; }
.badge-update { background: #e0e7ff; color: #3730a3; }
.badge-delete { background: #fee2e2; color: #991b1b; }

/* Forms */

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.form-grid.single {
    grid-template-columns: 1fr;
}

.form-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.form-field label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.form-field input,
.form-field select,
.form-field textarea {
    width: 100%;
    /* A <select> sizes its closed box to its longest option by default
       and won't shrink below that on its own (unlike a plain input) —
       without this, one long option (e.g. "Dadra and Nagar Haveli and
       Daman and Diu" in the state picker) forces the field, its grid
       track, and the whole page wider than the viewport on mobile. The
       dropdown itself still shows full option text when opened. */
    min-width: 0;
    height: 42px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: #ffffff;
    outline: none;
}

.form-field textarea {
    height: auto;
    padding: 10px 14px;
    min-height: 90px;
    resize: vertical;
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
    border-color: var(--primary);
}

/* Customer Voice — repeatable numbered complaint rows, see
   public/js/customer-voice-list.js. */
.customer-voice-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

.customer-voice-row .customer-voice-index {
    flex-shrink: 0;
    width: 22px;
    color: var(--muted);
    font-size: 13px;
    text-align: right;
}

.customer-voice-row input {
    flex: 1;
}

.customer-voice-row .remove-customer-voice {
    flex-shrink: 0;
    background: none;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 4px 6px;
}

.customer-voice-row .remove-customer-voice:hover {
    color: var(--danger);
}

/* Accessories checklist — the first real checkbox grid in the app
   (previously the only checkbox anywhere was a single inline-styled
   one in Settings), used both for ticking items at intake and for the
   read-only "what was present" display on the job card/print pages. */
.checkbox-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px 20px;
}

.checkbox-grid-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
}

.checkbox-grid-item input[type="checkbox"] {
    width: auto;
    margin: 0;
}

.checkbox-grid-readonly .checkbox-grid-item {
    color: var(--muted);
}

.checkbox-grid-readonly .checkbox-grid-item.checked {
    color: var(--text);
    font-weight: 500;
}

@media (max-width: 480px) {
    .checkbox-grid {
        grid-template-columns: 1fr;
    }
}

/* SVG vehicle-condition hit map — paths sit directly over the source image
   in its native 560 x 879 coordinate system. */
.damage-diagram-wrap {
    position: relative;
    width: min(100%, 440px);
    max-width: 440px;
    margin: 0 auto;
}

.damage-image-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 560 / 879;
    background: #fff;
}

.damage-diagram-image,
.damage-map {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}

.damage-map {
    overflow: visible;
}

.damage-region {
    fill: rgba(255, 255, 255, 0.01);
    stroke: transparent;
    stroke-width: 3;
    cursor: pointer;
    outline: none;
    transition: fill 120ms ease, stroke 120ms ease;
}

.damage-region:hover,
.damage-region:focus-visible {
    fill: rgba(13, 148, 136, 0.18);
    stroke: #0d9488;
}

.damage-region.selected {
    fill: rgba(13, 148, 136, 0.12);
    stroke: #0d9488;
}

.damage-orientation {
    display: flex;
    justify-content: space-between;
    padding: 4px 8px 0;
    color: var(--muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
}

.damage-image-readonly {
    width: 100%;
    max-width: 360px;
    display: block;
    margin: 0 auto;
}

.damage-mark-picker {
    position: absolute;
    width: 168px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 6px;
    transform: translate(-50%, -50%);
    z-index: 20;
}

.damage-mark-picker[hidden] {
    display: none;
}

.damage-picker-title {
    padding: 3px 4px 6px;
    color: var(--text);
    font-size: 11px;
    font-weight: 700;
    text-align: center;
}

.damage-picker-actions {
    display: flex;
    gap: 4px;
}

.damage-mark-picker button {
    flex: 1;
    height: 28px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    font-weight: 700;
    cursor: pointer;
}

.damage-mark-picker button:hover {
    background: var(--primary-soft);
    border-color: var(--primary);
}

.damage-mark-picker button span {
    font-size: 9px;
    font-weight: 500;
}

.damage-selection-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.damage-selection {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 28px;
    padding: 4px 8px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--damage-color);
    border-radius: 5px;
    background: var(--surface-sunken);
    color: var(--text);
    cursor: pointer;
    font-size: 11px;
}

.damage-selection strong {
    color: var(--damage-color);
    font-size: 10px;
}

.damage-selection:hover {
    border-color: var(--damage-color);
}

.damage-mark-list {
    display: grid;
    gap: 6px;
}

.damage-mark-item {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    border-bottom: 1px solid var(--border);
    padding: 5px 0;
    font-size: 12px;
}

.damage-mark-item strong {
    min-width: 0;
    overflow-wrap: anywhere;
}

.damage-mark-item span {
    color: var(--muted);
}

.damage-mark-item .damage-mark-number {
    color: var(--primary);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.recorded-at-label {
    display: block;
    margin-top: 4px;
    color: var(--muted);
    font-size: 11px;
}

.recorded-at-updated {
    color: var(--warning);
    font-weight: 600;
}

/* A small icon-only button in a .card-header, right-aligned by that
   element's own justify-content: space-between — same visual treatment
   as .kanban-card-icon-button, kept separate since this isn't kanban-
   specific. */
.card-header-icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    border-radius: 6px;
    border: none;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.card-header-icon-button:hover {
    background: var(--surface-sunken);
    color: var(--text);
}

/* Job-card intake wizard — see public/js/job-card-wizard.js. The step
   fragments themselves ([data-step]) need no styling of their own; JS
   toggles `hidden` on them directly. */
.wizard-progress {
    display: flex;
    gap: 4px;
    margin-bottom: 20px;
}

.wizard-progress-step {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    padding-bottom: 10px;
    border-bottom: 3px solid var(--border);
    color: var(--muted);
    font-size: 12.5px;
    font-weight: 600;
    transition: border-color 150ms ease, color 150ms ease;
}

.wizard-progress-dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    font-size: 11.5px;
}

.wizard-progress-step.active,
.wizard-progress-step.done {
    border-color: var(--primary);
    color: var(--text);
}

.wizard-progress-step.active .wizard-progress-dot,
.wizard-progress-step.done .wizard-progress-dot {
    background: var(--primary);
    border-color: var(--primary);
    color: #ffffff;
}

.wizard-progress-label {
    display: none;
}

@media (min-width: 480px) {
    .wizard-progress-label {
        display: inline;
    }
}

.wizard-nav {
    margin-top: 24px;
    display: flex;
    align-items: center;
    gap: 12px;
}

.wizard-nav-spacer {
    flex: 1;
}

.wizard-nav button[hidden] {
    display: none;
}

.wizard-step-error {
    width: 100%;
    margin-bottom: 4px;
    color: var(--danger);
    font-size: 12.5px;
}

.wizard-skip-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 4px;
}

.wizard-skip-note svg {
    flex-shrink: 0;
    margin-top: 1px;
    color: var(--muted);
}

.form-actions {
    margin-top: 24px;
    display: flex;
    gap: 12px;
}

.form-error {
    background: #fee2e2;
    color: #991b1b;
    padding: 12px 16px;
    border-radius: 8px;
    font-size: 13.5px;
    margin-bottom: 20px;
}

.form-success {
    background: #dcfce7;
    color: #15803d;
    padding: 12px 16px;
    border-radius: 8px;
    font-size: 13.5px;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Actionable, not wrong — a due-but-not-yet-a-problem state (e.g. a
   payroll card's "Payroll due" notice), so it gets the warning amber
   instead of .form-error's red or .form-success's green. */
.form-notice {
    background: var(--warning-soft);
    color: #92400e;
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 12.5px;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Date/month picker in a card-header (Attendance, Payroll, Finance).
   The native input's own click target only opens the picker when you
   hit its built-in calendar glyph exactly — clicking its text instead
   drops into inline keyboard-editing. The button is the one real,
   always-visible control (icon + a formatted label, e.g. "September
   2026"); the native input sits behind it, invisible and unclickable,
   purely to hold the value and drive showPicker()/name= submission —
   so clicking anywhere in the box behaves identically. */
.header-date-wrap {
    position: relative;
    display: inline-flex;
}

.header-date-trigger {
    display: inline-flex;
}

.header-date-wrap input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    pointer-events: none;
}

/* Search / results (job card intake, parts lookup) */

.search-row {
    display: flex;
    gap: 10px;
    margin-bottom: 16px;
}

.search-row input {
    flex: 1;
    height: 46px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    outline: none;
}

.search-row input:focus {
    border-color: var(--primary);
}

.search-result {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: border-color 0.15s ease;
}

.search-result:hover {
    border-color: var(--primary);
}

.result-meta {
    margin-top: 4px;
    color: var(--muted);
    font-size: 12px;
}

/* A "label: value" row inside a card — print.css has had this for
   invoice/job-card print sheets since the start, but nothing in the
   main app stylesheet did until a profile-style detail card needed it
   here (see employee.php's Contact/Payroll cards). */
.info-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13.5px;
}

.info-row:last-child {
    border-bottom: none;
}

.info-row .label {
    color: var(--muted);
}

.info-row .value {
    font-weight: 600;
    text-align: right;
}

.autocomplete-results {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 100;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    margin-top: 4px;
    max-height: 200px;
    overflow-y: auto;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.autocomplete-results.open {
    display: block;
}

.autocomplete-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    cursor: pointer;
    transition: background 0.12s ease;
}

.autocomplete-item:hover {
    background: var(--hover);
}

.autocomplete-item:first-child {
    border-radius: 10px 10px 0 0;
}

.autocomplete-item:last-child {
    border-radius: 0 0 10px 10px;
}

.autocomplete-item:only-child {
    border-radius: 10px;
}

.autocomplete-item + .autocomplete-item {
    border-top: 1px solid var(--border);
}

.autocomplete-item-name {
    font-size: 13px;
    font-weight: 500;
}

.autocomplete-item-price {
    font-size: 12px;
    color: var(--muted);
    white-space: nowrap;
    margin-left: 12px;
}

/* Kanban (job cards board) — every bit of vertical space reclaimed up
   here (page's own top padding, the search row's bottom margin, this
   margin-top) feeds directly into .kanban-column's height below, so
   the board itself gets taller instead of that space going to waste. */
.page:has(.kanban) {
    padding-top: 20px;
}

.page:has(.kanban) .search-row {
    margin-bottom: 10px;
}

.kanban {
    display: grid;
    grid-template-columns: repeat(5, minmax(220px, 1fr));
    gap: 16px;
    margin-top: 14px;
    align-items: stretch;
}

.kanban-column {
    background: #ece9e2;
    border-radius: var(--radius);
    border-top: 3px solid transparent;
    padding: 14px;
    height: calc(100vh - 254px);
    min-height: 470px;
    box-sizing: border-box;
    /* Grid's automatic row sizing otherwise uses the column's un-clipped
       *content* height (the full card stack, ignoring the scrollable
       child's overflow) to size the shared row track — every column
       then sits inside a row as tall as the busiest one, even though
       each column's own box still shows the height/min-height above.
       That invisible extra height is what was pushing everything below
       the board down. overflow: hidden makes this item's own box (not
       just the scrollable .kanban-column-cards inside it) the thing
       Grid measures, so the row track matches the height actually set
       above instead of the tallest column's true card count. */
    overflow: hidden;
    transition: box-shadow 0.15s ease, opacity 0.15s ease;
    display: flex;
    flex-direction: column;
}

.kanban-column-cards {
    min-height: 0;
    overflow-y: auto;
    flex: 1;
    padding-right: 2px;
    scrollbar-width: thin;
    scrollbar-color: rgba(0, 0, 0, 0.16) transparent;
}

.kanban-column-cards::-webkit-scrollbar {
    width: 5px;
}

.kanban-column-cards::-webkit-scrollbar-track {
    background: transparent;
}

.kanban-column-cards::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.16);
    border-radius: 4px;
}

.kanban-column-cards::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.28);
}

/* One accent colour per stage — carried by both the column's top edge
   and (more strongly) by every card inside it, so status reads at a
   glance without needing to read the column header. */
.kanban-column.col-received { border-top-color: #4338ca; }
.kanban-column.col-in_progress { border-top-color: #b45309; }
.kanban-column.col-quality_check { border-top-color: #eab308; }
.kanban-column.col-ready { border-top-color: #15803d; }
.kanban-column.col-delivered { border-top-color: #78716c; }

.kanban-column.drag-over {
    box-shadow: inset 0 0 0 2px var(--primary);
}

.kanban-column.drop-disabled {
    opacity: 0.4;
}

.kanban-column-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
}

.kanban-column-label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.kanban-column-label svg {
    color: #a39d8f;
}

.kanban-column-meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.kanban-column-total {
    color: var(--text);
    font-weight: 700;
}

.kanban-card-wrap {
    position: relative;
    margin-bottom: 10px;
    border-radius: 9px;
    box-shadow: var(--shadow);
}

.kanban-card-wrap[hidden] {
    display: none;
}

.kanban-card-link {
    display: block;
}

.kanban-card-link[draggable="true"] {
    cursor: grab;
    /* Touch drag (job-cards-board.js) takes over the gesture itself once
       it crosses the drag threshold — without these, touch devices would
       instead try to scroll the page and/or show the browser's own
       long-press link callout/context menu. */
    touch-action: none;
    -webkit-touch-callout: none;
}

.kanban-card-link[draggable="true"]:active {
    cursor: grabbing;
}

.kanban-card-link.dragging .kanban-card {
    opacity: 0.4;
}

.kanban-card-wrap:has(.kanban-card-link.dragging) .kanban-card-footer {
    opacity: 0.4;
}

/* The floating clone that follows the finger during a touch drag — the
   original card (above) just dims via .dragging in place until drop. */
.kanban-card-ghost {
    position: fixed;
    z-index: 1000;
    margin: 0;
    pointer-events: none;
    transform: scale(1.03);
    box-shadow: 0 14px 28px rgba(0, 0, 0, 0.22);
    /* The ghost is a clone of the card link only (no footer comes along
       for the drag), whose .kanban-card child now has square bottom
       corners to butt against the footer — clipping to a full radius
       here keeps the dragged clone looking like a complete card. */
    border-radius: 9px;
    overflow: hidden;
}

body.kanban-touch-dragging {
    overscroll-behavior: contain;
}

.kanban-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-bottom: none;
    border-left: 4px solid var(--border);
    border-radius: 9px 9px 0 0;
    padding: 12px 14px 8px;
    transition: opacity 0.15s ease;
}

/* Card colour matches the actual status, not just the column it sits
   in — an on_hold card visually lives in the In Progress column but
   is tinted distinctly so a paused job never looks like active work.
   The footer below (job-cards.php) carries the same card-<status>
   class so the print/edit row reads as part of the same card, not a
   separate strip sitting under it. */
.kanban-card.card-received, .kanban-card-footer.card-received { border-left-color: #4338ca; background: #eef0fd; }
.kanban-card.card-in_progress, .kanban-card-footer.card-in_progress { border-left-color: #b45309; background: #fef8ec; }
.kanban-card.card-on_hold, .kanban-card-footer.card-on_hold { border-left-color: #b91c1c; background: #fdeeee; }
.kanban-card.card-quality_check, .kanban-card-footer.card-quality_check { border-left-color: #eab308; background: #fefce8; }
.kanban-card.card-ready, .kanban-card-footer.card-ready { border-left-color: #15803d; background: #eefbf1; }
.kanban-card.card-delivered, .kanban-card-footer.card-delivered { border-left-color: #78716c; background: #f2f1ef; }

.kanban-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
}

.kanban-card-job-no {
    font-size: 11.5px;
    color: var(--muted);
    font-weight: 600;
    line-height: 1.3;
}

.kanban-card-top-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    text-align: right;
    flex-shrink: 0;
}

.kanban-card-vehicle {
    margin-top: 4px;
    font-weight: 600;
    font-size: 14px;
}

.kanban-card-customer {
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--muted);
}

.kanban-card-amount {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    line-height: 1.3;
}

.kanban-card-due {
    margin-top: 2px;
    font-size: 11px;
    font-weight: 700;
    color: #b45309;
    white-space: nowrap;
    line-height: 1.2;
}

.kanban-card-due-overdue {
    color: #b91c1c;
}

/* Footer sits below the card link as a plain sibling, not nested inside
   the <a> — a <button>/<a> nested in an anchor is invalid HTML and
   browsers handle it inconsistently, so the print/edit triggers live
   outside kanban-card-link entirely (see job-cards.php). Its border and
   status tint (above) pick up exactly where .kanban-card's leave off —
   border-bottom:none up top, border-top:none down here — so together
   they read as one continuous bordered card, not a card with a
   separate strip floating under it. */
.kanban-card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border: 1px solid var(--border);
    border-top: none;
    border-left: 4px solid var(--border);
    border-radius: 0 0 9px 9px;
    padding: 2px 10px 6px;
}

.kanban-card-print {
    position: relative;
    margin-left: auto;
}

.kanban-card-icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    border: none;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.kanban-card-icon-button:hover {
    background: var(--surface-sunken);
    color: var(--text);
}

.kanban-card-print-menu {
    top: calc(100% + 4px);
    right: 0;
    min-width: 170px;
}

/* App-wide toast stack (public/js/toast.js) — every save/change
   confirmation and every error that used to be a blocking alert()
   renders here, so feedback looks the same no matter which page or
   action triggered it. */
.toast-stack {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column-reverse;
    align-items: center;
    gap: 10px;
    z-index: 300;
    pointer-events: none;
}

.toast {
    background: var(--text);
    color: #fff;
    padding: 12px 18px;
    border-radius: 9px;
    font-size: 13.5px;
    font-weight: 600;
    max-width: 360px;
    text-align: center;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.toast.visible {
    opacity: 1;
    transform: translateY(0);
}

.toast-error {
    background: var(--danger-dark);
}

/* Status tracker — same stage colours as the kanban board, so the job
   card detail page reads as the same object, not a different UI. */
.status-tracker {
    display: flex;
    gap: 6px;
    margin-bottom: 16px;
}

.status-tracker-segment {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    text-align: center;
}

.status-tracker-dot {
    width: 100%;
    height: 6px;
    border-radius: 4px;
    background: var(--border);
}

.status-tracker-segment.filled .status-tracker-dot {
    background: var(--stage-color, var(--primary));
}

.status-tracker-label {
    font-size: 10.5px;
    color: var(--muted);
    line-height: 1.3;
}

.status-tracker-segment.filled .status-tracker-label {
    color: var(--stage-color, var(--text));
    font-weight: 600;
}

/* Login */

.auth-shell {
    min-height: 100vh;
    display: grid;
    place-items: center;
    position: relative;
    padding: 24px;
    background-color: var(--sidebar);
    background-image: url('/img/login-hero.jpg');
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

/* The night photo (6pm–6am, set server-side in index.php from the
   workshop's own local time) is already a moody, warm-lit shot on its
   own — it needs a much lighter scrim than the bright daytime photo,
   or the amber highlights that make it interesting get crushed. */
.auth-shell-night {
    background-image: url('/img/login-hero-night.jpg');
}

/* Soft scrim over the photo — keeps it readable as a backdrop without
   flattening it into a plain color, and darkens the bottom edge a
   touch more so the "Powered by" line never sits on a bright patch.
   Tinted warm (toward --primary-dark) rather than neutral charcoal so
   the photo, glass card, and ambient glow all read as one palette. */
.auth-shell::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(42, 27, 14, 0.5) 0%, rgba(42, 27, 14, 0.28) 45%, rgba(42, 27, 14, 0.6) 100%);
}

.auth-shell-night::before {
    background: linear-gradient(180deg, rgba(10, 7, 4, 0.28) 0%, rgba(10, 7, 4, 0.12) 45%, rgba(10, 7, 4, 0.4) 100%);
}

.auth-card {
    position: relative;
    z-index: 1;
    width: min(380px, 100%);
    background: rgba(255, 255, 255, 0.62);
    backdrop-filter: blur(28px) saturate(180%);
    -webkit-backdrop-filter: blur(28px) saturate(180%);
    border-top: 1px solid rgba(255, 255, 255, 0.85);
    border-left: 1px solid rgba(255, 255, 255, 0.65);
    border-right: 1px solid rgba(255, 255, 255, 0.18);
    border-bottom: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: var(--radius);
    padding: 40px 36px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

/* Ambient brand-color glow behind the glass — reads as though the card
   is lit from behind rather than just floating on the photo. Needs the
   card's own z-index above to keep this pseudo-element's negative
   z-index scoped to the card's stacking context instead of sinking
   behind .auth-shell::before. */
.auth-card::after {
    content: '';
    position: absolute;
    inset: -50px;
    z-index: -1;
    background: radial-gradient(circle at 30% 20%, rgba(194, 102, 10, 0.55), transparent 60%);
    filter: blur(70px);
}

.auth-brand {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.5px;
}

.auth-brand::before {
    content: '';
    display: block;
    width: 34px;
    height: 4px;
    margin-bottom: 14px;
    border-radius: 2px;
    background: var(--primary);
}

.auth-subtitle {
    margin: 6px 0 28px;
    color: var(--muted);
    font-size: 14px;
}

.auth-field {
    margin-bottom: 16px;
}

.auth-field label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    margin-bottom: 6px;
}

.auth-field input {
    width: 100%;
    height: 44px;
    padding: 0 14px;
    background: rgba(255, 255, 255, 0.55);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.6);
    border-radius: 8px;
    outline: none;
    transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.auth-field input:focus {
    background: rgba(255, 255, 255, 0.8);
    border-color: var(--primary);
    box-shadow: 0 0 0 4px rgba(194, 102, 10, 0.18);
}

.auth-submit {
    width: 100%;
    height: 46px;
    margin-top: 8px;
    border: 0;
    border-radius: 8px;
    background: linear-gradient(135deg, var(--primary), var(--primary-dark));
    color: #ffffff;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 8px 24px rgba(194, 102, 10, 0.45);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.auth-submit:hover {
    transform: translateY(-1px);
    box-shadow: 0 10px 28px rgba(194, 102, 10, 0.55);
}

/* Layout utilities — replace one-off inline styles so spacing stays
   consistent everywhere instead of drifting page by page. */

.stack {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.stack-sm {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.inline-form {
    display: inline-flex;
}

.panel-footer {
    padding: 16px 20px;
    border-top: 1px solid var(--border);
}

.selected-summary {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--surface-sunken);
    border-radius: 9px;
    padding: 12px 14px;
    margin-bottom: 16px;
}

.selected-summary .icon-badge {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    flex-shrink: 0;
}

/* A find-or-add helper row: search stays primary, "add new" is always
   visible right beside it — never hidden behind a failed search. */
.intake-add-new-row {
    margin: 12px 0 16px;
}

/* Modal ("quick create" dialogs launched from + New buttons) */

.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(20, 18, 15, 0.55);
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding: 5vh 20px;
    overflow-y: auto;
    z-index: 100;
}

.modal-backdrop.open {
    display: flex;
}

.modal {
    background: var(--surface);
    border-radius: var(--radius);
    width: 100%;
    max-width: 640px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
    margin: auto 0;
}

/* For a modal whose content genuinely needs more room — the job-card
   intake modal, once it grew a checklist and a diagram alongside its
   original fields — rather than every modal paying for that width. */
.modal-wide {
    max-width: 860px;
}

/* For the confirm dialog — a short question and two buttons don't need
   a modal sized for a form. */
.modal-narrow {
    max-width: 440px;
}

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border);
}

.modal-header-title {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 16px;
    font-weight: 700;
}

.modal-header-title .icon-badge {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    background: var(--primary-soft);
    color: var(--primary-dark);
}

.modal-header-title .icon-badge.danger {
    background: var(--danger-soft);
    color: var(--danger-dark);
}

.modal-close {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    color: var(--muted);
    background: transparent;
    border: 0;
    cursor: pointer;
}

.modal-close:hover {
    background: var(--surface-sunken);
    color: var(--text);
}

.modal-body {
    padding: 22px;
    max-height: 78vh;
    overflow-y: auto;
}

/* A submit button that floats above a long modal form instead of
   sitting at the end of it — for forms inside .modal-body whose content
   (e.g. the accessories checklist, the damage diagram) can run taller
   than the modal, so the button stays reachable without scrolling all
   the way down. Only the button itself is interactive: the sticky row
   has no background/padding of its own, so it doesn't eat into the
   content width or block clicks on whatever scrolls underneath it. */
.actions-float {
    position: sticky;
    bottom: 14px;
    display: flex;
    justify-content: flex-end;
    margin-top: 18px;
    pointer-events: none;
}

.actions-float .button {
    pointer-events: auto;
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.28);
}

/* Responsive
   Three tiers: desktop (>=1024px, untouched baseline), tablet
   (768-1023px, same layout as desktop but with an optional manual
   sidebar collapse), mobile (<=767px, off-canvas nav drawer). */

@media (max-width: 1100px) {
    .kanban {
        grid-template-columns: repeat(3, minmax(220px, 1fr));
        overflow-x: auto;
    }
}

@media (max-width: 1000px) {
    .stats {
        grid-template-columns: repeat(2, 1fr);
    }

    .content-grid,
    .content-grid-aside,
    .content-grid-reports {
        grid-template-columns: 1fr;
    }

    .form-grid {
        grid-template-columns: 1fr;
    }
}

/* Tablet and desktop: an icon-only rail is available from 768px up —
   collapsing it (see the toggle button, persisted in localStorage by
   responsive-nav.js) frees real width for pages that need it, like the
   job-cards board's five columns. Below 768px the sidebar becomes the
   off-canvas drawer instead (see the mobile block further down), where
   this collapsed rail concept doesn't apply. */
@media (min-width: 768px) {
    .sidebar-collapse-toggle {
        display: flex;
    }

    /* Left padding matches the expanded sidebar (16px), keeping every icon's
       exact horizontal X and vertical Y position 100% identical between
       opened and collapsed states without any shift. */
    .app.sidebar-collapsed .sidebar {
        width: 76px;
        padding: 22px 10px 22px 16px;
    }

    .app.sidebar-collapsed .brand-name,
    .app.sidebar-collapsed .brand-subtitle,
    .app.sidebar-collapsed .brand-text,
    .app.sidebar-collapsed .sidebar-footer,
    .app.sidebar-collapsed .sidebar-collapse-toggle span:last-child,
    .app.sidebar-collapsed .nav-item span:last-child,
    .app.sidebar-collapsed .nav-group-toggle,
    .app.sidebar-collapsed .nav-subitems {
        display: none;
    }

    /* Section label keeps its exact height and spacing to prevent vertical
       jumping of all subsequent icons. In collapsed state, intermediate
       sections show a subtle divider line to distinguish groups. */
    .app.sidebar-collapsed .nav-section:first-of-type .nav-label {
        opacity: 0;
        pointer-events: none;
        user-select: none;
        visibility: hidden;
    }

    .app.sidebar-collapsed .nav-section:not(:first-of-type) .nav-label {
        opacity: 1;
        visibility: visible;
        pointer-events: none;
        user-select: none;
        font-size: 0;
        color: transparent;
        padding: 0 4px;
        justify-content: center;
    }

    .app.sidebar-collapsed .nav-section:not(:first-of-type) .nav-label::after {
        content: '';
        display: block;
        width: 100%;
        height: 1px;
        background: rgba(255, 255, 255, 0.14);
        border-radius: 1px;
    }

    /* Flip the collapse toggle chevron when closed */
    .app.sidebar-collapsed .sidebar-collapse-toggle .nav-icon {
        transform: rotate(180deg);
    }

    .app.sidebar-collapsed .main {
        margin-left: 76px;
        width: calc(100% - 76px);
    }

}

/* Collapsed-rail tooltip — a single shared element (see sidebar.php),
   positioned by responsive-nav.js on hover rather than as a CSS
   ::after on the nav item itself. It has to live outside .sidebar's
   own DOM: .sidebar sets overflow-y:auto for its scrollbar, and per
   the CSS overflow spec that forces overflow-x to auto too, which
   would clip any tooltip positioned to poke out past the collapsed
   76px rail. Fixed positioning + a body-level element sidesteps that
   entirely. */
.sidebar-tooltip {
    position: fixed;
    z-index: 300;
    background: var(--sidebar);
    color: #ffffff;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    padding: 7px 11px;
    border-radius: 7px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.1s ease;
}

.sidebar-tooltip.visible {
    opacity: 1;
}

/* Collapsed-rail flyout for a nav group (Finance) — populated with real
   cloned .nav-item nodes by responsive-nav.js, so it needs no link/hover
   styling of its own beyond the dark "floating piece of the sidebar"
   shell; .nav-item's own rules (app.css, "Sidebar" section) already
   handle everything inside it. Fixed positioning for the same clipping
   reason as .sidebar-tooltip above. */
.sidebar-flyout {
    position: fixed;
    z-index: 250;
    min-width: 190px;
    background: var(--sidebar);
    border-radius: 10px;
    padding: 8px;
    box-shadow: 0 14px 32px rgba(0, 0, 0, 0.3);
}

.sidebar-flyout[hidden] {
    display: none;
}

/* Overrides .app.sidebar-collapsed .nav-subitems' display:none — the
   flyout is a clone of that same element (see openSidebarFlyout in
   responsive-nav.js) and is still a DOM descendant of .app while the
   rail is collapsed, so without this it would inherit "hidden" from a
   rule meant for the rail itself, not this floating copy of it. */
.sidebar-flyout .nav-subitems {
    display: flex !important;
    padding-left: 0;
}

/* Same override, for the label text next to each cloned icon — also
   caught by .app.sidebar-collapsed .nav-item span:last-child's
   display:none, which exists to hide labels on the rail itself, not on
   a flyout meant to show full rows. */
.sidebar-flyout .nav-item span:last-child {
    display: inline !important;
}

/* Mobile: the sidebar becomes a hidden off-canvas drawer (full width,
   labeled — it only appears on demand, so it can afford the space
   the tablet rail can't) opened by the topbar hamburger button. */
@media (max-width: 767px) {
    .hamburger-button {
        display: flex;
    }

    .sidebar-collapse-toggle {
        display: none;
    }

    .main {
        margin-left: 0;
        width: 100%;
    }

    .sidebar {
        z-index: 110;
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        box-shadow: 0 0 40px rgba(0, 0, 0, 0.3);
    }

    .app.mobile-nav-open .sidebar {
        transform: translateX(0);
    }

    .nav-backdrop {
        display: none;
        position: fixed;
        inset: 0;
        background: rgba(20, 18, 15, 0.55);
        z-index: 105;
    }

    .app.mobile-nav-open .nav-backdrop {
        display: block;
    }

    /* A drawer that only appears on demand can afford roomier tap
       targets than the always-visible desktop/tablet rail. */
    .nav-item {
        padding: 13px;
        font-size: 16px;
    }

    .topbar {
        padding: 0 16px;
    }

    .user-menu {
        gap: 8px;
    }

    .user-menu-name {
        display: none;
    }

    .topbar-search-results {
        left: auto;
        right: 0;
        min-width: 260px;
        max-width: calc(100vw - 32px);
    }

    .page {
        padding: 16px;
    }

    .page-header {
        flex-direction: column;
        align-items: stretch;
    }

    .page-header .button {
        width: 100%;
    }

    .stats,
    .kanban {
        grid-template-columns: 1fr;
    }

    /* Columns stack full-width above, so the whole page scrolls again —
       an internal per-column scroll region here would just be a second,
       fighting scroll area inside the page's own. */
    .kanban-column {
        height: auto;
        min-height: 200px;
        max-height: none;
    }

    .kanban-column-cards {
        overflow-y: visible;
    }

    .form-actions {
        flex-direction: column;
    }

    .form-actions .button {
        width: 100%;
    }

    .header-search {
        max-width: none !important;
        width: 100%;
        margin-top: 10px;
    }

    .modal-backdrop {
        padding: 0;
    }

    .modal {
        max-width: none;
        min-height: 100vh;
        border-radius: 0;
        margin: 0;
    }

    .data-table select,
    .data-table input[type="number"] {
        height: 44px;
    }
}

/* Fallback for the sidebar/nav tier when the layout viewport can't be
   trusted — notably iOS Safari's per-site "Request Desktop Website"
   toggle, which spoofs a desktop-width viewport so @media (max-width)
   never matches even on a phone (see sidebar.php, which adds
   .force-mobile to <html> using the true screen.width instead). Mirrors
   only the sidebar/topbar rules from the mobile tier above — the rest
   of that tier's cosmetic tweaks (page padding, modal width, etc.) just
   render at desktop proportions and get visually scaled down by the
   OS under Desktop Site mode, which isn't the reported issue. */
html.force-mobile .hamburger-button {
    display: flex;
}

html.force-mobile .sidebar-collapse-toggle {
    display: none;
}

html.force-mobile .main {
    margin-left: 0;
    width: 100%;
}

html.force-mobile .sidebar {
    z-index: 110;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.3);
}

html.force-mobile .app.mobile-nav-open .sidebar {
    transform: translateX(0);
}

html.force-mobile .nav-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(20, 18, 15, 0.55);
    z-index: 105;
}

html.force-mobile .app.mobile-nav-open .nav-backdrop {
    display: block;
}
