/* ============================================================================
   BrilZen Performance System — design system
   Signature: blue→magenta gradient · Space Grotesk display + Inter body
   ============================================================================ */
:root{
  --navy:#16324F; --navy-800:#122942; --navy-900:#0e2036; --navy-700:#1c4066;
  --magenta:#D81E7C; --blue:#1F5FA0; --blue-600:#2a6fb8;
  --grad:linear-gradient(135deg,#1F5FA0 0%,#8b2f8f 55%,#D81E7C 100%);
  --grad-soft:linear-gradient(135deg,rgba(31,95,160,.12),rgba(216,30,124,.12));

  --ink:#182437; --body:#3a4657; --muted:#6b7a8d; --faint:#93a1b2;
  --line:#e7ecf2; --line-soft:#eef2f7; --paper:#f4f7fb; --card:#ffffff;

  --green:#159068; --green-bg:#e4f5ee; --amber:#b9820f; --amber-bg:#fbf1dd;
  --red:#d23f3f; --red-bg:#fbe7e7; --blue-bg:#e7f0fa;
  --purple:#8b2f8f; --purple-bg:#f1e7fb;

  --sh-sm:0 1px 2px rgba(18,41,66,.06),0 1px 3px rgba(18,41,66,.05);
  --sh-md:0 4px 14px rgba(18,41,66,.08);
  --sh-lg:0 18px 48px rgba(18,41,66,.16);

  --r:16px; --r-sm:11px; --r-xs:8px;
  --font-display:'Space Grotesk',system-ui,sans-serif;
  --font-body:'Inter',system-ui,sans-serif;
  --sidebar-w:260px; --topbar-h:66px;
}

/* Type scale — 8 reading steps plus a hero step, derived from this codebase's real size
   clusters rather than guessed (see docs/superpowers/specs/2026-09-03-typography-scale-adoption-design.md).
   Size, tracking and leading are ONE decision per step, never three: small text takes
   slightly positive tracking and looser leading, display text negative tracking and tight
   leading (apple-design skill §15). Sizes are rem so they scale with the reader's own
   browser text-size setting. px equivalents at a 16px root are noted for orientation only. */
:root{
  --fs-2xs:.625rem;    --lh-2xs:1.35;  --ls-2xs:.03em;    /* 10px   — chart ticks, tiny badges */
  --fs-xs:.71875rem;   --lh-xs:1.4;    --ls-xs:.02em;     /* 11.5px — uppercase labels, meta */
  --fs-sm:.78125rem;   --lh-sm:1.45;   --ls-sm:.01em;     /* 12.5px — secondary text */
  --fs-md:.84375rem;   --lh-md:1.5;    --ls-md:0em;       /* 13.5px — table cells, dense primary */
  --fs-base:.9375rem;  --lh-base:1.55; --ls-base:0em;     /* 15px   — body default */
  --fs-lg:1.0625rem;   --lh-lg:1.35;   --ls-lg:-.005em;   /* 17px   — card and modal titles */
  --fs-xl:1.25rem;     --lh-xl:1.25;   --ls-xl:-.01em;    /* 20px   — page titles */
  --fs-2xl:1.5rem;     --lh-2xl:1.15;  --ls-2xl:-.015em;  /* 24px   — section heroes */
  --fs-hero:clamp(2rem,4vw,2.5rem); --lh-hero:1.05; --ls-hero:-.02em; /* 32-40px — KPI values, login hero */
}

:root{
  --glass-blur-sm:8px; --glass-blur-md:14px; --glass-blur-lg:16px;
  --glass-sat:160%; --glass-sat-strong:180%;
}

/* ============================================================================
   Themes — Light (the :root defaults above, no attribute needed) / Dark / Brand.
   Everything downstream of these tokens (body background/text, .card-bz, .kpi, tables,
   inputs, dropdowns, modals — anywhere the stylesheet already reads var(--ink)/var(--body)/
   var(--muted)/var(--line)/var(--paper)/var(--card)) re-themes automatically; the sidebar and
   any element already painted with var(--grad)/var(--navy) is brand-colored by design in
   every theme and is intentionally left alone. See app/services/theme.js for the switch
   itself (persists to localStorage, sets [data-theme] on <html> before Angular boots so
   there's no flash of the wrong theme on load).
   ============================================================================ */
:root[data-theme="dark"]{
  /* brand accents brightened for use as text/borders against a dark surface — these exact
     hexes already exist in three-fx.js's BzThreeColors for the same "brand, but on dark"
     purpose (the login hero/particle effects), reused here for one consistent pair app-wide. */
  --magenta:#e8579f; --blue:#4f9ce0; --blue-600:#6bb0ec;
  --grad:linear-gradient(135deg,#4f9ce0 0%,#a855c9 55%,#e8579f 100%);
  --grad-soft:linear-gradient(135deg,rgba(79,156,224,.16),rgba(216,30,124,.16));

  --ink:#eef2f7; --body:#c3ccd8; --muted:#8fa0b3; --faint:#5e7086;
  --line:#2a3648; --line-soft:#212b3a; --paper:#0d1420; --card:#151e2c;

  /* moderately brightened, not pastel — these do double duty as both text/borders against the
     dark surface AND as solid badge fills with white text on top (e.g. .tb-badge), so they can't
     swing as light as a typical "dark mode accent" without hurting that second, white-on-top use. */
  --green:#22a87e; --green-bg:rgba(34,168,126,.16); --amber:#d1962f; --amber-bg:rgba(209,150,47,.16);
  --red:#dd4f4f; --red-bg:rgba(221,79,79,.16); --blue-bg:rgba(79,156,224,.16);
  --purple:#a366d9; --purple-bg:rgba(163,102,217,.18);

  --sh-sm:0 1px 2px rgba(0,0,0,.35),0 1px 3px rgba(0,0,0,.3);
  --sh-md:0 4px 14px rgba(0,0,0,.4);
  --sh-lg:0 18px 48px rgba(0,0,0,.55);
}
/* Brand Color Scheme — still a light theme (readable, no dark-mode contrast concerns) but the
   neutral surface tokens shift into the brand's own navy/blue family instead of a cool neutral
   grey, plus a few extra rules below (page wash, gradient page title) lean further into it. */
:root[data-theme="brand"]{
  --line:#dbe4f3; --line-soft:#e9eefb; --paper:#eef2fc; --card:#ffffff;
  --sh-sm:0 1px 2px rgba(31,95,160,.10),0 1px 3px rgba(31,95,160,.08);
  --sh-md:0 4px 14px rgba(31,95,160,.14);
  --sh-lg:0 18px 48px rgba(31,95,160,.22);
}

*{box-sizing:border-box}
/* overflow-x:hidden belongs on <html> ONLY — never on <body>. Per spec, setting overflow on one
   axis forces the other to compute to `auto` instead of `visible`, which turns <body> into a
   scroll container. Because the viewport (<html>) is what actually scrolls, that container never
   scrolls itself — and every position:sticky descendant, which resolves against its nearest
   scrolling ancestor, silently stopped sticking: the topbar, the rich-text toolbar and the
   kanban column headers all scrolled away. Keeping the horizontal clip on <html> alone prevents
   sideways scrolling without creating that dead scroll container. */
html{overflow-x:hidden}
html,body{margin:0;padding:0}
body{
  font-family:var(--font-body);
  color:var(--body);
  background:var(--paper);
  font-size:var(--fs-base); line-height:var(--lh-base);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
/* Defaults sized to what this app actually uses. Authors were overriding these constantly
   — `<h2 style="font-size:17px">` appears 41 times because the old h2 default (21.6px) was
   wrong for a modal title. h3/h4/h5 deliberately share the body size and stay distinguished
   by weight instead (the shared rule below sets 600) — weight adds presence without taking
   more space (apple-design skill §15). */
h1{font-size:var(--fs-xl);line-height:var(--lh-xl);letter-spacing:var(--ls-xl)}
h2{font-size:var(--fs-lg);line-height:var(--lh-lg);letter-spacing:var(--ls-lg)}
h3,h4,h5{font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
h1,h2,h3,h4,h5,.display{font-family:var(--font-display);color:var(--ink);font-weight:600;margin:0;font-optical-sizing:auto}
a{color:var(--blue);text-decoration:none}
.text-grad{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}

/* Brand Color Scheme's extra touches — a soft two-corner gradient wash on the page itself (the
   sidebar/buttons are already brand-colored in every theme; this is what makes the *content*
   area feel branded too) and the current page's own title rendered in the signature gradient,
   same technique as .text-grad above. Both are additive/safe: a background layer and a
   text-fill, neither changes layout. */
[data-theme="brand"] body{
  background:
    radial-gradient(1100px 550px at 105% -12%, rgba(216,30,124,.06), transparent 60%),
    radial-gradient(1100px 550px at -10% 108%, rgba(31,95,160,.07), transparent 60%),
    var(--paper);
}
[data-theme="brand"] .title-block h1{
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.muted{color:var(--muted)}.faint{color:var(--faint)}
.mono{font-variant-numeric:tabular-nums}

/* ---- layout shell -------------------------------------------------------- */
.app-shell{display:flex;min-height:100vh}
/* Drawer at every screen size (not just mobile) — see AppController.js's sidebarOpen/
   toggleSidebar. Default open on wide screens (translateX(0) below), default closed on
   narrow ones (JS sets sidebarOpen accordingly before first paint, so there's no flash). */
.sidebar{
  width:var(--sidebar-w);position:fixed;inset:0 auto 0 0;z-index:40;
  background:
    radial-gradient(130% 55% at 0% 0%, rgba(22,201,141,.35) 0%, transparent 60%),
    radial-gradient(130% 60% at 100% 100%, rgba(31,95,160,.30) 0%, transparent 60%),
    linear-gradient(180deg,var(--navy) 0%,var(--navy-900) 100%);
  color:#cdd8e6;display:flex;flex-direction:column;padding:22px 16px;
  /* the sidebar hugs the left edge, so it (not .main) is what needs the inset in landscape on a
     notched phone — max() keeps the normal 16px on every non-notched device (env() is 0 there). */
  padding-left:max(16px, env(safe-area-inset-left, 16px));
  padding-top:calc(22px + env(safe-area-inset-top, 0px));
  overflow:hidden;   /* the inner .sidebar-scroll handles scrolling; brand + side-user stay pinned */
  transform:translateX(-100%);box-shadow:var(--sh-lg);
}
.sidebar.open{transform:none}
.sidebar .brand{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px 8px 26px;flex:none;position:relative}
.sidebar-collapse-btn{
  position:absolute;right:-2px;top:50%;transform:translateY(-50%);width:26px;height:26px;border-radius:50%;
  border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.08);color:#cdd8e6;
  display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;transition:background .15s;
}
.sidebar-collapse-btn:hover{background:rgba(255,255,255,.18);color:#fff}
.sidebar .brand img{height:42px;width:auto;display:block;filter:brightness(0) invert(1) drop-shadow(0 4px 14px rgba(0,0,0,.25))}
.sidebar .brand .sub{font-family:var(--font-display);font-size:var(--fs-xs);letter-spacing:.14em;color:#7f94ad;text-transform:uppercase;line-height:var(--lh-xs)}
.sidebar-scroll{
  position:relative;                           /* the glass bubble positions itself against this */
  flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;
  margin:0 -6px;padding:0 6px;                 /* room for the scrollbar without shifting nav items */
  scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.18) transparent;
}
.sidebar-scroll::-webkit-scrollbar{width:6px}
.sidebar-scroll::-webkit-scrollbar-thumb{background:rgba(255,255,255,.18);border-radius:999px}
.sidebar-scroll::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.3)}
.nav-group-label{font-size:var(--fs-2xs);letter-spacing:.16em;text-transform:uppercase;color:#61748c;margin:18px 12px 6px;line-height:var(--lh-2xs)}
.nav-item{
  display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:11px;
  color:#b7c4d4;font-weight:500;cursor:pointer;position:relative;z-index:1;
  transition:color .18s ease,transform .18s ease;
}
.nav-item i{font-size:18px;width:20px;text-align:center;opacity:.85}
.nav-item:hover{color:#fff;background:rgba(255,255,255,.07)}
.nav-item.active{color:#fff}
.nav-item.active:hover{background:none}
.nav-item.active::before{
  content:"";position:absolute;left:-16px;top:8px;bottom:8px;width:4px;border-radius:0 4px 4px 0;
  background:var(--grad);
}

/* ---- Liquid Glass nav highlight — a single pill (see bzGlassRail) pinned to whichever item
   is actually selected. It only moves when navigation changes the active item — hovering a
   different item never drags it there; that's just the plain .nav-item:hover background above. */
.nav-glass-bubble{
  position:absolute;top:0;left:0;z-index:0;pointer-events:none;opacity:0;
  border-radius:11px;
  background:linear-gradient(155deg,rgba(255,255,255,.24),rgba(216,30,124,.10) 55%,rgba(31,95,160,.16));
  -webkit-backdrop-filter:blur(var(--glass-blur-lg)) saturate(var(--glass-sat-strong));
  backdrop-filter:blur(var(--glass-blur-lg)) saturate(var(--glass-sat-strong));
  border:1px solid rgba(255,255,255,.28);
  box-shadow:
    0 10px 22px -6px rgba(6,14,26,.5),
    inset 0 1px 0 rgba(255,255,255,.5),
    inset 0 -1px 1px rgba(6,14,26,.18),
    0 0 0 1px rgba(255,255,255,.05);
  /* transform/width/height are now driven every frame by four BzSpring instances (see
     bzGlassRail in app/directives.js) — a CSS transition on those same properties would fight
     the per-frame inline styles the springs write, so only opacity (the show/hide fade) is
     still a plain CSS transition here. */
  transition: opacity .2s ease;
}
.side-user{border-top:1px solid rgba(255,255,255,.09);padding-top:14px;margin-top:8px;flex:none}
.side-user .who{display:flex;align-items:center;gap:10px}
.avatar{width:36px;height:36px;border-radius:50%;background:var(--grad);color:#fff;display:flex;
  align-items:center;justify-content:center;font-family:var(--font-display);font-weight:600;font-size:14px;flex:none;overflow:hidden}
.avatar.avatar-img img{width:100%;height:100%;object-fit:cover;display:block}
bz-avatar{display:inline-flex;flex:none;line-height:0}
.avatar-uploader{display:flex;align-items:center;gap:16px;margin-bottom:4px}
.avatar-uploader .avatar{width:64px;height:64px;font-size:22px}
.avatar-file-input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.side-user .name{color:#fff;font-weight:600;font-size:var(--fs-md);line-height:1.2;letter-spacing:var(--ls-md)}
.side-user .role{color:#8299b3;font-size:var(--fs-xs);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.side-signout{margin-top:12px;width:100%;background:rgba(255,255,255,.06);border:0;color:#c7d3e1;
  padding:9px;border-radius:10px;font:inherit;font-size:var(--fs-md);cursor:pointer;transition:background .18s;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.side-signout:hover{background:rgba(255,255,255,.12);color:#fff}

.main{margin-left:0;margin-right:0;flex:1;min-width:0;display:flex;flex-direction:column;
  transition:margin-left .42s cubic-bezier(.2,.7,.3,1),margin-right .42s cubic-bezier(.2,.7,.3,1)}
@media (min-width:901px){
  .sidebar.open + .main{margin-left:var(--sidebar-w)}
  /* mirrors the sidebar: the notifications drawer pushes content aside on a wide screen instead
     of floating over it — only overlays (see .notif-drawer's own media query) once the drawer's
     full width no longer leaves room to share the screen. */
  .app-shell.notif-open .main{margin-right:400px}
}
/* Global "you're offline" strip — see AppController.js's navigator.onLine tracking. Sits above
   the sticky topbar in normal flow (not fixed): scrolls away with the page like a normal
   announcement bar, and the topbar's own top:0 sticky behavior fills the gap once it does,
   exactly like any "banner above a sticky header" pattern. Deliberately carries no safe-area
   inset of its own — .topbar right below it already reserves that space unconditionally, so
   double-padding both would leave an oversized gap; this is plain informational text (no
   interactive controls), so the rare notched+standalone+offline combination at worst clipping
   its very top edge is an acceptable trade-off against that. */
.offline-bar{
  background:var(--amber-bg);color:var(--amber);font-size:var(--fs-sm);font-weight:600;
  text-align:center;padding:7px 16px;
;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.topbar{
  /* env(safe-area-inset-top) is 0 anywhere but a notched/Dynamic-Island phone running this as an
     installed standalone app (needs viewport-fit=cover too, set in index.html) — grows the bar
     to sit below the status bar there instead of tucking its buttons under it, and is a total
     no-op (0px) everywhere else, including every desktop/non-PWA case this already worked for. */
  height:calc(var(--topbar-h) + env(safe-area-inset-top, 0px));
  padding-top:env(safe-area-inset-top, 0px);
  position:sticky;top:0;z-index:30;
  background:linear-gradient(90deg, rgba(31,95,160,.05) 0%, rgba(216,30,124,.04) 100%), color-mix(in srgb, var(--card) 88%, transparent);
  backdrop-filter:blur(10px);
  display:flex;align-items:center;gap:16px;padding-left:30px;padding-right:30px;
}
[data-theme="brand"] .topbar{
  background:linear-gradient(90deg, rgba(31,95,160,.09) 0%, rgba(216,30,124,.08) 100%), color-mix(in srgb, var(--card) 88%, transparent);
}
.topbar::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--grad);opacity:0;
  transition:opacity .15s ease;
}
.topbar.scrolled::after{opacity:.9}
.topbar h1{font-size:var(--fs-xl);line-height:var(--lh-xl);letter-spacing:var(--ls-xl)}
.topbar .title-block{position:relative;padding-left:14px}
.topbar .title-block::before{
  content:"";position:absolute;left:0;top:1px;bottom:1px;width:4px;border-radius:4px;background:var(--grad);
}

/* ---- topbar actions (notifications, user menu) ----------------------------- */
.topbar-bell{
  position:relative;width:38px;height:38px;border-radius:50%;background:var(--card);border:1px solid var(--line);
  display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--muted);font-size:var(--fs-base);
  transition:background .15s,color .15s,border-color .15s;flex:none;
;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.topbar-bell:hover{background:var(--paper);color:var(--blue);border-color:var(--blue)}
.topbar-bell.active{background:var(--grad);color:#fff;border-color:transparent}
.tb-badge{
  position:absolute;top:-4px;right:-4px;min-width:17px;height:17px;border-radius:999px;background:var(--red);
  color:#fff;font-size:var(--fs-2xs);font-weight:700;display:flex;align-items:center;justify-content:center;
  padding:0 4px;box-shadow:0 0 0 2px var(--card);
;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.avatar.avatar-sm{width:28px;height:28px;font-size:12px}
.topbar-user{position:relative}
.topbar-chat{position:relative}
.topbar-user-btn{
  display:flex;align-items:center;gap:9px;cursor:pointer;padding:5px 10px 5px 5px;border-radius:999px;
  border:1px solid var(--line);background:var(--card);transition:background .15s,border-color .15s;
}
.topbar-user-btn:hover{background:var(--paper);border-color:var(--blue)}
.topbar-user-btn .tu-name{font-size:var(--fs-md);font-weight:600;color:var(--ink);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.topbar-user-btn .bi-chevron-down{font-size:10px;color:var(--faint)}
.topbar-user-menu{
  position:absolute;top:calc(100% + 8px);right:0;min-width:200px;background:var(--card);border:1px solid var(--line);
  border-radius:12px;box-shadow:var(--sh-lg);overflow:hidden;z-index:50;transform-origin:top right;
}
.tum-head{padding:14px 16px;border-bottom:1px solid var(--line)}
.tum-head .tum-name{font-weight:600;color:var(--ink);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.tum-head .tum-role{color:var(--muted);font-size:var(--fs-sm);margin-top:2px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.topbar-user-menu button,.topbar-user-menu a{
  width:100%;display:flex;align-items:center;gap:9px;border:0;background:none;text-align:left;
  padding:12px 16px;font:inherit;font-size:var(--fs-md);color:var(--body);cursor:pointer;transition:background .15s;
  text-decoration:none;box-sizing:border-box;
;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.topbar-user-menu button:hover,.topbar-user-menu a:hover{background:var(--red-bg);color:var(--red)}

.tum-theme{padding:12px 16px;border-bottom:1px solid var(--line)}
.tum-theme-label{font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.08em;color:var(--faint);font-weight:700;margin-bottom:8px;line-height:var(--lh-2xs)}
.topbar-user-menu .theme-switch{display:flex;gap:6px}
.topbar-user-menu .theme-switch button{
  flex:1;width:auto;display:flex;align-items:center;justify-content:center;height:34px;padding:0;
  border-radius:9px;border:1px solid var(--line);background:var(--line-soft);color:var(--muted);
  cursor:pointer;font-size:var(--fs-base);transition:all .15s;
;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.topbar-user-menu .theme-switch button:hover{border-color:var(--blue);color:var(--blue);background:var(--line-soft)}
.topbar-user-menu .theme-switch button.active{background:var(--grad);color:#fff;border-color:transparent}
.topbar .page-sub{font-size:var(--fs-sm);color:var(--muted);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.grow{flex:1}
.content{padding:28px 30px 56px}

/* sidebar drawer toggle — visible at every width now (used to be mobile-only) */
.menu-btn{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;
  border-radius:10px;border:1px solid var(--line);background:var(--card);cursor:pointer;font-size:19px;color:var(--ink);flex:none}
.menu-btn:hover{background:var(--paper)}

/* ---- cards --------------------------------------------------------------- */
.card-bz{background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh-sm)}
.section-title{display:flex;align-items:center;gap:10px;margin:26px 0 12px}
.section-title h2{font-size:var(--fs-base);letter-spacing:.02em;line-height:var(--lh-base)}
.section-title .rule{flex:1;height:1px;background:var(--line)}
.section-title .eyebrow{font-family:var(--font-display);font-size:var(--fs-xs);letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);line-height:var(--lh-xs)}

/* KPI card — the signature element */
.kpi{
  position:relative;background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:18px 18px 16px 22px;overflow:hidden;box-shadow:var(--sh-sm);
  transition:transform .2s cubic-bezier(.2,.7,.3,1),box-shadow .2s;
}
.kpi::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--grad)}
.kpi:hover{transform:translateY(-3px);box-shadow:var(--sh-md)}
.kpi .label{font-size:var(--fs-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:600;line-height:var(--lh-xs)}
.kpi .val{font-family:var(--font-display);font-weight:600;font-size:var(--fs-hero);color:var(--ink);
  line-height:1.05;margin-top:8px;font-variant-numeric:tabular-nums;letter-spacing:var(--ls-hero)}
.kpi .val .unit{font-size:var(--fs-base);color:var(--faint);font-weight:500;margin-left:3px;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.kpi .foot{display:flex;align-items:center;gap:7px;margin-top:9px;font-size:var(--fs-sm);color:var(--muted);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.kpi.rag-green::before{background:linear-gradient(180deg,#1fb37f,#159068)}
.kpi.rag-amber::before{background:linear-gradient(180deg,#e0a52b,#b9820f)}
.kpi.rag-red::before{background:linear-gradient(180deg,#e85c5c,#d23f3f)}
/* crm_updates.md Group 6 — a .kpi tile that opens a modal list on click (My Dashboard's "This
   week" tiles, Group 6 #19) instead of being purely decorative; .kpi's own :hover lift already
   reads as interactive, this just adds the pointer cursor. */
.bz-clickable{cursor:pointer}
.todo-list-card{cursor:pointer;display:block;color:inherit;text-decoration:none}

/* ---- chart card chrome (bz-bullet-chart's card/head/title/sub/table-toggle — bz-trend-chart,
   the other consumer of this block, was removed with the main dashboard's trend charts, Group 6
   #16/#17) ------------------------------------------------------------------------------------ */
.trend-card{padding:18px 20px 16px}
.trend-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:6px}
.trend-title{font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.trend-sub{font-size:var(--fs-xs);margin-top:2px;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.trend-toggle{
  display:flex;align-items:center;gap:6px;font-size:var(--fs-xs);font-weight:600;color:var(--muted);
  background:none;border:1px solid var(--line);border-radius:999px;padding:5px 10px;cursor:pointer;
  transition:background .15s,color .15s,border-color .15s;flex:none;
;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.trend-toggle:hover{background:var(--paper);color:var(--blue);border-color:var(--blue)}

/* ---- bullet charts (bz-bullet-chart) — capacity (track) vs committed (RAG fill), shared
   absolute hour scale across every row so bars stay comparable, not each normalized to its
   own 100%. ------------------------------------------------------------------------------- */
.bullet-grid{position:relative;height:15px;margin-left:150px;margin-bottom:4px}
.bullet-grid span{position:absolute;top:0;font-size:var(--fs-2xs);color:var(--faint);transform:translateX(-50%);white-space:nowrap;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.bullet-grid span:first-child{transform:none}
.bullet-body{display:flex;flex-direction:column;gap:11px}
.bullet-row{position:relative;display:flex;align-items:center;gap:10px;outline:none}
.bullet-label{width:150px;flex:0 0 150px;font-size:var(--fs-sm);color:var(--ink);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.bullet-sub{font-weight:400;color:var(--muted);font-size:var(--fs-xs);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.bullet-bar-wrap{position:relative;flex:1 1 auto}
.bullet-track{position:relative;height:16px;background:var(--line-soft);border-radius:4px;min-width:2px}
.bullet-fill{position:absolute;left:0;top:0;height:100%;border-radius:4px;transition:width .7s cubic-bezier(.2,.7,.3,1)}
.bullet-fill.green{background:var(--green)}
.bullet-fill.amber{background:var(--amber)}
.bullet-fill.red{background:var(--red)}
.bullet-overflow{position:absolute;top:-3px;transform:translateX(-50%);color:var(--red);font-size:var(--fs-xs);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.bullet-value{width:118px;flex:0 0 118px;text-align:right;font-size:var(--fs-xs);color:var(--muted);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.bullet-row:hover .bullet-label,.bullet-row:focus .bullet-label{color:var(--blue)}
.bullet-tooltip{
  position:absolute;left:150px;top:calc(100% + 6px);background:var(--navy-900);color:#fff;
  border-radius:9px;padding:8px 11px;pointer-events:none;white-space:nowrap;box-shadow:var(--sh-md);
  z-index:6;animation:fadeIn .1s;
}
.bullet-tooltip::before{
  content:"";position:absolute;left:16px;bottom:100%;border:5px solid transparent;border-bottom-color:var(--navy-900);
}
.bullet-legend{display:flex;align-items:center;gap:6px;margin-top:14px;font-size:var(--fs-xs);color:var(--muted);flex-wrap:wrap;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.bl-swatch{display:inline-block;width:10px;height:10px;border-radius:3px;flex:none}
.bl-swatch.track{background:var(--line-soft);border:1px solid var(--line)}
.bl-swatch.green{background:var(--green)}
.bl-swatch.amber{background:var(--amber)}
.bl-swatch.red{background:var(--red)}

/* ---- stacked allocation bar (Team Pipeline company total: committed vs headroom) -------- */
.stack-bar{display:flex;height:24px;border-radius:7px;overflow:hidden;background:var(--line-soft)}
.stack-bar .seg{display:flex;align-items:center;justify-content:center;font-size:var(--fs-xs);font-weight:700;
  color:#fff;white-space:nowrap;overflow:hidden;transition:width .7s cubic-bezier(.2,.7,.3,1);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.stack-bar .seg + .seg{margin-left:2px}
.stack-bar .seg.headroom{background:var(--faint);color:var(--ink)}

/* ---- RAG dots & chips ---------------------------------------------------- */
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex:none}
.dot.green{background:var(--green)}.dot.amber{background:var(--amber)}.dot.red{background:var(--red)}
.dot.pulse{box-shadow:0 0 0 0 rgba(210,63,63,.5);animation:pulse 1.8s infinite}
@keyframes pulse{70%{box-shadow:0 0 0 8px rgba(210,63,63,0)}100%{box-shadow:0 0 0 0 rgba(210,63,63,0)}}
.chip{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;
  font-size:var(--fs-sm);font-weight:600;line-height:1.6;letter-spacing:var(--ls-sm)}
.chip.green{background:var(--green-bg);color:var(--green)}
.chip.amber{background:var(--amber-bg);color:var(--amber)}
.chip.red{background:var(--red-bg);color:var(--red)}
.chip.neutral{background:var(--line-soft);color:var(--muted)}

/* ---- Performance Analytics: per-person metric rows ------------------------ */
.metric-row{display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--line-soft);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.metric-row:last-child{border-bottom:none}
.metric-label{color:var(--ink);flex:1 1 auto;min-width:0}
.metric-value{font-weight:700;color:var(--ink)}
.metric-target{font-size:var(--fs-xs);white-space:nowrap;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.chip.blue{background:var(--blue-bg);color:var(--blue)}
.chip.purple{background:var(--purple-bg);color:var(--purple)}
/* cancelled task/sub-task — deliberately the quietest chip: struck through, no status colour */
.chip.cancelled{background:var(--line-soft);color:var(--muted);text-decoration:line-through}

/* ---- a table/list row that navigates but still looks like plain text at rest — the primary
   cell carries a real <a href> (so ctrl/middle-click/"open in new tab" work) while the rest of
   the row keeps its own ng-click for whole-row convenience. Same hover treatment as
   .person-clickable so the two conventions read consistently. ---- */
.row-link{color:inherit;text-decoration:none}
.row-link:hover{color:var(--blue);text-decoration:underline}

/* ---- weight legend — "how this is scored," reused by Appraisals and the Performance drill-down
   so a weighted score's criteria/percentages are always spelled out, never just implied. ---- */
.weight-legend{display:flex;flex-direction:column;gap:10px}
.weight-bar{display:flex;height:10px;border-radius:6px;overflow:hidden;background:var(--line-soft)}
.weight-bar-seg{height:100%}
.weight-rows{display:flex;flex-wrap:wrap;gap:8px 18px;font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.weight-rows .wr{display:flex;align-items:center;gap:7px;color:var(--body)}
.weight-rows .wr .sw{width:10px;height:10px;border-radius:3px;flex:none}
.weight-rows .wr b{color:var(--ink)}

/* ---- kanban board (Tasks/Sub-Tasks) --------------------------------------- */
/* Status → color is the same mapping everywhere a task status appears: pending=neutral,
   in_progress=blue, delivered=amber (awaiting review), revision=red, on_hold=purple
   (paused for a client-side reason — never counts toward revision_count), completed=green.
   Glass treatment: bzKanbanAmbient (three-fx.js) paints a few soft drifting blue/magenta
   glows on a canvas behind the board; columns and cards are translucent + backdrop-blurred
   "Liquid Glass" panels above it, echoing the same recipe as the sidebar's nav-glass-bubble. */
.kanban-stage{position:relative;border-radius:var(--r);overflow:hidden}
.kanban-ambient-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;display:block}
/* Board scrolls horizontally as a whole (one axis, one scrollbar, always reachable along the
   bottom); each column scrolls its OWN card list vertically, independently, capped by
   .kanban-col's own max-height — the standard Trello/Jira/Linear board split, and a fix for the
   old single overflow:auto region trying to carry both axes at once (mouse-wheel/trackpad
   vertical scroll landing on whichever column the cursor happened to be over used to feel
   unreliable, and a lower column's horizontal reach was easy to miss below a tall neighbor).
   Column headers no longer need position:sticky at all — .kanban-col-head simply sits in normal
   flow ABOVE the one part of the column that scrolls (.kanban-col-body), so it's structurally
   fixed in place with no "nearest scrolling ancestor" fragility. Same end result ("the header
   stays put while cards scroll under it"), just implemented more robustly. */
.kanban-wrap{position:relative;z-index:1;display:flex;align-items:flex-start;gap:14px;overflow-x:auto;overflow-y:hidden;padding:4px 4px 14px}
/* The glass (gradient + backdrop-filter) lives on a ::before pseudo-element, NOT on .kanban-col
   itself: a non-none backdrop-filter makes an element the containing block for position:fixed
   descendants (Filter Effects L2 — same rule as `filter`), which would pin a dragged card
   (bz-drag-card goes position:fixed) to the column's own box and let the column's overflow clip
   it. position:relative + isolation:isolate do NOT create a fixed containing block; isolation
   keeps the z-index:-1 pseudo stacked inside this column instead of behind the whole page. */
.kanban-col{flex:0 0 272px;width:272px;max-height:70vh;display:flex;flex-direction:column;
  position:relative;isolation:isolate;
  border:1px solid color-mix(in srgb, var(--card) 55%, transparent);border-radius:var(--r-sm);
  box-shadow:var(--sh-md),inset 0 1px 0 color-mix(in srgb, var(--card) 60%, transparent)}
.kanban-col::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:linear-gradient(160deg,color-mix(in srgb, var(--card) 62%, transparent),color-mix(in srgb, var(--card) 38%, transparent));
  -webkit-backdrop-filter:blur(var(--glass-blur-md)) saturate(var(--glass-sat));backdrop-filter:blur(var(--glass-blur-md)) saturate(var(--glass-sat))}
.kanban-col-head{padding:12px 14px;font-weight:700;color:var(--ink);font-size:var(--fs-md);
  display:flex;align-items:center;gap:8px;border-bottom:3px solid var(--muted);border-radius:var(--r-sm) var(--r-sm) 0 0;
  flex:none;
  background:linear-gradient(160deg,color-mix(in srgb, var(--card) 92%, transparent),color-mix(in srgb, var(--card) 84%, transparent));
  -webkit-backdrop-filter:blur(var(--glass-blur-md)) saturate(var(--glass-sat));backdrop-filter:blur(var(--glass-blur-md)) saturate(var(--glass-sat));line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.kanban-col-count{margin-left:auto;font-size:var(--fs-xs);color:var(--muted);font-weight:600;
  background:color-mix(in srgb, var(--card) 70%, transparent);border-radius:999px;padding:1px 8px;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.kanban-col.s-pending .kanban-col-head{border-bottom-color:var(--muted)}
.kanban-col.s-in_progress .kanban-col-head{border-bottom-color:var(--blue)}
.kanban-col.s-delivered .kanban-col-head{border-bottom-color:var(--amber)}
.kanban-col.s-revision .kanban-col-head{border-bottom-color:var(--red)}
.kanban-col.s-on_hold .kanban-col-head{border-bottom-color:var(--purple)}
.kanban-col.s-completed .kanban-col-head{border-bottom-color:var(--green)}
.kanban-col.s-cancelled .kanban-col-head{border-bottom-color:var(--line);color:var(--muted)}
/* portfolio project board (projects.html) reuses the plain chip color names, not task statuses */
.kanban-col.c-green .kanban-col-head{border-bottom-color:var(--green)}
.kanban-col.c-amber .kanban-col-head{border-bottom-color:var(--amber)}
.kanban-col.c-red .kanban-col-head{border-bottom-color:var(--red)}
.kanban-col.c-neutral .kanban-col-head{border-bottom-color:var(--muted)}
.kanban-col.drag-over{outline:2px dashed var(--blue);outline-offset:-2px}
.kanban-col.drag-over::before{background:var(--blue-bg)}
.kanban-col-body{padding:10px;display:flex;flex-direction:column;gap:8px;flex:1;min-height:0;overflow-y:auto}
.kanban-card{background:linear-gradient(160deg,color-mix(in srgb, var(--card) 92%, transparent),color-mix(in srgb, var(--card) 78%, transparent));
  -webkit-backdrop-filter:blur(var(--glass-blur-sm)) saturate(var(--glass-sat));backdrop-filter:blur(var(--glass-blur-sm)) saturate(var(--glass-sat));
  border:1px solid color-mix(in srgb, var(--card) 70%, transparent);border-left:4px solid var(--muted);
  border-radius:var(--r-xs);padding:10px 12px;min-height:78px;flex:none;
  display:flex;flex-direction:column;gap:4px;
  box-shadow:var(--sh-sm),inset 0 1px 0 color-mix(in srgb, var(--card) 70%, transparent);cursor:grab;
  transition:box-shadow .2s,background .2s;will-change:transform;touch-action:none;-webkit-user-select:none;user-select:none}
.kanban-card:hover{box-shadow:var(--sh-lg),inset 0 1px 0 color-mix(in srgb, var(--card) 80%, transparent)}
.kanban-card:active{cursor:grabbing}
/* The card being dragged is now the real element (position:fixed, driven by bz-drag-card's own
   inline transform) rather than a dimmed original next to the browser's native ghost image — so
   this reads as "lifted off the board" (fuller shadow) rather than "half-gone." transform/rotate
   are owned entirely by the directive now, not set here. */
.kanban-card.dragging{box-shadow:var(--sh-lg),0 20px 40px rgba(16,24,40,.18);cursor:grabbing}
.kanban-card.s-pending{border-left-color:var(--muted)}
.kanban-card.s-in_progress{border-left-color:var(--blue)}
.kanban-card.s-delivered{border-left-color:var(--amber)}
.kanban-card.s-revision{border-left-color:var(--red)}
.kanban-card.s-on_hold{border-left-color:var(--purple)}
.kanban-card.s-completed{border-left-color:var(--green)}
.kanban-card.s-cancelled{border-left-color:var(--line);opacity:.7}
.kanban-card.s-cancelled .kanban-card-title{text-decoration:line-through}
/* One consistent vertical rhythm (the card's own flex `gap`, above) instead of each optional
   line carrying its own ad-hoc margin — so a 2-line card (title + one meta row) and a 5-line
   card (project + parent + title + two meta rows) both read as the same considered design
   rather than randomly-sized. Title clamps to 2 lines instead of growing unbounded; every
   optional line ellipsizes instead of wrapping or hard-clipping. */
.kanban-card-title{font-weight:600;color:var(--ink);font-size:var(--fs-md);line-height:1.35;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;letter-spacing:var(--ls-md)}
.kanban-card-project{font-size:var(--fs-2xs);color:var(--faint);display:flex;align-items:center;gap:4px;
  text-transform:uppercase;letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-2xs)}
.kanban-card-parent{font-size:var(--fs-xs);color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.kanban-card-meta{display:flex;justify-content:space-between;align-items:center;font-size:var(--fs-xs);color:var(--muted);gap:6px;flex-wrap:wrap;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.kanban-card-meta>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kanban-card-due.overdue{color:var(--red);font-weight:700}
.kanban-empty-col{color:var(--faint);font-size:var(--fs-sm);text-align:center;padding:18px 6px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.proj-bar{height:6px;border-radius:999px;background:var(--line-soft);overflow:hidden}
.proj-bar-fill{height:100%;border-radius:999px;background:var(--green);transition:width .3s}
.proj-bar-fill.amber{background:var(--amber)}
.proj-bar-fill.red{background:var(--red)}
/* file-upload progress — reuses .proj-bar/.proj-bar-fill as the fill track, wherever
   Api.upload()'s onProgress callback (app/services/api.js) reports 0-100. */
.upload-progress{margin-top:8px;display:flex;flex-direction:column;gap:4px}
.upload-progress .proj-bar{background:var(--line)}
.upload-progress .proj-bar-fill{background:var(--blue)}
/* max-width:100% + a horizontally-scrolling (not wrapped) row, same pattern as .notif-cat-tabs
   above — a 5-button row (project-detail.html's account hub) can exceed a phone viewport's width
   since buttons don't shrink below their own content (min-width:auto is the flex-item default);
   scrolling keeps every tab reachable instead of overlapping/spilling off-screen. A no-op on rows
   that already fit (every other .view-toggle in the app) — no visible scrollbar, same look. */
.view-toggle{display:inline-flex;max-width:100%;border:1px solid var(--line);border-radius:10px;
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.view-toggle::-webkit-scrollbar{display:none}
.view-toggle button{flex:none;white-space:nowrap;border:0;background:var(--card);padding:8px 14px;font:inherit;font-weight:600;
  font-size:var(--fs-md);color:var(--muted);cursor:pointer;display:flex;align-items:center;gap:6px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.view-toggle button.active{background:var(--navy);color:#fff}
@media(max-width:640px){.kanban-col{flex:0 0 240px;width:240px}
  /* Below the app's mobile breakpoint (matches window.innerWidth<=640 in ChatController.js and
     bzDragCard's own pointerdown gate below), drag-to-change-status is disabled entirely — touch
     input is left to the browser's native scroll gesture instead of being captured for 1:1 drag
     tracking. touch-action:auto (not the default touch-action:none above) is what actually lets
     native vertical (column body / page) and horizontal (kanban-wrap) touch-scroll work again;
     tap-to-open-card is untouched since it's a plain click/ng-click, not part of this directive. */
  .kanban-card{touch-action:auto}}

/* ---- project hub: To-do List / Docs & Files / Calendar (project-detail.html) --------- */
.todo-row{display:flex;align-items:center;gap:10px;padding:9px 10px;border-bottom:1px solid var(--line-soft)}
.todo-row:last-child{border-bottom:none}
.todo-row input[type=checkbox]{width:16px;height:16px;accent-color:var(--green);flex:none}
.todo-title{font-weight:600;color:var(--ink);cursor:pointer}
.todo-title.done{color:var(--muted);text-decoration:line-through;font-weight:500}
.todo-sub-wrap{margin-left:30px;border-left:2px solid var(--line)}
/* crm_updates.md #8 — Team/PM/Total hours footer under the team tasks table (todo-list-detail.html) */
.todo-hours-footer{display:flex;justify-content:flex-end;flex-wrap:wrap;gap:18px;padding:10px 10px 2px;margin-top:2px;border-top:1px solid var(--line-soft)}

.cal-head{display:flex;align-items:center;gap:12px;margin-bottom:14px}

/* ---- Calendar — connected multi-week Gantt-style bars (project-detail Calendar tab) -----
   Two stacked CSS grids per week, both repeat(7,1fr): a day-number row, and a bars row where
   each task is ONE div spanned via grid-column across every day it covers (not a pill repeated
   per day). Overlapping bars are pre-packed into lane rows by the controller (buildWeeks()). */
.cal-grid{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--card);overflow-x:auto}
/* Each day column has a hard floor (130px) instead of shrinking to whatever's left over on a
   narrower screen (§7) — a 7-column grid squeezed into ~1fr-per-column at typical laptop widths
   left day numbers/bars/titles unreadably cramped well before the old 760px media-query breakpoint
   ever kicked in. minmax(130px,1fr) grows columns to fill extra space when there's room, and past
   that floor lets .cal-grid's own overflow-x:auto take over — the grid EXPANDS to whatever width
   its content actually needs and scrolls, rather than always collapsing to fit. All three grids
   below share the exact same column formula so the header, day numbers, and bars never drift out
   of alignment with each other while scrolling. */
.cal-dow-row{display:grid;grid-template-columns:repeat(7,minmax(130px,1fr));background:var(--paper);border-bottom:1px solid var(--line);
  border-radius:var(--r-sm) var(--r-sm) 0 0;overflow:hidden}
.cal-dow{padding:6px 8px;font-size:var(--fs-xs);font-weight:700;color:var(--muted);text-align:center;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.cal-week{border-bottom:1px solid var(--line)}
.cal-week:last-child{border-bottom:0}
.cal-week-days{display:grid;grid-template-columns:repeat(7,minmax(130px,1fr))}
/* A fixed floor keeps every week row the same height regardless of how many recurring chips a
   given day happens to have — a busy Tuesday no longer stretches the whole week's row (and every
   other week's rows, since CSS Grid rows aren't independently sized per-week here) into a jagged
   shape. Recurring chips beyond what fits get their own small internal scroll instead of growing
   the cell — see .cal-day-recurring below. */
.cal-day{min-height:92px;padding:6px;border-right:1px solid var(--line-soft);display:flex;flex-direction:column}
.cal-day:last-child{border-right:0}
.cal-day.out{background:var(--line-soft)}
.cal-day-num{font-size:var(--fs-xs);color:var(--muted);font-weight:700;flex:none;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.cal-day.today .cal-day-num{color:#fff;background:var(--blue);border-radius:50%;width:20px;height:20px;
  display:inline-flex;align-items:center;justify-content:center}
/* recurring tasks/sub-tasks — a full detail chip (title + assignee, marked/unmarked) per day,
   inside the day cell itself (not a spanning bar in .cal-week-bars below — a recurring item
   shows on EVERY day it's active, not stretched across the range — see buildWeeks()'s
   recurringMarkersForDay()). Capped + internally scrollable rather than growing the cell
   unboundedly, so a handful of busy days don't distort the whole month grid's row heights. */
.cal-day-recurring{display:flex;flex-direction:column;gap:3px;margin-top:4px;flex:1;min-height:0;overflow-y:auto}
.cal-recur-chip{display:flex;align-items:flex-start;gap:5px;padding:3px 5px;border-radius:6px;flex:none;
  border-left:3px solid var(--line);background:var(--paper);cursor:pointer;text-decoration:none;
  transition:background .12s;line-height:1.25}
.cal-recur-chip:hover{background:var(--line-soft)}
.cal-recur-chip-icon{font-size:11px;color:var(--faint);flex:none;margin-top:2px}
.cal-recur-chip.marked{border-left-color:var(--green)}
.cal-recur-chip.marked .cal-recur-chip-icon{color:var(--green)}
.cal-recur-chip-body{min-width:0;flex:1;display:flex;flex-direction:column}
.cal-recur-chip-title{font-size:var(--fs-2xs);font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.cal-recur-chip-meta{font-size:var(--fs-2xs);color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
/* A repeating vertical-line background echoes .cal-day's own border-right dividers immediately
   above, so the spanning Gantt bars read as living in the SAME 7 date columns instead of a
   disconnected block underneath — the two grids were never actually misaligned (same
   repeat(7,1fr) track sizing), they just had nothing visually tying them together. Bottom
   padding is 9px (not the old 6px) so the row's real rendered height — 22px per lane + 3px gap
   between lanes — matches the controller's own `lanes*25+6` inline height exactly, instead of
   running 3px short. */
.cal-week-bars{display:grid;grid-template-columns:repeat(7,minmax(130px,1fr));grid-auto-rows:22px;gap:3px 0;padding:0 0 9px;
  background-image:linear-gradient(to right, var(--line-soft) 1px, transparent 1px);
  background-size:calc(100%/7) 100%}
.cal-bar{margin:0 3px;padding:2px 8px;border-radius:6px;background:var(--blue-bg);color:var(--blue);min-width:0;
  font-size:var(--fs-xs);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  cursor:pointer;display:flex;align-items:center;box-shadow:0 1px 2px rgba(0,0,0,.06);text-decoration:none;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
/* .cal-bar's own text-overflow:ellipsis only fires when its content is a plain inline text node
   (the Account-level Calendar tab, project-detail.html) — display:flex on an element otherwise
   defeats that entirely for any child ELEMENT (not text node), which is why the cross-Account
   Projects Calendar wraps its title in its own explicitly flex:1;min-width:0 span instead
   (.pcal-bar-title above) rather than relying on this rule. */
.cal-bar-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-bar.completed{background:var(--green-bg);color:var(--green);text-decoration:line-through}
.cal-bar.overdue{background:var(--red-bg);color:var(--red)}
.cal-bar.cont-l{border-top-left-radius:2px;border-bottom-left-radius:2px;margin-left:0}
.cal-bar.cont-r{border-top-right-radius:2px;border-bottom-right-radius:2px;margin-right:0}
/* Toggle for the lanes .cal-week-bars hides past MAX_VISIBLE_LANES (see the controller) — sits
   directly under that week's bars, so it reads as "reveal more of THIS week" rather than a
   month-wide control. */
.cal-more-btn{display:block;width:100%;border:0;border-top:1px solid var(--line-soft);background:var(--paper);
  color:var(--blue);font-size:var(--fs-2xs);font-weight:700;padding:4px;cursor:pointer;text-align:center;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.cal-more-btn:hover{background:var(--line-soft)}
/* The old max-width:760px breakpoint that used to be the ONLY place a min-width applied is gone —
   .cal-dow-row/.cal-week-days/.cal-week-bars's own minmax(130px,1fr) columns above now enforce a
   legible floor at every width, not just small screens, and .cal-grid's overflow-x:auto scrolls
   past it exactly the same "wide data grid" pattern already used for the Logs sheet. */

/* ---- Projects Calendar — cross-project version of the grid above (same .cal-* classes),
   plus a project-picker sidebar. Bars are colored per-project (inline style, computed from the
   controller's fixed categorical palette) rather than by status, since project identity is the
   thing this view exists to make scannable at a glance; the ambient canvas reuses the Kanban
   board's Three.js backdrop (bz-kanban-ambient) for the same soft, interactive feel. ------------ */
.pcal-stage{position:relative;border-radius:var(--r);overflow:hidden}
.pcal-body{position:relative;z-index:1}
.pcal-layout{display:flex;gap:18px;align-items:flex-start}

.pcal-sidebar{width:264px;flex:none;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:14px;box-shadow:var(--sh-sm);
  max-height:calc(100vh - var(--topbar-h) - 120px);overflow-y:auto;position:sticky;top:14px}
.pcal-all-row{display:flex;align-items:center;gap:9px;font-weight:700;font-size:var(--fs-md);color:var(--ink);
  padding-bottom:10px;margin-bottom:10px;border-bottom:1px solid var(--line);cursor:pointer;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.pcal-search{margin-bottom:10px}
.pcal-project-list{display:flex;flex-direction:column;gap:1px}
.pcal-project-row{display:flex;align-items:center;gap:9px;padding:7px 8px;border-radius:8px;
  cursor:pointer;font-size:var(--fs-md);color:var(--body);transition:background .12s;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.pcal-project-row:hover{background:var(--paper)}
.pcal-dot{width:10px;height:10px;border-radius:50%;flex:none;box-shadow:0 0 0 2px var(--card)}
.pcal-project-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pcal-empty{padding:10px 4px;color:var(--faint);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}

.pcal-main{flex:1;min-width:0}
.pcal-toggle{display:flex;align-items:center;gap:6px;font-size:var(--fs-md);color:var(--body);cursor:pointer;white-space:nowrap;line-height:var(--lh-md);letter-spacing:var(--ls-md)}

.pcal-bar{gap:5px}
/* flex:1;min-width:0 on the title lets it actually shrink and hit its own text-overflow:ellipsis
   instead of hard-clipping at the bar's edge (a flex item's default min-width:auto refuses to
   shrink below its content size, which was overriding the ellipsis entirely); the assignee name
   stays a fixed non-shrinking segment so it's never the one that gets cut mid-word. */
.pcal-bar-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700}
.pcal-bar-assignee{flex:none;opacity:.75;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:40%}
.pcal-bar.pcal-sub{opacity:.88}
.pcal-bar.completed .pcal-bar-title{text-decoration:line-through}
.pcal-overdue-flag,.pcal-completed-flag{font-size:var(--fs-xs);flex:none;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.pcal-overdue-flag{color:var(--red)}
.pcal-completed-flag{color:var(--green)}

@media (max-width:900px){
  .pcal-layout{flex-direction:column}
  .pcal-sidebar{width:100%;position:static;max-height:280px}
}

.folder-row,.file-row{display:flex;align-items:center;gap:10px;padding:9px 10px;border-bottom:1px solid var(--line-soft);cursor:pointer}
.folder-row:hover,.file-row:hover{background:var(--paper)}
.file-row{cursor:default}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--fs-md);color:var(--muted);margin-bottom:10px;flex-wrap:wrap;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.breadcrumb a{color:var(--blue);cursor:pointer}

.thread-row{padding:12px;border-bottom:1px solid var(--line-soft);cursor:pointer}
.thread-row:hover{background:var(--paper)}
.thread-row:last-child{border-bottom:none}

/* ---- Trix rich text (bz-rich-text directive) — matched to the .input/.select look ------- */
/* Locked/sticky: stays pinned at the top of whatever actually scrolls it out of view (the
   page, or a modal's scrolling backdrop) instead of scrolling away while composing a long
   description/comment. Needs `overflow:visible` on any .kpi card wrapping it — .kpi normally
   clips overflow (for its ::before accent bar), which would otherwise trap position:sticky
   inside the card's own box, where it has no scrollable distance to react to and never visibly
   sticks (see the :has() override below). */
/* Pins BELOW the topbar, not at the raw viewport top: .topbar is itself sticky at top:0 with
   z-index:30, so a toolbar stuck at top:0 slid underneath it and read as "the toolbar scrolled
   away" while writing a long description. Offsetting by the topbar's height parks it in the
   first visible strip of the page instead, and z-index:20 keeps it above page content while
   still passing under the topbar. */
trix-toolbar{border:1px solid var(--line);border-bottom:0;border-radius:var(--r-xs) var(--r-xs) 0 0;
  background:var(--paper);padding:6px 8px;position:sticky;top:var(--topbar-h);z-index:20}
/* In a modal the scrolling ancestor is .modal-back — a fixed overlay stacked ABOVE the topbar,
   so there's no topbar to clear and the toolbar pins to the container's own top edge. */
.modal-back trix-toolbar{top:0}
trix-toolbar .trix-button-group{border-color:var(--line);margin-bottom:0}
trix-toolbar .trix-button{color:var(--muted)}
trix-toolbar .trix-button.trix-active{color:var(--blue);background:var(--blue-bg)}
/* Trix's own stylesheet (trix.css) draws every button glyph as a fixed dark-gray SVG
   background-image on the button's own ::before, baked in at build time — it has no CSS
   custom property and never responds to var(--muted)/var(--blue) above, which only recolor
   text and the active-state background. Fine on Trix's assumed light toolbar; on the dark
   theme's near-black --paper toolbar those dark glyphs are nearly invisible. Inverting the
   pseudo-element's own image is the standard fix for a monochrome baked icon set with no
   color hook — turns the dark glyph light without touching the button's border/background,
   which are already theme-aware. */
:root[data-theme="dark"] trix-toolbar .trix-button::before{filter:invert(1)}
.kpi:has(trix-toolbar){overflow:visible}
/* Account/Project/Task/Sub-Task detail pages run long (description, activity, comments, sub-
   tasks...) — scroll far enough and the record's own name (as opposed to the generic page title
   "Account"/"Project"/"Task" in the topbar) scrolls out of view, so there's no longer any
   confirmation of WHICH one you're looking at. Sticking it below the topbar, same top offset and
   layering convention as the trix-toolbar sticky rule above, keeps it in view for the whole
   scroll — centering (rather than the page's normal left-aligned flow) reads as "this is a
   pinned status bar," not just a heading that got stuck. Needs a page-spanning ancestor to have
   room to travel while pinned (see bz-rich-text's display:block note above for what happens
   without one) — on todo-list-detail.html this IS the page-level <h1>; task-detail.html's own
   title lives inside a short header .kpi card instead, so it gets a separate slim bar placed as
   a direct child of the page wrapper (see .sticky-detail-title use in task-detail.html). */
.sticky-detail-title{position:sticky;top:var(--topbar-h);z-index:15;background:var(--card);
  text-align:center;font-family:var(--font-display);font-weight:700;font-size:var(--fs-base);color:var(--ink);
  padding:11px 16px;margin:0 0 14px;border:1px solid var(--line);border-radius:var(--r-xs);box-shadow:var(--sh-sm);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
/* The name text is padded symmetrically (not just on the badge's side) so it keeps reading as
   centered on the bar as a whole, rather than centered-then-shoved-left by the badge crowding one
   side; ellipsis guards the "Task - Project - Client" breadcrumb on a narrow viewport. The badge
   is absolutely positioned against .sticky-detail-title's own sticky (= positioned) box, so it
   floats independent of the text's length. */
.sticky-detail-title .sdt-name{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 100px}
.sticky-detail-title .sdt-badge{position:absolute;right:16px;top:50%;transform:translateY(-50%)}
/* zoomed-in, document-like writing view (Basecamp's own composer is roomy, not a cramped textarea) */
trix-editor.bz-trix{min-height:170px;border:1px solid var(--line);border-radius:0 0 var(--r-xs) var(--r-xs);
  font:inherit;font-size:var(--fs-lg);line-height:1.7;color:var(--body);padding:16px 18px;letter-spacing:var(--ls-lg)}
trix-editor.bz-trix:focus{outline:none;border-color:var(--blue)}
/* An unknown/custom element defaults to display:inline. Leaving <bz-rich-text> inline made the
   browser wrap its block children in anonymous block boxes, and the sticky toolbar's containing
   block collapsed to roughly its own height — so it had no distance to travel and scrolled away
   with the page no matter what `top` said. Making the host a block is what actually makes the
   toolbar stick. */
bz-rich-text{display:block}
.bz-trix-wrap{position:relative}
/* Basecamp-style reading column — Description + Comments/Activity read as a document, not a
   full-bleed panel; capped width keeps line length comfortable regardless of how wide the
   parent .kpi card is. Applied on task-detail.html/project-detail.html around the description,
   thread post, comments and activity feed only — the info-grid/sub-tasks table stay full width. */
.reading-col{max-width:740px}
/* Comment composers (Project/Task/Sub-Task) sit as a sibling of .reading-col, not inside it —
   the comment thread reads as a document (capped width), but writing a new comment should use
   the full card width. Toolbar is centered here only; description/other Trix editors keep their
   default left-aligned toolbar. */
.comment-composer{width:100%}
.comment-composer trix-toolbar .trix-button-row{display:flex;justify-content:center;flex-wrap:wrap}
/* Trix inserts a flex-grow spacer before the history-tools group to pin undo/redo to the far
   right — left uncancelled, that fights justify-content:center and splits the toolbar apart
   instead of clustering it. */
.comment-composer trix-toolbar .trix-button-group-spacer{flex-grow:0}
.bz-mention-pop{position:absolute;z-index:60;min-width:200px;max-width:280px;background:var(--card);
  border:1px solid var(--line);border-radius:12px;box-shadow:var(--sh-lg);overflow:hidden;animation:fadeIn .12s}
.bz-mention-opt{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 12px;
  font-size:var(--fs-md);color:var(--body);cursor:pointer;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-mention-opt small{color:var(--faint);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.bz-mention-opt:hover,.bz-mention-opt.active{background:var(--paper)}
.bz-mention-opt.active{color:var(--blue)}
.bz-mention-empty{padding:10px 12px;color:var(--faint);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
/* overflow-wrap:anywhere (inherited, so it cascades to .rich-body a below with no separate
   selector needed) lets a single unbroken long token — a long URL, an identifier — break instead
   of overflowing the 740px reading column horizontally; word-break:break-word is a belt-and-braces
   companion for older Safari. .chat-text (this file) carries word-break:break-word for the same
   reason, though not overflow-wrap:anywhere — this is the first use of the stronger property. */
.rich-body{font-size:var(--fs-lg);color:var(--body);line-height:1.7;letter-spacing:var(--ls-lg);
  overflow-wrap:anywhere;word-break:break-word}
.rich-body p{margin:0 0 16px}
.rich-body p:last-child{margin-bottom:0}
.rich-body a{color:var(--blue)}
.rich-body h1,.rich-body h2,.rich-body h3{margin:20px 0 10px}
.rich-body h1:first-child,.rich-body h2:first-child,.rich-body h3:first-child{margin-top:0}
/* an inline image is a real preview, not a cramped crop — full natural aspect ratio, capped so
   it never overwhelms the reading column, and CENTERED so it reads as a captioned figure sitting
   between paragraphs rather than a left-hugging thumbnail (bzRichBody opens it full-size on click). */
.rich-body img{display:block;max-width:100%;max-height:460px;width:auto;height:auto;
  border-radius:10px;border:1px solid var(--line);margin:14px 8px 14px 0;box-shadow:0 4px 16px rgba(16,24,40,.08);cursor:zoom-in}
/* inline-block, not block — so that multiple attachments sent together (image/chip/PDF, each its
   own sibling <a> — Trix emits one <figure> per file and Html::stripAttachmentCaptions() rebuilds
   each in place with no wrapper) lay out side by side and wrap like words in a line, instead of
   each one forcing its own row. A lone attachment (the common case) still reads identically to a
   block, since an inline-block with no sibling on its line renders the same either way. */
.rich-body a:has(img){display:inline-block;line-height:0;vertical-align:top}

/* non-image, non-PDF attachment (doc/xls/ppt/txt/zip/audio/video) — icon, line break, filename;
   same fc-* colour language as Chat's own file chips (see .chat-file-chip). inline-flex (not
   flex) + a non-auto margin so several chips attached together sit side by side and wrap onto
   fewer per row as the reading column narrows, instead of each stacking on its own line. */
.rich-attach-chip{display:inline-flex;width:fit-content;flex-direction:column;align-items:center;gap:5px;
  max-width:130px;margin:10px 8px 10px 0;padding:12px 10px;border-radius:10px;
  background:var(--paper);border:1px solid var(--line);text-decoration:none;
  font-size:var(--fs-xs);color:var(--body);text-align:center;vertical-align:top;cursor:pointer;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.rich-attach-chip:hover{background:var(--line-soft)}
.rich-attach-chip i{font-size:26px}
.rich-attach-chip .fc-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:110px}
.rich-attach-chip.fc-red i{color:var(--red)}
.rich-attach-chip.fc-blue i{color:var(--blue)}
.rich-attach-chip.fc-green i{color:var(--green)}
.rich-attach-chip.fc-amber i{color:var(--amber)}
.rich-attach-chip.fc-purple i{color:var(--purple)}
.rich-attach-chip.fc-slate i{color:var(--muted)}

/* PDF attachment — an actual page-1 preview (rendered client-side by PDF.js, no server-side
   Ghostscript/Imagick dependency — see bzRichBody's renderPdfPreview()), centered like an image.
   Falls back to a plain icon+filename row (.rich-pdf-preview.fc-fallback) if rendering fails.
   inline-flex (not flex) + a non-auto margin so multiple PDFs attached together sit side by side
   and wrap, like the image/chip attachment types above, instead of each stacking on its own line. */
.rich-pdf-preview{display:inline-flex;flex-direction:column;align-items:center;width:fit-content;
  max-width:100%;margin:14px 8px 14px 0;padding:10px;border-radius:10px;background:var(--paper);
  border:1px solid var(--line);text-decoration:none;cursor:pointer;transition:border-color .15s;vertical-align:top}
.rich-pdf-preview:hover{border-color:var(--blue)}
.rich-pdf-preview canvas{display:block;max-width:100%;height:auto;border-radius:6px;box-shadow:0 4px 16px rgba(16,24,40,.08)}
.rich-pdf-preview.fc-fallback canvas{display:none}
.rich-pdf-preview .rich-pdf-meta{display:flex;align-items:center;gap:8px;margin-top:8px;font-size:var(--fs-sm);color:var(--body);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.rich-pdf-preview.fc-fallback .rich-pdf-meta{margin-top:0;padding:14px 6px}
.rich-pdf-preview .rich-pdf-meta i{font-size:20px;color:var(--red)}
.rich-pdf-preview .fc-name{max-width:260px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- Basecamp-style original-post card (Message Board thread body) ------------------------
   Avatar + author + timestamp above the title, a always-visible delete (it's the one primary
   action on the post itself, not a per-item action in a list — unlike comments below, which
   reveal delete only on hover). ---------------------------------------------------------- */
.thread-post-head{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.thread-post-author{font-weight:700;color:var(--ink);font-size:var(--fs-base);display:block;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.thread-post-time{color:var(--faint);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.thread-post-title{margin:0;font-size:var(--fs-2xl);color:var(--ink);flex:1;min-width:0;line-height:var(--lh-2xl);letter-spacing:var(--ls-2xl)}
.thread-post-body{margin-top:8px}

/* ---- comment rows (Message Board comments, Task/Sub-task comments) — one consistent card
   language: avatar left, author+timestamp header, body below, delete revealed on hover so a
   long comment stream doesn't get cluttered with visible trash icons. Roomier padding + larger
   type than a chat log on purpose — Basecamp's comments read as a document thread, not a chat. */
.comment-row{display:flex;gap:16px;padding:28px 0;border-bottom:1px solid var(--line-soft);position:relative}
.comment-row:first-child{padding-top:2px}
.comment-row:last-child{border-bottom:0;padding-bottom:2px}
.comment-avatar{flex:none;margin-top:1px}
.comment-body-col{flex:1;min-width:0}
.comment-head{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}
.comment-author{font-weight:700;color:var(--ink);font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.comment-time{color:var(--faint);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.comment-delete-btn{position:absolute;top:26px;right:0;opacity:0;transition:opacity .12s;
  width:26px;height:26px;border-radius:8px;border:1px solid var(--line);background:var(--card);
  color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px}
.comment-delete-btn:hover{color:var(--red);border-color:var(--red-bg);background:var(--red-bg)}
.comment-row:hover .comment-delete-btn{opacity:1}
.comment-role{color:var(--faint);font-size:var(--fs-xs);font-weight:500;margin-left:6px;text-transform:uppercase;letter-spacing:.03em;line-height:var(--lh-xs)}

/* Reply-to-a-specific-comment (§ task/project/thread comments) — same concept as Chat's own
   .chat-reply-quote/.chat-reply-banner (see those for the DM/group precedent), styled to match
   this document-like comment-card language instead of a chat bubble. */
.reply-quote{border-left:3px solid var(--blue);background:var(--paper);border-radius:9px;padding:6px 10px;margin-bottom:10px;cursor:pointer;max-width:420px}
.reply-quote:hover{background:var(--line-soft)}
.rq-sender{font-size:var(--fs-xs);font-weight:700;color:var(--blue);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.rq-snippet{font-size:var(--fs-sm);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.reply-banner{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--paper);border:1px solid var(--line);border-radius:11px;padding:8px 12px;margin-bottom:10px}
.rb-label{font-weight:600;color:var(--blue);font-size:var(--fs-sm)}
.rb-snippet{color:var(--muted);font-size:var(--fs-sm);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:420px}
.reply-banner button{border:0;background:none;color:var(--faint);cursor:pointer;font-size:var(--fs-md)}
.reply-banner button:hover{color:var(--red)}
.comment-row.flash{animation:flashHighlight 1.3s ease}
/* Handover ticket comments are plain rows (no .comment-row card) — same flash when a
   notification's ?c= anchor scrolls to one (ProjectHandoverDetailController.scrollToComment). */
.handover-comment.flash{animation:flashHighlight 1.3s ease;border-radius:9px}

/* Unread divider (§ mentions/notifications) — a Slack/WhatsApp-style "new since you were last
   here" line, shown once above the first feed item newer than the viewer's own read marker
   (read_markers table / ReadMarker.php) so a burst of unread mentions/comments is easy to find
   and read together, rather than scrolling the whole thread hunting for what changed. */
.unread-divider{display:flex;align-items:center;gap:10px;margin:18px 0;color:var(--red)}
.unread-divider::before,.unread-divider::after{content:'';flex:1;height:1px;background:var(--red-bg)}
.unread-divider span{font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.08em;white-space:nowrap;line-height:var(--lh-xs)}

/* ---- Task Detail activity feed: status/date-change events interleaved with comments (see
   .comment-row above) as compact system rows — smaller avatar, muted body, no delete button.
   Deliberately kept small/quiet next to the enlarged .comment-row above it — Basecamp's own
   activity entries stay a quiet, secondary notice rather than competing with real comments. */
.tk-sys-row{display:flex;gap:12px;padding:14px 0;border-bottom:1px solid var(--line-soft);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.tk-sys-row:last-child{border-bottom:0}
.tk-sys-body{flex:1;min-width:0;color:var(--body)}
.tk-sys-time{color:var(--faint);font-size:var(--fs-xs);margin-left:6px;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}

/* ---- buttons ------------------------------------------------------------- */
.btn-bz{font:inherit;font-weight:600;font-size:var(--fs-base);border:0;border-radius:11px;padding:10px 18px;
  cursor:pointer;display:inline-flex;align-items:center;gap:8px;transition:transform .15s,box-shadow .2s,filter .2s;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.btn-bz:active{transform:translateY(1px)}
.btn-primary{background:var(--grad);color:#fff;box-shadow:0 6px 18px rgba(216,30,124,.24)}
.btn-primary:hover{filter:brightness(1.05);box-shadow:0 8px 22px rgba(216,30,124,.3)}
.btn-ghost{background:var(--card);color:var(--ink);border:1px solid var(--line)}
.btn-ghost:hover{border-color:var(--line);background:var(--line-soft)}
.btn-subtle{background:var(--line-soft);color:var(--body)}
.btn-subtle:hover{background:var(--line)}
.btn-danger{background:var(--red-bg);color:var(--red)}
.btn-danger:hover{background:var(--red-bg)}
.btn-sm{padding:7px 12px;font-size:var(--fs-md);border-radius:9px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.btn-bz:disabled{opacity:.55;cursor:not-allowed}

/* ---- forms --------------------------------------------------------------- */
.field{margin-bottom:15px}
.field > label{display:block;font-size:var(--fs-sm);font-weight:600;color:var(--ink);margin-bottom:6px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.input,.select,textarea.input{
  width:100%;font:inherit;font-size:var(--fs-base);color:var(--ink);background:var(--card);
  border:1px solid var(--line);border-radius:11px;padding:11px 13px;transition:border-color .15s,box-shadow .15s;
;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.input:focus,.select:focus,textarea.input:focus{
  outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(31,95,160,.15)
}
.input.sm{padding:8px 10px;border-radius:9px}
.input:disabled,.input.readonly{background:var(--paper);color:var(--muted);cursor:not-allowed}
.help{font-size:var(--fs-sm);color:var(--muted);margin-top:5px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.help a{color:var(--blue);font-weight:600;text-decoration:none}
.help a:hover{text-decoration:underline}
.form-err{background:var(--red-bg);color:var(--red);border-radius:11px;padding:10px 13px;font-size:var(--fs-md);font-weight:500;margin-bottom:14px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.form-ok{background:var(--green-bg);color:var(--green);border-radius:11px;padding:10px 13px;font-size:var(--fs-md);font-weight:500;margin-bottom:14px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}

/* switch */
.switch{position:relative;display:inline-block;width:44px;height:25px;flex:none}
.switch input{opacity:0;width:0;height:0}
.switch .track{position:absolute;inset:0;background:var(--line);border-radius:999px;transition:.2s;cursor:pointer}
.switch .track::before{content:"";position:absolute;height:19px;width:19px;left:3px;top:3px;background:var(--card);
  border-radius:50%;transition:.2s;box-shadow:0 1px 3px rgba(0,0,0,.2)}
.switch input:checked + .track{background:var(--grad)}
.switch input:checked + .track::before{transform:translateX(19px)}
.switch input:focus-visible + .track{box-shadow:0 0 0 3px rgba(31,95,160,.3)}

/* ---- tables -------------------------------------------------------------- */
.tbl-wrap{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow-x:auto;box-shadow:var(--sh-sm);-webkit-overflow-scrolling:touch}
table.bz{width:100%;border-collapse:collapse;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
table.bz thead th{background:var(--line-soft);text-align:left;padding:12px 16px;font-size:var(--fs-xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);font-weight:700;border-bottom:1px solid var(--line);white-space:nowrap;line-height:var(--lh-xs)}
table.bz tbody td{padding:13px 16px;border-bottom:1px solid var(--line-soft);vertical-align:middle}
table.bz tbody tr:last-child td{border-bottom:0}
table.bz tbody tr{transition:background .12s}
table.bz tbody tr:hover{background:var(--line-soft)}
table.bz td.num,table.bz th.num{text-align:right;font-variant-numeric:tabular-nums}
table.bz tbody tr.dirty{background:var(--amber-bg)}
table.bz tbody tr.dirty:hover{background:var(--amber-bg)}
.icon-btn{
  display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;
  border:1px solid var(--line);background:var(--card);color:var(--muted);cursor:pointer;font-size:14px;
  transition:background .15s,color .15s,border-color .15s;margin-left:4px;
}
.icon-btn:hover{background:var(--paper);color:var(--blue);border-color:var(--blue)}
.icon-btn[disabled]{opacity:.5;cursor:default}
.sheet-expand{background:var(--paper);padding:16px 20px !important;border-bottom:1px solid var(--line-soft)}
.person{display:flex;align-items:center;gap:11px}
.person .avatar{width:32px;height:32px;font-size:13px}
.tier-tag{font-family:var(--font-display);font-size:var(--fs-2xs);letter-spacing:.05em;color:var(--muted);
  border:1px solid var(--line);border-radius:6px;padding:2px 7px;text-transform:uppercase;line-height:var(--lh-2xs)}

/* ---- login --------------------------------------------------------------- */
.login-wrap{min-height:100vh;display:grid;grid-template-columns:1.05fr .95fr}
.login-art{
  position:relative;overflow:hidden;color:#fff;padding:60px;
  display:flex;flex-direction:column;justify-content:space-between;
  background:
    radial-gradient(circle at 1px 1px, rgba(255,255,255,.07) 1px, transparent 0) 0 0/24px 24px,
    linear-gradient(150deg,var(--navy) 0%,var(--navy-900) 60%);
}
.login-art::after{content:"";position:absolute;width:560px;height:560px;right:-180px;top:-140px;border-radius:50%;
  background:var(--grad);filter:blur(70px);opacity:.45}
.login-art::before{content:"";position:absolute;width:380px;height:380px;left:-130px;bottom:-160px;border-radius:50%;
  background:radial-gradient(circle,rgba(31,95,160,.55),transparent 70%);filter:blur(10px)}
.login-art .inner{position:relative;z-index:2}
.login-art .brand-mark{display:flex;align-items:center;gap:12px}
.login-art .brand-mark img{height:42px;filter:brightness(0) invert(1) drop-shadow(0 4px 14px rgba(0,0,0,.25))}
.login-art h2{color:#fff;font-size:var(--fs-hero);font-weight:600;line-height:1.14;margin-top:32px;max-width:13ch;letter-spacing:-.02em}
.login-art p{color:#a9bace;max-width:36ch;margin-top:18px;font-size:var(--fs-base);line-height:1.6;letter-spacing:var(--ls-base)}
.login-feature{display:flex;align-items:center;gap:14px;color:#c9d5e3;font-size:var(--fs-base);font-weight:500;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.login-feature .lf-icon{
  width:36px;height:36px;border-radius:11px;flex:none;display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);color:#f0a8d6;font-size:16px;
  box-shadow:0 4px 10px rgba(0,0,0,.15);
}
.login-form{
  display:flex;align-items:center;justify-content:center;padding:40px;
  background:radial-gradient(circle at 25% 15%, rgba(31,95,160,.06), transparent 55%), var(--paper);
}
.login-card{
  width:100%;max-width:400px;background:var(--card);border-radius:24px;padding:44px 40px 38px;
  box-shadow:0 24px 60px rgba(18,41,66,.09), 0 6px 18px rgba(18,41,66,.05);border:1px solid rgba(18,41,66,.05);
}
.login-card .logo-c{margin-bottom:28px}
.login-card .logo-c img{height:38px;filter:drop-shadow(0 6px 14px rgba(31,95,160,.18))}
.login-card .eyebrow-tag{
  display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--blue);background:var(--blue-bg,#eaf2fb);padding:5px 11px;border-radius:999px;
  margin-bottom:14px;
;line-height:var(--lh-xs)}
.login-card h3{font-size:var(--fs-2xl);margin-bottom:6px;letter-spacing:-.01em;line-height:var(--lh-2xl)}
.field-icon{position:relative}
.field-icon > i{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--faint);font-size:15px;pointer-events:none}
.field-icon .input{padding-left:40px}
.field-icon .pw-toggle{
  position:absolute;right:6px;top:50%;transform:translateY(-50%);border:0;background:none;cursor:pointer;
  color:var(--faint);font-size:15px;padding:8px;border-radius:8px;transition:color .15s;
}
.field-icon .pw-toggle:hover{color:var(--ink)}
.login-hint{
  display:flex;align-items:center;justify-content:center;gap:10px;margin-top:22px;padding:12px 14px;
  background:var(--paper);border:1px solid var(--line);border-radius:12px;font-size:var(--fs-sm);color:var(--muted);
  line-height:1.5;text-align:center;
;letter-spacing:var(--ls-sm)}
.login-hint i{color:var(--blue);font-size:14px;margin-top:2px}
.login-hint b{color:var(--ink)}

/* ---- misc ---------------------------------------------------------------- */
.grid{display:grid;gap:16px}
.g-2{grid-template-columns:repeat(2,1fr)}.g-3{grid-template-columns:repeat(3,1fr)}
.g-4{grid-template-columns:repeat(4,1fr)}
.g-5{grid-template-columns:repeat(5,1fr)}
.health-grid{grid-template-columns:.9fr 1.1fr;margin-top:26px}
@media(max-width:900px){.health-grid{grid-template-columns:1fr}}
/* full-page task/sub-task create form (TaskFormController) — main content (title/description)
   against a fixed-width details sidebar, so the page uses the full window instead of a
   narrow centered card floating in empty space. */
.taskform-layout{grid-template-columns:1fr 360px}
@media(max-width:900px){.taskform-layout{grid-template-columns:1fr}}
.row-line{display:flex;align-items:center;justify-content:space-between;gap:12px}
.progress{height:7px;background:var(--line-soft);border-radius:999px;overflow:hidden}
.progress > span{display:block;height:100%;border-radius:999px;background:var(--grad);transition:width .9s cubic-bezier(.2,.7,.3,1)}
/* RAG-colored variant — Team Pipeline's utilisation bar needs the color to carry the "over
   allocated" signal itself, not just the brand gradient every other progress bar uses. */
.progress.green > span{background:var(--green)}
.progress.amber > span{background:var(--amber)}
.progress.red > span{background:var(--red)}
.list-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh-sm);padding:6px 4px}
.list-row{display:flex;align-items:center;gap:14px;padding:13px 16px;border-bottom:1px solid var(--line-soft)}
.list-row:last-child{border-bottom:0}
.list-expand{background:var(--paper);padding:16px 18px;border-bottom:1px solid var(--line-soft);border-radius:0 0 10px 10px}
.badge-pay{font-family:var(--font-display);font-weight:600;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.badge-pay.up{color:var(--green)}.badge-pay.down{color:var(--red)}.badge-pay.flat{color:var(--muted)}
.empty{text-align:center;padding:44px 20px;color:var(--muted)}
.empty i{font-size:30px;color:var(--faint);display:block;margin-bottom:10px}
.spinner{width:34px;height:34px;border-radius:50%;border:3px solid var(--line);border-top-color:var(--blue);
  animation:spin .7s linear infinite;margin:40px auto}
@keyframes spin{to{transform:rotate(360deg)}}
/* feedback toast — success / error, slides in top-right after every submission */
.fx-toast{
  position:fixed;top:calc(var(--topbar-h) + 14px);right:24px;z-index:120;
  display:flex;align-items:flex-start;gap:12px;
  background:var(--card);border-radius:14px;box-shadow:var(--sh-lg);
  padding:16px 16px 16px 18px;width:100%;max-width:min(380px,calc(100vw - 48px));
  border-left:4px solid var(--green);
  transform:translateX(150%);opacity:0;pointer-events:none;
  transition:transform .5s cubic-bezier(.34,1.56,.64,1),opacity .35s ease;
}
.fx-toast.show{transform:translateX(0);opacity:1;pointer-events:auto}
.fx-toast.err{border-left-color:var(--red)}
.fx-toast-icon{font-size:21px;color:var(--green);flex:none;margin-top:1px}
.fx-toast.err .fx-toast-icon{color:var(--red)}
.fx-toast-msg{flex:1;font-size:var(--fs-md);font-weight:500;color:var(--ink);line-height:1.45;padding-top:2px;letter-spacing:var(--ls-md)}
.fx-toast-close{
  border:0;background:none;color:var(--faint);cursor:pointer;font-size:var(--fs-md);line-height:1;
  padding:3px;flex:none;transition:color .15s;
;letter-spacing:var(--ls-md)}
.fx-toast-close:hover{color:var(--ink)}

/* one-time "enable desktop notifications" banner (and the PWA install banner alongside it) —
   persistent (no auto-hide, unlike the toast above) until acted on/dismissed. Both share this
   fixed-position bottom-left stack (below the safe-area inset on a notched/home-indicator phone)
   so neither collides with the toast's top-right corner or the topbar's own bell/chat popovers,
   and so having BOTH visible at once (a first-ever mobile visit can trigger both) stacks them
   instead of overlapping identically-positioned cards. */
.banner-stack{
  position:fixed;left:24px;z-index:110;
  bottom:calc(24px + env(safe-area-inset-bottom, 0px));
  display:flex;flex-direction:column;gap:12px;
  width:100%;max-width:min(400px,calc(100vw - 48px));
}
.push-banner{
  display:flex;align-items:center;gap:12px;
  background:var(--card);border-radius:14px;box-shadow:var(--sh-lg);
  padding:14px 14px 14px 18px;width:100%;
  border-left:4px solid var(--blue);
  animation:fadeIn .3s ease;
}
.push-banner.install-banner{border-left-color:var(--magenta)}
.push-banner-icon{font-size:19px;color:var(--blue);flex:none}
.push-banner-msg{flex:1;font-size:var(--fs-md);font-weight:500;color:var(--ink);line-height:1.4;letter-spacing:var(--ls-md)}
.push-banner-close{
  border:0;background:none;color:var(--faint);cursor:pointer;font-size:var(--fs-md);line-height:1;
  padding:3px;flex:none;transition:color .15s;
;letter-spacing:var(--ls-md)}
.push-banner-close:hover{color:var(--ink)}
@media(max-width:640px){.banner-stack{left:16px}}

/* 3D brand motion — full-screen splash played on sign-in reveal and sign-out farewell */
.brand-fx{
  position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;
  perspective:1000px;animation:fadeIn .2s ease;opacity:1;transition:opacity .22s ease;
  background:
    radial-gradient(120% 60% at 0% 0%, rgba(31,95,160,.45) 0%, transparent 60%),
    radial-gradient(120% 60% at 100% 100%, rgba(216,30,124,.4) 0%, transparent 60%),
    linear-gradient(180deg,var(--navy) 0%,var(--navy-900) 100%);
}
.brand-fx img{
  height:64px;width:auto;filter:brightness(0) invert(1) drop-shadow(0 14px 30px rgba(0,0,0,.35));
  transform-style:preserve-3d;
}
.brand-fx.in img{animation:brandSpinIn .85s cubic-bezier(.2,.7,.2,1) both}
.brand-fx.out img{animation:brandSpinOut .85s cubic-bezier(.4,0,.6,1) both}
.brand-fx.leaving{opacity:0}
@keyframes brandSpinIn{
  0%{transform:rotateY(-110deg) scale(.35);opacity:0}
  60%{transform:rotateY(18deg) scale(1.1);opacity:1}
  100%{transform:rotateY(0deg) scale(1);opacity:1}
}
@keyframes brandSpinOut{
  0%{transform:rotateY(0deg) scale(1);opacity:1}
  35%{transform:rotateY(-18deg) scale(1.06);opacity:1}
  100%{transform:rotateY(130deg) scale(.3);opacity:0}
}

/* segmented control */
.seg{display:inline-flex;background:var(--line-soft);border-radius:11px;padding:4px;gap:2px}
.seg button{border:0;background:transparent;font:inherit;font-size:var(--fs-md);font-weight:600;color:var(--muted);
  padding:7px 15px;border-radius:8px;cursor:pointer;transition:.15s;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.seg button.active{background:var(--card);color:var(--ink);box-shadow:var(--sh-sm)}

/* modal */
.modal-back{position:fixed;inset:0;background:rgba(14,32,54,.5);z-index:60;
  display:flex;align-items:flex-start;justify-content:center;padding:48px 16px;overflow:auto}
.bz-modal{background:var(--card);border-radius:20px;box-shadow:var(--sh-lg);width:100%;max-width:560px}
.bz-modal .mhead{padding:20px 24px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}
.bz-modal .mbody{padding:22px 24px}
.bz-modal .mfoot{padding:16px 24px;border-top:1px solid var(--line);display:flex;justify-content:flex-end;gap:10px}
.x-btn{border:0;background:var(--line-soft);width:32px;height:32px;border-radius:9px;cursor:pointer;color:var(--muted);font-size:16px}
.x-btn:hover{background:var(--line);color:var(--ink)}
/* In-app preview for an inline comment/rich-text image — see bzRichBody's openLightbox() in
   directives.js. Plain DOM (not an Angular template — appended straight to <body> so it renders
   above everything regardless of which comment's directive instance triggered it), matching how
   bzRichText's own @mention popup is built. */
/* Same bar-over-full-bleed-content construction as .bz-pdf-preview below — opening an image is
   the same experience as opening a PDF (filename + download + close), not a different one. */
.bz-img-lightbox{position:fixed;inset:0;background:rgba(14,32,54,.92);z-index:70;
  display:flex;flex-direction:column;animation:fadeIn .15s}
.bz-img-lightbox-bar{flex:none;display:flex;align-items:center;gap:10px;padding:12px 16px;
  background:rgba(255,255,255,.06);border-bottom:1px solid rgba(255,255,255,.12)}
.bz-img-lightbox-name{flex:1;color:#fff;font-size:var(--fs-md);font-weight:600;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-img-lightbox-btn{flex:none;width:36px;height:36px;border-radius:9px;border:0;
  background:rgba(255,255,255,.12);color:#fff;font-size:15px;cursor:pointer;display:flex;
  align-items:center;justify-content:center;text-decoration:none;transition:background .12s}
.bz-img-lightbox-btn:hover{background:rgba(255,255,255,.24)}
.bz-img-lightbox-frame-wrap{flex:1;display:flex;align-items:center;justify-content:center;padding:24px;overflow:auto}
.bz-img-lightbox-frame-wrap img{max-width:100%;max-height:100%;border-radius:var(--r-xs);box-shadow:var(--sh-lg);object-fit:contain}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}

/* In-app PDF preview (see app/services/pdfpreview.js) — a top bar (filename + download + close)
   over a full-bleed iframe showing the browser's own native PDF viewer against a blob URL, same
   "plain DOM straight on <body>" construction as the image lightbox above. */
.bz-pdf-preview{position:fixed;inset:0;background:rgba(14,32,54,.92);z-index:70;
  display:flex;flex-direction:column;animation:fadeIn .15s}
.bz-pdf-preview-bar{flex:none;display:flex;align-items:center;gap:10px;padding:12px 16px;
  background:rgba(255,255,255,.06);border-bottom:1px solid rgba(255,255,255,.12)}
.bz-pdf-preview-name{flex:1;color:#fff;font-size:var(--fs-md);font-weight:600;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-pdf-preview-btn{flex:none;width:36px;height:36px;border-radius:9px;border:0;
  background:rgba(255,255,255,.12);color:#fff;font-size:15px;cursor:pointer;display:flex;
  align-items:center;justify-content:center;text-decoration:none;transition:background .12s}
.bz-pdf-preview-btn:hover{background:rgba(255,255,255,.24)}
.bz-pdf-preview-frame-wrap{flex:1;position:relative;display:flex;align-items:center;justify-content:center}
.bz-pdf-preview-frame-wrap iframe{width:100%;height:100%;border:0;background:#fff}
.bz-pdf-preview-fail{color:#fff;font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}

/* permission matrix */
.matrix{width:100%;border-collapse:collapse;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.matrix th{padding:12px;text-align:center;font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--muted);line-height:var(--lh-xs)}
.matrix th.rolehead{min-width:120px}
.matrix td{padding:11px 12px;border-top:1px solid var(--line-soft)}
.matrix td.pname{font-weight:500;color:var(--ink)}
.matrix td.pcat{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--faint);padding-top:18px;line-height:var(--lh-xs)}
.matrix .cell{text-align:center}
.check{width:22px;height:22px;border-radius:7px;border:1.5px solid var(--line);display:inline-flex;align-items:center;
  justify-content:center;cursor:pointer;transition:.15s;color:#fff}
.check.on{background:var(--grad);border-color:transparent}
.check.locked{background:var(--grad);border-color:transparent;opacity:.55;cursor:not-allowed}
.check.off:hover{border-color:var(--blue)}

/* ---- nested sidebar (sub-modules) ----------------------------------------- */
.nav-item.parent{justify-content:flex-start}
.nav-chevron{margin-left:auto !important;font-size:12px !important;width:auto !important;opacity:.6}
.nav-badge{margin-left:auto;background:var(--magenta);color:#fff;font-size:var(--fs-2xs);font-weight:700;
  border-radius:99px;padding:2px 7px;min-width:18px;text-align:center;line-height:1.4;flex:none;letter-spacing:var(--ls-2xs)}
.nav-children{margin:2px 0 6px 20px;border-left:1px solid rgba(255,255,255,.1);padding-left:10px}
.nav-subitem{
  display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:9px;
  color:#9fb0c4;font-weight:500;font-size:var(--fs-md);cursor:pointer;position:relative;z-index:1;
  transition:color .15s ease;
;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.nav-subitem:hover{color:#fff;background:rgba(255,255,255,.07)}
.nav-subitem.active{color:#fff;font-weight:600}
.nav-subitem.active:hover{background:none}
.dot-sm{width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.6;flex:none}

/* ---- searchable dropdown (bz-search-select) ------------------------------- */
bz-search-select{display:inline-block}
.bz-select{position:relative;display:inline-block;min-width:160px;z-index:1}
.bz-select.open{z-index:10}
/* .kpi has position:relative but no z-index of its own (z-index:auto), so it never forms its
   own stacking context — meaning a LATER sibling .kpi card simply paints on top of an earlier
   one's absolutely-positioned dropdown menu, regardless of that menu's own z-index. Promoting
   the specific card containing an OPEN dropdown fixes this without touching every .kpi. */
.kpi:has(.bz-select.open){z-index:10}
.bz-select-ctrl{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;font:inherit;font-size:var(--fs-md);
  color:var(--ink);background:var(--card);border:1px solid var(--line);border-radius:11px;padding:9px 12px;cursor:pointer;
  transition:border-color .15s;
;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-select-ctrl:hover{border-color:var(--blue)}
.bz-select-ctrl:disabled{opacity:.55;cursor:not-allowed;border-color:var(--line)}
.bz-select.open .bz-select-ctrl{border-color:var(--blue);box-shadow:0 0 0 3px rgba(31,95,160,.12)}
.bz-select-ctrl .ph{color:var(--faint)}
.bz-select-menu{
  position:absolute;top:calc(100% + 6px);left:0;z-index:50;min-width:220px;max-width:320px;
  background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:var(--sh-lg);overflow:hidden;
  animation:fadeIn .15s;
}
.bz-select-search{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);color:var(--faint)}
.bz-select-search input{border:0;outline:0;background:none;font:inherit;font-size:var(--fs-md);width:100%;color:var(--ink);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-select-list{max-height:260px;overflow:auto;padding:6px}
.bz-select-opt{padding:8px 10px;border-radius:8px;font-size:var(--fs-md);color:var(--body);cursor:pointer;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.bz-select-opt:hover{background:var(--paper)}
.bz-select-opt.active{background:var(--green-bg);color:var(--green);font-weight:600}
.bz-select-empty{padding:14px;text-align:center;color:var(--faint);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}

/* ---- shared emoji reactions (bz-reactions directive) — Chat, Project Chat, Task/Sub-task
   comments, Message Board all reuse this one component. ---------------------------------- */
.bz-reactions{position:relative;display:flex;align-items:center;flex-wrap:wrap;gap:5px;margin-top:6px}
.bz-reaction-pill{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:99px;
  background:var(--paper);border:1px solid var(--line);font-size:var(--fs-sm);cursor:pointer;transition:all .12s;line-height:1.6;letter-spacing:var(--ls-sm)}
.bz-reaction-pill:hover{border-color:var(--blue)}
.bz-reaction-pill.mine{background:var(--blue-bg);border-color:var(--blue)}
.bz-reaction-count{color:var(--muted);font-size:var(--fs-xs);font-weight:600;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.bz-reaction-pill.mine .bz-reaction-count{color:var(--blue)}
.bz-reaction-add{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;
  border-radius:50%;color:var(--faint);cursor:pointer;font-size:var(--fs-base);transition:all .12s;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.bz-reaction-add:hover{background:var(--paper);color:var(--blue)}
.bz-reaction-picker{position:absolute;bottom:calc(100% + 6px);left:0;z-index:55;display:flex;gap:2px;
  padding:6px;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--sh-lg);animation:fadeIn .12s}
.bz-reaction-picker span{display:flex;align-items:center;justify-content:center;width:30px;height:30px;
  font-size:var(--fs-lg);border-radius:8px;cursor:pointer;transition:background .12s,transform .12s;line-height:var(--lh-lg);letter-spacing:var(--ls-lg)}
.bz-reaction-picker span:hover{background:var(--paper);transform:scale(1.15)}

/* ---- filter toolbar (used across reports / logs sheet) -------------------- */
.filter-bar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:18px}
.filter-range{display:flex;align-items:center;gap:8px}
.filter-search{display:flex;align-items:center;gap:8px;background:var(--card);border:1px solid var(--line);
  border-radius:11px;padding:0 12px;color:var(--faint)}
.filter-search input{border:0;outline:0;background:none;font:inherit;font-size:var(--fs-md);padding:9px 0;color:var(--ink);min-width:180px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.input.sm{padding:8px 11px;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}

/* ---- integrated logs sheet (spreadsheet-style grid) ----------------------- */
.sheet-wrap{background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh-sm);overflow:auto;max-height:70vh}
table.sheet-tbl{border-collapse:separate;border-spacing:0}
table.sheet-tbl th,table.sheet-tbl td{white-space:nowrap}
.sheet-sticky-col{position:sticky;left:0;background:var(--card);z-index:2;box-shadow:1px 0 0 var(--line)}
table.sheet-tbl thead th{position:sticky;top:0;background:var(--line-soft);z-index:1}
table.sheet-tbl thead th.sheet-sticky-col{z-index:3}
.sheet-cell{text-align:center;font-family:var(--font-display);font-weight:600;font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.sheet-cell.green{background:var(--green-bg);color:var(--green)}
.sheet-cell.amber{background:var(--amber-bg);color:var(--amber)}
.sheet-cell.red{background:var(--red-bg);color:var(--red)}

/* ---- chat ------------------------------------------------------------------ */
.chat-wrap{
  display:flex;height:calc(100vh - var(--topbar-h) - 84px);min-height:520px;
  background:var(--card);border:1px solid var(--line);border-radius:20px;
  box-shadow:0 2px 8px rgba(18,41,66,.05),0 16px 40px rgba(18,41,66,.09);overflow:hidden;
}

/* ---- clickable person affordance — any name/avatar/@mention that jumps to that person's DM
   in general Chat (see app/services/personnav.js). Kept as one shared class rather than
   per-context rules so the "this is a person, click it" cue reads the same everywhere. -------- */
.person-clickable{cursor:pointer;transition:color .12s,opacity .12s}
span.person-clickable:hover,td.person-clickable:hover,div.person-clickable:hover,a.person-clickable:hover{color:var(--blue);text-decoration:underline}
.person-clickable:hover bz-avatar,span.person-clickable:hover .avatar{opacity:.85}
.rich-mention{color:var(--blue);font-weight:600;background:rgba(31,95,160,.09);border-radius:5px;padding:0 3px;transition:background .12s}
.rich-mention:hover{background:rgba(31,95,160,.18);text-decoration:underline}

/* ---- project chat (project-detail's Chat tab) — same bubble/composer language as the
   company-wide Chat above (.chat-thread/.chat-composer/.chat-msg etc. reused as-is), just a
   single fixed-height panel instead of a full-viewport list+thread layout. ------------------ */
.project-chat-wrap{
  display:flex;flex-direction:column;height:620px;background:var(--card);
  border:1px solid var(--line);border-radius:20px;
  box-shadow:0 2px 8px rgba(18,41,66,.05),0 16px 40px rgba(18,41,66,.09);overflow:hidden;
}
.project-chat-head{display:flex;align-items:center;gap:12px;padding:14px 22px;border-bottom:1px solid var(--line);
  background:var(--card);box-shadow:0 1px 0 rgba(18,41,66,.02)}

/* ---- project threads (project-detail's Chat tab, Slack-like) — reuses .chat-wrap's own
   list+panel layout as-is (same engine as company-wide Chat, see ProjectChatController.php),
   just a fixed height instead of full-viewport. ------------------------------------------ */
.project-threads-wrap{height:620px;border-radius:20px}

/* thin, unobtrusive scrollbars for the list + thread panes */
.chat-list-scroll,.chat-thread,.chat-people-list,.tcm-list{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.chat-list-scroll::-webkit-scrollbar,.chat-thread::-webkit-scrollbar,.chat-people-list::-webkit-scrollbar,.tcm-list::-webkit-scrollbar{width:7px}
.chat-list-scroll::-webkit-scrollbar-thumb,.chat-thread::-webkit-scrollbar-thumb,.chat-people-list::-webkit-scrollbar-thumb,.tcm-list::-webkit-scrollbar-thumb{
  background:var(--line);border-radius:999px}
.chat-list-scroll::-webkit-scrollbar-thumb:hover,.chat-thread::-webkit-scrollbar-thumb:hover{background:var(--faint)}

.chat-list{width:328px;flex:none;border-right:1px solid var(--line);display:flex;flex-direction:column;background:linear-gradient(180deg,var(--card),var(--paper))}
.chat-list-head{display:flex;gap:8px;padding:16px;border-bottom:1px solid var(--line);background:var(--card)}
/* All/Pings/Groups/Projects — narrows the same ng-repeat the search box and account filter
   already narrow (see vm.matchesType), so the three filters just compose. */
.chat-type-tabs{display:flex;padding:0 16px 12px;gap:6px}
.chat-type-tabs button{flex:1;border:1px solid var(--line);background:var(--paper);border-radius:9px;
  padding:6px 4px;font:inherit;font-size:var(--fs-sm);font-weight:600;color:var(--muted);cursor:pointer;transition:all .12s;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.chat-type-tabs button.active{background:var(--navy);color:#fff;border-color:transparent}
.chat-type-tabs button:not(.active):hover{background:var(--line-soft);color:var(--ink)}
.chat-list-head .btn-primary{box-shadow:0 6px 16px rgba(216,30,124,.28);transition:transform .15s}
.chat-list-head .btn-primary:hover{transform:rotate(90deg) scale(1.05)}
/* Account filter gets its own row below Search — the 328px sidebar has no room for search +
   filter + the new-chat button on one line (the search input alone won't shrink below 180px).
   bz-search-select's inner control is shrink-to-fit by default; stretch it across the row here
   (flex:1 on the lone flex item, then 100% down through its own inner elements) so it doesn't
   look like it's floating at the left of an otherwise-empty row. */
.chat-account-filter-row bz-search-select{display:block;flex:1}
.chat-account-filter-row .bz-select{width:100%}
.chat-account-filter-row .bz-select-ctrl{width:100%}
.chat-list-scroll{flex:1;overflow-y:auto;padding:6px}
.chat-list-item{display:flex;gap:12px;padding:12px;cursor:pointer;border-radius:14px;transition:background .15s,transform .1s;position:relative;margin-bottom:2px}
.chat-list-item:hover{background:var(--card);box-shadow:var(--sh-sm)}
.chat-list-item.active{background:linear-gradient(90deg,rgba(31,95,160,.08),rgba(216,30,124,.08))}
.chat-list-item.active .chat-list-name{color:var(--ink)}
.chat-list-item.active::before{content:"";position:absolute;left:-6px;top:50%;transform:translateY(-50%);height:60%;width:3px;
  border-radius:4px;background:var(--grad)}
.chat-list-body{flex:1;min-width:0}
.chat-list-row1{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.chat-list-name{font-weight:700;color:var(--ink);font-size:var(--fs-base);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:var(--font-display);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.chat-list-time{font-size:var(--fs-2xs);color:var(--faint);flex:none;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.chat-list-row2{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:3px}
.chat-list-snippet{font-size:var(--fs-sm);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.chat-unread-badge{background:var(--grad);color:#fff;font-size:var(--fs-2xs);font-weight:700;min-width:19px;height:19px;
  border-radius:999px;display:flex;align-items:center;justify-content:center;padding:0 5px;flex:none;box-shadow:0 3px 8px rgba(216,30,124,.35);line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.chat-avatar{flex:none}
.group-avatar{border-radius:50%;background:var(--grad-soft);color:var(--blue);
  display:flex;align-items:center;justify-content:center;box-shadow:inset 0 0 0 1px rgba(31,95,160,.12)}

.chat-panel{flex:1;display:flex;flex-direction:column;min-width:0;background:var(--card)}
.chat-panel-head{display:flex;align-items:center;gap:11px;padding:13px 22px;border-bottom:1px solid var(--line);
  background:var(--card);box-shadow:0 1px 0 rgba(18,41,66,.02)}
.chat-panel-head-info.clickable{cursor:pointer;border-radius:10px;padding:4px 8px;margin:-4px -8px;transition:background .12s}
.chat-panel-head-info.clickable:hover{background:var(--paper)}
.chat-panel-name{font-weight:700;color:var(--ink);font-size:var(--fs-base);font-family:var(--font-display);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.chat-panel-sub{font-size:var(--fs-sm);color:var(--muted);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.chat-empty-panel{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:var(--faint)}
.chat-empty-panel .cep-icon{width:84px;height:84px;border-radius:50%;background:var(--grad-soft);display:flex;
  align-items:center;justify-content:center;font-size:36px;color:var(--blue)}
.chat-empty-panel div{font-size:var(--fs-base);font-weight:500;line-height:var(--lh-base);letter-spacing:var(--ls-base)}

.chat-thread{flex:1;overflow-y:auto;padding:22px 26px;background:
  radial-gradient(circle at 15% 0%,rgba(31,95,160,.035),transparent 40%),
  radial-gradient(circle at 100% 30%,rgba(216,30,124,.03),transparent 45%)}

.chat-day-divider{display:flex;align-items:center;justify-content:center;margin:18px 0}
.chat-day-divider span{background:var(--card);border:1px solid var(--line);color:var(--muted);font-size:var(--fs-xs);font-weight:600;
  padding:5px 14px;border-radius:999px;box-shadow:var(--sh-sm);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}

.chat-msg{display:flex;gap:9px;max-width:70%;margin-bottom:14px;animation:msgIn .22s cubic-bezier(.2,.7,.3,1)}
@keyframes msgIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.chat-msg.mine{margin-left:auto;flex-direction:row-reverse}
.chat-msg.grp-start,.chat-msg.grp-mid{margin-bottom:3px}
.chat-msg-avatar{margin-top:auto;margin-bottom:2px}
.chat-msg-avatar-spacer{width:30px;flex:none}
.chat-bubble-col{display:flex;flex-direction:column;gap:3px;min-width:0}
.chat-msg.mine .chat-bubble-col{align-items:flex-end}
.chat-sender{font-size:var(--fs-xs);font-weight:700;color:var(--blue);margin-left:3px;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.chat-bubble{position:relative;padding:9px 13px;border-radius:17px;background:var(--card);border:1px solid var(--line);
  box-shadow:var(--sh-sm);max-width:100%;transition:box-shadow .15s}
.chat-msg:not(.mine).grp-start .chat-bubble{border-bottom-left-radius:6px}
.chat-msg:not(.mine).grp-mid .chat-bubble{border-top-left-radius:6px;border-bottom-left-radius:6px}
.chat-msg:not(.mine).grp-end .chat-bubble{border-top-left-radius:6px}
/* Solid brand blue rather than the blue->magenta signature gradient. An @mention is itself a
   coloured pill, and stacking one on a shifting multi-hue background made it unreadable at the
   magenta end whatever tint the pill used. A single flat colour gives every overlay — mentions,
   links, quoted text — one predictable backdrop to contrast against. The gradient still carries
   the brand everywhere it isn't competing with inline content. */
.chat-msg.mine .chat-bubble{background:var(--blue);border-color:transparent;color:#fff;
  box-shadow:0 3px 10px rgba(31,95,160,.22)}
/* On that solid blue, an opaque white pill with navy text is the highest-contrast pairing
   available — a translucent white pill just muddied into the bubble behind it. */
.chat-msg.mine .rich-mention{color:var(--navy);background:#fff;font-weight:700}
.chat-msg.mine .rich-mention:hover{background:#eaf1f9}
/* Links in my own bubble need the same treatment — brand blue on brand blue is invisible. */
.chat-msg.mine .chat-text a:not(.rich-mention){color:#fff;text-decoration:underline}
/* A someone-else's-message link relied on colour alone (no underline) to read as clickable, while
   my own messages' links were underlined — an inconsistent signal from one bubble to the next.
   Underlining both, and darkening on hover, makes "this is a link" read the same way everywhere
   regardless of whose message it's in or which theme is active. */
.chat-msg:not(.mine) .chat-text a:not(.rich-mention){text-decoration:underline}
.chat-text a:not(.rich-mention):hover{opacity:.8}
.chat-msg.mine.grp-start .chat-bubble{border-bottom-right-radius:6px}
.chat-msg.mine.grp-mid .chat-bubble{border-top-right-radius:6px;border-bottom-right-radius:6px}
.chat-msg.mine.grp-end .chat-bubble{border-top-right-radius:6px}
.chat-bubble.deleted{background:var(--paper);border-style:dashed;box-shadow:none}
.chat-msg.mine .chat-bubble.deleted{background:var(--paper);color:var(--faint)}
.chat-text{font-size:var(--fs-base);white-space:pre-wrap;word-break:break-word;line-height:1.45;letter-spacing:var(--ls-base)}
.chat-deleted{font-size:var(--fs-md);font-style:italic;color:var(--faint);display:flex;align-items:center;gap:6px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.chat-meta{font-size:var(--fs-2xs);margin-top:4px;opacity:.75;transition:opacity .15s;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.chat-msg.grp-start .chat-meta,.chat-msg.grp-mid .chat-meta{opacity:0;height:0;margin:0;overflow:hidden}
.chat-msg.grp-start:hover .chat-meta,.chat-msg.grp-mid:hover .chat-meta{opacity:.75;height:auto;margin-top:4px}
.chat-msg.mine .chat-meta{color:rgba(255,255,255,.85)}
.chat-msg:not(.mine) .chat-meta{color:var(--faint)}

.chat-reply-quote{border-left:3px solid rgba(255,255,255,.6);background:rgba(255,255,255,.14);border-radius:9px;
  padding:5px 9px;margin-bottom:7px;cursor:pointer;transition:background .12s}
.chat-msg:not(.mine) .chat-reply-quote{border-left-color:var(--blue);background:var(--paper)}
.chat-reply-quote:hover{background:rgba(255,255,255,.24)}
.chat-msg:not(.mine) .chat-reply-quote:hover{background:var(--line-soft)}
.crq-sender{font-size:var(--fs-xs);font-weight:700;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.crq-snippet{font-size:var(--fs-sm);opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}

.chat-hover-actions{display:flex;gap:3px;opacity:0;transform:translateY(2px);transition:opacity .15s,transform .15s;pointer-events:none}
.chat-msg:hover .chat-hover-actions{opacity:1;transform:none;pointer-events:auto}
.chat-hover-actions button{width:27px;height:27px;border-radius:9px;border:1px solid var(--line);background:var(--card);
  color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:var(--fs-sm);
  box-shadow:var(--sh-sm);transition:all .12s;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.chat-hover-actions button:hover{color:#fff;background:var(--grad);border-color:transparent;transform:translateY(-1px)}

/* A message bubble shrinks to fit its (often short) original text, and a bare <textarea> with no
   rows/height defaults to ~2 cramped lines — so editing inherited both constraints and felt tiny
   even though resize:vertical technically let you drag it bigger. min-height gives it real room
   up front; the :has() rule below widens the bubble itself while editing, not just the box inside
   it, so a one-word message doesn't edit inside a one-word-wide bubble. */
.chat-edit-box{width:100%;min-width:320px;min-height:72px;border:1px solid rgba(255,255,255,.5);border-radius:9px;padding:8px;
  font:inherit;font-size:var(--fs-base);resize:vertical;background:rgba(255,255,255,.15);color:inherit;line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.chat-msg:not(.mine) .chat-edit-box{background:var(--card);border-color:var(--line);color:var(--ink)}
.chat-bubble:has(.chat-edit-box){min-width:320px}
.chat-edit-actions{display:flex;gap:6px;justify-content:flex-end;margin-top:6px}

.chat-attachments{display:flex;flex-direction:column;gap:6px;margin-bottom:6px}
.chat-img-attach{display:block;max-width:240px;border-radius:12px;overflow:hidden;box-shadow:var(--sh-sm);transition:transform .15s}
.chat-img-attach:hover{transform:scale(1.02)}
.chat-img-attach img{width:100%;display:block}
.chat-audio-attach{display:block;width:260px;max-width:100%;height:38px}
.chat-video-attach{display:block;max-width:280px;border-radius:12px;box-shadow:var(--sh-sm)}
.chat-file-chip{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.14);border-radius:12px;
  padding:9px 11px;min-width:210px;color:inherit;transition:background .12s}
.chat-msg:not(.mine) .chat-file-chip{background:var(--paper);color:var(--ink)}
.chat-file-chip:hover{background:rgba(255,255,255,.26)}
.chat-msg:not(.mine) .chat-file-chip:hover{background:var(--line-soft)}
.chat-file-chip i:first-child{font-size:19px;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;
  justify-content:center;flex:none;background:rgba(255,255,255,.25);color:#fff}
.chat-msg:not(.mine) .chat-file-chip.fc-red i:first-child{background:var(--red-bg);color:var(--red)}
.chat-msg:not(.mine) .chat-file-chip.fc-blue i:first-child{background:var(--blue-bg);color:var(--blue)}
.chat-msg:not(.mine) .chat-file-chip.fc-green i:first-child{background:var(--green-bg);color:var(--green)}
.chat-msg:not(.mine) .chat-file-chip.fc-amber i:first-child{background:var(--amber-bg);color:var(--amber)}
.chat-msg:not(.mine) .chat-file-chip.fc-purple i:first-child{background:var(--purple-bg);color:var(--purple)}
.chat-msg:not(.mine) .chat-file-chip.fc-slate i:first-child{background:var(--line-soft);color:var(--muted)}
.cfc-info{flex:1;min-width:0}
.cfc-name{font-size:var(--fs-sm);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.cfc-size{font-size:var(--fs-2xs);opacity:.75;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}

@keyframes flashHighlight{0%,100%{box-shadow:var(--sh-sm)}30%{box-shadow:0 0 0 4px rgba(216,30,124,.35)}}
.chat-msg.flash .chat-bubble{animation:flashHighlight 1.3s ease}

.chat-composer{position:relative;border-top:1px solid var(--line);padding:14px 18px;background:var(--card)}
/* @mention popup for the plain-textarea composer (reuses .bz-mention-pop/.bz-mention-opt from
   the Trix version) — anchored above the composer rather than following the caret, matching how
   Slack/Discord position their own mention popup for a plain input. */
.chat-mention-pop{bottom:calc(100% + 8px);left:18px;right:18px;max-width:none}
.chat-reply-banner,.chat-file-banner{display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:var(--paper);border-left:3px solid var(--blue);border-radius:10px;padding:7px 12px;margin-bottom:8px;font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.chat-reply-banner button,.chat-file-banner button{border:0;background:none;color:var(--faint);cursor:pointer;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.chat-record-banner{display:flex;align-items:center;gap:10px;background:var(--red-bg);border-left:3px solid var(--red);
  border-radius:10px;padding:8px 12px;margin-bottom:8px;font-size:var(--fs-md);font-weight:600;color:var(--red);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.chat-record-banner button{margin-left:auto;border:0;background:none;color:var(--red);cursor:pointer;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.chat-record-banner .btn-primary{color:#fff;margin-left:0}
.chat-record-dot{width:9px;height:9px;border-radius:50%;background:var(--red);animation:recordPulse 1.1s ease-in-out infinite}
@keyframes recordPulse{0%,100%{opacity:1}50%{opacity:.3}}
.crb-label{font-weight:600;color:var(--blue)}
.crb-snippet{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:420px}
.chat-composer-row{display:flex;align-items:flex-end;gap:8px;background:var(--paper);border:1px solid var(--line);
  border-radius:22px;padding:6px 7px 6px 14px;transition:border-color .15s,box-shadow .15s}
.chat-composer-row:focus-within{border-color:var(--blue);box-shadow:0 0 0 3px rgba(31,95,160,.12);background:var(--card)}
.chat-composer-row textarea{flex:1;resize:none;border:0;background:transparent;padding:8px 0;
  font:inherit;font-size:var(--fs-base);max-height:140px;line-height:1.4;letter-spacing:var(--ls-base)}
.chat-composer-row textarea:focus{outline:none}
.chat-composer-row .icon-btn{background:transparent;border-color:transparent}
.chat-composer-row .icon-btn:hover{background:var(--card);border-color:var(--line)}
/* Live "@mention" highlight overlay (§5, bzMentionHighlight directive) — .bz-mh-wrap takes the
   textarea's old place as the flex item (flex:1 moves here since the real textarea is now nested
   one level deeper), the mirror paints the highlighted text, and the real textarea sits on top of
   it with its own text made transparent so only the mirror's colors show through. */
.bz-mh-wrap{position:relative;flex:1;min-width:0}
.bz-mh-mirror{position:absolute;inset:0;white-space:pre-wrap;word-wrap:break-word;overflow:hidden;
  pointer-events:none;color:var(--ink)}
/* The span is metrics-neutral on purpose: it inherits the textarea's font weight and adds no
   padding, so the mirror's text wraps exactly like the real textarea's and the native caret lines
   up with it — the highlight is background and rounding only. */
.bz-mh-mention{color:var(--blue);font-weight:inherit;background:var(--blue-bg);border-radius:3px}
textarea.bz-mh-input{position:relative;z-index:1;width:100%;background:transparent!important;
  color:transparent;caret-color:var(--ink)}
.chat-send-btn{width:38px;height:38px;border-radius:50%;background:var(--grad);border:0;color:#fff;font-size:14px;
  display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none;box-shadow:0 6px 18px rgba(216,30,124,.3);transition:transform .15s}
.chat-send-btn:hover{transform:scale(1.08) rotate(-8deg)}
.chat-send-btn:active{transform:scale(.94)}
.chat-send-btn[disabled]{opacity:.4;cursor:default;transform:none;box-shadow:none}

.chat-people-list{max-height:320px;overflow-y:auto;display:flex;flex-direction:column;gap:2px}
.chat-people-row{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:12px;cursor:pointer;transition:background .12s}
.chat-people-row:hover{background:var(--paper)}
.cpr-name{font-size:var(--fs-md);font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.cpr-sub{font-size:var(--fs-xs);color:var(--muted);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.cpr-check{color:var(--magenta);font-size:18px;flex:none}
.chat-people-empty{text-align:center;color:var(--faint);font-size:var(--fs-md);padding:20px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}

/* ---- Media Gallery (project-detail's Media Gallery tab) -------------------------------- */
.media-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px}
.media-card{background:var(--card);border:1px solid var(--line);border-radius:14px;overflow:hidden;
  cursor:pointer;transition:transform .15s,box-shadow .15s}
.media-card:hover{transform:translateY(-2px);box-shadow:var(--sh-md)}
.media-thumb{position:relative;height:130px;background:var(--paper);display:flex;align-items:center;justify-content:center}
.media-thumb img,.media-thumb video{width:100%;height:100%;object-fit:cover;display:block}
.media-thumb i{font-size:34px;color:var(--faint)}
.media-play-badge{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:30px;color:#fff;
  text-shadow:0 2px 8px rgba(0,0,0,.4)}
.media-caption{padding:10px 12px}
.media-name{font-size:var(--fs-sm);font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.media-meta{font-size:var(--fs-xs);color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}

/* ---- document cards (a Project's General Docs, and any Docs & Files listing) -------------
   Same card language as .media-card above, but the thumbnail is produced by <bz-doc-thumb>
   fetching the file through the authenticated download route (media-gallery items are plain
   public asset URLs and need no such dance — see the directive's docblock). */
.doc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:14px}
.doc-card{background:var(--card);border:1px solid var(--line);border-radius:14px;overflow:hidden;
  transition:transform .15s,box-shadow .15s;display:flex;flex-direction:column}
.doc-card:hover{transform:translateY(-2px);box-shadow:var(--sh-md)}
.doc-thumb{position:relative;height:118px;background:var(--paper);display:flex;align-items:center;justify-content:center;
  overflow:hidden}
.doc-thumb img,.doc-thumb video{width:100%;height:100%;object-fit:cover;display:block}
/* A PDF page is portrait — anchor it to the top so the preview shows the page's masthead
   (title/logo), which is what makes one document recognisable from another at a glance. */
.doc-thumb canvas{width:100%;height:100%;object-fit:cover;object-position:top;display:block;background:#fff}
.doc-thumb i{font-size:30px;color:var(--faint)}
.doc-play-badge{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:28px;color:#fff;
  text-shadow:0 2px 8px rgba(0,0,0,.4)}
.doc-caption{padding:9px 11px;display:flex;flex-direction:column;gap:2px;flex:1}
.doc-name{font-size:var(--fs-sm);font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.doc-meta{font-size:var(--fs-xs);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
/* The stored display name has its extension stripped, so the type is called out explicitly. */
.doc-type{display:inline-block;font-size:var(--fs-2xs);font-weight:700;letter-spacing:.4px;color:var(--blue);
  background:var(--blue-bg);border-radius:4px;padding:1px 4px;margin-right:4px;vertical-align:1px;line-height:var(--lh-2xs)}
.doc-actions{display:flex;gap:6px;padding:0 11px 10px}
.doc-actions .btn-bz{padding:3px 8px;font-size:var(--fs-xs);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}

/* ---- topbar chat notification bell (expandable) --------------------------- */
.topbar-chat-menu{
  position:absolute;top:calc(100% + 8px);right:0;width:360px;max-width:90vw;background:var(--card);border:1px solid var(--line);
  border-radius:16px;box-shadow:var(--sh-lg);overflow:hidden;z-index:50;transform-origin:top right;
}
.tcm-head{padding:14px 16px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}
.tcm-head span{font-weight:700;color:var(--ink);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.tcm-head a{font-size:var(--fs-sm);color:var(--blue);cursor:pointer;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.tcm-list{max-height:360px;overflow-y:auto}
.tcm-row{display:flex;gap:10px;align-items:center;padding:11px 16px;cursor:pointer;transition:background .12s;border-bottom:1px solid var(--line-soft)}
.tcm-row:hover{background:var(--paper)}
.tcm-row:last-child{border-bottom:0}
.tcm-body{flex:1;min-width:0}
.tcm-name{font-size:var(--fs-md);font-weight:600;color:var(--ink);display:flex;justify-content:space-between;gap:8px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.tcm-name small{font-weight:400;color:var(--faint);font-size:var(--fs-2xs);line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.tcm-snippet{font-size:var(--fs-sm);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.tcm-empty{padding:28px 16px;text-align:center;color:var(--faint);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.tcm-foot{border-top:1px solid var(--line);padding:10px 16px;text-align:center;
  padding-bottom:calc(10px + env(safe-area-inset-bottom, 0px))}
.tcm-foot a{font-size:var(--fs-sm);font-weight:600;cursor:pointer;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}

/* unread vs. read notification rows — bold+full-color while unread, plain+muted once read,
   matching the drawer's own two-section split (unread rows first, "Previous notifications"
   below) rather than the old single dimmed-opacity list. */
.tcm-row.is-unread .tcm-name{font-weight:700;color:var(--ink)}
.tcm-row.is-read .tcm-name{font-weight:400;color:var(--muted)}
.tcm-row.is-read .tcm-snippet{color:var(--faint)}

/* Mentions and chat messages get a soft accent wash + left border while unread, so they read
   as "someone's waiting on you" at a glance instead of blending into routine status-change
   notifications — deliberately only two types get this (not one per type) to avoid turning the
   drawer into a rainbow, and it fades away once read, same as everything else in this list. The
   colored badge icon (.tcm-badge) already carries the same signal too, so this is never the only
   cue distinguishing them (color + icon + the notification's own title text). */
.tcm-row.is-unread.tcm-type-mention{background:var(--amber-bg);border-left:3px solid var(--amber);padding-left:13px}
.tcm-row.is-unread.tcm-type-chat_message{background:var(--blue-bg);border-left:3px solid var(--blue);padding-left:13px}
.notif-section-label{
  padding:12px 16px 6px;font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;
  letter-spacing:.06em;color:var(--faint);
;line-height:var(--lh-xs)}
.notif-empty-note{padding:20px 16px 8px;text-align:center;color:var(--muted);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.notif-empty-note .nen-emoji{font-size:16px;margin-right:4px}

/* small colored badge icon on a notification's avatar, signalling its type (mention/comment/
   status change/etc.) at a glance — see Notifications service's typeIcon()/typeColor(). */
.tcm-avatar-wrap{position:relative;display:inline-flex;flex-shrink:0}
.tcm-badge{
  position:absolute;bottom:-2px;right:-2px;width:16px;height:16px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:9px;color:#fff;
  border:2px solid var(--card);
}
.tcm-badge.nb-blue{background:var(--blue)}
.tcm-badge.nb-green{background:var(--green)}
.tcm-badge.nb-amber{background:var(--amber)}
.tcm-badge.nb-red{background:var(--red)}
.tcm-badge.nb-magenta{background:var(--magenta)}
.tcm-badge.nb-grey{background:var(--faint)}
/* Per-row "open in a new tab" — hover-revealed so the row doesn't look cluttered at rest;
   stopPropagation in openNotificationNewTab() keeps it from also firing the row's own
   same-tab openNotification() underneath it. */
.tcm-open-tab-btn{flex:none;width:26px;height:26px;border-radius:8px;border:1px solid var(--line);
  background:var(--card);color:var(--muted);cursor:pointer;display:flex;align-items:center;
  justify-content:center;font-size:11px;opacity:0;transition:opacity .12s,background .12s,color .12s}
.tcm-row:hover .tcm-open-tab-btn{opacity:1}
.tcm-open-tab-btn:hover{background:var(--blue-bg);color:var(--blue)}

/* ---- right-side notifications drawer — a persistent panel, not a dropdown (see
   AppController.js). Fixed to the viewport (independent of .topbar/.main) so it survives
   route changes untouched; slides in from the right at every screen width. ------------------ */
.notif-drawer{
  position:fixed;top:0;right:0;bottom:0;width:400px;max-width:92vw;z-index:46;
  /* pinned to the right/top/bottom edges, so it's the one that needs the notch/home-indicator
     insets in either orientation on a standalone-mode phone — 0 everywhere else, as always. */
  padding-right:env(safe-area-inset-right, 0px);
  background:var(--card);box-shadow:var(--sh-lg);border-left:1px solid var(--line);
  display:flex;flex-direction:column;
  transform:translateX(100%);
}
.notif-drawer.open{transform:none}
.notif-drawer-head{display:flex;align-items:center;gap:10px;padding:16px 18px;border-bottom:1px solid var(--line);flex:none;
  padding-top:calc(16px + env(safe-area-inset-top, 0px))}
.notif-drawer-head span{font-weight:700;color:var(--ink);font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.notif-drawer-head a{font-size:var(--fs-sm);font-weight:600;color:var(--blue);cursor:pointer;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
/* All/Mentions/Messages/Updates/Assignments/Team deliveries/Status & reviews/Reminders/Reactions
   — see Notifications.categories (ids + labels; the server filters and counts by the same ids).
   A horizontally scrolling row (not wrapped) since nine tabs won't fit the drawer's own width. */
.notif-cat-tabs{display:flex;gap:6px;padding:10px 18px;border-bottom:1px solid var(--line);
  overflow-x:auto;flex:none;scrollbar-width:none}
.notif-cat-tabs::-webkit-scrollbar{display:none}
.notif-cat-tabs button{flex:none;border:1px solid var(--line);background:var(--paper);border-radius:999px;
  padding:5px 12px;font:inherit;font-size:var(--fs-xs);font-weight:600;color:var(--muted);cursor:pointer;
  white-space:nowrap;transition:all .12s;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.notif-cat-tabs button.active{background:var(--navy);color:#fff;border-color:transparent}
.notif-cat-tabs button:not(.active):hover{background:var(--line-soft);color:var(--ink)}
/* Unread count per filter tab (§1) — a small pill, not a raw number sitting in the button's own
   text color, so it still reads as a count rather than part of the label. */
.notif-cat-badge{display:inline-block;min-width:15px;padding:0 4px;border-radius:999px;font-size:var(--fs-2xs);
  font-weight:800;line-height:15px;text-align:center;background:var(--red);color:#fff;vertical-align:1px;letter-spacing:var(--ls-2xs)}
.notif-cat-tabs button.active .notif-cat-badge{background:#fff;color:var(--navy)}
.notif-drawer-list{flex:1;overflow-y:auto}
@media (max-width:640px){.notif-drawer{width:100vw;max-width:100vw}}

/* full-page "All Notifications" — reuses .tcm-row's layout, wider/unwrapped for a real page */
.all-notif-row{padding:14px 16px}
.all-notif-row.unread{background:var(--blue-bg)}
.all-notif-row .tcm-name{font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.all-notif-row .tcm-snippet{white-space:normal}
.all-notif-row.unread.tcm-type-mention{background:var(--amber-bg);border-left:3px solid var(--amber);padding-left:13px}
.all-notif-row.unread.tcm-type-chat_message{background:var(--blue-bg);border-left:3px solid var(--blue);padding-left:13px}

@media (max-width:900px){
  .chat-wrap{height:calc(100vh - 220px)}
  .chat-list{width:130px}
  .chat-list-name,.chat-list-snippet{max-width:70px}
}

/* ---- Notification Channels — department sidebar + read-only trigger feed. Same sidebar+main
   two-pane shape as the Projects Calendar, distinct classes since it's a different feature. -- */
.nchan-layout{display:flex;gap:18px;align-items:flex-start}
.nchan-sidebar{width:264px;flex:none;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:14px;box-shadow:var(--sh-sm);
  max-height:calc(100vh - var(--topbar-h) - 120px);overflow-y:auto;position:sticky;top:14px}
.nchan-search{margin-bottom:10px}
.nchan-dept-list{display:flex;flex-direction:column;gap:2px}
.nchan-dept-row{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:9px;cursor:pointer;transition:background .12s}
.nchan-dept-row:hover{background:var(--paper)}
.nchan-dept-row.active{background:var(--grad-soft)}
.nchan-dept-name{font-weight:700;color:var(--ink);font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.nchan-dept-sub{font-size:var(--fs-xs);color:var(--faint);margin-top:2px;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.nchan-empty{padding:10px 4px;color:var(--faint);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.nchan-badge{flex:none;background:var(--magenta);color:#fff;font-size:var(--fs-2xs);font-weight:700;
  border-radius:99px;padding:2px 7px;min-width:18px;text-align:center;line-height:1.4;letter-spacing:var(--ls-2xs)}

.nchan-main{flex:1;min-width:0;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-sm);box-shadow:var(--sh-sm);display:flex;flex-direction:column;
  height:calc(100vh - var(--topbar-h) - 120px)}
.nchan-head{padding:16px 20px;border-bottom:1px solid var(--line)}
.nchan-head-name{font-family:var(--font-display);font-size:var(--fs-lg);font-weight:700;color:var(--ink);line-height:var(--lh-lg);letter-spacing:var(--ls-lg)}
.nchan-head-sub{font-size:var(--fs-sm);color:var(--muted);margin-top:2px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}

.nchan-feed{flex:1;overflow-y:auto;padding:10px 16px}
.nchan-item{display:flex;gap:11px;padding:11px 4px;border-bottom:1px solid var(--line-soft);position:relative}
.nchan-item:last-child{border-bottom:0}
.nchan-item.unread{background:var(--blue-bg);border-radius:10px;margin:0 -8px;padding:11px 8px}
.nchan-item.unread .nchan-item-title{font-weight:600;color:var(--ink)}
.nchan-unread-dot{position:absolute;left:-8px;top:50%;transform:translateY(-50%);width:6px;height:6px;
  border-radius:50%;background:var(--magenta)}
.nchan-item-icon{flex:none;width:32px;height:32px;border-radius:9px;background:var(--paper);
  color:var(--blue);display:flex;align-items:center;justify-content:center;font-size:14px;margin-top:1px}
.nchan-item-body{flex:1;min-width:0;cursor:pointer}
.nchan-item-row1{display:flex;align-items:center;gap:7px}
.nchan-item-recipient{font-weight:700;color:var(--ink);font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.nchan-type-chip{background:var(--blue-bg);color:var(--blue);font-size:var(--fs-2xs);padding:2px 8px;line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.nchan-item-time{font-size:var(--fs-xs);color:var(--faint);white-space:nowrap;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.nchan-item-title{font-size:var(--fs-md);color:var(--body);margin-top:3px;line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.nchan-item-snippet{font-size:var(--fs-sm);color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}

.nchan-empty-main{align-items:center;justify-content:center;text-align:center;color:var(--faint);gap:8px}
.nchan-empty-main i{font-size:30px}

@media (max-width:900px){
  .nchan-layout{flex-direction:column}
  .nchan-sidebar{width:100%;position:static;max-height:240px}
  /* width:100% for the same reason .nchan-sidebar above already carries it: once the layout
     flips to a column, .nchan-layout's align-items:flex-start stops meaning "don't stretch the
     sticky sidebar's height" and starts meaning "size both children to their own content WIDTH,"
     so .nchan-main's flex:1/min-width:0 no longer constrain anything on the cross axis. A feed
     with longer event text then sizes the card to its max-content width and the whole page
     scrolls sideways — measured 314px of document overflow on a 412px phone for a Team Member
     whose channel feed is busier than a Super Admin's (the sidebar never showed it because it
     already had this line). Restores the card to the viewport width; the feed text wraps. */
  .nchan-main{height:calc(100vh - 320px);width:100%}
}

/* ---- animations (page load stagger + route) ------------------------------ */
.view-enter{animation:viewIn .35s cubic-bezier(.2,.7,.3,1)}
@keyframes viewIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.stagger > *{opacity:0;animation:fadeUp .5s cubic-bezier(.2,.7,.3,1) forwards}
.stagger > *:nth-child(1){animation-delay:.03s}.stagger > *:nth-child(2){animation-delay:.08s}
.stagger > *:nth-child(3){animation-delay:.13s}.stagger > *:nth-child(4){animation-delay:.18s}
.stagger > *:nth-child(5){animation-delay:.23s}.stagger > *:nth-child(6){animation-delay:.28s}
.stagger > *:nth-child(7){animation-delay:.33s}.stagger > *:nth-child(8){animation-delay:.38s}
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

/* ---- pending-work panels (bz-pending-panels — Dashboard/My Dashboard, "the first thing you
   see when logging in") ------------------------------------------------------------------ */
/* Each panel is a real card (§2): a header (icon+count+label), a short list of the actual
   pending items — every row a genuine <a href> so a right-click offers "Open in new tab" — and a
   "View all" link once there are more than the capped list shows. */
.pending-panels{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;align-items:start}
.pending-panel{display:flex;flex-direction:column;padding:16px;background:var(--card);
  border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh-sm)}
.pending-panel-head{display:flex;align-items:center;gap:12px}
.pending-panel-icon{flex:none;width:44px;height:44px;border-radius:12px;display:flex;
  align-items:center;justify-content:center;font-size:19px}
.pending-panel-icon.blue{background:var(--blue-bg);color:var(--blue)}
.pending-panel-icon.amber{background:var(--amber-bg);color:var(--amber)}
.pending-panel-icon.magenta{background:rgba(216,30,124,.12);color:var(--magenta)}
.pending-panel-icon.green{background:var(--green-bg);color:var(--green)}
.pending-panel-value{font-family:var(--font-display);font-size:var(--fs-2xl);font-weight:700;color:var(--ink);line-height:1.1;letter-spacing:var(--ls-2xl)}
.pending-panel-label{font-size:var(--fs-sm);color:var(--muted);margin-top:2px;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.pending-panel-list{margin-top:12px;padding-top:10px;border-top:1px solid var(--line-soft);
  display:flex;flex-direction:column}
.pending-panel-item{display:flex;align-items:center;gap:2px;padding:5px 2px;border-radius:6px;
  color:var(--ink);text-decoration:none;font-size:var(--fs-sm);font-weight:600;transition:background .12s;line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.pending-panel-item i{color:var(--faint);font-size:18px;flex:none}
.pending-panel-item span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pending-panel-item:hover{background:var(--line-soft)}
.pending-panel-empty{margin-top:12px;padding-top:10px;border-top:1px solid var(--line-soft);
  font-size:var(--fs-sm);color:var(--faint);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.pending-panel-viewall{display:block;width:100%;margin-top:6px;padding-top:6px;font-size:var(--fs-xs);font-weight:700;
  color:var(--blue);text-decoration:none;background:none;border:0;text-align:left;font-family:inherit;cursor:pointer;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.pending-panel-viewall:hover{text-decoration:underline}
.ppi-kind{flex:none;padding:1px 6px;border-radius:999px;background:var(--line-soft);
  color:var(--muted);font-size:var(--fs-2xs);font-weight:700;text-transform:uppercase;letter-spacing:.03em;line-height:var(--lh-2xs)}
.ppi-due{flex:none;margin-left:6px;color:var(--faint);font-size:var(--fs-xs);font-weight:600;line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.ppi-due.overdue{color:var(--red)}
/* auto-fit above already reflows responsively — no extra breakpoint override needed here. */
/* "View all" modal (§) — the header/close button stay put, only the list itself scrolls, since
   a full uncapped list can run well past a single screen. */
.pending-modal{max-width:520px}
.pending-modal-list{max-height:60vh;overflow-y:auto;margin-top:0;padding-top:0;border-top:0}
.pending-modal-list .pending-panel-item{padding:8px 4px;font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}

/* ---- responsive ---------------------------------------------------------- */
.sidebar-backdrop{display:none}
.chat-back-btn{display:none}

@media (max-width:1100px){.g-4,.g-5{grid-template-columns:repeat(2,1fr)}.g-3{grid-template-columns:repeat(2,1fr)}}
@media (max-width:900px){
  /* below this width the drawer always overlays (never pushes .main — see the min-width:901px
     rule above) and the sidebar's own collapse chevron isn't useful (the topbar hamburger and
     backdrop already cover it), so hide it to avoid a redundant control crowding the brand row. */
  .sidebar-collapse-btn{display:none}
  .login-wrap{grid-template-columns:1fr}.login-art{display:none}
  .content{padding:20px 16px 48px}.topbar{padding-left:16px;padding-right:16px;gap:10px}
  /* dims + blocks the page behind the off-canvas sidebar drawer, and gives a tap-outside-to-close target */
  .sidebar-backdrop{
    display:block;position:fixed;inset:0;z-index:39;background:rgba(14,32,54,.45);
    animation:fadeIn .2s;
  }
  /* A grid item's default min-width:auto is its min-content width, so a cell holding a wide
     table (.tbl-wrap) forces its whole 1fr track out to the table's width — the track stops
     tracking the viewport and the PAGE scrolls sideways instead of the .tbl-wrap that already
     has overflow-x:auto for exactly this. Measured on a 412px phone: .health-grid's track
     resolved to 644px on /dashboard and /my-dashboard, overflowing the document by 248px.
     min-width:0 lets the track shrink back to the viewport and hands the scrolling to the
     .tbl-wrap, same as every other table screen already does. Deliberately inside this
     breakpoint (nothing at 901px+ changes) and verified on-device to alter the rendered width
     of exactly those 4 .health-grid cells — the other 175 .grid items across every nav screen
     measured byte-identical with and without it. Same flex/grid min-width:0 idiom the rest of
     this file already uses (.cal-bar-title, .pcal-project-name, .kanban-card-meta>span, …). */
  .grid > *{min-width:0}
}
@media (max-width:640px){
  .g-2,.g-3,.g-4,.g-5{grid-template-columns:1fr}
  .topbar-user-btn .tu-name{display:none}
  .topbar-user-btn{padding:5px}
  .login-card{padding:32px 24px 28px;border-radius:18px}
  .login-form{padding:20px}
  .topbar{gap:8px;padding-left:14px;padding-right:14px}
  .topbar h1{font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
  .topbar .page-sub{display:none}
  /* .title-block is the topbar's only shrinkable child, but its default min-width:auto floors it
     at the title's min-content width — so a longer title (Departments, Performance Analytics,
     Notification Channels…) pushes .topbar-user past the bar's own right padding: measured 3-12px
     of sideways PAGE scroll on a 412px phone, with the user menu's chevron clipped off-screen.
     min-width:0 lets it give way and the title ellipsises instead, the same truncation idiom this
     file already uses in 30+ places (.chat-list-name, .pcal-bar-title, .sdt-name, …). Only bites
     when the row genuinely doesn't fit — short titles (Roster, Reports) keep their natural width. */
  .topbar .title-block{min-width:0}
  .topbar h1{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .filter-search{width:100%}
  .filter-search input{min-width:0}
  .filter-bar{gap:8px}

  /* Chat: side-by-side panes don't fit a phone screen — switch to single-pane navigation.
     .chat-wrap gets .thread-open (via ng-class) once a conversation is selected; the list and
     panel/empty-state toggle visibility off that instead of showing both cramped side by side. */
  .chat-wrap{height:calc(100vh - var(--topbar-h) - 40px);border-radius:14px}
  .chat-list{width:100%;display:flex}
  .chat-list-name,.chat-list-snippet{max-width:none}
  .chat-panel,.chat-empty-panel{display:none}
  .chat-wrap.thread-open .chat-list{display:none}
  .chat-wrap.thread-open .chat-panel{display:flex}
  .chat-back-btn{display:inline-flex}
  /* The chat/project-thread composer is a rows="1" textarea that auto-grows as you type, but its
     placeholder ("Type a message… (@ to mention someone, Ctrl+Enter to send)") wraps to three lines
     at phone width and a placeholder can't be scrolled — so lines 2-3 are clipped mid-glyph inside
     the 37px box. Measured on a 412px phone: scrollHeight 79 against a 37px box. Keeping the
     placeholder on one line clips it cleanly at the box edge instead. Placeholder-only: ::placeholder
     doesn't apply once there's a value, re-confirmed on-device (108 chars typed still wrapped and
     grew the box to 121px with this rule live). */
  .bz-mh-input::placeholder{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
}

/* ---- Three.js motion accents ---------------------------------------------- */
.bz-hero-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;display:block}

.bz-burst-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;display:block}
.brand-fx img{position:relative;z-index:1}

.kpi[bz-tilt]{transform-style:preserve-3d;will-change:transform}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;transition-duration:.001ms !important}
  .stagger > *{opacity:1}
  .bz-hero-canvas,.bz-burst-canvas{display:none}
  .nav-glass-bubble{transition:none !important}
}
@media (prefers-reduced-transparency: reduce){
  .topbar{background:var(--card);backdrop-filter:none}
  [data-theme="brand"] .topbar{background:var(--card)}
  .kanban-col::before,.kanban-col-head{background:var(--card);-webkit-backdrop-filter:none;backdrop-filter:none}
  .kanban-card{background:var(--card);-webkit-backdrop-filter:none;backdrop-filter:none}
  .nav-glass-bubble{background:rgba(255,255,255,.14);-webkit-backdrop-filter:none;backdrop-filter:none}
}
@media (prefers-contrast: more){
  :root{--line:#9fb0c4}
  :root[data-theme="dark"]{--line:#4a5b70}
  :root[data-theme="brand"]{--line:#8fa8cc}
}

/* ---- universal search palette (bz-command-palette) -------------------------
   A modal task, so translucency is paired with a dimming scrim that pushes the
   page back. Blur/scale/opacity are driven per-frame by BzSpring in the directive;
   this file supplies only the resting appearance. Reuses the shared glass tokens,
   so the prefers-reduced-transparency fallback further down applies here too. */
.cp-root{position:fixed;inset:0;z-index:130;display:flex;align-items:flex-start;justify-content:center;padding:14vh 16px 16px}
.cp-scrim{position:absolute;inset:0;background:rgba(14,32,54,.5);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px)}
.cp-panel{position:relative;width:100%;max-width:640px;border-radius:18px;overflow:hidden;
  background:color-mix(in srgb, var(--card) 88%, transparent);
  -webkit-backdrop-filter:blur(var(--glass-blur-lg)) saturate(var(--glass-sat-strong));
  backdrop-filter:blur(var(--glass-blur-lg)) saturate(var(--glass-sat-strong));
  border:1px solid color-mix(in srgb, var(--card) 60%, transparent);box-shadow:var(--sh-lg)}
.cp-inputwrap{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line)}
.cp-inputicon{color:var(--faint);flex:none}
.cp-input{flex:1;min-width:0;border:0;outline:0;background:none;color:var(--ink);font-family:inherit}
.cp-hint{flex:none;color:var(--faint);border:1px solid var(--line);border-radius:6px;padding:1px 6px;text-transform:uppercase;letter-spacing:.06em}
.cp-results{max-height:52vh;overflow-y:auto;padding:6px}
.cp-group{padding:10px 10px 4px;color:var(--faint);font-weight:700;text-transform:uppercase;letter-spacing:.06em}
.cp-row{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:10px;cursor:pointer;min-height:44px}
.cp-row:hover{background:var(--paper)}
/* The active row is keyboard-driven, so it carries a real focus ring — never an
   outline removed without a replacement. */
.cp-row.active{background:var(--blue-bg);outline:2px solid var(--blue);outline-offset:-2px}
.cp-rowbody{flex:1;min-width:0}
.cp-title{color:var(--ink);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cp-sub{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cp-type{flex:none;color:var(--muted);background:var(--line-soft);border-radius:999px;padding:2px 8px;font-weight:600}
.cp-msg{flex:none;border:1px solid var(--line);background:var(--card);color:var(--blue);
  border-radius:999px;padding:3px 10px;font:inherit;font-weight:600;cursor:pointer;min-height:28px}
.cp-msg:hover{background:var(--blue-bg);border-color:var(--blue)}
.cp-empty{padding:26px 18px;text-align:center;color:var(--muted)}
.cp-loading{padding:8px 16px 12px;color:var(--faint);text-align:center}
@media (max-width:640px){.cp-root{padding:8vh 10px 10px}.cp-panel{max-width:none}}

/* The topbar control that opens it. */
.topbar-search{display:flex;align-items:center;gap:8px;background:var(--card);border:1px solid var(--line);
  border-radius:999px;padding:6px 12px;color:var(--muted);cursor:pointer;min-height:38px;
  flex:1 1 auto;min-width:0;max-width:640px;
  transition:border-color .15s,background .15s}
.topbar-search:hover{border-color:var(--blue);color:var(--blue)}
.topbar-search kbd{font:inherit;font-weight:600;color:var(--faint);background:var(--line-soft);
  border-radius:5px;padding:1px 5px;margin-left:auto}
@media (max-width:900px){.topbar-search{flex:0 0 auto;max-width:none}.topbar-search .tsearch-label,.topbar-search kbd{display:none}}

/* ---- type-scale utilities -------------------------------------------------
   These live at the END of the stylesheet on purpose. They exist because ~300
   call sites were converted from inline `style="font-size:Npx"` (which beat
   every rule) to a utility class (0,1,0) — so at equal specificity a component
   rule declared later would otherwise win and silently override the author's
   chosen size. Keeping the utilities last restores "the utility wins", which is
   the whole contract of a utility class. Anything that must beat them needs
   real specificity, not source order. */
.text-2xs{font-size:var(--fs-2xs);line-height:var(--lh-2xs);letter-spacing:var(--ls-2xs)}
.text-xs{font-size:var(--fs-xs);line-height:var(--lh-xs);letter-spacing:var(--ls-xs)}
.text-sm{font-size:var(--fs-sm);line-height:var(--lh-sm);letter-spacing:var(--ls-sm)}
.text-md{font-size:var(--fs-md);line-height:var(--lh-md);letter-spacing:var(--ls-md)}
.text-base{font-size:var(--fs-base);line-height:var(--lh-base);letter-spacing:var(--ls-base)}
.text-lg{font-size:var(--fs-lg);line-height:var(--lh-lg);letter-spacing:var(--ls-lg)}
.text-xl{font-size:var(--fs-xl);line-height:var(--lh-xl);letter-spacing:var(--ls-xl)}
.text-2xl{font-size:var(--fs-2xl);line-height:var(--lh-2xl);letter-spacing:var(--ls-2xl)}
.text-hero{font-size:var(--fs-hero);line-height:var(--lh-hero);letter-spacing:var(--ls-hero)}

/* ---- Team Capacity (Super Admin) ------------------------------------------------------------
   Shared bar (sub-navigation + month picker), dense numeric tables for the hours/cost/P&L
   matrices, and inline-editable allocation lines. Built only from the tokens above, so light,
   dark and brand themes all apply. */
.tc-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px}
.tc-tabs{display:inline-flex;flex-wrap:wrap;gap:2px;background:var(--line-soft);border-radius:var(--r-sm);padding:4px}
.tc-tabs a{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:8px;color:var(--muted);font-weight:600;
  font-size:var(--fs-sm);line-height:var(--lh-sm);text-decoration:none;transition:background .15s,color .15s;white-space:nowrap}
.tc-tabs a:hover{color:var(--ink)}
.tc-tabs a.active{background:var(--card);color:var(--ink);box-shadow:var(--sh-sm)}
.tc-month{display:flex;align-items:center;gap:8px}
.tc-month-select{min-width:170px;padding:8px 12px}
.tc-num{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
table.bz.tc-dense thead th{padding:10px 12px}
table.bz.tc-dense tbody td{padding:10px 12px}
table.bz.tc-dense td.num,table.bz.tc-dense th.num{white-space:nowrap}
table.bz tr.tc-total td{background:var(--paper);font-weight:700;color:var(--ink);border-top:1px solid var(--line)}
table.bz tr.tc-summary td{background:var(--paper);font-weight:600;color:var(--ink)}
table.bz tr.tc-summary.strong td{font-weight:700}
table.bz tr.tc-child td{color:var(--muted)}
table.bz tr.tc-child td.tc-name{padding-left:30px}
table.bz tr.tc-out td{opacity:.6}
table.bz td.tc-zero{color:var(--faint)}
table.bz th.tc-sticky,table.bz td.tc-sticky{position:sticky;left:0;z-index:1;background:var(--card)}
table.bz tr.tc-summary td.tc-sticky,table.bz tr.tc-total td.tc-sticky{background:var(--paper)}
table.bz thead th.tc-sticky{background:var(--line-soft);z-index:2}
.tc-name{font-weight:600;color:var(--ink)}
.tc-sub{font-size:var(--fs-xs);color:var(--muted);line-height:var(--lh-xs);margin-top:2px}
.tc-neg{color:var(--red)}
.tc-pos{color:var(--green)}
.tc-util{display:flex;align-items:center;gap:10px;min-width:150px}
.tc-util .progress{flex:1}
.tc-util b{font-variant-numeric:tabular-nums;min-width:44px;text-align:right}
.tc-input{width:76px;padding:6px 8px;border-radius:8px;text-align:right}
.tc-expand{background:var(--paper)}
.tc-expand > td{padding:14px 18px 18px !important}
.tc-lines{width:100%;border-collapse:separate;border-spacing:0 6px}
.tc-lines th{font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:700;text-align:left;padding:0 6px}
.tc-lines td{padding:0 6px;vertical-align:middle}
.tc-lines .input,.tc-lines .select{padding:7px 9px;border-radius:8px;font-size:var(--fs-sm)}
.tc-lines tr.tc-new td{padding-top:6px;border-top:1px dashed var(--line)}
.tc-group-note{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-xs);color:var(--blue);background:var(--blue-bg);
  border-radius:999px;padding:2px 9px;margin-left:6px;white-space:nowrap}
.tc-attn{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line-soft)}
.tc-attn:last-child{border-bottom:0}
.tc-attn > i{font-size:var(--fs-lg);margin-top:1px}
.tc-attn.red > i{color:var(--red)}.tc-attn.amber > i{color:var(--amber)}.tc-attn.blue > i{color:var(--blue)}
.tc-overview-grid{grid-template-columns:minmax(0,2fr) minmax(0,1fr);align-items:start}
@media (max-width:1100px){.tc-overview-grid{grid-template-columns:1fr}}
.tc-empty-card{text-align:center;padding:56px 24px}
.tc-empty-card i{font-size:var(--fs-hero);color:var(--faint);display:block;margin-bottom:10px}
@media (max-width:760px){
  .tc-bar{flex-direction:column;align-items:stretch}
  .tc-tabs{overflow-x:auto;flex-wrap:nowrap}
  .tc-month{justify-content:space-between}
  .tc-month-select{flex:1;min-width:0}
}
.tc-team-chip{display:inline-flex;align-items:center;gap:4px;margin:2px 4px 2px 0;padding:2px 8px;border-radius:999px;
  background:var(--line-soft);color:var(--muted);font-size:var(--fs-xs);line-height:var(--lh-xs);white-space:nowrap}
.tc-team-chip b{color:var(--ink);font-variant-numeric:tabular-nums}
.tc-month .btn-bz{white-space:nowrap}
table.bz.tc-dense .chip{white-space:nowrap}
table.bz.tc-dense .tc-name{white-space:nowrap}
.tc-util{min-width:120px}
.row-line .btn-bz{white-space:nowrap}
table.bz.tc-projects .tc-name{white-space:normal}
table.bz.tc-projects td.tc-proj-cell{min-width:220px;max-width:340px}
table.bz.tc-projects td.tc-teams-cell{min-width:240px;max-width:320px}
/* Team Capacity table headers: brand navy with white labels. */
table.bz.tc-dense thead th{background:var(--navy);color:#fff;border-bottom-color:var(--navy)}
table.bz.tc-dense thead th.tc-sticky{background:var(--navy)}
/* P&L matrices scroll inside their own box: header row pinned on top, project column on the left. */
.tbl-wrap.tc-scroll{max-height:calc(100vh - 190px);overflow:auto}
.tc-scroll table.bz thead th{position:sticky;top:0;z-index:2}
.tc-scroll table.bz thead th.tc-sticky{left:0;z-index:4}
/* Full P&L reconciliation rows */
table.bz.tc-recon tr.tc-step-subtotal td{background:var(--paper);border-top:1px solid var(--line)}
table.bz.tc-recon tr.tc-step-total td{background:var(--paper);border-top:2px solid var(--navy)}
table.bz.tc-recon tr.tc-step-subtotal td.num,table.bz.tc-recon tr.tc-step-total td.num{font-weight:700;font-size:var(--fs-base)}
table.bz.tc-recon tr.tc-step-out td.num{color:var(--body)}
.tc-full-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start}
@media (max-width:1100px){.tc-full-grid{grid-template-columns:1fr}}
.tc-del-list{margin:0;padding-left:18px;color:var(--ink)}
.tc-del-list li{margin:3px 0}
/* Team Capacity · CRM sync: CRM badge, read-only CRM project panel (three hour figures, progress,
   team → task → sub-task tree) and the Planned / Actual switch on the P&L screens. */
.tc-sync-chip{white-space:nowrap}
.tc-crm-badge{display:inline-block;margin-right:6px;padding:1px 6px;border-radius:6px;background:var(--blue-bg);color:var(--blue);
  font-size:var(--fs-2xs);font-weight:700;letter-spacing:.06em;vertical-align:1px}
.tc-crm-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
.tc-crm-stats > div{background:var(--card);border:1px solid var(--line-soft);border-radius:10px;padding:10px 12px}
.tc-crm-v{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.tc-progress-pair{display:flex;flex-direction:column;gap:4px;margin:8px 0 2px}
.progress.tc-time{height:4px}
.progress.tc-time > span{background:var(--faint)}
@media (max-width:900px){.tc-crm-stats{grid-template-columns:repeat(2,minmax(0,1fr))}}
.tc-tree{border-spacing:0}
.tc-tree td{padding:6px;border-bottom:1px solid var(--line-soft);font-size:var(--fs-sm)}
.tc-tree tr.tc-tree-team td{background:var(--line-soft);font-weight:700;color:var(--ink)}
.tc-tree tr.tc-tree-task td:first-child{padding-left:14px;font-weight:600}
.tc-tree tr.tc-tree-sub td:first-child{padding-left:30px}
.tc-tree tr.tc-tree-sub td{color:var(--muted)}
.tc-view-row{justify-content:flex-start;gap:12px;margin-bottom:14px}
/* Team Capacity · which CRM Projects are on the capacity sheet (Super Admin only). */
.tc-sheet-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:-8px 0 16px}
.tc-sheet-mark{color:var(--blue);margin-left:6px;font-size:var(--fs-xs)}
.tc-bulk-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 14px;margin-bottom:12px;
  background:var(--blue-bg);border-radius:12px;color:var(--ink)}
.tc-pick-list{max-height:55vh;overflow:auto;border:1px solid var(--line);border-radius:12px}
.tc-pick-row{display:flex;align-items:center;gap:10px;padding:9px 12px;border-bottom:1px solid var(--line-soft);cursor:pointer;margin:0}
.tc-pick-row:last-child{border-bottom:0}
.tc-pick-row:hover{background:var(--paper)}
.tc-pick-row.tc-pick-all{position:sticky;top:0;z-index:1;background:var(--card);font-weight:600;font-size:var(--fs-sm)}
.tc-pick-main{flex:1;min-width:0;display:flex;flex-direction:column}
.tc-pick-meta{display:flex;align-items:center;gap:8px;white-space:nowrap}
@media (max-width:640px){.tc-pick-row{flex-wrap:wrap}.tc-pick-meta{width:100%;padding-left:24px;flex-wrap:wrap}}

/* ---- Team Pipeline month view (team-pipeline.html). Day columns share the full width (34px
   minimum, MIN_CELL in TeamPipelineController.js) and scroll sideways below that; the name
   column stays pinned. Bars are placed in % of the strip, so they follow the columns. ---- */
.tp-scroll{overflow-x:auto;padding:0}
.tp-row{display:flex;min-width:max-content;border-bottom:1px solid var(--line-soft)}
.tp-name{position:sticky;left:0;z-index:2;flex:none;width:250px;display:flex;align-items:center;gap:8px;
  padding:8px 10px;background:var(--card);border-right:1px solid var(--line);cursor:pointer}
.tp-name-sub{cursor:default;padding-left:44px;color:var(--muted);font-size:var(--fs-sm)}
.tp-head .tp-name{cursor:default;font-weight:700;color:var(--muted);font-size:var(--fs-sm)}
.tp-caret{color:var(--faint);font-size:12px;width:12px}
.tp-who{min-width:0}
.tp-who-name{font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px;display:block}
.tp-who-sub{color:var(--muted);font-size:var(--fs-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px}
.tp-strip{display:flex;position:relative;flex:1 1 auto}
.tp-cell{flex:1 1 0;min-width:34px;min-height:40px;display:flex;flex-direction:column;align-items:center;justify-content:center;
  border-right:1px solid var(--line-soft);font-size:var(--fs-xs);color:var(--muted)}
.tp-head .tp-cell{min-height:36px}
.tp-head .tp-cell b{color:var(--ink);font-weight:700}
.tp-cell.wk{background:var(--paper)}
.tp-cell.today{box-shadow:inset 0 0 0 2px var(--blue)}
.tp-heat{cursor:pointer;font-variant-numeric:tabular-nums;font-weight:600}
.tp-heat.green{background:var(--green-bg);color:var(--green)}
.tp-heat.amber{background:var(--amber-bg);color:var(--amber)}
.tp-heat.red{background:var(--red-bg);color:var(--red)}
.tp-heat.has{background:var(--blue-bg);color:var(--ink);cursor:default}
.tp-lanes{display:flex;flex-direction:column;gap:3px;padding:6px 0}
.tp-lanes-one{justify-content:center}
.tp-lane{position:relative;height:24px}
.tp-bar{position:absolute;top:0;display:flex;align-items:center;gap:6px;height:24px;padding:0 8px;border-radius:7px;
  background:var(--blue-bg);color:var(--ink);font-size:var(--fs-xs);text-decoration:none;overflow:hidden;white-space:nowrap}
.tp-bar:hover{filter:brightness(.97);text-decoration:none}
.tp-bar.subtask{opacity:.85}
.tp-bar.completed{background:var(--green-bg)}
.tp-bar.managing{background:transparent;border:1px dashed var(--faint);color:var(--muted)}
.tp-bar.overdue{box-shadow:inset 0 0 0 2px var(--red)}
.tp-bar.clip-l{border-top-left-radius:0;border-bottom-left-radius:0}
.tp-bar.clip-r{border-top-right-radius:0;border-bottom-right-radius:0;
  -webkit-mask-image:linear-gradient(90deg,#000 85%,transparent);mask-image:linear-gradient(90deg,#000 85%,transparent)}
.tp-bar-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-weight:600}
.tp-bar-hours{flex:none;font-variant-numeric:tabular-nums}
.tp-after{position:absolute;top:0;line-height:24px;font-size:var(--fs-xs);color:var(--muted);white-space:nowrap;max-width:200px;overflow:hidden;text-overflow:ellipsis}
.tp-outside{padding:0 10px;color:var(--faint);font-size:var(--fs-xs);font-style:italic;white-space:nowrap}
.tp-legend{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:var(--fs-sm)}
.tp-swatch{display:inline-block;width:12px;height:12px;border-radius:3px;margin-left:8px}
.tp-swatch.green{background:var(--green-bg);box-shadow:inset 0 0 0 1px var(--green)}
.tp-swatch.amber{background:var(--amber-bg);box-shadow:inset 0 0 0 1px var(--amber)}
.tp-swatch.red{background:var(--red-bg);box-shadow:inset 0 0 0 1px var(--red)}
.tp-swatch.wk{background:var(--paper);box-shadow:inset 0 0 0 1px var(--line)}
.tp-day-item{padding:8px 0;border-bottom:1px solid var(--line-soft);text-decoration:none;color:inherit}
.tp-day-item:hover{background:var(--paper)}
@media (max-width:720px){ .tp-name{width:170px} .tp-who-name,.tp-who-sub{max-width:120px} }
/* "Compared with Team Capacity" panel (Super Admin) — Pipeline planned → Team Capacity committed. */
.tp-compare{margin-top:16px;padding:0;overflow:hidden}
.tp-compare-head{display:flex;align-items:center;gap:10px;width:100%;padding:12px 16px;border:0;background:none;
  font:inherit;font-size:var(--fs-md);color:var(--body);text-align:left;cursor:pointer}
.tp-compare-head > i:first-child{color:var(--blue)}
.tp-compare-head b{color:var(--ink);font-variant-numeric:tabular-nums}
.tp-compare-body{padding:4px 16px 14px;border-top:1px solid var(--line-soft)}
.tp-compare-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed var(--line-soft);font-size:var(--fs-md)}
.tp-compare-row b{font-variant-numeric:tabular-nums;white-space:nowrap}
.tp-compare-row.strong{font-weight:600;color:var(--ink)}
.tp-compare-row.total{border-bottom:0;border-top:1px solid var(--line);margin-top:2px}
/* Team Pipeline: a day cell's planned hours (large) over its logged hours (small). */
.tp-plan{line-height:1.1}
.tp-log{font-size:var(--fs-2xs);font-weight:500;color:var(--muted);line-height:1.1;margin-top:1px}
.tp-log::before{content:"\2713\00a0";opacity:.7}
.tp-legend-log{display:inline-flex;align-items:center;gap:4px;margin-left:10px}
.tp-legend-log b{font-weight:600;color:var(--ink)}
.tp-legend-log i{font-style:normal;font-size:var(--fs-2xs);color:var(--muted)}
.tp-legend-log i::before{content:"\2713\00a0";opacity:.7}
.tp-pm-name{color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px}
.tp-pm-hours{font-size:var(--fs-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px}

/* ---- Hours by day (<bz-day-planner>, app/services/dayplanner.js): one column per day — busy hours
   and who they're for, this task's hours box, and what's left. Weekends are narrow and greyed
   (never working days); an over-capacity day turns red. ---- */
.dp-head{display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;margin-bottom:10px;font-size:var(--fs-sm);color:var(--body)}
.dp-legend{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:var(--fs-xs)}
.dp-sw{display:inline-block;width:11px;height:11px;border-radius:3px;margin-left:6px}
.dp-sw.busy{background:var(--blue-bg);box-shadow:inset 0 0 0 1px var(--blue)}
.dp-sw.mine{background:var(--card);box-shadow:inset 0 0 0 1px var(--magenta)}
.dp-sw.over{background:var(--red-bg);box-shadow:inset 0 0 0 1px var(--red)}
.dp-scroll{overflow-x:auto;padding-bottom:4px}
.dp-grid{display:flex;gap:6px;min-width:max-content}
.dp-col{flex:none;width:150px;border:1px solid var(--line);border-radius:10px;background:var(--card);display:flex;flex-direction:column;overflow:hidden}
.dp-col.wk{width:58px;background:var(--paper);border-style:dashed}
.dp-col.over{border-color:var(--red);box-shadow:inset 0 0 0 1px var(--red)}
.dp-date{padding:6px 8px;font-size:var(--fs-sm);color:var(--ink);background:var(--line-soft);border-bottom:1px solid var(--line);white-space:nowrap}
.dp-col.wk .dp-date{background:transparent;color:var(--faint)}
.dp-weekend{flex:1;display:flex;align-items:center;justify-content:center;writing-mode:vertical-rl;transform:rotate(180deg);
  color:var(--faint);font-size:var(--fs-xs);letter-spacing:.08em;text-transform:uppercase;padding:10px 0}
.dp-body{display:flex;flex-direction:column;gap:6px;padding:8px;flex:1}
.dp-k{color:var(--muted);font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.06em;font-weight:700}
.dp-busy{font-size:var(--fs-sm);font-variant-numeric:tabular-nums}
.dp-items{display:flex;flex-direction:column;gap:4px;min-height:34px;max-height:150px;overflow-y:auto}
.dp-item{background:var(--blue-bg);border-radius:6px;padding:4px 6px;font-size:var(--fs-xs);line-height:1.3}
.dp-item.faint{background:transparent;padding:4px 0}
.dp-item-p{display:flex;justify-content:space-between;gap:4px;color:var(--ink);font-weight:600}
.dp-item-p span{flex:none;font-variant-numeric:tabular-nums}
.dp-item-m{color:var(--muted);overflow:hidden;text-overflow:ellipsis}
.dp-input{display:flex;flex-direction:column;gap:3px;margin:0}
.dp-input .input{padding:7px 9px;border-radius:8px;border-color:var(--magenta);text-align:right;font-variant-numeric:tabular-nums}
.dp-free{font-size:var(--fs-sm);font-variant-numeric:tabular-nums;color:var(--green)}
.dp-free.neg{color:var(--red)}
.dp-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px;font-size:var(--fs-md)}
.dp-total{font-family:var(--font-display);font-size:var(--fs-lg);color:var(--ink);font-variant-numeric:tabular-nums}
.dp-warn{color:var(--amber);font-weight:600}
.dp-over{margin-top:10px;padding:9px 12px;border-radius:10px;background:var(--red-bg);color:var(--red);font-size:var(--fs-sm);line-height:var(--lh-sm)}
.bz-modal.dp-modal{max-width:min(1100px,100%)}
.dp-summary{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 8px}
.dp-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;background:var(--line-soft);
  color:var(--body);font-size:var(--fs-xs);font-variant-numeric:tabular-nums}
.dp-chip b{color:var(--ink)}
/* Task page: the "Hours by day" strip under the task's info grid. */
.tk-plan{margin-top:14px;padding-top:12px;border-top:1px solid var(--line-soft)}
.tk-plan .dp-summary{margin:6px 0}
/* Team Pipeline "Project managers" tab: an indented tree — PM → Project → person → task — with
   assigned / logged hours and a delivery pill. Columns line up across every level. */
.tp-mgr{padding:0;overflow-x:auto}
.tp-mgr-legend{margin:2px 0 10px;display:flex;flex-wrap:wrap}
.tp-mgr-row{display:grid;grid-template-columns:minmax(300px,1fr) 90px 90px 120px minmax(170px,240px);gap:10px;align-items:center;
  padding:9px 14px;border-bottom:1px solid var(--line-soft);color:inherit;text-decoration:none;min-width:820px}
.tp-mgr-row .num{text-align:right}
.tp-mgr-head{font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);background:var(--line-soft)}
.tp-mgr-row.lvl0{cursor:pointer;background:var(--card)}
.tp-mgr-row.lvl0:hover,.tp-mgr-row.lvl1:hover,.tp-mgr-row.lvl2:hover,.tp-mgr-row.lvl3:hover{background:var(--paper)}
.tp-mgr-row.lvl1{cursor:pointer}
.tp-mgr-row.lvl1 .tp-mgr-name{padding-left:24px}
.tp-mgr-row.lvl2{cursor:pointer}
.tp-mgr-row.lvl2 .tp-mgr-name{padding-left:48px}
.tp-mgr-row.lvl3 .tp-mgr-name{padding-left:78px;flex-wrap:wrap}
.tp-mgr-name{display:flex;align-items:center;gap:6px;min-width:0}
.tp-task-title{font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:320px}
.tp-pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:var(--fs-xs);font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums}
.tp-pill.green{background:var(--green-bg);color:var(--green)}
.tp-pill.amber{background:var(--amber-bg);color:var(--amber)}
.tp-pill.red{background:var(--red-bg);color:var(--red)}
.tp-pill.open{background:var(--line-soft);color:var(--muted)}
.tp-pill.none{background:transparent;color:var(--faint);box-shadow:inset 0 0 0 1px var(--line)}
