/* ==========================================================================
   Bablu Car Care - billing application
   ==========================================================================
   Direction: "service bay". The workshop's own signage, made digital.
   Graphite floor, caution amber, and the Indian number plate as the recurring
   artifact. Colour is never decoration here - every hue is doing a job:

     amber   attention, the thing you must deal with
     blue    bills and information
     emerald money that came in
     rose    overdue
     violet  people
     teal    vehicles

   Each area of the app owns one of those hues. `body[data-area]` in _Layout
   sets --accent, so a page's panels, tabs and focus rings match the colour of
   its own icon in the sidebar without a single view making a colour decision.
   ========================================================================== */

:root {
    /* -- Surfaces. Graphite has a blue cast; a neutral grey read as dead. -- */
    --ink: #12161d;
    --ink-2: #1a202a;
    --ink-3: #242c39;
    --ink-line: #2c3542;
    --paper: #e9edf3;
    --surface: #ffffff;
    --surface-2: #f7f9fb;
    --line: #e2e7ee;
    --line-strong: #c8d1dd;

    /* -- Text -- */
    --text: #10151c;
    --text-muted: #5b6675;
    --text-faint: #8b96a6;
    --text-on-ink: #ced6e2;
    --text-on-ink-muted: #7c8798;

    /* -- The accent. Reserved for attention, never for decoration. -- */
    --amber: #f59e0b;
    --amber-deep: #b45309;
    --amber-wash: #fff8ec;

    /* -- The working palette. Each hue has a job; see the header. -- */
    --blue: #1d4ed8;
    --blue-wash: #eff4ff;
    --emerald: #047857;
    --emerald-wash: #ecfdf5;
    --rose: #be123c;
    --rose-wash: #fff1f3;
    --violet: #6d28d9;
    --violet-wash: #f5f1ff;
    --teal: #0f766e;
    --teal-wash: #effcfa;
    --cyan: #0e7490;
    --cyan-wash: #ecfeff;
    --slate: #475569;
    --slate-wash: #f4f6f9;

    /* Semantic aliases, so components read by meaning rather than by hue. */
    --danger: var(--rose);
    --danger-wash: var(--rose-wash);
    --ok: var(--emerald);
    --ok-wash: var(--emerald-wash);
    --info: var(--blue);
    --info-wash: var(--blue-wash);

    /* -- Type. Barlow Condensed is signage: narrow, square-shouldered, the
          lettering on a workshop board. Inter carries the reading. Roboto Mono
          keeps every rupee column aligned. -- */
    --font-display: "Barlow Condensed", "Bahnschrift", "Arial Narrow", sans-serif;
    --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-data: "Roboto Mono", ui-monospace, Consolas, monospace;

    --radius-sm: 6px;
    --radius: 9px;
    --radius-lg: 14px;

    --shadow-sm: 0 1px 2px rgb(16 24 40 / 5%), 0 1px 3px rgb(16 24 40 / 4%);
    --shadow: 0 2px 4px rgb(16 24 40 / 4%), 0 8px 20px -8px rgb(16 24 40 / 12%);
    --shadow-lg: 0 12px 32px -10px rgb(16 24 40 / 22%);

    --sidebar-w: 254px;
    --topbar-h: 62px;

    /* Overridden per area by body[data-area]. Amber is the fallback. */
    --accent: #b45309;
    --accent-wash: var(--amber-wash);

    /* The colour of "this button commits my work". Deliberately NOT --accent: that changes per
       area, which made Save brown on Parts, violet on Customers and teal on Vehicles. The owner
       has to recognise Save at a glance on every screen, so it is fixed. Area identity still shows
       in panel ticks, focus rings, the title tab and chart fills. */
    --action: #b45309;
}

/* An area owns a hue. Same hue as that area's icon in the sidebar, so the page
   and the menu agree about where you are. */
body[data-area="home"]              { --accent: var(--blue);    --accent-wash: var(--blue-wash); }
body[data-area="bills"]             { --accent: var(--blue);    --accent-wash: var(--blue-wash); }
body[data-area="payments"]          { --accent: var(--emerald); --accent-wash: var(--emerald-wash); }
body[data-area="notifications"]     { --accent: var(--amber-deep); --accent-wash: var(--amber-wash); }
body[data-area="customers"]         { --accent: var(--violet);  --accent-wash: var(--violet-wash); }
body[data-area="vehicles"]          { --accent: var(--teal);    --accent-wash: var(--teal-wash); }
body[data-area="parts"]             { --accent: var(--amber-deep); --accent-wash: var(--amber-wash); }
body[data-area="reports"]           { --accent: var(--cyan);    --accent-wash: var(--cyan-wash); }
body[data-area="users"],
body[data-area="billsettings"],
body[data-area="whatsappsettings"],
body[data-area="googledrive"]       { --accent: var(--slate);   --accent-wash: var(--slate-wash); }

/* ========================================================================== */
/* Base                                                                       */
/* ========================================================================== */

* { box-sizing: border-box; }

html { font-size: 16px; }

/* No screen in this application may scroll sideways. */
html, body { max-width: 100%; overflow-x: hidden; }

body {
    margin: 0;
    background: var(--paper);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.9375rem;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6, .display-face {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: 0.005em;
    color: var(--text);
}

a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Money and any figure that must line up column-wise. */
.num {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    letter-spacing: -0.01em;
}

/* -------------------------------------------------------------------- icons */

/* One vendored Lucide sprite, referenced by <use>. Stroke colour follows the
   text colour of whatever the icon sits in, so an icon never needs its own rule. */
.i {
    width: 1.125em;
    height: 1.125em;
    flex: none;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    vertical-align: -0.2em;
}

.i-sm { width: 1em; height: 1em; }
.i-lg { width: 1.375em; height: 1.375em; stroke-width: 1.85; }

/* ========================================================================== */
/* Signature: the number plate, and the caution stripe                        */
/* ========================================================================== */

.plate {
    display: inline-flex;
    align-items: center;
    padding: 1px 7px;
    border: 2px solid #111;
    border-radius: 4px;
    background: #fff;
    color: #111;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.875rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1.35;
    white-space: nowrap;
}

.plate-sm { font-size: 0.75rem; padding: 0 5px; border-width: 1.5px; }
.plate-lg { font-size: 1.0625rem; padding: 3px 11px; }

/* Commercial vehicles carry yellow plates. */
.plate-commercial { background: #ffce00; }

/* Hazard tape. The one ornament in the application, and it means caution:
   it edges the brand block and the "needs attention" board, nowhere else. */
.hazard {
    height: 4px;
    flex: none;
    background: repeating-linear-gradient(
        -45deg,
        var(--amber) 0 8px,
        #1a202a 8px 16px
    );
}

/* ========================================================================== */
/* Shell                                                                      */
/* ========================================================================== */

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

/* Fixed, not sticky: a sticky sidebar stops scrolling once the page grows past
   one screen, which left it cut off half way down a long bill. */
.sidebar {
    width: var(--sidebar-w);
    background:
        radial-gradient(120% 60% at 0% 0%, rgb(245 158 11 / 13%), transparent 62%),
        linear-gradient(180deg, #161c25 0%, var(--ink) 42%, #0d1117 100%);
    color: var(--text-on-ink);
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0 auto 0 0;
    height: 100vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 30;
    scrollbar-width: thin;
    scrollbar-color: #333c4a transparent;
}

.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-thumb { background: #333c4a; border-radius: 4px; }

/* ---------------------------------------------------------- page footer

   One line, 12px type, no background of its own. Deliberately the quietest thing on the screen:
   it is a legal line and a name to call, not a piece of the workshop's work. */

.app-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 4px 18px;
    padding: 10px 26px 14px;
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-faint);
    border-top: 1px solid var(--line);
    margin-top: auto;
}

.app-foot-credit {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 12px;
}

.app-foot-credit strong { color: var(--text-muted); font-weight: 600; }

.app-foot-credit a {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--text-muted);
}

.app-foot-credit a:hover { color: var(--accent); text-decoration: none; }
.app-foot-credit .i { width: 13px; height: 13px; opacity: .75; }

@media (max-width: 620px) {
    .app-foot { padding-inline: 16px; justify-content: flex-start; }
}

/* ------------------------------------------------------- narrowed menu

   Icons only, so a wide bill gets the room. The whole layout is driven by --sidebar-w - the rail
   and the main column's margin both read it - so narrowing is one variable. The name of each item
   is not removed: it becomes a label beside the rail on hover or keyboard focus, which is the only
   way to know what an icon means.

   The label is position: fixed rather than absolute because the rail scrolls, and a scrolling box
   clips anything sticking out of it. Fixed escapes that; site.js supplies the top. */

.nav-narrow-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border: 1px solid var(--line-strong);
    border-radius: 9px;
    background: var(--surface);
    color: var(--text-muted);
    transition: transform .16s ease, background-color .13s ease;
}

.nav-narrow-toggle:hover { background: var(--surface-2); color: var(--text); }

html.nav-narrow .nav-narrow-toggle { transform: rotate(180deg); }

html.nav-narrow {
    --sidebar-w: 68px;
}

html.nav-narrow .sidebar-brand {
    justify-content: center;
    padding-inline: 0;
}

html.nav-narrow .brand-text,
html.nav-narrow .nav-section {
    display: none;
}

html.nav-narrow .sidebar-nav { padding-inline: 10px; }

html.nav-narrow .nav-link-app {
    justify-content: center;
    padding: 8px 0;
    gap: 0;
}

/* The count would not fit beside a hidden name, so it becomes a dot on the icon. */
html.nav-narrow .nav-count {
    position: absolute;
    top: 2px;
    right: 8px;
    min-width: 8px;
    width: 8px;
    height: 8px;
    padding: 0;
    font-size: 0;
    line-height: 0;
    border-radius: 50%;
}

html.nav-narrow .nav-link-app .txt {
    position: fixed;
    left: calc(var(--sidebar-w) + 8px);
    top: var(--flyout-top, -100px);
    z-index: 60;
    padding: 6px 11px;
    border-radius: 8px;
    background: var(--ink);
    color: #fff;
    font-size: 0.875rem;
    white-space: nowrap;
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transform: translateX(-4px);
    pointer-events: none;
    transition: opacity .12s ease, transform .12s ease;
}

html.nav-narrow .nav-link-app:hover .txt,
html.nav-narrow .nav-link-app:focus-visible .txt {
    opacity: 1;
    transform: translateX(0);
}

/* On a phone the menu is off-canvas at full width, so narrowing has nothing to do there. */
@media (max-width: 1000px) {
    .nav-narrow-toggle { display: none; }
    html.nav-narrow { --sidebar-w: 254px; }
    html.nav-narrow .brand-text,
    html.nav-narrow .nav-section { display: revert; }
    html.nav-narrow .nav-link-app { justify-content: flex-start; padding: 8px 11px; gap: 11px; }
    html.nav-narrow .nav-link-app .txt { position: static; opacity: 1; background: none;
                                         padding: 0; box-shadow: none; transform: none; }
}

/* ------------------------------------------------------------ brand block */

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 17px 18px;
    color: inherit;
}

.sidebar-brand:hover { text-decoration: none; }

/* The shop's initials on an amber plate - the same object as a number plate,
   shrunk to a badge. */
.sidebar-brand .mark {
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: 10px;
    background: linear-gradient(150deg, var(--amber), #d97706);
    color: #14181f;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: 0.04em;
    display: grid;
    place-items: center;
    box-shadow: 0 4px 12px rgb(245 158 11 / 30%);
}

.sidebar-brand .brand-text { min-width: 0; }

.sidebar-brand .shop {
    font-family: var(--font-display);
    font-size: 1.3125rem;
    font-weight: 700;
    letter-spacing: 0.035em;
    text-transform: uppercase;
    color: #fff;
    line-height: 1.05;
    display: block;
}

.sidebar-brand .tagline {
    font-family: var(--font-display);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.19em;
    text-transform: uppercase;
    color: var(--amber);
    display: block;
    margin-top: 1px;
}

/* -------------------------------------------------------------------- nav */

.sidebar-nav { padding: 12px 10px 20px; flex: 1; }

.nav-section {
    font-family: var(--font-display);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: #6b7787;
    padding: 18px 10px 7px;
    display: flex;
    align-items: center;
    gap: 9px;
}

/* A hairline that runs out from the label to the edge, the way a rule does on
   a printed job card. */
.nav-section::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--ink-line), transparent);
}

.nav-link-app {
    position: relative;
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 8px 11px;
    margin-bottom: 2px;
    border-radius: var(--radius);
    color: var(--text-on-ink);
    font-size: 0.9375rem;
    font-weight: 500;
    transition: background-color .13s ease, color .13s ease;
}

/* Each area's icon sits on a tinted tile in that area's own hue, so the menu
   is read by colour and shape before it is read as words. */
.nav-link-app .ico {
    width: 29px;
    height: 29px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 8px;
    background: color-mix(in srgb, var(--nav-accent, #94a3b8) 16%, transparent);
    color: var(--nav-accent, #94a3b8);
    transition: background-color .13s ease, color .13s ease;
}

.nav-link-app .ico .i { width: 17px; height: 17px; }
.nav-link-app .txt { min-width: 0; }

.nav-link-app:hover {
    background: rgb(255 255 255 / 6%);
    color: #fff;
    text-decoration: none;
}

.nav-link-app:hover .ico { background: color-mix(in srgb, var(--nav-accent, #94a3b8) 28%, transparent); }

/* Bay marking: the active route gets a filled tile in its own colour and an
   amber tab on the rail, the way a parked bay is marked on a workshop floor. */
.nav-link-app.active {
    background: rgb(255 255 255 / 9%);
    color: #fff;
    font-weight: 600;
}

.nav-link-app.active .ico {
    background: var(--nav-accent, #94a3b8);
    color: #0f1319;
    box-shadow: 0 3px 10px -2px var(--nav-accent, #94a3b8);
}

.nav-link-app.active::before {
    content: "";
    position: absolute;
    left: -10px;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 22px;
    border-radius: 0 3px 3px 0;
    background: var(--amber);
}

.nav-dashboard { --nav-accent: #60a5fa; }
.nav-new       { --nav-accent: #fbbf24; }
.nav-bills     { --nav-accent: #7dd3fc; }
.nav-payments  { --nav-accent: #4ade80; }
.nav-reminders { --nav-accent: #fb7185; }
.nav-customers { --nav-accent: #c4b5fd; }
.nav-vehicles  { --nav-accent: #5eead4; }
.nav-parts     { --nav-accent: #fdba74; }
.nav-reports   { --nav-accent: #67e8f9; }

/* Admin gets its own hues too. WhatsApp keeps its real brand green, because
   that is the colour the owner already associates with the thing. */
.nav-users     { --nav-accent: #f472b6; }
.nav-settings  { --nav-accent: #fb923c; }
.nav-whatsapp  { --nav-accent: #25d366; }
.nav-drive     { --nav-accent: #38bdf8; }

.nav-count {
    margin-left: auto;
    font-family: var(--font-data);
    font-size: 0.6875rem;
    background: var(--amber);
    color: #14181f;
    border-radius: 999px;
    min-width: 20px;
    text-align: center;
    padding: 1px 6px;
    font-weight: 700;
}

/* -------------------------------------------------------------- main column */

.main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    margin-left: var(--sidebar-w);
}

.topbar {
    height: var(--topbar-h);
    background: rgb(255 255 255 / 88%);
    backdrop-filter: saturate(160%) blur(10px);
    -webkit-backdrop-filter: saturate(160%) blur(10px);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 1px 0 rgb(16 24 40 / 3%);
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 26px;
    position: sticky;
    top: 0;
    z-index: 20;
}

/* The page's own colour, as a short tab under the title. */
.topbar h1 {
    position: relative;
    font-size: 1.375rem;
    font-weight: 600;
    margin: 0;
    letter-spacing: 0.01em;
    padding-left: 13px;
}

.topbar h1::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 4px;
    height: 1.05em;
    border-radius: 2px;
    background: var(--accent);
}

.topbar .spacer { flex: 1; }

.user-chip {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 0.875rem;
    color: var(--text-muted);
}

.user-avatar {
    width: 31px;
    height: 31px;
    border-radius: 50%;
    background: linear-gradient(150deg, var(--ink-3), var(--ink));
    color: #fff;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.875rem;
    display: grid;
    place-items: center;
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--accent);
}

/* A max width stops tables stretching edge-to-edge on a wide monitor, which is
   the single biggest thing that makes a business app look unfinished. */
.content {
    padding: 24px 26px 60px;
    flex: 1;
    width: 100%;
    max-width: 1680px;
    margin: 0 auto;
}

/* Every screen is the same width - the owner noticed forms sitting narrower than the lists and
   asked for them to match. The fields themselves stay readable because the forms use a 4-up grid
   (col-xl-3) rather than stretching two fields across the whole page. */
.content-narrow { width: 100%; }


.sidebar-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    width: 36px;
    height: 34px;
    color: var(--text);
    cursor: pointer;
}

.sidebar-toggle:hover { background: var(--surface-2); }

/* ========================================================================== */
/* Panels                                                                     */
/* ========================================================================== */

.panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.panel + .panel { margin-top: 18px; }

.panel-head {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 14px 20px;
    border-bottom: 1px solid var(--line);
    background: linear-gradient(180deg, var(--surface), var(--surface-2));
}

/* The page's colour, repeated as a short tick before every section title. It is
   the same mark as the tab beside the page title, one size down. */
.panel-head::before {
    content: "";
    width: 3px;
    height: 15px;
    flex: none;
    border-radius: 2px;
    background: var(--accent);
}

/* The icon already carries the page's colour, so the tick would say it twice. */
.panel-head:has(.head-ico)::before { display: none; }

.panel-head h2 {
    font-family: var(--font-display);
    font-size: 1rem;
    font-weight: 600;
    margin: 0;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text);
}

.panel-head .spacer { flex: 1; }
.panel-body { padding: 20px; }

/* The one panel on a screen that is the actual work. Used on the bill editor's
   item list: a heavier edge and a tinted head, so the eye lands there first. */
.panel--primary {
    border-color: color-mix(in srgb, var(--accent) 32%, var(--line));
    box-shadow: var(--shadow);
}

.panel--primary > .panel-head {
    background: linear-gradient(180deg, var(--accent-wash), var(--surface));
    border-bottom-color: color-mix(in srgb, var(--accent) 24%, var(--line));
    padding-top: 16px;
    padding-bottom: 16px;
}

.panel--primary > .panel-head h2 { font-size: 1.125rem; }

/* Subtotal under the lines it adds up, the way it reads on the printed bill. */
/* Two columns - hints and buttons on the left, the money on the right - on ONE row, because a
   third row of its own is pure scrolling on a screen the owner is already scrolling. The left
   column stretches to the height of the totals and pushes the buttons to its bottom, so Generate
   bill lands level with the Total line instead of floating above it. */
.items-foot {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: stretch;
    gap: 14px 18px;
    padding: 14px 18px 16px;
    border-top: 1px solid var(--line);
    background: var(--surface-2);
}

.items-foot-note {
    grid-column: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.items-foot .totals-list { grid-column: 2; min-width: 230px; }

/* Autosave status. Silent when there is nothing to say, quiet when it saved, and only
   coloured when something did not save - which is the case the owner has to notice. */
.autosave {
    min-height: 1.25rem;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

.autosave[data-state="saved"] { color: var(--ok); }
.autosave[data-state="busy"] { color: var(--text-muted); }

.autosave[data-state="failed"] {
    color: var(--rose);
    font-weight: 600;
}

.items-foot-actions {
    margin: 0;
    margin-top: auto;                 /* sits on the baseline of the row, next to the Total */
    justify-content: flex-start;
}

@media (max-width: 820px) {
    .items-foot { grid-template-columns: minmax(0, 1fr); }
    .items-foot .totals-list { grid-column: 1; }
}

/* A head that carries its own icon, for the few places where the section needs
   naming twice - the dashboard board, mainly. */
.panel-head .head-ico {
    width: 26px;
    height: 26px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 7px;
    background: var(--accent-wash);
    color: var(--accent);
}

/* ========================================================================== */
/* Stat tiles                                                                 */
/* ========================================================================== */

.stat-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
    gap: 14px;
    margin-bottom: 18px;
}

/* Explicit counts where the layout must not reflow into an odd shape. */
.stat-strip--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stat-strip--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* Each tile carries the colour of the thing it counts, so the eye finds
   "overdue" or "still to collect" without reading a word. */
.stat {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: 16px 17px;
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: transform .14s ease, box-shadow .14s ease;
}

.stat:hover { transform: translateY(-2px); box-shadow: var(--shadow); }

/* A wash of the tile's own hue, bled in from the top-right. A soft field, not a
   disc - a hard circle edge read as a rendering artefact rather than as design. */
.stat::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(90% 120% at 100% 0%,
        color-mix(in srgb, var(--stat-accent, var(--line-strong)) 16%, transparent),
        transparent 62%);
    pointer-events: none;
}

.stat::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--stat-accent, var(--line-strong));
}

.stat .label {
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--text-muted);
    display: block;
}

.stat .value {
    font-family: var(--font-display);
    font-size: 2.125rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    margin-top: 2px;
    display: block;
    color: var(--stat-accent, var(--text));
}

.stat.is-money .value { font-size: 1.75rem; }

/* Position in the strip decides the hue, so views stay free of colour decisions. */
.stat-strip .stat:nth-child(1) { --stat-accent: var(--blue); }
.stat-strip .stat:nth-child(2) { --stat-accent: var(--violet); }
.stat-strip .stat:nth-child(3) { --stat-accent: var(--emerald); }
.stat-strip .stat:nth-child(4) { --stat-accent: var(--cyan); }
.stat-strip .stat:nth-child(5) { --stat-accent: var(--amber-deep); }

/* State always beats position. */
.stat.is-alert {
    --stat-accent: var(--rose);
    border-color: #fbcfd8;
    background: linear-gradient(180deg, #fff, var(--rose-wash));
}

.stat.is-due {
    --stat-accent: var(--amber-deep);
    border-color: #f8dfb4;
    background: linear-gradient(180deg, #fff, var(--amber-wash));
}

.stat.is-ok {
    --stat-accent: var(--emerald);
    border-color: #a7f3d0;
    background: linear-gradient(180deg, #fff, var(--emerald-wash));
}

/* ========================================================================== */
/* Attention board                                                            */
/* ========================================================================== */

.board-group + .board-group { margin-top: 2px; }

.board-group-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 13px 18px 6px;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.board-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 18px;
    border-top: 1px solid var(--line);
    border-left: 3px solid transparent;
    transition: background-color .12s ease;
}

.board-row:hover { background: var(--surface-2); }
.board-row.is-overdue { border-left-color: var(--rose); background: linear-gradient(90deg, var(--rose-wash), transparent 42%); }
.board-row.is-today { border-left-color: var(--amber); background: linear-gradient(90deg, var(--amber-wash), transparent 42%); }
.board-row.is-upcoming { border-left-color: var(--line-strong); }

/* Marked as read: acknowledged, still listed, no longer shouting. The left edge keeps
   its due-status colour so the row can still be scanned by urgency. */
.board-row.is-read { background: var(--surface-2); }
.board-row.is-read .who .name,
.board-row.is-read .amount { color: var(--text-muted); font-weight: 500; }
.board-row.is-read .when { font-weight: 500; opacity: .75; }

.board-row .who { min-width: 0; flex: 1; }
.board-row .who .name { font-weight: 600; display: block; }

.board-row .meta {
    font-size: 0.8125rem;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 2px;
}

.board-row .amount {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: right;
    white-space: nowrap;
}

.board-row .when {
    font-family: var(--font-display);
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    white-space: nowrap;
    text-align: right;
    min-width: 108px;
}

.board-row.is-overdue .when { color: var(--rose); font-weight: 700; }
.board-row.is-today .when { color: var(--amber-deep); font-weight: 700; }
.board-row.is-upcoming .when { color: var(--text-muted); }

/* ========================================================================== */
/* Tables                                                                     */
/* ========================================================================== */

.table-app { width: 100%; border-collapse: collapse; }

.table-app thead th {
    font-family: var(--font-display);
    font-size: 0.8125rem;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
    text-align: left;
    padding: 11px 15px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
    white-space: nowrap;
}

.table-app tbody td {
    padding: 12px 15px;
    border-bottom: 1px solid var(--line);
    vertical-align: middle;
}

/* Column widths as classes, so no view needs an inline style. */
.col-sr { width: 42px; }
.col-num { width: 120px; }

/* An invoice number and a date are single tokens - breaking "0001/2026-27"
   across two lines makes the whole row look broken. */
.table-app td .num,
.table-app td.num { white-space: nowrap; font-size: 0.875rem; }
.col-unit { width: 130px; }
.col-money { width: 124px; }
.col-tax { width: 95px; }
.col-action { width: 52px; }

.table-app tbody tr:last-child td { border-bottom: 0; }
.table-app tbody tr { transition: background-color .1s ease; }
.table-app tbody tr:hover { background: var(--accent-wash); }
.table-app .right { text-align: right; }

.table-app .money {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
    letter-spacing: -0.02em;
}

/* Every wide table scrolls inside its own box rather than dragging the page
   sideways - html and body are overflow-x:hidden, so without this the far
   columns would simply be unreachable on a phone. */
.table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
}

/* ========================================================================== */
/* Status tags                                                                */
/* ========================================================================== */

.tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-weight: 600;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid transparent;
    white-space: nowrap;
    line-height: 1.4;
}

/* A dot ahead of the word, so status survives being read in a hurry. */
.tag::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.tag-draft { background: #eef1f5; color: var(--slate); border-color: var(--line-strong); }
.tag-pending { background: var(--blue-wash); color: var(--blue); border-color: #c3d6ff; }
.tag-partial { background: var(--amber-wash); color: var(--amber-deep); border-color: #f8dfb4; }
.tag-paid { background: var(--emerald-wash); color: var(--emerald); border-color: #a7f3d0; }
.tag-overdue { background: var(--rose-wash); color: var(--rose); border-color: #fbcfd8; }

.tag-cancelled {
    background: #eef1f5;
    color: var(--text-faint);
    border-color: var(--line);
    text-decoration: line-through;
}

/* ========================================================================== */
/* Controls                                                                   */
/* ========================================================================== */

.btn {
    --bs-btn-border-radius: var(--radius);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    font-family: var(--font-body);
    font-weight: 550;
    font-size: 0.875rem;
    padding: 8px 15px;
    border-radius: var(--radius);
    transition: background-color .13s ease, border-color .13s ease,
                transform .13s ease, box-shadow .13s ease;
}

.btn-sm { padding: 5px 11px; font-size: 0.8125rem; }

/* A disabled button has to stay readable. Bootstrap's default opacity turned the
   dark buttons into a grey box with grey text on it - it looked broken rather
   than unavailable. Flat, light, obviously inert, and the label still legible. */
.btn:disabled,
.btn.disabled,
a.btn.disabled {
    background: #eef1f5;
    background-image: none;
    border-color: var(--line-strong);
    color: #7c8798;
    opacity: 1;
    box-shadow: none;
    transform: none;
    cursor: not-allowed;
    pointer-events: auto;
}

/* One button hierarchy, used the same way on every screen. Getting this wrong is what made
   Save brown on one dialog and black on the next.

     .btn-accent  the page's single primary action - commit a form, or start the create flow
                  the page exists for. Always --action, never the per-area --accent.
     .btn-ink     a strong secondary: a filter submit (Search, Apply), or an alternative to the
                  primary. Small in-panel buttons are never accent, so they land here or quieter.
     .btn-quiet   everything else, including Cancel.
     .btn-danger-quiet  destructive.

   The same action keeps the same colour wherever it appears, including in an empty state. */
.btn-ink {
    background: linear-gradient(175deg, var(--ink-3), var(--ink));
    border: 1px solid var(--ink);
    color: #fff;
    box-shadow: 0 1px 2px rgb(16 24 40 / 20%);
}

.btn-ink:hover, .btn-ink:focus {
    background: linear-gradient(175deg, #2d3746, #171d26);
    border-color: #000;
    color: #fff;
    transform: translateY(-1px);
    box-shadow: 0 6px 16px -4px rgb(16 24 40 / 40%);
}

.btn-ink:active { transform: translateY(0); box-shadow: none; }

/* The page's own colour, for the one action that page exists to perform. */
.btn-accent {
    background: var(--action);
    border: 1px solid var(--action);
    color: #fff;
    box-shadow: 0 1px 2px rgb(16 24 40 / 16%);
}

/* Every hover state must restate background AND border-color. Bootstrap's own `.btn:hover` sets
   both from --bs-btn-hover-* variables that are only defined for its colour variants; on a bare
   `.btn` they resolve to nothing, which computes as transparent. Leaving them out here turned the
   button invisible on hover, white text on the page behind it. */
.btn-accent:hover, .btn-accent:focus {
    background: var(--action);
    border-color: var(--action);
    color: #fff;
    filter: brightness(1.08);
    transform: translateY(-1px);
    box-shadow: 0 6px 16px -5px var(--action);
}

.btn-accent:active { transform: translateY(0); box-shadow: none; }

.btn-quiet {
    background: var(--surface);
    border: 1px solid var(--line-strong);
    color: var(--text);
}

.btn-quiet:hover {
    background: var(--surface-2);
    border-color: #aab6c6;
    color: var(--text);
}

.btn-danger-quiet {
    background: var(--surface);
    border: 1px solid #fbcfd8;
    color: var(--rose);
}

/* Only the initials show in the bar, so the button is a circle rather than a pill. */
.btn-account {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 8px 5px 5px;
}

/* Who is signed in, above the menu items: context, not something to click. */
.dropdown-account {
    display: block;
    padding: 8px 16px 6px;
    line-height: 1.35;
}

.dropdown-account strong { display: block; color: var(--text); font-size: 0.9375rem; }

.dropdown-account span {
    display: block;
    font-size: 0.8125rem;
    color: var(--text-muted);
    font-family: var(--font-data);
    word-break: break-all;
}

.btn-danger-quiet:hover { background: var(--rose-wash); border-color: #f5b8c5; color: var(--rose); }

/* ------------------------------------------------------------------- fields */

.form-label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    color: #33404f;
    margin-bottom: 5px;
    letter-spacing: 0.005em;
}

.form-label .req { color: var(--rose); margin-left: 2px; }

.form-control, .form-select {
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background-color: var(--surface);
    font-size: 0.9375rem;
    padding: 9px 12px;
    transition: border-color .13s ease, box-shadow .13s ease, background-color .13s ease;
}

/* Room for the chevron. Without this the shorthand above clipped the last letter
   of a narrow select against its own arrow. */
.form-select { padding-right: 2.4rem; }

.form-control::placeholder { color: #a7b1bf; }

.form-control:hover:not(:focus), .form-select:hover:not(:focus) { border-color: #aab6c6; }

.form-control:focus, .form-select:focus {
    border-color: var(--accent);
    background-color: #fff;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

textarea.form-control { line-height: 1.6; }

.form-check-input { width: 1.05em; height: 1.05em; margin-top: 0.22em; cursor: pointer; }
.form-check-input:checked { background-color: var(--accent); border-color: var(--accent); }
.form-check-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
.form-check-label { cursor: pointer; }

.field-hint { font-size: 0.8125rem; color: var(--text-muted); margin-top: 4px; }
.text-danger, .validation-summary-errors { color: var(--rose) !important; font-size: 0.8125rem; }

/* A checkbox that has to sit level with the input beside it, not with its label. */
.field-aligned { padding-top: 27px; }

/* A field that only exists because of the answer to the field above it. Indented
   and washed so the dependency is visible without a sentence explaining it. */
.subfield {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
    padding: 13px 15px;
}

/* ==========================================================================
   Page scaffolding. Every screen uses these three pieces in the same order:
   .page-bar (context + actions) -> .toolbar (filters) -> .panel (content).
   The page title itself lives in the topbar and is never repeated in the body.
   ========================================================================== */

.page-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px 18px;
    flex-wrap: wrap;
    margin-bottom: 18px;
    min-height: 36px;
}

/* Summary line under the title: "12 bills - Rs 1,20,000 outstanding" */
.page-meta {
    display: flex;
    align-items: center;
    gap: 8px 18px;
    flex-wrap: wrap;
    color: var(--text-muted);
    font-size: 0.875rem;
    margin: 0;
}

.page-meta .k {
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-right: 5px;
}

.page-meta strong { color: var(--text); font-weight: 600; }

.page-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* --------------------------------------------------------- summary strip --
   The headline figures for a list page. These were loose grey text sitting on
   the page background, which read as a caption rather than as the numbers the
   page is about. Now one card, split into cells by hairlines, with the page's
   primary action riding on the right of the same card. */

.page-summary {
    position: relative;
    display: flex;
    align-items: stretch;
    flex-wrap: wrap;
    gap: 0;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 0 18px;
    margin-bottom: 18px;
    overflow: hidden;
}

/* The page's colour, as the card's left edge - the same mark as the tab beside
   the page title. */
.page-summary::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--accent);
}

.fig {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 15px 24px 15px 0;
    min-width: 0;
}

.fig + .fig {
    border-left: 1px solid var(--line);
    padding-left: 24px;
}

.fig-ico {
    width: 34px;
    height: 34px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 9px;
    background: color-mix(in srgb, var(--fig-accent, var(--accent)) 12%, transparent);
    color: var(--fig-accent, var(--accent));
}

.fig-ico .i { width: 18px; height: 18px; }

.fig .k {
    display: block;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-muted);
    line-height: 1.3;
}

.fig .v {
    display: block;
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.15;
    color: var(--text);
    white-space: nowrap;
}

.fig .v.owing { color: var(--rose); }
.fig .v.is-ok { color: var(--emerald); }

/* Position decides the hue, exactly as it does for the dashboard tiles, so no
   view has to make a colour decision. */
.page-summary .fig:nth-child(1) { --fig-accent: var(--blue); }
.page-summary .fig:nth-child(2) { --fig-accent: var(--violet); }
.page-summary .fig:nth-child(3) { --fig-accent: var(--emerald); }
.page-summary .fig:nth-child(4) { --fig-accent: var(--amber-deep); }
.page-summary .fig:nth-child(5) { --fig-accent: var(--cyan); }

/* Meaning beats position, exactly as it does on the dashboard tiles. The doubled
   class is deliberate: it has to outrank the :nth-child rules above. */
.page-summary .fig.fig--alert { --fig-accent: var(--rose); }
.page-summary .fig.fig--due { --fig-accent: var(--amber-deep); }
.page-summary .fig.fig--ok { --fig-accent: var(--emerald); }

.page-summary .page-tools {
    margin-left: auto;
    padding: 12px 0 12px 24px;
}

/* When the strip carries the page's Save button it has to stay put, because the
   form underneath it can be taller than the screen. Detected rather than declared:
   a strip only holds a submit when a view puts one there. */
.page-summary:has(button[form]) {
    position: sticky;
    top: var(--topbar-h);
    z-index: 15;
    box-shadow: var(--shadow);
}

@media (max-width: 780px) {
    .page-summary { padding: 0 14px; }
    .fig { padding: 12px 16px 12px 0; }
    .fig + .fig { padding-left: 16px; }
    .fig .v { font-size: 1.25rem; }
    .page-summary .page-tools { padding-left: 0; width: 100%; margin-left: 0; border-top: 1px solid var(--line); }
}

/* Filters always sit in their own bordered strip, never loose on the page. */
.toolbar {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 15px 17px;
    margin-bottom: 18px;
}

/* A fixed 4-column grid, not auto-fit. auto-fit packed as many controls as would
   fit and left the remainder stranded alone on the next row, which read as a
   mistake. Four columns with the search box spanning two means every toolbar
   lands on tidy, full rows. */
.toolbar form,
form.toolbar {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 14px 16px;
    align-items: end;
}

.toolbar .form-label {
    margin-bottom: 4px;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.toolbar .grow { grid-column: span 2; }

/* Buttons always take their own final row, right-aligned, so they never sit
   alone in a stray column. */
.toolbar-actions {
    grid-column: 1 / -1;
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    border-top: 1px solid var(--line);
    padding-top: 14px;
    margin-top: 2px;
}

.toolbar-actions .btn { white-space: nowrap; min-width: 96px; }

.form-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 24px;
}

/* Form buttons ride along the bottom of the screen on a long form, so "Save"
   is never a scroll away. */
.form-actions--sticky {
    position: sticky;
    bottom: 0;
    z-index: 10;
    margin: 0 -4px;
    padding: 14px 4px;
    background: linear-gradient(180deg, rgb(233 237 243 / 0%), var(--paper) 42%);
}

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

@media (max-width: 620px) {
    .toolbar form,
    form.toolbar { grid-template-columns: minmax(0, 1fr); }

    .toolbar .grow { grid-column: span 1; }
    .toolbar-actions .btn { flex: 1; }
    .field-aligned { padding-top: 0; }
}

/* ------------------------------------------------------- detail list, cells */

.detail-list {
    display: grid;
    grid-template-columns: minmax(100px, auto) 1fr;
    gap: 9px 18px;
    margin: 0;
}

.detail-list dt {
    font-family: var(--font-display);
    font-size: 0.8125rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 600;
    padding-top: 2px;
}

.detail-list dd { margin: 0; }

.cell-sub { font-size: 0.8125rem; color: var(--text-muted); margin-top: 1px; }

.table-app .money.owing,
.owing { color: var(--rose); font-weight: 600; }

.note-block {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm);
    padding: 11px 13px;
    font-size: 0.875rem;
    white-space: pre-wrap;
}

.plate-input {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* ========================================================================== */
/* Bill editor + totals                                                       */
/* ========================================================================== */

.filter-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px;
    padding: 17px 19px;
}

.filter-grid .form-label { margin-bottom: 4px; }

/* table-layout: fixed makes the pinned column widths below authoritative, but on a
   narrow screen it also lets the browser crush every column to fit the viewport -
   the inputs then overlap each other. The min-width is the floor: below it the
   .table-wrap scrolls sideways instead of the columns collapsing.
   34 sr + 78 qty + 88 unit + 120 price + 114 amount + 84 actions = 518 fixed,
   plus a readable description, plus 70 more when the tax column is on. */
.items-table { table-layout: fixed; min-width: 760px; }
.items-table.has-tax { min-width: 830px; }

/* The editor is a grid of inputs, not a reading table, so it gets tighter cells
   than the list tables do. `.table-app tbody td` is (0,1,2), so these have to
   carry both classes or the roomier list padding wins and the money columns get
   squeezed until the amounts clip. */
.table-app.items-table tbody td { padding: 4px 4px; vertical-align: middle; }
.table-app.items-table thead th { padding: 8px 4px; letter-spacing: 0.06em; }
.table-app.items-table tbody td:first-child,
.table-app.items-table thead th:first-child { padding-left: 12px; }
.table-app.items-table tbody td:last-child,
.table-app.items-table thead th:last-child { padding-right: 10px; }

/* "Part or work done" holds the longest text on the bill - real lines run to
   "COMBINETION SWITCH AND WIPER LINKEG MOTORS RR LB". Every other column is
   pinned as narrow as its own content allows so the description takes the rest. */
.items-table thead th:nth-child(2) { width: auto; }
/* Widths are measured, not guessed. In Roboto Mono at 15px the widest figure the
   garage will realistically bill - 99,99,999.00 - renders at 97px, and 1,00,557.00
   at 87px. The amount cell therefore needs 106px of inner width and the price
   input 88px; everything else is squeezed to pay for that. */
.items-table .col-sr { width: 34px; }
.items-table .col-num { width: 78px; }
.items-table .col-unit { width: 88px; }
.items-table .col-money { width: 120px; }
.items-table .col-tax { width: 70px; }
.items-table thead th.col-money.right { width: 114px; }

.items-table input,
.items-table select { width: 100%; }

/* Roomy enough to read the whole number at a glance - the narrow boxes were
   cutting values like "0.98" off mid-digit. */
.items-table input,
.items-table select {
    font-size: 0.9375rem;
    padding: 7px 7px;
    height: auto;
}

.items-table .form-select { padding-right: 1.6rem; background-position: right 0.4rem center; }

/* The rupee sign is tucked in close so the digits get the width, not the symbol. */
.items-table .rupee-input .rupee-sign { left: 7px; }
.items-table .rupee-input input { padding-left: 19px !important; padding-right: 5px; }

/* Spinner arrows are dead weight on a price and a quantity - nobody nudges a rate
   up by one rupee at a time - and they were eating the width the digits needed. */
.items-table input[type="number"] { appearance: textfield; -moz-appearance: textfield; }

.items-table input[type="number"]::-webkit-outer-spin-button,
.items-table input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.items-table .qty,
.items-table .rate {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.items-table .sr { color: var(--text-faint); font-family: var(--font-data); font-size: 0.875rem; }

.items-table .line-total {
    font-family: var(--font-data);
    font-weight: 700;
    white-space: nowrap;
    font-size: 0.9375rem;
}

/* Rupee sign sits inside the box so there is no doubt what the number means. */
.rupee-input { position: relative; }

.rupee-input .rupee-sign {
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 0.9375rem;
    pointer-events: none;
}

.rupee-input input { padding-left: 25px !important; }

/* Reorder and remove sit together at the end of the row, in that order: the two
   safe actions first, the destructive one last and set apart by its own hover. */
.row-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0;
}

.btn-row-move,
.btn-row-remove {
    border: 0;
    background: none;
    color: var(--text-faint);
    display: inline-flex;
    padding: 5px 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease, opacity .12s ease;
}

.btn-row-move:hover { background: var(--accent-wash); color: var(--accent); }
.btn-row-remove:hover { background: var(--rose-wash); color: var(--rose); }

/* A finger is not a mouse pointer. On a touch-sized screen these grow to a
   comfortable target - the items table already scrolls, so the extra width
   costs nothing that was not scrollable anyway. */
@media (max-width: 820px) {
    .btn-row-move,
    .btn-row-remove {
        min-width: 34px;
        min-height: 34px;
        padding: 8px;
        align-items: center;
        justify-content: center;
    }

    .items-table .col-rowactions { width: 112px; }
}

/* The first row cannot move up and the last cannot move down. Kept in the layout
   rather than hidden, so the buttons never shift position under the pointer. */
.btn-row-move:disabled {
    opacity: .25;
    cursor: default;
    background: none;
    color: var(--text-faint);
}

.items-table .col-rowactions { width: 84px; }

.totals-list {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px 16px;
    margin: 0;
}

.totals-list dt { color: var(--text-muted); font-weight: 500; }

.totals-list dd {
    margin: 0;
    text-align: right;
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
}

.totals-list dt.grand,
.totals-list dd.grand {
    border-top: 1px solid var(--line);
    padding-top: 9px;
    margin-top: 2px;
    font-weight: 700;
    color: var(--text);
    font-size: 1.1875rem;
}

.totals-list dd.grand { color: var(--accent); }

.totals-panel { position: sticky; top: calc(var(--topbar-h) + 16px); }
.totals-list--narrow { max-width: 340px; margin-left: auto; }

/* ========================================================================== */
/* Step list, settings, WhatsApp                                              */
/* ========================================================================== */

.steps { list-style: none; margin: 0 0 20px; padding: 0; }

.step {
    display: flex;
    gap: 15px;
    padding: 17px 0;
    border-bottom: 1px solid var(--line);
}

.step:last-child { border-bottom: 0; }

.step-num {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1rem;
    display: grid;
    place-items: center;
}

.step-body h3 { font-size: 1.125rem; margin: 3px 0 4px; }
.step-body p { color: var(--text-muted); margin-bottom: 10px; }

.wa-preview {
    background: #e7f6ea;
    border: 1px solid #bfe3c6;
    border-radius: var(--radius);
    padding: 14px 16px;
    font-size: 0.9375rem;
    white-space: pre-wrap;
    margin: 0 0 10px;
}

.wa-editor { font-family: var(--font-data); font-size: 0.875rem; line-height: 1.7; }

.logo-preview {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 13px 17px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-bottom: 14px;
}

.logo-preview img { max-height: 70px; max-width: 260px; display: block; }

.sample-number {
    display: inline-block;
    background: var(--amber-wash);
    border: 1px solid #f8dfb4;
    border-radius: var(--radius);
    padding: 9px 14px;
}

.sample-number .label {
    display: block;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--amber-deep);
}

.sample-number .value {
    display: block;
    font-family: var(--font-data);
    font-size: 1.1875rem;
    font-weight: 700;
    color: var(--text);
    margin-top: 2px;
}

/* Placeholder chips on the WhatsApp template editor. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
    font-family: var(--font-data);
    font-size: 0.75rem;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    background: var(--surface);
    color: #33404f;
    cursor: pointer;
    transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}

.chip:hover { background: var(--ink); color: #fff; border-color: var(--ink); }

.folder-tree {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 13px 15px;
    font-family: var(--font-data);
    font-size: 0.8125rem;
    line-height: 1.7;
    margin: 0 0 12px;
    overflow-x: auto;
    white-space: pre;
}

/* ------------------------------------------------------ report bar chart -- */

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

.bar-row {
    display: grid;
    grid-template-columns: 96px 1fr 112px;
    align-items: center;
    gap: 12px;
}

.bar-label { font-size: 0.8125rem; color: var(--text-muted); white-space: nowrap; }

.bar-track {
    background: #eaeef3;
    border-radius: 5px;
    height: 22px;
    overflow: hidden;
}

.bar-fill {
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--accent) 60%, #fff),
        var(--accent));
    height: 100%;
    border-radius: 5px;
    min-width: 3px;
}

.bar-value {
    text-align: right;
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    font-weight: 600;
}

.error-code {
    font-family: var(--font-display);
    font-size: 4.5rem;
    font-weight: 700;
    color: var(--text-faint);
    line-height: 1;
}

/* ========================================================================== */
/* Pager                                                                      */
/* ========================================================================== */

.pager {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    padding: 12px 18px;
    border-top: 1px solid var(--line);
    background: var(--surface-2);
    font-size: 0.8125rem;
    color: var(--text-muted);
}

.pager-count { white-space: nowrap; }
.pager-count strong { color: var(--text); font-weight: 600; }

.pager-size { display: flex; align-items: center; gap: 4px; }

.pager-size a {
    padding: 3px 9px;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    text-decoration: none;
    font-weight: 500;
}

.pager-size a:hover { background: #e6ebf1; text-decoration: none; }
.pager-size a.on { background: var(--ink); color: #fff; font-weight: 600; }

.pager-pages { margin-left: auto; display: flex; align-items: center; gap: 10px; }

.pager-pages a {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-weight: 600;
}

.pager-pages a:hover { background: var(--accent-wash); border-color: var(--accent); text-decoration: none; }
.pager-pages .off { color: var(--text-faint); padding: 4px 10px; }
.pager-current { font-family: var(--font-display); font-weight: 600; letter-spacing: 0.04em; }

/* ========================================================================== */
/* Empty state, notices                                                       */
/* ========================================================================== */

.empty {
    text-align: center;
    padding: 52px 20px;
    color: var(--text-muted);
}

.empty .empty-ico {
    width: 52px;
    height: 52px;
    margin: 0 auto 14px;
    display: grid;
    place-items: center;
    border-radius: 14px;
    background: var(--accent-wash);
    color: var(--accent);
}

.empty .empty-ico .i { width: 26px; height: 26px; }

.empty .headline {
    font-family: var(--font-display);
    font-size: 1.3125rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 4px;
}

.notice {
    display: flex;
    gap: 11px;
    align-items: flex-start;
    padding: 12px 15px;
    border-radius: var(--radius);
    border: 1px solid;
    border-left-width: 4px;
    font-size: 0.875rem;
    margin-bottom: 18px;
    box-shadow: var(--shadow-sm);
}

.notice .i { margin-top: 0.15em; }
.notice-ok { background: var(--emerald-wash); border-color: #a7f3d0; border-left-color: var(--emerald); color: #04543c; }
.notice-warn { background: var(--amber-wash); border-color: #f8dfb4; border-left-color: var(--amber); color: #7a3f02; }
.notice-error { background: var(--rose-wash); border-color: #fbcfd8; border-left-color: var(--rose); color: #8c0f2f; }

/* ========================================================================== */
/* Login                                                                      */
/* ========================================================================== */

.auth {
    min-height: 100vh;
    display: grid;
    /* minmax(0, ...) - a bare 1fr floors at min-content and would push the card off-screen. */
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    background: var(--paper);
    overflow-x: hidden;
}

.auth-side {
    position: relative;
    overflow: hidden;
    background: linear-gradient(152deg, #10141b 0%, var(--ink) 46%, #232b37 100%);
    color: #fff;
    padding: 52px 56px 56px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 32px;
}

/* Warmth off the top-left corner so the panel is not a flat black rectangle. */
.auth-side::before {
    content: "";
    position: absolute;
    inset: -30% 40% 45% -25%;
    background: radial-gradient(closest-side, rgb(245 158 11 / 20%), transparent 70%);
    pointer-events: none;
}

/* ---- Signature: a tachometer bleeding off the panel edge. Ornament, not a
       readout: no numbers, nothing that could be mistaken for real data. ---- */

.gauge {
    position: absolute;
    right: -110px;
    bottom: -120px;
    width: clamp(280px, 42%, 460px);
    height: auto;
    opacity: .38;
    pointer-events: none;
    z-index: 0;
}

.gauge .track { fill: none; stroke: #39414e; stroke-width: 8; stroke-linecap: round; }
.gauge .sweep { fill: none; stroke: var(--amber); stroke-width: 8; stroke-linecap: round; }
.gauge .tick { stroke: #525b6c; stroke-width: 4; stroke-linecap: round; }
.gauge .tick-hot { stroke: var(--amber); }
.gauge .hub { fill: #12151a; stroke: #525b6c; stroke-width: 3; }
.gauge .needle { fill: var(--amber); }

.gauge .needle-group {
    transform-box: view-box;
    transform-origin: 300px 300px;
    transform: rotate(414deg);
}

/* Content sits above the ornament. Scoped to the content elements only - a bare
   `> *` here would also hit .gauge and cancel its absolute positioning. */
.auth-side > div,
.auth-side > p { position: relative; z-index: 1; }

.auth-side .shop {
    font-family: var(--font-display);
    font-size: clamp(2.5rem, 5vw, 3.75rem);
    font-weight: 700;
    letter-spacing: 0.025em;
    text-transform: uppercase;
    line-height: 1;
}

.auth-side .tagline {
    font-family: var(--font-display);
    font-size: 0.9375rem;
    font-weight: 500;
    letter-spacing: 0.34em;
    text-transform: uppercase;
    color: var(--amber);
    margin-top: 10px;
}

/* Short amber rule: the same mark that flags the active page in the sidebar. */
.auth-side .rule {
    width: 56px;
    height: 3px;
    background: var(--amber);
    margin: 26px 0 22px;
    border-radius: 2px;
}

.auth-side .blurb {
    color: #c7cdd7;
    font-size: 1rem;
    line-height: 1.6;
    max-width: 32ch;
    margin: 0;
}

.auth-side .contact {
    margin: 0;
    font-size: 0.875rem;
    color: var(--text-on-ink-muted);
    line-height: 1.8;
}

.auth-side .contact strong {
    display: block;
    font-family: var(--font-display);
    font-size: 0.8125rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: #7c8698;
    font-weight: 600;
    margin-bottom: 4px;
}

.auth-main { display: grid; place-items: center; padding: 32px; min-width: 0; }
.auth-main--full { min-height: 100vh; }
.auth-card { width: min(100%, 410px); min-width: 0; }
.auth-top { margin-bottom: 4px; }

.auth-card .panel {
    padding: 34px 32px;
    box-shadow: 0 20px 48px rgb(16 24 40 / 13%), 0 2px 6px rgb(16 24 40 / 6%);
}

.auth-card h1 { font-size: 2rem; margin: 0 0 4px; letter-spacing: 0.01em; }
.auth-card .sub { color: var(--text-muted); font-size: 0.9375rem; margin-bottom: 24px; }
.auth-card .form-control { padding: 11px 13px; }

/* Monogram badge: the shop's initials, cut by an amber corner. */
.monogram {
    width: 46px;
    height: 46px;
    border-radius: 11px;
    background: var(--ink);
    color: #fff;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.125rem;
    letter-spacing: 0.06em;
    display: grid;
    place-items: center;
    position: relative;
    overflow: hidden;
    margin-bottom: 18px;
}

.monogram::after {
    content: "";
    position: absolute;
    right: -12px;
    bottom: -12px;
    width: 24px;
    height: 24px;
    background: var(--amber);
    transform: rotate(45deg);
}

.auth-foot {
    text-align: center;
    margin-top: 18px;
    font-size: 0.8125rem;
    color: var(--text-faint);
}

/* ========================================================================== */
/* Motion - one orchestrated entrance, nothing scattered                      */
/* ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
    .gauge .needle-group { animation: sweep 1.5s cubic-bezier(.34, .8, .3, 1) both; }

    @keyframes sweep {
        0% { transform: rotate(225deg); }
        68% { transform: rotate(428deg); }
        100% { transform: rotate(414deg); }
    }

    .fade-up { animation: fadeUp .5s ease-out both; }
    .d1 { animation-delay: .05s; }
    .d2 { animation-delay: .12s; }
    .d3 { animation-delay: .19s; }
    .d4 { animation-delay: .26s; }

    @keyframes fadeUp {
        from { opacity: 0; transform: translateY(10px); }
        to { opacity: 1; transform: none; }
    }

    .auth-card .panel { animation: cardIn .55s cubic-bezier(.2, .7, .3, 1) both .1s; }

    @keyframes cardIn {
        from { opacity: 0; transform: translateY(14px) scale(.985); }
        to { opacity: 1; transform: none; }
    }

    .board-row { animation: rise .28s ease-out both; }
    .board-row:nth-child(2) { animation-delay: .03s; }
    .board-row:nth-child(3) { animation-delay: .06s; }
    .board-row:nth-child(4) { animation-delay: .09s; }
    .board-row:nth-child(n+5) { animation-delay: .12s; }

    @keyframes rise {
        from { opacity: 0; transform: translateY(4px); }
        to { opacity: 1; transform: none; }
    }
}

/* ========================================================================== */
/* Responsive                                                                 */
/* ========================================================================== */

@media (max-width: 1100px) {
    .stat-strip--5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
    .auth { grid-template-columns: minmax(0, 1fr); }
    .auth-side { padding: 30px 22px; gap: 0; }
    .auth-main { padding: 24px 16px 32px; }
    .auth-card { max-width: 100%; }
    .auth-card .panel { padding: 26px 20px; }
    .auth-side .shop { font-size: 2.125rem; }
    .auth-side .blurb,
    .auth-side .contact { display: none; }

    /* The gauge is ornament; on a phone the space belongs to the form. */
    .gauge { display: none; }
}

@media (max-width: 780px) {
    .stat-strip,
    .stat-strip--3,
    .stat-strip--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .sidebar {
        z-index: 60;
        transform: translateX(-100%);
        transition: transform .18s ease-out;
        box-shadow: 0 0 40px rgb(0 0 0 / 35%);
    }

    .sidebar.open { transform: none; }
    .main { margin-left: 0; }
    .sidebar-toggle { display: inline-flex; }
    .content { padding: 16px 14px 44px; }
    .topbar { padding: 0 14px; gap: 10px; }
    .topbar h1 { font-size: 1.1875rem; }

    .board-row { flex-wrap: wrap; gap: 6px 12px; }
    .board-row .when { min-width: 0; text-align: left; }

    .scrim {
        position: fixed;
        inset: 0;
        background: rgb(16 24 40 / 45%);
        z-index: 50;
    }
}

@media (max-width: 600px) {
    .bar-row { grid-template-columns: 78px 1fr 94px; gap: 8px; }
    .stat .value { font-size: 1.75rem; }
    .stat.is-money .value { font-size: 1.375rem; }
}

@media (prefers-reduced-motion: reduce) {
    * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ==========================================================================
   Searchable selects (Tom Select)
   ==========================================================================
   Tom Select ships a generic grey theme. These rules make it indistinguishable
   from .form-select so a searchable customer box and a plain Unit box look like
   the same control, and it picks up the page's --accent like everything else.
   ========================================================================== */

.ts-wrapper { min-width: 0; }

/* Belt and braces for the same problem: if a Bootstrap form class ever reaches the wrapper,
   strip its chrome so only .ts-control draws the box. */
.ts-wrapper.form-select,
.ts-wrapper.form-control {
    padding: 0;
    border: 0;
    background-image: none;
    background-color: transparent;
    box-shadow: none;
    height: auto;
    min-height: 0;
}

.ts-wrapper.single .ts-control,
.ts-wrapper.multi .ts-control {
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    padding: 9px 12px;
    font-family: var(--font-body);
    font-size: 0.9375rem;
    line-height: 1.5;          /* same as .form-control, so the boxes are the same height */
    box-shadow: none;
    min-height: 0;
    transition: border-color .13s ease, box-shadow .13s ease;
}

.ts-wrapper.single .ts-control:hover { border-color: #aab6c6; }

/* The focus ring has to be the page's colour, exactly as a plain input's is. */
.ts-wrapper.single.focus .ts-control,
.ts-wrapper.single .ts-control:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

.ts-wrapper.single .ts-control input { font-size: 0.9375rem; line-height: 1.5; color: var(--text); }

/* Tom Select gives its input size="1", so it renders ~42px wide and clips the placeholder - the
   "Add a saved part..." picker showed only "Add a". Stretch the input while nothing is selected;
   once there is an item, leave the layout alone so the chosen text shows normally. */
.ts-wrapper.single:not(.has-items) .ts-control > input {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
}
.ts-wrapper .ts-control > .item { color: var(--text); line-height: 1.5; }

/* A single select holds exactly one value and must stay one line tall. Tom Select sets
   flex-wrap on the control for the multi-select case; left on, a long label like
   "MR. PRADIP CHAUDHARI - 9673987130" wrapped and grew the field from 43px to 65px,
   pushing it out of line with every other field in the row. */
.ts-wrapper.single .ts-control { flex-wrap: nowrap; overflow: hidden; }

.ts-wrapper.single .ts-control > .item {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The search box must be allowed to shrink, or it forces the item to wrap anyway. */
.ts-wrapper.single .ts-control > input { min-width: 0; flex: 0 1 auto; }
.ts-control input::placeholder { color: #a7b1bf; }

/* The chevron, drawn to match the one Bootstrap puts on a plain select. */
.ts-wrapper.single .ts-control::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 12px;
    width: 12px;
    height: 8px;
    margin-top: -4px;
    background: var(--text-muted);
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    pointer-events: none;
}

.ts-wrapper.single.dropdown-active .ts-control::after { transform: rotate(180deg); }
.ts-wrapper.single .ts-control { padding-right: 32px; }

.ts-wrapper.disabled .ts-control {
    background: #eef1f5;
    border-color: var(--line-strong);
    color: var(--text-faint);
    opacity: 1;
}

/* ------------------------------------------------------------- the dropdown */

.ts-dropdown {
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    margin-top: 4px;
    overflow: hidden;
    font-size: 0.9375rem;
}

.ts-dropdown .option { padding: 8px 12px; color: var(--text); }
.ts-dropdown .active { background: var(--accent-wash); color: var(--accent); }
.ts-dropdown .selected { font-weight: 600; }

/* Tom Select wraps the matched substring in <span class="highlight">. */
.ts-dropdown .highlight {
    background: color-mix(in srgb, var(--amber) 40%, transparent);
    border-radius: 2px;
    padding: 0 1px;
}

.ts-dropdown .no-results { padding: 10px 12px; color: var(--text-muted); }

.ts-dropdown-content { max-height: 320px; }

/* A validation failure must show on the box the user can actually see. */
.ts-wrapper.input-validation-error .ts-control { border-color: var(--rose); }


/* The saved-part suggestions under a bill line. Deliberately the same box as .ts-dropdown so
   the two kinds of picker on this page do not read as two different controls. Positioned against
   the viewport because the items table scrolls, and a list clipped by its own row is useless. */
.part-suggest {
    position: fixed;
    z-index: 60;
    margin: 0;
    padding: 4px;
    list-style: none;
    max-height: 320px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    font-size: 0.9375rem;
}

.part-suggest li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 10px;
    border-radius: calc(var(--radius) - 4px);
    cursor: pointer;
}

.part-suggest .ps-name { color: var(--text); font-weight: 500; }

.part-suggest .ps-meta {
    color: var(--text-muted);
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    white-space: nowrap;
}

/* Keyboard and mouse land on the same highlight, so there is only one idea of "current". */
.part-suggest li.is-on,
.part-suggest li:hover {
    background: var(--accent-wash);
}

.part-suggest li.is-on .ps-name,
.part-suggest li:hover .ps-name { color: var(--accent); }

/* An empty validation summary still rendered its border and padding, which showed up as a
   stray red line above the form. ASP.NET marks the valid state with .validation-summary-valid
   and leaves the element in place, so both cases have to be hidden. */
.notice:empty,
.notice.validation-summary-valid,
.field-validation-valid { display: none; }

/* ==========================================================================
   Confirm dialog
   ==========================================================================
   Replaces window.confirm() for destructive actions. The browser's box cannot be
   themed and shows the origin in its title, which looks alarming on a bill page.
   ========================================================================== */

.confirm-scrim {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: 20px;
    background: rgb(16 24 40 / 55%);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

.confirm-scrim[hidden] { display: none; }

/* Stops the page scrolling behind the dialog. */
body.has-dialog { overflow: hidden; }

.confirm-box {
    width: min(100%, 460px);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 22px 24px 20px;
}

.confirm-head {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-bottom: 10px;
}

.confirm-head h2 {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin: 0;
}

.confirm-ico {
    width: 34px;
    height: 34px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 9px;
    background: var(--rose-wash);
    color: var(--rose);
}

.confirm-ico .i { width: 19px; height: 19px; }

.confirm-text {
    margin: 0 0 18px;
    color: var(--text-muted);
    font-size: 0.9375rem;
    line-height: 1.6;
}

.confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    flex-wrap: wrap;
}

@media (prefers-reduced-motion: no-preference) {
    .confirm-box { animation: confirmIn .16s ease-out both; }

    @keyframes confirmIn {
        from { opacity: 0; transform: translateY(8px) scale(.98); }
        to { opacity: 1; transform: none; }
    }
}

/* --------------------------------------------------------------- notice close

   Every banner can be dismissed; a success banner also clears itself after a few
   seconds (see site.js). Warnings and errors wait to be dismissed on purpose. */

.notice { position: relative; padding-right: 44px; }

.notice-close {
    position: absolute;
    top: 8px;
    right: 8px;
    display: inline-flex;
    padding: 5px;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: inherit;
    opacity: .55;
    cursor: pointer;
    transition: opacity .12s ease, background-color .12s ease;
}

.notice-close:hover { opacity: 1; background: rgb(16 24 40 / 8%); }

.notice.is-going { opacity: 0; transition: opacity .18s ease; }

@media (prefers-reduced-motion: reduce) {
    .notice.is-going { transition: none; }
}


/* The saved-parts picker in the Items header. w-auto collapsed the Tom Select control to 68px
   and clipped its placeholder, which made the whole feature invisible. It needs a real width. */
.parts-picker,
.panel-head .ts-wrapper:has(.parts-picker) {
    min-width: 260px;
}

.panel-head .ts-wrapper { flex: 0 1 300px; min-width: 240px; }

@media (max-width: 780px) {
    .panel-head .ts-wrapper { flex: 1 1 100%; min-width: 0; }
}

/* ------------------------------------------------------- add another line

   A second "Add row" sitting directly under the last item. The one in the panel
   header meant scrolling up on a long bill, clicking, and being thrown back down
   to the new row - so the button is now where your eyes already are. */

.add-line-row td {
    padding: 0 !important;
    border-bottom: 0 !important;
    background: var(--surface-2);
}

.btn-add-line {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    width: 100%;
    padding: 11px 12px;
    border: 0;
    border-top: 1px dashed var(--line-strong);
    background: none;
    color: var(--accent);
    font-family: var(--font-body);
    font-size: 0.875rem;
    font-weight: 550;
    cursor: pointer;
    transition: background-color .12s ease;
}

.btn-add-line:hover { background: var(--accent-wash); }

.btn-add-line:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* ==========================================================================
   Busy states
   ==========================================================================
   Two levels. Every POST button goes quiet and shows a spinner the moment it is
   pressed, which also stops a double submit creating two bills. Anything slow
   enough to look broken - generating a bill renders a PDF and talks to Drive -
   opts in to the full car loader with data-loading on the form.
   ========================================================================== */

.btn.is-busy {
    position: relative;
    pointer-events: none;
    color: transparent !important;
}

.btn.is-busy .i { visibility: hidden; }

.btn.is-busy::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 15px;
    height: 15px;
    margin: -8px 0 0 -8px;
    border: 2px solid currentColor;
    border-radius: 50%;
    border-top-color: transparent;
    color: #fff;
    animation: btnSpin .6s linear infinite;
}

.btn-quiet.is-busy::after { color: var(--text-muted); }

@keyframes btnSpin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------- the car loader */

.loading-overlay {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: grid;
    place-items: center;
    background: rgb(16 24 40 / 55%);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.loading-overlay[hidden] { display: none; }

.loading-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 26px 34px 22px;
    text-align: center;
    min-width: 260px;
}

.loading-text {
    margin: 6px 0 0;
    font-family: var(--font-display);
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--text);
}

.loading-sub {
    margin: 2px 0 0;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/* The car: body in graphite, wheels spinning, road sliding underneath. Ornament with a job -
   it says "still working" without a percentage nobody can trust. */
.car-loader { width: 132px; height: auto; display: block; margin: 0 auto; }

.car-loader .body { fill: var(--ink); }
.car-loader .glass { fill: #7dd3fc; }
.car-loader .lamp { fill: var(--amber); }
.car-loader .tyre { fill: #2a3340; }
.car-loader .hub { fill: var(--amber); }
.car-loader .road { stroke: var(--line-strong); stroke-width: 3; stroke-linecap: round; }

@media (prefers-reduced-motion: no-preference) {
    .car-loader .wheel { animation: wheelSpin .55s linear infinite; transform-box: fill-box; transform-origin: center; }
    .car-loader .shell { animation: carBob 1.1s ease-in-out infinite; }
    .car-loader .road { animation: roadRun .55s linear infinite; }

    @keyframes wheelSpin { to { transform: rotate(360deg); } }
    @keyframes carBob {
        0%, 100% { transform: translateY(0); }
        50% { transform: translateY(-1.5px); }
    }
    @keyframes roadRun { to { stroke-dashoffset: -22; } }
}

/* ==========================================================================
   Add/edit in a dialog
   ==========================================================================
   Small forms - a part, a vehicle - open here instead of taking over the screen.
   The links still point at the real page, so this is an enhancement, not a
   replacement: without JavaScript the full screen still works.
   ========================================================================== */

.modal-form {
    position: fixed;
    inset: 0;
    z-index: 150;
    display: grid;
    place-items: start center;
    padding: 40px 20px;
    overflow-y: auto;
    background: rgb(16 24 40 / 55%);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.modal-form[hidden] { display: none; }

.modal-card {
    width: min(100%, 780px);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.modal-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--line);
    background: linear-gradient(180deg, var(--accent-wash), var(--surface));
}

.modal-head h2 {
    flex: 1;
    min-width: 0;
    margin: 0;
    font-family: var(--font-display);
    font-size: 1.1875rem;
    font-weight: 600;
    letter-spacing: 0.03em;
}

.modal-close {
    flex: none;
    display: inline-flex;
    padding: 6px;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}

.modal-close:hover { background: rgb(16 24 40 / 8%); color: var(--text); }

.modal-body { padding: 20px; }
.modal-loading { margin: 0; padding: 24px 0; text-align: center; color: var(--text-muted); }

/* Inside the dialog the form is the whole content, so its own panel chrome and page
   strip would be a box within a box. */
.modal-body .panel { border: 0; box-shadow: none; border-radius: 0; }
.modal-body .panel-head { display: none; }
.modal-body .panel-body { padding: 0; }
.modal-body .page-summary { display: none; }
.modal-body .form-actions { margin: 18px 0 0; padding-top: 16px; border-top: 1px solid var(--line); }

@media (prefers-reduced-motion: no-preference) {
    .modal-card { animation: modalIn .18s ease-out both; }

    @keyframes modalIn {
        from { opacity: 0; transform: translateY(-10px) scale(.99); }
        to { opacity: 1; transform: none; }
    }
}
