/* ==========================================================================
   Architect App — static utility stylesheet
   --------------------------------------------------------------------------
   No build step. No npm. No Tailwind/PostCSS/Vite. Just a plain CSS file
   served directly from /public/css/app.css and linked with a normal
   <link rel="stylesheet"> tag in the layouts.

   This exists because every Blade view in this app was originally written
   using Tailwind-style utility class names (bg-white, rounded-xl, gap-4,
   text-gray-400, etc.). Rather than hand-rewriting every one of ~130 view
   files to Bootstrap's different class names/spacing scale (error-prone
   without a way to visually verify each page), this file hand-implements
   the specific utility classes actually used across the app as real CSS
   rules with the same names. Bootstrap 5 (loaded via CDN in the layout,
   before this file) supplies base element resets, forms, buttons, and
   anything else you build fresh — this file's classes take precedence
   for anything already in the existing views.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* ==========================================================================
   Design tokens — Architect App visual identity (Step 62 — full repaint)
   --------------------------------------------------------------------------
   Replaced the earlier "blueprint navy + brass" identity with a genuinely
   modern SaaS palette: a vivid indigo-violet primary against cool slate
   neutrals (not warm beige), bigger corner radii, softer/more diffuse
   shadows. This is the same high-leverage approach as before — every
   view already uses these exact class names, so changing the values
   here reskins the whole app without touching a single Blade file.
   ========================================================================== */
:root {
    /* Step 68 — replaced the Step 67 approximation with H.D. Wire's
       EXACT real hex values, extracted directly from their actual
       design-system CSS file rather than guessed.

       Step 69 fix: the H.D. Wire component block pasted in below
       referenced 27 variables (--amber-600, --border, --radius-sm,
       --ink-700, --shadow-sm, --navy-950 …) across ~160 declarations
       that were NEVER defined here. A `var()` pointing at an
       undefined custom property makes the whole declaration invalid,
       so effectively every .btn-*, .card, .badge-*, .form-*,
       .app-sidebar and .app-nav-link rule was silently dead — the
       sidebar had no background at all, buttons no colour. All 27 are
       defined below with H.D. Wire's real values. The navy scale is
       also realigned to their exact steps (previously --navy-900 held
       what is actually their -950 value, which collapsed the
       sidebar's two-stop gradient into a flat fill). */

    /* Navy scale — H.D. Wire exact */
    --navy-950: #0a1a2a;
    --navy-900: #0f2438;
    --navy-800: #17324b;
    --navy-700: #204261;
    --navy-600: #204261;
    --navy-100: #e4eaf1;
    --navy-50:  #e4eaf1;

    /* Amber accent — H.D. Wire exact */
    --amber-600: #c77d2e;
    --amber-500: #db9645;
    --amber-100: #fbebda;

    /* Status colours — H.D. Wire exact */
    --green-600: #1d7a4c;
    --green-100: #dff3e7;
    --blue-600:  #2158a6;
    --blue-100:  #e1eaf7;
    --red-600:   #b7333b;
    --red-100:   #fbe4e5;
    --yellow-700:#8a6414;
    --yellow-100:#fbf0d6;

    /* Ink / neutrals — H.D. Wire exact */
    --ink-900: #101826;
    --ink-700: #3b4657;
    --ink-500: #6b7686;
    --ink-400: #8d96a3;
    --border:        #dde3ea;
    --border-strong: #c3ccd6;

    /* Radii + shadows — H.D. Wire exact */
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --shadow-sm: 0 1px 2px rgba(15,36,56,.06);
    --shadow-md: 0 4px 14px rgba(15,36,56,.08);
    --shadow-lg: 0 12px 32px rgba(15,36,56,.14);

    /* Font stacks referenced by the component block */
    --font-ui: 'Poppins', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

    --brass:      #c77d2e;
    --brass-dark: #8a6414;
    --ink:    #101826;
    --paper:  #f7f8fa;
    --line:   #dde3ea;
    --line-soft: rgba(32,66,97,.07);
    --shadow-card: 0 1px 2px rgba(15,36,56,.06);
    --shadow-card-hover: 0 4px 14px rgba(15,36,56,.08);
    --ease: cubic-bezier(.4,0,.2,1);
    --radius: 10px;

    /* ---- Surface/text/border tokens (dark mode support) ----
       Cool slate/zinc neutrals instead of the earlier warm-tinted
       grays — a deliberately cooler, more neutral "modern app" feel. */
    --surface: #ffffff;
    --surface-alt: #fafafa;
    --surface-alt-2: #f4f4f5;
    --text-primary: #18181b;
    --text-secondary: #3f3f46;
    --text-muted: #71717a;
    --text-faint: #a1a1aa;
    --border-color: #e4e4e7;
    --border-light: #f4f4f5;
    --input-bg: #ffffff;
}

/* ---- Dark theme ----
   Toggled via a `data-theme="dark"` attribute on <html> — see the
   toggle button + script in each layout. Defaults to the visitor's
   OS preference (prefers-color-scheme) on first visit, then
   remembers their explicit choice in localStorage after that. Every
   token above gets a dark-appropriate value; status colors (green/
   red/yellow/blue pills) are deliberately left alone since their
   *-100/*-700 pairings already have enough built-in contrast to read
   fine on a dark surface without a separate override. */
[data-theme="dark"] {
    /* Step 69: the H.D. Wire variables added to :root also need dark
       counterparts, otherwise every .card/.btn-outline/.form-input
       keeps its light-mode border and near-black ink text on a dark
       surface. Accent hues (amber/green/blue/red/yellow) are left at
       their light values deliberately — they already carry enough
       contrast on both backgrounds. */
    --border:        #2d3542;
    --border-strong: #3d4757;
    --ink-900: #f3f4f6;
    --ink-700: #d1d5db;
    --ink-500: #9ca3af;
    --ink-400: #6b7280;
    --navy-100: #cbd5e1;
    --shadow-sm: 0 1px 2px rgba(0,0,0,.3);
    --shadow-md: 0 4px 14px rgba(0,0,0,.4);
    --shadow-lg: 0 12px 32px rgba(0,0,0,.5);

    --navy-50:  #1c2836;
    --ink:      #e5e7eb;
    --paper:    #0f1319;
    --line:     #2d3542;
    --line-soft: rgba(129,163,206,.08);
    --shadow-card: 0 1px 2px rgba(0,0,0,.3), 0 8px 20px -10px rgba(0,0,0,.4);
    --shadow-card-hover: 0 4px 10px -2px rgba(0,0,0,.35), 0 16px 32px -14px rgba(0,0,0,.5);

    --surface: #1a2029;
    --surface-alt: #12161d;
    --surface-alt-2: #232a35;
    --text-primary: #f3f4f6;
    --text-secondary: #d1d5db;
    --text-muted: #9ca3af;
    --text-faint: #6b7280;
    --border-color: #2d3542;
    --border-light: #262d38;
    --input-bg: #1a2029;
}
[data-theme="dark"] body { background-color: var(--paper); }
[data-theme="dark"] ::selection { background: var(--brass); color: #fff; }
[data-theme="dark"] .app-sidebar.border-r,
[data-theme="dark"] .app-sidebar > div.border-b.border-gray-100 {
    background: var(--surface);
    background-image: none;
}
[data-theme="dark"] table thead th { background: var(--surface-alt); color: var(--text-muted); }
[data-theme="dark"] table tbody tr:hover td { background-color: var(--surface-alt); }
[data-theme="dark"] input[type="text"], [data-theme="dark"] input[type="email"],
[data-theme="dark"] input[type="password"], [data-theme="dark"] input[type="number"],
[data-theme="dark"] input[type="date"], [data-theme="dark"] input[type="datetime-local"],
[data-theme="dark"] select, [data-theme="dark"] textarea {
    background-color: var(--input-bg);
    border-color: var(--border-color);
    color: var(--text-primary);
}
[data-theme="dark"] input:disabled, [data-theme="dark"] select:disabled, [data-theme="dark"] textarea:disabled {
    background-color: var(--surface-alt-2);
    color: var(--text-faint);
}
[data-theme="dark"] .app-empty-state { background: var(--surface-alt); border-color: var(--border-color); }
[data-theme="dark"] * { scrollbar-color: #3a4453 transparent; }
[data-theme="dark"] ::-webkit-scrollbar-thumb { background: #3a4453; }

html, body {
    max-width: 100%;
    /* Step 82 fix: this used to be `overflow-x: clip`, chosen in Step 70
       because `overflow-x: hidden` on html/body broke every `position:
       sticky` descendant (any non-'visible' overflow value turns an
       element into a scroll container, and the sidebar/section-rail were
       sticking relative to the wrong box).
       `clip` fixed that, but it's a newer CSS value with inconsistent
       support in older Chromium/WebView builds — exactly the runtime this
       app ships inside per the user's stated deployment plan — and on some
       of those the vertical scrollbar stopped appearing on tall pages
       (Project Detail, being the longest page, was the first to show it).
       Removed entirely: html/body now have NO overflow property on either
       axis, which is the most universally-supported configuration and
       guarantees native scroll behaviour. Horizontal overflow is instead
       contained at the content-wrapper level below (.app-content /
       .proj-content), which is never an ancestor of a sticky element, so
       nothing here can break sticky positioning again. */
}
/* Step 75 — base reset. Bootstrap used to supply this (its Reboot layer
   zeroes body margin and normalises heading/paragraph margins). When
   Bootstrap was removed in Step 72 the browser's DEFAULT 8px body margin
   came straight back, which is the thin white gutter visible along the
   left, top and right edges of every page in the submitted screenshot.
   Nothing else replaced it, so it's declared explicitly here. */
html, body { margin: 0; padding: 0; }
body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    color: var(--ink);
    min-height: 100vh;
}
h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; }
img, svg, video, canvas { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; }
::selection { background: var(--brass); color: #fff; }

/* ---- Preflight-style reset ----
   The views were authored in Tailwind, which zeroes every heading's/
   paragraph's font-size, weight, and margin by default (font-size:
   inherit, margin: 0) and expects an explicit text-*/font-* utility
   class to do the actual sizing. Bootstrap (loaded first) instead
   ships its own large default sizes AND margins for h1–h6 (h1 ≈ 40px,
   h2 ≈ 32px, h3 ≈ 28px, each with margin-bottom: .5rem) plus a 1rem
   bottom margin on every bare <p>. Wherever a view uses a bare
   heading/paragraph without pairing it with a text-*/font-* class —
   which the comment below confirms happens throughout this app —
   Bootstrap's oversized default was winning, which is the actual
   cause of headings/section spacing rendering far bigger than
   designed everywhere in the app, not just one page. */
h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    margin: 0;
}
p, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* Headings pick up bold weight automatically wherever this app's
   established pattern (text-xl/text-lg + font-semibold, or a bare
   heading tag) already appears — no Blade template changes needed
   across ~130 files, since this exact class combination is already
   how every page title/card header in this app is written.
   Step 68 — matches H.D. Wire exactly: a single font family (Inter)
   throughout at different weights, not a separate display face. */
h1, h2, h3,
.text-2xl.font-semibold, .text-xl.font-semibold, .text-lg.font-semibold {
    font-family: 'Inter', -apple-system, sans-serif;
    font-weight: 700;
    letter-spacing: -0.015em;
}
.font-mono, .ref-code {
    font-family: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
    font-variant-numeric: tabular-nums;
}

/* ---- Colors: backgrounds ---- */
.bg-white { background-color: var(--surface); }
.bg-gray-50 { background-color: var(--surface-alt); }
.bg-gray-100 { background-color: var(--surface-alt-2); }
.bg-gray-800 { background-color: #1f2937; }
.bg-gray-900 { background-color: #111827; }
/* "Indigo" is this app's primary brand color throughout — every
   button, link, and active-state badge already uses these exact
   class names, so redefining the values here reskins the entire
   app without touching any of the ~130 view files. Was a generic
   SaaS-template indigo (#17324b); now a deep blueprint navy —
   evoking architectural drafting/blueprint paper, distinct from the
   default. */
.bg-indigo-50 { background-color: #eef3f8; }
.bg-indigo-100 { background-color: #d7e3ef; }
.bg-indigo-600 { background-color: #204261; }
.bg-indigo-700 { background-color: #17324b; }
.bg-green-50 { background-color: #ecfdf5; }
.bg-green-100 { background-color: #d1fae5; }
.bg-green-600 { background-color: #16a34a; }
.bg-green-700 { background-color: #15803d; }
.bg-red-50 { background-color: #fef2f2; }
.bg-red-100 { background-color: #fee2e2; }
.bg-red-600 { background-color: #dc2626; }
.bg-yellow-50 { background-color: #fefce8; }
.bg-yellow-100 { background-color: #fef9c3; }
.bg-blue-50 { background-color: #eff6ff; }
.bg-blue-100 { background-color: #dbeafe; }
.bg-blue-600 { background-color: #204261; }
.bg-orange-50 { background-color: #fff7ed; }

/* ---- Brass accent (new) — the one signature element, used sparingly:
   the sidebar's active nav indicator and the top loading bar only.
   Not a replacement for any existing class; purely additive. ---- */
.bg-brass { background-color: #c77d2e; }
.text-brass { color: #8a6414; }
.border-brass { border-color: #c77d2e; }

/* ---- Dark sidebar (Super-Admin layout) ---- */
.border-gray-800 { border-color: #1f2937; }
.hover\:bg-gray-800:hover { background-color: #1f2937; }
.text-red-400 { color: #f87171; }

/* ---- Colors: text ---- */
.text-white { color: #fff; }
.text-gray-300 { color: #d1d5db; }
.text-gray-400 { color: var(--text-faint); }
.text-gray-500 { color: var(--text-muted); }
.text-gray-600 { color: var(--text-secondary); }
.text-gray-700 { color: var(--text-secondary); }
.text-gray-800 { color: var(--text-primary); }
.text-gray-900 { color: var(--text-primary); }
.text-indigo-600 { color: #204261; }
.text-indigo-700 { color: #17324b; }
.text-green-600 { color: #16a34a; }
.text-green-700 { color: #15803d; }
.text-red-500 { color: #ef4444; }
.text-red-600 { color: #dc2626; }
.text-yellow-600 { color: #ca8a04; }
.text-yellow-700 { color: #a16207; }
.text-blue-600 { color: #204261; }
.text-orange-600 { color: #ea580c; }

/* ---- Borders ---- */
.border { border-width: 1px; border-style: solid; border-color: var(--border-color); }
.border-t { border-top-width: 1px; border-top-style: solid; border-color: var(--border-color); }
.border-b { border-bottom-width: 1px; border-bottom-style: solid; border-color: var(--border-color); }
.border-b-2 { border-bottom-width: 2px; border-bottom-style: solid; border-color: #e5e7eb; }
.border-2 { border-width: 2px; border-style: solid; }
.border-gray-100 { border-color: var(--border-light); }
.border-gray-200 { border-color: var(--border-color); }
.border-gray-300 { border-color: var(--border-color); }
.border-yellow-200 { border-color: #fef08a; }
.border-green-200 { border-color: #bbf7d0; }
.border-red-200 { border-color: #fecaca; }
.border-indigo-600 { border-color: #204261; }
.divide-y > * + * { border-top-width: 1px; border-top-style: solid; }
.divide-gray-100 > * + * { border-color: var(--border-light); }
.divide-gray-200 > * + * { border-color: var(--border-color); }
.last\:border-0:last-child { border-width: 0; }

/* ---- Rounded corners ---- */
.rounded { border-radius: .375rem; }
.rounded-md { border-radius: .5rem; }
.rounded-lg { border-radius: .75rem; }
.rounded-xl { border-radius: 1rem; }
.rounded-full { border-radius: 9999px; }
.rounded-t-md { border-top-left-radius: .375rem; border-top-right-radius: .375rem; }

/* ---- Shadow ---- */
.shadow-sm { box-shadow: var(--shadow-card); }
.shadow-md { box-shadow: var(--shadow-card-hover); }
.hover\:shadow-md:hover { box-shadow: var(--shadow-card-hover); }

/* Cards — any element already combining these three classes (the app's
   established bg-white + rounded-xl + shadow-sm "card" pattern) gets a
   crisp hairline border. Step 63: cards now also lift and deepen their
   shadow on hover — real depth/motion instead of a static flat panel,
   matching the modern-SaaS direction rather than architectural-drafting
   flatness. */
.bg-white.rounded-xl.shadow-sm,
.bg-white.rounded-lg.shadow-sm {
    border: 1px solid var(--line);
    transition: box-shadow .18s var(--ease), transform .18s var(--ease), border-color .18s var(--ease);
}
a.bg-white.rounded-xl.shadow-sm:hover,
.bg-white.rounded-xl.shadow-sm:hover:has(a),
.bg-white.rounded-lg.shadow-sm:hover:has(a) {
    border-color: #c7d3e0;
    box-shadow: var(--shadow-card-hover);
    transform: translateY(-2px);
}

/* ---- Spacing: padding ---- */
.p-1 { padding: .25rem; } .p-2 { padding: .5rem; } .p-3 { padding: .75rem; }
.p-4 { padding: 1rem; } .p-5 { padding: 1.25rem; } .p-6 { padding: 1.5rem; }
.p-8 { padding: 2rem; } .p-10 { padding: 2.5rem; }
.px-1 { padding-left: .25rem; padding-right: .25rem; }
.px-2 { padding-left: .5rem; padding-right: .5rem; }
.px-3 { padding-left: .75rem; padding-right: .75rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
.py-0\.5 { padding-top: .125rem; padding-bottom: .125rem; }
.py-1 { padding-top: .25rem; padding-bottom: .25rem; }
.py-1\.5 { padding-top: .375rem; padding-bottom: .375rem; }
.py-2 { padding-top: .5rem; padding-bottom: .5rem; }
.py-2\.5 { padding-top: .625rem; padding-bottom: .625rem; }
.py-3 { padding-top: .75rem; padding-bottom: .75rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-5 { padding-top: 1.25rem; padding-bottom: 1.25rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
.py-10 { padding-top: 2.5rem; padding-bottom: 2.5rem; }
.pt-2 { padding-top: .5rem; } .pt-3 { padding-top: .75rem; } .pt-4 { padding-top: 1rem; }
.pb-1 { padding-bottom: .25rem; } .pb-3 { padding-bottom: .75rem; } .pb-6 { padding-bottom: 1.5rem; }
.pl-3 { padding-left: .75rem; }
.pl-8 { padding-left: 2rem; }
.px-3\.5 { padding-left: .875rem; padding-right: .875rem; }

/* ---- Spacing: margin ---- */
.m-0 { margin: 0; }
.mx-2 { margin-left: .5rem; margin-right: .5rem; }
.mr-1 { margin-right: .25rem; }
.mr-2 { margin-right: .5rem; }
.mr-3 { margin-right: .75rem; }
.-mx-2 { margin-left: -.5rem; margin-right: -.5rem; }
.mb-1 { margin-bottom: .25rem; } .mb-2 { margin-bottom: .5rem; } .mb-3 { margin-bottom: .75rem; }
.mb-4 { margin-bottom: 1rem; } .mb-5 { margin-bottom: 1.25rem; } .mb-6 { margin-bottom: 1.5rem; }
.mb-0 { margin-bottom: 0; }
.mb-0\.5 { margin-bottom: .125rem; }
.mt-0\.5 { margin-top: .125rem; }
.mt-1 { margin-top: .25rem; } .mt-2 { margin-top: .5rem; } .mt-3 { margin-top: .75rem; }
.mt-4 { margin-top: 1rem; } .mt-5 { margin-top: 1.25rem; } .mt-6 { margin-top: 1.5rem; }
.ml-1 { margin-left: .25rem; }
.ml-2 { margin-left: .5rem; }
.-mb-px { margin-bottom: -1px; }

/* ---- Gap / space-between ---- */
.gap-1 { gap: .25rem; } .gap-1\.5 { gap: .375rem; } .gap-2 { gap: .5rem; } .gap-2\.5 { gap: .625rem; } .gap-3 { gap: .75rem; }
.gap-4 { gap: 1rem; } .gap-5 { gap: 1.25rem; } .gap-6 { gap: 1.5rem; }
.space-y-1 > * + * { margin-top: .25rem; }
.space-y-2 > * + * { margin-top: .5rem; }
.space-y-3 > * + * { margin-top: .75rem; }
.space-y-4 > * + * { margin-top: 1rem; }

/* ---- Flexbox ---- */
.flex { display: flex; }
.inline-flex { display: inline-flex; }
.flex-1 { flex: 1 1 0%; }
.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.justify-end { justify-content: flex-end; }
.justify-start { justify-content: flex-start; }
.shrink-0 { flex-shrink: 0; }
.grow { flex-grow: 1; }

/* ---- Grid ---- */
.grid { display: grid; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.grid-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.col-span-1 { grid-column: span 1 / span 1; }
.col-span-2 { grid-column: span 2 / span 2; }
.col-span-3 { grid-column: span 3 / span 3; }
.col-span-4 { grid-column: span 4 / span 4; }
.col-span-full { grid-column: 1 / -1; }
/* sm: breakpoint (640px) — fills the gap between the mobile-stacked
   default and md:'s 768px jump, e.g. a large phone in landscape or a
   small tablet portrait that can already fit 2 columns. */
@media (min-width: 640px) {
    .sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .sm\:flex { display: flex; }
    .sm\:hidden { display: none; }
    .sm\:block { display: block; }
}
@media (min-width: 768px) {
    .md\:col-span-1 { grid-column: span 1; } .md\:col-span-2 { grid-column: span 2; }
    .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
    .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .lg\:grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
    .lg\:grid-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
    .lg\:col-span-1 { grid-column: span 1; } .lg\:col-span-2 { grid-column: span 2; }
    .xl\:col-span-1 { grid-column: span 1; }
    .xl\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
    .xl\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Mobile-first safety net (Step 54): this whole app was written with
   bare, unprefixed grid-cols-2 through grid-cols-7 throughout ~130
   form and dashboard views (e.g. class="grid grid-cols-3 gap-4"),
   which — without this override — apply at EVERY screen width
   including a narrow phone, cramming 3-7 columns into ~320px and
   making form fields/numbers illegible. This collapses all of them
   to a single stacked column below 640px, site-wide, with zero Blade
   template changes needed. Any view that explicitly opted into
   md:/lg:-prefixed multi-column behavior still gets it at those
   larger breakpoints — only the un-prefixed base classes are
   overridden here. */
@media (max-width: 639px) {
    .grid-cols-2, .grid-cols-3, .grid-cols-4,
    .grid-cols-5, .grid-cols-6, .grid-cols-7 {
        grid-template-columns: repeat(1, minmax(0, 1fr));
    }
    .col-span-2, .col-span-3, .col-span-4 {
        grid-column: 1 / -1;
    }
}

/* ---- Sizing ---- */
.w-full { width: 100%; }
.w-8 { width: 2rem; } .w-16 { width: 4rem; } .w-20 { width: 5rem; }
.w-24 { width: 6rem; } .w-40 { width: 10rem; } .w-60 { width: 15rem; }
.h-8 { height: 2rem; } .h-16 { height: 4rem; } .h-40 { height: 10rem; }
.h-96 { height: 24rem; }
.min-h-screen { min-height: 100vh; }
.max-w-sm { max-width: 24rem; } .max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; } .max-w-2xl { max-width: 42rem; }
.aspect-square { aspect-ratio: 1 / 1; }

/* ---- Typography ---- */
.text-xs { font-size: .75rem; line-height: 1rem; }
.text-sm { font-size: .875rem; line-height: 1.25rem; }
.text-base { font-size: 1rem; line-height: 1.5rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.uppercase { text-transform: uppercase; }
.capitalize { text-transform: capitalize; }
.tracking-widest { letter-spacing: .1em; }
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.underline { text-decoration: underline; }
.hover\:underline:hover { text-decoration: underline; }
.whitespace-nowrap { white-space: nowrap; }
.whitespace-pre-line { white-space: pre-line; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Display / position / overflow ---- */
.block { display: block; }
.inline-block { display: inline-block; }
.hidden { display: none; }
.relative { position: relative; }
.absolute { position: absolute; }
.sticky { position: sticky; }
.top-1 { top: .25rem; }
.right-1 { right: .25rem; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }
.overflow-hidden { overflow: hidden; }

/* ---- Misc ---- */
.cursor-pointer { cursor: pointer; }
.transition { transition: all .15s ease-in-out; }
.opacity-0 { opacity: 0; }
.opacity-50 { opacity: .5; }
.opacity-100 { opacity: 1; }
.disabled\:opacity-50:disabled { opacity: .5; }
.object-cover { object-fit: cover; }
.animate-pulse { animation: app-pulse 1.5s cubic-bezier(.4,0,.6,1) infinite; }
@keyframes app-pulse { 0%,100% { opacity: 1; } 50% { opacity: .5; } }
.group:hover .group-hover\:opacity-100 { opacity: 1; }

/* ---- Interactive states ---- */
a { color: inherit; text-decoration: none; }
/* Step 70 fix: these were hardcoded light hex, so every hover in dark
   mode flashed a near-white block. Tokenised so they follow the theme. */
.hover\:bg-gray-50:hover  { background-color: var(--surface-alt); }
.hover\:bg-gray-100:hover { background-color: var(--surface-alt-2); }
.hover\:bg-gray-200:hover { background-color: var(--border); }
.hover\:bg-indigo-700:hover { background-color: #17324b; }
.hover\:bg-green-700:hover { background-color: #15803d; }
.hover\:bg-red-100:hover { background-color: #fee2e2; }
.hover\:text-gray-700:hover { color: #374151; }
/* Step 57 fix: browsers apply a native border/background/font to bare
   <button> elements by default (visible as an ugly gray 3D-ish outline
   on any button whose utility classes don't happen to cover every one
   of those properties — most visibly on tab strips and any button
   relying only on text-color/border-bottom, like the tab nav). This
   reset was missing this whole time. Utility classes like .px-4/.py-2
   still win where used (class selectors beat element selectors
   regardless of source order), so nothing else needs to change. */
button {
    cursor: pointer;
    border: none;
    background: none;
    font: inherit;
    color: inherit;
    padding: 0;
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
}

/* ---- Form elements ---- */
input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="datetime-local"], input[type="file"], select, textarea {
    border: 1px solid var(--border-color);
    border-radius: .5rem;
    padding: .5rem .75rem;
    font-size: .875rem;
    width: 100%;
    background-color: var(--input-bg);
    color: var(--text-primary);
}
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: #204261;
    box-shadow: 0 0 0 3px rgba(32,66,97,.15);
}
input[type="checkbox"], input[type="radio"] {
    border: 1px solid var(--border-color);
    border-radius: .25rem;
    width: 1.05rem;
    height: 1.05rem;
    accent-color: #204261;
    cursor: pointer;
    flex-shrink: 0;
}
.focus\:ring-indigo-500:focus { box-shadow: 0 0 0 3px rgba(32,66,97,.18); }
.focus\:border-indigo-500:focus { border-color: #204261; }
input:disabled, select:disabled, textarea:disabled {
    background-color: var(--surface-alt-2);
    color: var(--text-faint);
    cursor: not-allowed;
}
textarea { resize: vertical; }

/* ---- Mobile form + touch-target fixes ----
   Two real bugs on phones: (1) iOS Safari auto-zooms the whole page
   the instant you focus any input/select/textarea with a font-size
   under 16px — every form in this app is 14px (.875rem), so every
   field tap on an iPhone was zooming and needing a manual zoom-out.
   (2) buttons/links sized only by their padding (py-1, py-1.5) land
   under Apple/Google's ~44px minimum touch-target guidance, which is
   the #1 cause of mis-taps on a phone screen. Both are scoped to
   <640px only, so desktop/tablet density is untouched. */
@media (max-width: 639px) {
    input[type="text"], input[type="email"], input[type="password"], input[type="number"],
    input[type="date"], input[type="datetime-local"], input[type="file"], select, textarea {
        font-size: 16px;
    }
    button, a.btn, .btn {
        min-height: 44px;
    }
    input[type="checkbox"], input[type="radio"] {
        width: 1.25rem;
        height: 1.25rem;
    }
}

/* ---- Extra fractional/utility fallbacks ---- */
.px-1\.5 { padding-left: .375rem; padding-right: .375rem; }
.py-0 { padding-top: 0; padding-bottom: 0; }

/* ==========================================================================
   Responsive app shell — sidebar mobile/desktop behavior now lives
   entirely in the H.D. Wire component system section below (its own
   .app-sidebar rule for desktop, plus the @media(max-width:960px)
   block for the mobile off-canvas drawer). The previous unconditional
   `.app-sidebar { transform: translateX(-100%) }` here applied at
   EVERY width with no override left to cancel it for desktop after
   this file's Step 68 cleanup — removed entirely rather than left as
   a conflicting duplicate.
   ========================================================================== */

/* .app-sidebar-overlay — still genuinely needed by super-admin.blade.php,
   which has its own separate, independent sidebar toggle implementation
   (a different layout file from the main app, with its own JS function
   and element IDs) that predates the H.D. Wire adoption and was never
   touched by it. Removing this without checking cross-layout usage
   first would silently break Super-Admin's mobile overlay backdrop —
   restored here, scoped generically so both layouts can use it. */
.app-sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.4);
    z-index: 150;
}
.app-sidebar-overlay.is-open { display: block; }

/* Sticky, not just static-in-flow: without this the topbar scrolls
   away with the page content on a long list/form, which is what made
   the mobile header feel "off" — it should stay pinned like the
   hamburger/brand bar users expect on every screen. */
.app-mobile-topbar {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 20;
}
.app-main-content {
    padding: 1rem;
    background-color: var(--paper);
    background-image:
        linear-gradient(var(--line-soft) 1px, transparent 1px),
        linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
    background-size: 28px 28px;
    min-height: 100vh;
}

/* Tablet and up: sidebar becomes a normal static column, topbar/hamburger
   and overlay disappear entirely */
@media (min-width: 768px) {
    /* Step 68 cleanup: this block previously also set .app-sidebar/
       .app-sidebar-overlay/.app-mobile-topbar/.app-main-content rules,
       which are now all orphaned dead weight — the H.D. Wire component
       rebuild replaced every one of those classes (.app-sidebar now
       has its own real desktop/mobile behavior below, the topbar is
       .app-topbar, content wrapper is .app-content). Only the
       md\:* utility classes are still genuinely used (17 places
       across other views) and stay here. */

    /* Step 57 fix: these two classes were used throughout the sidebar
       header markup (the mobile-only "Menu" bar with its close button,
       and the desktop-only company-name/notification-bell bar) but
       were never actually defined anywhere in this file — meaning
       neither responsive swap ever worked. The mobile header showed
       at EVERY width including full desktop, and the desktop header
       (relying on .hidden + .md\:flex to re-show itself) never
       appeared at any width at all, since nothing overrode .hidden's
       display:none once the screen got wider. */
    .md\:hidden { display: none; }
    .md\:flex { display: flex; }
    .md\:block { display: block; }
    .md\:grid { display: grid; }
}

/* ---- H.D. Wire sidebar: mobile off-canvas behavior (Step 68 fix) ----
   The component system adopted from H.D. Wire (above) defined the
   sidebar's *desktop* look (sticky, always visible, 240px) and the
   mobile toggle/close BUTTONS' visibility — but never actually wrote
   the sidebar's own off-canvas slide-in/out behavior below 960px, nor
   the JS function every one of those buttons calls. Both were
   completely missing, meaning the mobile menu was fully broken: the
   hamburger button would throw "appToggleSidebar is not defined" the
   moment it was clicked. Uses H.D. Wire's own 960px breakpoint
   (not this app's older 768px) for consistency with the rest of
   their system. */
/* (The mobile off-canvas rules that used to live here were moved to the
   end of this file — they were being overridden by .app-sidebar's own
   `position: sticky` declared further down, which left a dead 240px
   column on phones. See "Responsive shell" at the bottom.) */

/* Sidebar brand/logo row — was inheriting the plain .text-lg.font-semibold
   size (18px) at full Space Grotesk display weight, which reads oversized
   for a compact 64px header strip next to the notification bell. Scoped
   to just the sidebar so page titles elsewhere that legitimately use
   text-lg are untouched. */
.app-sidebar .text-lg.font-semibold {
    font-size: .9375rem;
    letter-spacing: -0.005em;
}

/* ---- Invalid-input styling (Step 50) — pairs with @error() in forms ---- */
.is-invalid {
    border-color: #ef4444 !important;
    box-shadow: 0 0 0 1px rgba(239,68,68,.2);
}

/* ---- Notification bell badge ---- */
.notif-badge {
    position: absolute;
    top: -2px; right: -2px;
    background: #dc2626;
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    line-height: 1;
    padding: 2px 5px;
    border-radius: 9999px;
    min-width: 16px;
    text-align: center;
}

/* ==========================================================================
   Global loading indicators (Step 52) — a top progress bar for ANY
   Livewire request, plus a spinner utility for individual buttons via
   wire:loading. No JS framework needed — driven by Livewire's own
   livewire:navigate/request lifecycle hooks (see the script block in
   each layout) toggling a CSS class.
   ========================================================================== */

#app-loading-bar {
    position: fixed;
    top: 0; left: 0;
    height: 3px;
    width: 0%;
    background: linear-gradient(90deg, #17324b, #c77d2e);
    z-index: 99999;
    opacity: 0;
    box-shadow: 0 0 8px rgba(32,66,97,.5);
}
/* Instant, obvious jump to 30% the moment a request starts (visible
   even for a request that finishes in 100ms), then a much slower
   creep toward 85% for anything that takes longer — never quite
   reaching 100% on its own, since only an actual completion should
   do that (see .is-done below). This mirrors how NProgress/GitHub's
   own top-loading-bar behaves. */
#app-loading-bar.is-active {
    width: 30%;
    opacity: 1;
    transition: width 0.3s ease-out, opacity 0.15s ease-in;
}
#app-loading-bar.is-active.is-creeping {
    width: 85%;
    transition: width 6s cubic-bezier(0.1, 0.5, 0.1, 1), opacity 0.15s ease-in;
}
#app-loading-bar.is-done {
    width: 100%;
    opacity: 1;
    transition: width 0.2s ease-out;
}
#app-loading-bar.is-hidden {
    opacity: 0;
    transition: opacity 0.3s ease-in 0.15s;
}

/* Button-level spinner: put <span class="app-btn-spinner"></span> inside
   any button, then wire:loading.class="is-loading" on that same button —
   the text fades slightly and the spinner appears, without needing a
   separate wire:loading element per action. */
.app-btn-spinner {
    display: none;
    width: 14px; height: 14px;
    border: 2px solid rgba(255,255,255,.4);
    border-top-color: #fff;
    border-radius: 50%;
    animation: app-spin .6s linear infinite;
    margin-right: 6px;
    vertical-align: -2px;
}
button.is-loading .app-btn-spinner { display: inline-block; }
button.is-loading { opacity: 0.75; cursor: wait; pointer-events: none; }
@keyframes app-spin { to { transform: rotate(360deg); } }

/* Full-panel loading overlay — wrap a card/section in
   <div wire:loading.class="app-panel-loading" class="relative">...</div>
   to dim it and show a centered spinner while that specific
   component/action is processing. */
.app-panel-loading { opacity: 0.5; pointer-events: none; position: relative; }
.app-panel-loading::after {
    content: '';
    position: absolute; inset: 0;
    display: flex;
    width: 24px; height: 24px;
    margin: auto;
    border: 3px solid #e5e7eb;
    border-top-color: #204261;
    border-radius: 50%;
    animation: app-spin .6s linear infinite;
}

/* ==========================================================================
   Button polish (Step 56) — subtle lift + deeper shadow on hover for
   primary/danger action buttons, and a visible focus ring for keyboard
   navigation. Applies automatically to every button already using
   these exact class combinations throughout the app — no template
   changes needed.
   ========================================================================== */
.bg-indigo-600, .bg-green-600, .bg-red-600 {
    transition: transform .12s ease-out, box-shadow .12s ease-out, background-color .12s ease-out;
}
/* Step 63 — pill-shaped action buttons. Overrides whatever rounded-*
   class the button also carries (rounded-lg, rounded-md, etc.) with
   a fully rounded pill, a strong and immediately recognizable
   "modern SaaS" signal distinct from the previous boxier look.
   Scoped to real <button>/<a> elements only, so cards/inputs sharing
   the same rounded-* classes are unaffected. */
button.bg-indigo-600, a.bg-indigo-600,
button.bg-green-600, a.bg-green-600,
button.bg-red-600, a.bg-red-600 {
    border-radius: 9999px !important;
    padding-left: 1.375em;
    padding-right: 1.375em;
}
button.bg-indigo-600:hover, a.bg-indigo-600:hover,
button.bg-green-600:hover, a.bg-green-600:hover,
button.bg-red-600:hover, a.bg-red-600:hover {
    transform: translateY(-1px);
    box-shadow: 0 6px 16px -4px rgba(32,66,97,.4);
}
button.bg-indigo-600:active, a.bg-indigo-600:active,
button.bg-green-600:active, a.bg-green-600:active,
button.bg-red-600:active, a.bg-red-600:active {
    transform: translateY(0);
    box-shadow: none;
}
/* Visible keyboard focus outline on interactive elements — accessible
   and consistent with the new brand color, since browsers' bare
   default focus ring looked inconsistent with everything else here. */
button:focus-visible, a:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: 2px solid #204261;
    outline-offset: 2px;
}

/* Android/Chrome shows a gray flash on every tap by default (buttons,
   cards, list rows) — harmless on desktop, but on mobile it fights
   with the app's own hover/active states defined above. */
button, a, .cursor-pointer {
    -webkit-tap-highlight-color: transparent;
}

/* ==========================================================================
   Visual polish pass — buttons, status pills, sidebar, tables, scrollbars
   ========================================================================== */

/* Primary/success/danger buttons get a faint top-highlight gloss instead
   of a flat fill — reads as a considered, physical surface (like a brass
   fitting) rather than a generic flat SaaS button. Layers on top of the
   existing background-color, so nothing else about them changes. */
.bg-indigo-600, .bg-green-600, .bg-red-600 {
    background-image: linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,0) 60%);
    border: 1px solid rgba(0,0,0,.06);
}

/* ---- Status pills ----
   The app's established pattern for a status/label pill is a *-100
   background paired with a *-700/*-600 text color at text-xs font-medium
   (lead status, task status, payment status, etc.). Wherever that exact
   pairing appears, add a small solid dot and tighten the type — turns a
   plain tinted rectangle into a real status indicator. */
.bg-green-100.text-green-700, .bg-yellow-100.text-yellow-700,
.bg-red-100.text-red-600, .bg-blue-100.text-blue-600,
.bg-indigo-100.text-indigo-700 {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-weight: 600;
    letter-spacing: .01em;
}
.bg-green-100.text-green-700::before, .bg-yellow-100.text-yellow-700::before,
.bg-red-100.text-red-600::before, .bg-blue-100.text-blue-600::before,
.bg-indigo-100.text-indigo-700::before {
    content: '';
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

/* ==========================================================================
   Sidebar — full visual system
   ========================================================================== */

/* Container: subtle depth on the right edge instead of a flat 1px line,
   like a raised drafting panel rather than a plain divider. */
.app-sidebar.border-r {
    background: rgba(255,255,255,.85);
    backdrop-filter: blur(12px);
    border-right: 1px solid var(--line);
    box-shadow: 6px 0 24px -18px rgba(20,30,45,.25);
}

/* Header/brand strip — the two "h-16" (desktop) / "h-14" (mobile) bars
   both use border-b border-gray-100 in the markup; giving them a faint
   letterhead gradient (rather than flat white) makes the brand row read
   as a distinct block from the nav below it, the way a real letterhead
   sits above a page. */
.app-sidebar > div.border-b.border-gray-100 {
    background: linear-gradient(180deg, #fff 0%, var(--paper) 100%);
    border-color: var(--line);
}
.app-sidebar .text-lg.font-semibold {
    font-size: .9375rem;
    letter-spacing: -0.005em;
    color: var(--ink);
}

/* Nav rhythm + type — 13px/tight tracking is the modern-sidebar standard
   (Linear/Notion/Vercel all sit at 13–14px here), a notch calmer than
   the 14px body-text size used in the rest of the app. */
.app-sidebar nav {
    padding-top: .5rem;
    padding-bottom: .5rem;
    /* Fade the very top/bottom of the scroll area instead of an abrupt
       hard cut when the nav list is taller than the viewport. */
    mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
}
.app-sidebar nav a {
    font-size: .8125rem;
    letter-spacing: .01em;
    position: relative;
    transition: background-color .15s var(--ease), color .15s var(--ease), transform .1s var(--ease);
}
.app-sidebar nav a:active { transform: scale(.98); }

/* Step 71: the previous "icon chip" block here (a light rounded box behind
   each nav icon, plus a light gradient fill on the active item) was written
   for the OLD light sidebar. Against the current dark navy rail it painted
   a near-white square behind every icon on hover — which is exactly the
   "icons go white on hover" report. Removed; the dark rail's own
   .app-nav-link rules already handle hover and active state. */
.app-sidebar nav svg { width: 18px; height: 18px; flex-shrink: 0; }


/* Section labels ("Master Data", "Firm Admin") — the app's established
   text-xs + font-semibold + text-gray-400 + uppercase + tracking-wide
   combo, refined with a hairline rule so each group reads as its own
   block rather than a slightly darker line of text sitting in the list. */
.app-sidebar nav p.uppercase {
    font-size: .6875rem;
    letter-spacing: .08em;
    border-top: 1px solid var(--line);
    margin-top: .5rem !important;
    padding-top: 1rem !important;
}

/* Logout row */
.app-sidebar .border-t.border-gray-100 {
    border-color: var(--line);
}
.app-sidebar .border-t.border-gray-100 button {
    border-radius: .5rem;
    transition: background-color .15s var(--ease);
}

/* ---- Custom scrollbars ----
   Thin, quiet, brass-tinted on hover — a small crafted detail instead of
   the default chunky OS scrollbar, on the sidebar nav and any scrollable
   panel/modal in the app. */
* { scrollbar-width: thin; scrollbar-color: #cbd3dd transparent; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: #cbd3dd;
    border-radius: 9999px;
    border: 2px solid transparent;
    background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--brass); }

/* ---- Tables ----
   No baseline table styling existed anywhere — every list page (leads,
   projects, tasks, clients, reports) was rendering raw unstyled <table>
   markup. This is the missing default: quiet uppercase header, zebra
   rows, and a hover highlight so scanning a long list is easy on a
   phone screen or a desktop monitor alike. */
table { width: 100%; border-collapse: collapse; font-size: .875rem; color: var(--text-primary); }
table thead th {
    text-align: left;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-alt);
    padding: .625rem .875rem;
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
table tbody td {
    padding: .625rem .875rem;
    border-bottom: 1px solid var(--line);
    vertical-align: middle;
}
table tbody tr:hover td { background-color: var(--surface-alt); }
table tbody tr:last-child td { border-bottom: none; }

/* ---- Empty states ----
   Opt-in helper for "no data yet" panels (no leads, no tasks this week,
   empty search results) — a dashed drafting-frame instead of a blank
   white box. Wrap content in <div class="app-empty-state">. */
.app-empty-state {
    border: 1.5px dashed #cbd3dd;
    border-radius: .75rem;
    padding: 2.5rem 1.5rem;
    text-align: center;
    color: #6b7280;
    background: rgba(248,249,251,.6);
}
.app-empty-state svg { color: #9ca3af; margin: 0 auto .75rem; }

/* ==========================================================================
   Responsive data tables — every admin list (RF/PSR/clients/tasks/BOQ-style
   tables) is a wide <table>. On a phone a plain table just gets crushed or
   forces horizontal scroll on the whole page. Wrap the <table> in
   <div class="table-responsive-x">...</div> to scope the scroll to just
   the table, with a visible thin scrollbar as a hint there's more columns,
   and momentum scrolling on iOS.
   ========================================================================== */
.table-responsive-x {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
}
.table-responsive-x::-webkit-scrollbar { height: 6px; }
.table-responsive-x::-webkit-scrollbar-thumb {
    background: #d1d5db;
    border-radius: 9999px;
}
.table-responsive-x table { width: 100%; }

/* Sticky header for long tables — pairs with a scrollable container
   (table-responsive-x or a tall overflow-y-auto card) so column
   headers stay visible while scrolling dozens of rows. */
.table-sticky-head thead th {
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 1;
}

/* ==========================================================================
   Page container — nothing currently caps content width, so on a large
   desktop monitor form fields/tables can stretch edge-to-edge and become
   hard to scan. Wrap page content in <div class="app-container">.
   ========================================================================== */
.app-container {
    width: 100%;
    max-width: 1280px;
    margin-left: auto;
    margin-right: auto;
}

/* ==========================================================================
   Safe-area insets (notches / gesture bars) — needed anywhere a bar is
   pinned to the top or bottom of the screen in the Android WebView build
   (mobile topbar, any fixed bottom action bar), so content doesn't sit
   under the status bar or the gesture-nav strip on edge-to-edge devices.
   ========================================================================== */
.app-mobile-topbar {
    padding-top: env(safe-area-inset-top, 0px);
}
.app-safe-bottom {
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* ==========================================================================
   Motion + print
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}

/* Architect app exports (BOQ, quotes, site reports) get printed a lot —
   hide chrome that makes no sense on paper. */
@media print {
    .app-sidebar, .app-mobile-topbar, .app-sidebar-overlay,
    #app-loading-bar, button, .no-print {
        display: none !important;
    }
    .app-main-content { padding: 0 !important; }
    body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
/* ==========================================================================
   Tab-strip scroll affordance (Step 68) — Project Detail's 13-tab strip
   was overflowing with no visual indication there were more tabs to the
   right (they just got cut off at the viewport edge). A right-edge fade
   gradient signals "scroll for more" the same way iOS/many apps do.
   ========================================================================== */
.app-tab-scroll-wrap { overflow: hidden; }
.app-tab-scroll { scrollbar-width: none; -ms-overflow-style: none; }
.app-tab-scroll::-webkit-scrollbar { display: none; }
.app-tab-scroll-fade {
    position: absolute;
    top: 0; right: 0; bottom: 8px;
    width: 2.5rem;
    background: linear-gradient(90deg, transparent, var(--surface-alt) 70%);
    pointer-events: none;
}
[data-theme="dark"] .app-tab-scroll-fade {
    background: linear-gradient(90deg, transparent, var(--paper) 70%);
}

/* ==========================================================================
   H.D. Wire component system (Step 68) — adopted from this firm's other
   product for real cross-tool visual consistency, per direct request.
   These are genuine BEM-style component classes (.app-sidebar__nav,
   .card__header, .btn-primary, .badge-draft, .form-input, etc.) — a
   different architecture from this app's existing atomic utility
   classes (.bg-white, .rounded-xl, .shadow-sm), and intentionally
   layered alongside them rather than replacing them, since neither
   set of class names collides with the other. The shared app shell
   (sidebar + topbar) below has been rebuilt to actually use these
   real classes; individual page content still uses the existing
   utility classes for now — converting every one of ~130 views to
   this component system would be a much larger, separate undertaking
   (see the README note on scope).
   ========================================================================== */
/* ==========================================================================
   App Shell — Sidebar Layout
   ========================================================================== */

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

.app-sidebar {
  width: 240px;
  flex-shrink: 0;
  background: linear-gradient(180deg, var(--navy-950) 0%, var(--navy-900) 100%);
  color: var(--navy-100);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  box-shadow: 2px 0 16px rgba(0,0,0,0.15);
}

.app-sidebar__brand {
  padding: 24px 20px 20px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  position: relative;
}

.app-sidebar__brand::after {
  content: '';
  position: absolute;
  left: 20px;
  bottom: -1px;
  width: 32px;
  height: 2px;
  background: var(--amber-600);
}

.app-sidebar__brand-name {
  font-weight: 700;
  font-size: 15px;
  color: #fff;
  letter-spacing: 0.01em;
}

.app-sidebar__brand-sub {
  font-size: 10.5px;
  color: var(--navy-100);
  opacity: 0.5;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-top: 3px;
}

.app-sidebar__nav {
  flex: 1;
  overflow-y: auto;
  padding: 16px 12px;
}

.app-sidebar__section-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--navy-100);
  opacity: 0.35;
  padding: 16px 10px 7px;
}

.app-nav-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  color: var(--navy-100);
  opacity: 0.72;
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  margin-bottom: 2px;
  position: relative;
  transition: background 0.15s ease, opacity 0.15s ease, padding-left 0.15s ease;
}

.app-nav-link:hover {
  background: rgba(255,255,255,0.055);
  opacity: 1;
  text-decoration: none;
  padding-left: 16px;
}

.app-nav-link.is-active {
  background: rgba(219, 150, 69, 0.14);
  color: #fff;
  opacity: 1;
  font-weight: 600;
}

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

.app-sidebar__footer {
  padding: 16px 20px 20px;
  border-top: 1px solid rgba(255,255,255,0.08);
  font-size: 12px;
  color: var(--navy-100);
  opacity: 0.6;
}

.app-main {
  flex: 1;
  min-width: 0;
}

.app-topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 18px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.app-topbar__title {
  font-size: 19px;
  font-weight: 700;
  color: var(--ink-900);
  letter-spacing: -0.01em;
}

.app-content {
  padding: 22px 28px 48px;
  max-width: 1200px;
  /* Step 82: horizontal overflow containment lives here instead of on
     html/body — this wrapper is never an ancestor of a sticky element,
     so it can safely use overflow without any risk to position: sticky
     or to native vertical scrollbar behaviour. */
  overflow-x: hidden;
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.2s ease;
}

.card__body { padding: 22px 24px; }

.card__header {
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.card__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-900);
}

.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 18px 20px;
  position: relative;
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }

.stat-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--border-strong);
}

.stat-card__label {
  font-size: 11.5px;
  color: var(--ink-500);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.stat-card__value {
  font-family: var(--font-mono);
  font-size: 28px;
  font-weight: 600;
  color: var(--navy-900);
  margin-top: 6px;
}

.stat-card--accent::before { background: var(--amber-600); }
.stat-card--green::before { background: var(--green-600); }
.stat-card--blue::before { background: var(--blue-600); }

/* ==========================================================================
   Document Group Cards — signature element (customs "stamp" top bar)
   Used on shipment show page: Export Customs / COO / LC groups
   ========================================================================== */

.doc-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.doc-group:hover { box-shadow: var(--shadow-md); }

.doc-group__bar {
  height: 5px;
  background-repeat: repeat-x;
  background-size: 14px 5px;
}

.doc-group--customs .doc-group__bar { background-color: var(--green-600); }
.doc-group--coo .doc-group__bar { background-color: var(--blue-600); }
.doc-group--lc .doc-group__bar { background-color: var(--amber-600); }

.doc-group__body { padding: 20px 22px; }

.doc-group__header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}

.doc-group__icon {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.doc-group__icon svg { width: 18px; height: 18px; stroke-width: 1.8; fill: none; }

.doc-group--customs .doc-group__icon { background: var(--green-100); color: var(--green-600); }
.doc-group--coo .doc-group__icon { background: var(--blue-100); color: var(--blue-600); }
.doc-group--lc .doc-group__icon { background: var(--amber-100); color: var(--amber-600); }

.doc-group__title { font-size: 15px; font-weight: 700; color: var(--navy-950); }
.doc-group__subtitle { font-size: 12px; color: var(--ink-500); margin-top: 1px; }

.doc-group__desc {
  font-size: 12.5px;
  color: var(--ink-500);
  margin: 10px 0 14px;
  line-height: 1.5;
}

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

.doc-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--paper);
  color: var(--ink-700);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  transition: all 0.15s ease;
}
.doc-chip:hover {
  background: var(--navy-900);
  border-color: var(--navy-900);
  color: #fff;
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
.doc-chip svg { width: 14px; height: 14px; stroke-width: 2; fill: none; }

/* ==========================================================================
   Empty States
   ========================================================================== */

.empty-state {
  text-align: center;
  padding: 44px 24px;
  color: var(--ink-500);
}
.empty-state__icon {
  width: 44px; height: 44px;
  margin: 0 auto 12px;
  color: var(--ink-400);
}
.empty-state__title { font-size: 14px; font-weight: 600; color: var(--ink-700); margin-bottom: 4px; }
.empty-state__desc { font-size: 12.5px; }

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  font-weight: 600;
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: all 0.15s ease;
  line-height: 1.2;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn:active { transform: translateY(0); box-shadow: none; }
.btn svg { width: 15px; height: 15px; stroke-width: 2; fill: none; }

.btn-primary { background: var(--navy-900); color: #fff; }
.btn-primary:hover { background: var(--navy-800); color: #fff; }

.btn-accent { background: var(--amber-600); color: #fff; }
.btn-accent:hover { background: var(--amber-500); color: #fff; }

.btn-outline { background: var(--surface); color: var(--ink-700); border-color: var(--border-strong); }
.btn-outline:hover { background: var(--paper); color: var(--ink-900); }

.btn-success { background: var(--green-600); color: #fff; }
.btn-success:hover { background: #176B42; color: #fff; }

.btn-info { background: var(--blue-600); color: #fff; }
.btn-info:hover { background: #1B4A8E; color: #fff; }

.btn-warning { background: #C99A1F; color: #fff; }
.btn-warning:hover { background: #B0860F; color: #fff; }

.btn-danger { background: var(--red-600); color: #fff; }
.btn-danger:hover { background: #9C2A31; color: #fff; }

.btn-success-outline { background: var(--green-100); color: var(--green-600); border-color: #B4E1C6; }
.btn-success-outline:hover { background: #CDEBDA; color: var(--green-600); }

.btn-info-outline { background: var(--blue-100); color: var(--blue-600); border-color: #B9CCE9; }
.btn-info-outline:hover { background: #D3E1F4; color: var(--blue-600); }

.btn-warning-outline { background: var(--yellow-100); color: var(--yellow-700); border-color: #EBD69A; }
.btn-warning-outline:hover { background: #F5E4B8; color: var(--yellow-700); }

.btn-danger-outline { background: var(--red-100); color: var(--red-600); border-color: #F0BFC2; }
.btn-danger-outline:hover { background: #F8D2D4; color: var(--red-600); }

.btn-block { width: 100%; justify-content: center; }
.btn-lg { padding: 12px 22px; font-size: 14.5px; }

.btn-danger-text { color: var(--red-600); font-weight: 600; font-size: 13px; background: none; border: none; cursor: pointer; }
.btn-danger-text:hover { text-decoration: underline; }

.btn-link-text { color: var(--blue-600); font-weight: 600; font-size: 13px; text-decoration: none; }
.btn-link-text:hover { text-decoration: underline; }

.btn-sm { padding: 6px 12px; font-size: 12.5px; }

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

.table-pro {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.table-pro thead th {
  background: var(--navy-950);
  color: var(--navy-100);
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  text-align: left;
  padding: 11px 16px;
}

.table-pro tbody td {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  font-size: 13.5px;
  color: var(--ink-700);
}

.table-pro tbody tr:hover { background: var(--paper); }

.table-pro tbody tr:last-child td:first-child { border-bottom-left-radius: var(--radius-md); }
.table-pro tbody tr:last-child td:last-child { border-bottom-right-radius: var(--radius-md); }

/* ==========================================================================
   Status Badges — "seal stamp" signature
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 11px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 1px solid transparent;
}

.badge::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background: currentColor;
}

.badge-draft { background: var(--yellow-100); color: var(--yellow-700); border-color: #EBD69A; }
.badge-sealed { background: var(--blue-100); color: var(--blue-600); border-color: #B9CCE9; }
.badge-shipped { background: #E4E9F7; color: var(--navy-700); border-color: #C6D0EE; }
.badge-completed { background: var(--green-100); color: var(--green-600); border-color: #B4E1C6; }

/* Stamp badge — larger, used on the shipment show page header */
.stamp {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 16px;
  border: 1.5px solid currentColor;
  border-radius: var(--radius-sm);
  font-size: 11.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transform: rotate(-1.5deg);
  position: relative;
}
.stamp::before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px solid currentColor;
  border-radius: 4px;
  opacity: 0.5;
}
.stamp-draft { color: var(--yellow-700); }
.stamp-sealed { color: var(--blue-600); }
.stamp-shipped { color: var(--navy-700); }
.stamp-completed { color: var(--green-600); }

/* ==========================================================================
   Forms
   ========================================================================== */

.form-label {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-700);
  margin-bottom: 6px;
}

.form-label__required {
  color: var(--red-600);
  font-size: 13px;
}

.form-section-title {
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--navy-800);
  margin: 30px 0 16px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 9px;
}
.form-section-title:first-child { margin-top: 0; }

.form-section-title__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--navy-900);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  flex-shrink: 0;
}

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  font-size: 13.5px;
  color: var(--ink-900);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

.form-input:hover,
.form-select:hover,
.form-textarea:hover {
  border-color: var(--ink-400);
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
  outline: none;
  border-color: var(--amber-600);
  box-shadow: 0 0 0 3.5px rgba(199, 125, 46, 0.14);
  background: #fff;
}

.form-input::placeholder, .form-textarea::placeholder { color: var(--ink-400); }

.form-input:disabled, .form-select:disabled { background: var(--paper); color: var(--ink-400); cursor: not-allowed; }

.form-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%236B7686' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

.form-hint { font-size: 12px; color: var(--ink-500); margin-top: 5px; }
.form-error { font-size: 12px; color: var(--red-600); margin-top: 5px; font-weight: 500; }

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

@media (max-width: 768px) {
  .form-grid--2, .form-grid--3, .form-grid--4 { grid-template-columns: 1fr; }
}

/* Form shell wrapping the whole page form */
.form-shell {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.form-shell__body { padding: 28px 30px; }

.form-shell__actions {
  padding: 16px 30px;
  background: var(--paper);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  position: sticky;
  bottom: 0;
}

/* ==========================================================================
   Alerts
   ========================================================================== */

.alert {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  font-weight: 500;
  border: 1px solid transparent;
  margin-bottom: 18px;
}

.alert-success { background: var(--green-100); color: var(--green-600); border-color: #B4E1C6; }
.alert-error { background: var(--red-100); color: var(--red-600); border-color: #F0BFC2; }

/* ==========================================================================
   Repeatable Row Group (items / containers / coils in forms)
   ========================================================================== */

.row-group {
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px 16px 16px 44px;
  margin-bottom: 12px;
  background: var(--paper);
  position: relative;
  transition: border-color 0.15s ease;
}
.row-group:hover { border-color: var(--border-strong); }

.row-group__num {
  position: absolute;
  left: 14px;
  top: 16px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--navy-900);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.row-group__remove {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--red-600);
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: none;
  border: none;
  transition: background 0.15s ease;
}
.row-group__remove:hover { background: var(--red-100); }
.row-group__remove svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 2; fill: none; }

.add-row-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--navy-800);
  background: var(--navy-100);
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 9px 16px;
  cursor: pointer;
  transition: all 0.15s ease;
}
.add-row-btn:hover { background: #D8E1EC; border-color: var(--navy-700); }
.add-row-btn svg { width: 15px; height: 15px; stroke-width: 2.2; fill: none; }

/* ==========================================================================
   Pagination (Laravel default markup passthrough)
   ========================================================================== */

.pagination-wrap nav { font-size: 13px; }

/* ==========================================================================
   Table Action Buttons — global, used in every index/show table row
   ========================================================================== */

.table-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-end;
}

.action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-500);
  cursor: pointer;
  text-decoration: none;
  transition: all 0.15s ease;
  flex-shrink: 0;
}

.action-btn svg {
  width: 15px;
  height: 15px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
}

.action-btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}

/* View — neutral navy */
.action-btn--view:hover {
  background: var(--navy-100);
  border-color: var(--navy-700);
  color: var(--navy-900);
}

/* Edit — amber accent */
.action-btn--edit:hover {
  background: var(--amber-100);
  border-color: var(--amber-600);
  color: var(--amber-600);
}

/* Delete — red, and slightly different resting state to signal caution */
.action-btn--delete {
  color: var(--red-600);
  border-color: #F0BFC2;
}
.action-btn--delete:hover {
  background: var(--red-100);
  border-color: var(--red-600);
  color: var(--red-600);
}

/* Wide variant — icon + label, for primary row actions like "Manage Coils" */
.action-btn--wide {
  width: auto;
  padding: 0 12px;
  font-size: 12.5px;
  font-weight: 600;
}

/* Delete forms sit inline with the icon buttons without breaking the flex row */
.table-actions form { display: inline-flex; margin: 0; }



/* ==========================================================================
   Additions for app.blade.php / sidebar.blade.php update
   Append to app-custom.css
   ========================================================================== */

/* Nav link icons */
.app-nav-link svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  flex-shrink: 0;
}

/* Mobile hamburger toggle — hidden on desktop, shown under 960px */
.sidebar-toggle-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-700);
  cursor: pointer;
  flex-shrink: 0;
}
.sidebar-toggle-btn svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
}
.sidebar-toggle-btn:hover {
  background: var(--paper);
  border-color: var(--border-strong);
}

/* Mobile sidebar close button (X) inside the sidebar itself */
.sidebar-close-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  border: none;
  background: rgba(255,255,255,0.08);
  color: var(--navy-100);
  cursor: pointer;
  flex-shrink: 0;
}
.sidebar-close-btn svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
}
.sidebar-close-btn:hover { background: rgba(255,255,255,0.15); }

/* Backdrop shown behind the sidebar on mobile when open */
.sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(10, 26, 42, 0.5);
  z-index: 150; /* below .app-sidebar's z-index (--z-sticky: 200) */
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.sidebar-backdrop.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* Cleaned-up logout button — replaces the inline-styled version */
.sidebar-logout-btn {
  font-size: 12px;
  font-weight: 600;
  color: var(--navy-100);
  opacity: 0.75;
  background: none;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  cursor: pointer;
  font-family: var(--font-ui);
  transition: all 0.15s ease;
  flex-shrink: 0;
}
.sidebar-logout-btn:hover {
  opacity: 1;
  background: rgba(255,255,255,0.08);
  border-color: rgba(255,255,255,0.25);
}

@media (max-width: 960px) {
  .sidebar-toggle-btn { display: inline-flex; }
  .sidebar-close-btn { display: inline-flex; }
  .sidebar-backdrop { display: block; }
}
/* ==========================================================================
   Project Detail — two-column shell with a vertical section rail
   --------------------------------------------------------------------------
   Replaces the previous horizontal 13-tab strip. Thirteen tabs never fit
   one line at any sensible width; the wrapped version that replaced the
   scrolling one ate three rows of vertical space and still read as an
   undifferentiated wall of links. A grouped vertical rail is scannable,
   leaves the content column clean, and scales if more sections get added.
   Collapses to a horizontal scroll strip below 1024px so it doesn't eat
   a phone screen.
   ========================================================================== */

.proj-shell {
  display: grid;
  grid-template-columns: 208px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

.proj-rail {
  position: sticky;
  top: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 8px;
  box-shadow: var(--shadow-sm);
}

.proj-rail__label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-400);
  padding: 12px 10px 5px;
  margin: 0;
}
.proj-rail__label:first-child { padding-top: 4px; }

.proj-rail__link {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  background: none;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-700);
  text-align: left;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.proj-rail__link:hover { background: var(--paper); color: var(--ink-900); }

.proj-rail__link.is-active {
  background: var(--navy-50);
  color: var(--navy-900);
  font-weight: 600;
  position: relative;
}
.proj-rail__link.is-active::before {
  content: '';
  position: absolute;
  left: -8px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 17px;
  background: var(--amber-600);
  border-radius: 0 3px 3px 0;
}

/* Live count badge (open tasks, drawings awaiting approval) — surfaces
   what needs attention without opening the section first. */
.proj-rail__count {
  margin-left: auto;
  font-size: 10.5px;
  font-weight: 700;
  background: var(--amber-100);
  color: var(--amber-600);
  border-radius: 999px;
  padding: 1px 7px;
  line-height: 1.5;
}

.proj-content { min-width: 0; }

/* Clickable "needs attention" chips on the Overview */
.proj-alert {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 13px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform .14s ease, box-shadow .14s ease;
}
.proj-alert:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.proj-alert--red   { background: var(--red-100);   color: var(--red-600);   border-color: #F0BFC2; }
.proj-alert--amber { background: var(--amber-100); color: var(--amber-600); border-color: #EBD69A; }

/* Tablet/phone: rail becomes a horizontal scroll strip above the content */
@media (max-width: 1023px) {
  .proj-shell { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .proj-rail {
    position: static;
    display: flex;
    gap: 4px;
    overflow-x: auto;
    padding: 8px;
    scrollbar-width: none;
  }
  .proj-rail::-webkit-scrollbar { display: none; }
  .proj-rail__label { display: none; }
  .proj-rail__link { width: auto; white-space: nowrap; flex-shrink: 0; }
  .proj-rail__link.is-active::before { display: none; }
}

/* ==========================================================================
   Dark mode — status colour surfaces (Step 70)
   --------------------------------------------------------------------------
   The tinted status classes (bg-green-50/100, bg-red-50, bg-yellow-50,
   bg-blue-50, bg-indigo-50 …) were all hardcoded pale hex with no dark
   counterpart, so every badge, alert row and highlighted panel stayed a
   near-white block on a dark page — the single biggest reason dark mode
   looked half-finished. Each gets a deep, desaturated equivalent plus a
   lifted text colour so contrast holds both ways.
   ========================================================================== */
[data-theme="dark"] .bg-indigo-50  { background-color: #16283c; }
[data-theme="dark"] .bg-indigo-100 { background-color: #1b3550; }
[data-theme="dark"] .bg-green-50   { background-color: #0f2a1e; }
[data-theme="dark"] .bg-green-100  { background-color: #143a29; }
[data-theme="dark"] .bg-red-50     { background-color: #2e1416; }
[data-theme="dark"] .bg-red-100    { background-color: #3d1a1d; }
[data-theme="dark"] .bg-yellow-50  { background-color: #2b2410; }
[data-theme="dark"] .bg-yellow-100 { background-color: #3a3115; }
[data-theme="dark"] .bg-blue-50    { background-color: #12243a; }
[data-theme="dark"] .bg-blue-100   { background-color: #17304d; }
[data-theme="dark"] .bg-orange-50  { background-color: #2e1e10; }

[data-theme="dark"] .text-green-600, [data-theme="dark"] .text-green-700 { color: #4ade80; }
[data-theme="dark"] .text-red-500,   [data-theme="dark"] .text-red-600   { color: #f87171; }
[data-theme="dark"] .text-yellow-600,[data-theme="dark"] .text-yellow-700{ color: #fbbf24; }
[data-theme="dark"] .text-blue-600  { color: #60a5fa; }
[data-theme="dark"] .text-orange-600{ color: #fb923c; }
[data-theme="dark"] .text-indigo-600,[data-theme="dark"] .text-indigo-700{ color: #93b4d6; }

[data-theme="dark"] .border-green-200 { border-color: #1d5138; }
[data-theme="dark"] .border-red-200    { border-color: #5a2429; }
[data-theme="dark"] .border-yellow-200 { border-color: #55491f; }

/* H.D. Wire component surfaces that also need dark treatment */
[data-theme="dark"] .card,
[data-theme="dark"] .stat-card,
[data-theme="dark"] .proj-rail        { background: var(--surface); border-color: var(--border); }
[data-theme="dark"] .btn-outline      { background: var(--surface); color: var(--ink-700); border-color: var(--border-strong); }
[data-theme="dark"] .btn-outline:hover{ background: var(--surface-alt-2); color: var(--ink-900); }
[data-theme="dark"] .proj-alert--red  { background: #3d1a1d; border-color: #5a2429; color: #f87171; }
[data-theme="dark"] .proj-alert--amber{ background: #3a2a15; border-color: #5c4522; color: #e0a458; }
[data-theme="dark"] .proj-rail__count { background: #3a2a15; color: #e0a458; }
[data-theme="dark"] .proj-rail__link.is-active { background: #16283c; color: #dce7f2; }

/* ==========================================================================
   Density, motion & polish pass (Step 70)
   --------------------------------------------------------------------------
   Tighter spacing, entrance animation on content, richer shadows, and
   webview-friendly touches (no tap-highlight, momentum scrolling, safe-area
   padding) since this ships inside a mobile/tablet WebView shell.
   ========================================================================== */

/* ---- Compactness: the app was built at desktop-dashboard spacing, which
   wastes a lot of room on a tablet/phone webview. Trim the card padding
   and vertical rhythm without making it cramped. ---- */
.app-content { padding: 18px 22px 40px; }
.p-6 { padding: 1.15rem; }
.p-5 { padding: 1rem; }
.px-6 { padding-left: 1.15rem; padding-right: 1.15rem; }
.py-6 { padding-top: 1.15rem; padding-bottom: 1.15rem; }
.mb-5 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.15rem; }
.gap-4 { gap: .85rem; }

/* ---- Motion: content fades/rises in on load and on every Livewire
   section swap, so switching sections reads as a transition rather than
   an instant hard cut. Respects prefers-reduced-motion. ---- */
@keyframes appFadeUp {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.app-content > * { animation: appFadeUp .28s var(--ease) both; }
.proj-content > * { animation: appFadeUp .24s var(--ease) both; }

/* Staggered entrance for grids of cards */
.app-card-grid > *:nth-child(1) { animation-delay: .02s; }
.app-card-grid > *:nth-child(2) { animation-delay: .05s; }
.app-card-grid > *:nth-child(3) { animation-delay: .08s; }
.app-card-grid > *:nth-child(4) { animation-delay: .11s; }
.app-card-grid > *:nth-child(5) { animation-delay: .14s; }
.app-card-grid > *:nth-child(6) { animation-delay: .17s; }
.app-card-grid > * { animation: appFadeUp .3s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  .app-content > *, .proj-content > *, .app-card-grid > * { animation: none; }
}

/* ---- Record cards: the shared look for every list that used to be a
   table (Leads, Tasks, Clients, Team, Attendance, Companies). ---- */
.rec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}

.rec-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px;
  text-decoration: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
  position: relative;
  /* Step 75: was `overflow: hidden`, which clipped any tooltip rendered
     above the card. The rounded left accent bar is what needed clipping,
     so that's handled with a border-radius on the ::before itself instead
     and the card is left un-clipped. */
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s var(--ease);
}
.rec-card::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--border-strong);
  transition: background .16s var(--ease);
}
.rec-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
  text-decoration: none;
  color: inherit;
}
.rec-card:hover::before { background: var(--amber-600); }

.rec-card--green::before  { background: var(--green-600); }
.rec-card--red::before    { background: var(--red-600); }
.rec-card--amber::before  { background: var(--amber-600); }
.rec-card--blue::before   { background: var(--blue-600); }

.rec-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.rec-card__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-900);
  line-height: 1.35;
}
.rec-card__sub {
  font-size: 12px;
  color: var(--ink-500);
  margin-top: 2px;
}

/* Icon + text meta rows inside a record card */
.rec-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.rec-meta__item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--ink-500);
}
.rec-meta__item svg {
  width: 13px; height: 13px;
  stroke: currentColor; fill: none;
  stroke-width: 1.9;
  flex-shrink: 0;
  opacity: .8;
}
.rec-meta__item strong { color: var(--ink-700); font-weight: 600; }

/* Avatar bubble used across record cards (people, clients, team) */
.rec-avatar {
  width: 34px; height: 34px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, var(--navy-700), var(--navy-900));
  flex-shrink: 0;
}

/* ---- WebView / touch device niceties ---- */
* { -webkit-tap-highlight-color: transparent; }
body {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: none;
}
.app-content, .proj-rail { padding-bottom: max(1rem, env(safe-area-inset-bottom)); }
@media (hover: none) {
  /* Kill lift-on-hover on touch devices — it sticks after a tap and
     looks like a stuck state rather than feedback. */
  .rec-card:hover, .proj-alert:hover, .btn:hover { transform: none; }
  .rec-card:active { transform: scale(.995); }
}

/* ---- Phone density ---- */
@media (max-width: 640px) {
  .app-content { padding: 14px 14px 32px; }
  .rec-grid { grid-template-columns: 1fr; gap: 10px; }
  .p-6, .p-5 { padding: .9rem; }
}

/* Sidebar nav scrollbar — the default light scrollbar looked wrong on the
   dark navy rail, and on short viewports the nav genuinely does need to
   scroll (13+ links plus two section headers). */
.app-sidebar__nav { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.22) transparent; }
.app-sidebar__nav::-webkit-scrollbar { width: 6px; }
.app-sidebar__nav::-webkit-scrollbar-track { background: transparent; }
.app-sidebar__nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); border-radius: 999px; }
.app-sidebar__nav::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.3); }

/* Nav links get a subtle icon treatment consistent with the record cards */
.app-nav-link svg { opacity: .85; flex-shrink: 0; }
.app-nav-link.is-active svg { opacity: 1; }

/* ==========================================================================
   Missing sizing / positioning utilities (Step 71)
   --------------------------------------------------------------------------
   Found by sweeping every w-/h-/z-/inset- class used across the views
   against what this file actually defines. 22 were referenced but never
   declared. The visible symptom was the notification dropdown: it uses
   `w-80 z-50 mt-2`, none of which existed, so it had no width and no
   stacking context — it rendered as a full-bleed unstyled block behind
   other content the moment you clicked the bell.
   ========================================================================== */
.w-5  { width: 1.25rem; }   .h-5  { height: 1.25rem; }
.w-6  { width: 1.5rem; }    .h-6  { height: 1.5rem; }
.w-7  { width: 1.75rem; }   .h-7  { height: 1.75rem; }
.w-11 { width: 2.75rem; }   .h-11 { height: 2.75rem; }
.w-32 { width: 8rem; }      .h-14 { height: 3.5rem; }
.w-80 { width: 20rem; }
.w-100 { width: 100%; }
.w-px { width: 1px; }
.h-full { height: 100%; }
.h-screen { height: 100vh; }
.max-w-3xl { max-width: 48rem; }
.min-w-0 { min-width: 0; }

.inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
.top-5 { top: 1.25rem; }
.right-0 { right: 0; }
.bottom-0 { bottom: 0; }
.z-50 { z-index: 50; }

/* The dropdown itself: give it a real surface + elevation so it reads as a
   floating panel in both themes rather than inheriting page background. */
.notif-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: 20rem;
  max-width: calc(100vw - 24px);
  max-height: 420px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 300;
  animation: appFadeUp .16s var(--ease) both;
}
@media (max-width: 480px) {
  /* On a phone/WebView an anchored 320px panel can overflow the viewport —
     pin it to the screen edges instead. */
  .notif-dropdown { position: fixed; left: 12px; right: 12px; top: 64px; width: auto; }
}

/* ==========================================================================
   Filter bar (Step 71)
   --------------------------------------------------------------------------
   The list filters were bare grids of unlabelled inputs — on Leads that
   meant eight identical boxes with no indication of what each one did until
   you clicked it. This gives every control a small icon-led label, groups
   them on one wrapping row, and surfaces an active-filter count with a
   one-tap clear.
   ========================================================================== */
.filter-bar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px;
  margin-bottom: 14px;
}

.filter-bar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
}
.filter-bar__title {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-500);
}
.filter-bar__title svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; }

.filter-bar__clear {
  font-size: 12px;
  font-weight: 600;
  color: var(--red-600);
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  transition: background .14s var(--ease);
}
.filter-bar__clear:hover { background: var(--red-100); }

.filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 10px 12px;
}
.filter-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.filter-field--wide { grid-column: span 2; }

.filter-field__label {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-500);
  letter-spacing: .01em;
}
.filter-field__label svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2; opacity: .75; }

.filter-field input,
.filter-field select {
  font-size: 13px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-900);
  width: 100%;
  transition: border-color .14s var(--ease), box-shadow .14s var(--ease);
}
.filter-field input:focus,
.filter-field select:focus {
  border-color: var(--navy-700);
  box-shadow: 0 0 0 3px rgba(32,66,97,.12);
  outline: none;
}

.filter-count {
  font-size: 10.5px;
  font-weight: 700;
  background: var(--amber-100);
  color: var(--amber-600);
  border-radius: 999px;
  padding: 1px 7px;
}

@media (max-width: 640px) {
  .filter-grid { grid-template-columns: 1fr 1fr; }
  .filter-field--wide { grid-column: span 2; }
}

/* ==========================================================================
   Dashboard (Step 71)
   ========================================================================== */
.dash-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.dash-head__title {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink-900);
  margin: 0;
}
.dash-head__sub { font-size: 13px; color: var(--ink-500); margin: 3px 0 0; }

/* KPI tiles — each is a link into the list it summarises, with a tinted
   icon chip and a coloured top edge so the four read as distinct at a
   glance rather than four identical grey boxes. */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.kpi {
  position: relative;
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 16px 16px 14px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s var(--ease);
}
.kpi::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--border-strong);
}
.kpi:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
  text-decoration: none;
  color: inherit;
}
.kpi__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  border-radius: var(--radius-sm);
  margin-bottom: 10px;
  background: var(--surface-alt-2);
  color: var(--ink-500);
}
.kpi__value {
  display: block;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink-900);
  line-height: 1.15;
}
.kpi__label {
  display: block;
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-500);
  margin-top: 3px;
}
.kpi--navy::before  { background: var(--navy-700); }
.kpi--navy  .kpi__icon { background: var(--navy-50);   color: var(--navy-700); }
.kpi--blue::before  { background: var(--blue-600); }
.kpi--blue  .kpi__icon { background: var(--blue-100);  color: var(--blue-600); }
.kpi--green::before { background: var(--green-600); }
.kpi--green .kpi__icon { background: var(--green-100); color: var(--green-600); }
.kpi--red::before   { background: var(--red-600); }
.kpi--red   .kpi__icon { background: var(--red-100);   color: var(--red-600); }
.kpi--amber::before { background: var(--amber-600); }
.kpi--amber .kpi__icon { background: var(--amber-100); color: var(--amber-600); }

[data-theme="dark"] .kpi { background: var(--surface); border-color: var(--border); }
[data-theme="dark"] .kpi__icon { background: var(--surface-alt-2); }

@media (max-width: 640px) {
  .kpi-row { grid-template-columns: 1fr 1fr; gap: 10px; }
  .kpi { padding: 13px 13px 11px; }
  .kpi__value { font-size: 19px; }
  .dash-head__title { font-size: 18px; }
}

/* ==========================================================================
   Site cards (Projects list) — Step 71
   --------------------------------------------------------------------------
   For an architecture practice the building is the fastest identifier, so
   each site leads with its own gallery cover photo rather than a text row.
   Falls back to a blueprint-grid tile when no photo exists yet.
   ========================================================================== */
.site-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 14px;
}

.site-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
}
.site-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
  text-decoration: none;
  color: inherit;
}

.site-card__media {
  position: relative;
  height: 124px;
  background-size: cover;
  background-position: center;
  overflow: hidden;
}
.site-card__grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
  background-size: 22px 22px;
}
.site-card__media::after {
  /* Subtle zoom-in on hover, the way a portfolio thumbnail behaves. */
  content: '';
  position: absolute; inset: 0;
  transition: background .18s var(--ease);
}
.site-card:hover .site-card__media { background-size: 106%; }
.site-card__media { transition: background-size .5s var(--ease); }

.site-card__status {
  position: absolute;
  top: 10px; left: 10px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  backdrop-filter: blur(6px);
  background: rgba(255,255,255,.9);
  color: var(--ink-700);
}
.site-card__status.is-active  { background: rgba(223,243,231,.94); color: var(--green-600); }
.site-card__status.is-done    { background: rgba(225,234,247,.94); color: var(--blue-600); }
.site-card__status.is-pending { background: rgba(251,240,214,.94); color: var(--yellow-700); }
.site-card__status.is-muted   { background: rgba(240,242,245,.94); color: var(--ink-500); }

.site-card__delay {
  position: absolute;
  top: 10px; right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  background: rgba(251,228,229,.94);
  color: var(--red-600);
  backdrop-filter: blur(6px);
}
.site-card__delay svg { stroke: currentColor; fill: none; stroke-width: 2.2; }

.site-card__body { padding: 13px 15px 15px; }

@media (max-width: 640px) {
  .site-grid { grid-template-columns: 1fr; gap: 12px; }
  .site-card__media { height: 108px; }
}

/* Segmented control — used for the Reports date presets. Previously four
   identical grey buttons with no indication of which range was active. */
.seg-group {
  display: inline-flex;
  background: var(--surface-alt-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 3px;
  gap: 2px;
  flex-wrap: wrap;
}
.seg {
  border: none;
  background: none;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-500);
  padding: 6px 12px;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .14s var(--ease), color .14s var(--ease);
}
.seg:hover { color: var(--ink-900); }
.seg.is-active {
  background: var(--surface);
  color: var(--navy-900);
  box-shadow: var(--shadow-sm);
}
[data-theme="dark"] .seg.is-active { background: var(--navy-700); color: #fff; }

/* ==========================================================================
   List rows + panels (Step 72)
   --------------------------------------------------------------------------
   The "flex items-center justify-between py-2.5 border-b hover:bg-gray-50
   -mx-2 px-2 rounded" pattern was repeated inline across every dashboard
   panel. Extracted so the hover, spacing and dark-mode behaviour are
   defined once instead of drifting per file.
   ========================================================================== */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 16px 18px;
}
.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.panel__title {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-900);
}
.panel__title svg { stroke: currentColor; fill: none; stroke-width: 2; color: var(--ink-400); }
.panel__count {
  font-size: 10.5px; font-weight: 700;
  background: var(--surface-alt-2); color: var(--ink-500);
  border-radius: 999px; padding: 1px 8px;
}

.list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 10px;
  margin: 0 -10px;
  border-radius: var(--radius-sm);
  border-bottom: 1px solid var(--border-light);
  text-decoration: none;
  color: inherit;
  transition: background .14s var(--ease);
}
.list-row:last-child { border-bottom: none; }
a.list-row:hover { background: var(--surface-alt); text-decoration: none; color: inherit; }
.list-row__main { min-width: 0; }
.list-row__title {
  font-size: 13px; font-weight: 600; color: var(--ink-900);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.list-row__sub {
  display: flex; align-items: center; gap: 5px;
  font-size: 11.5px; color: var(--ink-500); margin-top: 2px;
}
.list-row__sub svg { stroke: currentColor; fill: none; stroke-width: 2; opacity: .8; flex-shrink: 0; }
.list-row__meta { font-size: 12px; font-weight: 600; color: var(--ink-500); flex-shrink: 0; }

/* Empty state used inside panels */
.panel-empty {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px; padding: 26px 12px; text-align: center;
}
.panel-empty svg { color: var(--ink-400); stroke: currentColor; fill: none; stroke-width: 1.7; opacity: .55; }
.panel-empty p { font-size: 12.5px; color: var(--ink-500); margin: 0; }

.dash-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px;
}
@media (max-width: 640px) { .dash-cols { grid-template-columns: 1fr; } }

/* ==========================================================================
   Bootstrap replacement layer (Step 72) — THE ROOT CAUSE FIX
   --------------------------------------------------------------------------
   Bootstrap 5's CSS was loaded on every page, and its utility classes are
   all declared with `!important`. That meant Bootstrap silently beat this
   app's own utility layer on every colliding name regardless of source
   order — including `.bg-white`, `.shadow-sm`, `.rounded`, `.border`,
   `.w-100`, `.text-white`, and the entire `.p-1..5 / .m-1..5 / .px-* /
   .py-* / .mt-* / .mb-*` spacing scale (whose Bootstrap values differ from
   ours).

   Visible symptom, from a submitted screenshot: in dark mode the Activity
   Log card stayed pure white because Bootstrap's
   `.bg-white { background-color: … !important }` overrode our
   `.bg-white { background-color: var(--surface) }` — while the text inside
   correctly followed our dark token and turned near-white, leaving it
   unreadable on the white card. The same collision also explains the
   recurring "spacing looks off / not compact" reports across earlier
   rounds: our padding/margin values were never actually applying.

   Bootstrap's JS was also loaded but had zero usage (no `data-bs-*`
   anywhere), so both files are now dropped entirely — which also removes
   two CDN round-trips, worth having in a mobile WebView on a slow link.
   The 13 Bootstrap-only class names the views genuinely use are redefined
   below.
   ========================================================================== */
.d-none { display: none; }
.d-flex { display: flex; }
.d-block { display: block; }
.d-inline-flex { display: inline-flex; }

.position-relative { position: relative; }
.position-absolute { position: absolute; }
.position-fixed { position: fixed; }
.position-sticky { position: sticky; }

.align-items-center { align-items: center; }
.align-items-start { align-items: flex-start; }
.align-items-end { align-items: flex-end; }
.justify-content-between { justify-content: space-between; }
.justify-content-center { justify-content: center; }
.justify-content-end { justify-content: flex-end; }

.flex-column { flex-direction: column; }
.flex-row { flex-direction: row; }
.flex-wrap { flex-wrap: wrap; }
.flex-fill { flex: 1 1 auto; }

.text-center { text-align: center; }
.text-start { text-align: left; }
.text-end { text-align: right; }

.ms-auto { margin-left: auto; }
.me-auto { margin-right: auto; }
.mx-auto { margin-left: auto; margin-right: auto; }

.fw-semibold { font-weight: 600; }
.fw-bold { font-weight: 700; }
.fst-italic { font-style: italic; }

.rounded-circle { border-radius: 50%; }

@media (min-width: 768px) {
  .d-md-flex { display: flex; }
  .d-md-none { display: none; }
  .d-md-block { display: block; }
}
@media (min-width: 992px) {
  .d-lg-flex { display: flex; }
  .d-lg-none { display: none; }
}
.flex-grow-1 { flex-grow: 1; }

/* ==========================================================================
   Activity Log timeline (Step 72)
   ========================================================================== */
.act-row { display: flex; gap: 12px; padding: 12px 0; }
.act-row + .act-row { border-top: 1px solid var(--border-light); }

.act-row__rail { position: relative; flex-shrink: 0; width: 28px; }
.act-row__dot {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--navy-50);
  color: var(--navy-700);
  border: 1px solid var(--border);
}
.act-row__dot.is-created { background: var(--green-100); color: var(--green-600); border-color: transparent; }
.act-row__dot.is-deleted { background: var(--red-100);   color: var(--red-600);   border-color: transparent; }
.act-row__dot svg { stroke: currentColor; fill: none; stroke-width: 2; }

.act-row__body { flex: 1; min-width: 0; }
.act-row__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
}
.act-row__text { font-size: 13px; color: var(--ink-700); margin: 0; line-height: 1.5; }
.act-row__text strong { color: var(--ink-900); font-weight: 600; }
.act-row__subject { color: var(--ink-900); font-weight: 600; }
.act-row__time { font-size: 11.5px; color: var(--ink-400); flex-shrink: 0; white-space: nowrap; }

/* before → after chips, so the actual diff is scannable rather than
   buried in a run-on sentence. */
.act-diffs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.act-diff {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--surface-alt);
  border: 1px solid var(--border-light);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 11.5px;
  max-width: 100%;
}
.act-diff__field { color: var(--ink-400); font-weight: 600; }
.act-diff__old { color: var(--ink-500); text-decoration: line-through; opacity: .8; }
.act-diff__new { color: var(--green-600); font-weight: 600; }
.act-diff svg { stroke: var(--ink-400); fill: none; stroke-width: 2; flex-shrink: 0; }

@media (max-width: 640px) {
  .act-row__time { width: 100%; }
}

/* ==========================================================================
   Icon buttons + inline alerts (Step 72)
   --------------------------------------------------------------------------
   Replaces the "Edit / Delete" text links that were repeated across every
   master-data row. On a touch WebView a 40px icon target is far easier to
   hit than an 11px underlined word.
   ========================================================================== */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius-sm);
  color: var(--ink-500);
  cursor: pointer;
  transition: background .14s var(--ease), color .14s var(--ease), border-color .14s var(--ease);
}
.icon-btn:hover { background: var(--surface-alt-2); color: var(--ink-900); border-color: var(--border-strong); }
.icon-btn svg { stroke: currentColor; fill: none; stroke-width: 2; }
.icon-btn--danger:hover { background: var(--red-100); color: var(--red-600); border-color: transparent; }

.alert-inline {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  margin-bottom: 14px;
  border: 1px solid transparent;
}
.alert-inline svg { stroke: currentColor; fill: none; stroke-width: 2; flex-shrink: 0; }
.alert-inline--red   { background: var(--red-100);   color: var(--red-600);   border-color: #F0BFC2; }
.alert-inline--green { background: var(--green-100); color: var(--green-600); border-color: #B8E0C8; }
.alert-inline--amber { background: var(--amber-100); color: var(--amber-600); border-color: #EBD69A; }

/* rec-avatar holding an icon rather than initials */
.rec-avatar svg { stroke: currentColor; fill: none; stroke-width: 2; }

/* ==========================================================================
   Responsive shell (Step 73) — MUST stay last
   --------------------------------------------------------------------------
   These rules have to come after `.app-sidebar`'s base definition. When they
   lived earlier in the file, that base rule's `position: sticky` won on
   mobile too, so the sidebar kept its 240px flex slot while being translated
   off-screen — producing a dead white column beside the content on phones,
   which is exactly what a submitted screenshot showed.
   ========================================================================== */
@media (max-width: 960px) {
  .app-sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: min(268px, 82vw);
    height: 100dvh;
    z-index: 200;
    transform: translateX(-100%);
    transition: transform .22s var(--ease);
  }
  .app-sidebar.is-open { transform: translateX(0); }

  .app-topbar { padding: 12px 16px; gap: 10px; }
  .app-content { padding: 14px 16px 40px; max-width: none; }
  .dash-head { margin-bottom: 14px; }
  .dash-head__title { font-size: 18px; }
}

@media (max-width: 560px) {
  .app-topbar { padding: 10px 12px; }
  .app-content { padding: 12px 12px 36px; }
  /* Stack header actions full-width rather than letting them wrap into a
     ragged second line. */
  .dash-head { flex-direction: column; align-items: stretch; gap: 10px; }
  .dash-head .btn { justify-content: center; }
  .panel { padding: 13px 14px; }
}

/* Desktop: the content column was capped at 1200px but left-aligned, which
   stranded a wide empty gutter on large monitors. Centre it instead. */
.app-content { margin-inline: auto; width: 100%; }

/* ==========================================================================
   Widget rows + pills (Step 73)
   ========================================================================== */
.panel__link {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; font-weight: 600;
  color: var(--navy-700); text-decoration: none;
}
.panel__link:hover { color: var(--amber-600); }
.panel__link svg { stroke: currentColor; fill: none; stroke-width: 2.2; }

.wrow {
  display: flex; gap: 10px;
  padding: 10px 10px; margin: 0 -10px;
  border-radius: var(--radius-sm);
  border-bottom: 1px solid var(--border-light);
  text-decoration: none; color: inherit;
  transition: background .14s var(--ease);
}
.wrow:last-child { border-bottom: none; }
.wrow:hover { background: var(--surface-alt); text-decoration: none; color: inherit; }

/* Status rail on the left edge of each row — lets you scan a whole panel
   for problems without reading any text. */
.wrow__bar { width: 3px; border-radius: 999px; flex-shrink: 0; background: var(--border-strong); }
.wrow__bar.is-late { background: var(--red-600); }
.wrow__bar.is-ok   { background: var(--green-600); }

.wrow__body { flex: 1; min-width: 0; }
.wrow__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 5px;
}
.wrow__dot {
  display: inline-block; width: 3px; height: 3px; border-radius: 50%;
  background: var(--ink-400); margin: 0 2px; flex-shrink: 0;
}

.wrow__progress { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.wrow__track { flex: 1; height: 5px; background: var(--border); border-radius: 999px; overflow: hidden; }
.wrow__fill { display: block; height: 100%; border-radius: 999px; background: var(--amber-600); transition: width .4s var(--ease); }
.wrow__pct { font-size: 11px; font-weight: 700; color: var(--ink-500); min-width: 30px; text-align: right; }

.pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; font-weight: 700;
  padding: 2px 9px; border-radius: 999px;
  white-space: nowrap; flex-shrink: 0;
}
.pill--red   { background: var(--red-100);   color: var(--red-600); }
.pill--green { background: var(--green-100); color: var(--green-600); }
.pill--amber { background: var(--amber-100); color: var(--amber-600); }
.pill--blue  { background: var(--blue-100);  color: var(--blue-600); }
.pill--grey  { background: var(--surface-alt-2); color: var(--ink-500); }

/* ==========================================================================
   Form submit loader + tooltips (Step 74)
   ========================================================================== */

/* Full-panel submit state: dims the form and shows a centred spinner, so a
   slow save reads as "working" rather than as a frozen page. Applied with
   wire:loading.class="is-submitting" on the form wrapper. */
.is-submitting {
  position: relative;
  pointer-events: none;
  user-select: none;
}
.is-submitting > * { opacity: .45; transition: opacity .15s var(--ease); }
.is-submitting::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 30px; height: 30px;
  margin: -15px 0 0 -15px;
  border: 3px solid var(--border);
  border-top-color: var(--amber-600);
  border-radius: 50%;
  animation: app-spin .65s linear infinite;
  z-index: 5;
}

/* Button-level spinner, sized to sit inline with label text */
.app-btn-spinner {
  display: inline-block;
  width: 13px; height: 13px;
  border: 2px solid rgba(255,255,255,.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: app-spin .6s linear infinite;
  vertical-align: -2px;
  margin-right: 6px;
}
.btn-outline .app-btn-spinner,
.icon-btn .app-btn-spinner {
  border-color: var(--border);
  border-top-color: var(--amber-600);
}
@keyframes app-spin { to { transform: rotate(360deg); } }

/* Full-screen overlay for navigations that replace the page */
.app-form-overlay {
  position: fixed; inset: 0;
  background: color-mix(in srgb, var(--paper) 72%, transparent);
  backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center;
  z-index: 400;
}
.app-form-overlay__box {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 22px 30px;
}
.app-form-overlay__spin {
  width: 26px; height: 26px;
  border: 3px solid var(--border);
  border-top-color: var(--amber-600);
  border-radius: 50%;
  animation: app-spin .65s linear infinite;
}
.app-form-overlay__text { font-size: 12.5px; font-weight: 600; color: var(--ink-500); }

/* ---- Tooltips ----
   CSS-only, driven by a data-tip attribute so any element can have one
   without extra markup or a JS library. Suppressed on touch devices,
   where a hover tooltip can't be triggered and would only ever appear
   stuck after a tap. */
[data-tip] { position: relative; }
@media (hover: hover) {
  [data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    bottom: calc(100% + 7px);
    left: 50%;
    transform: translateX(-50%) translateY(3px);
    background: var(--navy-950);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    padding: 5px 9px;
    border-radius: 6px;
    opacity: 0;
    pointer-events: none;
    transition: opacity .14s var(--ease), transform .14s var(--ease);
    z-index: 500;
    box-shadow: var(--shadow-md);
  }
  [data-tip]::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%) translateY(3px);
    border: 5px solid transparent;
    border-top-color: var(--navy-950);
    opacity: 0;
    pointer-events: none;
    transition: opacity .14s var(--ease), transform .14s var(--ease);
    z-index: 500;
  }
  [data-tip]:hover::after,
  [data-tip]:hover::before { opacity: 1; transform: translateX(-50%) translateY(0); }

  /* Edge-safe variants for elements near the viewport sides */
  [data-tip][data-tip-pos="right"]::after { left: calc(100% + 8px); bottom: auto; top: 50%; transform: translateY(-50%) translateX(-3px); }
  [data-tip][data-tip-pos="right"]::before { display: none; }
  [data-tip][data-tip-pos="right"]:hover::after { transform: translateY(-50%) translateX(0); }
  [data-tip][data-tip-pos="left"]::after { right: calc(100% + 8px); left: auto; bottom: auto; top: 50%; transform: translateY(-50%) translateX(3px); }
  [data-tip][data-tip-pos="left"]::before { display: none; }
  [data-tip][data-tip-pos="left"]:hover::after { transform: translateY(-50%) translateX(0); }
}

/* ==========================================================================
   Lead pipeline track (Step 74)
   --------------------------------------------------------------------------
   A lead's whole story is where it sits in the funnel, so the detail page
   leads with the stage sequence rather than a generic stat row.
   ========================================================================== */
.lead-track { display: flex; align-items: flex-start; gap: 0; }
.lead-track__step {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  position: relative;
  min-width: 0;
}
/* Connector line between steps */
.lead-track__step::before {
  content: '';
  position: absolute;
  top: 6px; left: -50%;
  width: 100%; height: 2px;
  background: var(--border);
}
.lead-track__step:first-child::before { display: none; }
.lead-track__step.is-done::before,
.lead-track__step.is-current::before { background: var(--amber-600); }

.lead-track__dot {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--border);
  position: relative;
  z-index: 1;
  transition: background .18s var(--ease), border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.lead-track__step.is-done .lead-track__dot { background: var(--amber-600); border-color: var(--amber-600); }
.lead-track__step.is-current .lead-track__dot {
  background: var(--amber-600);
  border-color: var(--amber-600);
  box-shadow: 0 0 0 4px var(--amber-100);
}
.lead-track__step.is-lost .lead-track__dot { border-color: var(--red-600); background: var(--surface); }

.lead-track__label {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--ink-400);
  text-align: center;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.lead-track__step.is-done .lead-track__label,
.lead-track__step.is-current .lead-track__label { color: var(--ink-900); }

@media (max-width: 560px) {
  .lead-track__label { display: none; }
  .lead-track { padding: 4px 0; }
}

/* Step 75 — tooltip clipping. Cards and panels that need `overflow: hidden`
   for their own decoration were also cutting off tooltips rendered above
   them. Any element with a tooltip gets lifted into its own stacking
   context while hovered so it draws over neighbouring cards. */
.rec-card::before { border-radius: var(--radius-md) 0 0 var(--radius-md); }
[data-tip]:hover { z-index: 600; }
.rec-card:has([data-tip]:hover),
.panel:has([data-tip]:hover),
.kpi:has([data-tip]:hover) { overflow: visible; z-index: 600; }

/* Rows near the top of a scroll area flip their tooltip below instead of
   above, so it can't be cut off by the container edge. */
[data-tip][data-tip-pos="bottom"]::after {
  bottom: auto; top: calc(100% + 7px);
  transform: translateX(-50%) translateY(-3px);
}
[data-tip][data-tip-pos="bottom"]::before {
  bottom: auto; top: calc(100% + 2px);
  border-top-color: transparent;
  border-bottom-color: var(--navy-950);
  transform: translateX(-50%) translateY(-3px);
}
[data-tip][data-tip-pos="bottom"]:hover::after,
[data-tip][data-tip-pos="bottom"]:hover::before { transform: translateX(-50%) translateY(0); }

/* ==========================================================================
   Global blocking loader (Step 75)
   --------------------------------------------------------------------------
   The previous loader was a thin top bar plus a per-button spinner, which
   left the rest of the page looking clickable while a save was in flight.
   This blocks interaction outright and states what's happening. It's held
   back 220ms by the JS so quick actions don't flash it.
   ========================================================================== */
.app-block {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--navy-950) 34%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .16s var(--ease), visibility .16s var(--ease);
}
.app-block.is-on { opacity: 1; visibility: visible; }

.app-block__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 26px 34px;
  transform: translateY(8px) scale(.97);
  transition: transform .2s var(--ease);
}
.app-block.is-on .app-block__card { transform: none; }

.app-block__ring { line-height: 0; }
.app-block__ring svg { display: block; animation: app-spin 1s linear infinite; }
.app-block__arc { animation: app-block-arc 1.4s ease-in-out infinite; }

@keyframes app-block-arc {
  0%   { stroke-dashoffset: 100; }
  50%  { stroke-dashoffset: 30; }
  100% { stroke-dashoffset: 100; }
}

.app-block__text {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink-500);
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .app-block__ring svg, .app-block__arc { animation: none; }
  .app-block__card { transform: none; }
}

/* ==========================================================================
   Mobile refinements (Step 79) — must stay after the base responsive block
   --------------------------------------------------------------------------
   Targeted at the WebView shell this ships inside: bigger touch targets,
   no accidental zoom on input focus, and layouts that don't strand content
   off-screen on a narrow viewport.
   ========================================================================== */
@media (max-width: 960px) {
  /* iOS zooms the whole page when a focused input's font-size is under
     16px. Every form field in the app was 13-14px, so tapping any of them
     zoomed the viewport and left it zoomed. */
  input, select, textarea, .form-input, .form-select, .form-textarea,
  .filter-field input, .filter-field select {
    font-size: 16px;
  }

  /* Comfortable touch targets — 30px icon buttons are fine with a mouse
     but below the ~44px recommended minimum for a finger. */
  .icon-btn { width: 38px; height: 38px; }
  .btn { min-height: 42px; padding-block: 10px; }
  .proj-rail__link { padding: 10px 12px; }
  .app-nav-link { padding-block: 11px; }

  /* Hero headers ate most of a phone screen before any content showed. */
  .site-card__media { height: 128px; }

  /* KPI tiles: 2-up rather than cramped 4-up */
  .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Wide inline stat strips wrapped into a ragged mess */
  .rec-meta { gap: 6px 12px; }
}

@media (max-width: 560px) {
  /* Single column everywhere that still tried two */
  .grid-cols-2, .grid-cols-3, .grid-cols-4 { grid-template-columns: 1fr !important; }
  .dash-cols, .site-grid, .rec-grid { grid-template-columns: 1fr; }

  /* Detail-page heroes shrink further so the content below is reachable */
  .site-card__media { height: 112px; }

  /* Filter grid: one field per row is easier to hit than two cramped ones */
  .filter-grid { grid-template-columns: 1fr; }

  /* Long values (₹ amounts, emails) were overflowing their cards */
  .kpi__value { font-size: 17px; word-break: break-word; }
  .rec-card__title, .list-row__title { white-space: normal; }

  /* The blocking loader card was near-full-width on small screens */
  .app-block__card { padding: 20px 24px; }
}

/* Landscape phones / short viewports: a 100vh sidebar with 13 nav items
   needs its own scroll rather than clipping the logout button. */
@media (max-height: 560px) {
  .app-sidebar__nav { overflow-y: auto; }
  .app-sidebar__footer { position: sticky; bottom: 0; background: inherit; }
}

/* ==========================================================================
   Attendance (Step 80)
   ========================================================================== */
.attend-card { max-width: 480px; }

.attend-status {
  display: flex; align-items: center; gap: 11px;
  padding: 14px 16px; margin: -16px -18px 14px;
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.attend-status.is-in  { background: var(--green-100); }
.attend-status.is-out { background: var(--surface-alt); }

/* Pulsing dot — reads as "live" at a glance rather than needing to be read */
.attend-status__dot {
  width: 11px; height: 11px; border-radius: 50%; flex-shrink: 0;
  position: relative;
}
.attend-status.is-in .attend-status__dot  { background: var(--green-600); }
.attend-status.is-out .attend-status__dot { background: var(--ink-400); }
.attend-status.is-in .attend-status__dot::after {
  content: ''; position: absolute; inset: -4px;
  border-radius: 50%; border: 2px solid var(--green-600);
  animation: attend-pulse 1.8s ease-out infinite;
}
@keyframes attend-pulse {
  0%   { transform: scale(.7); opacity: .9; }
  100% { transform: scale(1.5); opacity: 0; }
}

.attend-status__label { font-size: 14px; font-weight: 700; color: var(--ink-900); margin: 0; }
.attend-status.is-in .attend-status__label { color: var(--green-600); }
.attend-status__meta { font-size: 11.5px; color: var(--ink-500); margin: 2px 0 0; }

.attend-visits { margin-bottom: 14px; }
.attend-visit {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-light);
}
.attend-visit:last-child { border-bottom: none; }
.attend-visit__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--green-600); flex-shrink: 0;
}
.attend-visit__body { flex: 1; min-width: 0; }
.attend-visit__time { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-900); }
.attend-visit__site {
  display: flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--ink-500); margin-top: 1px;
}
.attend-visit__site svg { stroke: currentColor; fill: none; stroke-width: 2; }

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

/* Numbered steps that tick over as they're completed */
.attend-step {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.attend-step.is-done { border-color: var(--green-600); background: var(--green-100); }
.attend-step__num {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface-alt-2); color: var(--ink-500);
  font-size: 11.5px; font-weight: 700; flex-shrink: 0;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.attend-step.is-done .attend-step__num { background: var(--green-600); color: #fff; }
.attend-step.is-done .attend-step__num svg { stroke: currentColor; fill: none; stroke-width: 2.6; }
.attend-step__label { font-size: 13px; font-weight: 600; color: var(--ink-900); margin: 0; }
.attend-step__hint { font-size: 11.5px; color: var(--ink-500); margin: 1px 0 0; }

.attend-action { width: 100%; justify-content: center; margin-top: 4px; }

/* ==========================================================================
   Motion layer (Step 80)
   --------------------------------------------------------------------------
   Interaction feedback rather than decoration: things that respond to a
   click, appear, or change value get a short transition so the change is
   traceable. All of it collapses under prefers-reduced-motion.
   ========================================================================== */

/* Press feedback on every interactive surface */
.btn:active, .icon-btn:active, .kpi:active,
.rec-card:active, .site-card:active, .proj-rail__link:active,
.app-nav-link:active, .seg:active { transform: scale(.985); }
.btn, .icon-btn, .seg { transition: transform .1s var(--ease), background .16s var(--ease), color .16s var(--ease), border-color .16s var(--ease), box-shadow .16s var(--ease); }

/* Sidebar nav: icon nudges toward the label on hover */
.app-nav-link svg { transition: transform .18s var(--ease), opacity .18s var(--ease); }
.app-nav-link:hover svg { transform: translateX(2px); }

/* Section rail: active marker slides rather than snapping */
.proj-rail__link::before { transition: transform .2s var(--ease), opacity .2s var(--ease); }

/* Numbers that change (KPI values, counts) get a brief lift so an updated
   figure is noticeable instead of silently swapping */
@keyframes app-value-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.kpi__value, .proj-rail__count, .filter-count, .panel__count {
  animation: app-value-in .3s var(--ease) both;
}

/* Progress bars fill from zero on first paint rather than appearing full */
@keyframes app-bar-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.wrow__fill, .site-card .rec-meta ~ * > div > div { transform-origin: left; }
.wrow__fill { animation: app-bar-grow .55s var(--ease) both; }

/* Toast/alert entrances */
@keyframes app-slide-in {
  from { opacity: 0; transform: translateX(12px); }
  to   { opacity: 1; transform: none; }
}
.alert-inline { animation: app-slide-in .26s var(--ease) both; }

/* Pills and badges pop in slightly */
@keyframes app-pop { from { transform: scale(.9); opacity: 0; } to { transform: none; opacity: 1; } }
.pill, .proj-alert { animation: app-pop .22s var(--ease) both; }

/* Rail/tab content: a subtle horizontal shift on section change reinforces
   that you moved sideways through the record, not to a new page */
@keyframes app-section-in {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: none; }
}
.proj-content > * { animation: app-section-in .26s var(--ease) both; }

/* Skeleton shimmer for lazy-loaded widgets */
@keyframes app-shimmer { to { background-position: 200% 0; } }
.animate-pulse {
  background: linear-gradient(90deg, var(--surface-alt) 25%, var(--surface-alt-2) 37%, var(--surface-alt) 63%);
  background-size: 200% 100%;
  animation: app-shimmer 1.3s linear infinite;
  border-radius: var(--radius-md);
}

/* Empty-state icons breathe very slightly — signals "nothing here yet"
   rather than "this failed to load" */
@keyframes app-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
.panel-empty svg { animation: app-float 3.5s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .kpi__value, .proj-rail__count, .filter-count, .panel__count,
  .wrow__fill, .alert-inline, .pill, .proj-alert,
  .proj-content > *, .panel-empty svg, .animate-pulse,
  .attend-status__dot::after { animation: none !important; }
  .btn:active, .icon-btn:active, .kpi:active,
  .rec-card:active, .site-card:active { transform: none; }
}

/* ==========================================================================
   Voice notes (Step 82)
   ========================================================================== */
.voice-bubble {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface-alt); border: 1px solid var(--border-light);
  border-radius: 999px; padding: 4px 12px 4px 4px;
}
.voice-bubble audio { height: 32px; max-width: 220px; }
.voice-bubble__len { font-size: 11px; font-weight: 600; color: var(--ink-500); }

.voice-recording-bar {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px; padding: 8px 12px;
  background: var(--red-100); border-radius: var(--radius-sm);
  font-size: 12.5px; font-weight: 600; color: var(--red-600);
}
.voice-rec-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--red-600); flex-shrink: 0;
  animation: attend-pulse-solid 1s ease-in-out infinite;
}
@keyframes attend-pulse-solid { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.voice-bars { display: flex; align-items: center; gap: 2px; height: 16px; margin-left: 2px; }
.voice-bars i {
  display: block; width: 2.5px; border-radius: 2px;
  background: var(--red-600);
  animation: voice-bar 0.9s ease-in-out infinite;
}
.voice-bars i:nth-child(1) { height: 6px;  animation-delay: 0s;   }
.voice-bars i:nth-child(2) { height: 12px; animation-delay: .12s; }
.voice-bars i:nth-child(3) { height: 16px; animation-delay: .24s; }
.voice-bars i:nth-child(4) { height: 10px; animation-delay: .36s; }
.voice-bars i:nth-child(5) { height: 5px;  animation-delay: .48s; }
@keyframes voice-bar { 0%,100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }

.voice-ready {
  display: flex; align-items: center; gap: 6px;
  margin-top: 8px; font-size: 12px; font-weight: 600; color: var(--green-600);
}
.voice-ready svg { stroke: currentColor; fill: none; stroke-width: 2.4; }

@media (prefers-reduced-motion: reduce) {
  .voice-rec-dot, .voice-bars i { animation: none; }
}

/* Video thumbnail play badge (Gallery, Step 82) */
.gallery-video-badge {
  position: absolute; bottom: 6px; left: 6px;
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: rgba(15,19,25,.65); color: #fff;
  pointer-events: none;
}
.gallery-video-badge svg { stroke: none; fill: currentColor; margin-left: 1px; }
