/* ── Nocturne design system ───────────────────────────────────────────────
   Two skins on one token set, not two palettes: "flat" (default — no
   [data-skin] attribute) and "depth" ([data-skin="depth"], toggled via the
   sidebar's skin switcher, see .skin-switch below). Same colors both ways;
   depth adds a lit page ground, grain, shadows-not-highlights on every
   surface, and one saturated field (the dashboard KPI band). Status
   colors (good/warn/bad/info) are intentionally NOT part of the accent
   ramp in either skin — "green = paid, red = cancelled" must never shift
   meaning. (The old SPA's five-palette theme/layout switcher was retired
   earlier this pass — this is a new, unrelated mechanism, not its
   return: one design system, two finishes, not five color palettes.) */
:root{
  --color-bg:#161826;
  --color-surface:#232532;
  --color-text:#e9e9ed;
  --color-accent:#9184d9;

  --color-neutral-100:#f3f5fe; --color-neutral-200:#e4e7f5; --color-neutral-300:#cfd3e5;
  --color-neutral-400:#b2b6ca; --color-neutral-500:#9397ab; --color-neutral-600:#75798c;
  --color-neutral-700:#595d6c; --color-neutral-800:#3f424d; --color-neutral-900:#292b31;

  --color-accent-100:#f5f4ff; --color-accent-200:#e7e5fe; --color-accent-300:#d2cefd;
  --color-accent-400:#b5abfc; --color-accent-500:#968ae0; --color-accent-600:#796cbf;
  --color-accent-700:#5d5294; --color-accent-800:#423a6a; --color-accent-900:#2b2741;

  --status-good:#6fbf8b;   --status-good-bg:rgba(111,191,139,.14);
  --status-warn:#e5b567;   --status-warn-bg:rgba(229,181,103,.14);
  --status-bad:#d97a72;    --status-bad-bg:rgba(217,122,114,.14);
  --status-info:#d2cefd;   --status-info-bg:rgba(145,132,217,.16);

  --surface-sunken:#1b1d29;
  --surface-rail:#141621;
  --surface-col:#1a1c27;
  --track:#2a2d3a;

  --font-heading:"Inter",system-ui,sans-serif; --font-heading-weight:500;
  --font-body:"Inter",system-ui,sans-serif;

  --radius-sm:4px; --radius-md:8px; --radius-lg:14px;

  /* AsPerPlan wordmark's own teal — the "Per" in the logo, used ONLY in
     the brand name itself. Every other accent use in the app stays
     --color-accent (blurple); this is not a second UI accent color. */
  --brand-teal:#34837a;
}

*{box-sizing:border-box;margin:0;padding:0}
body{font-family:var(--font-body);background:var(--color-bg);color:var(--color-text);font-size:13.5px;line-height:1.55}
h1,h2,h3{font-family:var(--font-heading);font-weight:var(--font-heading-weight);text-wrap:balance}
@media (prefers-reduced-motion:reduce){ *{transition:none !important} }

/* ── App shell — fixed left sidebar ───────────────────────────────────────
   Replaces the old horizontal topbar+nav pair entirely. Structure lives in
   layouts/legacy.blade.php: brand, then grouped nav links (group headers
   only render when a group actually has visible items for the current
   role — the "primary" group is deliberately headerless, which is also
   why Crew Lead — Dashboard + Work Orders only, both in "primary" — never
   sees a group header), then margin-top:auto pins Admin + the user card
   to the bottom. */
.sidebar{
  position:fixed; top:0; left:0; bottom:0; width:236px;
  background:var(--surface-rail); border-right:1px solid var(--color-neutral-800);
  padding:20px 14px; display:flex; flex-direction:column; gap:22px;
  overflow-y:auto; z-index:100;
}
.sidebar-brand{display:flex;align-items:center;gap:10px;padding:0 6px}
.sidebar-brand .mark{height:26px;width:auto;display:block;flex-shrink:0}
.sidebar-brand .name{font:500 15px var(--font-heading);color:var(--color-text);letter-spacing:-.01em}
.sidebar-brand .name .brand-accent{color:var(--brand-teal)}
.nav-group{display:flex;flex-direction:column;gap:2px}
.nav-group-label{
  font:500 10.5px var(--font-heading);letter-spacing:.09em;text-transform:uppercase;
  color:var(--color-neutral-700);padding:0 12px 7px;
}
.nav{display:flex;flex-direction:column;gap:2px}
.nav-item{
  display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:var(--radius-md);
  font:500 13px var(--font-body);color:var(--color-neutral-400);text-decoration:none;
  white-space:nowrap;transition:background-color .12s ease,color .12s ease;
}
.nav-item i{font-size:17px;flex-shrink:0}
.nav-item:hover{background:rgba(145,132,217,.10);color:var(--color-neutral-200)}
.nav-item.active{background:rgba(145,132,217,.14);color:var(--color-neutral-100);box-shadow:inset 2px 0 0 var(--color-accent)}
.nav-item:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
.nav-item .count-pill{
  margin-left:auto;font:500 11px var(--font-body);color:var(--color-accent-200);
  background:var(--color-accent-800);padding:2px 7px;border-radius:999px;
}
/* Shortcut to a list page's board view — sits beside the main nav-item
   row instead of being buried behind a small "Board view" button only
   reachable once you're already on that list page. */
.nav-item-row{display:flex;align-items:center;gap:2px}
.nav-item-row .nav-item{flex:1;min-width:0}
.nav-kanban-link{
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  width:28px;height:28px;border-radius:var(--radius-sm);
  color:var(--color-neutral-600);text-decoration:none;font-size:14px;
  transition:background-color .12s ease,color .12s ease;
}
.nav-kanban-link:hover{background:rgba(145,132,217,.10);color:var(--color-neutral-200)}
.nav-kanban-link.active{color:var(--color-accent-200);background:rgba(145,132,217,.14)}
.nav-kanban-link:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
.sidebar-footer{margin-top:auto;display:flex;flex-direction:column;gap:10px}
.user-card{
  display:flex;align-items:center;gap:10px;border:1px solid var(--color-neutral-800);
  border-radius:10px;padding:11px 12px;
}
.user-card .avatar{
  width:28px;height:28px;border-radius:50%;flex-shrink:0;
  background:var(--color-accent-800);color:var(--color-accent-200);
  display:flex;align-items:center;justify-content:center;font:500 11.5px var(--font-heading);
}
.user-card .who{flex:1;min-width:0}
.user-card .who .name{font:500 12.5px var(--font-body);color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.user-card .who .role{font-size:11px;color:var(--color-neutral-600)}
.user-card form{display:contents}
.user-card button,.user-card a{
  background:none;border:none;color:var(--color-neutral-500);cursor:pointer;text-decoration:none;
  padding:4px;font-size:17px;line-height:1;flex-shrink:0;border-radius:var(--radius-sm);display:flex;
}
.user-card button:hover,.user-card a:hover{color:var(--color-neutral-200)}
.user-card button:focus-visible,.user-card a:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}

/* Mobile nav (<900px): sidebar collapses to a top bar + a <details>
   dropdown of the same links — no JS. */
.mobile-topbar{display:none}
@media (max-width:899px){
  .sidebar{display:none}
  .mobile-topbar{
    display:flex;align-items:center;justify-content:space-between;
    background:var(--surface-rail);border-bottom:1px solid var(--color-neutral-800);
    padding:12px 16px;position:sticky;top:0;z-index:100;
  }
  .mobile-topbar summary{list-style:none;cursor:pointer;color:var(--color-neutral-400);font-size:20px;padding:4px}
  .mobile-topbar summary::-webkit-details-marker{display:none}
  .mobile-nav-panel{
    position:fixed;top:53px;left:0;right:0;bottom:0;z-index:99;
    background:var(--surface-rail);padding:14px;overflow-y:auto;
  }
  .main{margin-left:0 !important;padding:18px !important}
}

.main{margin-left:236px;max-width:1100px;padding:26px 30px 34px}

/* ── Mobile bottom tab bar (<900px) ───────────────────────────────────────
   Crew Lead and FM are the two roles actually working off a phone in the
   field, not a desk — the sidebar's nav already collapses into the
   hamburger-only .mobile-nav-panel above, but a menu you have to open
   first isn't where primary navigation belongs on a phone. Thumb reach on
   a one-handed phone splits into three bands (bottom = easy, top = a
   stretch), so the 2-4 links each role actually lives in during a shift
   move down here instead; anything that overflows stays one tap away
   behind "More", which just opens the same .mobile-nav-panel <details>
   the hamburger already used (see the script below) rather than
   duplicating that panel's contents. */
.mobile-tabbar{display:none}
@media (max-width:899px){
  .mobile-tabbar{
    display:flex;position:fixed;left:0;right:0;bottom:0;z-index:150;
    background:var(--surface-rail);border-top:1px solid var(--color-neutral-800);
    padding:4px 4px calc(4px + env(safe-area-inset-bottom));
  }
  .mobile-tabbar-item{
    position:relative;flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    min-height:54px;padding:5px 2px;border:none;background:none;cursor:pointer;
    color:var(--color-neutral-500);text-decoration:none;font:500 10.5px var(--font-body);
    border-radius:var(--radius-md);-webkit-tap-highlight-color:transparent;
  }
  .mobile-tabbar-item i{font-size:22px}
  .mobile-tabbar-item.active{color:var(--color-accent-200)}
  .mobile-tabbar-item:active{background:rgba(145,132,217,.10)}
  .mobile-tabbar-item:focus-visible{outline:2px solid var(--color-accent);outline-offset:-2px}
  .mobile-tabbar-item .tab-dot{
    position:absolute;top:3px;left:calc(50% + 8px);width:8px;height:8px;border-radius:50%;
    background:var(--status-bad);border:2px solid var(--surface-rail);
  }
  /* Room for the fixed tab bar so it never covers the page's own last
     row of content/buttons — supersedes the shorthand padding above
     (same specificity, declared later, longhand wins on this one side). */
  .main{padding-bottom:calc(70px + env(safe-area-inset-bottom)) !important}
}

/* ── Mobile field UX pass (<640px) ────────────────────────────────────────
   Below this width every desktop data table collapses from an
   inherently-sideways grid into a stack of full-width cards (one field
   per line, labeled via each <td>'s data-label — see work-orders/index,
   lots/show-crew, dashboard/index), and the handful of controls a crew
   or FM actually taps mid-shift (Accept, sign+invoice, dashboard
   attention-row actions) grow to real thumb-sized targets instead of the
   desktop-scaled buttons everything else uses. 640px, not 899px — this
   is about cramped WIDTH for a data-dense row/form, not "is this a
   phone", so it also kicks in on a narrow split-screen tablet. */
@media (max-width:640px){
  .row2,.row3,.row4{grid-template-columns:1fr}

  .table-wrap:not(.no-card-collapse){overflow-x:visible}
  .table-wrap:not(.no-card-collapse) table{display:block;width:100%}
  .table-wrap:not(.no-card-collapse) thead{display:none}
  .table-wrap:not(.no-card-collapse) tbody{display:block}
  .table-wrap:not(.no-card-collapse) tr{
    display:block;background:var(--surface-sunken);border:1px solid var(--color-neutral-800);
    border-radius:var(--radius-md);padding:12px 14px;margin-bottom:10px;
  }
  .table-wrap:not(.no-card-collapse) tr:last-child{margin-bottom:0}
  .table-wrap:not(.no-card-collapse) td{
    display:block;padding:7px 0 !important;border:none;border-bottom:1px solid rgba(255,255,255,.05);
    white-space:normal;text-align:left;
  }
  .table-wrap:not(.no-card-collapse) td:first-child{padding-top:0 !important}
  .table-wrap:not(.no-card-collapse) td:last-child{border-bottom:none;padding-bottom:0 !important}
  .table-wrap:not(.no-card-collapse) td.empty{background:none;border:none;padding:30px 0 !important;text-align:center}
  .table-wrap:not(.no-card-collapse) td[data-label]::before{
    content:attr(data-label);display:block;font:600 10.5px var(--font-heading);
    letter-spacing:.05em;text-transform:uppercase;color:var(--color-neutral-600);margin-bottom:3px;
  }
  .table-sticky-first-col table th:first-child,
  .table-sticky-first-col table td:first-child{position:static;background:none}

  /* Dashboard "needs your attention" feed — an FM's actual daily triage
     list. Action buttons go full-width instead of squeezing beside the
     title text, and the approve-material-request row's inline
     select+button (the one non-.btn-shaped action here) grows to match. */
  .attn-row{flex-wrap:wrap}
  .attn-actions{width:100%;flex-direction:column;align-items:stretch;gap:8px}
  .attn-actions form{width:100%;display:flex !important;gap:8px}
  .attn-actions .btn{width:100%;min-height:42px;font-size:13.5px}
  .attn-actions select{flex:1;min-width:0;min-height:38px}

  /* Crew Lead's two field actions -- Accept a generated work order, then
     sign + attest to invoice an accepted one -- are the entire reason a
     crew lead opens this app on site. Full-width, 46px-tall, no longer
     sharing a table cell's normal 13px text. */
  .crew-accept-form .btn,.sign-invoice-form .btn{width:100%;min-height:46px;font-size:14.5px}
  .sign-invoice-form{width:100%}
  .sign-invoice-form input[type="text"]{width:100% !important;min-height:44px;font-size:15px}
  .sign-attest-label{font-size:12.5px}
  .sign-attest-label input[type="checkbox"]{width:19px;height:19px}
  .wo-status-line{flex-direction:column;align-items:flex-start !important;gap:8px}
  .wo-status-line .crew-accept-form,.wo-status-line .sign-invoice-form{width:100%}
}

.flash{border-radius:var(--radius-md);padding:10px 14px;font-size:13px;margin-bottom:14px}
.flash-status{color:var(--status-good);background:var(--status-good-bg)}
.flash-error{color:var(--status-bad);background:var(--status-bad-bg)}

/* Bare, unclassed links — lot names in tables, "back to X" breadcrumbs,
   "+N more" kanban links — render as a pill instead of the browser's
   default underline. Anything meant to look like a button gets an
   explicit .btn class instead. Gotcha (hit once on the schedule board's
   card title, 2026-08-27): white-space:nowrap here clips any longer text
   that's meant to *wrap* instead (e.g. inside a fixed-width card) rather
   than render as a pill — give that kind of link a real class, even an
   unstyled one, so it stops matching this selector at all. */
a:not([class]){
  display:inline-flex;align-items:center;gap:4px;
  text-decoration:none;white-space:nowrap;
  background:rgba(145,132,217,.08);color:var(--color-accent-300);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  font-weight:600;font-size:12px;
  padding:3px 10px;border-radius:999px;
  transition:background-color .12s ease,color .12s ease;
}
a:not([class]):hover{background:rgba(145,132,217,.15);color:var(--color-accent-200)}
a:not([class]):visited{color:var(--color-accent-300)}
a:not([class]):focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}

.card{background:var(--color-surface);border:1px solid var(--color-neutral-800);border-radius:var(--radius-lg);padding:16px 18px;margin-bottom:14px}
.card h2{font-size:15px;font-weight:500;font-family:var(--font-heading);color:var(--color-text);margin-bottom:12px}

.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;margin-bottom:16px}
.kpi{
  padding:16px 18px;border-radius:10px;
  background:linear-gradient(180deg,#23253260,#1c1e2a);border:1px solid var(--color-neutral-800);
  container-type:inline-size;overflow:hidden;
}
.kpi-label{font:500 11.5px var(--font-heading);letter-spacing:.05em;text-transform:uppercase;color:var(--color-neutral-500)}
/* Fluid, not fixed at 30px — a 4-tile row (Dashboard) has room to run
   large, but pages sharing this same .kpi with 6+ tiles (Financial
   Report, Cash Position) don't, and a long dollar figure at a flat 30px
   overflowed straight past the tile border into its neighbor. Scales
   off the tile's own measured width via a container query, so it's
   correct regardless of how many tiles share a row. */
.kpi-val{font-size:clamp(16px, 10cqi, 30px);font-weight:500;font-family:var(--font-heading);color:var(--color-text);margin-top:4px;font-variant-numeric:tabular-nums;white-space:nowrap}
.kpi-val.warn{color:var(--status-warn)}
.kpi-val.bad{color:var(--status-bad)}
.kpi-val.good{color:var(--status-good)}
.kpi-context{font-size:12px;color:var(--color-neutral-500);margin-top:4px}

/* ── Dashboard ─────────────────────────────────────────────────────────── */
.dashboard{display:flex;flex-direction:column;gap:22px}
.dash-header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}
.dash-header .date-line{font-size:12px;color:var(--color-neutral-600)}
.dash-header h1{font-size:26px;margin-top:2px}
.dash-header .actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.search-field{position:relative;width:260px;max-width:100%}
.search-field i{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--color-neutral-500);font-size:15px}
.search-field input{
  width:100%;padding:9px 13px 9px 34px;font-size:13px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);
  font-family:var(--font-body);
}
.search-field input::placeholder{color:var(--color-neutral-600)}
.search-field input:focus{outline:2px solid var(--color-accent);outline-offset:1px;border-color:transparent}

.dashboard-kpis{grid-template-columns:repeat(4,1fr)}

.card-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
.card-head h2{margin-bottom:0}
.card-count{font-size:12px;color:var(--color-neutral-500)}

.dash-body{display:grid;grid-template-columns:1.35fr .9fr;gap:18px;align-items:start}

.attn-row{display:flex;align-items:flex-start;gap:12px;padding:11px 0;border-bottom:1px solid rgba(255,255,255,.05)}
.attn-row:last-child{border-bottom:none;padding-bottom:0}
.attn-row:first-child{padding-top:2px}
.attn-icon{width:18px;flex-shrink:0;margin-top:1px;font-size:18px;text-align:center}
.attn-icon.warn{color:var(--status-warn)}
.attn-icon.bad{color:var(--status-bad)}
.attn-icon.accent{color:var(--color-accent-300)}
.attn-icon.neutral{color:var(--color-neutral-500)}
.attn-body{flex:1;min-width:0}
.attn-title{font-size:13.5px;font-weight:500;color:var(--color-text)}
.attn-sub{font-size:12.5px;color:var(--color-neutral-500);margin-top:2px}
.attn-actions{display:flex;gap:8px;flex-shrink:0;align-items:center}
.attn-actions form{display:inline}

.cash-rows{display:flex;flex-direction:column;gap:12px}
.cash-label-line{display:flex;justify-content:space-between;font-size:12.5px;color:var(--color-neutral-400);margin-bottom:5px}
.cash-label-line .val{color:var(--color-text);font-variant-numeric:tabular-nums}
.cash-track{height:5px;border-radius:999px;background:var(--track);overflow:hidden}
.cash-track-fill{height:100%;border-radius:999px;background:linear-gradient(90deg,var(--color-accent-700),var(--color-accent))}
.cash-net{display:flex;justify-content:space-between;align-items:baseline;padding-top:12px;margin-top:2px;border-top:1px solid var(--color-neutral-800)}
.cash-net .label{font-size:12.5px;color:var(--color-neutral-400)}
.cash-net .val{font-size:20px;font-weight:500;font-family:var(--font-heading);color:var(--status-good);font-variant-numeric:tabular-nums}
.cash-net .val.bad{color:var(--status-bad)}

.phase-rows{display:flex;flex-direction:column;gap:10px}
.phase-row{display:flex;align-items:center;gap:10px}
.phase-row .phase-name{width:112px;flex-shrink:0;font-size:12.5px;color:var(--color-neutral-400);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.phase-bar-track{flex:1;height:22px;border-radius:5px;background:var(--track);overflow:hidden}
.phase-bar-fill{height:100%;border-radius:5px;background:var(--color-accent-700)}
.phase-bar-fill.active{background:var(--color-accent)}
.phase-row .phase-count{width:26px;text-align:right;font-size:12.5px;color:var(--color-neutral-500);font-variant-numeric:tabular-nums}

@media (max-width:900px){
  .dashboard-kpis{grid-template-columns:repeat(2,1fr)}
  .dash-body{grid-template-columns:1fr}
}

.field{margin-bottom:10px}
.field label{display:block;font:500 12.5px var(--font-body);color:var(--color-neutral-400);margin-bottom:6px}
.field input,.field select,.field textarea{
  width:100%;padding:12px 13px;font-size:14px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);
  font-family:var(--font-body);
}
.field input::placeholder,.field textarea::placeholder{color:var(--color-neutral-600)}
.field input:focus,.field select:focus,.field textarea:focus{outline:2px solid var(--color-accent);outline-offset:1px;border-color:transparent}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.row3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px}
.row4{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:12px}
.filter-bar{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid var(--color-neutral-800)}
.filter-bar .field{margin-bottom:0;min-width:200px;flex:1}

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  border-radius:var(--radius-md);font:500 13px var(--font-body);padding:9px 14px;
  border:1px solid transparent;cursor:pointer;transition:background-color .12s ease,border-color .12s ease,color .12s ease;
}
.btn:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
.btn-primary,.btn-gold{border-color:var(--color-accent);color:var(--color-accent-200);background:transparent}
.btn-primary:hover,.btn-gold:hover{background:rgba(145,132,217,.14)}
.btn-primary:active,.btn-gold:active{border-color:var(--color-accent-400)}
.btn-ghost{background:none;border-color:var(--color-neutral-800);color:var(--color-neutral-300)}
.btn-ghost:hover{border-color:var(--color-neutral-700);color:var(--color-neutral-100)}
.btn-green{border-color:var(--status-good);color:var(--status-good);background:transparent}
.btn-green:hover{background:var(--status-good-bg)}
.btn-red{border-color:var(--status-bad);color:var(--status-bad);background:transparent}
.btn-red:hover{background:var(--status-bad-bg)}
.btn-sm{padding:6px 11px;font-size:12.5px}

.table-wrap{overflow-x:auto;position:relative}
/* Scroll-shadow affordance for any horizontally-scrollable container that
   opts in (.table-wrap and .kanban-wrap both do, see below) — a soft fade
   on whichever edge still has unscrolled content, so a wide table/board
   doesn't read as "that's everything" when there's really more off-screen.
   Toggled by a small script in layouts/legacy.blade.php that adds/removes
   .scroll-shadow-left/-right on scroll/resize; sits on ::before/::after so
   it never fights the container's own content or padding. */
/* Fades to the surface color underneath, not a flat dark tint over the
   content -- a solid rgba(0,0,0,.45) here used to paint directly on top
   of whatever cell text/values sat at that scroll boundary, reading as a
   dark smear across real data rather than an edge cue. Fading to
   --color-surface (what .card/.table-wrap actually sit on) makes it read
   as "content trailing off the edge" on every skin, light or dark, since
   each skin already redefines that token correctly. */
.table-wrap.scroll-shadow-right::after,
.kanban-wrap.scroll-shadow-right::after{
  content:'';position:absolute;top:0;right:0;bottom:0;width:32px;
  background:linear-gradient(to right,transparent,var(--color-surface));
  pointer-events:none;
}
.table-wrap.scroll-shadow-left::before,
.kanban-wrap.scroll-shadow-left::before{
  content:'';position:absolute;top:0;left:0;bottom:0;width:32px;
  background:linear-gradient(to left,transparent,var(--color-surface));
  pointer-events:none;
}
/* Explicit click-to-scroll buttons for kanban boards — inserted by the
   scroll-shadow script in layouts/legacy.blade.php, one pair per
   .kanban-wrap, only shown while that edge actually has more to scroll
   to (same overflow check the shadow itself uses). A shadow alone reads
   as "trust me, scroll" — these give an actual thing to click instead of
   relying on a trackpad two-finger swipe nobody's told is the way in. */
.kanban-scroll-btn{
  /* Fixed offset from the top, not vertically centered — .kanban-wrap has
     no height cap (it grows with however many cards a column holds), so
     centering "top:50%" against its full scrollHeight could park these
     well below the fold. Anchoring near the column headers instead keeps
     them where you're actually looking when you first land on the board. */
  position:absolute;top:44px;z-index:3;
  width:30px;height:30px;border-radius:50%;
  background:var(--color-surface);border:1px solid var(--color-neutral-700);
  color:var(--color-neutral-200);cursor:pointer;
  display:flex;align-items:center;justify-content:center;font-size:15px;
  box-shadow:0 4px 12px rgba(0,0,0,.4);
  transition:background-color .12s ease,border-color .12s ease;
}
.kanban-scroll-btn:hover{background:var(--surface-sunken);border-color:var(--color-accent-700);color:var(--color-accent-200)}
.kanban-scroll-btn.kanban-scroll-btn-left{left:6px}
.kanban-scroll-btn.kanban-scroll-btn-right{right:6px}
/* Opt-in: keeps a wide table's leading label column (Lot, WO#, Name…)
   in view while the rest of the row scrolls underneath it — added only
   to the specific data-dense tables that actually run wider than the
   page (Work Orders, Financial Report, Suppliers, Cash Position), not
   blanket-applied to every .table-wrap. */
.table-sticky-first-col table th:first-child,
.table-sticky-first-col table td:first-child{
  position:sticky;left:0;z-index:1;background:var(--color-surface);
}
.table-sticky-first-col table thead th:first-child{z-index:2}
#lotsTableWrap, #posTableWrap{
  max-height:clamp(360px, calc(100vh - 300px), 640px);
  overflow-y:auto;
}
#lotsTableWrap thead th, #posTableWrap thead th{
  position:sticky; top:0; z-index:2; background:var(--color-surface);
}
table{width:100%;border-collapse:collapse}
th{
  text-align:left;font:500 11.5px var(--font-heading);letter-spacing:.05em;text-transform:uppercase;
  color:var(--color-neutral-500);padding:0 14px 10px;border-bottom:1px solid var(--color-neutral-800);white-space:nowrap;
}
td{padding:13px 14px;border-bottom:1px solid rgba(255,255,255,.05);font-size:13.5px;color:var(--color-neutral-200);white-space:nowrap}
tbody tr:hover td{background:rgba(145,132,217,.06)}
td.num, th.num{font-variant-numeric:tabular-nums}

/* Work Orders — each cell stacks two related values (WO#/Lot,
   Phase/Crew, Amount/Status) instead of each getting its own column, so
   the table reads as 4 wide columns rather than 7 narrow ones. */
.wo-table-2line td{padding:10px 14px}
.wo-line-1{font-size:13.5px;color:var(--color-text)}
.wo-line-2{font-size:12px;color:var(--color-neutral-500);margin-top:3px}
/* Work Orders' Crew column — plain text + a pencil by default; the
   select+Assign form (wo-crew-form) only appears once .wo-crew-cell has
   .editing, so a page full of rows stays as narrow as any other text
   column instead of every row carrying a full form. */
.wo-crew-view{display:flex;align-items:center;gap:6px}
.wo-crew-view button{
  background:none;border:none;color:var(--color-neutral-600);cursor:pointer;
  padding:2px;display:flex;align-items:center;font-size:13px;border-radius:var(--radius-sm);
}
.wo-crew-view button:hover{color:var(--color-accent-300);background:rgba(145,132,217,.10)}
.wo-crew-form{display:none;gap:4px;align-items:center}
.wo-crew-form select{max-width:140px}
.wo-crew-cell.editing .wo-crew-view{display:none}
.wo-crew-cell.editing .wo-crew-form{display:flex}

/* Same click-to-edit shape as .wo-crew-cell above, for the Scheduled
   column and the week board's per-card reschedule control — a bare
   <input type="date"> outside a .field wrapper picks up zero of this
   app's styling (browser default chrome, "mm/dd/yyyy" placeholder even
   when a real value exists to show instead), which is what made it look
   broken rather than just plain. */
.wo-schedule-view{display:flex;align-items:center;gap:6px;font-size:13.5px;color:var(--color-neutral-200)}
.wo-schedule-view.unset{color:var(--status-warn)}
.wo-schedule-view button{
  background:none;border:none;color:var(--color-neutral-600);cursor:pointer;
  padding:2px;display:flex;align-items:center;font-size:13px;border-radius:var(--radius-sm);
}
.wo-schedule-view button:hover{color:var(--color-accent-300);background:rgba(145,132,217,.10)}
.wo-schedule-form{display:none;gap:6px;align-items:center}
.wo-schedule-form input[type="date"]{
  padding:7px 9px;font-size:12.5px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-sm);
  font-family:var(--font-body);color-scheme:dark;
}
[data-skin="light"] .wo-schedule-form input[type="date"],
[data-skin="paper"] .wo-schedule-form input[type="date"],
[data-skin="canvas"] .wo-schedule-form input[type="date"]{color-scheme:light}
.wo-schedule-cell.editing .wo-schedule-view{display:none}
.wo-schedule-cell.editing .wo-schedule-form{display:flex}

/* Same click-to-edit shape again, for the Lien Waiver column -- three
   fields instead of one, so the form wraps onto its own line under the
   pill rather than trying to force everything into one row. */
.wo-lien-view{display:flex;align-items:center;gap:6px;font-size:13.5px}
.wo-lien-view button{
  background:none;border:none;color:var(--color-neutral-600);cursor:pointer;
  padding:2px;display:flex;align-items:center;font-size:13px;border-radius:var(--radius-sm);
}
.wo-lien-view button:hover{color:var(--color-accent-300);background:rgba(145,132,217,.10)}
/* .wo-lien-pdf-link is an <a>, not a <button> (a real download link, not
   JS-driven), so it needs its own rule -- same icon-button look as the
   buttons right next to it. */
.wo-lien-pdf-link{
  background:none;border:none;color:var(--color-neutral-600);cursor:pointer;text-decoration:none;
  padding:2px;display:flex;align-items:center;font-size:13px;border-radius:var(--radius-sm);
}
.wo-lien-pdf-link:hover{color:var(--color-accent-300);background:rgba(145,132,217,.10)}
.wo-lien-form{display:none;flex-wrap:wrap;gap:6px;align-items:center;margin-top:6px;max-width:280px}
.wo-lien-form select,.wo-lien-form input[type="date"]{
  padding:6px 8px;font-size:12px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-sm);
  font-family:var(--font-body);color-scheme:dark;
}
[data-skin="light"] .wo-lien-form input[type="date"],
[data-skin="paper"] .wo-lien-form input[type="date"],
[data-skin="canvas"] .wo-lien-form input[type="date"]{color-scheme:light}
.wo-lien-cell.editing .wo-lien-form{display:flex}

/* Crew self-invoice sign-off -- name + Invoice button stay on one line
   (existing compact table-row layout), the attestation checkbox wraps
   onto its own line underneath rather than fighting for horizontal room
   with everything else in the cell. */
.sign-invoice-form{display:flex;flex-direction:column;gap:5px;min-width:220px}
.sign-attest-label{display:flex;align-items:flex-start;gap:6px;font-size:11px;color:var(--color-neutral-500);cursor:pointer;line-height:1.35}
.sign-attest-label input[type="checkbox"]{width:14px;height:14px;flex-shrink:0;margin-top:1px}

/* ── Schedule board (/schedule) — day/week/month, plus a persistent
   "unscheduled" sidebar. Its own card language (.sched-card), not the
   kanban one — a status board wants columns that are basically lists;
   a calendar wants small, dense, evenly-sized cells, so the unscheduled
   queue is a fixed side panel rather than one more column competing with
   7 day columns for width (the old layout, one wide "column" per day plus
   an equally-wide unscheduled column, forced horizontal scrolling on an
   ordinary laptop). ─────────────────────────────────────────────────── */
.sched-toolbar{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;margin-bottom:12px}
.sched-tabgroup{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.sched-tabs{display:inline-flex;background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:999px;padding:3px;gap:2px}
.sched-tabs a{padding:6px 16px;border-radius:999px;font:600 12.5px var(--font-body);color:var(--color-neutral-500);text-decoration:none;transition:background-color .12s ease,color .12s ease}
.sched-tabs a:hover{color:var(--color-neutral-200)}
.sched-tabs a.active{background:var(--color-accent-700);color:var(--color-accent-100)}
.sched-tabs.sched-type-tabs a.active{background:var(--brand-teal);color:#fff}
.sched-range{display:flex;align-items:center;gap:8px}
.sched-range-btn{
  width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--radius-md);border:1px solid var(--color-neutral-800);background:var(--surface-sunken);
  color:var(--color-neutral-400);text-decoration:none;font-size:15px;flex-shrink:0;
}
.sched-range-btn:hover{color:var(--color-accent-300);border-color:var(--color-accent-700)}
.sched-range h3{margin:0 4px;font:600 16px var(--font-heading);color:var(--color-text);white-space:nowrap}
.sched-today-btn{font:600 12px var(--font-body);color:var(--color-accent-300);background:var(--color-accent-900);border:1px solid var(--color-accent-800);padding:6px 13px;border-radius:999px;text-decoration:none;white-space:nowrap}
.sched-today-btn:hover{background:var(--color-accent-800)}

.sched-filters{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:16px}
.sched-filters select{
  padding:6px 10px;font-size:12px;color:var(--color-text);min-width:118px;
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-sm);
  font-family:var(--font-body);
}
.sched-filters select.sched-sort-select{
  min-width:150px;color:var(--color-neutral-400);border-style:dashed;
}

/* Below ~1300px available width, the sidebar (fixed 260px) plus 7 day
   columns (150px min each) genuinely can't both fit -- rather than let
   flexbox squeeze the sidebar half off-screen (it and the grid's last
   column or two were getting clipped by the viewport edge, not just
   scrolled to), stack the sidebar under the grid instead, full width.
   The grid keeps its own independent horizontal scroll (.week-grid-wrap)
   either way. */
.schedule-shell{display:flex;align-items:flex-start;gap:18px;flex-wrap:wrap}
.schedule-main{flex:1;min-width:0}
.schedule-sidebar{width:260px;flex-shrink:0;background:var(--status-warn-bg);border:1px solid rgba(229,181,103,.35);border-radius:var(--radius-lg);padding:14px;max-height:calc(100vh - 260px);overflow-y:auto}
@media (max-width:1300px){
  .schedule-shell{flex-direction:column}
  .schedule-main,.schedule-sidebar{width:100%}
  .schedule-sidebar{max-height:340px}
}
.schedule-sidebar h4{display:flex;align-items:center;gap:8px;font:600 12px var(--font-heading);color:var(--status-warn);margin-bottom:12px;text-transform:uppercase;letter-spacing:.03em}
.schedule-sidebar h4 .kn{background:rgba(229,181,103,.22);color:var(--status-warn);font-size:11px;font-weight:700;padding:1px 8px;border-radius:999px}
.schedule-sidebar-more{display:block;text-align:center;font-size:11.5px;color:var(--color-neutral-600);padding:8px 0 2px}
.schedule-sidebar-hint{font-size:11px;color:var(--status-warn);opacity:.8;margin:-4px 0 10px}

/* Visit-booking sidebar — swaps in for the unscheduled queue when
   type=visits (a visit is booked directly, there's nothing to drag). */
.schedule-sidebar.visits-sidebar{background:var(--surface-col);border-color:var(--color-neutral-800)}
.schedule-sidebar.visits-sidebar h4{color:var(--color-text)}
.visit-form{display:flex;flex-direction:column;gap:10px}
.visit-form .field{display:flex;flex-direction:column;gap:4px}
.visit-form .field label{font-size:11px;color:var(--color-neutral-500);font-weight:600}
.visit-form select,.visit-form input,.visit-form textarea{
  padding:7px 9px;font-size:12.5px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-sm);
  font-family:var(--font-body);width:100%;
}
.visit-form textarea{resize:vertical}

/* Drag and drop — an unscheduled work order card onto a day target. Plain
   HTML5 DnD (dragstart/dragover/drop in schedule/index.blade.php), no
   library: dragging swaps the card's own opacity, hovering a valid target
   outlines it, and a drop submits the same POST the Reschedule button
   already uses via a single shared hidden form. */
.sched-card[draggable="true"]{cursor:grab}
.sched-card[draggable="true"]:active{cursor:grabbing}
.sched-card.dragging{opacity:.4}
[data-drop-date].drop-target{outline:2px dashed var(--color-accent-500);outline-offset:-2px;background:rgba(145,132,217,.08)}

/* Site-visit cards — distinct from .sched-card's work-order shape: a
   teal rail (matches the wordmark's own accent, see --brand-teal) instead
   of the blurple work-order rail, so the two schedule types stay visually
   distinct at a glance even before reading a card's content. */
.visit-card{border-left-color:var(--brand-teal)}
.visit-purpose{font-size:11px;color:var(--color-neutral-400);margin-top:4px;font-style:italic;overflow-wrap:anywhere}
.visit-actions{display:flex;gap:10px;margin-top:7px}
.visit-action-btn{background:none;border:none;color:var(--color-accent-300);font:600 11px var(--font-body);cursor:pointer;display:inline-flex;align-items:center;gap:4px;padding:0}
.visit-action-btn:hover{text-decoration:underline}
.visit-action-remove{color:var(--color-neutral-600)}
.visit-action-remove:hover{color:var(--status-bad)}
.visit-confirmed{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--status-good);margin-top:7px}
.sched-card.is-completed{opacity:.65}

.sched-card{background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-left:3px solid var(--color-accent-600);border-radius:var(--radius-md);padding:9px 11px;margin-bottom:8px;min-width:0;transition:border-color .12s ease,transform .12s ease}
.sched-card:last-child{margin-bottom:0}
.sched-card:hover{border-color:var(--color-accent-700)}
.sched-card .kc-t{font-size:12.5px;font-weight:600;color:var(--color-text);margin-bottom:2px;overflow-wrap:anywhere}
.sched-card .kc-t a{color:inherit;text-decoration:none}
.sched-card .kc-t a:hover{text-decoration:underline}
.sched-card .kc-s{font-size:11px;color:var(--color-neutral-500);display:flex;flex-wrap:wrap;justify-content:space-between;gap:2px 6px}
.sched-card .kc-s span:first-child{min-width:0;overflow-wrap:anywhere}
.sched-card .kc-s .amt{font-variant-numeric:tabular-nums;color:var(--color-neutral-400);font-weight:600;flex-shrink:0}
.sched-resched{margin-top:6px}
.sched-resched .wo-schedule-view{font-size:11px}
.sched-resched .wo-schedule-view button{font-size:11px;font-weight:600;color:var(--color-accent-300);gap:4px;padding:0}
.sched-resched .wo-schedule-view button:hover{background:none;text-decoration:underline}
.sched-resched .wo-schedule-view.unset button{color:var(--status-warn)}
.sched-resched .wo-schedule-form{margin-top:5px;flex-wrap:wrap}

/* Week view */
.week-grid-wrap{overflow-x:auto;padding-bottom:4px}
.week-grid{display:grid;grid-template-columns:repeat(7,minmax(150px,1fr));gap:10px;min-width:820px}
/* min-width:0 overrides the grid item's default min-width:auto, which
   otherwise sizes to the content's max-content width (a card's amount +
   crew text on one line) and lets a single wide card blow the whole
   column past its 1fr track into its neighbor -- exactly what happened
   before this was added. */
.week-col{background:var(--surface-col);border:1px solid var(--color-neutral-800);border-radius:var(--radius-lg);padding:10px;min-height:150px;min-width:0;overflow-x:hidden}
.week-col.is-today{border-color:var(--color-accent-600);box-shadow:0 0 0 1px var(--color-accent-700)}
.week-col-head{display:flex;align-items:baseline;gap:4px;margin-bottom:9px;padding-bottom:8px;border-bottom:1px solid var(--color-neutral-800)}
.week-col-head .wd{font:600 11.5px var(--font-body);color:var(--color-neutral-400);text-transform:uppercase;letter-spacing:.03em}
.week-col-head .wcn{font-size:15px;font-weight:700;color:var(--color-text)}
.week-col.is-today .week-col-head .wd,.week-col.is-today .week-col-head .wcn{color:var(--color-accent-200)}
.week-col-head .kn{margin-left:auto;background:var(--track);color:var(--color-neutral-400);font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:999px}
.sched-empty{font-size:11.5px;color:var(--color-neutral-600);padding:14px 0;text-align:center;font-style:italic}

/* Day view */
.day-view-wrap{display:flex;flex-direction:column;gap:10px;max-width:640px}
.day-view-total{font-size:12px;color:var(--color-neutral-500);margin-bottom:2px}
.day-view-wrap .sched-card{padding:13px 15px}
.day-view-wrap .sched-card .kc-t{font-size:13.5px}
.day-view-wrap .sched-card .kc-s{font-size:11.5px}

/* Month view */
.month-dow-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px;margin-bottom:6px}
.month-dow{font:600 11px var(--font-body);color:var(--color-neutral-600);text-transform:uppercase;letter-spacing:.04em;padding:0 2px}
.month-week{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px;margin-bottom:8px}
.month-cell{background:var(--surface-col);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);padding:7px;min-height:92px;min-width:0;overflow-x:hidden;display:flex;flex-direction:column;gap:4px}
.month-cell.not-in-month{opacity:.42}
.month-cell.is-today{border-color:var(--color-accent-600);box-shadow:0 0 0 1px var(--color-accent-700)}
.month-cell-num{font:600 12px var(--font-body);color:var(--color-neutral-400);text-decoration:none;align-self:flex-start;padding:1px 5px;border-radius:5px}
.month-cell-num:hover{background:rgba(145,132,217,.12);color:var(--color-accent-200)}
.month-cell.is-today .month-cell-num{background:var(--color-accent-700);color:var(--color-accent-100)}
.month-chip{display:block;font-size:10.5px;color:var(--color-neutral-300);background:var(--surface-sunken);border-left:2px solid var(--color-accent-600);border-radius:3px;padding:2px 5px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.month-chip:hover{color:var(--color-text);border-left-color:var(--color-accent-300)}
.month-more{font-size:10px;color:var(--color-neutral-500);text-decoration:none;padding:0 3px}
.month-more:hover{color:var(--color-accent-300)}

@media (max-width:960px){
  .schedule-shell{flex-direction:column}
  .schedule-sidebar{width:100%;max-height:none}
}

.tag-chip-row{display:inline-flex;flex-wrap:wrap;gap:4px;align-items:center}
.tag-chip{display:inline-flex;align-items:center;gap:4px;background:var(--color-accent-800);color:var(--color-accent-200);font-size:11px;font-weight:600;padding:3px 8px;border-radius:12px}
.tag-chip-x{cursor:pointer;color:var(--color-neutral-500);font-weight:700}
.tag-chip-x:hover{color:var(--status-bad)}
.tag-add-btn{font-size:11px !important;padding:3px 8px !important}

.attachment-thumb{width:70px;height:70px;object-fit:cover;border-radius:var(--radius-md);border:1px solid var(--color-neutral-800);cursor:pointer}
.attachment-thumb-video{width:160px;height:90px;border-radius:var(--radius-md);border:1px solid var(--color-neutral-800);background:#000}
.update-card{border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);padding:12px 14px;margin-bottom:10px}
.update-card:last-child{margin-bottom:0}

/* Status pill — same class name + modifiers as before ("badge") so no
   Blade changes are needed, restyled to the semantic status tokens. */
.badge{display:inline-flex;align-items:center;gap:6px;font:500 11.5px var(--font-body);padding:4px 9px;border-radius:999px}
.badge.paid,.badge.accepted,.badge.approved,.badge.confirmed,.badge.synced{color:var(--status-good);background:var(--status-good-bg)}
.badge.generated,.badge.pending,.badge.draft,.badge.suggested{color:var(--status-warn);background:var(--status-warn-bg)}
.badge.cancelled,.badge.rejected,.badge.error{color:var(--status-bad);background:var(--status-bad-bg)}
.badge.invoiced{color:var(--status-info);background:var(--status-info-bg)}

.warn-icon{color:var(--status-warn);font-weight:700}
.empty{text-align:center;padding:30px;color:var(--color-neutral-600);font-size:13px}

/* ── Lots list, grouped by community (/lots) ──────────────────────────────
   Replaces one flat table of every lot in the company with a collapsed-
   by-default <details> per community — the actual complaint (2026-08-27)
   was too much on one screen at once with no way to focus on just the
   job at hand. Native <details>/<summary> (same mechanism the mobile nav
   panel already uses) rather than a JS accordion — works with zero JS,
   the open/closed state is real DOM state a screen reader announces for
   free, and the small script in lots/index.blade.php only adds memory
   (localStorage) and the two bulk expand/collapse-all buttons on top. */
.community-toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
.community-toolbar-count{font-size:12px;color:var(--color-neutral-500)}
.community-groups{display:flex;flex-direction:column;gap:10px}
.community-group{
  border:1px solid var(--color-neutral-800);border-radius:var(--radius-lg);
  background:var(--surface-sunken);overflow:hidden;
}
.community-summary{
  display:flex;align-items:center;gap:12px;padding:13px 16px;cursor:pointer;
  list-style:none;-webkit-tap-highlight-color:transparent;
}
.community-summary::-webkit-details-marker{display:none}
.community-summary:hover{background:rgba(145,132,217,.06)}
.community-summary:focus-visible{outline:2px solid var(--color-accent);outline-offset:-2px}
.community-caret{
  flex-shrink:0;font-size:13px;color:var(--color-neutral-500);
  transition:transform .15s ease;
}
.community-group[open] .community-caret{transform:rotate(90deg)}
.community-summary-main{flex-shrink:0;min-width:0}
.community-summary-title{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.community-name{font:500 14.5px var(--font-heading);color:var(--color-text)}
.community-summary-sub{font-size:12px;color:var(--color-neutral-500);margin-top:2px}
.community-summary-chips{
  display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end;flex:1;min-width:0;
}
.status-chip{
  display:inline-flex;align-items:center;gap:4px;white-space:nowrap;
  font:500 11px var(--font-body);color:var(--color-neutral-400);
  background:var(--surface-col);border:1px solid var(--color-neutral-800);
  padding:3px 9px;border-radius:999px;
}
.status-chip b{font-weight:600;color:var(--color-neutral-200)}
.status-chip-variance.good{color:var(--status-good);border-color:transparent;background:var(--status-good-bg)}
.status-chip-variance.bad{color:var(--status-bad);border-color:transparent;background:var(--status-bad-bg)}
.community-group .table-wrap{padding:0 16px 14px}
.community-group table{background:var(--color-surface);border-radius:var(--radius-md);overflow:hidden}
.community-overflow-note{padding:0 16px 14px;font-size:12px;color:var(--color-neutral-500)}
@media (max-width:640px){
  .community-summary{flex-wrap:wrap}
  .community-summary-chips{justify-content:flex-start;width:100%}
}

.modal-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);z-index:200;align-items:center;justify-content:center;padding:16px}
.modal-overlay.open{display:flex}
.modal{background:var(--color-surface);border:1px solid var(--color-neutral-800);border-radius:var(--radius-lg);padding:22px;width:100%;max-width:520px;max-height:90vh;overflow-y:auto}
.modal h3{font:500 15px var(--font-heading);color:var(--color-text);margin-bottom:14px}
.modal-actions{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}

.suggest-row{display:grid;grid-template-columns:1fr 80px 110px 110px 90px;gap:8px;align-items:center;padding:8px;border-bottom:1px solid var(--color-neutral-800);font-size:12px}
.suggest-row.warn{background:var(--status-warn-bg)}
.suggest-row input{padding:5px 8px;font-size:12px;border:1px solid var(--color-neutral-800);background:var(--surface-sunken);color:var(--color-text);border-radius:5px;width:100%}

.toast{
  position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(80px);
  background:var(--color-surface);border:1px solid var(--color-neutral-800);color:var(--color-text);
  padding:11px 20px;border-radius:var(--radius-md);font-size:13px;font-weight:600;z-index:999;
  transition:transform .3s;white-space:nowrap;
}
.toast.show{transform:translateX(-50%) translateY(0)}
.toast.success{border-color:var(--status-good);color:var(--status-good)}
.toast.error{border-color:var(--status-bad);color:var(--status-bad)}

.swatch{width:18px;height:18px;border-radius:4px;display:inline-block;vertical-align:middle;margin-right:6px;border:1px solid rgba(255,255,255,.15)}
.loading{text-align:center;padding:40px;color:var(--color-neutral-600)}

@media (max-width:768px){
  #posTableWrap.mobile-hide{display:none}
  #posFilterBar.mobile-hide, #posBulkBar.mobile-hide{display:none !important}
}

/* ── Kanban boards — /lots/kanban, /purchase-orders/kanban, /work-orders/kanban.
   Structural/visual pass (column totals, the "live" accent column, card
   hover states) is a later phase; tokens updated now so nothing renders
   with a missing color in the meantime. */
.kanban-wrap{overflow-x:auto;padding-bottom:6px;position:relative}
.kanban-cols{display:flex;gap:12px;min-width:min-content}
.kanban-col{background:var(--surface-col);border:1px solid var(--color-neutral-800);border-radius:12px;padding:11px;width:198px;flex-shrink:0}
.kanban-col-head{display:flex;justify-content:space-between;align-items:center;padding:0 0 9px;font:500 13px var(--font-body);color:var(--color-text)}
.kanban-col-head .kn{background:var(--track);border-radius:999px;padding:1px 8px;font-size:11.5px;color:var(--color-neutral-400)}
.kanban-card{background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:10px;padding:10px 11px;margin-bottom:7px;cursor:grab;transition:border-color .12s ease,background-color .12s ease}
.kanban-card:hover{border-color:var(--color-accent-700);background:#212433}
.kanban-card:active{cursor:grabbing}
.kanban-card[dragging]{opacity:.4}
.kanban-col.kb-accepted{border-color:var(--color-accent-800);background:linear-gradient(180deg,rgba(145,132,217,.10),#1a1c27 60%);box-shadow:0 0 0 1px rgba(145,132,217,.18)}
.kanban-col.kb-accepted .kanban-col-head .kn{background:var(--color-accent-700);color:var(--color-accent-100)}
.kanban-col.kb-paid{opacity:.85}
.kanban-col{transition:background-color .1s ease}
.kanban-col.drag-over{background:#242737}
.kanban-card .kc-t{font-size:13px;font-weight:500;color:var(--color-text);margin-bottom:3px;overflow-wrap:break-word}
.kanban-card .kc-s{font-size:11.5px;color:var(--color-neutral-500);margin-bottom:6px;overflow-wrap:break-word}
.kanban-card .kc-amt{font-size:13.5px;font-weight:500;color:var(--color-neutral-200);font-variant-numeric:tabular-nums}
/* The one figure worth calling out on a card (margin %, PO total…) as an
   actual pill instead of plain text — every other status/summary figure
   in the app reads as a pill (badges, count chips); kanban cards were
   the one place that broke that visual language. */
.kc-pill{
  display:inline-flex;align-items:center;padding:3px 10px;border-radius:999px;
  background:var(--surface-col);border:1px solid var(--color-neutral-800);
}

/* ── Chat drawer — persistent (layouts/legacy.blade.php), toggled from the
   sidebar footer on desktop and the mobile topbar below 900px. One shared
   team room; live via Reverb (see routes/channels.php + ChatMessageSent).
   ─────────────────────────────────────────────────────────────────────── */
/* .ai-toggle shares this visual styling but is deliberately NOT also
   given the .chat-toggle class in markup — chat-drawer.blade.php's own
   script does querySelectorAll('.chat-toggle') to wire up the TEAM
   drawer, so a button carrying both classes would open/close both
   drawers at once instead of just the one it's actually for. */
.chat-toggle,.ai-toggle{
  position:relative;display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:var(--radius-md);border:1px solid var(--color-neutral-800);
  background:none;color:var(--color-neutral-400);cursor:pointer;font-size:17px;flex-shrink:0;
}
.chat-toggle:hover,.ai-toggle:hover{border-color:var(--color-neutral-700);color:var(--color-neutral-100)}
.chat-toggle:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}
.chat-toggle .chat-badge{
  position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;
  background:var(--status-bad);color:#fff;font:600 10px var(--font-body);
  display:none;align-items:center;justify-content:center;line-height:1;
}
.chat-toggle .chat-badge.show{display:flex}
.mobile-topbar .chat-toggle{margin-left:auto;margin-right:8px}
.mobile-topbar .ai-toggle{margin-right:10px}

.chat-drawer{
  position:fixed;top:0;right:-380px;bottom:0;width:380px;max-width:100vw;z-index:300;
  background:var(--surface-rail);border-left:1px solid var(--color-neutral-800);
  display:flex;flex-direction:column;transition:right .18s ease;box-shadow:-8px 0 24px rgba(0,0,0,.3);
}
.chat-drawer.open{right:0}
.chat-drawer-head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:16px 18px;border-bottom:1px solid var(--color-neutral-800);flex-shrink:0;
}
.chat-drawer-head-title{display:flex;align-items:center;gap:8px;min-width:0}
.chat-drawer-head h3{font:500 15px var(--font-heading);color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chat-drawer-head-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}
.chat-back,.chat-new-dm,.chat-drawer-close{background:none;border:none;color:var(--color-neutral-500);cursor:pointer;font-size:18px;padding:4px;border-radius:var(--radius-sm);display:flex}
.chat-back:hover,.chat-new-dm:hover,.chat-drawer-close:hover{color:var(--color-neutral-200)}

/* ── Conversation list — the drawer's default/home view: Team room
   pinned at top, then every DM partner, newest activity first. ── */
.chat-conv-list{flex:1;overflow-y:auto;padding:6px 0}
.chat-conv-row{
  display:flex;align-items:center;gap:10px;padding:10px 18px;cursor:pointer;
  transition:background-color .1s ease;
}
.chat-conv-row:hover{background:var(--surface-sunken)}
.chat-conv-avatar{
  width:34px;height:34px;border-radius:50%;flex-shrink:0;position:relative;
  background:var(--color-accent-800);color:var(--color-accent-100);
  display:flex;align-items:center;justify-content:center;font:600 13px var(--font-heading);
}
.chat-conv-avatar-team{background:rgba(145,132,217,.14);color:var(--color-accent-300);font-size:16px}
.chat-conv-avatar.online::after{
  content:'';position:absolute;bottom:-1px;right:-1px;width:9px;height:9px;border-radius:50%;
  background:var(--status-good);border:2px solid var(--surface-rail);
}
.chat-conv-info{flex:1;min-width:0}
.chat-conv-name{font:500 13.5px var(--font-body);color:var(--color-text)}
.chat-conv-preview{font-size:12px;color:var(--color-neutral-500);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}
.chat-conv-unread{
  flex-shrink:0;min-width:18px;height:18px;padding:0 5px;border-radius:999px;
  background:var(--status-bad);color:#fff;font:600 10.5px var(--font-body);
  display:flex;align-items:center;justify-content:center;
}
.chat-conv-divider{padding:12px 18px 6px;font:600 10.5px var(--font-heading);letter-spacing:.06em;text-transform:uppercase;color:var(--color-neutral-600)}
.chat-conv-empty{padding:16px 18px;color:var(--color-neutral-600);font-size:12.5px;line-height:1.5}

/* ── New-DM people picker — replaces the conv list, not a modal, so the
   back button always returns to the same place. ── */
.chat-people-picker{flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0}
.chat-people-search{
  margin:10px 18px;padding:8px 11px;font-size:13px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);
  font-family:var(--font-body);flex-shrink:0;
}
.chat-people-search:focus{outline:2px solid var(--color-accent);outline-offset:1px;border-color:transparent}
.chat-people-list{flex:1;overflow-y:auto}
.chat-people-item{display:flex;align-items:center;gap:10px;padding:9px 18px;cursor:pointer}
.chat-people-item:hover{background:var(--surface-sunken)}
.chat-people-empty{padding:16px 18px;color:var(--color-neutral-600);font-size:12.5px}

/* Wraps presence/messages/compose so only one of conv-list / people-
   picker / thread is visible at a time, each filling the same space. */
.chat-thread{display:flex;flex-direction:column;flex:1;min-height:0}

.chat-presence{
  display:flex;gap:6px;padding:10px 18px;border-bottom:1px solid var(--color-neutral-800);
  overflow-x:auto;flex-shrink:0;
}
.chat-presence-empty{font-size:12px;color:var(--color-neutral-600);padding:2px 0}
.chat-presence-user{
  display:flex;align-items:center;gap:5px;flex-shrink:0;
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:999px;
  padding:4px 9px 4px 6px;font:500 11.5px var(--font-body);color:var(--color-neutral-300);white-space:nowrap;
}
.chat-presence-dot{width:6px;height:6px;border-radius:50%;background:var(--status-good);flex-shrink:0}

.chat-messages{flex:1;overflow-y:auto;padding:14px 18px;display:flex;flex-direction:column;gap:12px}
.chat-load-older{
  align-self:center;font-size:12px;color:var(--color-accent-300);background:none;border:none;cursor:pointer;
  padding:4px 8px;
}
.chat-msg{display:flex;flex-direction:column;gap:2px;max-width:88%}
.chat-msg.own{align-self:flex-end;align-items:flex-end}
.chat-msg-meta{display:flex;gap:6px;align-items:baseline;font-size:11px;color:var(--color-neutral-600)}
.chat-msg-meta .who{font-weight:600;color:var(--color-neutral-400)}
.chat-msg-body{
  font-size:13.5px;line-height:1.5;color:var(--color-neutral-200);background:var(--surface-sunken);
  border:1px solid var(--color-neutral-800);border-radius:10px;padding:8px 12px;white-space:pre-wrap;word-break:break-word;
}
.chat-msg.own .chat-msg-body{background:rgba(145,132,217,.14);border-color:var(--color-accent-800)}
.chat-msg-body .mention{color:var(--color-accent-300);font-weight:600}
.chat-msg.mentions-me .chat-msg-body{border-color:var(--status-warn)}
.chat-empty{margin:auto;color:var(--color-neutral-600);font-size:13px;text-align:center}

.chat-compose{border-top:1px solid var(--color-neutral-800);padding:12px 14px;flex-shrink:0;position:relative}
.chat-mention-menu{
  position:absolute;left:14px;right:14px;bottom:100%;margin-bottom:6px;
  background:var(--color-surface);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);
  max-height:160px;overflow-y:auto;display:none;box-shadow:0 8px 20px rgba(0,0,0,.35);
}
.chat-mention-menu.show{display:block}
.chat-mention-item{padding:7px 12px;font-size:12.5px;color:var(--color-neutral-300);cursor:pointer}
.chat-mention-item.active,.chat-mention-item:hover{background:rgba(145,132,217,.12);color:var(--color-neutral-100)}
.chat-compose-row{display:flex;gap:8px;align-items:flex-end}
.chat-compose textarea{
  flex:1;resize:none;max-height:90px;padding:9px 11px;font-size:13.5px;line-height:1.4;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-md);
  font-family:var(--font-body);
}
.chat-compose textarea:focus{outline:2px solid var(--color-accent);outline-offset:1px;border-color:transparent}
.chat-compose-send{
  width:38px;height:38px;flex-shrink:0;border-radius:var(--radius-md);border:1px solid var(--color-accent);
  background:transparent;color:var(--color-accent-200);cursor:pointer;font-size:16px;
  display:flex;align-items:center;justify-content:center;
}
.chat-compose-send:hover{background:rgba(145,132,217,.14)}
.chat-compose-send:disabled{opacity:.4;cursor:default}

@media (max-width:480px){ .chat-drawer{width:100vw;right:-100vw} }

/* ── "View as" (sidebar footer, Admin only) — quick role/permission
   preview without a real logout/login. See startImpersonation() in
   legacy/lib/functions.php. ── */
.view-as-form{padding:0 6px;display:flex;flex-direction:column;gap:5px}
.view-as-form label{font:500 10.5px var(--font-body);letter-spacing:.05em;text-transform:uppercase;color:var(--color-neutral-600)}
.view-as-row{display:flex;gap:6px}
.view-as-row select{
  flex:1;min-width:0;height:30px;padding:0 8px;font-size:12px;color:var(--color-text);
  background:var(--surface-sunken);border:1px solid var(--color-neutral-800);border-radius:var(--radius-sm);
  font-family:var(--font-body);
}
.view-as-row .btn{height:30px}
/* Unmissable — this must never read as just another flash message, since
   the entire point is "you are not actually this person, don't forget
   it" for as long as it's up. */
.impersonate-banner{
  display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  background:var(--status-warn-bg);border:1px solid var(--status-warn);color:var(--status-warn);
  border-radius:var(--radius-md);padding:10px 14px;font-size:13px;margin-bottom:14px;
}
.impersonate-banner strong{color:var(--color-text)}
.impersonate-banner .btn{border-color:var(--status-warn);color:var(--status-warn);background:transparent}
.impersonate-banner .btn:hover{background:var(--status-warn-bg)}

/* ── Skin switcher control (sidebar footer) ──────────────────────────────
   Two small swatches, not a settings page — matches how understated
   everything else in the sidebar footer is. */
.skin-switch{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;padding:0 6px}
.skin-swatch{
  height:26px;border-radius:var(--radius-sm);border:1px solid var(--color-neutral-800);
  background:var(--surface-sunken);color:var(--color-neutral-500);cursor:pointer;
  font:500 10.5px var(--font-body);display:flex;align-items:center;justify-content:center;
  letter-spacing:.03em;
}
.skin-swatch:hover{color:var(--color-neutral-200);border-color:var(--color-neutral-700)}
.skin-swatch.active{color:var(--color-accent-200);border-color:var(--color-accent-700);background:rgba(145,132,217,.14)}
.skin-swatch:focus-visible{outline:2px solid var(--color-accent);outline-offset:1px}

/* ── Depth skin ───────────────────────────────────────────────────────────
   "Shadows yes, highlights no." No inset light edges, no text-shadow glow,
   no filter:drop-shadow, no second saturated field anywhere — those were
   tried against this exact design and rejected. Everything below is
   additive over the flat skin's rules above; same tokens, no new colors. */
[data-skin="depth"] body{
  background:
    radial-gradient(85% 65% at 6% -5%, rgba(76,83,151,.34), transparent 62%),
    radial-gradient(70% 60% at 104% 104%, rgba(145,132,217,.13), transparent 66%),
    linear-gradient(180deg,#191b2b,#111220);
  background-attachment:fixed;
}
[data-skin="depth"] body::before{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:0;opacity:.05;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* .sidebar is NOT included here on purpose — it's already position:fixed
   with its own z-index:100 (see the base .sidebar rule), which already
   sits above the grain layer below. Adding position:relative here too
   (an earlier version of this rule did) overrode that fixed positioning
   entirely, pulling the sidebar's ~900px of nav content into normal
   document flow and shoving .main down by exactly that much — the
   depth skin's "everything is pushed down the page" bug. */
[data-skin="depth"] .main{position:relative;z-index:1}

[data-skin="depth"] .card,[data-skin="depth"] .login-shell{
  background:linear-gradient(180deg,#232634,#1b1d29);
  border-color:#2b2e3c;box-shadow:0 14px 34px rgba(0,0,0,.5);
}

[data-skin="depth"] .sidebar{
  background:linear-gradient(180deg,rgba(18,19,32,.85),rgba(13,14,24,.92));
  border-right-color:transparent;
}
[data-skin="depth"] .sidebar::after{
  content:"";position:absolute;top:0;right:0;bottom:0;width:1px;
  background:linear-gradient(180deg,transparent,rgba(145,132,217,.30) 14%,rgba(63,66,77,.9) 55%,transparent);
}
[data-skin="depth"] .nav-item:hover{background:linear-gradient(90deg,rgba(145,132,217,.14),rgba(145,132,217,.02))}
[data-skin="depth"] .nav-item.active{background:linear-gradient(90deg,rgba(145,132,217,.22),rgba(145,132,217,.02))}

[data-skin="depth"] .btn-primary,[data-skin="depth"] .btn-gold{
  border-color:rgba(145,132,217,.75);
  background:linear-gradient(180deg,rgba(145,132,217,.20),rgba(145,132,217,.06));
  box-shadow:0 4px 12px rgba(0,0,0,.3);
}
[data-skin="depth"] .btn-primary:hover,[data-skin="depth"] .btn-gold:hover{
  background:linear-gradient(180deg,rgba(145,132,217,.30),rgba(145,132,217,.10));
  box-shadow:0 6px 16px rgba(0,0,0,.38);
}
[data-skin="depth"] .btn-ghost{
  background:linear-gradient(180deg,#22242f,#1c1e28);box-shadow:0 3px 9px rgba(0,0,0,.28);
}
[data-skin="depth"] .btn-ghost:hover{border-color:#454a5c}

/* the one saturated field — dashboard's 4-tile band only (see
   .dashboard-kpis), never the 6+-tile .kpi-row on Financial Report/Cash
   Position: "exactly one per page," and those pages already read as a
   row of stat cards, not a hero band. They still get the shadow language
   via the plain .card-style .kpi rule just below. */
[data-skin="depth"] .kpi{
  background:linear-gradient(180deg,#23253260,#1c1e2a);
  box-shadow:0 6px 16px rgba(0,0,0,.28);
}
[data-skin="depth"] .dashboard-kpis{
  background:linear-gradient(118deg,#272b60,#212449 42%,#1b1d30 78%,#191b29);
  border:1px solid #333868;border-radius:16px;padding:20px;margin-bottom:0;
  box-shadow:0 18px 46px rgba(10,11,30,.6);
}
[data-skin="depth"] .dashboard-kpis .kpi{
  background:rgba(10,11,26,.30);border:1px solid rgba(255,255,255,.055);box-shadow:0 6px 16px rgba(0,0,0,.28);
}
[data-skin="depth"] .dashboard-kpis .kpi-label{color:#b6b3e0}
[data-skin="depth"] .dashboard-kpis .kpi-val{color:#fff}
[data-skin="depth"] .dashboard-kpis .kpi-context{color:#9a97c4}

[data-skin="depth"] .kanban-card{
  background:linear-gradient(180deg,#212431,#1a1c27);border-color:#2a2d3b;
  box-shadow:0 6px 16px rgba(0,0,0,.35);transition:transform .14s ease,border-color .14s ease,background-color .14s ease;
}
[data-skin="depth"] .kanban-card:hover{transform:translateY(-1px)}

[data-skin="depth"] .cash-track,[data-skin="depth"] .phase-bar-track{box-shadow:inset 0 1px 2px rgba(0,0,0,.5)}
[data-skin="depth"] .cash-track-fill{background:linear-gradient(90deg,#5d5294,#a99cf0)}

[data-skin="depth"] .badge.paid,[data-skin="depth"] .badge.accepted,[data-skin="depth"] .badge.approved,[data-skin="depth"] .badge.confirmed{
  background:linear-gradient(180deg,rgba(111,191,139,.22),rgba(111,191,139,.07));
}
[data-skin="depth"] .badge.generated,[data-skin="depth"] .badge.pending,[data-skin="depth"] .badge.draft,[data-skin="depth"] .badge.suggested{
  background:linear-gradient(180deg,rgba(229,181,103,.22),rgba(229,181,103,.07));
}
[data-skin="depth"] .badge.cancelled,[data-skin="depth"] .badge.rejected{
  background:linear-gradient(180deg,rgba(217,122,114,.22),rgba(217,122,114,.07));
}
[data-skin="depth"] .badge.invoiced{
  background:linear-gradient(180deg,rgba(145,132,217,.22),rgba(145,132,217,.07));
}

/* ── Novelty skins ────────────────────────────────────────────────────────
   Three "just for fun" palettes on the same structure as flat/depth above
   — real token swaps (every color below feeds the SAME custom properties
   every component already reads), not a fifth CSS framework. Status colors
   still keep their own hue family per skin (good=green-ish, bad=red-ish,
   etc.) — meaning must never shift, only mood. Each also gets a few of the
   highest-traffic hardcoded accent tints (nav hover/active, primary
   button, chat's own-message bubble, the login page's corner glow)
   repointed at the new accent — same proportional scope Depth itself
   used, not a hunt for all 23 rgba(145,132,217,…) accent-tint spots in
   this file. Headings only switch font — body text stays Inter
   everywhere, on purpose, so none of this trades away readability. */

/* Soda — neon arcade-cup pink/cyan under blacklight, chunky poster type. */
:root[data-skin="soda"]{
  --color-bg:#10091f; --color-surface:#1f1233; --color-text:#f5eefd; --color-accent:#ff2e88;

  --color-neutral-100:#fbeaff; --color-neutral-200:#ecd6f7; --color-neutral-300:#d9bfe8;
  --color-neutral-400:#b79fcf; --color-neutral-500:#9884b3; --color-neutral-600:#7a6796;
  --color-neutral-700:#5c4d75; --color-neutral-800:#402f57; --color-neutral-900:#2a1c3d;

  --color-accent-100:#fff0f8; --color-accent-200:#ffd6ec; --color-accent-300:#ffadd8;
  --color-accent-400:#ff7cbe; --color-accent-500:#ff2e88; --color-accent-600:#e01a70;
  --color-accent-700:#b01259; --color-accent-800:#7a0d3f; --color-accent-900:#4d0828;

  --status-good:#3cffb0;  --status-good-bg:rgba(60,255,176,.16);
  --status-warn:#ffe135;  --status-warn-bg:rgba(255,225,53,.18);
  --status-bad:#ff4d6a;   --status-bad-bg:rgba(255,77,106,.16);
  --status-info:#35d4ff;  --status-info-bg:rgba(53,212,255,.16);

  --surface-sunken:#180b28; --surface-rail:#0e0719; --surface-col:#170a26; --track:#2e1a45;
  --brand-teal:#00e0d1;
  --font-heading:"Bungee","Arial Black",sans-serif; --font-heading-weight:400;
}
[data-skin="soda"] body{
  background:
    radial-gradient(60% 50% at 8% 0%, rgba(255,46,136,.22), transparent 60%),
    radial-gradient(55% 50% at 100% 100%, rgba(0,224,209,.18), transparent 62%),
    linear-gradient(180deg,#150a26,#0d0718);
  background-attachment:fixed;
}
[data-skin="soda"] .card,[data-skin="soda"] .login-shell{border-width:2px;border-color:#3a2755}
[data-skin="soda"] .nav-item:hover{background:rgba(255,46,136,.12);color:var(--color-neutral-200)}
[data-skin="soda"] .nav-item.active{background:rgba(255,46,136,.16);color:var(--color-neutral-100)}
[data-skin="soda"] .btn-primary,[data-skin="soda"] .btn-gold{border-width:2px}
[data-skin="soda"] .btn-primary:hover,[data-skin="soda"] .btn-gold:hover{background:rgba(255,46,136,.16)}
[data-skin="soda"] .chat-msg.own .chat-msg-body{background:rgba(255,46,136,.16);border-color:var(--color-accent-800)}
[data-skin="soda"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, rgba(255,46,136,.24), transparent 60%)}

/* Viking — full replacement (2026-08 handoff, "oak, iron, brass"), not the
   original thin skin. Old version painted wood-brown behind every table
   and set MedievalSharp as --font-heading, which meant KPI values and
   table headers rendered in blackletter — reads as costume, not data.
   The fix: one rule only. Oak (page ground, sidebar) never sits behind a
   table/card/stat — those are all struck-iron plates instead. Brass is
   reserved for money and the primary action, not decoration. Cinzel
   (--font-heading) stays on true headings/labels/buttons/tags; EB Garamond
   (--font-body) carries all read text and table data, which is also why
   .kpi-val (a *big standalone* money figure, not a table cell) is left on
   --font-heading below — the handoff's own rule is "large money figures
   use the carved face, table-cell numbers use the read face." */
:root[data-skin="viking"]{
  --color-bg:#241a11; --color-surface:#1d1b1a; --color-text:#f0e2c2; --color-accent:#c9973f;

  --color-neutral-100:#f0e2c2; --color-neutral-200:#ddd0b2; --color-neutral-300:#c3b498;
  --color-neutral-400:#a2937a; --color-neutral-500:#8f8069; --color-neutral-600:#7d7159;
  --color-neutral-700:#45403a; --color-neutral-800:#33302e; --color-neutral-900:#22201f;

  --color-accent-100:#fbf1de; --color-accent-200:#f5e3ae; --color-accent-300:#e8c274;
  --color-accent-400:#deae45; --color-accent-500:#c9973f; --color-accent-600:#9c7229;
  --color-accent-700:#7c5a1e; --color-accent-800:#523a12; --color-accent-900:#31220b;

  /* moss/gold/blood — same green=good/amber=pending/red=bad meaning as
     every other skin (see badge rules below), just this skin's materials. */
  --status-good:#a9c07d;  --status-good-bg:rgba(123,143,90,.16);
  --status-warn:#e0a95c;  --status-warn-bg:rgba(201,151,63,.14);
  --status-bad:#d08076;   --status-bad-bg:rgba(164,68,58,.16);
  --status-info:#e0a95c;  --status-info-bg:rgba(201,151,63,.14);

  --surface-sunken:#191614; --surface-rail:#2c2014; --surface-col:#201d1b; --track:#141312;
  --brand-teal:#e0a95c;
  --font-heading:"Cinzel",Georgia,serif; --font-heading-weight:600;
  --font-body:"EB Garamond",Georgia,serif;
  --radius-sm:2px; --radius-md:3px; --radius-lg:4px;

  /* the handoff's own material tokens, kept for readability in the rules
     below rather than inlining every hex twice */
  --oak:#3a2a1c; --iron:#22201f; --iron-edge:#45403a; --iron-black:#100f0e;
  --brass:#c9973f; --brass-light:#e8c274; --brass-deep:#9c7229; --brass-border:#7c5a1e; --brass-ink:#2a1c07;
  --moss-border:#4d5c33; --blood-border:#5e2f2a;
}
/* Oak ground — layered grain + a warm hearth glow top-left, cool falloff
   bottom-right, as a ::before so it never tints content sitting on top. */
[data-skin="viking"] body{
  background:
    repeating-linear-gradient(92deg, rgba(0,0,0,.22) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(90deg, rgba(255,255,255,.022) 0 2px, transparent 2px 9px),
    repeating-linear-gradient(89deg, rgba(0,0,0,.13) 0 3px, transparent 3px 17px),
    linear-gradient(180deg,#43301f,#2c2014 45%,#241a11);
  background-attachment:fixed;
}
[data-skin="viking"] body::before{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
  background:
    radial-gradient(70% 50% at 22% -6%, rgba(226,150,72,.16), transparent 62%),
    radial-gradient(60% 46% at 108% 108%, rgba(120,60,20,.16), transparent 60%);
}
/* .sidebar already has its own z-index:100 in the base rule (needs to
   stay above page content regardless of skin) — only .main needs lifting
   above the ::before overlay here, so this doesn't touch that. */
[data-skin="viking"] .main{position:relative;z-index:1}
/* Planked oak — the sidebar. Vertical seams, inset shadow at the right
   edge, capped with a raised iron strip. */
[data-skin="viking"] .sidebar{
  background:
    repeating-linear-gradient(180deg, rgba(0,0,0,.42) 0 2px, rgba(0,0,0,.12) 2px 4px, transparent 4px 74px),
    repeating-linear-gradient(91deg, rgba(0,0,0,.20) 0 1px, transparent 1px 5px),
    linear-gradient(180deg,#4a3521,#33251733 60%,#2a1e13);
  box-shadow:inset -14px 0 26px rgba(0,0,0,.5);
}
[data-skin="viking"] .sidebar::after{
  content:"";position:absolute;top:0;right:0;bottom:0;width:4px;
  background:linear-gradient(180deg,#5a5148,#2a2723);box-shadow:-1px 0 6px rgba(0,0,0,.6);
}
/* Iron — every plate that holds data. One lit top edge (a bevel on struck
   metal), hard black border, deep shadow. Never a page background. */
[data-skin="viking"] .card,[data-skin="viking"] .table-wrap,[data-skin="viking"] .kpi,[data-skin="viking"] .login-shell{
  background:linear-gradient(180deg,#2b2827,#1d1b1a 55%,#161514);
  border:1px solid var(--iron-black);
  border-top:1px solid var(--iron-edge);
  box-shadow:0 14px 32px rgba(0,0,0,.62), 0 2px 0 rgba(0,0,0,.5);
}
[data-skin="viking"] .card h2{font-weight:600;letter-spacing:.02em}
/* Brass — money received, total paid, the primary action, the active nav
   mark. Roughly four moments per screen, nowhere else. */
[data-skin="viking"] .btn-primary,[data-skin="viking"] .btn-gold{
  background:linear-gradient(180deg,#e0b465,var(--brass) 46%,var(--brass-deep));
  color:var(--brass-ink); border:1px solid var(--brass-border);
  box-shadow:0 5px 14px rgba(0,0,0,.55);
}
[data-skin="viking"] .btn-primary:hover,[data-skin="viking"] .btn-gold:hover{
  background:linear-gradient(180deg,#eec37a,#d5a34a 46%,#a87c2c);
}
[data-skin="viking"] .btn{font:600 12.5px var(--font-heading);letter-spacing:.06em;text-transform:uppercase}
[data-skin="viking"] .btn-ghost{
  background:linear-gradient(180deg,#3a3634,#262422);color:#d9cbaa;
  border:1px solid #141312;box-shadow:0 4px 10px rgba(0,0,0,.5);
}
[data-skin="viking"] .btn-ghost:hover{background:linear-gradient(180deg,#464240,#2d2b28);color:var(--color-text)}
[data-skin="viking"] .btn:focus-visible{outline:2px solid var(--brass);outline-offset:2px}
/* Status tags — square-cut, carved caps, moss/brass/blood. */
[data-skin="viking"] .badge{
  padding:4px 11px;border-radius:2px;border:1px solid;
  font:600 10.5px var(--font-heading);letter-spacing:.1em;text-transform:uppercase;
}
[data-skin="viking"] .badge.paid,[data-skin="viking"] .badge.accepted,[data-skin="viking"] .badge.approved,[data-skin="viking"] .badge.confirmed{
  border-color:var(--moss-border);
}
[data-skin="viking"] .badge.generated,[data-skin="viking"] .badge.pending,[data-skin="viking"] .badge.draft,[data-skin="viking"] .badge.suggested,[data-skin="viking"] .badge.invoiced{
  border-color:var(--brass-border);
}
[data-skin="viking"] .badge.cancelled,[data-skin="viking"] .badge.rejected{
  border-color:var(--blood-border);
}
/* Tables — headers carved, cells in the read face, quiet hover. */
[data-skin="viking"] th{color:#9d8c6c;letter-spacing:.13em}
[data-skin="viking"] td{color:var(--color-neutral-200)}
[data-skin="viking"] tbody tr:hover td{background:rgba(201,151,63,.07)}
/* Nav */
[data-skin="viking"] .nav-item:hover{background:rgba(201,151,63,.10);color:var(--color-text)}
[data-skin="viking"] .nav-item.active{
  background:linear-gradient(90deg,rgba(201,151,63,.24),rgba(201,151,63,.03));
  color:#f3e6c8;box-shadow:inset 2px 0 0 var(--brass);
}
[data-skin="viking"] .nav-item .count-pill{
  background:linear-gradient(180deg,#e0b465,#a87c2c);color:var(--brass-ink);
  font:600 11px var(--font-heading);
}
/* Group headers get a rule that trails off, not a plain label — needs
   display:flex since the base rule is just padding+font. */
[data-skin="viking"] .nav-group-label{display:flex;align-items:center;gap:9px}
[data-skin="viking"] .nav-group-label::after{
  content:"";flex:1;height:1px;
  background:linear-gradient(90deg,#4a4238,transparent);
}
[data-skin="viking"] a:not([class]){background:none;color:var(--color-accent-300)}
[data-skin="viking"] a:not([class]):hover{background:none;color:var(--brass-light);text-decoration:underline}
[data-skin="viking"] .chat-msg.own .chat-msg-body{background:rgba(201,151,63,.14);border-color:var(--brass-border)}
[data-skin="viking"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, rgba(201,151,63,.20), transparent 60%)}
/* Kanban — same treatment level as Depth/Paper get. */
[data-skin="viking"] .kanban-card:hover{background:#242120;border-color:var(--brass-border)}
[data-skin="viking"] .kanban-col.kb-accepted{background:linear-gradient(180deg,#241f16,#1d1b1a);border-color:var(--brass-border)}
[data-skin="viking"] .kanban-col.drag-over{background:#242120}
/* The dashboard's one saturated field — brass-lit iron instead of the
   plain .kpi plate every other page's stat row gets (see comment on
   Depth's version above for why only the 4-tile dashboard band, never
   the 6+-tile .kpi-row on Financial Report/Cash Position). */
[data-skin="viking"] .dashboard-kpis{
  background:linear-gradient(118deg,#332821,#241d17 42%,#1a1613 78%,#161311);
  border:1px solid var(--brass-border);box-shadow:0 18px 46px rgba(0,0,0,.6);
}
[data-skin="viking"] .dashboard-kpis .kpi{
  background:rgba(16,15,14,.4);border:1px solid rgba(201,151,63,.18);box-shadow:0 6px 16px rgba(0,0,0,.4);
}
[data-skin="viking"] .dashboard-kpis .kpi-label{color:var(--brass-light)}
[data-skin="viking"] .dashboard-kpis .kpi-val{color:var(--color-text)}
[data-skin="viking"] .dashboard-kpis .kpi-context{color:var(--color-neutral-400)}

/* Ancient scroll — parchment read by candlelight, wax-seal red, gold leaf. */
:root[data-skin="scroll"]{
  --color-bg:#1f160c; --color-surface:#2b2013; --color-text:#ecd9b0; --color-accent:#8a2f22;

  --color-neutral-100:#f7ecd2; --color-neutral-200:#ecd9ac; --color-neutral-300:#dcc084;
  --color-neutral-400:#c2a065; --color-neutral-500:#a3824f; --color-neutral-600:#7d6339;
  --color-neutral-700:#5c4829; --color-neutral-800:#3c2f1b; --color-neutral-900:#261f13;

  --color-accent-100:#fbeae7; --color-accent-200:#f0c3ba; --color-accent-300:#e0968a;
  --color-accent-400:#c2665a; --color-accent-500:#8a2f22; --color-accent-600:#6f251b;
  --color-accent-700:#521c14; --color-accent-800:#38130d; --color-accent-900:#210b08;

  --status-good:#7f8f5c;  --status-good-bg:rgba(127,143,92,.16);
  --status-warn:#c2973f;  --status-warn-bg:rgba(194,151,63,.18);
  --status-bad:#9c4a3a;   --status-bad-bg:rgba(156,74,58,.16);
  --status-info:#6a6a9b;  --status-info-bg:rgba(106,106,155,.16);

  --surface-sunken:#241b0f; --surface-rail:#160f08; --surface-col:#221a10; --track:#3a2c1a;
  --brand-teal:#c2973f;
  --font-heading:"IM Fell English SC",serif; --font-heading-weight:400;
}
[data-skin="scroll"] body{
  background:
    radial-gradient(90% 70% at 50% 0%, rgba(236,217,176,.06), transparent 55%),
    radial-gradient(120% 90% at 50% 100%, rgba(0,0,0,.35), transparent 60%),
    linear-gradient(180deg,#1c1409,#140e07);
  background-attachment:fixed;
}
[data-skin="scroll"] .card,[data-skin="scroll"] .login-shell{border-color:#4a3a22;box-shadow:0 10px 26px rgba(0,0,0,.45)}
[data-skin="scroll"] .nav-item:hover{background:rgba(138,47,34,.16);color:var(--color-neutral-200)}
[data-skin="scroll"] .nav-item.active{background:rgba(138,47,34,.22);color:var(--color-neutral-100)}
[data-skin="scroll"] .btn-primary,[data-skin="scroll"] .btn-gold{border-width:2px}
[data-skin="scroll"] .btn-primary:hover,[data-skin="scroll"] .btn-gold:hover{background:rgba(138,47,34,.20)}
[data-skin="scroll"] .chat-msg.own .chat-msg-body{background:rgba(138,47,34,.20);border-color:var(--color-accent-800)}
[data-skin="scroll"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, rgba(138,47,34,.28), transparent 60%)}

/* Light — the one non-dark skin. Everything above assumes dark surfaces
   with near-white text; this inverts that on purpose, not just at the
   bg/surface/text/accent level but through the WHOLE neutral ramp: every
   component already reads --color-neutral-100 as "the highest-contrast
   text tone" and --color-neutral-900 as "barely-there, blends into the
   surface" — dark mode gets that by making 100 near-white and 900
   near-black. Light mode gets the exact same components right by simply
   flipping which end is which: 100 is now near-black, 900 near-white.
   No component rule anywhere needed to change for text/border color to
   end up correct — that's the entire point of routing everything through
   the ramp instead of one-off hex values.
   Most of the accent ramp plays the same trick fine — but --color-
   accent-200 specifically is used BOTH as light text sitting on a dark
   accent-800 chip (count-pill, tag-chip: needs to stay light there) AND
   as plain accent-colored text sitting directly on a card/button
   (btn-primary, active nav-kanban-link, the chat send icon, the active
   skin swatch: needs to be dark enough to read on white). No single
   value is right in both places, so accent-200 stays light (the chip
   role, used in more places) and those handful of "direct on white"
   users get pointed at plain --color-accent instead, just below.
   --color-accent-300 had the same shape of problem but only ONE role
   (always direct text — mentions, visited pill-links, attn icons —
   never chip text), so it's simply defined dark from the start instead.
   A few components hardcode a dark hex instead of a token — invisible
   in dark mode's own math, but only because dark-on-dark never came up
   there. On white they'd be exactly the "light text on light bg"
   failure this skin exists to avoid, just the inverse (dark-on-dark):
   .kpi's own background (every KPI tile app-wide), two kanban card
   states, and the plain rgba(255,255,255,…) hairline row borders. All
   four get an explicit override below — everything else on this list
   only needed a token, not a rule. */
:root[data-skin="light"]{
  --color-bg:#f2f3f7; --color-surface:#ffffff; --color-text:#1a1c25; --color-accent:#6656c9;

  --color-neutral-100:#1a1c25; --color-neutral-200:#2c2e3a; --color-neutral-300:#41434f;
  --color-neutral-400:#585b68; --color-neutral-500:#6f7280; --color-neutral-600:#8a8d99;
  --color-neutral-700:#a8abb6; --color-neutral-800:#d3d5dd; --color-neutral-900:#e9ebf1;

  --color-accent-100:#efe9fc; --color-accent-200:#d3c6f5; --color-accent-300:#5c4bb8;
  --color-accent-400:#8873db; --color-accent-500:#6656c9; --color-accent-600:#5544a8;
  --color-accent-700:#443686; --color-accent-800:#332863; --color-accent-900:#241c47;

  --status-good:#2f8b5a;  --status-good-bg:rgba(47,139,90,.13);
  --status-warn:#a6720f;  --status-warn-bg:rgba(166,114,15,.14);
  --status-bad:#c23b30;   --status-bad-bg:rgba(194,59,48,.12);
  --status-info:#5544a8;  --status-info-bg:rgba(85,68,168,.12);

  --surface-sunken:#f0f1f5; --surface-rail:#ffffff; --surface-col:#eef0f4; --track:#e2e4ec;
  --brand-teal:#1f7c72;
}
[data-skin="light"] .card,[data-skin="light"] .login-shell{
  border-color:var(--color-neutral-800);box-shadow:0 1px 3px rgba(20,20,40,.06),0 8px 20px rgba(20,20,40,.05);
}
[data-skin="light"] .nav-item:hover{background:rgba(102,86,201,.08);color:var(--color-neutral-200)}
[data-skin="light"] .nav-item.active{background:rgba(102,86,201,.10);color:var(--color-neutral-100)}
[data-skin="light"] .btn-primary,[data-skin="light"] .btn-gold{color:var(--color-accent);background:rgba(102,86,201,.04)}
[data-skin="light"] .btn-primary:hover,[data-skin="light"] .btn-gold:hover{background:rgba(102,86,201,.10)}
[data-skin="light"] .nav-kanban-link.active,[data-skin="light"] .chat-compose-send,[data-skin="light"] .skin-swatch.active{color:var(--color-accent)}
[data-skin="light"] .chat-msg.own .chat-msg-body{background:rgba(102,86,201,.10);border-color:var(--color-accent-300)}
[data-skin="light"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, rgba(102,86,201,.10), transparent 60%)}
/* The four hardcoded-hex spots flagged in the comment above. */
[data-skin="light"] .kpi{background:var(--surface-sunken)}
[data-skin="light"] .kanban-card:hover{background:var(--color-neutral-900)}
[data-skin="light"] .kanban-col.kb-accepted{background:linear-gradient(180deg,rgba(102,86,201,.08),var(--surface-col) 60%)}
[data-skin="light"] .kanban-col.drag-over{background:var(--color-neutral-900)}
[data-skin="light"] td{border-bottom-color:rgba(20,20,40,.07)}
[data-skin="light"] .attn-row{border-bottom-color:rgba(20,20,40,.07)}

/* Paper — from a real design handoff doc (design_handoff_nocturne_ui,
   the "★ LIGHT THEME" section): a warm-paper Nocturne variant with a
   deliberately different personality from the plain "Light" skin above
   — violet is FILLED here, not outlined, on primary actions and the
   dashboard KPI band ("a light ground needs a solid anchor" — that
   spec's words). Same inversion trick as Light for the neutral ramp
   (see that skin's comment); same accent-200 dual-role fix, but this
   spec sidesteps most of it by putting white text on a SOLID violet
   fill instead of colored text directly on white — closer to the
   handoff's actual intent than Light's outline-button treatment.
   Scope note: this implements the spec's color system, card/button/
   table/badge treatment and the filled KPI band — not the per-screen
   restructuring further down that doc (phase-hue card borders, the
   dashboard's icon-tile attention rows, the crew-lead mobile redesign)
   — those need new Blade markup, not new CSS, and apply to every skin's
   layout, not this one's palette. */
:root[data-skin="paper"]{
  --color-bg:#f7f6fb; --color-surface:#ffffff; --color-text:#231f38; --color-accent:#6d5fa8;

  --color-neutral-100:#231f38; --color-neutral-200:#403c55; --color-neutral-300:#4b4763;
  --color-neutral-400:#605b78; --color-neutral-500:#7b7691; --color-neutral-600:#8b869f;
  --color-neutral-700:#a49fb8; --color-neutral-800:#dedbec; --color-neutral-900:#eeecf7;

  --color-accent-100:#f1eefc; --color-accent-200:#efecfc; --color-accent-300:#5a4e92;
  --color-accent-400:#8b7dd4; --color-accent-500:#6d5fa8; --color-accent-600:#5a4e92;
  --color-accent-700:#54488a; --color-accent-800:#6d5fa8; --color-accent-900:#3b3163;

  --status-good:#2f7d5c;  --status-good-bg:rgba(47,125,92,.13);
  --status-warn:#96671a;  --status-warn-bg:rgba(150,103,26,.14);
  --status-bad:#b8433a;   --status-bad-bg:rgba(184,67,58,.12);
  --status-info:#6d5fa8;  --status-info-bg:rgba(109,95,168,.12);

  --surface-sunken:#f6f5fb; --surface-rail:#fbfaff; --surface-col:#f4f2fb; --track:#eeecf7;
  --brand-teal:#3f7d74;
}
[data-skin="paper"] body{
  background:
    radial-gradient(90% 70% at 4% -8%, #eeecfb, transparent 60%),
    radial-gradient(70% 60% at 104% 102%, #e7f3f2, transparent 62%),
    linear-gradient(180deg,#f7f6fb,#f1f0f7);
  background-attachment:fixed;
}
[data-skin="paper"] .card,[data-skin="paper"] .login-shell{
  border-color:var(--color-neutral-800);box-shadow:0 10px 26px rgba(43,39,65,.07),0 1px 2px rgba(43,39,65,.05);
}
[data-skin="paper"] .nav-item:hover{background:#f1eefc;color:#2b2741}
[data-skin="paper"] .nav-item.active{background:linear-gradient(90deg,#ebe7fa,#f6f4fe);color:#3b3163;box-shadow:inset 2px 0 0 #7a6cb8}
/* The spec's one deliberate break from every other skin's outline-button
   convention: "a light ground needs a solid anchor," so primary actions
   are a filled gradient with white text instead of accent-colored text
   on transparent — sidesteps needing a separate override for accent-200
   used as direct button text, the way Light needed. */
[data-skin="paper"] .btn-primary,[data-skin="paper"] .btn-gold{
  background:linear-gradient(180deg,var(--color-accent),#5a4e92);color:#fff;border-color:transparent;
  box-shadow:0 6px 14px rgba(90,78,146,.28);
}
[data-skin="paper"] .btn-primary:hover,[data-skin="paper"] .btn-gold:hover{box-shadow:0 8px 18px rgba(90,78,146,.36)}
[data-skin="paper"] .nav-kanban-link.active,[data-skin="paper"] .chat-compose-send,[data-skin="paper"] .skin-swatch.active{color:var(--color-accent)}
[data-skin="paper"] .chat-msg.own .chat-msg-body{background:#f1eefc;border-color:#ddd7f5}
[data-skin="paper"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, #eae6fb, transparent 60%)}
[data-skin="paper"] a:not([class]){background:none;color:var(--color-accent)}
[data-skin="paper"] a:not([class]):hover{background:none;color:#5a4e92;text-decoration:underline}
/* The four hardcoded-hex spots flagged in Light's comment, same fix. */
[data-skin="paper"] .kpi{background:var(--surface-sunken)}
[data-skin="paper"] .kanban-card:hover{background:var(--color-neutral-900);border-color:#ddd7f5}
[data-skin="paper"] .kanban-col.kb-accepted{background:linear-gradient(180deg,#f0edfc,#f8f7fd);border-color:#ddd7f5}
[data-skin="paper"] .kanban-col.drag-over{background:var(--color-neutral-900)}
[data-skin="paper"] td{border-bottom-color:#f2f0f9}
[data-skin="paper"] .attn-row{border-bottom-color:#f2f0f9}
/* The KPI band — the spec's "exactly one saturated field per page." The
   spec's own translucent-glass tile treatment (rgba(255,255,255,.14) on
   the gradient) read as one undifferentiated purple slab in practice —
   the tile and the band it sits on were too close in tone to tell
   apart. Solid white tiles floating on the gradient instead: a plainer
   pattern, but one that actually reads as "four cards," not a guess at
   how translucent is translucent enough. */
[data-skin="paper"] .dashboard-kpis{
  background:linear-gradient(135deg,#4a4080,#6d5fa8 45%,#8577c4 75%,#9a8dd6);
  border:none;border-radius:16px;padding:20px;margin-bottom:0;box-shadow:0 16px 36px rgba(60,50,110,.35);
}
[data-skin="paper"] .dashboard-kpis .kpi{
  background:#ffffff;border:1px solid rgba(255,255,255,.5);
  box-shadow:0 6px 18px rgba(30,20,60,.18);
}
[data-skin="paper"] .dashboard-kpis .kpi-label{color:var(--color-neutral-500)}
[data-skin="paper"] .dashboard-kpis .kpi-val{color:var(--color-text)}
[data-skin="paper"] .dashboard-kpis .kpi-context{color:var(--color-neutral-500)}

/* Canvas — modeled on PaintScout's marketing-site design language (their
   actual in-app screenshots weren't fetchable; this is the closest real
   reference available and SaaS marketing sites normally track the actual
   product's look closely): light, teal, and deliberately FLAT — "flat,
   minimal-to-no shadow, separation from whitespace and tint, not drop
   shadows" was the whole point of that reference, so unlike Paper (soft
   shadows, atmospheric gradient body, a saturated hero KPI band) this
   skin gets none of those three on purpose. Same inversion trick as
   Light/Paper for the neutral ramp, same filled-button treatment as
   Paper (a light ground still needs a solid anchor on primary actions),
   same ramp-value-reuse pragmatism (only the rungs something actually
   reads get a distinct value). Requested by the user as "just the visual
   polish/UX feel," explicitly not PaintScout's actual bidding/estimating
   workflow -- this is a palette/surface treatment only, not a new
   business flow. */
:root[data-skin="canvas"]{
  --color-bg:#f7f8f9; --color-surface:#ffffff; --color-text:#23262b; --color-accent:#0d9488;

  --color-neutral-100:#20242b; --color-neutral-200:#2f333c; --color-neutral-300:#454a54;
  --color-neutral-400:#5c6169; --color-neutral-500:#72777f; --color-neutral-600:#8c9096;
  --color-neutral-700:#a9adb2; --color-neutral-800:#d8dbdd; --color-neutral-900:#eceeef;

  --color-accent-100:#e6f7f5; --color-accent-200:#e6f7f5; --color-accent-300:#0f766e;
  --color-accent-400:#2dd4bf; --color-accent-500:#0d9488; --color-accent-600:#0f766e;
  --color-accent-700:#0b7d73; --color-accent-800:#0d9488; --color-accent-900:#053a34;

  --status-good:#2f8b5a;  --status-good-bg:rgba(47,139,90,.13);
  --status-warn:#a6720f;  --status-warn-bg:rgba(166,114,15,.14);
  --status-bad:#c23b30;   --status-bad-bg:rgba(194,59,48,.12);
  --status-info:#0b7d73;  --status-info-bg:rgba(11,125,115,.12);

  --surface-sunken:#f0f2f2; --surface-rail:#ffffff; --surface-col:#eef1f1; --track:#e2e5e6;
  --brand-teal:#0f766e;
}
/* No body-gradient override, on purpose -- flat var(--color-bg), no
   radial glow, unlike Depth/Paper's atmospheric treatment. */
[data-skin="canvas"] .card,[data-skin="canvas"] .login-shell{
  border-color:var(--color-neutral-800);box-shadow:none;
}
[data-skin="canvas"] .nav-item:hover{background:rgba(13,148,136,.08);color:var(--color-neutral-200)}
[data-skin="canvas"] .nav-item.active{background:rgba(13,148,136,.10);color:var(--color-neutral-100)}
/* Same filled-primary-button break from the outline convention as Paper,
   for the same reason -- see this block's top comment. */
[data-skin="canvas"] .btn-primary,[data-skin="canvas"] .btn-gold{
  background:var(--color-accent);color:#fff;border-color:transparent;
}
[data-skin="canvas"] .btn-primary:hover,[data-skin="canvas"] .btn-gold:hover{background:var(--color-accent-600)}
[data-skin="canvas"] .nav-kanban-link.active,[data-skin="canvas"] .chat-compose-send,[data-skin="canvas"] .skin-swatch.active{color:var(--color-accent)}
[data-skin="canvas"] .chat-msg.own .chat-msg-body{background:rgba(13,148,136,.08);border-color:var(--color-accent-300)}
[data-skin="canvas"] .login-panel-left{background:radial-gradient(120% 90% at 0% 100%, rgba(13,148,136,.08), transparent 60%)}
[data-skin="canvas"] a:not([class]){background:none;color:var(--color-accent)}
[data-skin="canvas"] a:not([class]):hover{background:none;color:var(--color-accent-600);text-decoration:underline}
/* The same hardcoded-hex spots Light/Paper both fix, same fix. */
[data-skin="canvas"] .kpi{background:var(--surface-sunken)}
[data-skin="canvas"] .kanban-card:hover{background:var(--color-neutral-900)}
[data-skin="canvas"] .kanban-col.kb-accepted{background:linear-gradient(180deg,rgba(13,148,136,.06),var(--surface-col) 60%)}
[data-skin="canvas"] .kanban-col.drag-over{background:var(--color-neutral-900)}
[data-skin="canvas"] td{border-bottom-color:rgba(20,20,40,.06)}
[data-skin="canvas"] .attn-row{border-bottom-color:rgba(20,20,40,.06)}
/* Deliberately no .dashboard-kpis hero-band override (unlike Depth/Paper/
   Viking) -- a saturated gradient hero is exactly what the flat/
   whitespace-driven reference argues against. */
